linux/net/core/fib_notifier.c
<<
>>
Prefs
   1#include <linux/rtnetlink.h>
   2#include <linux/notifier.h>
   3#include <linux/rcupdate.h>
   4#include <linux/kernel.h>
   5#include <linux/module.h>
   6#include <linux/init.h>
   7#include <net/net_namespace.h>
   8#include <net/fib_notifier.h>
   9
  10static ATOMIC_NOTIFIER_HEAD(fib_chain);
  11
  12int call_fib_notifier(struct notifier_block *nb, struct net *net,
  13                      enum fib_event_type event_type,
  14                      struct fib_notifier_info *info)
  15{
  16        int err;
  17
  18        info->net = net;
  19        err = nb->notifier_call(nb, event_type, info);
  20        return notifier_to_errno(err);
  21}
  22EXPORT_SYMBOL(call_fib_notifier);
  23
  24int call_fib_notifiers(struct net *net, enum fib_event_type event_type,
  25                       struct fib_notifier_info *info)
  26{
  27        int err;
  28
  29        info->net = net;
  30        err = atomic_notifier_call_chain(&fib_chain, event_type, info);
  31        return notifier_to_errno(err);
  32}
  33EXPORT_SYMBOL(call_fib_notifiers);
  34
  35static unsigned int fib_seq_sum(void)
  36{
  37        struct fib_notifier_ops *ops;
  38        unsigned int fib_seq = 0;
  39        struct net *net;
  40
  41        rtnl_lock();
  42        down_read(&net_rwsem);
  43        for_each_net(net) {
  44                rcu_read_lock();
  45                list_for_each_entry_rcu(ops, &net->fib_notifier_ops, list) {
  46                        if (!try_module_get(ops->owner))
  47                                continue;
  48                        fib_seq += ops->fib_seq_read(net);
  49                        module_put(ops->owner);
  50                }
  51                rcu_read_unlock();
  52        }
  53        up_read(&net_rwsem);
  54        rtnl_unlock();
  55
  56        return fib_seq;
  57}
  58
  59static int fib_net_dump(struct net *net, struct notifier_block *nb)
  60{
  61        struct fib_notifier_ops *ops;
  62
  63        list_for_each_entry_rcu(ops, &net->fib_notifier_ops, list) {
  64                int err;
  65
  66                if (!try_module_get(ops->owner))
  67                        continue;
  68                err = ops->fib_dump(net, nb);
  69                module_put(ops->owner);
  70                if (err)
  71                        return err;
  72        }
  73
  74        return 0;
  75}
  76
  77static bool fib_dump_is_consistent(struct notifier_block *nb,
  78                                   void (*cb)(struct notifier_block *nb),
  79                                   unsigned int fib_seq)
  80{
  81        atomic_notifier_chain_register(&fib_chain, nb);
  82        if (fib_seq == fib_seq_sum())
  83                return true;
  84        atomic_notifier_chain_unregister(&fib_chain, nb);
  85        if (cb)
  86                cb(nb);
  87        return false;
  88}
  89
  90#define FIB_DUMP_MAX_RETRIES 5
  91int register_fib_notifier(struct notifier_block *nb,
  92                          void (*cb)(struct notifier_block *nb))
  93{
  94        int retries = 0;
  95        int err;
  96
  97        do {
  98                unsigned int fib_seq = fib_seq_sum();
  99                struct net *net;
 100
 101                rcu_read_lock();
 102                for_each_net_rcu(net) {
 103                        err = fib_net_dump(net, nb);
 104                        if (err)
 105                                goto err_fib_net_dump;
 106                }
 107                rcu_read_unlock();
 108
 109                if (fib_dump_is_consistent(nb, cb, fib_seq))
 110                        return 0;
 111        } while (++retries < FIB_DUMP_MAX_RETRIES);
 112
 113        return -EBUSY;
 114
 115err_fib_net_dump:
 116        rcu_read_unlock();
 117        return err;
 118}
 119EXPORT_SYMBOL(register_fib_notifier);
 120
 121int unregister_fib_notifier(struct notifier_block *nb)
 122{
 123        return atomic_notifier_chain_unregister(&fib_chain, nb);
 124}
 125EXPORT_SYMBOL(unregister_fib_notifier);
 126
 127static int __fib_notifier_ops_register(struct fib_notifier_ops *ops,
 128                                       struct net *net)
 129{
 130        struct fib_notifier_ops *o;
 131
 132        list_for_each_entry(o, &net->fib_notifier_ops, list)
 133                if (ops->family == o->family)
 134                        return -EEXIST;
 135        list_add_tail_rcu(&ops->list, &net->fib_notifier_ops);
 136        return 0;
 137}
 138
 139struct fib_notifier_ops *
 140fib_notifier_ops_register(const struct fib_notifier_ops *tmpl, struct net *net)
 141{
 142        struct fib_notifier_ops *ops;
 143        int err;
 144
 145        ops = kmemdup(tmpl, sizeof(*ops), GFP_KERNEL);
 146        if (!ops)
 147                return ERR_PTR(-ENOMEM);
 148
 149        err = __fib_notifier_ops_register(ops, net);
 150        if (err)
 151                goto err_register;
 152
 153        return ops;
 154
 155err_register:
 156        kfree(ops);
 157        return ERR_PTR(err);
 158}
 159EXPORT_SYMBOL(fib_notifier_ops_register);
 160
 161void fib_notifier_ops_unregister(struct fib_notifier_ops *ops)
 162{
 163        list_del_rcu(&ops->list);
 164        kfree_rcu(ops, rcu);
 165}
 166EXPORT_SYMBOL(fib_notifier_ops_unregister);
 167
 168static int __net_init fib_notifier_net_init(struct net *net)
 169{
 170        INIT_LIST_HEAD(&net->fib_notifier_ops);
 171        return 0;
 172}
 173
 174static void __net_exit fib_notifier_net_exit(struct net *net)
 175{
 176        WARN_ON_ONCE(!list_empty(&net->fib_notifier_ops));
 177}
 178
 179static struct pernet_operations fib_notifier_net_ops = {
 180        .init = fib_notifier_net_init,
 181        .exit = fib_notifier_net_exit,
 182};
 183
 184static int __init fib_notifier_init(void)
 185{
 186        return register_pernet_subsys(&fib_notifier_net_ops);
 187}
 188
 189subsys_initcall(fib_notifier_init);
 190