linux-network

View the Project on GitHub super-learners/linux-network

Chapter 4. Notification Chains

Why notification chains?

Overview

Chain Definition

struct notifier_block {
	int (*notifier_call)(struct notifier_block *nb, unsigned long action, void *data);
	struct notifier_block __rcu *next;
	int priority;
};

Chain Registration

기능 함수 프로토타입
등록 int notifier_chain_register(struct notifier_block **list, struct notifier_block *n)

Wrappers
inetaddr_chain register_inetaddr_notifier
inet6addr_chain register_inet6addr_notifier
netdev_chain register_netdevice_notifier
해제 int notifier_chain_unregister(struct notifier_block **nl, struct notifier_block *n)

Wrappers
inetaddr_chain unregister_inetaddr_notifier
inet6addr_chain unregister_inet6addr_notifier
netdev_chain unregister_netdevice_notifier
알림 int notifier_call_chain(struct notifier_block **n, unsigned long val, void *v)

Event Notification

static int notifier_call_chain(struct notifier_block **nl,
			       unsigned long val, void *v,
			       int nr_to_call, int *nr_calls)
{
	int ret = NOTIFY_DONE;
	struct notifier_block *nb, *next_nb;

	nb = rcu_dereference_raw(*nl);

	while (nb && nr_to_call) {
		next_nb = rcu_dereference_raw(nb->next);

		ret = nb->notifier_call(nb, val, v);

		if (nr_calls)
			(*nr_calls)++;

		if (ret & NOTIFY_STOP_MASK)
			break;
		nb = next_nb;
		nr_to_call--;
	}
	return ret;
}

Return Code

Notification Chains Type

Notification Chains for Networking Subsystems

Exercise

Analysis

static BLOCKING_NOTIFIER_HEAD(lkm_example_chain);
struct blocking_notifier_head {
	struct rw_semaphore rwsem;
	struct notifier_block __rcu *head;
};
static int lkm_example_event(struct notifier_block *nb,
                            unsigned long event,
                            void *ptr)
{
    printk(KERN_INFO "Event %lu is fired!\n", event);

    return NOTIFY_STOP;
}
static struct notifier_block lkm_example_notifier = {
    .notifier_call = lkm_example_event,
};
static int __init lkm_example_init(void) {
    major_num = register_chrdev(0, "lkm_example", &file_ops);
    if (major_num < 0) {
        printk(KERN_ALERT "Could not register device: %d\n", major_num);
        return major_num;
    } else {
        printk(KERN_INFO "lkm_example module loaded with device major number %d\n", major_num);
        blocking_notifier_chain_register(&lkm_example_chain, &lkm_example_notifier);
        return 0;
    }
}
static struct file_operations file_ops = {
   .read = device_read,
   .write = device_write,
   .open = device_open,
   .release = device_release
};
static ssize_t device_read(struct file *flip, char *buffer, size_t len, loff_t *offset) {
    blocking_notifier_call_chain(&lkm_example_chain, 1, NULL);

    return 0;
}
static ssize_t device_write(struct file *flip, const char *buffer, size_t len, loff_t *offset) {
    blocking_notifier_call_chain(&lkm_example_chain, 2, NULL);

    return -EINVAL;
}

Execution

sudo apt install build-essential linux-headers-`uname -r`
setproxy git clone https://github.com/super-learners/linux-network.git
cd linux-network/exercise/ch4

make
sudo -i
insmod lkm_example.ko

dmesg
...
[xxxx.xxxx] lkm example module loaded with device major number 236
mknod /dev/lkm_example c MAJOR 0
cat /dev/lkm_example

dmesg
[xxxx.xxxx] Event 1 is fired!
echo 1 > /dev/lkm_example

dmesg
[xxxx.xxxx] Event 2 is fired!
rm /dev/lkm_example
rmmod lkm_example