Skip to content

Use probes instead of version/distribution conditionals#572

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

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

Conversation

@scaronni

@scaronni scaronni commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

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

scaronni commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

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

scaronni commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

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

scaronni commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

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

scaronni commented Jun 4, 2026

Copy link
Copy Markdown
Contributor Author

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

scaronni commented Jun 4, 2026

Copy link
Copy Markdown
Contributor Author

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

scaronni commented Jun 4, 2026

Copy link
Copy Markdown
Contributor Author

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

richgieg commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

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

richgieg commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

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

@richgieg

richgieg commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

I submitted another PR to your branch (based on scaronni#1) that adds the necessary probes to get the build to succeed on kernels 4.15 to 5.9:
scaronni#2

@synaptics-lspintzyk

Copy link
Copy Markdown
Contributor

Looks very promising. First please make sure it passes, it was not working for me, perhaps you need fixes from Richard.

./ci/build_against_kernel --repo-ci all
and
./ci/build_against_kernel --repo-ci master

Also please extend ./ci/build_against_kernel script to also build on centos/redhat kernels. So we can validate it in daily builds.

@scaronni

scaronni commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

@richgieg thank you. I merged them both. I see also needs a rebase now.

Will check the ci/build_against_kernel script.

@scaronni

scaronni commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

Everything has been rebased against main. I found these repositories for the sources:

https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-10.git -b main
https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9.git -b main
https://git.almalinux.org/almalinux/kernel -b a10
https://git.almalinux.org/almalinux/kernel -b a9
https://git.almalinux.org/almalinux/kernel -b a8

The last 3 are not RHEL but AlmaLinux, but for the purposes of the test this does not matter. The RHEL sources require a valid subscription to download.

@scaronni

scaronni commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

Considering build dependencies and what not, it would probably be easier to have a ci script that builds iterating over containers, but i'll try to wire in those URLs directly.

@scaronni

scaronni commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

@synaptics-lspintzyk where do you execute the CI script? Wiring in the RHEL kernels is quite an effort, you must select OpenSSL versions specific to a certain range, some libraries which are patched only on RHEL, etc.

Would you accept a replacement for:

./ci/build_against_kernel --repo-ci all
./ci/build_against_kernel --repo-ci master

That accepts the same parameters (so wherever you run this should not be impacted) but uses containers for all builds instead? This also speeds up dramatically by using prebuilt packages, as all the kernel repositories are huge.

@scaronni

scaronni commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

I couldn't build the EL9 kernel on my Fedora system at all. I had to resort to an EL9 container with matching gcc and openssl/engine.h.

@scaronni

scaronni commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

For DKMS i run the very long suite of tests against all these distributions (I keep them up to date): https://github.com/dkms-project/dkms/blob/main/.github/workflows/tests.yml

These are not the actual upstream kernels, but you have anyway ~10 years of kernels at your disposal for testing.

@synaptics-lspintzyk

Copy link
Copy Markdown
Contributor

We are running ci internally. See Jenskinsfile and Dockerfile it this repo.
I see this is more work to do and would be hard for you to test without accessing that. So lets skip that for now.
I thought it would be enough to add git repo param to ./ci/build_against_kernel to select linux repo.

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.

5 participants