Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199// SPDX-License-Identifier: GPL-2.0#include <linux/rtnetlink.h>#include <linux/notifier.h>#include <linux/rcupdate.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <net/net_namespace.h>#include <net/netns/generic.h>#include <net/fib_notifier.h>
static unsigned int fib_notifier_net_id;
struct fib_notifier_net { struct list_head fib_notifier_ops; struct atomic_notifier_head fib_chain;};
int call_fib_notifier(struct notifier_block *nb, enum fib_event_type event_type, struct fib_notifier_info *info){ int err;
err = nb->notifier_call(nb, event_type, info); return notifier_to_errno(err);}EXPORT_SYMBOL(call_fib_notifier);
int call_fib_notifiers(struct net *net, enum fib_event_type event_type, struct fib_notifier_info *info){ struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id); int err;
err = atomic_notifier_call_chain(&fn_net->fib_chain, event_type, info); return notifier_to_errno(err);}EXPORT_SYMBOL(call_fib_notifiers);
static unsigned int fib_seq_sum(struct net *net){ struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id); struct fib_notifier_ops *ops; unsigned int fib_seq = 0;
rcu_read_lock(); list_for_each_entry_rcu(ops, &fn_net->fib_notifier_ops, list) { if (!try_module_get(ops->owner)) continue; fib_seq += ops->fib_seq_read(net); module_put(ops->owner); } rcu_read_unlock();
return fib_seq;}
static int fib_net_dump(struct net *net, struct notifier_block *nb, struct netlink_ext_ack *extack){ struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id); struct fib_notifier_ops *ops; int err = 0;
rcu_read_lock(); list_for_each_entry_rcu(ops, &fn_net->fib_notifier_ops, list) { if (!try_module_get(ops->owner)) continue; err = ops->fib_dump(net, nb, extack); module_put(ops->owner); if (err) goto unlock; }
unlock: rcu_read_unlock();
return err;}
static bool fib_dump_is_consistent(struct net *net, struct notifier_block *nb, void (*cb)(struct notifier_block *nb), unsigned int fib_seq){ struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);
atomic_notifier_chain_register(&fn_net->fib_chain, nb); if (fib_seq == fib_seq_sum(net)) return true; atomic_notifier_chain_unregister(&fn_net->fib_chain, nb); if (cb) cb(nb); return false;}
#define FIB_DUMP_MAX_RETRIES 5int register_fib_notifier(struct net *net, struct notifier_block *nb, void (*cb)(struct notifier_block *nb), struct netlink_ext_ack *extack){ int retries = 0; int err;
do { unsigned int fib_seq = fib_seq_sum(net);
err = fib_net_dump(net, nb, extack); if (err) return err;
if (fib_dump_is_consistent(net, nb, cb, fib_seq)) return 0; } while (++retries < FIB_DUMP_MAX_RETRIES);
return -EBUSY;}EXPORT_SYMBOL(register_fib_notifier);
int unregister_fib_notifier(struct net *net, struct notifier_block *nb){ struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);
return atomic_notifier_chain_unregister(&fn_net->fib_chain, nb);}EXPORT_SYMBOL(unregister_fib_notifier);
static int __fib_notifier_ops_register(struct fib_notifier_ops *ops, struct net *net){ struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id); struct fib_notifier_ops *o;
list_for_each_entry(o, &fn_net->fib_notifier_ops, list) if (ops->family == o->family) return -EEXIST; list_add_tail_rcu(&ops->list, &fn_net->fib_notifier_ops); return 0;}
struct fib_notifier_ops *fib_notifier_ops_register(const struct fib_notifier_ops *tmpl, struct net *net){ struct fib_notifier_ops *ops; int err;
ops = kmemdup(tmpl, sizeof(*ops), GFP_KERNEL); if (!ops) return ERR_PTR(-ENOMEM);
err = __fib_notifier_ops_register(ops, net); if (err) goto err_register;
return ops;
err_register: kfree(ops); return ERR_PTR(err);}EXPORT_SYMBOL(fib_notifier_ops_register);
void fib_notifier_ops_unregister(struct fib_notifier_ops *ops){ list_del_rcu(&ops->list); kfree_rcu(ops, rcu);}EXPORT_SYMBOL(fib_notifier_ops_unregister);
static int __net_init fib_notifier_net_init(struct net *net){ struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);
INIT_LIST_HEAD(&fn_net->fib_notifier_ops); ATOMIC_INIT_NOTIFIER_HEAD(&fn_net->fib_chain); return 0;}
static void __net_exit fib_notifier_net_exit(struct net *net){ struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);
WARN_ON_ONCE(!list_empty(&fn_net->fib_notifier_ops));}
static struct pernet_operations fib_notifier_net_ops = { .init = fib_notifier_net_init, .exit = fib_notifier_net_exit, .id = &fib_notifier_net_id, .size = sizeof(struct fib_notifier_net),};
static int __init fib_notifier_init(void){ return register_pernet_subsys(&fib_notifier_net_ops);}
subsys_initcall(fib_notifier_init);