mlxsw: spectrum_router: Support FID-less RIFs
[sfrench/cifs-2.6.git] / drivers / net / ethernet / mellanox / mlxsw / spectrum_router.c
index 0b989e16a4243fde12718b8b92e577de407b8ef9..38477c5f7d4dfd42a54cda697a98168c6ecad4f1 100644 (file)
@@ -62,6 +62,7 @@
 #include "reg.h"
 #include "spectrum_cnt.h"
 #include "spectrum_dpipe.h"
+#include "spectrum_ipip.h"
 #include "spectrum_router.h"
 
 struct mlxsw_sp_vr;
@@ -89,6 +90,7 @@ struct mlxsw_sp_router {
        bool aborted;
        struct notifier_block fib_nb;
        const struct mlxsw_sp_rif_ops **rif_ops_arr;
+       const struct mlxsw_sp_ipip_ops **ipip_ops_arr;
 };
 
 struct mlxsw_sp_rif {
@@ -405,11 +407,6 @@ struct mlxsw_sp_rt6 {
        struct rt6_info *rt;
 };
 
-enum mlxsw_sp_l3proto {
-       MLXSW_SP_L3_PROTO_IPV4,
-       MLXSW_SP_L3_PROTO_IPV6,
-};
-
 struct mlxsw_sp_lpm_tree {
        u8 id; /* tree ID */
        unsigned int ref_count;
@@ -901,6 +898,8 @@ struct mlxsw_sp_neigh_entry {
                                        * this neigh entry
                                        */
        struct list_head nexthop_neighs_list_node;
+       unsigned int counter_index;
+       bool counter_valid;
 };
 
 static const struct rhashtable_params mlxsw_sp_neigh_ht_params = {
@@ -945,6 +944,26 @@ u32 mlxsw_sp_neigh4_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)
        return ntohl(*((__be32 *) n->primary_key));
 }
 
+struct in6_addr *
+mlxsw_sp_neigh6_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)
+{
+       struct neighbour *n;
+
+       n = neigh_entry->key.n;
+       return (struct in6_addr *) &n->primary_key;
+}
+
+int mlxsw_sp_neigh_counter_get(struct mlxsw_sp *mlxsw_sp,
+                              struct mlxsw_sp_neigh_entry *neigh_entry,
+                              u64 *p_counter)
+{
+       if (!neigh_entry->counter_valid)
+               return -EINVAL;
+
+       return mlxsw_sp_flow_counter_get(mlxsw_sp, neigh_entry->counter_index,
+                                        p_counter, NULL);
+}
+
 static struct mlxsw_sp_neigh_entry *
 mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n,
                           u16 rif)
@@ -985,6 +1004,53 @@ mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp,
                               mlxsw_sp_neigh_ht_params);
 }
 
+static bool
+mlxsw_sp_neigh_counter_should_alloc(struct mlxsw_sp *mlxsw_sp,
+                                   struct mlxsw_sp_neigh_entry *neigh_entry)
+{
+       struct devlink *devlink;
+       const char *table_name;
+
+       switch (mlxsw_sp_neigh_entry_type(neigh_entry)) {
+       case AF_INET:
+               table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST4;
+               break;
+       case AF_INET6:
+               table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST6;
+               break;
+       default:
+               WARN_ON(1);
+               return false;
+       }
+
+       devlink = priv_to_devlink(mlxsw_sp->core);
+       return devlink_dpipe_table_counter_enabled(devlink, table_name);
+}
+
+static void
+mlxsw_sp_neigh_counter_alloc(struct mlxsw_sp *mlxsw_sp,
+                            struct mlxsw_sp_neigh_entry *neigh_entry)
+{
+       if (!mlxsw_sp_neigh_counter_should_alloc(mlxsw_sp, neigh_entry))
+               return;
+
+       if (mlxsw_sp_flow_counter_alloc(mlxsw_sp, &neigh_entry->counter_index))
+               return;
+
+       neigh_entry->counter_valid = true;
+}
+
+static void
+mlxsw_sp_neigh_counter_free(struct mlxsw_sp *mlxsw_sp,
+                           struct mlxsw_sp_neigh_entry *neigh_entry)
+{
+       if (!neigh_entry->counter_valid)
+               return;
+       mlxsw_sp_flow_counter_free(mlxsw_sp,
+                                  neigh_entry->counter_index);
+       neigh_entry->counter_valid = false;
+}
+
 static struct mlxsw_sp_neigh_entry *
 mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
 {
@@ -1004,6 +1070,7 @@ mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
        if (err)
                goto err_neigh_entry_insert;
 
+       mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry);
        list_add(&neigh_entry->rif_list_node, &rif->neigh_list);
 
        return neigh_entry;
@@ -1018,6 +1085,7 @@ mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp *mlxsw_sp,
                             struct mlxsw_sp_neigh_entry *neigh_entry)
 {
        list_del(&neigh_entry->rif_list_node);
+       mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry);
        mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
        mlxsw_sp_neigh_entry_free(neigh_entry);
 }
@@ -1323,6 +1391,9 @@ mlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp,
 
        mlxsw_reg_rauht_pack4(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
                              dip);
+       if (neigh_entry->counter_valid)
+               mlxsw_reg_rauht_pack_counter(rauht_pl,
+                                            neigh_entry->counter_index);
        mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
 }
 
@@ -1337,11 +1408,16 @@ mlxsw_sp_router_neigh_entry_op6(struct mlxsw_sp *mlxsw_sp,
 
        mlxsw_reg_rauht_pack6(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
                              dip);
+       if (neigh_entry->counter_valid)
+               mlxsw_reg_rauht_pack_counter(rauht_pl,
+                                            neigh_entry->counter_index);
        mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
 }
 
-static bool mlxsw_sp_neigh_ipv6_ignore(struct neighbour *n)
+bool mlxsw_sp_neigh_ipv6_ignore(struct mlxsw_sp_neigh_entry *neigh_entry)
 {
+       struct neighbour *n = neigh_entry->key.n;
+
        /* Packets with a link-local destination address are trapped
         * after LPM lookup and never reach the neighbour table, so
         * there is no need to program such neighbours to the device.
@@ -1364,7 +1440,7 @@ mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp,
                mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry,
                                                mlxsw_sp_rauht_op(adding));
        } else if (neigh_entry->key.n->tbl->family == AF_INET6) {
-               if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry->key.n))
+               if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry))
                        return;
                mlxsw_sp_router_neigh_entry_op6(mlxsw_sp, neigh_entry,
                                                mlxsw_sp_rauht_op(adding));
@@ -1373,6 +1449,18 @@ mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp,
        }
 }
 
+void
+mlxsw_sp_neigh_entry_counter_update(struct mlxsw_sp *mlxsw_sp,
+                                   struct mlxsw_sp_neigh_entry *neigh_entry,
+                                   bool adding)
+{
+       if (adding)
+               mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry);
+       else
+               mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry);
+       mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, true);
+}
+
 struct mlxsw_sp_neigh_event_work {
        struct work_struct work;
        struct mlxsw_sp *mlxsw_sp;
@@ -1837,7 +1925,8 @@ static int mlxsw_sp_nexthop_mac_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
        char ratr_pl[MLXSW_REG_RATR_LEN];
 
        mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY,
-                           true, adj_index, neigh_entry->rif);
+                           true, MLXSW_REG_RATR_TYPE_ETHERNET,
+                           adj_index, neigh_entry->rif);
        mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha);
        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
 }
@@ -3568,7 +3657,7 @@ static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp_fib_entry *fib_entry,
         * local, which will cause them to be trapped with a lower
         * priority than packets that need to be locally received.
         */
-       if (rt->rt6i_flags & RTF_LOCAL)
+       if (rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST))
                fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
        else if (rt->rt6i_flags & RTF_REJECT)
                fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
@@ -3902,9 +3991,6 @@ static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp,
                char raltb_pl[MLXSW_REG_RALTB_LEN];
                char ralue_pl[MLXSW_REG_RALUE_LEN];
 
-               if (!mlxsw_sp_vr_is_used(vr))
-                       continue;
-
                mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id);
                err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb),
                                      raltb_pl);
@@ -4358,9 +4444,9 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
 {
        u32 tb_id = l3mdev_fib_table(params->dev);
        const struct mlxsw_sp_rif_ops *ops;
+       struct mlxsw_sp_fid *fid = NULL;
        enum mlxsw_sp_rif_type type;
        struct mlxsw_sp_rif *rif;
-       struct mlxsw_sp_fid *fid;
        struct mlxsw_sp_vr *vr;
        u16 rif_index;
        int err;
@@ -4384,12 +4470,14 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
        rif->mlxsw_sp = mlxsw_sp;
        rif->ops = ops;
 
-       fid = ops->fid_get(rif);
-       if (IS_ERR(fid)) {
-               err = PTR_ERR(fid);
-               goto err_fid_get;
+       if (ops->fid_get) {
+               fid = ops->fid_get(rif);
+               if (IS_ERR(fid)) {
+                       err = PTR_ERR(fid);
+                       goto err_fid_get;
+               }
+               rif->fid = fid;
        }
-       rif->fid = fid;
 
        if (ops->setup)
                ops->setup(rif, params);
@@ -4398,22 +4486,15 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
        if (err)
                goto err_configure;
 
-       err = mlxsw_sp_rif_fdb_op(mlxsw_sp, params->dev->dev_addr,
-                                 mlxsw_sp_fid_index(fid), true);
-       if (err)
-               goto err_rif_fdb_op;
-
        mlxsw_sp_rif_counters_alloc(rif);
-       mlxsw_sp_fid_rif_set(fid, rif);
        mlxsw_sp->router->rifs[rif_index] = rif;
        vr->rif_count++;
 
        return rif;
 
-err_rif_fdb_op:
-       ops->deconfigure(rif);
 err_configure:
-       mlxsw_sp_fid_put(fid);
+       if (fid)
+               mlxsw_sp_fid_put(fid);
 err_fid_get:
        kfree(rif);
 err_rif_alloc:
@@ -4434,12 +4515,11 @@ void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
 
        vr->rif_count--;
        mlxsw_sp->router->rifs[rif->rif_index] = NULL;
-       mlxsw_sp_fid_rif_set(fid, NULL);
        mlxsw_sp_rif_counters_free(rif);
-       mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->dev->dev_addr,
-                           mlxsw_sp_fid_index(fid), false);
        ops->deconfigure(rif);
-       mlxsw_sp_fid_put(fid);
+       if (fid)
+               /* Loopback RIFs are not associated with a FID. */
+               mlxsw_sp_fid_put(fid);
        kfree(rif);
        mlxsw_sp_vr_put(vr);
 }
@@ -4867,8 +4947,8 @@ static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable)
 
        rif_subport = mlxsw_sp_rif_subport_rif(rif);
        mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF,
-                           rif->rif_index, rif->vr_id, rif->dev->mtu,
-                           rif->dev->dev_addr);
+                           rif->rif_index, rif->vr_id, rif->dev->mtu);
+       mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr);
        mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag,
                                  rif_subport->lag ? rif_subport->lag_id :
                                                     rif_subport->system_port,
@@ -4879,11 +4959,32 @@ static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable)
 
 static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif)
 {
-       return mlxsw_sp_rif_subport_op(rif, true);
+       int err;
+
+       err = mlxsw_sp_rif_subport_op(rif, true);
+       if (err)
+               return err;
+
+       err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
+                                 mlxsw_sp_fid_index(rif->fid), true);
+       if (err)
+               goto err_rif_fdb_op;
+
+       mlxsw_sp_fid_rif_set(rif->fid, rif);
+       return 0;
+
+err_rif_fdb_op:
+       mlxsw_sp_rif_subport_op(rif, false);
+       return err;
 }
 
 static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)
 {
+       struct mlxsw_sp_fid *fid = rif->fid;
+
+       mlxsw_sp_fid_rif_set(fid, NULL);
+       mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
+                           mlxsw_sp_fid_index(fid), false);
        mlxsw_sp_rif_subport_op(rif, false);
 }
 
@@ -4910,7 +5011,8 @@ static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif,
        char ritr_pl[MLXSW_REG_RITR_LEN];
 
        mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id,
-                           rif->dev->mtu, rif->dev->dev_addr);
+                           rif->dev->mtu);
+       mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr);
        mlxsw_reg_ritr_fid_set(ritr_pl, type, vid_fid);
 
        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
@@ -4941,8 +5043,17 @@ static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif)
        if (err)
                goto err_fid_bc_flood_set;
 
+       err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
+                                 mlxsw_sp_fid_index(rif->fid), true);
+       if (err)
+               goto err_rif_fdb_op;
+
+       mlxsw_sp_fid_rif_set(rif->fid, rif);
        return 0;
 
+err_rif_fdb_op:
+       mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
+                              mlxsw_sp_router_port(mlxsw_sp), false);
 err_fid_bc_flood_set:
        mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
                               mlxsw_sp_router_port(mlxsw_sp), false);
@@ -4953,9 +5064,13 @@ err_fid_mc_flood_set:
 
 static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif)
 {
-       struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
        u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
+       struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
+       struct mlxsw_sp_fid *fid = rif->fid;
 
+       mlxsw_sp_fid_rif_set(fid, NULL);
+       mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
+                           mlxsw_sp_fid_index(fid), false);
        mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
                               mlxsw_sp_router_port(mlxsw_sp), false);
        mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
@@ -5000,8 +5115,17 @@ static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif)
        if (err)
                goto err_fid_bc_flood_set;
 
+       err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
+                                 mlxsw_sp_fid_index(rif->fid), true);
+       if (err)
+               goto err_rif_fdb_op;
+
+       mlxsw_sp_fid_rif_set(rif->fid, rif);
        return 0;
 
+err_rif_fdb_op:
+       mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
+                              mlxsw_sp_router_port(mlxsw_sp), false);
 err_fid_bc_flood_set:
        mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
                               mlxsw_sp_router_port(mlxsw_sp), false);
@@ -5012,9 +5136,13 @@ err_fid_mc_flood_set:
 
 static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
 {
-       struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
        u16 fid_index = mlxsw_sp_fid_index(rif->fid);
+       struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
+       struct mlxsw_sp_fid *fid = rif->fid;
 
+       mlxsw_sp_fid_rif_set(fid, NULL);
+       mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
+                           mlxsw_sp_fid_index(fid), false);
        mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
                               mlxsw_sp_router_port(mlxsw_sp), false);
        mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
@@ -5067,6 +5195,16 @@ static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp)
        kfree(mlxsw_sp->router->rifs);
 }
 
+static int mlxsw_sp_ipips_init(struct mlxsw_sp *mlxsw_sp)
+{
+       mlxsw_sp->router->ipip_ops_arr = mlxsw_sp_ipip_ops_arr;
+       return 0;
+}
+
+static void mlxsw_sp_ipips_fini(struct mlxsw_sp *mlxsw_sp)
+{
+}
+
 static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
 {
        struct mlxsw_sp_router *router;
@@ -5126,6 +5264,10 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
        if (err)
                goto err_rifs_init;
 
+       err = mlxsw_sp_ipips_init(mlxsw_sp);
+       if (err)
+               goto err_ipips_init;
+
        err = rhashtable_init(&mlxsw_sp->router->nexthop_ht,
                              &mlxsw_sp_nexthop_ht_params);
        if (err)
@@ -5167,6 +5309,8 @@ err_lpm_init:
 err_nexthop_group_ht_init:
        rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
 err_nexthop_ht_init:
+       mlxsw_sp_ipips_fini(mlxsw_sp);
+err_ipips_init:
        mlxsw_sp_rifs_fini(mlxsw_sp);
 err_rifs_init:
        __mlxsw_sp_router_fini(mlxsw_sp);
@@ -5183,6 +5327,7 @@ void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
        mlxsw_sp_lpm_fini(mlxsw_sp);
        rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
        rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
+       mlxsw_sp_ipips_fini(mlxsw_sp);
        mlxsw_sp_rifs_fini(mlxsw_sp);
        __mlxsw_sp_router_fini(mlxsw_sp);
        kfree(mlxsw_sp->router);