cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

fib_notifier.c (4885B)


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