contributed by < otischung >
c_cpp_properties.json
If you are using Visual Studio Code to develop the code, you may need the following settings to update the custom include path for this repository.
If there is no such file in your workspace, press ctrl+shift+p, enter "C/C++: Edit Configurations (JSON)", and you'll get the json file.
Question: What is the difference between
linux-hwe-6.11-headers-6.11.0-24
linux-headers-6.11.0-24-generic
This project involves kernel OOT (out-of-tree) modules. If bugs exists, there may cause a kernel panic. Therefore, make sure you understand the project structure and know exactly what needs to be done before making any modifications.
In the project directory, after running make
in the terminal, you're excepted to get the kxo.ko
kernel object file. You can then insert the kernel module by
To unload the kernel module, run the following command:
This project provides a userspace interface called xo-user
to display the currect Tic-Tac-Toe game board and control the kernel module. The following control commands are available:
ctrl+p
: Toggle pause/resume for the game board display.ctrl+q
: Terminate all Tic-Tac-Toe games running in kernel space.To enable terminal input shortcuts, run the following command:
After completing the above settings, you can run the userspace control program with root privileges.
make all
expends to the following command:
The directory specified in the -C
option is actually a symbolic link that points to /usr/src/linux-headers-$(shell uname -r)
.
Next, we read the manual of the make
command.
-C dir, –directory=dir
Change to directory dir before reading the makefiles or doing anything else. If multiple -C options are specified, each is interpreted relative to the previous one: -C / -C etc is equivalent to -C /etc. This is typically used with recursive invocations of make.
Therefore, we also need the Makefile
in the Linux header directory. You can find the definition of modules
here.
The definition of M=dir
is shown below:
Use make M=dir or set the environment variable KBUILD_EXTMOD to specify the directory of external module to build. Setting M= takes precedence.
The definition of O=dir
is shown below:
If you want to save output files in a different location, there are two syntaxes to specify it.
- O=
Use "make O=dir/to/store/output/files/"- Set KBUILD_OUTPUT
Set the environment variable KBUILD_OUTPUT to point to the output directory.
export KBUILD_OUTPUT=dir/to/store/output/files/; makeThe O= assignment takes precedence over the KBUILD_OUTPUT environment variable.
There are also another interesting environment variables, such as CROSS_COMPILE
and INSTALL_MOD_PATH
. I’m excited to discover that kernel compilation on the NVIDIA Jetson Orin Nano in my previous work is similar to this project. I successfully enabled custom features like exFAT, nftables, and WireGuard, then compiled and flashed them to the board.
xo-user
This is a userspace program that can read the status of the kernel module, display the current Tic-Tac-Toe game board, and control the kernel module via the kxo-state
device file.
The main process uses select
on 2 file descriptors, stdin
and XO_DEVICE_FILE
, to perform I/O multiplexing.
If the user presses the keys, stdin
is set, and listen_keyboard_handler
is triggered.
The handler opens XO_DEVICE_ATTR_FILE
to retrieve the current status of the kxo
kernel module. It then modifies the values and writes back to the file to control the kernel module.
main
The functions, kxo_state_show
and kxo_state_store
, aren't called directly in the code; rather, they're used indirectly by the sysfs subsystem. They are assigned as the callbacks for a device attribute via the macro:
This macro creates a device attribute (named dev_attr_kxo_state
) that ties kxo_state_show
to the read (show) operation and kxo_state_store
to the write (store) operation.
In linux/device.h
:
In linux/sysfs.h
:
Again, in linux/device.h
:
According to these definitions, the macro expands to:
Later in the initialization function (kxo_init
), the attribute is registered with the device by this call:
Once registered, whenever a user reads from or writes to the sysfs file (typically found at /sys/class/kxo/kxo/kxo_state
), the corresponding function is automatically invoked by the sysfs infrastructure.
The timer periodically triggers events (simulated IRQs) that update the game state.
The updated game board is rendered into the draw_buffer
.
The board message is shown below:
Data from draw_buffer
is then transferred into the rx_fifo
FIFO buffer.
This macro expands to:
A user-space process reading the device file is blocked on rx_wait
until data becomes available.
Once data is available, it is safely read using the read_lock
mutex, and the user sees the current state of the tic-tac-toe game.
These macros expands to:
Use Ftrace to analyze the above interconnect.
The producer mutex is used within workqueue handlers to serialize writes to the kfifo buffer. Since multiple work items (e.g., drawing the board or processing AI moves) might try to produce output concurrently, this lock ensures that only one producer writes to the FIFO at any given time, preventing data corruption.
Although the fast circular buffer (described next) is used in an interrupt context, its consumers run in a workqueue (kernel thread context). The consumer mutex serializes access to the fast buffer, ensuring that only one consumer retrieves data at a time, which avoids race conditions during the transfer of data from the fast buffer to the kfifo.
The fast buffer is an additional circular buffer intended to capture data quickly from the interrupt context. Because interrupt handlers must execute very fast, this buffer allows data to be temporarily stored with minimal overhead. Later, the workqueue handler can safely retrieve and process this data (using the consumer_lock) before moving it into the main kfifo buffer for userspace consumption.
It starts by writing two newline characters at the beginning of draw_buffer to provide some spacing at the top, as shown above.
After each write, smp_wmb()
(a write memory barrier) is called to ensure that the write operations are globally visible in the intended order. This is important in SMP (Symmetric Multi-Processing) environments to prevent reordering of memory writes.
The smp_wmb()
expands to:
Not exactly. Check your system configurations.
In include/generated/autoconf.h
, we have the following definition:
In include/asm-generic/barrier.h
, we have the following definition:
We have to figure out why VSCode doesn't include the autoconf.h
.
These macros ensure that the function is not being executed in interrupt or softirq context, as workqueue handlers run in process context. If either check fails, a warning is issued.
We have 3 workqueue handlers run in process context.
drawboard_work_func
display
attribute is set to '1', the board is drawn from static char table[N_GRIDS]
into static char draw_buffer[DRAWBUFFER_SIZE]
, serialized by the producer lock.kfifo
buffer, serialized by the consumer lock.ai_one_work_func
ai_two_work_func
Use eBPF to trace the above.