Notification Chains in Linux Kernel
Notification chains in Linux kernel is a mechanism for the subsystems being informed by the events generated by the other subsystems.
In other words, if you are developing a new driver in Linux and you want to listen some events from other driver and handle these events in your own driver, you need to use the Linux's notification chains mechanism.
As an example, if you develop a driver and this driver needs to be informed about the net device events such as net device UP, DOWN events, the driver needs to register itself to the net device notifications with the register_netdevice_notifier function. Similarly, it unregisters itself with the unregister_netdevice_notifier function. These functions are provided by the net device subsystem.
The APIs and the data structures of notification chains are defined in the include/linux/notifier.h and kernel/notifier.c files under linux source code.
In this post, I am going to explain how this notification chains mechanism works in Linux kernel with some examples.
In Linux kernel, there are different types of notification chains based on the context they are called in. Process context or interrupt context.
1. Atomic Notifier Chains: Chain callbacks run in interrupt/atomic context. Callouts are not allowed to block. [1]
2. Blocking Notifier Chains: Chain callbacks run in process context. Callouts are allowed to block. [1]
3. Raw Notifier Chains: All locking and protection must be provided by the caller. [1]
4. SRCU Notifier Chains: A variant of blocking notifier chain. [1]
I will not explain all of these chain types here. The APIs and data structures are very similar. My example is implemented with the atomic notifier chains.
I implemented a notifier module (registrar) and 2 registrant modules which receive the notifications and handles them.
The registrar module is running a timer and it increases a counter and notifies the registrant modules with the value of this counter.
If the counter is an odd number odd module handles the notification and prints the value of the counter.
If the counter is an even number even module handles the notification and prints the value of the counter.
Linux timers is a separate subject and you can refer any Linux kernel development book to learn its details.
The type of the elements in the chain are "struct notifier_block". Each registrar has an object of this data structure and registers this object with the registrant module.
The notifier module (registrar) initializes a list and lets the registrants add themselves to this list with the register function calls.
Source Code Download:
https://github.com/sezginmurat/notification_chain.git
References:
[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/notifier.h
In other words, if you are developing a new driver in Linux and you want to listen some events from other driver and handle these events in your own driver, you need to use the Linux's notification chains mechanism.
As an example, if you develop a driver and this driver needs to be informed about the net device events such as net device UP, DOWN events, the driver needs to register itself to the net device notifications with the register_netdevice_notifier function. Similarly, it unregisters itself with the unregister_netdevice_notifier function. These functions are provided by the net device subsystem.
The APIs and the data structures of notification chains are defined in the include/linux/notifier.h and kernel/notifier.c files under linux source code.
In this post, I am going to explain how this notification chains mechanism works in Linux kernel with some examples.
In Linux kernel, there are different types of notification chains based on the context they are called in. Process context or interrupt context.
1. Atomic Notifier Chains: Chain callbacks run in interrupt/atomic context. Callouts are not allowed to block. [1]
2. Blocking Notifier Chains: Chain callbacks run in process context. Callouts are allowed to block. [1]
3. Raw Notifier Chains: All locking and protection must be provided by the caller. [1]
4. SRCU Notifier Chains: A variant of blocking notifier chain. [1]
I will not explain all of these chain types here. The APIs and data structures are very similar. My example is implemented with the atomic notifier chains.
I implemented a notifier module (registrar) and 2 registrant modules which receive the notifications and handles them.
The registrar module is running a timer and it increases a counter and notifies the registrant modules with the value of this counter.
If the counter is an odd number odd module handles the notification and prints the value of the counter.
If the counter is an even number even module handles the notification and prints the value of the counter.
Linux timers is a separate subject and you can refer any Linux kernel development book to learn its details.
struct notifier_block { notifier_fn_t notifier_call; struct notifier_block __rcu *next; int priority; };notifier_call is the function which handles the notification message. The next pointer is used to add the notifier object to the chain list. priority is the priority of the function and the functions which has highest priority is called first.
The notifier module (registrar) initializes a list and lets the registrants add themselves to this list with the register function calls.
ATOMIC_NOTIFIER_HEAD(counter_notifier_list);
The following register and unregister functions are provided by the notifier module as well.
In the init and exit functions, call the register and unregister function respectively with the address of these objects.void counter_notifier_register(struct notifier_block *nb) { atomic_notifier_chain_register(&counter_notifier_list, nb); } void counter_notifier_unregister(struct notifier_block *nb) { atomic_notifier_chain_unregister(&counter_notifier_list, nb); }The odd and even modules declares notifier_block data structures.struct notifier_block odd_nb = { .notifier_call = odd_handler_callback, } struct notifier_block even_nb = { .notifier_call = even_handler_callback, }
counter_notifier_register(&odd_nb); counter_notifier_register(&even_nb); counter_notifier_unregister(&odd_nb); counter_notifier_unregister(&even_nb);
Output of the test code:The test code is compiled and run on a Raspberry Pi 3 board running official Raspbian image.
pi@msezgin-pi:~/workspace/pi/test/notification_chains $ sudo insmod counter.ko
pi@msezgin-pi:~/workspace/pi/test/notification_chains $ sudo insmod even.ko
pi@msezgin-pi:~/workspace/pi/test/notification_chains $ sudo insmod odd.ko
[ 3047.522302] counter init
[ 3048.592209] Odd event handler: 1
[ 3049.632215] Even event handler: 2
[ 3050.672212] Odd event handler: 3
[ 3051.712234] Even event handler: 4
[ 3052.752240] Odd event handler: 5
[ 3053.792249] Even event handler: 6
[ 3054.832249] Odd event handler: 7
[ 3055.872245] Even event handler: 8
[ 3056.912258] Odd event handler: 9
[ 3057.952269] Even event handler: 10
[ 3058.992269] Odd event handler: 11
[ 3060.032273] Even event handler: 12
[ 3061.072280] Odd event handler: 13
[ 3062.112289] Even event handler: 14
[ 3063.152296] Odd event handler: 15
[ 3064.192303] Even event handler: 16
Source Code Download:
https://github.com/sezginmurat/notification_chain.git
References:
[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/notifier.h
Comments