mlxsw: spectrum_router: Implement private fib
authorJiri Pirko <jiri@mellanox.com>
Mon, 4 Jul 2016 06:23:04 +0000 (08:23 +0200)
committerDavid S. Miller <davem@davemloft.net>
Tue, 5 Jul 2016 01:25:14 +0000 (18:25 -0700)
Shadow FIB is needed in order to hold additional information for FIB
entries and keep track of used prefixes. That is needed for the LPM tree
construction to be introduced later on in this set.

Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Reviewed-by: Ido Schimmel <idosch@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/mellanox/mlxsw/spectrum.h
drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c

index 83d5807832a084923b6060b187eed1db2562970e..da0e0720389bb3b0badf023906a23db5a4026c5e 100644 (file)
@@ -43,6 +43,7 @@
 #include <linux/if_vlan.h>
 #include <linux/list.h>
 #include <linux/dcbnl.h>
+#include <linux/in6.h>
 #include <net/switchdev.h>
 
 #include "port.h"
@@ -160,6 +161,12 @@ struct mlxsw_sp_sb {
        } ports[MLXSW_PORT_MAX_PORTS];
 };
 
+#define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE)
+
+struct mlxsw_sp_prefix_usage {
+       DECLARE_BITMAP(b, MLXSW_SP_PREFIX_COUNT);
+};
+
 struct mlxsw_sp {
        struct {
                struct list_head list;
index 8d70496ca396d65e8180690c04f1c4a142157d93..af4a35da9d9f4e5583f05f16d635d6e914891d93 100644 (file)
 
 #include <linux/kernel.h>
 #include <linux/types.h>
+#include <linux/rhashtable.h>
+#include <linux/bitops.h>
+#include <linux/in6.h>
 
 #include "spectrum.h"
 #include "core.h"
 #include "reg.h"
 
+static void
+mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage,
+                         unsigned char prefix_len)
+{
+       set_bit(prefix_len, prefix_usage->b);
+}
+
+static void
+mlxsw_sp_prefix_usage_clear(struct mlxsw_sp_prefix_usage *prefix_usage,
+                           unsigned char prefix_len)
+{
+       clear_bit(prefix_len, prefix_usage->b);
+}
+
+struct mlxsw_sp_fib_key {
+       unsigned char addr[sizeof(struct in6_addr)];
+       unsigned char prefix_len;
+};
+
+struct mlxsw_sp_fib_entry {
+       struct rhash_head ht_node;
+       struct mlxsw_sp_fib_key key;
+};
+
+struct mlxsw_sp_fib {
+       struct rhashtable ht;
+       unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];
+       struct mlxsw_sp_prefix_usage prefix_usage;
+};
+
+static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
+       .key_offset = offsetof(struct mlxsw_sp_fib_entry, key),
+       .head_offset = offsetof(struct mlxsw_sp_fib_entry, ht_node),
+       .key_len = sizeof(struct mlxsw_sp_fib_key),
+       .automatic_shrinking = true,
+};
+
+static int mlxsw_sp_fib_entry_insert(struct mlxsw_sp_fib *fib,
+                                    struct mlxsw_sp_fib_entry *fib_entry)
+{
+       unsigned char prefix_len = fib_entry->key.prefix_len;
+       int err;
+
+       err = rhashtable_insert_fast(&fib->ht, &fib_entry->ht_node,
+                                    mlxsw_sp_fib_ht_params);
+       if (err)
+               return err;
+       if (fib->prefix_ref_count[prefix_len]++ == 0)
+               mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
+       return 0;
+}
+
+static void mlxsw_sp_fib_entry_remove(struct mlxsw_sp_fib *fib,
+                                     struct mlxsw_sp_fib_entry *fib_entry)
+{
+       unsigned char prefix_len = fib_entry->key.prefix_len;
+
+       if (--fib->prefix_ref_count[prefix_len] == 0)
+               mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
+       rhashtable_remove_fast(&fib->ht, &fib_entry->ht_node,
+                              mlxsw_sp_fib_ht_params);
+}
+
+static struct mlxsw_sp_fib_entry *
+mlxsw_sp_fib_entry_create(struct mlxsw_sp_fib *fib, const void *addr,
+                         size_t addr_len, unsigned char prefix_len)
+{
+       struct mlxsw_sp_fib_entry *fib_entry;
+
+       fib_entry = kzalloc(sizeof(*fib_entry), GFP_KERNEL);
+       if (!fib_entry)
+               return NULL;
+       memcpy(fib_entry->key.addr, addr, addr_len);
+       fib_entry->key.prefix_len = prefix_len;
+       return fib_entry;
+}
+
+static void mlxsw_sp_fib_entry_destroy(struct mlxsw_sp_fib_entry *fib_entry)
+{
+       kfree(fib_entry);
+}
+
+static struct mlxsw_sp_fib_entry *
+mlxsw_sp_fib_entry_lookup(struct mlxsw_sp_fib *fib, const void *addr,
+                         size_t addr_len, unsigned char prefix_len)
+{
+       struct mlxsw_sp_fib_key key = {{ 0 } };
+
+       memcpy(key.addr, addr, addr_len);
+       key.prefix_len = prefix_len;
+       return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
+}
+
+static struct mlxsw_sp_fib *mlxsw_sp_fib_create(void)
+{
+       struct mlxsw_sp_fib *fib;
+       int err;
+
+       fib = kzalloc(sizeof(*fib), GFP_KERNEL);
+       if (!fib)
+               return ERR_PTR(-ENOMEM);
+       err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params);
+       if (err)
+               goto err_rhashtable_init;
+       return fib;
+
+err_rhashtable_init:
+       kfree(fib);
+       return ERR_PTR(err);
+}
+
+static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib)
+{
+       rhashtable_destroy(&fib->ht);
+       kfree(fib);
+}
+
 static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
 {
        char rgcr_pl[MLXSW_REG_RGCR_LEN];