Saturday, November 19, 2016

Notifier Chains

A Linux kernel consists from many different subsystems.
Often one subsystem wants to be notified of something happening in another subsystem. Notifier Chains solve this problem by providing a way for different subsystems to subscribe on asynchronous events from other subsystems.

Examples of such notifications include the following:
  1. Die notification - sent when a kernel function triggers a trap or a fault.
  2. Net device notification - sent when a network interface goes up or down.
  3. Internet address notification - Sent when IP is changed on a network interface.
A custom notifier may be written for some kind of a new event, example will be shown later...

To be able to receive notification, a user must register its handler function with a specific notifier.

A handler function signature is:
int some_handler(struct notifier_block *self, unsigned long val, void *data)
  • val - type of event
  • data - pointer to some data structure
Both value and data have different kind of content and it depends on the kind of event notifier.

For example, for - die event, val equals - 0 if it is an "oops" event, and data points to a structure which contains all CPU registers status.

Some example code:

#include <linux/notifier.h>
#include <asm/kdebug.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>

/* Net Device notifier definition */
static struct notifier_block my_net_dev_notifier = 
{
 .notifier_call = my_net_dev_event_handler,
};

/* Net Device notification event handler */
int my_net_dev_event_handler(struct notifier_block *self, 
    unsigned long val, void *data)
{
 printk("my_net_dev_event: status=%ld, Interface=%s\n", val, 
        ((struct net_device *) data)->name);
 return 0;
}

static int __init my_init(void)
{
 // Register Net Device Notifier
 register_netdevice_notifier(&my_net_dev_notifier);
}

You can generate this event by changing a state of a network interface, for example:
$sudo ifconfig eth0 up

You should receive a message:
my_net_dev_event_handler: status=1, Interface=eth0

When status=1, it means NETDEV_UP event.

Some example of a custom of a custom notifier:

#include <linux/notifier.h>
#include <asm/kdebug.h>

/* User-defined notifier chain implementation */
static BLOCKING_NOTIFIER_HEAD(my_notif_chain);

static struct notifier_block my_notifier = 
{
 .notifier_call = my_event_handler,
};

/* User-defined notification event handler */
int my_event_handler(struct notifier_block *self, 
                        unsigned long val, void *data)
{
 printk("my_event_handler: status=%ld\n", val);
 return 0;
}

/* Driver Initialization */
static int __init my_init(void)
{
 // Register a user-defined Notifier
 blocking_notifier_chain_register(&my_notif_chain,  
                                    &my_notifier);
} 

You may invoke this new custom event anywhere in the code by calling:
blocking_notifier_call_chain(&my_notif_chain, 1000, NULL);
You should receive a message:
my_event_handler: status=1000

Note:
We have declared our notification chain using - BLOCKING_NOTIFIER_HEAD
and registered our notifier using blocking_notifier_chain_register() function. By doing that, we declared that the notifier will be called from a process and because of that the handler function is allowed to sleep.
Now, if your notifier can be called from interrupt context, declare the notifier chain using ATOMIC_NOTIFIER_HEAD(), and register it via atomic_notifier_chain_register().

Thats all for now... Thanks for reading!


No comments:

Post a Comment