您的位置:首页 > 运维架构 > Linux

深入理解Linux网络技术内幕 第4章 通知链

2020-06-07 06:12 232 查看

通知链

概述

内核多个子系统之间具有相互依赖性,因此一个子系统需要侦测或产生其他子系统感兴趣的事件。
在网络协议栈中,比如路由子系统对网卡设备的UP、DOWN就非常感兴趣,路由子系统根据网卡产生的事件执行相应的动作。
通知链就是一个函数链表,当给定的事情发生时,就调用链表中的所有的函数。通知链有两个角色:

  • 被通知者(notified),注册回调函数给通知者。
  • 通知者(notifier),产生事件并调用回调函数。

定义通知链

内核使用struct notifier_block描述一个通知链元素。
notifier_call成员是回调函数。

struct notifier_block {
notifier_fn_t notifier_call;
struct notifier_block __rcu *next;
int priority;
};

注册通知链

notifier_chain_register函数用于实现将通知链元素放入链表中。

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函数依次调用链表中的回调函数。回调函数的返回值会影响到后面回调函数是否被调用。

#define NOTIFY_DONE		0x0000		/* Don't care */
#define NOTIFY_OK		0x0001		/* Suits me */
#define NOTIFY_STOP_MASK	0x8000		/* Don't call further */
#define NOTIFY_BAD		(NOTIFY_STOP_MASK|0x0002)
/* Bad/Veto action */
/*
* Clean way to return from the notifier and stop further calls.
*/
#define NOTIFY_STOP		(NOTIFY_OK|NOTIFY_STOP_MASK)
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);

#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)
break;
nb = next_nb;
nr_to_call--;
}
return ret;
}

网络子系统的通知链

网络设备通知链

/**
* register_netdevice_notifier - register a network notifier block
* @nb: notifier
*
* Register a notifier to be called when network device events occur.
* The notifier passed is linked into the kernel structures and must
* not be reused until it has been unregistered. A negative errno code
* is returned on a failure.
*
* When registered all registration and up events are replayed
* to the new notifier to allow device to have a race free
* view of the network device list.
*/

int register_netdevice_notifier(struct notifier_block *nb)
{
struct net_device *dev;
struct net_device *last;
struct net *net;
int err;

/* Close race with setup_net() and cleanup_net() */
down_write(&pernet_ops_rwsem);
rtnl_lock();
err = raw_notifier_chain_register(&netdev_chain, nb);
if (err)
goto unlock;
if (dev_boot_phase)
goto unlock;
for_each_net(net) {
for_each_netdev(net, dev) {
err = call_netdevice_notifier(nb, NETDEV_REGISTER, dev);
err = notifier_to_errno(err);
if (err)
goto rollback;

if (!(dev->flags & IFF_UP))
continue;

call_netdevice_notifier(nb, NETDEV_UP, dev);
}
}

unlock:
rtnl_unlock();
up_write(&pernet_ops_rwsem);
return err;

rollback:
last = dev;
for_each_net(net) {
for_each_netdev(net, dev) {
if (dev == last)
goto outroll;

if (dev->flags & IFF_UP) {
call_netdevice_notifier(nb, NETDEV_GOING_DOWN,
dev);
call_netdevice_notifier(nb, NETDEV_DOWN, dev);
}
call_netdevice_notifier(nb, NETDEV_UNREGISTER, dev);
}
}

outroll:
raw_notifier_chain_unregister(&netdev_chain, nb);
goto unlock;
}

IPV4相关的通知链

int register_inetaddr_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&inetaddr_chain, nb);
}

int register_inetaddr_validator_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&inetaddr_validator_chain, nb);
}

IPV6相关的通知链

int inet6addr_notifier_call_chain(unsigned long val, void *v)
{
return atomic_notifier_call_chain(&inet6addr_chain, val, v);
}

int register_inet6addr_validator_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&inet6addr_validator_chain, nb);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: