other subsystem know about an event that occurred within, or was detected by, the subsystem calling the
function.
so-called publish-and-subscribe model:
function to invoke.
• The notifier is the subsystem that experiences an event and calls the callback function.
The functions executed are chosen by the notified subsystems. It is never up to the owner of the chain (the
subsystem that generates the notifications) to decide what functions to execute. The owner simply defines thelist; any kernel subsystem can register a callback function with that chain to receive the notification.
Notification Chain 首先是一个链表,类型如下:
struct notifier_block {
int (*notifier_call)(struct notifier_block *, unsigned long, void *);
struct notifier_block __rcu *next;
int priority;
};
Notification Chain 的分类:
/*
* Notifier chains are of four types:
*
* Atomic notifier chains: Chain callbacks run in interrupt/atomic
* context. Callouts are not allowed to block.
* Blocking notifier chains: Chain callbacks run in process context.
* Callouts are allowed to block.
* Raw notifier chains: There are no restrictions on callbacks,
* registration, or unregistration. All locking and protection
* must be provided by the caller.
* SRCU notifier chains: A variant of blocking notifier chains, with
* the same restrictions.
*
* atomic_notifier_chain_register() may be called from an atomic context,
* but blocking_notifier_chain_register() and srcu_notifier_chain_register()
* must be called from a process context. Ditto for the corresponding
* _unregister() routines.
*
* atomic_notifier_chain_unregister(), blocking_notifier_chain_unregister(),
* and srcu_notifier_chain_unregister() _must not_ be called from within
* the call chain.
*
* SRCU notifier chains are an alternative form of blocking notifier chains.
* They use SRCU (Sleepable Read-Copy Update) instead of rw-semaphores for
* protection of the chain links. This means there is _very_ low overhead
* in srcu_notifier_call_chain(): no cache bounces and no memory barriers.
* As compensation, srcu_notifier_chain_unregister() is rather expensive.
* SRCU notifier chains should be used when the chain will be called very
* often but notifier_blocks will seldom be removed. Also, SRCU notifier
* chains are slightly more difficult to use because they require special
* runtime initialization.
*/
下面就 inetaddr_chain 通知链举例说明使用方法,
1) 定义
static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
#define BLOCKING_NOTIFIER_HEAD(name) \
struct blocking_notifier_head name = \
BLOCKING_NOTIFIER_INIT(name)
#define BLOCKING_NOTIFIER_INIT(name) { \
.rwsem = __RWSEM_INITIALIZER((name).rwsem), \
.head = NULL }
2)Register & Unregister API
int register_inetaddr_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&inetaddr_chain, nb);
}
EXPORT_SYMBOL(register_inetaddr_notifier);
int unregister_inetaddr_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
}
3)inetaddr_chain 通知链 注册
---- register_inetaddr_notifier Matches (13 in 12 files) ----
Clip.c (net\atm): register_inetaddr_notifier(&clip_inet_notifier);
Devinet.c (net\ipv4):int register_inetaddr_notifier(struct notifier_block *nb)
Devinet.c (net\ipv4):EXPORT_SYMBOL(register_inetaddr_notifier);
Fib_frontend.c (net\ipv4): register_inetaddr_notifier(&fib_inetaddr_notifier);
Inetdevice.h (include\linux):extern int register_inetaddr_notifier(struct notifier_block *nb);
ipt_MASQUERADE.c (net\ipv4\netfilter): register_inetaddr_notifier(&masq_inet_notifier);
Main.c (net\mac80211): result = register_inetaddr_notifier(&local->ifa_notifier);
Nes.c (drivers\infiniband\hw\nes): register_inetaddr_notifier(&nes_inetaddr_notifier);
Netxen_nic_main.c (drivers\net\ethernet\qlogic\netxen): register_inetaddr_notifier(&netxen_inetaddr_cb);
Protocol.c (net\sctp): register_inetaddr_notifier(&sctp_inetaddr_notifier);
Qeth_l3_main.c (drivers\s390\net): rc = register_inetaddr_notifier(&qeth_l3_ip_notifier);
Qlcnic_main.c (drivers\net\ethernet\qlogic\qlcnic): register_inetaddr_notifier(&qlcnic_inetaddr_cb);
Via-velocity.c (drivers\net\ethernet\via): register_inetaddr_notifier(&velocity_inetaddr_notifier);
void __init ip_fib_init(void)
{
rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL, NULL);
rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL, NULL);
rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib, NULL);
register_pernet_subsys(&fib_net_ops);
register_netdevice_notifier(&fib_netdev_notifier);
register_inetaddr_notifier(&fib_inetaddr_notifier);
fib_trie_init();
}
以 ip_fib_init() 为例,在此向 inetaddr_chain 通知链 注册(请求在发生event 的时候调用自己的Callback),
static struct notifier_block fib_inetaddr_notifier = {
.notifier_call = fib_inetaddr_event,
};
之前提到了 struct notifier_block 结构体的类型,这个结构体类型有 priority,也就是优先级,注册到通知链如果不指定优先级的话,采取先注册先调用的原则,优先级高的链表元素插入在链表的前面,这样最终在通知链调用 callback 的时候直接从链表头遍历就行了。
这里注册的callback是
static int fib_inetaddr_event(struct notifier_block *this, unsigned long event, void *ptr)
4)inetaddr_chain 通知链 事件通知
接下来当发生 ipaddr 变化相关的操作时就会调用通知链通知的函数
如,
static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
u32 pid)
{
struct in_device *in_dev = ifa->ifa_dev;
struct in_ifaddr *ifa1, **ifap, **last_primary;
/* Send message first, then call notifier.
Notifier will trigger FIB update, so that
listeners of netlink will know about new ifaddr */
rtmsg_ifa(RTM_NEWADDR, ifa, nlh, pid);
blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
总结, 通知链的分析从定义,内核子系统注册,通知,都是围绕 当前通知链 链表 + 通知链链表元素 进行,因此search 通知链链表就会一目了然,分析具体的元素就知道需要做什么事情了。