]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
Merge tag 'arm64-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64...
[karo-tx-linux.git] / drivers / net / ethernet / mellanox / mlxsw / spectrum_router.c
index fe4a55e3272befaed619814b090c3a0a298ef116..33cec1cc164259ad9d7dd022a811e957447a61ff 100644 (file)
@@ -52,6 +52,9 @@
 #include "spectrum.h"
 #include "core.h"
 #include "reg.h"
+#include "spectrum_cnt.h"
+#include "spectrum_dpipe.h"
+#include "spectrum_router.h"
 
 struct mlxsw_sp_rif {
        struct list_head nexthop_list;
@@ -62,8 +65,157 @@ struct mlxsw_sp_rif {
        int mtu;
        u16 rif_index;
        u16 vr_id;
+       unsigned int counter_ingress;
+       bool counter_ingress_valid;
+       unsigned int counter_egress;
+       bool counter_egress_valid;
 };
 
+static unsigned int *
+mlxsw_sp_rif_p_counter_get(struct mlxsw_sp_rif *rif,
+                          enum mlxsw_sp_rif_counter_dir dir)
+{
+       switch (dir) {
+       case MLXSW_SP_RIF_COUNTER_EGRESS:
+               return &rif->counter_egress;
+       case MLXSW_SP_RIF_COUNTER_INGRESS:
+               return &rif->counter_ingress;
+       }
+       return NULL;
+}
+
+static bool
+mlxsw_sp_rif_counter_valid_get(struct mlxsw_sp_rif *rif,
+                              enum mlxsw_sp_rif_counter_dir dir)
+{
+       switch (dir) {
+       case MLXSW_SP_RIF_COUNTER_EGRESS:
+               return rif->counter_egress_valid;
+       case MLXSW_SP_RIF_COUNTER_INGRESS:
+               return rif->counter_ingress_valid;
+       }
+       return false;
+}
+
+static void
+mlxsw_sp_rif_counter_valid_set(struct mlxsw_sp_rif *rif,
+                              enum mlxsw_sp_rif_counter_dir dir,
+                              bool valid)
+{
+       switch (dir) {
+       case MLXSW_SP_RIF_COUNTER_EGRESS:
+               rif->counter_egress_valid = valid;
+               break;
+       case MLXSW_SP_RIF_COUNTER_INGRESS:
+               rif->counter_ingress_valid = valid;
+               break;
+       }
+}
+
+static int mlxsw_sp_rif_counter_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
+                                    unsigned int counter_index, bool enable,
+                                    enum mlxsw_sp_rif_counter_dir dir)
+{
+       char ritr_pl[MLXSW_REG_RITR_LEN];
+       bool is_egress = false;
+       int err;
+
+       if (dir == MLXSW_SP_RIF_COUNTER_EGRESS)
+               is_egress = true;
+       mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
+       err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
+       if (err)
+               return err;
+
+       mlxsw_reg_ritr_counter_pack(ritr_pl, counter_index, enable,
+                                   is_egress);
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
+}
+
+int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp,
+                                  struct mlxsw_sp_rif *rif,
+                                  enum mlxsw_sp_rif_counter_dir dir, u64 *cnt)
+{
+       char ricnt_pl[MLXSW_REG_RICNT_LEN];
+       unsigned int *p_counter_index;
+       bool valid;
+       int err;
+
+       valid = mlxsw_sp_rif_counter_valid_get(rif, dir);
+       if (!valid)
+               return -EINVAL;
+
+       p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
+       if (!p_counter_index)
+               return -EINVAL;
+       mlxsw_reg_ricnt_pack(ricnt_pl, *p_counter_index,
+                            MLXSW_REG_RICNT_OPCODE_NOP);
+       err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
+       if (err)
+               return err;
+       *cnt = mlxsw_reg_ricnt_good_unicast_packets_get(ricnt_pl);
+       return 0;
+}
+
+static int mlxsw_sp_rif_counter_clear(struct mlxsw_sp *mlxsw_sp,
+                                     unsigned int counter_index)
+{
+       char ricnt_pl[MLXSW_REG_RICNT_LEN];
+
+       mlxsw_reg_ricnt_pack(ricnt_pl, counter_index,
+                            MLXSW_REG_RICNT_OPCODE_CLEAR);
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
+}
+
+int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp,
+                              struct mlxsw_sp_rif *rif,
+                              enum mlxsw_sp_rif_counter_dir dir)
+{
+       unsigned int *p_counter_index;
+       int err;
+
+       p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
+       if (!p_counter_index)
+               return -EINVAL;
+       err = mlxsw_sp_counter_alloc(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
+                                    p_counter_index);
+       if (err)
+               return err;
+
+       err = mlxsw_sp_rif_counter_clear(mlxsw_sp, *p_counter_index);
+       if (err)
+               goto err_counter_clear;
+
+       err = mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
+                                       *p_counter_index, true, dir);
+       if (err)
+               goto err_counter_edit;
+       mlxsw_sp_rif_counter_valid_set(rif, dir, true);
+       return 0;
+
+err_counter_edit:
+err_counter_clear:
+       mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
+                             *p_counter_index);
+       return err;
+}
+
+void mlxsw_sp_rif_counter_free(struct mlxsw_sp *mlxsw_sp,
+                              struct mlxsw_sp_rif *rif,
+                              enum mlxsw_sp_rif_counter_dir dir)
+{
+       unsigned int *p_counter_index;
+
+       p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
+       if (WARN_ON(!p_counter_index))
+               return;
+       mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
+                                 *p_counter_index, false, dir);
+       mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
+                             *p_counter_index);
+       mlxsw_sp_rif_counter_valid_set(rif, dir, false);
+}
+
 static struct mlxsw_sp_rif *
 mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
                         const struct net_device *dev);
@@ -206,8 +358,8 @@ mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp)
        static struct mlxsw_sp_lpm_tree *lpm_tree;
        int i;
 
-       for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
-               lpm_tree = &mlxsw_sp->router.lpm_trees[i];
+       for (i = 0; i < mlxsw_sp->router.lpm.tree_count; i++) {
+               lpm_tree = &mlxsw_sp->router.lpm.trees[i];
                if (lpm_tree->ref_count == 0)
                        return lpm_tree;
        }
@@ -303,8 +455,8 @@ mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
        struct mlxsw_sp_lpm_tree *lpm_tree;
        int i;
 
-       for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
-               lpm_tree = &mlxsw_sp->router.lpm_trees[i];
+       for (i = 0; i < mlxsw_sp->router.lpm.tree_count; i++) {
+               lpm_tree = &mlxsw_sp->router.lpm.trees[i];
                if (lpm_tree->ref_count != 0 &&
                    lpm_tree->proto == proto &&
                    mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage,
@@ -329,15 +481,36 @@ static int mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
        return 0;
 }
 
-static void mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
+#define MLXSW_SP_LPM_TREE_MIN 2 /* trees 0 and 1 are reserved */
+
+static int mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
 {
        struct mlxsw_sp_lpm_tree *lpm_tree;
+       u64 max_trees;
        int i;
 
-       for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
-               lpm_tree = &mlxsw_sp->router.lpm_trees[i];
+       if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LPM_TREES))
+               return -EIO;
+
+       max_trees = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_LPM_TREES);
+       mlxsw_sp->router.lpm.tree_count = max_trees - MLXSW_SP_LPM_TREE_MIN;
+       mlxsw_sp->router.lpm.trees = kcalloc(mlxsw_sp->router.lpm.tree_count,
+                                            sizeof(struct mlxsw_sp_lpm_tree),
+                                            GFP_KERNEL);
+       if (!mlxsw_sp->router.lpm.trees)
+               return -ENOMEM;
+
+       for (i = 0; i < mlxsw_sp->router.lpm.tree_count; i++) {
+               lpm_tree = &mlxsw_sp->router.lpm.trees[i];
                lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN;
        }
+
+       return 0;
+}
+
+static void mlxsw_sp_lpm_fini(struct mlxsw_sp *mlxsw_sp)
+{
+       kfree(mlxsw_sp->router.lpm.trees);
 }
 
 static bool mlxsw_sp_vr_is_used(const struct mlxsw_sp_vr *vr)
@@ -1253,7 +1426,6 @@ mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
        bool old_adj_index_valid;
        u32 old_adj_index;
        u16 old_ecmp_size;
-       int ret;
        int i;
        int err;
 
@@ -1291,15 +1463,14 @@ mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
                 */
                goto set_trap;
 
-       ret = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size);
-       if (ret < 0) {
+       err = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size, &adj_index);
+       if (err) {
                /* We ran out of KVD linear space, just set the
                 * trap and let everything flow through kernel.
                 */
                dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n");
                goto set_trap;
        }
-       adj_index = ret;
        old_adj_index_valid = nh_grp->adj_index_valid;
        old_adj_index = nh_grp->adj_index;
        old_ecmp_size = nh_grp->ecmp_size;
@@ -2761,6 +2932,16 @@ mlxsw_sp_rif_alloc(u16 rif_index, u16 vr_id, struct net_device *l3_dev,
        return rif;
 }
 
+u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif)
+{
+       return rif->rif_index;
+}
+
+int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif)
+{
+       return rif->dev->ifindex;
+}
+
 static struct mlxsw_sp_rif *
 mlxsw_sp_vport_rif_sp_create(struct mlxsw_sp_port *mlxsw_sp_vport,
                             struct net_device *l3_dev)
@@ -2803,6 +2984,15 @@ mlxsw_sp_vport_rif_sp_create(struct mlxsw_sp_port *mlxsw_sp_vport,
                goto err_rif_alloc;
        }
 
+       if (devlink_dpipe_table_counter_enabled(priv_to_devlink(mlxsw_sp->core),
+                                               MLXSW_SP_DPIPE_TABLE_NAME_ERIF)) {
+               err = mlxsw_sp_rif_counter_alloc(mlxsw_sp, rif,
+                                                MLXSW_SP_RIF_COUNTER_EGRESS);
+               if (err)
+                       netdev_dbg(mlxsw_sp_vport->dev,
+                                  "Counter alloc Failed err=%d\n", err);
+       }
+
        f->rif = rif;
        mlxsw_sp->rifs[rif_index] = rif;
        vr->rif_count++;
@@ -2833,6 +3023,9 @@ static void mlxsw_sp_vport_rif_sp_destroy(struct mlxsw_sp_port *mlxsw_sp_vport,
 
        mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
 
+       mlxsw_sp_rif_counter_free(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
+       mlxsw_sp_rif_counter_free(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_INGRESS);
+
        vr->rif_count--;
        mlxsw_sp->rifs[rif_index] = NULL;
        f->rif = NULL;
@@ -2905,7 +3098,9 @@ static int mlxsw_sp_inetaddr_vport_event(struct net_device *l3_dev,
 static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev,
                                        unsigned long event)
 {
-       if (netif_is_bridge_port(port_dev) || netif_is_lag_port(port_dev))
+       if (netif_is_bridge_port(port_dev) ||
+           netif_is_lag_port(port_dev) ||
+           netif_is_ovs_port(port_dev))
                return 0;
 
        return mlxsw_sp_inetaddr_vport_event(port_dev, port_dev, event, 1);
@@ -2955,6 +3150,11 @@ static struct mlxsw_sp_fid *mlxsw_sp_bridge_fid_get(struct mlxsw_sp *mlxsw_sp,
        return mlxsw_sp_fid_find(mlxsw_sp, fid);
 }
 
+static u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp)
+{
+       return mlxsw_core_max_ports(mlxsw_sp->core) + 1;
+}
+
 static enum mlxsw_flood_table_type mlxsw_sp_flood_table_type_get(u16 fid)
 {
        return mlxsw_sp_fid_is_vfid(fid) ? MLXSW_REG_SFGC_TABLE_TYPE_FID :
@@ -2969,6 +3169,7 @@ static u16 mlxsw_sp_flood_table_index_get(u16 fid)
 static int mlxsw_sp_router_port_flood_set(struct mlxsw_sp *mlxsw_sp, u16 fid,
                                          bool set)
 {
+       u8 router_port = mlxsw_sp_router_port(mlxsw_sp);
        enum mlxsw_flood_table_type table_type;
        char *sftr_pl;
        u16 index;
@@ -2981,7 +3182,7 @@ static int mlxsw_sp_router_port_flood_set(struct mlxsw_sp *mlxsw_sp, u16 fid,
        table_type = mlxsw_sp_flood_table_type_get(fid);
        index = mlxsw_sp_flood_table_index_get(fid);
        mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BC, index, table_type,
-                           1, MLXSW_PORT_ROUTER_PORT, set);
+                           1, router_port, set);
        err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl);
 
        kfree(sftr_pl);
@@ -3144,6 +3345,21 @@ static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
        return 0;
 }
 
+static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
+                                    unsigned long event)
+{
+       if (mlxsw_sp_port_dev_check(dev))
+               return mlxsw_sp_inetaddr_port_event(dev, event);
+       else if (netif_is_lag_master(dev))
+               return mlxsw_sp_inetaddr_lag_event(dev, event);
+       else if (netif_is_bridge_master(dev))
+               return mlxsw_sp_inetaddr_bridge_event(dev, dev, event);
+       else if (is_vlan_dev(dev))
+               return mlxsw_sp_inetaddr_vlan_event(dev, event);
+       else
+               return 0;
+}
+
 int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
                            unsigned long event, void *ptr)
 {
@@ -3161,15 +3377,7 @@ int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
        if (!mlxsw_sp_rif_should_config(rif, ifa->ifa_dev, event))
                goto out;
 
-       if (mlxsw_sp_port_dev_check(dev))
-               err = mlxsw_sp_inetaddr_port_event(dev, event);
-       else if (netif_is_lag_master(dev))
-               err = mlxsw_sp_inetaddr_lag_event(dev, event);
-       else if (netif_is_bridge_master(dev))
-               err = mlxsw_sp_inetaddr_bridge_event(dev, dev, event);
-       else if (is_vlan_dev(dev))
-               err = mlxsw_sp_inetaddr_vlan_event(dev, event);
-
+       err = __mlxsw_sp_inetaddr_event(dev, event);
 out:
        return notifier_from_errno(err);
 }
@@ -3232,71 +3440,53 @@ err_rif_edit:
        return err;
 }
 
-int mlxsw_sp_vport_vrf_join(struct mlxsw_sp_port *mlxsw_sp_vport)
+static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
+                                 struct net_device *l3_dev)
 {
-       struct mlxsw_sp_fid *f = mlxsw_sp_vport_fid_get(mlxsw_sp_vport);
-       struct net_device *dev = mlxsw_sp_vport->dev;
+       struct mlxsw_sp_rif *rif;
 
-       /* In case vPort already has a RIF, then we need to drop it.
-        * A new one will be created using the VRF's VR.
+       /* If netdev is already associated with a RIF, then we need to
+        * destroy it and create a new one with the new virtual router ID.
         */
-       if (f && f->rif)
-               mlxsw_sp_vport_rif_sp_leave(mlxsw_sp_vport);
-
-       return mlxsw_sp_vport_rif_sp_join(mlxsw_sp_vport, dev);
-}
-
-void mlxsw_sp_vport_vrf_leave(struct mlxsw_sp_port *mlxsw_sp_vport)
-{
-       mlxsw_sp_vport_rif_sp_leave(mlxsw_sp_vport);
-}
-
-int mlxsw_sp_port_vrf_join(struct mlxsw_sp_port *mlxsw_sp_port)
-{
-       struct mlxsw_sp_port *mlxsw_sp_vport;
-
-       mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, 1);
-       if (WARN_ON(!mlxsw_sp_vport))
-               return -EINVAL;
+       rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
+       if (rif)
+               __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
 
-       return mlxsw_sp_vport_vrf_join(mlxsw_sp_vport);
+       return __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_UP);
 }
 
-void mlxsw_sp_port_vrf_leave(struct mlxsw_sp_port *mlxsw_sp_port)
+static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
+                                   struct net_device *l3_dev)
 {
-       struct mlxsw_sp_port *mlxsw_sp_vport;
+       struct mlxsw_sp_rif *rif;
 
-       mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, 1);
-       if (WARN_ON(!mlxsw_sp_vport))
+       rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
+       if (!rif)
                return;
-
-       mlxsw_sp_vport_vrf_leave(mlxsw_sp_vport);
+       __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
 }
 
-int mlxsw_sp_bridge_vrf_join(struct mlxsw_sp *mlxsw_sp,
-                            struct net_device *l3_dev)
+int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
+                                struct netdev_notifier_changeupper_info *info)
 {
-       struct mlxsw_sp_fid *f;
-
-       f = mlxsw_sp_bridge_fid_get(mlxsw_sp, l3_dev);
-       if (WARN_ON(!f))
-               return -EINVAL;
-
-       if (f->rif)
-               mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->rif);
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
+       int err = 0;
 
-       return mlxsw_sp_rif_bridge_create(mlxsw_sp, l3_dev, f);
-}
+       if (!mlxsw_sp)
+               return 0;
 
-void mlxsw_sp_bridge_vrf_leave(struct mlxsw_sp *mlxsw_sp,
-                              struct net_device *l3_dev)
-{
-       struct mlxsw_sp_fid *f;
+       switch (event) {
+       case NETDEV_PRECHANGEUPPER:
+               return 0;
+       case NETDEV_CHANGEUPPER:
+               if (info->linking)
+                       err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev);
+               else
+                       mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev);
+               break;
+       }
 
-       f = mlxsw_sp_bridge_fid_get(mlxsw_sp, l3_dev);
-       if (WARN_ON(!f))
-               return;
-       mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->rif);
+       return err;
 }
 
 static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
@@ -3372,7 +3562,10 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
        if (err)
                goto err_nexthop_group_ht_init;
 
-       mlxsw_sp_lpm_init(mlxsw_sp);
+       err = mlxsw_sp_lpm_init(mlxsw_sp);
+       if (err)
+               goto err_lpm_init;
+
        err = mlxsw_sp_vrs_init(mlxsw_sp);
        if (err)
                goto err_vrs_init;
@@ -3394,6 +3587,8 @@ err_register_fib_notifier:
 err_neigh_init:
        mlxsw_sp_vrs_fini(mlxsw_sp);
 err_vrs_init:
+       mlxsw_sp_lpm_fini(mlxsw_sp);
+err_lpm_init:
        rhashtable_destroy(&mlxsw_sp->router.nexthop_group_ht);
 err_nexthop_group_ht_init:
        rhashtable_destroy(&mlxsw_sp->router.nexthop_ht);
@@ -3407,6 +3602,7 @@ void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
        unregister_fib_notifier(&mlxsw_sp->fib_nb);
        mlxsw_sp_neigh_fini(mlxsw_sp);
        mlxsw_sp_vrs_fini(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_router_fini(mlxsw_sp);