Commit d8782ec5 authored by Amit Cohen's avatar Amit Cohen Committed by Paolo Abeni

mlxsw: Add an initial PGT table support

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: default avatarAmit Cohen <amcohen@nvidia.com>
Reviewed-by: default avatarPetr Machata <petrm@nvidia.com>
Signed-off-by: default avatarIdo Schimmel <idosch@nvidia.com>
Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent 8c2da081
......@@ -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
......
......@@ -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,
......
......@@ -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
// 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);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment