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