Skip to content

Use probes instead of version/distribution conditionals#572

Open
scaronni wants to merge 2 commits into
DisplayLink:mainfrom
scaronni:guess
Open

Use probes instead of version/distribution conditionals#572
scaronni wants to merge 2 commits into
DisplayLink:mainfrom
scaronni:guess

Conversation

@scaronni
Copy link
Copy Markdown
Contributor

@scaronni scaronni commented Jun 3, 2026

Something I wanted to do for a while. I think the various conditions based on kernel version mixed with kernels that perform backports (mostly RHEL based kernels) is not very maintainable, and parsing /etc/os-release is not very robust with the plethora of derivatives distributions (Oracle Linux, Alma Linux, etc.). Every new CentOS kernel after a RHEL point release requires adding new conditions.

This pull request uses the same approach as NVIDIA's modules, that is run a conftest.sh script that tests for each API instead of relying on manual range/versions kernel definitions plus EL8/EL9/EL10/CENTOS9/CENTOS10/RPI. It's a bit more effort in the initial part but it's more maintainable in the long run, and should not break when a distribution backports something from newer kernels.

Instead of guessing an API's presence from a version number or a distro string, we test for it: a small shell helper compiles a tiny snippet against the actual target kernel headers. If it compiles, the API is present.

  • conftest.sh is invoked from the Kbuild stage, where the kernel's exact compiler ($(CC)) and include/cflags are available. For each feature it compiles a probe snippet with -fsyntax-only -Werror=implicit-function-declaration -Werror=incompatible-pointer-types -DMODULE and, on success, emits #define EVDI_HAVE_xxx 1 into a generated evdi_detect.h (failures emit a self-documenting comment instead). -fsyntax-only performs a full parse + type-check without code generation, so it catches missing headers, removed struct members, changed function signatures, renamed/removed functions, and absent macros.

  • evdi_detect.h is force-included into every object via ccflags-y, and every object is made to depend on it. It is regenerated on each build but only rewritten when its contents change, so incremental builds and target-kernel switches both behave correctly.

  • Source guards become "intent-revealing" capability checks, e.g.:

    // before
    #if KERNEL_VERSION(6, 17, 0) <= LINUX_VERSION_CODE \
        || defined(CENTOS9) || defined(CENTOS10)
            info,
    #endif
    
    // after
    #ifdef EVDI_HAVE_FB_FORMAT_INFO
            info,
    #endif

It works identically for standalone make, DKMS, and in-tree builds, and it lets the Makefile drop the /etc/os-release and /proc/cpuinfo parsing entirely.

To support a new API difference, add a compile_test block to conftest.sh and use the resulting EVDI_HAVE_* macro.

@scaronni
Copy link
Copy Markdown
Contributor Author

scaronni commented Jun 3, 2026

This also fixes some strange conditions/comments, for example:

/* EL9 kernel removed the callback that was returning void. Do not use for EL9 */
#if KERNEL_VERSION(6, 11, 0) <= LINUX_VERSION_CODE
/* EL9 kernel removed the callback that was returning void. Do not use for EL9  */
#if KERNEL_VERSION(6, 11, 0) <= LINUX_VERSION_CODE
* EL9 kernel removed the callback that was returning void. Do not use for EL9 */
#if KERNEL_VERSION(6, 11, 0) <= LINUX_VERSION_CODE

In all cases those checks had the opposite effects on EL9.

@scaronni
Copy link
Copy Markdown
Contributor Author

scaronni commented Jun 3, 2026

Another alternative, with the same model as of now, would be to replace the host-derived EL*/CENTOS*flags with the version macros that RHEL and CentOS Stream kernels define ininclude/generated/uapi/linux/version.h`:

#define RHEL_MAJOR 9
#define RHEL_MINOR 4
#define RHEL_RELEASE_VERSION(a, b) (((a) << 8) + (b))
#define RHEL_RELEASE_CODE 2308   /* == RHEL_RELEASE_VERSION(9, 4) */

Because these come from the target kernel's own headers, they fix two of the three weaknesses above: no /etc/os-release parsing (so cross/DKMS builds are target-accurate), and minor-level granularity. Guards would become, e.g.:

#if KERNEL_VERSION(5, 16, 0) <= LINUX_VERSION_CODE || \
    (defined(RHEL_RELEASE_CODE) && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(9, 0))

But it still encodes which version backported what by hand for each RHEL minior release, and it will eventually suffer the same issues as of today, where it's constantly broken.

@timothy-lassiter
Copy link
Copy Markdown

I was working on this same issue yesterday. Had issues building against the latest AlmaLinux kernel (6.12.0-211.7.4.el10_2.x86_64) due to a backport added in 6.12.0-211.7.3.el10_2, so I also implemented the feature flag concept just like you, but just for the specific backport that was broken for me (your EVDI_HAVE_FB_FORMAT_INFO flag). I got mine working and was going to submit a PR and then noticed this. I just pulled your fork and tested it on that same kernel and sure enough it built fine. I successfully tested compilation on all of the following kernels:

  • 6.12.0-211.7.4.el10_2.x86_64
  • 6.12.0-211.7.3.el10_2.x86_64
  • 6.12.0-124.8.1.el10_1.x86_64 (pre backport)

So just wanted to let you know it appears to be compiling fine on the latest AlmaLinux 10 kernels. Thanks for providing this!

@scaronni
Copy link
Copy Markdown
Contributor Author

scaronni commented Jun 3, 2026

Awesome, thank you for the feedback. Testing all the RHEL/CentOS kernels was next on my todo list. @jakub-prussak-synaptics any chance this can be merged? If yes, I'll do all the other tests and paste the results here. Thanks.

@scaronni
Copy link
Copy Markdown
Contributor Author

scaronni commented Jun 4, 2026

I've tested all kernels and made a small adjustment for el8 and aarch64. Here is the complete set of kernels tested:

  • AlmaLinux 8 - 4.18.0-553.126.2.el8_10.x86_64
  • AlmaLinux 9 - 5.14.0-687.5.4.el9_8.x86_64
  • AlmaLinux 10 - 6.12.0-211.7.4.el10_2.x86_64
  • CentOS Stream 9 - 5.14.0-710.el9.x86_64
  • CentOS Stream 10 - 6.12.0-233.el10.x86_64
  • Fedora 44 - 7.0.10-201.fc44.x86_64
  • AlmaLinux 9 (kernel-64k) - 5.14.0-687.5.4.el9_8.aarch64+64k
  • AlmaLinux 10 (kernel-64k) - 6.12.0-211.7.4.el10_2.aarch64+64k
  • Fedora 44 - 7.0.10-201.fc44.aarch64

@scaronni
Copy link
Copy Markdown
Contributor Author

scaronni commented Jun 4, 2026

I've also found a way to extract the root filesystem from the Raspberry PI image, so I could test it as a container, so I can add this to the list:

  • Raspberry Pi OS trixie / kernel-2712 (aarch64) - 6.12.75+rpt-rpi-2712

@scaronni
Copy link
Copy Markdown
Contributor Author

scaronni commented Jun 4, 2026

For those intersted, attached is the log with all the autodetection tests of the kernels listed above.

conftest-tested-kernels.txt

Markging this MR as ready!

@scaronni scaronni marked this pull request as ready for review June 4, 2026 09:27
@jakub-prussak-synaptics
Copy link
Copy Markdown
Collaborator

That's a lot of work, thank you. We will take a look at it in the near future

@richgieg
Copy link
Copy Markdown
Contributor

richgieg commented Jun 5, 2026

I think this is really great and it's working perfectly on my EL systems! There are some errors when testing on non-EL kernels with ./ci/build_against_kernel though.

For example, when running ./ci/build_against_kernel 6.19, it shows this:

make: Entering directory '/home/richard/repos/evdi-scaronni/module'
make -C /home/richard/repos/evdi-scaronni/tmp/linux-6.19 M=$PWD
make[1]: Entering directory '/home/richard/repos/evdi-scaronni/tmp/linux-6.19'
make[2]: Entering directory '/home/richard/repos/evdi-scaronni/module'
  CC [M]  evdi_platform_drv.o
  CC [M]  evdi_platform_dev.o
  CC [M]  evdi_sysfs.o
  CC [M]  evdi_modeset.o
evdi_modeset.c:501:22: error: initialization of ‘struct drm_framebuffer * (*)(struct drm_device *, struct drm_file *, const struct drm_format_info *, const struct drm_mode_fb_cmd2 *)’ from incompatible pointer type ‘struct drm_framebuffer * (*)(struct drm_device *, struct drm_file *, const struct drm_mode_fb_cmd2 *)’ [-Wincompatible-pointer-types]
  501 |         .fb_create = evdi_fb_user_fb_create,
      |                      ^~~~~~~~~~~~~~~~~~~~~~
evdi_modeset.c:501:22: note: (near initialization for ‘evdi_mode_funcs.fb_create’)
make[4]: *** [/home/richard/repos/evdi-scaronni/tmp/linux-6.19/scripts/Makefile.build:289: evdi_modeset.o] Error 1
make[3]: *** [/home/richard/repos/evdi-scaronni/tmp/linux-6.19/Makefile:2055: .] Error 2
make[2]: *** [/home/richard/repos/evdi-scaronni/tmp/linux-6.19/Makefile:248: __sub-make] Error 2
make[2]: Leaving directory '/home/richard/repos/evdi-scaronni/module'
make[1]: *** [Makefile:248: __sub-make] Error 2
make[1]: Leaving directory '/home/richard/repos/evdi-scaronni/tmp/linux-6.19'
make: *** [Makefile:97: module] Error 2
make: Leaving directory '/home/richard/repos/evdi-scaronni/module'

Just to see if it was something weird going on with the build_against_kernel script and these new changes, I installed kernel 6.19.14 on my system, booted into it, but got a similar result when trying to build.

@richgieg
Copy link
Copy Markdown
Contributor

richgieg commented Jun 5, 2026

I found out what was causing the issue and submitted a PR to your branch:
scaronni#1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants