[PATCH net-next 10/11] net: marvell: prestera: add storm control (rate limiter) implementation

From: Oleksandr Mazur
Date: Wed Jun 09 2021 - 11:17:38 EST


Storm control (BUM) provides a mechanism to limit rate of ingress
port traffic (matched by type). Devlink port parameter API is used:
driver registers a set of per-port parameters that can be accessed to both
get/set per-port per-type rate limit.
Add new FW command - RATE_LIMIT_MODE_SET.

Signed-off-by: Oleksandr Mazur <oleksandr.mazur@xxxxxxxxxxx>
---
.../net/ethernet/marvell/prestera/prestera.h | 7 +
.../marvell/prestera/prestera_devlink.c | 134 +++++++++++++++++-
.../ethernet/marvell/prestera/prestera_hw.c | 25 ++++
.../ethernet/marvell/prestera/prestera_hw.h | 9 ++
4 files changed, 174 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/marvell/prestera/prestera.h b/drivers/net/ethernet/marvell/prestera/prestera.h
index 2c94bdec84b1..4b99a7421452 100644
--- a/drivers/net/ethernet/marvell/prestera/prestera.h
+++ b/drivers/net/ethernet/marvell/prestera/prestera.h
@@ -60,6 +60,12 @@ struct prestera_port_caps {
u8 transceiver;
};

+struct prestera_strom_control_cfg {
+ u32 bc_kbyte_per_sec_rate;
+ u32 unk_uc_kbyte_per_sec_rate;
+ u32 unreg_mc_kbyte_per_sec_rate;
+};
+
struct prestera_port {
struct net_device *dev;
struct prestera_switch *sw;
@@ -79,6 +85,7 @@ struct prestera_port {
struct prestera_port_stats stats;
struct delayed_work caching_dw;
} cached_hw_stats;
+ struct prestera_strom_control_cfg storm_control;
};

struct prestera_device {
diff --git a/drivers/net/ethernet/marvell/prestera/prestera_devlink.c b/drivers/net/ethernet/marvell/prestera/prestera_devlink.c
index d12e21db9fd6..0786fbb09f71 100644
--- a/drivers/net/ethernet/marvell/prestera/prestera_devlink.c
+++ b/drivers/net/ethernet/marvell/prestera/prestera_devlink.c
@@ -2,6 +2,8 @@
/* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */

#include <net/devlink.h>
+#include <linux/bitops.h>
+#include <linux/bitfield.h>

#include "prestera_devlink.h"
#include "prestera_hw.h"
@@ -159,6 +161,34 @@ struct prestera_trap_data {
DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \
PRESTERA_TRAP_METADATA)

+#define PRESTERA_PORT_PARAM_DRIVER_RUNTIME(_id, _name, _type) \
+ DEVLINK_PARAM_DRIVER(PRESTERA_DEVLINK_PORT_PARAM_ID_##_id, _name, \
+ _type, BIT(DEVLINK_PARAM_CMODE_RUNTIME), NULL, \
+ NULL, NULL)
+
+struct prestera_storm_control {
+ struct prestera_switch *sw;
+ struct prestera_strom_control_cfg *cfg;
+};
+
+enum prestera_devlink_port_param_id {
+ PRESTERA_DEVLINK_PORT_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX + 1,
+ PRESTERA_DEVLINK_PORT_PARAM_ID_BC_RATE,
+ PRESTERA_DEVLINK_PORT_PARAM_ID_UC_UNK_RATE,
+ PRESTERA_DEVLINK_PORT_PARAM_ID_MC_RATE,
+};
+
+struct devlink_param prestera_devlink_port_params[] = {
+ PRESTERA_PORT_PARAM_DRIVER_RUNTIME(BC_RATE, "bc_kbyte_per_sec_rate",
+ DEVLINK_PARAM_TYPE_U32),
+ PRESTERA_PORT_PARAM_DRIVER_RUNTIME(UC_UNK_RATE,
+ "unk_uc_kbyte_per_sec_rate",
+ DEVLINK_PARAM_TYPE_U32),
+ PRESTERA_PORT_PARAM_DRIVER_RUNTIME(MC_RATE,
+ "unreg_mc_kbyte_per_sec_rate",
+ DEVLINK_PARAM_TYPE_U32),
+};
+
static const struct devlink_trap_group prestera_trap_groups_arr[] = {
/* No policer is associated with following groups (policerid == 0)*/
DEVLINK_TRAP_GROUP_GENERIC(L2_DROPS, 0),
@@ -350,6 +380,10 @@ static void prestera_devlink_traps_fini(struct prestera_switch *sw);
static int prestera_drop_counter_get(struct devlink *devlink,
const struct devlink_trap *trap,
u64 *p_drops);
+static int prestera_devlink_port_param_set(struct devlink_port *dl_port, u32 id,
+ struct devlink_param_gset_ctx *ctx);
+static int prestera_devlink_port_param_get(struct devlink_port *dl_port, u32 id,
+ struct devlink_param_gset_ctx *ctx);

static int prestera_dl_info_get(struct devlink *dl,
struct devlink_info_req *req,
@@ -383,11 +417,17 @@ static int prestera_trap_action_set(struct devlink *devlink,

static int prestera_devlink_traps_register(struct prestera_switch *sw);

+static const struct devlink_port_param_ops prestera_devlink_port_param_ops = {
+ .get = prestera_devlink_port_param_get,
+ .set = prestera_devlink_port_param_set,
+};
+
static const struct devlink_ops prestera_dl_ops = {
.info_get = prestera_dl_info_get,
.trap_init = prestera_trap_init,
.trap_action_set = prestera_trap_action_set,
.trap_drop_counter_get = prestera_drop_counter_get,
+ .port_param_ops = &prestera_devlink_port_param_ops,
};

struct prestera_switch *prestera_devlink_alloc(void)
@@ -443,10 +483,12 @@ void prestera_devlink_unregister(struct prestera_switch *sw)
int prestera_devlink_port_register(struct prestera_port *port)
{
struct prestera_switch *sw = port->sw;
- struct devlink *dl = priv_to_devlink(sw);
struct devlink_port_attrs attrs = {};
+ struct devlink *dl;
int err;

+ dl = priv_to_devlink(sw);
+
attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
attrs.phys.port_number = port->fp_id;
attrs.switch_id.id_len = sizeof(sw->id);
@@ -460,12 +502,32 @@ int prestera_devlink_port_register(struct prestera_port *port)
return err;
}

+ err = devlink_port_params_register(
+ &port->dl_port,
+ prestera_devlink_port_params,
+ ARRAY_SIZE(prestera_devlink_port_params));
+ if (err) {
+ devlink_port_unregister(&port->dl_port);
+ dev_err(sw->dev->dev, "devlink_port_params_register failed\n");
+ return err;
+ }
+
+ devlink_port_params_publish(&port->dl_port);
+
return 0;
}

void prestera_devlink_port_unregister(struct prestera_port *port)
{
+ devlink_port_params_unpublish(&port->dl_port);
+
+ devlink_port_params_unregister(
+ &port->dl_port,
+ prestera_devlink_port_params,
+ ARRAY_SIZE(prestera_devlink_port_params));
+
devlink_port_unregister(&port->dl_port);
+
}

void prestera_devlink_port_set(struct prestera_port *port)
@@ -622,6 +684,76 @@ static int prestera_drop_counter_get(struct devlink *devlink,
cpu_code_type, p_drops);
}

+static int prestera_devlink_port_param_set(struct devlink_port *dl_port, u32 id,
+ struct devlink_param_gset_ctx *ctx)
+{
+ struct prestera_strom_control_cfg *cfg;
+ u32 kbyte_per_sec_rate = ctx->val.vu32;
+ struct prestera_port *port;
+ struct prestera_switch *sw;
+ u32 *param_to_set;
+ u32 storm_type;
+ int ret;
+
+ port = container_of(dl_port, struct prestera_port, dl_port);
+ sw = devlink_priv(dl_port->devlink);
+ cfg = &port->storm_control;
+
+ switch (id) {
+ case PRESTERA_DEVLINK_PORT_PARAM_ID_BC_RATE:
+ param_to_set = &cfg->bc_kbyte_per_sec_rate;
+ storm_type = PRESTERA_PORT_STORM_CTL_TYPE_BC;
+ break;
+ case PRESTERA_DEVLINK_PORT_PARAM_ID_UC_UNK_RATE:
+ param_to_set = &cfg->unk_uc_kbyte_per_sec_rate;
+ storm_type = PRESTERA_PORT_STORM_CTL_TYPE_UC_UNK;
+ break;
+ case PRESTERA_DEVLINK_PORT_PARAM_ID_MC_RATE:
+ param_to_set = &cfg->unreg_mc_kbyte_per_sec_rate;
+ storm_type = PRESTERA_PORT_STORM_CTL_TYPE_MC;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (kbyte_per_sec_rate != *param_to_set) {
+ ret = prestera_hw_port_storm_control_cfg_set(port, storm_type,
+ kbyte_per_sec_rate);
+ if (ret)
+ return ret;
+
+ *param_to_set = kbyte_per_sec_rate;
+ }
+
+ return 0;
+}
+
+static int prestera_devlink_port_param_get(struct devlink_port *dl_port, u32 id,
+ struct devlink_param_gset_ctx *ctx)
+{
+ struct prestera_strom_control_cfg *cfg;
+ struct prestera_port *port;
+ struct prestera_switch *sw;
+
+ port = container_of(dl_port, struct prestera_port, dl_port);
+ sw = devlink_priv(dl_port->devlink);
+ cfg = &port->storm_control;
+
+ switch (id) {
+ case PRESTERA_DEVLINK_PORT_PARAM_ID_BC_RATE:
+ ctx->val.vu32 = cfg->bc_kbyte_per_sec_rate;
+ return 0;
+ case PRESTERA_DEVLINK_PORT_PARAM_ID_UC_UNK_RATE:
+ ctx->val.vu32 = cfg->unk_uc_kbyte_per_sec_rate;
+ return 0;
+ case PRESTERA_DEVLINK_PORT_PARAM_ID_MC_RATE:
+ ctx->val.vu32 = cfg->unreg_mc_kbyte_per_sec_rate;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
static void prestera_devlink_traps_fini(struct prestera_switch *sw)
{
struct devlink *dl = priv_to_devlink(sw);
diff --git a/drivers/net/ethernet/marvell/prestera/prestera_hw.c b/drivers/net/ethernet/marvell/prestera/prestera_hw.c
index 0e5b3f8e7dc7..85a1a15717df 100644
--- a/drivers/net/ethernet/marvell/prestera/prestera_hw.c
+++ b/drivers/net/ethernet/marvell/prestera/prestera_hw.c
@@ -20,6 +20,7 @@ enum prestera_cmd_type_t {
PRESTERA_CMD_TYPE_PORT_ATTR_SET = 0x100,
PRESTERA_CMD_TYPE_PORT_ATTR_GET = 0x101,
PRESTERA_CMD_TYPE_PORT_INFO_GET = 0x110,
+ PRESTERA_CMD_TYPE_PORT_RATE_LIMIT_MODE_SET = 0x111,

PRESTERA_CMD_TYPE_VLAN_CREATE = 0x200,
PRESTERA_CMD_TYPE_VLAN_DELETE = 0x201,
@@ -251,6 +252,14 @@ struct prestera_msg_port_info_resp {
u16 fp_id;
};

+struct prestera_msg_port_storm_control_cfg_set_req {
+ struct prestera_msg_cmd cmd;
+ u32 port;
+ u32 dev;
+ u32 storm_type;
+ u32 kbyte_per_sec_rate;
+};
+
struct prestera_msg_vlan_req {
struct prestera_msg_cmd cmd;
u32 port;
@@ -639,6 +648,22 @@ int prestera_hw_port_accept_frm_type(struct prestera_port *port,
&req.cmd, sizeof(req));
}

+int prestera_hw_port_storm_control_cfg_set(const struct prestera_port *port,
+ u32 storm_type,
+ u32 kbyte_per_sec_rate)
+{
+ struct prestera_msg_port_storm_control_cfg_set_req req = {
+ .port = port->hw_id,
+ .dev = port->dev_id,
+ .storm_type = storm_type,
+ .kbyte_per_sec_rate = kbyte_per_sec_rate
+ };
+
+ return prestera_cmd(port->sw,
+ PRESTERA_CMD_TYPE_PORT_RATE_LIMIT_MODE_SET,
+ &req.cmd, sizeof(req));
+}
+
int prestera_hw_port_cap_get(const struct prestera_port *port,
struct prestera_port_caps *caps)
{
diff --git a/drivers/net/ethernet/marvell/prestera/prestera_hw.h b/drivers/net/ethernet/marvell/prestera/prestera_hw.h
index aafecf0ecd16..85373f1d3971 100644
--- a/drivers/net/ethernet/marvell/prestera/prestera_hw.h
+++ b/drivers/net/ethernet/marvell/prestera/prestera_hw.h
@@ -89,6 +89,12 @@ enum {
PRESTERA_STP_FORWARD,
};

+enum {
+ PRESTERA_PORT_STORM_CTL_TYPE_BC = 0,
+ PRESTERA_PORT_STORM_CTL_TYPE_UC_UNK = 1,
+ PRESTERA_PORT_STORM_CTL_TYPE_MC = 2
+};
+
enum prestera_hw_cpu_code_cnt_t {
PRESTERA_HW_CPU_CODE_CNT_TYPE_DROP = 0,
PRESTERA_HW_CPU_CODE_CNT_TYPE_TRAP = 1,
@@ -123,6 +129,9 @@ int prestera_hw_port_mac_set(const struct prestera_port *port, const char *mac);
int prestera_hw_port_mac_get(const struct prestera_port *port, char *mac);
int prestera_hw_port_cap_get(const struct prestera_port *port,
struct prestera_port_caps *caps);
+int prestera_hw_port_storm_control_cfg_set(const struct prestera_port *port,
+ u32 storm_type,
+ u32 kbyte_per_sec_rate);
int prestera_hw_port_remote_cap_get(const struct prestera_port *port,
u64 *link_mode_bitmap);
int prestera_hw_port_remote_fc_get(const struct prestera_port *port,
--
2.17.1