Commit b3eb04be authored by Ido Schimmel's avatar Ido Schimmel Committed by Jakub Kicinski

mlxsw: spectrum_port_range: Add port range core

The Spectrum ASICs have a fixed number of port range registers, each of
which maintains the following parameters:

* Minimum and maximum port.
* Apply port range for source port, destination port or both.
* Apply port range for TCP, UDP or both.
* Apply port range for IPv4, IPv6 or both.

Implement a port range core which takes care of the allocation and
configuration of these registers and exposes an API that allows
in-driver consumers (e.g., the ACL code) to request matching on a range
of either source or destination port.

These registers are going to be used for port range matching in the
flower classifier that already matches on EtherType being IPv4 / IPv6 and
IP protocol being TCP / UDP. As such, there is no need to limit these
registers to a specific EtherType or IP protocol, which will increase
the likelihood of a register being shared by multiple flower filters.

It is unlikely that a filter will match on the same range of both source
and destination ports, which is why each register is only configured to
match on either source or destination port. If a filter requires
matching on a range of both source and destination ports, it will
utilize two port range registers and match on the output of both.

For efficient lookup and traversal, use XArray to store the allocated
port range registers. The XArray uses RCU and an internal spinlock to
synchronise access, so there is no need for a dedicate lock.
Signed-off-by: default avatarIdo Schimmel <idosch@nvidia.com>
Reviewed-by: default avatarPetr Machata <petrm@nvidia.com>
Signed-off-by: default avatarPetr Machata <petrm@nvidia.com>
Link: https://lore.kernel.org/r/674f00539a0072d455847663b5feb504db51a259.1689092769.git.petrm@nvidia.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 9f53a760
...@@ -29,7 +29,7 @@ mlxsw_spectrum-objs := spectrum.o spectrum_buffers.o \ ...@@ -29,7 +29,7 @@ mlxsw_spectrum-objs := spectrum.o spectrum_buffers.o \
spectrum_nve.o spectrum_nve_vxlan.o \ spectrum_nve.o spectrum_nve_vxlan.o \
spectrum_dpipe.o spectrum_trap.o \ spectrum_dpipe.o spectrum_trap.o \
spectrum_ethtool.o spectrum_policer.o \ spectrum_ethtool.o spectrum_policer.o \
spectrum_pgt.o spectrum_pgt.o spectrum_port_range.o
mlxsw_spectrum-$(CONFIG_MLXSW_SPECTRUM_DCB) += spectrum_dcb.o mlxsw_spectrum-$(CONFIG_MLXSW_SPECTRUM_DCB) += spectrum_dcb.o
mlxsw_spectrum-$(CONFIG_PTP_1588_CLOCK) += spectrum_ptp.o mlxsw_spectrum-$(CONFIG_PTP_1588_CLOCK) += spectrum_ptp.o
obj-$(CONFIG_MLXSW_MINIMAL) += mlxsw_minimal.o obj-$(CONFIG_MLXSW_MINIMAL) += mlxsw_minimal.o
......
...@@ -3188,6 +3188,12 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core, ...@@ -3188,6 +3188,12 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
goto err_nve_init; goto err_nve_init;
} }
err = mlxsw_sp_port_range_init(mlxsw_sp);
if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize port ranges\n");
goto err_port_range_init;
}
err = mlxsw_sp_acl_init(mlxsw_sp); err = mlxsw_sp_acl_init(mlxsw_sp);
if (err) { if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize ACL\n"); dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize ACL\n");
...@@ -3280,6 +3286,8 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core, ...@@ -3280,6 +3286,8 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
err_router_init: err_router_init:
mlxsw_sp_acl_fini(mlxsw_sp); mlxsw_sp_acl_fini(mlxsw_sp);
err_acl_init: err_acl_init:
mlxsw_sp_port_range_fini(mlxsw_sp);
err_port_range_init:
mlxsw_sp_nve_fini(mlxsw_sp); mlxsw_sp_nve_fini(mlxsw_sp);
err_nve_init: err_nve_init:
mlxsw_sp_ipv6_addr_ht_fini(mlxsw_sp); mlxsw_sp_ipv6_addr_ht_fini(mlxsw_sp);
...@@ -3462,6 +3470,7 @@ static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core) ...@@ -3462,6 +3470,7 @@ static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core)
} }
mlxsw_sp_router_fini(mlxsw_sp); mlxsw_sp_router_fini(mlxsw_sp);
mlxsw_sp_acl_fini(mlxsw_sp); mlxsw_sp_acl_fini(mlxsw_sp);
mlxsw_sp_port_range_fini(mlxsw_sp);
mlxsw_sp_nve_fini(mlxsw_sp); mlxsw_sp_nve_fini(mlxsw_sp);
mlxsw_sp_ipv6_addr_ht_fini(mlxsw_sp); mlxsw_sp_ipv6_addr_ht_fini(mlxsw_sp);
mlxsw_sp_afa_fini(mlxsw_sp); mlxsw_sp_afa_fini(mlxsw_sp);
......
...@@ -175,6 +175,7 @@ struct mlxsw_sp { ...@@ -175,6 +175,7 @@ struct mlxsw_sp {
struct mlxsw_sp_acl *acl; struct mlxsw_sp_acl *acl;
struct mlxsw_sp_fid_core *fid_core; struct mlxsw_sp_fid_core *fid_core;
struct mlxsw_sp_policer_core *policer_core; struct mlxsw_sp_policer_core *policer_core;
struct mlxsw_sp_port_range_core *pr_core;
struct mlxsw_sp_kvdl *kvdl; struct mlxsw_sp_kvdl *kvdl;
struct mlxsw_sp_nve *nve; struct mlxsw_sp_nve *nve;
struct notifier_block netdevice_nb; struct notifier_block netdevice_nb;
...@@ -1484,4 +1485,18 @@ int mlxsw_sp_pgt_entry_port_set(struct mlxsw_sp *mlxsw_sp, u16 mid, ...@@ -1484,4 +1485,18 @@ int mlxsw_sp_pgt_entry_port_set(struct mlxsw_sp *mlxsw_sp, u16 mid,
int mlxsw_sp_pgt_init(struct mlxsw_sp *mlxsw_sp); int mlxsw_sp_pgt_init(struct mlxsw_sp *mlxsw_sp);
void mlxsw_sp_pgt_fini(struct mlxsw_sp *mlxsw_sp); void mlxsw_sp_pgt_fini(struct mlxsw_sp *mlxsw_sp);
/* spectrum_port_range.c */
struct mlxsw_sp_port_range {
u16 min;
u16 max;
u8 source:1; /* Source or destination */
};
int mlxsw_sp_port_range_reg_get(struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_port_range *range,
struct netlink_ext_ack *extack,
u8 *p_prr_index);
void mlxsw_sp_port_range_reg_put(struct mlxsw_sp *mlxsw_sp, u8 prr_index);
int mlxsw_sp_port_range_init(struct mlxsw_sp *mlxsw_sp);
void mlxsw_sp_port_range_fini(struct mlxsw_sp *mlxsw_sp);
#endif #endif
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/* Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
#include <linux/bits.h>
#include <linux/netlink.h>
#include <linux/refcount.h>
#include <linux/xarray.h>
#include "spectrum.h"
struct mlxsw_sp_port_range_reg {
struct mlxsw_sp_port_range range;
refcount_t refcount;
u32 index;
};
struct mlxsw_sp_port_range_core {
struct xarray prr_xa;
struct xa_limit prr_ids;
};
static int
mlxsw_sp_port_range_reg_configure(struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_port_range_reg *prr)
{
char pprr_pl[MLXSW_REG_PPRR_LEN];
/* We do not care if packet is IPv4/IPv6 and TCP/UDP, so set all four
* fields.
*/
mlxsw_reg_pprr_pack(pprr_pl, prr->index);
mlxsw_reg_pprr_ipv4_set(pprr_pl, true);
mlxsw_reg_pprr_ipv6_set(pprr_pl, true);
mlxsw_reg_pprr_src_set(pprr_pl, prr->range.source);
mlxsw_reg_pprr_dst_set(pprr_pl, !prr->range.source);
mlxsw_reg_pprr_tcp_set(pprr_pl, true);
mlxsw_reg_pprr_udp_set(pprr_pl, true);
mlxsw_reg_pprr_port_range_min_set(pprr_pl, prr->range.min);
mlxsw_reg_pprr_port_range_max_set(pprr_pl, prr->range.max);
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pprr), pprr_pl);
}
static struct mlxsw_sp_port_range_reg *
mlxsw_sp_port_range_reg_create(struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_port_range *range,
struct netlink_ext_ack *extack)
{
struct mlxsw_sp_port_range_core *pr_core = mlxsw_sp->pr_core;
struct mlxsw_sp_port_range_reg *prr;
int err;
prr = kzalloc(sizeof(*prr), GFP_KERNEL);
if (!prr)
return ERR_PTR(-ENOMEM);
prr->range = *range;
refcount_set(&prr->refcount, 1);
err = xa_alloc(&pr_core->prr_xa, &prr->index, prr, pr_core->prr_ids,
GFP_KERNEL);
if (err) {
if (err == -EBUSY)
NL_SET_ERR_MSG_MOD(extack, "Exceeded number of port range registers");
goto err_xa_alloc;
}
err = mlxsw_sp_port_range_reg_configure(mlxsw_sp, prr);
if (err) {
NL_SET_ERR_MSG_MOD(extack, "Failed to configure port range register");
goto err_reg_configure;
}
return prr;
err_reg_configure:
xa_erase(&pr_core->prr_xa, prr->index);
err_xa_alloc:
kfree(prr);
return ERR_PTR(err);
}
static void mlxsw_sp_port_range_reg_destroy(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_port_range_reg *prr)
{
struct mlxsw_sp_port_range_core *pr_core = mlxsw_sp->pr_core;
xa_erase(&pr_core->prr_xa, prr->index);
kfree(prr);
}
static struct mlxsw_sp_port_range_reg *
mlxsw_sp_port_range_reg_find(struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_port_range *range)
{
struct mlxsw_sp_port_range_core *pr_core = mlxsw_sp->pr_core;
struct mlxsw_sp_port_range_reg *prr;
unsigned long index;
xa_for_each(&pr_core->prr_xa, index, prr) {
if (prr->range.min == range->min &&
prr->range.max == range->max &&
prr->range.source == range->source)
return prr;
}
return NULL;
}
int mlxsw_sp_port_range_reg_get(struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_port_range *range,
struct netlink_ext_ack *extack,
u8 *p_prr_index)
{
struct mlxsw_sp_port_range_reg *prr;
prr = mlxsw_sp_port_range_reg_find(mlxsw_sp, range);
if (prr) {
refcount_inc(&prr->refcount);
*p_prr_index = prr->index;
return 0;
}
prr = mlxsw_sp_port_range_reg_create(mlxsw_sp, range, extack);
if (IS_ERR(prr))
return PTR_ERR(prr);
*p_prr_index = prr->index;
return 0;
}
void mlxsw_sp_port_range_reg_put(struct mlxsw_sp *mlxsw_sp, u8 prr_index)
{
struct mlxsw_sp_port_range_core *pr_core = mlxsw_sp->pr_core;
struct mlxsw_sp_port_range_reg *prr;
prr = xa_load(&pr_core->prr_xa, prr_index);
if (WARN_ON(!prr))
return;
if (!refcount_dec_and_test(&prr->refcount))
return;
mlxsw_sp_port_range_reg_destroy(mlxsw_sp, prr);
}
int mlxsw_sp_port_range_init(struct mlxsw_sp *mlxsw_sp)
{
struct mlxsw_sp_port_range_core *pr_core;
struct mlxsw_core *core = mlxsw_sp->core;
u64 max;
if (!MLXSW_CORE_RES_VALID(core, ACL_MAX_L4_PORT_RANGE))
return -EIO;
max = MLXSW_CORE_RES_GET(core, ACL_MAX_L4_PORT_RANGE);
/* Each port range register is represented using a single bit in the
* two bytes "l4_port_range" ACL key element.
*/
WARN_ON(max > BITS_PER_BYTE * sizeof(u16));
pr_core = kzalloc(sizeof(*mlxsw_sp->pr_core), GFP_KERNEL);
if (!pr_core)
return -ENOMEM;
mlxsw_sp->pr_core = pr_core;
pr_core->prr_ids.max = max - 1;
xa_init_flags(&pr_core->prr_xa, XA_FLAGS_ALLOC);
return 0;
}
void mlxsw_sp_port_range_fini(struct mlxsw_sp *mlxsw_sp)
{
struct mlxsw_sp_port_range_core *pr_core = mlxsw_sp->pr_core;
WARN_ON(!xa_empty(&pr_core->prr_xa));
xa_destroy(&pr_core->prr_xa);
kfree(pr_core);
}
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