mlxsw: Add an initial PGT table support
authorAmit Cohen <amcohen@nvidia.com>
Mon, 27 Jun 2022 07:06:15 +0000 (10:06 +0300)
committerPaolo Abeni <pabeni@redhat.com>
Tue, 28 Jun 2022 12:31:36 +0000 (14:31 +0200)
The PGT (Port Group Table) table maps an index to a bitmap of local ports
to which a packet needs to be replicated. This table is used for layer 2
multicast and flooding.

In the legacy model, software did not interact with this table directly.
Instead, it was accessed by firmware in response to registers such as
SFTR and SMID. In the new model, the SFTR register is deprecated and
software has full control over the PGT table using the SMID register.

The entire state of the PGT table needs to be maintained in software
because member ports in a PGT entry needs to be reference counted to avoid
releasing entries which are still in use.

Add the following APIs:
1. mlxsw_sp_pgt_{init, fini}() - allocate/free the PGT table.
2. mlxsw_sp_pgt_mid_alloc_range() - allocate a range of MID indexes in PGT.
   To be used by FID code during initialization to reserve specific PGT
   indexes for flooding entries.
3. mlxsw_sp_pgt_mid_free_range() - free indexes in a given range.
4. mlxsw_sp_pgt_mid_alloc() - allocate one MID index in the PGT at a
   non-specific range, just search for free index. To be used by MDB code.
5. mlxsw_sp_pgt_mid_free() - free the given index.

Note that alloc() functions do not allocate the entries in software, just
allocate IDs using 'idr'.

Signed-off-by: Amit Cohen <amcohen@nvidia.com>
Reviewed-by: Petr Machata <petrm@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
drivers/net/ethernet/mellanox/mlxsw/Makefile
drivers/net/ethernet/mellanox/mlxsw/resources.h
drivers/net/ethernet/mellanox/mlxsw/spectrum.h
drivers/net/ethernet/mellanox/mlxsw/spectrum_pgt.c [new file with mode: 0644]

index c57e293..c2d6d64 100644 (file)
@@ -28,7 +28,8 @@ mlxsw_spectrum-objs           := spectrum.o spectrum_buffers.o \
                                   spectrum_qdisc.o spectrum_span.o \
                                   spectrum_nve.o spectrum_nve_vxlan.o \
                                   spectrum_dpipe.o spectrum_trap.o \
-                                  spectrum_ethtool.o spectrum_policer.o
+                                  spectrum_ethtool.o spectrum_policer.o \
+                                  spectrum_pgt.o
 mlxsw_spectrum-$(CONFIG_MLXSW_SPECTRUM_DCB)    += spectrum_dcb.o
 mlxsw_spectrum-$(CONFIG_PTP_1588_CLOCK)                += spectrum_ptp.o
 obj-$(CONFIG_MLXSW_MINIMAL)    += mlxsw_minimal.o
index daacf62..826e47f 100644 (file)
@@ -11,6 +11,7 @@ enum mlxsw_res_id {
        MLXSW_RES_ID_KVD_SIZE,
        MLXSW_RES_ID_KVD_SINGLE_MIN_SIZE,
        MLXSW_RES_ID_KVD_DOUBLE_MIN_SIZE,
+       MLXSW_RES_ID_PGT_SIZE,
        MLXSW_RES_ID_MAX_KVD_LINEAR_RANGE,
        MLXSW_RES_ID_MAX_KVD_ACTION_SETS,
        MLXSW_RES_ID_MAX_TRAP_GROUPS,
@@ -69,6 +70,7 @@ static u16 mlxsw_res_ids[] = {
        [MLXSW_RES_ID_KVD_SIZE] = 0x1001,
        [MLXSW_RES_ID_KVD_SINGLE_MIN_SIZE] = 0x1002,
        [MLXSW_RES_ID_KVD_DOUBLE_MIN_SIZE] = 0x1003,
+       [MLXSW_RES_ID_PGT_SIZE] = 0x1004,
        [MLXSW_RES_ID_MAX_KVD_LINEAR_RANGE] = 0x1005,
        [MLXSW_RES_ID_MAX_KVD_ACTION_SETS] = 0x1007,
        [MLXSW_RES_ID_MAX_TRAP_GROUPS] = 0x2201,
index 828d5a2..b42b23d 100644 (file)
@@ -143,6 +143,7 @@ struct mlxsw_sp_ptp_ops;
 struct mlxsw_sp_span_ops;
 struct mlxsw_sp_qdisc_state;
 struct mlxsw_sp_mall_entry;
+struct mlxsw_sp_pgt;
 
 struct mlxsw_sp_port_mapping {
        u8 module;
@@ -217,6 +218,7 @@ struct mlxsw_sp {
        struct rhashtable ipv6_addr_ht;
        struct mutex ipv6_addr_ht_lock; /* Protects ipv6_addr_ht */
        bool ubridge;
+       struct mlxsw_sp_pgt *pgt;
 };
 
 struct mlxsw_sp_ptp_ops {
@@ -1448,4 +1450,14 @@ int mlxsw_sp_policers_init(struct mlxsw_sp *mlxsw_sp);
 void mlxsw_sp_policers_fini(struct mlxsw_sp *mlxsw_sp);
 int mlxsw_sp_policer_resources_register(struct mlxsw_core *mlxsw_core);
 
+/* spectrum_pgt.c */
+int mlxsw_sp_pgt_mid_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_mid);
+void mlxsw_sp_pgt_mid_free(struct mlxsw_sp *mlxsw_sp, u16 mid_base);
+int mlxsw_sp_pgt_mid_alloc_range(struct mlxsw_sp *mlxsw_sp, u16 mid_base,
+                                u16 count);
+void mlxsw_sp_pgt_mid_free_range(struct mlxsw_sp *mlxsw_sp, u16 mid_base,
+                                u16 count);
+int mlxsw_sp_pgt_init(struct mlxsw_sp *mlxsw_sp);
+void mlxsw_sp_pgt_fini(struct mlxsw_sp *mlxsw_sp);
+
 #endif
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_pgt.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_pgt.c
new file mode 100644 (file)
index 0000000..27db277
--- /dev/null
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
+
+#include <linux/refcount.h>
+#include <linux/idr.h>
+
+#include "spectrum.h"
+#include "reg.h"
+
+struct mlxsw_sp_pgt {
+       struct idr pgt_idr;
+       u16 end_index; /* Exclusive. */
+       struct mutex lock; /* Protects PGT. */
+};
+
+int mlxsw_sp_pgt_mid_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_mid)
+{
+       int index, err = 0;
+
+       mutex_lock(&mlxsw_sp->pgt->lock);
+       index = idr_alloc(&mlxsw_sp->pgt->pgt_idr, NULL, 0,
+                         mlxsw_sp->pgt->end_index, GFP_KERNEL);
+
+       if (index < 0) {
+               err = index;
+               goto err_idr_alloc;
+       }
+
+       *p_mid = index;
+       mutex_unlock(&mlxsw_sp->pgt->lock);
+       return 0;
+
+err_idr_alloc:
+       mutex_unlock(&mlxsw_sp->pgt->lock);
+       return err;
+}
+
+void mlxsw_sp_pgt_mid_free(struct mlxsw_sp *mlxsw_sp, u16 mid_base)
+{
+       mutex_lock(&mlxsw_sp->pgt->lock);
+       WARN_ON(idr_remove(&mlxsw_sp->pgt->pgt_idr, mid_base));
+       mutex_unlock(&mlxsw_sp->pgt->lock);
+}
+
+int
+mlxsw_sp_pgt_mid_alloc_range(struct mlxsw_sp *mlxsw_sp, u16 mid_base, u16 count)
+{
+       unsigned int idr_cursor;
+       int i, err;
+
+       mutex_lock(&mlxsw_sp->pgt->lock);
+
+       /* This function is supposed to be called several times as part of
+        * driver init, in specific order. Verify that the mid_index is the
+        * first free index in the idr, to be able to free the indexes in case
+        * of error.
+        */
+       idr_cursor = idr_get_cursor(&mlxsw_sp->pgt->pgt_idr);
+       if (WARN_ON(idr_cursor != mid_base)) {
+               err = -EINVAL;
+               goto err_idr_cursor;
+       }
+
+       for (i = 0; i < count; i++) {
+               err = idr_alloc_cyclic(&mlxsw_sp->pgt->pgt_idr, NULL,
+                                      mid_base, mid_base + count, GFP_KERNEL);
+               if (err < 0)
+                       goto err_idr_alloc_cyclic;
+       }
+
+       mutex_unlock(&mlxsw_sp->pgt->lock);
+       return 0;
+
+err_idr_alloc_cyclic:
+       for (i--; i >= 0; i--)
+               idr_remove(&mlxsw_sp->pgt->pgt_idr, mid_base + i);
+err_idr_cursor:
+       mutex_unlock(&mlxsw_sp->pgt->lock);
+       return err;
+}
+
+void
+mlxsw_sp_pgt_mid_free_range(struct mlxsw_sp *mlxsw_sp, u16 mid_base, u16 count)
+{
+       struct idr *pgt_idr = &mlxsw_sp->pgt->pgt_idr;
+       int i;
+
+       mutex_lock(&mlxsw_sp->pgt->lock);
+
+       for (i = 0; i < count; i++)
+               WARN_ON_ONCE(idr_remove(pgt_idr, mid_base + i));
+
+       mutex_unlock(&mlxsw_sp->pgt->lock);
+}
+
+int mlxsw_sp_pgt_init(struct mlxsw_sp *mlxsw_sp)
+{
+       struct mlxsw_sp_pgt *pgt;
+
+       if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, PGT_SIZE))
+               return -EIO;
+
+       pgt = kzalloc(sizeof(*mlxsw_sp->pgt), GFP_KERNEL);
+       if (!pgt)
+               return -ENOMEM;
+
+       idr_init(&pgt->pgt_idr);
+       pgt->end_index = MLXSW_CORE_RES_GET(mlxsw_sp->core, PGT_SIZE);
+       mutex_init(&pgt->lock);
+       mlxsw_sp->pgt = pgt;
+       return 0;
+}
+
+void mlxsw_sp_pgt_fini(struct mlxsw_sp *mlxsw_sp)
+{
+       mutex_destroy(&mlxsw_sp->pgt->lock);
+       WARN_ON(!idr_is_empty(&mlxsw_sp->pgt->pgt_idr));
+       idr_destroy(&mlxsw_sp->pgt->pgt_idr);
+       kfree(mlxsw_sp->pgt);
+}