Linux Audio

Check our new training course

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