本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,
// 基本的通知块结构
struct notifier_block { // 回调函数 int (*notifier_call)(struct notifier_block *, unsigned long, void *); // 链表中的下一个结构, 这个一个单向链表 struct notifier_block *next; // 该块的优先级, 在链表中各个块是按此优先级值进行排序的, 值大的在链表前, 表明 // 相应回调函数执行的顺序 int priority; }; #define ATOMIC_NOTIFIER_INIT(name) { \
.lock = __SPIN_LOCK_UNLOCKED(name.lock), \ .head = NULL } #define BLOCKING_NOTIFIER_INIT(name) { \ .rwsem = __RWSEM_INITIALIZER((name).rwsem), \ .head = NULL } #define RAW_NOTIFIER_INIT(name) { \ .head = NULL } #define ATOMIC_NOTIFIER_HEAD(name) \
struct atomic_notifier_head name = \ ATOMIC_NOTIFIER_INIT(name) #define BLOCKING_NOTIFIER_HEAD(name) \ struct blocking_notifier_head name = \ BLOCKING_NOTIFIER_INIT(name) #define RAW_NOTIFIER_HEAD(name) \ struct raw_notifier_head name = \ RAW_NOTIFIER_INIT(name) // nl是链表头块的地址, n是要添加到该链表的通知块
static int notifier_chain_register(struct notifier_block **nl, struct notifier_block *n) { // 使用的是dummy header算法, 即使刚开始时链表为空也不用显示判断区分 while ((*nl) != NULL) { // 判断优先权值, 优先权值越大位置越靠前 if (n->priority > (*nl)->priority) break; nl = &((*nl)->next); } // 将节点n链接到链表nl中的合适位置 n->next = *nl; // 使用rcu处理函数保证SMP下的安全性, 相当于加上锁再赋值 rcu_assign_pointer(*nl, n); return 0; } // nl是链表头块的地址, n是要删除的通知块
static int notifier_chain_unregister(struct notifier_block **nl, struct notifier_block *n) { while ((*nl) != NULL) { // 地址匹配, 进行拆除操作 if ((*nl) == n) { // *nl=n->next的安全赋值操作,相当于将节点从链表断开 rcu_assign_pointer(*nl, n->next); return 0; } nl = &((*nl)->next); } return -ENOENT; } // 安全地获取通知块指针
nb = rcu_dereference(*nl); // 链表循环 while (nb) { // 找下一个块 next_nb = rcu_dereference(nb->next); // 调用回调函数 ret = nb->notifier_call(nb, val, v); // 如果返回停止标志, 不执行后续结构 if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK) break; // 循环到下一节点 nb = next_nb; } return ret; } /**
* atomic_notifier_chain_register - Add notifier to an atomic notifier chain * @nh: Pointer to head of the atomic notifier chain * @n: New entry in notifier chain * * Adds a notifier to an atomic notifier chain. * * Currently always returns zero. */ /**
* atomic_notifier_chain_unregister - Remove notifier from an atomic notifier chain * @nh: Pointer to head of the atomic notifier chain * @n: Entry to remove from notifier chain * * Removes a notifier from an atomic notifier chain. * * Returns zero on success or %-ENOENT on failure. */ // 只是在基本通知块撤销操作前后加锁解锁进行保护 int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, struct notifier_block *n) { unsigned long flags; int ret; // 加锁 spin_lock_irqsave(&nh->lock, flags); ret = notifier_chain_unregister(&nh->head, n); // 解锁 spin_unlock_irqrestore(&nh->lock, flags); // 同步rcu, 等待一个grace period synchronize_rcu(); return ret; } /*
* This code gets used during boot-up, when task switching is * not yet working and interrupts must remain disabled. At * such times we must not call down_write(). */ // 这是内核启动时就进行调用了, 虽然可能性很小, 直接执行基本登记函数 // 不用处理信号灯, 因为此时是不能阻塞 if (unlikely(system_state == SYSTEM_BOOTING)) return notifier_chain_register(&nh->head, n); // 使用信号灯进行同步, 可能阻塞 down_write(&nh->rwsem); // 基本登记函数 ret = notifier_chain_register(&nh->head, n); up_write(&nh->rwsem); return ret; } /*
* This code gets used during boot-up, when task switching is * not yet working and interrupts must remain disabled. At * such times we must not call down_write(). */ // 这是内核启动时就进行调用了, 虽然可能性很小, 直接执行基本撤销函数 // 不用处理信号灯, 因为此时是不能阻塞 if (unlikely(system_state == SYSTEM_BOOTING)) return notifier_chain_unregister(&nh->head, n); int blocking_notifier_call_chain(struct blocking_notifier_head *nh, unsigned long val, void *v) { int ret; // 信号灯同步 down_read(&nh->rwsem); // 进行基本回调处理 ret = notifier_call_chain(&nh->head, val, v); up_read(&nh->rwsem); return ret; } /*
* This code gets used during boot-up, when task switching is * not yet working and interrupts must remain disabled. At * such times we must not call mutex_lock(). */ if (unlikely(system_state == SYSTEM_BOOTING)) return notifier_chain_register(&nh->head, n); /*
* This code gets used during boot-up, when task switching is * not yet working and interrupts must remain disabled. At * such times we must not call mutex_lock(). */ if (unlikely(system_state == SYSTEM_BOOTING)) return notifier_chain_unregister(&nh->head, n); int srcu_notifier_call_chain(struct srcu_notifier_head *nh,
unsigned long val, void *v) { int ret; int idx; // 使用srcu读锁来加锁 idx = srcu_read_lock(&nh->srcu); ret = notifier_call_chain(&nh->head, val, v); srcu_read_unlock(&nh->srcu, idx); return ret; } /* include/linux/netfilter_ipv4/ip_conntrack.h */
包装函数就可以实现回调。// 连接事件处理 static inline void ip_conntrack_event(enum ip_conntrack_events event, struct ip_conntrack *ct) { // 判断连接是否合法 if (is_confirmed(ct) && !is_dying(ct)) // 调用原子通知回调函数, 执行登记的回调函数 atomic_notifier_call_chain(&ip_conntrack_chain, event, ct); } |