Merge branch 'i2c/for-4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
[cascardo/linux.git] / net / ipv4 / fib_trie.c
index e2ffc2a..31cef36 100644 (file)
@@ -73,6 +73,7 @@
 #include <linux/slab.h>
 #include <linux/export.h>
 #include <linux/vmalloc.h>
+#include <linux/notifier.h>
 #include <net/net_namespace.h>
 #include <net/ip.h>
 #include <net/protocol.h>
 #include <net/tcp.h>
 #include <net/sock.h>
 #include <net/ip_fib.h>
-#include <net/switchdev.h>
 #include <trace/events/fib.h>
 #include "fib_lookup.h"
 
+static BLOCKING_NOTIFIER_HEAD(fib_chain);
+
+int register_fib_notifier(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_register(&fib_chain, nb);
+}
+EXPORT_SYMBOL(register_fib_notifier);
+
+int unregister_fib_notifier(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_unregister(&fib_chain, nb);
+}
+EXPORT_SYMBOL(unregister_fib_notifier);
+
+int call_fib_notifiers(struct net *net, enum fib_event_type event_type,
+                      struct fib_notifier_info *info)
+{
+       info->net = net;
+       return blocking_notifier_call_chain(&fib_chain, event_type, info);
+}
+
+static int call_fib_entry_notifiers(struct net *net,
+                                   enum fib_event_type event_type, u32 dst,
+                                   int dst_len, struct fib_info *fi,
+                                   u8 tos, u8 type, u32 tb_id, u32 nlflags)
+{
+       struct fib_entry_notifier_info info = {
+               .dst = dst,
+               .dst_len = dst_len,
+               .fi = fi,
+               .tos = tos,
+               .type = type,
+               .tb_id = tb_id,
+               .nlflags = nlflags,
+       };
+       return call_fib_notifiers(net, event_type, &info.info);
+}
+
 #define MAX_STAT_DEPTH 32
 
 #define KEYLENGTH      (8*sizeof(t_key))
@@ -1076,12 +1114,13 @@ static int fib_insert_alias(struct trie *t, struct key_vector *tp,
 }
 
 /* Caller must hold RTNL. */
-int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
+int fib_table_insert(struct net *net, struct fib_table *tb,
+                    struct fib_config *cfg)
 {
        struct trie *t = (struct trie *)tb->tb_data;
        struct fib_alias *fa, *new_fa;
        struct key_vector *l, *tp;
-       unsigned int nlflags = 0;
+       u16 nlflags = NLM_F_EXCL;
        struct fib_info *fi;
        u8 plen = cfg->fc_dst_len;
        u8 slen = KEYLENGTH - plen;
@@ -1126,6 +1165,8 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
                if (cfg->fc_nlflags & NLM_F_EXCL)
                        goto out;
 
+               nlflags &= ~NLM_F_EXCL;
+
                /* We have 2 goals:
                 * 1. Find exact match for type, scope, fib_info to avoid
                 * duplicate routes
@@ -1151,6 +1192,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
                        struct fib_info *fi_drop;
                        u8 state;
 
+                       nlflags |= NLM_F_REPLACE;
                        fa = fa_first;
                        if (fa_match) {
                                if (fa == fa_match)
@@ -1172,17 +1214,6 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
                        new_fa->tb_id = tb->tb_id;
                        new_fa->fa_default = -1;
 
-                       err = switchdev_fib_ipv4_add(key, plen, fi,
-                                                    new_fa->fa_tos,
-                                                    cfg->fc_type,
-                                                    cfg->fc_nlflags,
-                                                    tb->tb_id);
-                       if (err) {
-                               switchdev_fib_ipv4_abort(fi);
-                               kmem_cache_free(fn_alias_kmem, new_fa);
-                               goto out;
-                       }
-
                        hlist_replace_rcu(&fa->fa_list, &new_fa->fa_list);
 
                        alias_free_mem_rcu(fa);
@@ -1190,8 +1221,13 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
                        fib_release_info(fi_drop);
                        if (state & FA_S_ACCESSED)
                                rt_cache_flush(cfg->fc_nlinfo.nl_net);
+
+                       call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_ADD,
+                                                key, plen, fi,
+                                                new_fa->fa_tos, cfg->fc_type,
+                                                tb->tb_id, cfg->fc_nlflags);
                        rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen,
-                               tb->tb_id, &cfg->fc_nlinfo, NLM_F_REPLACE);
+                               tb->tb_id, &cfg->fc_nlinfo, nlflags);
 
                        goto succeeded;
                }
@@ -1203,7 +1239,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
                        goto out;
 
                if (cfg->fc_nlflags & NLM_F_APPEND)
-                       nlflags = NLM_F_APPEND;
+                       nlflags |= NLM_F_APPEND;
                else
                        fa = fa_first;
        }
@@ -1211,6 +1247,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
        if (!(cfg->fc_nlflags & NLM_F_CREATE))
                goto out;
 
+       nlflags |= NLM_F_CREATE;
        err = -ENOBUFS;
        new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL);
        if (!new_fa)
@@ -1224,30 +1261,22 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
        new_fa->tb_id = tb->tb_id;
        new_fa->fa_default = -1;
 
-       /* (Optionally) offload fib entry to switch hardware. */
-       err = switchdev_fib_ipv4_add(key, plen, fi, tos, cfg->fc_type,
-                                    cfg->fc_nlflags, tb->tb_id);
-       if (err) {
-               switchdev_fib_ipv4_abort(fi);
-               goto out_free_new_fa;
-       }
-
        /* Insert new entry to the list. */
        err = fib_insert_alias(t, tp, l, new_fa, fa, key);
        if (err)
-               goto out_sw_fib_del;
+               goto out_free_new_fa;
 
        if (!plen)
                tb->tb_num_default++;
 
        rt_cache_flush(cfg->fc_nlinfo.nl_net);
+       call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_ADD, key, plen, fi, tos,
+                                cfg->fc_type, tb->tb_id, cfg->fc_nlflags);
        rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, new_fa->tb_id,
                  &cfg->fc_nlinfo, nlflags);
 succeeded:
        return 0;
 
-out_sw_fib_del:
-       switchdev_fib_ipv4_del(key, plen, fi, tos, cfg->fc_type, tb->tb_id);
 out_free_new_fa:
        kmem_cache_free(fn_alias_kmem, new_fa);
 out:
@@ -1486,7 +1515,8 @@ static void fib_remove_alias(struct trie *t, struct key_vector *tp,
 }
 
 /* Caller must hold RTNL. */
-int fib_table_delete(struct fib_table *tb, struct fib_config *cfg)
+int fib_table_delete(struct net *net, struct fib_table *tb,
+                    struct fib_config *cfg)
 {
        struct trie *t = (struct trie *) tb->tb_data;
        struct fib_alias *fa, *fa_to_delete;
@@ -1539,9 +1569,9 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg)
        if (!fa_to_delete)
                return -ESRCH;
 
-       switchdev_fib_ipv4_del(key, plen, fa_to_delete->fa_info, tos,
-                              cfg->fc_type, tb->tb_id);
-
+       call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, key, plen,
+                                fa_to_delete->fa_info, tos, cfg->fc_type,
+                                tb->tb_id, 0);
        rtmsg_fib(RTM_DELROUTE, htonl(key), fa_to_delete, plen, tb->tb_id,
                  &cfg->fc_nlinfo, 0);
 
@@ -1730,82 +1760,8 @@ out:
        return NULL;
 }
 
-/* Caller must hold RTNL */
-void fib_table_flush_external(struct fib_table *tb)
-{
-       struct trie *t = (struct trie *)tb->tb_data;
-       struct key_vector *pn = t->kv;
-       unsigned long cindex = 1;
-       struct hlist_node *tmp;
-       struct fib_alias *fa;
-
-       /* walk trie in reverse order */
-       for (;;) {
-               unsigned char slen = 0;
-               struct key_vector *n;
-
-               if (!(cindex--)) {
-                       t_key pkey = pn->key;
-
-                       /* cannot resize the trie vector */
-                       if (IS_TRIE(pn))
-                               break;
-
-                       /* resize completed node */
-                       pn = resize(t, pn);
-                       cindex = get_index(pkey, pn);
-
-                       continue;
-               }
-
-               /* grab the next available node */
-               n = get_child(pn, cindex);
-               if (!n)
-                       continue;
-
-               if (IS_TNODE(n)) {
-                       /* record pn and cindex for leaf walking */
-                       pn = n;
-                       cindex = 1ul << n->bits;
-
-                       continue;
-               }
-
-               hlist_for_each_entry_safe(fa, tmp, &n->leaf, fa_list) {
-                       struct fib_info *fi = fa->fa_info;
-
-                       /* if alias was cloned to local then we just
-                        * need to remove the local copy from main
-                        */
-                       if (tb->tb_id != fa->tb_id) {
-                               hlist_del_rcu(&fa->fa_list);
-                               alias_free_mem_rcu(fa);
-                               continue;
-                       }
-
-                       /* record local slen */
-                       slen = fa->fa_slen;
-
-                       if (!fi || !(fi->fib_flags & RTNH_F_OFFLOAD))
-                               continue;
-
-                       switchdev_fib_ipv4_del(n->key, KEYLENGTH - fa->fa_slen,
-                                              fi, fa->fa_tos, fa->fa_type,
-                                              tb->tb_id);
-               }
-
-               /* update leaf slen */
-               n->slen = slen;
-
-               if (hlist_empty(&n->leaf)) {
-                       put_child_root(pn, n->key, NULL);
-                       node_free(n);
-               }
-       }
-}
-
 /* Caller must hold RTNL. */
-int fib_table_flush(struct fib_table *tb)
+int fib_table_flush(struct net *net, struct fib_table *tb)
 {
        struct trie *t = (struct trie *)tb->tb_data;
        struct key_vector *pn = t->kv;
@@ -1854,9 +1810,11 @@ int fib_table_flush(struct fib_table *tb)
                                continue;
                        }
 
-                       switchdev_fib_ipv4_del(n->key, KEYLENGTH - fa->fa_slen,
-                                              fi, fa->fa_tos, fa->fa_type,
-                                              tb->tb_id);
+                       call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_DEL,
+                                                n->key,
+                                                KEYLENGTH - fa->fa_slen,
+                                                fi, fa->fa_tos, fa->fa_type,
+                                                tb->tb_id, 0);
                        hlist_del_rcu(&fa->fa_list);
                        fib_release_info(fa->fa_info);
                        alias_free_mem_rcu(fa);