????Linux ???????????????????????Notification Chains??????????????????????????????????????????????????????á?
?????????????
????????????????????£?
????struct notifier_block {
????int (*notifier_call)(struct notifier_block *?? unsigned long?? void *);
????struct notifier_block *next;
????int priority;
????};
?????? ??notifier_call ?????????е??????????????????????????????next ?????????????notifier_block??priority?????????????????????????notifier_block???????????е????? ??????0?????????????????????????С?
??????????????????????????xxx_chain??xxx_notifier_chain ??????????????
??????????notifier_block
???????????????????notifier_block????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_block
????static int notifier_chain_unregister(struct notifier_block **nl?? struct notifier_block *n)
????{
????while ((*nl) != NULL) {
????if ((*nl) == n) {
????rcu_assign_pointer(*nl?? n->next);
????return 0;
????}
????nl = &((*nl)->next);
????}
????return -ENOENT;
????}
??????????????????
?????????????????????????????????notifier_call_chain()????????ɡ????????????????????????notifier_call_chain()????????????е??
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(*nl);
while (nb && nr_to_call) {
next_nb = rcu_dereference(nb->next);
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;
}
????????nl?????????????val???????????????NETDEV_REGISTER??v???????????????????????????????????????????????? ?????????????????統(tǒng)?????????????????v???net_device????nr_to_call ????????????????-1??????????????????nr_calls ????????????????????
???????????е?notifier_block???????????????漸??????????
????NOTIFY_DONE???????????????????????
????NOTIFY_OK????????
????NOTIFY_BAD??????д?
????NOTIFY_STOP??????к??????????
????NOTIFY_STOP_MASK??????е?????
?????????????????壬??ο?include/linux/notifier.h??
????notifier_call_chain() ?????????????????????????????????????????
???????????????ж??????????
??????????????????????????????????????????Щ????
????inetaddr_chain ipv4????????????
????netdev_chain ?????豸?????????
????????????ж????????????????????????????????netdev_chain?????????register_netdevice_notifier() ?????????
int register_netdevice_notifier(struct notifier_block *nb)
{
struct net_device *dev;
struct net_device *last;
struct net *net;
int err;
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 = nb->notifier_call(nb?? NETDEV_REGISTER?? dev);
err = notifier_to_errno(err);
if (err)
goto rollback;
if (!(dev->flags & IFF_UP))
continue;
nb->notifier_call(nb?? NETDEV_UP?? dev);
}
}
unlock:
rtnl_unlock();
return err;
rollback:
last = dev;
for_each_net(net) {
for_each_netdev(net?? dev) {
if (dev == last)
break;
if (dev->flags & IFF_UP) {
nb->notifier_call(nb?? NETDEV_GOING_DOWN?? dev);
nb->notifier_call(nb?? NETDEV_DOWN?? dev);
}
nb->notifier_call(nb?? NETDEV_UNREGISTER?? dev);
}
}
raw_notifier_chain_unregister(&netdev_chain?? nb);
goto unlock;
}