内核通知链
/* test_chain_0.c :0. 申明一个通知链;1. 向内核注册通知链;2. 定义事件; 3. 导出符号,因而必需最后退出*/ #include <linux/notifier.h> #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> /* printk() */ #include <linux/fs.h> /* everything() */ #define TESTCHAIN_INIT 0x52U static RAW_NOTIFIER_HEAD(test_chain); /* define our own notifier_call_chain */ int call_test_notifiers(unsigned long val, void *v) { return raw_notifier_call_chain(&test_chain, val, v); } EXPORT_SYMBOL(call_test_notifiers); /* define our own notifier_chain_register func */ int register_test_notifier(struct notifier_block *nb) { int err; err = raw_notifier_chain_register(&test_chain, nb); return err; } EXPORT_SYMBOL(register_test_notifier); static int __init test_chain_0_init(void) { printk(KERN_DEBUG "I'm in test_chain_0\n"); return 0; } static void __exit test_chain_0_exit(void) { printk(KERN_DEBUG "Goodbye to test_chain_0\n"); // call_test_notifiers(TESTCHAIN_EXIT, (int *)NULL); } MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("fishOnFly"); module_init(test_chain_0_init); module_exit(test_chain_0_exit); |
/* test_chain_1.c :1. 定义回调函数;2. 定义notifier_block;3. 向chain_0注册notifier_block;*/ #include <linux/notifier.h> #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> /* printk() */ #include <linux/fs.h> /* everything() */ extern int register_test_notifier(struct notifier_block *nb); #define TESTCHAIN_INIT 0x52U /* realize the notifier_call func */ int test_init_event(struct notifier_block *nb, unsigned long event, void *v) { switch (event){ case TESTCHAIN_INIT: printk(KERN_DEBUG "I got the chain event: test_chain_2 is on the way of init\n"); break; default: break; } return NOTIFY_DONE; } /* define a notifier_block */ static struct notifier_block test_init_notifier = { .notifier_call = test_init_event, }; static int __init test_chain_1_init(void) { printk(KERN_DEBUG "I'm in test_chain_1\n"); register_test_notifier(&test_init_notifier); // 由chain_0提供的设施 return 0; } static void __exit test_chain_1_exit(void) { printk(KERN_DEBUG "Goodbye to test_clain_l\n"); } MODULE_LICENSE("GPL"); MODULE_AUTHOR("fishOnFly"); module_init(test_chain_1_init); module_exit(test_chain_1_exit); |
/* test_chain_2.c:发出通知链事件*/ #include <linux/notifier.h> #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> /* printk() */ #include <linux/fs.h> /* everything() */ extern int call_test_notifiers(unsigned long val, void *v); #define TESTCHAIN_INIT 0x52U static int __init test_chain_2_init(void) { printk(KERN_DEBUG "I'm in test_chain_2\n"); call_test_notifiers(TESTCHAIN_INIT, "no_use"); return 0; } static void __exit test_chain_2_exit(void) { printk(KERN_DEBUG "Goodbye to test_chain_2\n"); } MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("fishOnFly"); module_init(test_chain_2_init); module_exit(test_chain_2_exit); |
# Makefile # Comment/uncomment the following line to disable/enable debugging # DEBUG = y # Add your debugging flag (or not) to CFLAGS ifeq ($(DEBUG),y) DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines else DEBFLAGS = -O2 endif ifneq ($(KERNELRELEASE),) # call from kernel build system obj-m := test_chain_0.o test_chain_1.o test_chain_2.o else KERNELDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules endif clean: rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions depend .depend dep: $(CC) $(CFLAGS) -M *.c > .depend ifeq (.depend,$(wildcard .depend)) include .depend endif |
[wang2@iwooing: notifier_chian]$ sudo insmod./test_chain_0.ko [wang2@iwooing: notifier_chian]$ sudo insmod./test_chain_1.ko [wang2@iwooing: notifier_chian]$ sudo insmod./test_chain_2.ko [wang2@iwooing: notifier_chian]$ dmesg [ 5950.112649] I'm in test_chain_0 [ 5956.766610] I'm in test_chain_1 [ 5962.570003] I'm in test_chain_2 [ 5962.570008] I got the chain event: test_chain_2 is on the way of init [ 6464.042975] Goodbye to test_chain_2 [ 6466.368030] Goodbye to test_clain_l [ 6468.371479] Goodbye to test_chain_0 |
/* * Notifier chain core routines. The exported routines below * are layered on top of these, with appropriate locking added. */ static int notifier_chain_register(struct notifier_block **nl, struct notifier_block *n) { while ((*nl) != NULL) { if (n->priority > (*nl)->priority) break; nl = &((*nl)->next); } n->next = *nl; rcu_assign_pointer(*nl, n); return 0; } |
/** * notifier_call_chain - Informs the registered notifiers about an event. * @nl: Pointer to head of the blocking notifier chain * @val: Value passed unmodified to notifier function * @v: Pointer passed unmodified to notifier function * @nr_to_call: Number of notifier functions to be called. Don't care * value of this parameter is -1. * @nr_calls: Records the number of notifications sent. Don't care * value of this field is NULL. * @returns: notifier_call_chain returns the value returned by the * last notifier function called. */ static int __kprobes 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); #ifdef CONFIG_DEBUG_NOTIFIERS if (unlikely(!func_ptr_is_kernel_text(nb->notifier_call))) { WARN(1, "Invalid notifier called!"); nb = next_nb; continue; } #endif ret = nb->notifier_call(nb, val, v); if (nr_calls) (*nr_calls)++; if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK) break; nb = next_nb; nr_to_call--; } return ret; } |
/** * netif_carrier_on - set carrier * @dev: network device * * Device has detected that carrier. */ void netif_carrier_on(struct net_device *dev) { if (test_and_clear_bit(__LINK_STATE_NOCARRIER, &dev->state)) { if (dev->reg_state == NETREG_UNINITIALIZED) return; linkwatch_fire_event(dev); if (netif_running(dev)) __netdev_watchdog_up(dev); } } |
void linkwatch_fire_event(struct net_device *dev) { bool urgent = linkwatch_urgent_event(dev); if (!test_and_set_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state)) { linkwatch_add_event(dev); } else if (!urgent) return; linkwatch_schedule_work(urgent); } |
static void linkwatch_add_event(struct net_device *dev) { unsigned long flags; spin_lock_irqsave(&lweventlist_lock, flags); if (list_empty(&dev->link_watch_list)) { list_add_tail(&dev->link_watch_list, &lweventlist); dev_hold(dev); } spin_unlock_irqrestore(&lweventlist_lock, flags); } |
static void linkwatch_schedule_work(int urgent) { unsigned long delay = linkwatch_nextevent - jiffies; if (test_bit(LW_URGENT, &linkwatch_flags)) return; /* Minimise down-time: drop delay for up event. */ if (urgent) { if (test_and_set_bit(LW_URGENT, &linkwatch_flags)) return; delay = 0; } /* If we wrap around we'll delay it by at most HZ. */ if (delay > HZ) delay = 0; /* * This is true if we've scheduled it immeditately or if we don't * need an immediate execution and it's already pending. */ if (schedule_delayed_work(&linkwatch_work, delay) == !delay) return; /* Don't bother if there is nothing urgent. */ if (!test_bit(LW_URGENT, &linkwatch_flags)) return; /* It's already running which is good enough. */ if (!__cancel_delayed_work(&linkwatch_work)) return; /* Otherwise we reschedule it again for immediate execution. */ schedule_delayed_work(&linkwatch_work, 0); } |
static void linkwatch_event(struct work_struct *dummy) { rtnl_lock(); __linkwatch_run_queue(time_after(linkwatch_nextevent, jiffies)); rtnl_unlock(); } |
static void __linkwatch_run_queue(int urgent_only) { struct net_device *dev; LIST_HEAD(wrk); /* * Limit the number of linkwatch events to one * per second so that a runaway driver does not * cause a storm of messages on the netlink * socket. This limit does not apply to up events * while the device qdisc is down. */ if (!urgent_only) linkwatch_nextevent = jiffies + HZ; /* Limit wrap-around effect on delay. */ else if (time_after(linkwatch_nextevent, jiffies + HZ)) linkwatch_nextevent = jiffies; clear_bit(LW_URGENT, &linkwatch_flags); spin_lock_irq(&lweventlist_lock); list_splice_init(&lweventlist, &wrk); while (!list_empty(&wrk)) { dev = list_first_entry(&wrk, struct net_device, link_watch_list); list_del_init(&dev->link_watch_list); if (urgent_only && !linkwatch_urgent_event(dev)) { list_add_tail(&dev->link_watch_list, &lweventlist); continue; } spin_unlock_irq(&lweventlist_lock); linkwatch_do_dev(dev); spin_lock_irq(&lweventlist_lock); } if (!list_empty(&lweventlist)) linkwatch_schedule_work(0); spin_unlock_irq(&lweventlist_lock); } |
/** * netdev_state_change - device changes state * @dev: device to cause notification * * Called to indicate a device has changed state. This function calls * the notifier chains for netdev_chain and sends a NEWLINK message * to the routing socket. */ void netdev_state_change(struct net_device *dev) { if (dev->flags & IFF_UP) { call_netdevice_notifiers(NETDEV_CHANGE, dev); rtmsg_ifinfo(RTM_NEWLINK, dev, 0); } } |
/** * call_netdevice_notifiers - call all network notifier blocks * @val: value passed unmodified to notifier function * @dev: net_device pointer passed unmodified to notifier function * * Call all network notifier blocks. Parameters and return value * are as for raw_notifier_call_chain(). */ int call_netdevice_notifiers(unsigned long val, struct net_device *dev) { ASSERT_RTNL(); return raw_notifier_call_chain(&netdev_chain, val, dev); } |
/** * dev_open - prepare an interface for use. * @dev: device to open * * Takes a device from down to up state. The device's private open * function is invoked and then the multicast lists are loaded. Finally * the device is moved into the up state and a %NETDEV_UP message is * sent to the netdev notifier chain. * * Calling this function on an active interface is a nop. On a failure * a negative errno code is returned. */ int dev_open(struct net_device *dev) { int ret; if (dev->flags & IFF_UP) return 0; ret = __dev_open(dev); if (ret < 0) return ret; rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING); call_netdevice_notifiers(NETDEV_UP, dev); return ret; } |
联系客服