Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new(driver): kmod configure system #1452

Merged
merged 5 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions driver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,30 @@ configure_file(dkms.conf.in src/dkms.conf)
configure_file(Makefile.in src/Makefile)
configure_file(driver_config.h.in src/driver_config.h)

#
# Copy all the "configure" modules
#
file(GLOB configure_modules "${CMAKE_CURRENT_SOURCE_DIR}/configure/*")
foreach(subdir ${configure_modules})
if(IS_DIRECTORY "${subdir}")
file(RELATIVE_PATH CONFIGURE_MODULE "${CMAKE_CURRENT_SOURCE_DIR}/configure" "${subdir}")
configure_file(configure/${CONFIGURE_MODULE}/test.c src/configure/${CONFIGURE_MODULE}/test.c COPYONLY)
configure_file(configure/Makefile src/configure/${CONFIGURE_MODULE}/Makefile COPYONLY)
configure_file(configure/build.sh src/configure/${CONFIGURE_MODULE}/build.sh COPYONLY)
configure_file(configure/Makefile.inc.in src/configure/${CONFIGURE_MODULE}/Makefile.inc)
if(ENABLE_DKMS)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/src/configure/${CONFIGURE_MODULE}/build.sh"
"${CMAKE_CURRENT_BINARY_DIR}/src/configure/${CONFIGURE_MODULE}/test.c"
"${CMAKE_CURRENT_BINARY_DIR}/src/configure/${CONFIGURE_MODULE}/Makefile"
"${CMAKE_CURRENT_BINARY_DIR}/src/configure/${CONFIGURE_MODULE}/Makefile.inc"
DESTINATION "src/${DRIVER_PACKAGE_NAME}-${DRIVER_VERSION}/configure/${CONFIGURE_MODULE}"
COMPONENT ${DRIVER_COMPONENT_NAME})
endif()
endif()
endforeach()


set(DRIVER_SOURCES
dynamic_params_table.c
event_table.c
Expand Down
27 changes: 25 additions & 2 deletions driver/Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@
obj-m += @[email protected]
ccflags-y := @KBUILD_FLAGS@

KERNELDIR ?= /lib/modules/$(shell uname -r)/build
ifeq ($(strip $(MAKEFILE_LIST)),Makefile)
#
# If MAKEFILE_LIST is just "Makefile", it means `make` was invoked pointing to
# this Makefile. Targets don't make any sense if the Makefile was included.
#
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
TOP := $(shell pwd)

TOP := $(shell pwd)
all:
$(MAKE) -C $(KERNELDIR) M=$(TOP) modules

Expand All @@ -21,3 +26,21 @@ clean:

install: all
$(MAKE) -C $(KERNELDIR) M=$(TOP) modules_install

else

KERNELDIR ?= $(CURDIR)
#
# Get the path of the module sources
#
FIRST_MAKEFILE := $(firstword $(MAKEFILE_LIST))
FIRST_MAKEFILE_FILENAME := $(notdir $(FIRST_MAKEFILE))
FIRST_MAKEFILE_DIRNAME := $(shell basename $(dir $(FIRST_MAKEFILE)))
ifeq ($(FIRST_MAKEFILE_DIRNAME)/$(FIRST_MAKEFILE_FILENAME), scripts/Makefile.build)
# Build phase
MODULE_MAKEFILE_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
MAKEFILE_INC_FILES := $(shell find $(MODULE_MAKEFILE_DIR)/configure -type f -name Makefile.inc)
$(info [configure] Including $(MAKEFILE_INC_FILES))
include $(MAKEFILE_INC_FILES)
endif
endif # $(strip $(MAKEFILE_LIST)),Makefile
48 changes: 48 additions & 0 deletions driver/README.configure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Kernel module "configure" mechanism

## Rationale
The kernel module has several `#if` directives based on the linux kernel version,
to deal with breaking changes.
This unfortunately doesn't work when breaking changes are being backported by kernel providers.
Red Hat is known to do this, but they provide `RHEL_RELEASE_CODE` we can test against.

Eventually we hit some backported changes within the same RHEL release that gave us some headaches.
The last drop was EulerOS, which backports breaking changes without providing `RHEL_RELEASE_CODE` nor any other macro.

## Solution
We introduce a *configure-ish* mechanism mimicking autoconf `AC_TRY_COMPILE`.

The kernel module Makefile will include all the *sub-kmod* inside `configure` folder and compile them with the host kernel headers.
Based on the result of the compilation we'll define macros to be used in the `#if` directives.

### First use-case: `access_ok()`
Kernel change https://github.com/torvalds/linux/commit/96d4f267e introduced in 5.0 removed an argument from `access_ok()` function.
In the past we already covered RHEL backporting it with:
```c
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)) || (PPM_RHEL_RELEASE_CODE > 0 && PPM_RHEL_RELEASE_CODE >= PPM_RHEL_RELEASE_VERSION(8, 1))
#define ppm_access_ok(type, addr, size) access_ok(addr, size)
#else
#define ppm_access_ok(type, addr, size) access_ok(type, addr, size)
#endif
```
What about EulerOS and alike?

Now we have `ACCESS_OK_2` *sub-kmod* which is a basic kernel module calling:
```c
access_ok(0, 0);
```
If it builds, we'll add `-DHAS_ACCESS_OK_2` to `ccflags-y`.
The kernel module code of course has been changed to:
```c
#ifdef HAS_ACCESS_OK_2
#define ppm_access_ok(type, addr, size) access_ok(addr, size)
#else
#define ppm_access_ok(type, addr, size) access_ok(type, addr, size)
#endif
```

## How to add a new "configure" check
1. Create a new folder under `configure/` with a meaningful name. That has to be all UPPERCASE with underscores, because it will be used as a macro name, prefixed by HAS_ (e.g. `ACCESS_OK_2` generates `HAS_ACCESS_OK_2`).
2. Name the *sub-kmod* source `test.c`. CMake and the predefined Makefile relies on the name being `test.c`.
3. Update the kernel module code to use the new macro.
4. Bob's your uncle.
32 changes: 32 additions & 0 deletions driver/configure/ACCESS_OK_2/test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*

Copyright (C) 2023 The Falco Authors.

This file is dual licensed under either the MIT or GPL 2. See MIT.txt
or GPL2.txt for full copies of the license.

*/

/*
* Check that access_ok builds with 2 parameters
* See https://github.com/torvalds/linux/commit/96d4f267e
*/

#include <linux/module.h>
#include <linux/uaccess.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("the Falco authors");

static int access_ok_init(void)
{
access_ok(0, 0);
return 0;
}

static void access_ok_exit(void)
{
}

module_init(access_ok_init);
module_exit(access_ok_exit);
41 changes: 41 additions & 0 deletions driver/configure/DEVNODE_ARG1_CONST/test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*

Copyright (C) 2023 The Falco Authors.

This file is dual licensed under either the MIT or GPL 2. See MIT.txt
or GPL2.txt for full copies of the license.

*/

/*
* Check for devnode() in struct class taking a const first argument
* See https://github.com/torvalds/linux/commit/ff62b8e6588fb07bedda7423622c140c4edd66a7
*/

#include <linux/module.h>
#include <linux/device.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("the Falco authors");

static char *ppm_devnode(const struct device *dev, umode_t *mode)
{
return NULL;
}

static int devnode_dev_const_init(void)
{
struct class g_ppm_class = {
.devnode = ppm_devnode
};
/* suppress unused variable warning by casting to void */
(void)g_ppm_class;
return 0;
}

static void devnode_dev_const_exit(void)
{
}

module_init(devnode_dev_const_init);
module_exit(devnode_dev_const_exit);
21 changes: 21 additions & 0 deletions driver/configure/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#
# Copyright (C) 2023 The Falco Authors.
#
# This file is dual licensed under either the MIT or GPL 2. See
# MIT.txt or GPL.txt for full copies of the license.
#

testmod-y += test.o
obj-m += testmod.o

KERNELDIR ?= /lib/modules/$(shell uname -r)/build
TOP := $(shell pwd)

all:
$(MAKE) -C $(KERNELDIR) M=$(TOP) modules

clean:
$(MAKE) -C $(KERNELDIR) M=$(TOP) clean

install: all
$(MAKE) -C $(KERNELDIR) M=$(TOP) modules_install
13 changes: 13 additions & 0 deletions driver/configure/Makefile.inc.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
MODULE_MAKEFILE_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))

# Run the module build.sh (wrapper for make) script with an empty environment, but PATH
HAS_@CONFIGURE_MODULE@ := $(shell env -i PATH="$(PATH)" KERNELDIR="$(KERNELDIR)" sh $(MODULE_MAKEFILE_DIR)/build.sh ; echo $$?)

ifeq ($(HAS_@CONFIGURE_MODULE@),0)
$(info [configure] Setting HAS_@CONFIGURE_MODULE@ flag)
ccflags-y += -DHAS_@CONFIGURE_MODULE@
else
HAS_@CONFIGURE_MODULE@_OUT := $(shell cat $(MODULE_MAKEFILE_DIR)/build.log)
$(info [configure] Build output for HAS_@CONFIGURE_MODULE@:)
$(info [configure] $(HAS_@CONFIGURE_MODULE@_OUT))
endif
13 changes: 13 additions & 0 deletions driver/configure/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/sh

#
# Copyright (C) 2023 The Falco Authors.
#
# This file is dual licensed under either the MIT or GPL 2. See
# MIT.txt or GPL.txt for full copies of the license.
#

SCRIPT=$(readlink -f "$0")
SCRIPT_DIR=$(dirname ${SCRIPT})

make -C ${SCRIPT_DIR} > ${SCRIPT_DIR}/build.log 2>&1
13 changes: 2 additions & 11 deletions driver/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2718,24 +2718,15 @@ static int get_tracepoint_handles(void)
#endif

#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0)) || \
( \
(PPM_RHEL_RELEASE_CODE > 0) && \
( \
((PPM_RHEL_RELEASE_CODE >= PPM_RHEL_RELEASE_VERSION(8, 9)) && \
(PPM_RHEL_RELEASE_CODE < PPM_RHEL_RELEASE_VERSION(9, 0))) \
|| \
(PPM_RHEL_RELEASE_CODE >= PPM_RHEL_RELEASE_VERSION(9, 3)) \
) \
)
#ifdef HAS_DEVNODE_ARG1_CONST
static char *ppm_devnode(const struct device *dev, umode_t *mode)
#else
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)
static char *ppm_devnode(struct device *dev, umode_t *mode)
#else
static char *ppm_devnode(struct device *dev, mode_t *mode)
#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(3, 3, 0) */
#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(6, 2, 0) */
#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) */
{
if (mode) {
*mode = 0400;
Expand Down
2 changes: 1 addition & 1 deletion driver/ppm_events.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ or GPL2.txt for full copies of the license.
#ifdef access_ok_noprefault
#define ppm_access_ok access_ok_noprefault
#else
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)) || (PPM_RHEL_RELEASE_CODE > 0 && PPM_RHEL_RELEASE_CODE >= PPM_RHEL_RELEASE_VERSION(8, 1))
#ifdef HAS_ACCESS_OK_2
#define ppm_access_ok(type, addr, size) access_ok(addr, size)
#else
#define ppm_access_ok(type, addr, size) access_ok(type, addr, size)
Expand Down
Loading