This patch series enables hardware offload of ingress port policing
on the MSCC ocelot board.
Joergen Andreasen (3):
net/sched: act_police: move police parameters into separate header
file
net: mscc: ocelot: Implement port policers via tc command
MIPS: generic: Add police related options to ocelot_defconfig
arch/mips/configs/generic/board-ocelot.config | 7 +
drivers/net/ethernet/mscc/Makefile | 2 +-
drivers/net/ethernet/mscc/ocelot.c | 6 +-
drivers/net/ethernet/mscc/ocelot.h | 3 +
drivers/net/ethernet/mscc/ocelot_police.c | 289 ++++++++++++++++++
drivers/net/ethernet/mscc/ocelot_police.h | 16 +
drivers/net/ethernet/mscc/ocelot_tc.c | 151 +++++++++
drivers/net/ethernet/mscc/ocelot_tc.h | 19 ++
include/net/tc_act/tc_police.h | 41 +++
net/sched/act_police.c | 27 +-
10 files changed, 532 insertions(+), 29 deletions(-)
create mode 100644 drivers/net/ethernet/mscc/ocelot_police.c
create mode 100644 drivers/net/ethernet/mscc/ocelot_police.h
create mode 100644 drivers/net/ethernet/mscc/ocelot_tc.c
create mode 100644 drivers/net/ethernet/mscc/ocelot_tc.h
create mode 100644 include/net/tc_act/tc_police.h
--
2.17.1
Hardware offload of port policers are now supported via the tc command.
Supported police parameters are: rate, burst and overhead.
Example:
Add:
tc qdisc add dev eth3 handle ffff: ingress
tc filter add dev eth3 parent ffff: prio 1 handle 2 \
matchall skip_sw \
action police rate 100Mbit burst 10000 overhead 20
Show:
tc -s -d qdisc show dev eth3
tc -s -d filter show dev eth3 ingress
Delete:
tc filter del dev eth3 parent ffff: prio 1
tc qdisc del dev eth3 handle ffff: ingress
Signed-off-by: Joergen Andreasen <[email protected]>
---
drivers/net/ethernet/mscc/Makefile | 2 +-
drivers/net/ethernet/mscc/ocelot.c | 6 +-
drivers/net/ethernet/mscc/ocelot.h | 3 +
drivers/net/ethernet/mscc/ocelot_police.c | 289 ++++++++++++++++++++++
drivers/net/ethernet/mscc/ocelot_police.h | 16 ++
drivers/net/ethernet/mscc/ocelot_tc.c | 151 +++++++++++
drivers/net/ethernet/mscc/ocelot_tc.h | 19 ++
7 files changed, 483 insertions(+), 3 deletions(-)
create mode 100644 drivers/net/ethernet/mscc/ocelot_police.c
create mode 100644 drivers/net/ethernet/mscc/ocelot_police.h
create mode 100644 drivers/net/ethernet/mscc/ocelot_tc.c
create mode 100644 drivers/net/ethernet/mscc/ocelot_tc.h
diff --git a/drivers/net/ethernet/mscc/Makefile b/drivers/net/ethernet/mscc/Makefile
index cb52a3b128ae..5e694dc1f7f8 100644
--- a/drivers/net/ethernet/mscc/Makefile
+++ b/drivers/net/ethernet/mscc/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: (GPL-2.0 OR MIT)
obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot_common.o
mscc_ocelot_common-y := ocelot.o ocelot_io.o
-mscc_ocelot_common-y += ocelot_regs.o
+mscc_ocelot_common-y += ocelot_regs.o ocelot_tc.o ocelot_police.o
obj-$(CONFIG_MSCC_OCELOT_SWITCH_OCELOT) += ocelot_board.o
diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
index d715ef4fc92f..3ec7864d9dc8 100644
--- a/drivers/net/ethernet/mscc/ocelot.c
+++ b/drivers/net/ethernet/mscc/ocelot.c
@@ -943,6 +943,7 @@ static const struct net_device_ops ocelot_port_netdev_ops = {
.ndo_vlan_rx_kill_vid = ocelot_vlan_rx_kill_vid,
.ndo_set_features = ocelot_set_features,
.ndo_get_port_parent_id = ocelot_get_port_parent_id,
+ .ndo_setup_tc = ocelot_setup_tc,
};
static void ocelot_get_strings(struct net_device *netdev, u32 sset, u8 *data)
@@ -1663,8 +1664,9 @@ int ocelot_probe_port(struct ocelot *ocelot, u8 port,
dev->netdev_ops = &ocelot_port_netdev_ops;
dev->ethtool_ops = &ocelot_ethtool_ops;
- dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXFCS;
- dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+ dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXFCS |
+ NETIF_F_HW_TC;
+ dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_TC;
memcpy(dev->dev_addr, ocelot->base_mac, ETH_ALEN);
dev->dev_addr[ETH_ALEN - 1] += port;
diff --git a/drivers/net/ethernet/mscc/ocelot.h b/drivers/net/ethernet/mscc/ocelot.h
index ba3b3380b4d0..9514979fa075 100644
--- a/drivers/net/ethernet/mscc/ocelot.h
+++ b/drivers/net/ethernet/mscc/ocelot.h
@@ -22,6 +22,7 @@
#include "ocelot_rew.h"
#include "ocelot_sys.h"
#include "ocelot_qs.h"
+#include "ocelot_tc.h"
#define PGID_AGGR 64
#define PGID_SRC 80
@@ -458,6 +459,8 @@ struct ocelot_port {
phy_interface_t phy_mode;
struct phy *serdes;
+
+ struct ocelot_port_tc tc;
};
u32 __ocelot_read_ix(struct ocelot *ocelot, u32 reg, u32 offset);
diff --git a/drivers/net/ethernet/mscc/ocelot_police.c b/drivers/net/ethernet/mscc/ocelot_police.c
new file mode 100644
index 000000000000..b40382dcc748
--- /dev/null
+++ b/drivers/net/ethernet/mscc/ocelot_police.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/* Microsemi Ocelot Switch TC driver
+ *
+ * Copyright (c) 2019 Microsemi Corporation
+ */
+
+#include "ocelot_police.h"
+
+#define MSCC_RC(expr) \
+ do { \
+ int __rc__ = (expr); \
+ if (__rc__ < 0) \
+ return __rc__; \
+ } \
+ while (0)
+
+/* The following two functions do the same as in iproute2 */
+#define TIME_UNITS_PER_SEC 1000000
+static unsigned int tc_core_tick2time(unsigned int tick)
+{
+ return (tick * (u32)PSCHED_TICKS2NS(1)) / 1000;
+}
+
+static unsigned int tc_calc_xmitsize(u64 rate, unsigned int ticks)
+{
+ return div_u64(rate * tc_core_tick2time(ticks), TIME_UNITS_PER_SEC);
+}
+
+enum mscc_qos_rate_mode {
+ MSCC_QOS_RATE_MODE_DISABLED, /* Policer/shaper disabled */
+ MSCC_QOS_RATE_MODE_LINE, /* Measure line rate in kbps incl. IPG */
+ MSCC_QOS_RATE_MODE_DATA, /* Measures data rate in kbps excl. IPG */
+ MSCC_QOS_RATE_MODE_FRAME, /* Measures frame rate in fps */
+ __MSCC_QOS_RATE_MODE_END,
+ NUM_MSCC_QOS_RATE_MODE = __MSCC_QOS_RATE_MODE_END,
+ MSCC_QOS_RATE_MODE_MAX = __MSCC_QOS_RATE_MODE_END - 1,
+};
+
+/* Round x divided by y to nearest integer. x and y are integers */
+#define MSCC_ROUNDING_DIVISION(x, y) (((x) + ((y) / 2)) / (y))
+
+/* Round x divided by y to nearest higher integer. x and y are integers */
+#define MSCC_DIV_ROUND_UP(x, y) (((x) + (y) - 1) / (y))
+
+/* Types for ANA:POL[0-192]:POL_MODE_CFG.FRM_MODE */
+#define POL_MODE_LINERATE 0 /* Incl IPG. Unit: 33 1/3 kbps, 4096 bytes */
+#define POL_MODE_DATARATE 1 /* Excl IPG. Unit: 33 1/3 kbps, 4096 bytes */
+#define POL_MODE_FRMRATE_HI 2 /* Unit: 33 1/3 fps, 32.8 frames */
+#define POL_MODE_FRMRATE_LO 3 /* Unit: 1/3 fps, 0.3 frames */
+
+/* Policer indexes */
+#define POL_IX_PORT 0 /* 0-11 : Port policers */
+#define POL_IX_QUEUE 32 /* 32-127 : Queue policers */
+
+/* Default policer order */
+#define POL_ORDER 0x1d3 /* Ocelot policer order: Serial (QoS -> Port -> VCAP) */
+
+struct qos_policer_conf {
+ enum mscc_qos_rate_mode mode;
+ bool dlb; /* Enable DLB (dual leaky bucket mode */
+ bool cf; /* Coupling flag (ignored in SLB mode) */
+ u32 cir; /* CIR in kbps/fps (ignored in SLB mode) */
+ u32 cbs; /* CBS in bytes/frames (ignored in SLB mode) */
+ u32 pir; /* PIR in kbps/fps */
+ u32 pbs; /* PBS in bytes/frames */
+ u8 ipg; /* Size of IPG when MSCC_QOS_RATE_MODE_LINE is chosen */
+};
+
+static int qos_policer_conf_set(struct ocelot_port *port,
+ u32 pol_ix,
+ struct qos_policer_conf *conf)
+{
+ struct ocelot *ocelot = port->ocelot;
+ u32 cir = 0, cbs = 0, pir = 0, pbs = 0;
+ u32 cf = 0, cir_ena = 0, frm_mode = 0;
+ u32 pbs_max = 0, cbs_max = 0;
+ bool cir_discard = 0, pir_discard = 0;
+ u8 ipg = 20;
+ u32 value;
+
+ pir = conf->pir;
+ pbs = conf->pbs;
+
+ switch (conf->mode) {
+ case MSCC_QOS_RATE_MODE_LINE:
+ case MSCC_QOS_RATE_MODE_DATA:
+ if (conf->mode == MSCC_QOS_RATE_MODE_LINE) {
+ frm_mode = POL_MODE_LINERATE;
+ ipg = min_t(u8, GENMASK(4, 0), conf->ipg);
+ } else {
+ frm_mode = POL_MODE_DATARATE;
+ }
+ if (conf->dlb) {
+ cir_ena = 1;
+ cir = conf->cir;
+ cbs = conf->cbs;
+ if (cir == 0 && cbs == 0) {
+ /* Discard cir frames */
+ cir_discard = 1;
+ } else {
+ cir = MSCC_DIV_ROUND_UP(cir, 100);
+ cir *= 3; /* 33 1/3 kbps */
+ cbs = MSCC_DIV_ROUND_UP(cbs, 4096);
+ cbs = (cbs ? cbs : 1); /* No zero burst size */
+ cbs_max = 60; /* Limit burst size */
+ cf = conf->cf;
+ if (cf)
+ pir += conf->cir;
+ }
+ }
+ if (pir == 0 && pbs == 0) {
+ /* Discard PIR frames */
+ pir_discard = 1;
+ } else {
+ pir = MSCC_DIV_ROUND_UP(pir, 100);
+ pir *= 3; /* 33 1/3 kbps */
+ pbs = MSCC_DIV_ROUND_UP(pbs, 4096);
+ pbs = (pbs ? pbs : 1); /* No zero burst size */
+ pbs_max = 60; /* Limit burst size */
+ }
+ break;
+ case MSCC_QOS_RATE_MODE_FRAME:
+ if (pir >= 100) {
+ frm_mode = POL_MODE_FRMRATE_HI;
+ pir = MSCC_DIV_ROUND_UP(pir, 100);
+ pir *= 3; /* 33 1/3 fps */
+ pbs = (pbs * 10) / 328; /* 32.8 frames */
+ pbs = (pbs ? pbs : 1); /* No zero burst size */
+ pbs_max = GENMASK(6, 0); /* Limit burst size */
+ } else {
+ frm_mode = POL_MODE_FRMRATE_LO;
+ if (pir == 0 && pbs == 0) {
+ /* Discard all frames */
+ pir_discard = 1;
+ cir_discard = 1;
+ } else {
+ pir *= 3; /* 1/3 fps */
+ pbs = (pbs * 10) / 3; /* 0.3 frames */
+ pbs = (pbs ? pbs : 1); /* No zero burst size */
+ pbs_max = 61; /* Limit burst size */
+ }
+ }
+ break;
+ default: /* MSCC_QOS_RATE_MODE_DISABLED */
+ /* Disable policer using maximum rate and zero burst */
+ pir = GENMASK(15, 0);
+ pbs = 0;
+ break;
+ }
+
+ /* Limit to maximum values */
+ pir = min_t(u32, GENMASK(15, 0), pir);
+ cir = min_t(u32, GENMASK(15, 0), cir);
+ pbs = min_t(u32, pbs_max, pbs);
+ cbs = min_t(u32, cbs_max, cbs);
+
+ value = (ANA_POL_MODE_CFG_IPG_SIZE(ipg) |
+ ANA_POL_MODE_CFG_FRM_MODE(frm_mode) |
+ (cf ? ANA_POL_MODE_CFG_DLB_COUPLED : 0) |
+ (cir_ena ? ANA_POL_MODE_CFG_CIR_ENA : 0) |
+ ANA_POL_MODE_CFG_OVERSHOOT_ENA);
+
+ ocelot_write_gix(ocelot,
+ value,
+ ANA_POL_MODE_CFG,
+ pol_ix);
+
+ ocelot_write_gix(ocelot,
+ ANA_POL_PIR_CFG_PIR_RATE(pir) |
+ ANA_POL_PIR_CFG_PIR_BURST(pbs),
+ ANA_POL_PIR_CFG,
+ pol_ix);
+
+ ocelot_write_gix(ocelot,
+ (pir_discard ? GENMASK(22, 0) : 0),
+ ANA_POL_PIR_STATE,
+ pol_ix);
+
+ ocelot_write_gix(ocelot,
+ ANA_POL_CIR_CFG_CIR_RATE(cir) |
+ ANA_POL_CIR_CFG_CIR_BURST(cbs),
+ ANA_POL_CIR_CFG,
+ pol_ix);
+
+ ocelot_write_gix(ocelot,
+ (cir_discard ? GENMASK(22, 0) : 0),
+ ANA_POL_CIR_STATE,
+ pol_ix);
+
+ return 0;
+}
+
+int ocelot_port_policer_add(struct ocelot_port *port,
+ struct tcf_police *p)
+{
+ struct ocelot *ocelot = port->ocelot;
+ struct qos_policer_conf pp;
+
+ if (!p)
+ return -EINVAL;
+
+ netdev_dbg(port->dev,
+ "result %d ewma_rate %u burst %lld mtu %u mtu_pktoks %lld\n",
+ p->params->tcfp_result,
+ p->params->tcfp_ewma_rate,
+ p->params->tcfp_burst,
+ p->params->tcfp_mtu,
+ p->params->tcfp_mtu_ptoks);
+
+ if (p->params->rate_present)
+ netdev_dbg(port->dev,
+ "rate: rate %llu mult %u over %u link %u shift %u\n",
+ p->params->rate.rate_bytes_ps,
+ p->params->rate.mult,
+ p->params->rate.overhead,
+ p->params->rate.linklayer,
+ p->params->rate.shift);
+
+ if (p->params->peak_present)
+ netdev_dbg(port->dev,
+ "peak: rate %llu mult %u over %u link %u shift %u\n",
+ p->params->peak.rate_bytes_ps,
+ p->params->peak.mult,
+ p->params->peak.overhead,
+ p->params->peak.linklayer,
+ p->params->peak.shift);
+
+ memset(&pp, 0, sizeof(pp));
+
+ if (p->params->tcfp_ewma_rate) {
+ netdev_err(port->dev, "tcfp_ewma_rate is not supported\n");
+ return -EOPNOTSUPP;
+ }
+ if (p->params->peak_present) {
+ netdev_err(port->dev, "peakrate is not supported\n");
+ return -EOPNOTSUPP;
+ }
+ if (!p->params->rate_present) {
+ netdev_err(port->dev, "rate not specified\n");
+ return -EINVAL;
+ }
+ if (p->params->rate.overhead) {
+ pp.mode = MSCC_QOS_RATE_MODE_LINE;
+ pp.ipg = p->params->rate.overhead;
+ } else {
+ pp.mode = MSCC_QOS_RATE_MODE_DATA;
+ }
+
+ pp.pir = (u32)div_u64(p->params->rate.rate_bytes_ps, 1000) * 8;
+ pp.pbs = tc_calc_xmitsize(p->params->rate.rate_bytes_ps,
+ PSCHED_NS2TICKS(p->params->tcfp_burst));
+ netdev_dbg(port->dev,
+ "%s: port %u pir %u kbps, pbs %u bytes, ipg %u bytes\n",
+ __func__, port->chip_port, pp.pir, pp.pbs, pp.ipg);
+
+ MSCC_RC(qos_policer_conf_set(port, POL_IX_PORT + port->chip_port, &pp));
+
+ ocelot_rmw_gix(ocelot,
+ ANA_PORT_POL_CFG_PORT_POL_ENA |
+ ANA_PORT_POL_CFG_POL_ORDER(POL_ORDER),
+ ANA_PORT_POL_CFG_PORT_POL_ENA |
+ ANA_PORT_POL_CFG_POL_ORDER_M,
+ ANA_PORT_POL_CFG,
+ port->chip_port);
+
+ return 0;
+}
+
+int ocelot_port_policer_del(struct ocelot_port *port)
+{
+ struct ocelot *ocelot = port->ocelot;
+ struct qos_policer_conf pp;
+
+ netdev_dbg(port->dev, "%s: port %u\n", __func__, port->chip_port);
+
+ memset(&pp, 0, sizeof(pp));
+ pp.mode = MSCC_QOS_RATE_MODE_DISABLED;
+
+ MSCC_RC(qos_policer_conf_set(port, POL_IX_PORT + port->chip_port, &pp));
+
+ ocelot_rmw_gix(ocelot, 0 |
+ ANA_PORT_POL_CFG_POL_ORDER(POL_ORDER),
+ ANA_PORT_POL_CFG_PORT_POL_ENA |
+ ANA_PORT_POL_CFG_POL_ORDER_M,
+ ANA_PORT_POL_CFG,
+ port->chip_port);
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/mscc/ocelot_police.h b/drivers/net/ethernet/mscc/ocelot_police.h
new file mode 100644
index 000000000000..bc4dc34c684e
--- /dev/null
+++ b/drivers/net/ethernet/mscc/ocelot_police.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/* Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2019 Microsemi Corporation
+ */
+
+#ifndef _MSCC_OCELOT_POLICE_H_
+#define _MSCC_OCELOT_POLICE_H_
+
+#include <net/tc_act/tc_police.h>
+#include "ocelot.h"
+
+int ocelot_port_policer_add(struct ocelot_port *port, struct tcf_police *p);
+int ocelot_port_policer_del(struct ocelot_port *port);
+
+#endif /* _MSCC_OCELOT_POLICE_H_ */
diff --git a/drivers/net/ethernet/mscc/ocelot_tc.c b/drivers/net/ethernet/mscc/ocelot_tc.c
new file mode 100644
index 000000000000..97b0a7bf5d06
--- /dev/null
+++ b/drivers/net/ethernet/mscc/ocelot_tc.c
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/* Microsemi Ocelot Switch TC driver
+ *
+ * Copyright (c) 2019 Microsemi Corporation
+ */
+
+#include "ocelot_tc.h"
+#include "ocelot_police.h"
+#include <net/pkt_cls.h>
+
+static int ocelot_setup_tc_cls_matchall(struct ocelot_port *port,
+ struct tc_cls_matchall_offload *f,
+ bool ingress)
+{
+ const struct tc_action *a;
+ int err;
+
+ netdev_dbg(port->dev,
+ "%s: port %u cookie %lu\n",
+ __func__, port->chip_port, f->cookie);
+ switch (f->command) {
+ case TC_CLSMATCHALL_REPLACE:
+ if (!tcf_exts_has_one_action(f->exts)) {
+ netdev_err(port->dev, "only one action is supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ a = tcf_exts_first_action(f->exts);
+
+ if (is_tcf_police(a)) {
+ if (!ingress)
+ return -EOPNOTSUPP;
+
+ if (port->tc.police_id &&
+ port->tc.police_id != f->cookie) {
+ netdev_warn(port->dev,
+ "Only one policer per port is supported\n");
+ return -EEXIST;
+ }
+
+ err = ocelot_port_policer_add(port, to_police(a));
+ if (err) {
+ netdev_err(port->dev, "Could not add policer\n");
+ return err;
+ }
+ port->tc.police_id = f->cookie;
+ return 0;
+ } else {
+ return -EOPNOTSUPP;
+ }
+ case TC_CLSMATCHALL_DESTROY:
+ if (port->tc.police_id != f->cookie)
+ return -ENOENT;
+
+ err = ocelot_port_policer_del(port);
+ if (err) {
+ netdev_err(port->dev, "Could not delete policer\n");
+ return err;
+ }
+ port->tc.police_id = 0;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ocelot_setup_tc_block_cb(enum tc_setup_type type,
+ void *type_data,
+ void *cb_priv, bool ingress)
+{
+ struct ocelot_port *port = cb_priv;
+
+ if (!tc_cls_can_offload_and_chain0(port->dev, type_data))
+ return -EOPNOTSUPP;
+
+ switch (type) {
+ case TC_SETUP_CLSMATCHALL:
+ netdev_dbg(port->dev, "tc_block_cb: TC_SETUP_CLSMATCHALL %s\n",
+ ingress ? "ingress" : "egress");
+
+ return ocelot_setup_tc_cls_matchall(port, type_data, ingress);
+ case TC_SETUP_CLSFLOWER:
+ netdev_dbg(port->dev, "tc_block_cb: TC_SETUP_CLSFLOWER %s\n",
+ ingress ? "ingress" : "egress");
+
+ return -EOPNOTSUPP;
+ default:
+ netdev_dbg(port->dev, "tc_block_cb: type %d %s\n",
+ type,
+ ingress ? "ingress" : "egress");
+
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ocelot_setup_tc_block_cb_ig(enum tc_setup_type type,
+ void *type_data,
+ void *cb_priv)
+{
+ return ocelot_setup_tc_block_cb(type, type_data,
+ cb_priv, true);
+}
+
+static int ocelot_setup_tc_block_cb_eg(enum tc_setup_type type,
+ void *type_data,
+ void *cb_priv)
+{
+ return ocelot_setup_tc_block_cb(type, type_data,
+ cb_priv, false);
+}
+
+static int ocelot_setup_tc_block(struct ocelot_port *port,
+ struct tc_block_offload *f)
+{
+ tc_setup_cb_t *cb;
+
+ netdev_dbg(port->dev, "tc_block command %d, binder_type %d\n",
+ f->command, f->binder_type);
+
+ if (f->binder_type == TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
+ cb = ocelot_setup_tc_block_cb_ig;
+ else if (f->binder_type == TCF_BLOCK_BINDER_TYPE_CLSACT_EGRESS)
+ cb = ocelot_setup_tc_block_cb_eg;
+ else
+ return -EOPNOTSUPP;
+
+ switch (f->command) {
+ case TC_BLOCK_BIND:
+ return tcf_block_cb_register(f->block, cb, port,
+ port, f->extack);
+ case TC_BLOCK_UNBIND:
+ tcf_block_cb_unregister(f->block, cb, port);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type,
+ void *type_data)
+{
+ struct ocelot_port *port = netdev_priv(dev);
+
+ switch (type) {
+ case TC_SETUP_BLOCK:
+ return ocelot_setup_tc_block(port, type_data);
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
diff --git a/drivers/net/ethernet/mscc/ocelot_tc.h b/drivers/net/ethernet/mscc/ocelot_tc.h
new file mode 100644
index 000000000000..c905b98b6b4c
--- /dev/null
+++ b/drivers/net/ethernet/mscc/ocelot_tc.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/* Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2019 Microsemi Corporation
+ */
+
+#ifndef _MSCC_OCELOT_TC_H_
+#define _MSCC_OCELOT_TC_H_
+
+#include <linux/netdevice.h>
+
+struct ocelot_port_tc {
+ unsigned long police_id;
+};
+
+int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type,
+ void *type_data);
+
+#endif /* _MSCC_OCELOT_TC_H_ */
--
2.17.1
Hardware offloading a policer requires access to it's parameters.
This is now possible by including net/tc_act/tc_police.h.
Signed-off-by: Joergen Andreasen <[email protected]>
---
include/net/tc_act/tc_police.h | 41 ++++++++++++++++++++++++++++++++++
net/sched/act_police.c | 27 +---------------------
2 files changed, 42 insertions(+), 26 deletions(-)
create mode 100644 include/net/tc_act/tc_police.h
diff --git a/include/net/tc_act/tc_police.h b/include/net/tc_act/tc_police.h
new file mode 100644
index 000000000000..052dc5f37aa9
--- /dev/null
+++ b/include/net/tc_act/tc_police.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __NET_TC_POLICE_H
+#define __NET_TC_POLICE_H
+
+#include <net/act_api.h>
+
+struct tcf_police_params {
+ int tcfp_result;
+ u32 tcfp_ewma_rate;
+ s64 tcfp_burst;
+ u32 tcfp_mtu;
+ s64 tcfp_mtu_ptoks;
+ struct psched_ratecfg rate;
+ bool rate_present;
+ struct psched_ratecfg peak;
+ bool peak_present;
+ struct rcu_head rcu;
+};
+
+struct tcf_police {
+ struct tc_action common;
+ struct tcf_police_params __rcu *params;
+
+ spinlock_t tcfp_lock ____cacheline_aligned_in_smp;
+ s64 tcfp_toks;
+ s64 tcfp_ptoks;
+ s64 tcfp_t_c;
+};
+
+#define to_police(pc) ((struct tcf_police *)pc)
+
+static inline bool is_tcf_police(const struct tc_action *a)
+{
+#ifdef CONFIG_NET_CLS_ACT
+ if (a->ops && a->ops->id == TCA_ID_POLICE)
+ return true;
+#endif
+ return false;
+}
+
+#endif /* __NET_TC_POLICE_H */
diff --git a/net/sched/act_police.c b/net/sched/act_police.c
index 2b8581f6ab51..5cb053f2c7b1 100644
--- a/net/sched/act_police.c
+++ b/net/sched/act_police.c
@@ -19,35 +19,10 @@
#include <linux/rtnetlink.h>
#include <linux/init.h>
#include <linux/slab.h>
-#include <net/act_api.h>
+#include <net/tc_act/tc_police.h>
#include <net/netlink.h>
#include <net/pkt_cls.h>
-struct tcf_police_params {
- int tcfp_result;
- u32 tcfp_ewma_rate;
- s64 tcfp_burst;
- u32 tcfp_mtu;
- s64 tcfp_mtu_ptoks;
- struct psched_ratecfg rate;
- bool rate_present;
- struct psched_ratecfg peak;
- bool peak_present;
- struct rcu_head rcu;
-};
-
-struct tcf_police {
- struct tc_action common;
- struct tcf_police_params __rcu *params;
-
- spinlock_t tcfp_lock ____cacheline_aligned_in_smp;
- s64 tcfp_toks;
- s64 tcfp_ptoks;
- s64 tcfp_t_c;
-};
-
-#define to_police(pc) ((struct tcf_police *)pc)
-
/* old policer structure from before tc actions */
struct tc_police_compat {
u32 index;
--
2.17.1
Add default support for ingress qdisc, matchall classification
and police action on MSCC Ocelot.
Signed-off-by: Joergen Andreasen <[email protected]>
---
arch/mips/configs/generic/board-ocelot.config | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/mips/configs/generic/board-ocelot.config b/arch/mips/configs/generic/board-ocelot.config
index 5e53b4bc47f1..5c7360dd819c 100644
--- a/arch/mips/configs/generic/board-ocelot.config
+++ b/arch/mips/configs/generic/board-ocelot.config
@@ -25,6 +25,13 @@ CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_NETDEVICES=y
CONFIG_NET_SWITCHDEV=y
CONFIG_NET_DSA=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_INGRESS=y
+CONFIG_NET_CLS_MATCHALL=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_POLICE=y
+CONFIG_NET_ACT_GACT=y
+
CONFIG_MSCC_OCELOT_SWITCH=y
CONFIG_MSCC_OCELOT_SWITCH_OCELOT=y
CONFIG_MDIO_MSCC_MIIM=y
--
2.17.1
Hi Joergen
> +
> +#define MSCC_RC(expr) \
> + do { \
> + int __rc__ = (expr); \
> + if (__rc__ < 0) \
> + return __rc__; \
> + } \
> + while (0)
I'm sure checkpatch warned about this. A return inside a macros is a
bad idea. I inherited code doing this, and broke it when adding
locking, because it was not obvious there was a return.
> +
> +/* The following two functions do the same as in iproute2 */
> +#define TIME_UNITS_PER_SEC 1000000
> +static unsigned int tc_core_tick2time(unsigned int tick)
> +{
> + return (tick * (u32)PSCHED_TICKS2NS(1)) / 1000;
> +}
> +
> +static unsigned int tc_calc_xmitsize(u64 rate, unsigned int ticks)
> +{
> + return div_u64(rate * tc_core_tick2time(ticks), TIME_UNITS_PER_SEC);
> +}
Should these but put somewhere others can use them?
> +
> +enum mscc_qos_rate_mode {
> + MSCC_QOS_RATE_MODE_DISABLED, /* Policer/shaper disabled */
> + MSCC_QOS_RATE_MODE_LINE, /* Measure line rate in kbps incl. IPG */
> + MSCC_QOS_RATE_MODE_DATA, /* Measures data rate in kbps excl. IPG */
> + MSCC_QOS_RATE_MODE_FRAME, /* Measures frame rate in fps */
> + __MSCC_QOS_RATE_MODE_END,
> + NUM_MSCC_QOS_RATE_MODE = __MSCC_QOS_RATE_MODE_END,
> + MSCC_QOS_RATE_MODE_MAX = __MSCC_QOS_RATE_MODE_END - 1,
> +};
> +
> +/* Round x divided by y to nearest integer. x and y are integers */
> +#define MSCC_ROUNDING_DIVISION(x, y) (((x) + ((y) / 2)) / (y))
linux/kernel.h defines DIV_ROUND_UP(). Maybe add DIV_ROUND_DOWN()?
> +
> +/* Round x divided by y to nearest higher integer. x and y are integers */
> +#define MSCC_DIV_ROUND_UP(x, y) (((x) + (y) - 1) / (y))
DIV_ROUND_UP() ?
> + /* Limit to maximum values */
> + pir = min_t(u32, GENMASK(15, 0), pir);
> + cir = min_t(u32, GENMASK(15, 0), cir);
> + pbs = min_t(u32, pbs_max, pbs);
> + cbs = min_t(u32, cbs_max, cbs);
If it does need to limit, maybe return -EOPNOTSUPP?
> +int ocelot_port_policer_add(struct ocelot_port *port,
> + struct tcf_police *p)
> +{
> + struct ocelot *ocelot = port->ocelot;
> + struct qos_policer_conf pp;
> +
> + if (!p)
> + return -EINVAL;
> +
> + netdev_dbg(port->dev,
> + "result %d ewma_rate %u burst %lld mtu %u mtu_pktoks %lld\n",
> + p->params->tcfp_result,
> + p->params->tcfp_ewma_rate,
> + p->params->tcfp_burst,
> + p->params->tcfp_mtu,
> + p->params->tcfp_mtu_ptoks);
> +
> + if (p->params->rate_present)
> + netdev_dbg(port->dev,
> + "rate: rate %llu mult %u over %u link %u shift %u\n",
> + p->params->rate.rate_bytes_ps,
> + p->params->rate.mult,
> + p->params->rate.overhead,
> + p->params->rate.linklayer,
> + p->params->rate.shift);
> +
> + if (p->params->peak_present)
> + netdev_dbg(port->dev,
> + "peak: rate %llu mult %u over %u link %u shift %u\n",
> + p->params->peak.rate_bytes_ps,
> + p->params->peak.mult,
> + p->params->peak.overhead,
> + p->params->peak.linklayer,
> + p->params->peak.shift);
> +
> + memset(&pp, 0, sizeof(pp));
Rather than memset, you can do:
struct qos_policer_conf pp = { 0 };
Andrew
Hi Joergen,
On 02/05/2019 11:40:29+0200, Joergen Andreasen wrote:
> Add default support for ingress qdisc, matchall classification
> and police action on MSCC Ocelot.
>
This patch should be separated from the series as this doesn't have any
dependencies and should go through the MIPS tree.
> Signed-off-by: Joergen Andreasen <[email protected]>
> ---
> arch/mips/configs/generic/board-ocelot.config | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/arch/mips/configs/generic/board-ocelot.config b/arch/mips/configs/generic/board-ocelot.config
> index 5e53b4bc47f1..5c7360dd819c 100644
> --- a/arch/mips/configs/generic/board-ocelot.config
> +++ b/arch/mips/configs/generic/board-ocelot.config
> @@ -25,6 +25,13 @@ CONFIG_SERIAL_OF_PLATFORM=y
> CONFIG_NETDEVICES=y
> CONFIG_NET_SWITCHDEV=y
> CONFIG_NET_DSA=y
> +CONFIG_NET_SCHED=y
> +CONFIG_NET_SCH_INGRESS=y
> +CONFIG_NET_CLS_MATCHALL=y
> +CONFIG_NET_CLS_ACT=y
> +CONFIG_NET_ACT_POLICE=y
> +CONFIG_NET_ACT_GACT=y
> +
> CONFIG_MSCC_OCELOT_SWITCH=y
> CONFIG_MSCC_OCELOT_SWITCH_OCELOT=y
> CONFIG_MDIO_MSCC_MIIM=y
> --
> 2.17.1
>
--
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Thu, May 02, 2019 at 11:40:28AM CEST, [email protected] wrote:
>Hardware offload of port policers are now supported via the tc command.
>Supported police parameters are: rate, burst and overhead.
Interesting, you offload matchall cls, yet you don't mention it at all.
Please, do it.
>
>Example:
>
>Add:
>tc qdisc add dev eth3 handle ffff: ingress
>tc filter add dev eth3 parent ffff: prio 1 handle 2 \
> matchall skip_sw \
> action police rate 100Mbit burst 10000 overhead 20
>
>Show:
>tc -s -d qdisc show dev eth3
>tc -s -d filter show dev eth3 ingress
>
>Delete:
>tc filter del dev eth3 parent ffff: prio 1
>tc qdisc del dev eth3 handle ffff: ingress
>
>Signed-off-by: Joergen Andreasen <[email protected]>
>---
> drivers/net/ethernet/mscc/Makefile | 2 +-
> drivers/net/ethernet/mscc/ocelot.c | 6 +-
> drivers/net/ethernet/mscc/ocelot.h | 3 +
> drivers/net/ethernet/mscc/ocelot_police.c | 289 ++++++++++++++++++++++
> drivers/net/ethernet/mscc/ocelot_police.h | 16 ++
> drivers/net/ethernet/mscc/ocelot_tc.c | 151 +++++++++++
> drivers/net/ethernet/mscc/ocelot_tc.h | 19 ++
> 7 files changed, 483 insertions(+), 3 deletions(-)
> create mode 100644 drivers/net/ethernet/mscc/ocelot_police.c
> create mode 100644 drivers/net/ethernet/mscc/ocelot_police.h
> create mode 100644 drivers/net/ethernet/mscc/ocelot_tc.c
> create mode 100644 drivers/net/ethernet/mscc/ocelot_tc.h
>
>diff --git a/drivers/net/ethernet/mscc/Makefile b/drivers/net/ethernet/mscc/Makefile
>index cb52a3b128ae..5e694dc1f7f8 100644
>--- a/drivers/net/ethernet/mscc/Makefile
>+++ b/drivers/net/ethernet/mscc/Makefile
>@@ -1,5 +1,5 @@
> # SPDX-License-Identifier: (GPL-2.0 OR MIT)
> obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot_common.o
> mscc_ocelot_common-y := ocelot.o ocelot_io.o
>-mscc_ocelot_common-y += ocelot_regs.o
>+mscc_ocelot_common-y += ocelot_regs.o ocelot_tc.o ocelot_police.o
> obj-$(CONFIG_MSCC_OCELOT_SWITCH_OCELOT) += ocelot_board.o
>diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
>index d715ef4fc92f..3ec7864d9dc8 100644
>--- a/drivers/net/ethernet/mscc/ocelot.c
>+++ b/drivers/net/ethernet/mscc/ocelot.c
>@@ -943,6 +943,7 @@ static const struct net_device_ops ocelot_port_netdev_ops = {
> .ndo_vlan_rx_kill_vid = ocelot_vlan_rx_kill_vid,
> .ndo_set_features = ocelot_set_features,
> .ndo_get_port_parent_id = ocelot_get_port_parent_id,
>+ .ndo_setup_tc = ocelot_setup_tc,
> };
>
> static void ocelot_get_strings(struct net_device *netdev, u32 sset, u8 *data)
>@@ -1663,8 +1664,9 @@ int ocelot_probe_port(struct ocelot *ocelot, u8 port,
> dev->netdev_ops = &ocelot_port_netdev_ops;
> dev->ethtool_ops = &ocelot_ethtool_ops;
>
>- dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXFCS;
>- dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
>+ dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXFCS |
>+ NETIF_F_HW_TC;
>+ dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_TC;
>
> memcpy(dev->dev_addr, ocelot->base_mac, ETH_ALEN);
> dev->dev_addr[ETH_ALEN - 1] += port;
>diff --git a/drivers/net/ethernet/mscc/ocelot.h b/drivers/net/ethernet/mscc/ocelot.h
>index ba3b3380b4d0..9514979fa075 100644
>--- a/drivers/net/ethernet/mscc/ocelot.h
>+++ b/drivers/net/ethernet/mscc/ocelot.h
>@@ -22,6 +22,7 @@
> #include "ocelot_rew.h"
> #include "ocelot_sys.h"
> #include "ocelot_qs.h"
>+#include "ocelot_tc.h"
>
> #define PGID_AGGR 64
> #define PGID_SRC 80
>@@ -458,6 +459,8 @@ struct ocelot_port {
>
> phy_interface_t phy_mode;
> struct phy *serdes;
>+
>+ struct ocelot_port_tc tc;
> };
>
> u32 __ocelot_read_ix(struct ocelot *ocelot, u32 reg, u32 offset);
>diff --git a/drivers/net/ethernet/mscc/ocelot_police.c b/drivers/net/ethernet/mscc/ocelot_police.c
>new file mode 100644
>index 000000000000..b40382dcc748
>--- /dev/null
>+++ b/drivers/net/ethernet/mscc/ocelot_police.c
>@@ -0,0 +1,289 @@
>+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
>+/* Microsemi Ocelot Switch TC driver
"TC driver" ? That sounds quite odd...
>+ *
>+ * Copyright (c) 2019 Microsemi Corporation
>+ */
>+
>+#include "ocelot_police.h"
>+
>+#define MSCC_RC(expr) \
>+ do { \
>+ int __rc__ = (expr); \
>+ if (__rc__ < 0) \
>+ return __rc__; \
>+ } \
>+ while (0)
Please don't use macros like this.
>+
>+/* The following two functions do the same as in iproute2 */
>+#define TIME_UNITS_PER_SEC 1000000
>+static unsigned int tc_core_tick2time(unsigned int tick)
>+{
>+ return (tick * (u32)PSCHED_TICKS2NS(1)) / 1000;
>+}
>+
>+static unsigned int tc_calc_xmitsize(u64 rate, unsigned int ticks)
>+{
>+ return div_u64(rate * tc_core_tick2time(ticks), TIME_UNITS_PER_SEC);
>+}
>+
>+enum mscc_qos_rate_mode {
>+ MSCC_QOS_RATE_MODE_DISABLED, /* Policer/shaper disabled */
>+ MSCC_QOS_RATE_MODE_LINE, /* Measure line rate in kbps incl. IPG */
>+ MSCC_QOS_RATE_MODE_DATA, /* Measures data rate in kbps excl. IPG */
>+ MSCC_QOS_RATE_MODE_FRAME, /* Measures frame rate in fps */
>+ __MSCC_QOS_RATE_MODE_END,
>+ NUM_MSCC_QOS_RATE_MODE = __MSCC_QOS_RATE_MODE_END,
>+ MSCC_QOS_RATE_MODE_MAX = __MSCC_QOS_RATE_MODE_END - 1,
>+};
>+
>+/* Round x divided by y to nearest integer. x and y are integers */
>+#define MSCC_ROUNDING_DIVISION(x, y) (((x) + ((y) / 2)) / (y))
>+
>+/* Round x divided by y to nearest higher integer. x and y are integers */
>+#define MSCC_DIV_ROUND_UP(x, y) (((x) + (y) - 1) / (y))
>+
>+/* Types for ANA:POL[0-192]:POL_MODE_CFG.FRM_MODE */
>+#define POL_MODE_LINERATE 0 /* Incl IPG. Unit: 33 1/3 kbps, 4096 bytes */
>+#define POL_MODE_DATARATE 1 /* Excl IPG. Unit: 33 1/3 kbps, 4096 bytes */
>+#define POL_MODE_FRMRATE_HI 2 /* Unit: 33 1/3 fps, 32.8 frames */
>+#define POL_MODE_FRMRATE_LO 3 /* Unit: 1/3 fps, 0.3 frames */
>+
>+/* Policer indexes */
>+#define POL_IX_PORT 0 /* 0-11 : Port policers */
>+#define POL_IX_QUEUE 32 /* 32-127 : Queue policers */
>+
>+/* Default policer order */
>+#define POL_ORDER 0x1d3 /* Ocelot policer order: Serial (QoS -> Port -> VCAP) */
>+
>+struct qos_policer_conf {
>+ enum mscc_qos_rate_mode mode;
>+ bool dlb; /* Enable DLB (dual leaky bucket mode */
>+ bool cf; /* Coupling flag (ignored in SLB mode) */
>+ u32 cir; /* CIR in kbps/fps (ignored in SLB mode) */
>+ u32 cbs; /* CBS in bytes/frames (ignored in SLB mode) */
>+ u32 pir; /* PIR in kbps/fps */
>+ u32 pbs; /* PBS in bytes/frames */
>+ u8 ipg; /* Size of IPG when MSCC_QOS_RATE_MODE_LINE is chosen */
>+};
>+
>+static int qos_policer_conf_set(struct ocelot_port *port,
>+ u32 pol_ix,
>+ struct qos_policer_conf *conf)
>+{
>+ struct ocelot *ocelot = port->ocelot;
>+ u32 cir = 0, cbs = 0, pir = 0, pbs = 0;
>+ u32 cf = 0, cir_ena = 0, frm_mode = 0;
>+ u32 pbs_max = 0, cbs_max = 0;
>+ bool cir_discard = 0, pir_discard = 0;
>+ u8 ipg = 20;
>+ u32 value;
>+
>+ pir = conf->pir;
>+ pbs = conf->pbs;
>+
>+ switch (conf->mode) {
>+ case MSCC_QOS_RATE_MODE_LINE:
>+ case MSCC_QOS_RATE_MODE_DATA:
>+ if (conf->mode == MSCC_QOS_RATE_MODE_LINE) {
>+ frm_mode = POL_MODE_LINERATE;
>+ ipg = min_t(u8, GENMASK(4, 0), conf->ipg);
>+ } else {
>+ frm_mode = POL_MODE_DATARATE;
>+ }
>+ if (conf->dlb) {
>+ cir_ena = 1;
>+ cir = conf->cir;
>+ cbs = conf->cbs;
>+ if (cir == 0 && cbs == 0) {
>+ /* Discard cir frames */
>+ cir_discard = 1;
>+ } else {
>+ cir = MSCC_DIV_ROUND_UP(cir, 100);
>+ cir *= 3; /* 33 1/3 kbps */
>+ cbs = MSCC_DIV_ROUND_UP(cbs, 4096);
>+ cbs = (cbs ? cbs : 1); /* No zero burst size */
>+ cbs_max = 60; /* Limit burst size */
>+ cf = conf->cf;
>+ if (cf)
>+ pir += conf->cir;
>+ }
>+ }
>+ if (pir == 0 && pbs == 0) {
>+ /* Discard PIR frames */
>+ pir_discard = 1;
>+ } else {
>+ pir = MSCC_DIV_ROUND_UP(pir, 100);
>+ pir *= 3; /* 33 1/3 kbps */
>+ pbs = MSCC_DIV_ROUND_UP(pbs, 4096);
>+ pbs = (pbs ? pbs : 1); /* No zero burst size */
>+ pbs_max = 60; /* Limit burst size */
>+ }
>+ break;
>+ case MSCC_QOS_RATE_MODE_FRAME:
>+ if (pir >= 100) {
>+ frm_mode = POL_MODE_FRMRATE_HI;
>+ pir = MSCC_DIV_ROUND_UP(pir, 100);
>+ pir *= 3; /* 33 1/3 fps */
>+ pbs = (pbs * 10) / 328; /* 32.8 frames */
>+ pbs = (pbs ? pbs : 1); /* No zero burst size */
>+ pbs_max = GENMASK(6, 0); /* Limit burst size */
>+ } else {
>+ frm_mode = POL_MODE_FRMRATE_LO;
>+ if (pir == 0 && pbs == 0) {
>+ /* Discard all frames */
>+ pir_discard = 1;
>+ cir_discard = 1;
>+ } else {
>+ pir *= 3; /* 1/3 fps */
>+ pbs = (pbs * 10) / 3; /* 0.3 frames */
>+ pbs = (pbs ? pbs : 1); /* No zero burst size */
>+ pbs_max = 61; /* Limit burst size */
>+ }
>+ }
>+ break;
>+ default: /* MSCC_QOS_RATE_MODE_DISABLED */
>+ /* Disable policer using maximum rate and zero burst */
>+ pir = GENMASK(15, 0);
>+ pbs = 0;
>+ break;
>+ }
>+
>+ /* Limit to maximum values */
>+ pir = min_t(u32, GENMASK(15, 0), pir);
>+ cir = min_t(u32, GENMASK(15, 0), cir);
>+ pbs = min_t(u32, pbs_max, pbs);
>+ cbs = min_t(u32, cbs_max, cbs);
>+
>+ value = (ANA_POL_MODE_CFG_IPG_SIZE(ipg) |
>+ ANA_POL_MODE_CFG_FRM_MODE(frm_mode) |
>+ (cf ? ANA_POL_MODE_CFG_DLB_COUPLED : 0) |
>+ (cir_ena ? ANA_POL_MODE_CFG_CIR_ENA : 0) |
>+ ANA_POL_MODE_CFG_OVERSHOOT_ENA);
>+
>+ ocelot_write_gix(ocelot,
>+ value,
>+ ANA_POL_MODE_CFG,
>+ pol_ix);
>+
>+ ocelot_write_gix(ocelot,
>+ ANA_POL_PIR_CFG_PIR_RATE(pir) |
>+ ANA_POL_PIR_CFG_PIR_BURST(pbs),
>+ ANA_POL_PIR_CFG,
>+ pol_ix);
>+
>+ ocelot_write_gix(ocelot,
>+ (pir_discard ? GENMASK(22, 0) : 0),
>+ ANA_POL_PIR_STATE,
>+ pol_ix);
>+
>+ ocelot_write_gix(ocelot,
>+ ANA_POL_CIR_CFG_CIR_RATE(cir) |
>+ ANA_POL_CIR_CFG_CIR_BURST(cbs),
>+ ANA_POL_CIR_CFG,
>+ pol_ix);
>+
>+ ocelot_write_gix(ocelot,
>+ (cir_discard ? GENMASK(22, 0) : 0),
>+ ANA_POL_CIR_STATE,
>+ pol_ix);
I understand that you want to wrap all 5 calls in the same way. But
still, pol_ix does not have to be on a separate line.
>+
>+ return 0;
>+}
>+
>+int ocelot_port_policer_add(struct ocelot_port *port,
>+ struct tcf_police *p)
>+{
>+ struct ocelot *ocelot = port->ocelot;
>+ struct qos_policer_conf pp;
>+
>+ if (!p)
>+ return -EINVAL;
>+
>+ netdev_dbg(port->dev,
Unnecessary line wrap.
>+ "result %d ewma_rate %u burst %lld mtu %u mtu_pktoks %lld\n",
>+ p->params->tcfp_result,
>+ p->params->tcfp_ewma_rate,
>+ p->params->tcfp_burst,
>+ p->params->tcfp_mtu,
>+ p->params->tcfp_mtu_ptoks);
>+
>+ if (p->params->rate_present)
>+ netdev_dbg(port->dev,
Again, no need to wrap.
>+ "rate: rate %llu mult %u over %u link %u shift %u\n",
>+ p->params->rate.rate_bytes_ps,
>+ p->params->rate.mult,
>+ p->params->rate.overhead,
>+ p->params->rate.linklayer,
>+ p->params->rate.shift);
>+
>+ if (p->params->peak_present)
>+ netdev_dbg(port->dev,
Again, no need to wrap.
>+ "peak: rate %llu mult %u over %u link %u shift %u\n",
>+ p->params->peak.rate_bytes_ps,
>+ p->params->peak.mult,
>+ p->params->peak.overhead,
>+ p->params->peak.linklayer,
>+ p->params->peak.shift);
>+
>+ memset(&pp, 0, sizeof(pp));
>+
>+ if (p->params->tcfp_ewma_rate) {
>+ netdev_err(port->dev, "tcfp_ewma_rate is not supported\n");
>+ return -EOPNOTSUPP;
>+ }
>+ if (p->params->peak_present) {
>+ netdev_err(port->dev, "peakrate is not supported\n");
>+ return -EOPNOTSUPP;
>+ }
>+ if (!p->params->rate_present) {
>+ netdev_err(port->dev, "rate not specified\n");
>+ return -EINVAL;
>+ }
>+ if (p->params->rate.overhead) {
>+ pp.mode = MSCC_QOS_RATE_MODE_LINE;
>+ pp.ipg = p->params->rate.overhead;
>+ } else {
>+ pp.mode = MSCC_QOS_RATE_MODE_DATA;
>+ }
>+
>+ pp.pir = (u32)div_u64(p->params->rate.rate_bytes_ps, 1000) * 8;
>+ pp.pbs = tc_calc_xmitsize(p->params->rate.rate_bytes_ps,
>+ PSCHED_NS2TICKS(p->params->tcfp_burst));
>+ netdev_dbg(port->dev,
Again, no need to wrap.
>+ "%s: port %u pir %u kbps, pbs %u bytes, ipg %u bytes\n",
>+ __func__, port->chip_port, pp.pir, pp.pbs, pp.ipg);
>+
>+ MSCC_RC(qos_policer_conf_set(port, POL_IX_PORT + port->chip_port, &pp));
>+
>+ ocelot_rmw_gix(ocelot,
>+ ANA_PORT_POL_CFG_PORT_POL_ENA |
>+ ANA_PORT_POL_CFG_POL_ORDER(POL_ORDER),
>+ ANA_PORT_POL_CFG_PORT_POL_ENA |
>+ ANA_PORT_POL_CFG_POL_ORDER_M,
>+ ANA_PORT_POL_CFG,
>+ port->chip_port);
>+
>+ return 0;
>+}
>+
>+int ocelot_port_policer_del(struct ocelot_port *port)
>+{
>+ struct ocelot *ocelot = port->ocelot;
>+ struct qos_policer_conf pp;
>+
>+ netdev_dbg(port->dev, "%s: port %u\n", __func__, port->chip_port);
>+
>+ memset(&pp, 0, sizeof(pp));
>+ pp.mode = MSCC_QOS_RATE_MODE_DISABLED;
>+
>+ MSCC_RC(qos_policer_conf_set(port, POL_IX_PORT + port->chip_port, &pp));
>+
>+ ocelot_rmw_gix(ocelot, 0 |
>+ ANA_PORT_POL_CFG_POL_ORDER(POL_ORDER),
>+ ANA_PORT_POL_CFG_PORT_POL_ENA |
>+ ANA_PORT_POL_CFG_POL_ORDER_M,
>+ ANA_PORT_POL_CFG,
>+ port->chip_port);
>+
>+ return 0;
>+}
>diff --git a/drivers/net/ethernet/mscc/ocelot_police.h b/drivers/net/ethernet/mscc/ocelot_police.h
>new file mode 100644
>index 000000000000..bc4dc34c684e
>--- /dev/null
>+++ b/drivers/net/ethernet/mscc/ocelot_police.h
>@@ -0,0 +1,16 @@
>+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
>+/* Microsemi Ocelot Switch driver
>+ *
>+ * Copyright (c) 2019 Microsemi Corporation
>+ */
>+
>+#ifndef _MSCC_OCELOT_POLICE_H_
>+#define _MSCC_OCELOT_POLICE_H_
>+
>+#include <net/tc_act/tc_police.h>
>+#include "ocelot.h"
>+
>+int ocelot_port_policer_add(struct ocelot_port *port, struct tcf_police *p);
>+int ocelot_port_policer_del(struct ocelot_port *port);
>+
>+#endif /* _MSCC_OCELOT_POLICE_H_ */
>diff --git a/drivers/net/ethernet/mscc/ocelot_tc.c b/drivers/net/ethernet/mscc/ocelot_tc.c
>new file mode 100644
>index 000000000000..97b0a7bf5d06
>--- /dev/null
>+++ b/drivers/net/ethernet/mscc/ocelot_tc.c
>@@ -0,0 +1,151 @@
>+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
>+/* Microsemi Ocelot Switch TC driver
>+ *
>+ * Copyright (c) 2019 Microsemi Corporation
>+ */
>+
>+#include "ocelot_tc.h"
>+#include "ocelot_police.h"
>+#include <net/pkt_cls.h>
>+
>+static int ocelot_setup_tc_cls_matchall(struct ocelot_port *port,
>+ struct tc_cls_matchall_offload *f,
>+ bool ingress)
>+{
>+ const struct tc_action *a;
>+ int err;
>+
>+ netdev_dbg(port->dev,
Again, no need to wrap.
>+ "%s: port %u cookie %lu\n",
>+ __func__, port->chip_port, f->cookie);
>+ switch (f->command) {
>+ case TC_CLSMATCHALL_REPLACE:
>+ if (!tcf_exts_has_one_action(f->exts)) {
>+ netdev_err(port->dev, "only one action is supported\n");
>+ return -EOPNOTSUPP;
>+ }
>+
>+ a = tcf_exts_first_action(f->exts);
>+
>+ if (is_tcf_police(a)) {
>+ if (!ingress)
>+ return -EOPNOTSUPP;
>+
>+ if (port->tc.police_id &&
>+ port->tc.police_id != f->cookie) {
>+ netdev_warn(port->dev,
Again, no need to wrap.
>+ "Only one policer per port is supported\n");
>+ return -EEXIST;
>+ }
>+
>+ err = ocelot_port_policer_add(port, to_police(a));
>+ if (err) {
>+ netdev_err(port->dev, "Could not add policer\n");
>+ return err;
>+ }
>+ port->tc.police_id = f->cookie;
>+ return 0;
>+ } else {
>+ return -EOPNOTSUPP;
>+ }
>+ case TC_CLSMATCHALL_DESTROY:
>+ if (port->tc.police_id != f->cookie)
>+ return -ENOENT;
>+
>+ err = ocelot_port_policer_del(port);
>+ if (err) {
>+ netdev_err(port->dev, "Could not delete policer\n");
>+ return err;
>+ }
>+ port->tc.police_id = 0;
>+ return 0;
>+ default:
>+ return -EOPNOTSUPP;
>+ }
>+}
>+
>+static int ocelot_setup_tc_block_cb(enum tc_setup_type type,
>+ void *type_data,
>+ void *cb_priv, bool ingress)
>+{
>+ struct ocelot_port *port = cb_priv;
>+
>+ if (!tc_cls_can_offload_and_chain0(port->dev, type_data))
>+ return -EOPNOTSUPP;
>+
>+ switch (type) {
>+ case TC_SETUP_CLSMATCHALL:
>+ netdev_dbg(port->dev, "tc_block_cb: TC_SETUP_CLSMATCHALL %s\n",
>+ ingress ? "ingress" : "egress");
>+
>+ return ocelot_setup_tc_cls_matchall(port, type_data, ingress);
>+ case TC_SETUP_CLSFLOWER:
>+ netdev_dbg(port->dev, "tc_block_cb: TC_SETUP_CLSFLOWER %s\n",
>+ ingress ? "ingress" : "egress");
>+
>+ return -EOPNOTSUPP;
>+ default:
>+ netdev_dbg(port->dev, "tc_block_cb: type %d %s\n",
>+ type,
>+ ingress ? "ingress" : "egress");
>+
>+ return -EOPNOTSUPP;
>+ }
>+}
>+
>+static int ocelot_setup_tc_block_cb_ig(enum tc_setup_type type,
>+ void *type_data,
>+ void *cb_priv)
>+{
>+ return ocelot_setup_tc_block_cb(type, type_data,
Again, no need to wrap.
>+ cb_priv, true);
>+}
>+
>+static int ocelot_setup_tc_block_cb_eg(enum tc_setup_type type,
>+ void *type_data,
>+ void *cb_priv)
>+{
>+ return ocelot_setup_tc_block_cb(type, type_data,
Again, no need to wrap.
>+ cb_priv, false);
>+}
>+
>+static int ocelot_setup_tc_block(struct ocelot_port *port,
>+ struct tc_block_offload *f)
>+{
>+ tc_setup_cb_t *cb;
>+
>+ netdev_dbg(port->dev, "tc_block command %d, binder_type %d\n",
>+ f->command, f->binder_type);
>+
>+ if (f->binder_type == TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
>+ cb = ocelot_setup_tc_block_cb_ig;
>+ else if (f->binder_type == TCF_BLOCK_BINDER_TYPE_CLSACT_EGRESS)
>+ cb = ocelot_setup_tc_block_cb_eg;
>+ else
>+ return -EOPNOTSUPP;
>+
>+ switch (f->command) {
>+ case TC_BLOCK_BIND:
>+ return tcf_block_cb_register(f->block, cb, port,
>+ port, f->extack);
>+ case TC_BLOCK_UNBIND:
>+ tcf_block_cb_unregister(f->block, cb, port);
>+ return 0;
>+ default:
>+ return -EOPNOTSUPP;
>+ }
>+}
>+
>+int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type,
>+ void *type_data)
>+{
>+ struct ocelot_port *port = netdev_priv(dev);
>+
>+ switch (type) {
>+ case TC_SETUP_BLOCK:
>+ return ocelot_setup_tc_block(port, type_data);
>+ default:
>+ return -EOPNOTSUPP;
>+ }
>+ return 0;
>+}
>diff --git a/drivers/net/ethernet/mscc/ocelot_tc.h b/drivers/net/ethernet/mscc/ocelot_tc.h
>new file mode 100644
>index 000000000000..c905b98b6b4c
>--- /dev/null
>+++ b/drivers/net/ethernet/mscc/ocelot_tc.h
>@@ -0,0 +1,19 @@
>+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
>+/* Microsemi Ocelot Switch driver
>+ *
>+ * Copyright (c) 2019 Microsemi Corporation
>+ */
>+
>+#ifndef _MSCC_OCELOT_TC_H_
>+#define _MSCC_OCELOT_TC_H_
>+
>+#include <linux/netdevice.h>
>+
>+struct ocelot_port_tc {
>+ unsigned long police_id;
>+};
>+
>+int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type,
>+ void *type_data);
>+
>+#endif /* _MSCC_OCELOT_TC_H_ */
>--
>2.17.1
>
Thu, May 02, 2019 at 11:40:27AM CEST, [email protected] wrote:
>Hardware offloading a policer requires access to it's parameters.
>This is now possible by including net/tc_act/tc_police.h.
>
>Signed-off-by: Joergen Andreasen <[email protected]>
Acked-by: Jiri Pirko <[email protected]>
Hi Andrew,
The 05/02/2019 14:32, Andrew Lunn wrote:
> External E-Mail
>
>
> Hi Joergen
>
> > +
> > +#define MSCC_RC(expr) \
> > + do { \
> > + int __rc__ = (expr); \
> > + if (__rc__ < 0) \
> > + return __rc__; \
> > + } \
> > + while (0)
>
> I'm sure checkpatch warned about this. A return inside a macros is a
> bad idea. I inherited code doing this, and broke it when adding
> locking, because it was not obvious there was a return.
>
I saw the warning but I assumed that it wasn't a problem in this small context.
The macro will be removed in v2.
> > +
> > +/* The following two functions do the same as in iproute2 */
> > +#define TIME_UNITS_PER_SEC 1000000
> > +static unsigned int tc_core_tick2time(unsigned int tick)
> > +{
> > + return (tick * (u32)PSCHED_TICKS2NS(1)) / 1000;
> > +}
> > +
> > +static unsigned int tc_calc_xmitsize(u64 rate, unsigned int ticks)
> > +{
> > + return div_u64(rate * tc_core_tick2time(ticks), TIME_UNITS_PER_SEC);
> > +}
>
> Should these but put somewhere others can use them?
>
It would be nice to put them in a more public place, but I am in doubt where to
put them and what to call them.
Maybe they belong in the new file: include/net/tc_act/tc_police.h.
Would that be ok?
> > +
> > +enum mscc_qos_rate_mode {
> > + MSCC_QOS_RATE_MODE_DISABLED, /* Policer/shaper disabled */
> > + MSCC_QOS_RATE_MODE_LINE, /* Measure line rate in kbps incl. IPG */
> > + MSCC_QOS_RATE_MODE_DATA, /* Measures data rate in kbps excl. IPG */
> > + MSCC_QOS_RATE_MODE_FRAME, /* Measures frame rate in fps */
> > + __MSCC_QOS_RATE_MODE_END,
> > + NUM_MSCC_QOS_RATE_MODE = __MSCC_QOS_RATE_MODE_END,
> > + MSCC_QOS_RATE_MODE_MAX = __MSCC_QOS_RATE_MODE_END - 1,
> > +};
> > +
> > +/* Round x divided by y to nearest integer. x and y are integers */
> > +#define MSCC_ROUNDING_DIVISION(x, y) (((x) + ((y) / 2)) / (y))
>
> linux/kernel.h defines DIV_ROUND_UP(). Maybe add DIV_ROUND_DOWN()?
>
This macro is currently not used and I will remove it in v2.
> > +
> > +/* Round x divided by y to nearest higher integer. x and y are integers */
> > +#define MSCC_DIV_ROUND_UP(x, y) (((x) + (y) - 1) / (y))
>
> DIV_ROUND_UP() ?
>
I will use DIV_ROUND_UP() in v2.
> > + /* Limit to maximum values */
> > + pir = min_t(u32, GENMASK(15, 0), pir);
> > + cir = min_t(u32, GENMASK(15, 0), cir);
> > + pbs = min_t(u32, pbs_max, pbs);
> > + cbs = min_t(u32, cbs_max, cbs);
>
> If it does need to limit, maybe return -EOPNOTSUPP?
>
It seems fine to return -EOPBITSUPP here.
I will do that in v2.
> > +int ocelot_port_policer_add(struct ocelot_port *port,
> > + struct tcf_police *p)
> > +{
> > + struct ocelot *ocelot = port->ocelot;
> > + struct qos_policer_conf pp;
> > +
> > + if (!p)
> > + return -EINVAL;
> > +
> > + netdev_dbg(port->dev,
> > + "result %d ewma_rate %u burst %lld mtu %u mtu_pktoks %lld\n",
> > + p->params->tcfp_result,
> > + p->params->tcfp_ewma_rate,
> > + p->params->tcfp_burst,
> > + p->params->tcfp_mtu,
> > + p->params->tcfp_mtu_ptoks);
> > +
> > + if (p->params->rate_present)
> > + netdev_dbg(port->dev,
> > + "rate: rate %llu mult %u over %u link %u shift %u\n",
> > + p->params->rate.rate_bytes_ps,
> > + p->params->rate.mult,
> > + p->params->rate.overhead,
> > + p->params->rate.linklayer,
> > + p->params->rate.shift);
> > +
> > + if (p->params->peak_present)
> > + netdev_dbg(port->dev,
> > + "peak: rate %llu mult %u over %u link %u shift %u\n",
> > + p->params->peak.rate_bytes_ps,
> > + p->params->peak.mult,
> > + p->params->peak.overhead,
> > + p->params->peak.linklayer,
> > + p->params->peak.shift);
> > +
> > + memset(&pp, 0, sizeof(pp));
>
> Rather than memset, you can do:
>
> struct qos_policer_conf pp = { 0 };
>
I will do as you suggest in v2.
> Andrew
>
--
Joergen Andreasen, Microchip
Hi Alexandre,
The 05/02/2019 18:27, Alexandre Belloni wrote:
> External E-Mail
>
>
> Hi Joergen,
>
> On 02/05/2019 11:40:29+0200, Joergen Andreasen wrote:
> > Add default support for ingress qdisc, matchall classification
> > and police action on MSCC Ocelot.
> >
>
> This patch should be separated from the series as this doesn't have any
> dependencies and should go through the MIPS tree.
>
I will create a separate patch for this when the other patches has been
accepted.
> > Signed-off-by: Joergen Andreasen <[email protected]>
> > ---
> > arch/mips/configs/generic/board-ocelot.config | 7 +++++++
> > 1 file changed, 7 insertions(+)
> >
> > diff --git a/arch/mips/configs/generic/board-ocelot.config b/arch/mips/configs/generic/board-ocelot.config
> > index 5e53b4bc47f1..5c7360dd819c 100644
> > --- a/arch/mips/configs/generic/board-ocelot.config
> > +++ b/arch/mips/configs/generic/board-ocelot.config
> > @@ -25,6 +25,13 @@ CONFIG_SERIAL_OF_PLATFORM=y
> > CONFIG_NETDEVICES=y
> > CONFIG_NET_SWITCHDEV=y
> > CONFIG_NET_DSA=y
> > +CONFIG_NET_SCHED=y
> > +CONFIG_NET_SCH_INGRESS=y
> > +CONFIG_NET_CLS_MATCHALL=y
> > +CONFIG_NET_CLS_ACT=y
> > +CONFIG_NET_ACT_POLICE=y
> > +CONFIG_NET_ACT_GACT=y
> > +
> > CONFIG_MSCC_OCELOT_SWITCH=y
> > CONFIG_MSCC_OCELOT_SWITCH_OCELOT=y
> > CONFIG_MDIO_MSCC_MIIM=y
> > --
> > 2.17.1
> >
>
> --
> Alexandre Belloni, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
>
--
Joergen Andreasen, Microchip
Hi Jiri,
The 05/02/2019 22:36, Jiri Pirko wrote:
> External E-Mail
>
>
> Thu, May 02, 2019 at 11:40:28AM CEST, [email protected] wrote:
> >Hardware offload of port policers are now supported via the tc command.
> >Supported police parameters are: rate, burst and overhead.
>
> Interesting, you offload matchall cls, yet you don't mention it at all.
> Please, do it.
>
I will mention that we also offload matchall cls in v2.
>
> >
> >Example:
> >
> >Add:
> >tc qdisc add dev eth3 handle ffff: ingress
> >tc filter add dev eth3 parent ffff: prio 1 handle 2 \
> > matchall skip_sw \
> > action police rate 100Mbit burst 10000 overhead 20
> >
> >Show:
> >tc -s -d qdisc show dev eth3
> >tc -s -d filter show dev eth3 ingress
> >
> >Delete:
> >tc filter del dev eth3 parent ffff: prio 1
> >tc qdisc del dev eth3 handle ffff: ingress
> >
> >Signed-off-by: Joergen Andreasen <[email protected]>
> >---
> > drivers/net/ethernet/mscc/Makefile | 2 +-
> > drivers/net/ethernet/mscc/ocelot.c | 6 +-
> > drivers/net/ethernet/mscc/ocelot.h | 3 +
> > drivers/net/ethernet/mscc/ocelot_police.c | 289 ++++++++++++++++++++++
> > drivers/net/ethernet/mscc/ocelot_police.h | 16 ++
> > drivers/net/ethernet/mscc/ocelot_tc.c | 151 +++++++++++
> > drivers/net/ethernet/mscc/ocelot_tc.h | 19 ++
> > 7 files changed, 483 insertions(+), 3 deletions(-)
> > create mode 100644 drivers/net/ethernet/mscc/ocelot_police.c
> > create mode 100644 drivers/net/ethernet/mscc/ocelot_police.h
> > create mode 100644 drivers/net/ethernet/mscc/ocelot_tc.c
> > create mode 100644 drivers/net/ethernet/mscc/ocelot_tc.h
> >
> >diff --git a/drivers/net/ethernet/mscc/Makefile b/drivers/net/ethernet/mscc/Makefile
> >index cb52a3b128ae..5e694dc1f7f8 100644
> >--- a/drivers/net/ethernet/mscc/Makefile
> >+++ b/drivers/net/ethernet/mscc/Makefile
> >@@ -1,5 +1,5 @@
> > # SPDX-License-Identifier: (GPL-2.0 OR MIT)
> > obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot_common.o
> > mscc_ocelot_common-y := ocelot.o ocelot_io.o
> >-mscc_ocelot_common-y += ocelot_regs.o
> >+mscc_ocelot_common-y += ocelot_regs.o ocelot_tc.o ocelot_police.o
> > obj-$(CONFIG_MSCC_OCELOT_SWITCH_OCELOT) += ocelot_board.o
> >diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
> >index d715ef4fc92f..3ec7864d9dc8 100644
> >--- a/drivers/net/ethernet/mscc/ocelot.c
> >+++ b/drivers/net/ethernet/mscc/ocelot.c
> >@@ -943,6 +943,7 @@ static const struct net_device_ops ocelot_port_netdev_ops = {
> > .ndo_vlan_rx_kill_vid = ocelot_vlan_rx_kill_vid,
> > .ndo_set_features = ocelot_set_features,
> > .ndo_get_port_parent_id = ocelot_get_port_parent_id,
> >+ .ndo_setup_tc = ocelot_setup_tc,
> > };
> >
> > static void ocelot_get_strings(struct net_device *netdev, u32 sset, u8 *data)
> >@@ -1663,8 +1664,9 @@ int ocelot_probe_port(struct ocelot *ocelot, u8 port,
> > dev->netdev_ops = &ocelot_port_netdev_ops;
> > dev->ethtool_ops = &ocelot_ethtool_ops;
> >
> >- dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXFCS;
> >- dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
> >+ dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXFCS |
> >+ NETIF_F_HW_TC;
> >+ dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_TC;
> >
> > memcpy(dev->dev_addr, ocelot->base_mac, ETH_ALEN);
> > dev->dev_addr[ETH_ALEN - 1] += port;
> >diff --git a/drivers/net/ethernet/mscc/ocelot.h b/drivers/net/ethernet/mscc/ocelot.h
> >index ba3b3380b4d0..9514979fa075 100644
> >--- a/drivers/net/ethernet/mscc/ocelot.h
> >+++ b/drivers/net/ethernet/mscc/ocelot.h
> >@@ -22,6 +22,7 @@
> > #include "ocelot_rew.h"
> > #include "ocelot_sys.h"
> > #include "ocelot_qs.h"
> >+#include "ocelot_tc.h"
> >
> > #define PGID_AGGR 64
> > #define PGID_SRC 80
> >@@ -458,6 +459,8 @@ struct ocelot_port {
> >
> > phy_interface_t phy_mode;
> > struct phy *serdes;
> >+
> >+ struct ocelot_port_tc tc;
> > };
> >
> > u32 __ocelot_read_ix(struct ocelot *ocelot, u32 reg, u32 offset);
> >diff --git a/drivers/net/ethernet/mscc/ocelot_police.c b/drivers/net/ethernet/mscc/ocelot_police.c
> >new file mode 100644
> >index 000000000000..b40382dcc748
> >--- /dev/null
> >+++ b/drivers/net/ethernet/mscc/ocelot_police.c
> >@@ -0,0 +1,289 @@
> >+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
> >+/* Microsemi Ocelot Switch TC driver
>
> "TC driver" ? That sounds quite odd...
>
I will change it to "Microsemi Ocelot Switch driver" in v2.
>
> >+ *
> >+ * Copyright (c) 2019 Microsemi Corporation
> >+ */
> >+
> >+#include "ocelot_police.h"
> >+
> >+#define MSCC_RC(expr) \
> >+ do { \
> >+ int __rc__ = (expr); \
> >+ if (__rc__ < 0) \
> >+ return __rc__; \
> >+ } \
> >+ while (0)
>
> Please don't use macros like this.
>
This macro will be removed in v2.
>
> >+
> >+/* The following two functions do the same as in iproute2 */
> >+#define TIME_UNITS_PER_SEC 1000000
> >+static unsigned int tc_core_tick2time(unsigned int tick)
> >+{
> >+ return (tick * (u32)PSCHED_TICKS2NS(1)) / 1000;
> >+}
> >+
> >+static unsigned int tc_calc_xmitsize(u64 rate, unsigned int ticks)
> >+{
> >+ return div_u64(rate * tc_core_tick2time(ticks), TIME_UNITS_PER_SEC);
> >+}
> >+
> >+enum mscc_qos_rate_mode {
> >+ MSCC_QOS_RATE_MODE_DISABLED, /* Policer/shaper disabled */
> >+ MSCC_QOS_RATE_MODE_LINE, /* Measure line rate in kbps incl. IPG */
> >+ MSCC_QOS_RATE_MODE_DATA, /* Measures data rate in kbps excl. IPG */
> >+ MSCC_QOS_RATE_MODE_FRAME, /* Measures frame rate in fps */
> >+ __MSCC_QOS_RATE_MODE_END,
> >+ NUM_MSCC_QOS_RATE_MODE = __MSCC_QOS_RATE_MODE_END,
> >+ MSCC_QOS_RATE_MODE_MAX = __MSCC_QOS_RATE_MODE_END - 1,
> >+};
> >+
> >+/* Round x divided by y to nearest integer. x and y are integers */
> >+#define MSCC_ROUNDING_DIVISION(x, y) (((x) + ((y) / 2)) / (y))
> >+
> >+/* Round x divided by y to nearest higher integer. x and y are integers */
> >+#define MSCC_DIV_ROUND_UP(x, y) (((x) + (y) - 1) / (y))
> >+
> >+/* Types for ANA:POL[0-192]:POL_MODE_CFG.FRM_MODE */
> >+#define POL_MODE_LINERATE 0 /* Incl IPG. Unit: 33 1/3 kbps, 4096 bytes */
> >+#define POL_MODE_DATARATE 1 /* Excl IPG. Unit: 33 1/3 kbps, 4096 bytes */
> >+#define POL_MODE_FRMRATE_HI 2 /* Unit: 33 1/3 fps, 32.8 frames */
> >+#define POL_MODE_FRMRATE_LO 3 /* Unit: 1/3 fps, 0.3 frames */
> >+
> >+/* Policer indexes */
> >+#define POL_IX_PORT 0 /* 0-11 : Port policers */
> >+#define POL_IX_QUEUE 32 /* 32-127 : Queue policers */
> >+
> >+/* Default policer order */
> >+#define POL_ORDER 0x1d3 /* Ocelot policer order: Serial (QoS -> Port -> VCAP) */
> >+
> >+struct qos_policer_conf {
> >+ enum mscc_qos_rate_mode mode;
> >+ bool dlb; /* Enable DLB (dual leaky bucket mode */
> >+ bool cf; /* Coupling flag (ignored in SLB mode) */
> >+ u32 cir; /* CIR in kbps/fps (ignored in SLB mode) */
> >+ u32 cbs; /* CBS in bytes/frames (ignored in SLB mode) */
> >+ u32 pir; /* PIR in kbps/fps */
> >+ u32 pbs; /* PBS in bytes/frames */
> >+ u8 ipg; /* Size of IPG when MSCC_QOS_RATE_MODE_LINE is chosen */
> >+};
> >+
> >+static int qos_policer_conf_set(struct ocelot_port *port,
> >+ u32 pol_ix,
> >+ struct qos_policer_conf *conf)
> >+{
> >+ struct ocelot *ocelot = port->ocelot;
> >+ u32 cir = 0, cbs = 0, pir = 0, pbs = 0;
> >+ u32 cf = 0, cir_ena = 0, frm_mode = 0;
> >+ u32 pbs_max = 0, cbs_max = 0;
> >+ bool cir_discard = 0, pir_discard = 0;
> >+ u8 ipg = 20;
> >+ u32 value;
> >+
> >+ pir = conf->pir;
> >+ pbs = conf->pbs;
> >+
> >+ switch (conf->mode) {
> >+ case MSCC_QOS_RATE_MODE_LINE:
> >+ case MSCC_QOS_RATE_MODE_DATA:
> >+ if (conf->mode == MSCC_QOS_RATE_MODE_LINE) {
> >+ frm_mode = POL_MODE_LINERATE;
> >+ ipg = min_t(u8, GENMASK(4, 0), conf->ipg);
> >+ } else {
> >+ frm_mode = POL_MODE_DATARATE;
> >+ }
> >+ if (conf->dlb) {
> >+ cir_ena = 1;
> >+ cir = conf->cir;
> >+ cbs = conf->cbs;
> >+ if (cir == 0 && cbs == 0) {
> >+ /* Discard cir frames */
> >+ cir_discard = 1;
> >+ } else {
> >+ cir = MSCC_DIV_ROUND_UP(cir, 100);
> >+ cir *= 3; /* 33 1/3 kbps */
> >+ cbs = MSCC_DIV_ROUND_UP(cbs, 4096);
> >+ cbs = (cbs ? cbs : 1); /* No zero burst size */
> >+ cbs_max = 60; /* Limit burst size */
> >+ cf = conf->cf;
> >+ if (cf)
> >+ pir += conf->cir;
> >+ }
> >+ }
> >+ if (pir == 0 && pbs == 0) {
> >+ /* Discard PIR frames */
> >+ pir_discard = 1;
> >+ } else {
> >+ pir = MSCC_DIV_ROUND_UP(pir, 100);
> >+ pir *= 3; /* 33 1/3 kbps */
> >+ pbs = MSCC_DIV_ROUND_UP(pbs, 4096);
> >+ pbs = (pbs ? pbs : 1); /* No zero burst size */
> >+ pbs_max = 60; /* Limit burst size */
> >+ }
> >+ break;
> >+ case MSCC_QOS_RATE_MODE_FRAME:
> >+ if (pir >= 100) {
> >+ frm_mode = POL_MODE_FRMRATE_HI;
> >+ pir = MSCC_DIV_ROUND_UP(pir, 100);
> >+ pir *= 3; /* 33 1/3 fps */
> >+ pbs = (pbs * 10) / 328; /* 32.8 frames */
> >+ pbs = (pbs ? pbs : 1); /* No zero burst size */
> >+ pbs_max = GENMASK(6, 0); /* Limit burst size */
> >+ } else {
> >+ frm_mode = POL_MODE_FRMRATE_LO;
> >+ if (pir == 0 && pbs == 0) {
> >+ /* Discard all frames */
> >+ pir_discard = 1;
> >+ cir_discard = 1;
> >+ } else {
> >+ pir *= 3; /* 1/3 fps */
> >+ pbs = (pbs * 10) / 3; /* 0.3 frames */
> >+ pbs = (pbs ? pbs : 1); /* No zero burst size */
> >+ pbs_max = 61; /* Limit burst size */
> >+ }
> >+ }
> >+ break;
> >+ default: /* MSCC_QOS_RATE_MODE_DISABLED */
> >+ /* Disable policer using maximum rate and zero burst */
> >+ pir = GENMASK(15, 0);
> >+ pbs = 0;
> >+ break;
> >+ }
> >+
> >+ /* Limit to maximum values */
> >+ pir = min_t(u32, GENMASK(15, 0), pir);
> >+ cir = min_t(u32, GENMASK(15, 0), cir);
> >+ pbs = min_t(u32, pbs_max, pbs);
> >+ cbs = min_t(u32, cbs_max, cbs);
> >+
> >+ value = (ANA_POL_MODE_CFG_IPG_SIZE(ipg) |
> >+ ANA_POL_MODE_CFG_FRM_MODE(frm_mode) |
> >+ (cf ? ANA_POL_MODE_CFG_DLB_COUPLED : 0) |
> >+ (cir_ena ? ANA_POL_MODE_CFG_CIR_ENA : 0) |
> >+ ANA_POL_MODE_CFG_OVERSHOOT_ENA);
> >+
> >+ ocelot_write_gix(ocelot,
> >+ value,
> >+ ANA_POL_MODE_CFG,
> >+ pol_ix);
> >+
> >+ ocelot_write_gix(ocelot,
> >+ ANA_POL_PIR_CFG_PIR_RATE(pir) |
> >+ ANA_POL_PIR_CFG_PIR_BURST(pbs),
> >+ ANA_POL_PIR_CFG,
> >+ pol_ix);
> >+
> >+ ocelot_write_gix(ocelot,
> >+ (pir_discard ? GENMASK(22, 0) : 0),
> >+ ANA_POL_PIR_STATE,
> >+ pol_ix);
> >+
> >+ ocelot_write_gix(ocelot,
> >+ ANA_POL_CIR_CFG_CIR_RATE(cir) |
> >+ ANA_POL_CIR_CFG_CIR_BURST(cbs),
> >+ ANA_POL_CIR_CFG,
> >+ pol_ix);
> >+
> >+ ocelot_write_gix(ocelot,
> >+ (cir_discard ? GENMASK(22, 0) : 0),
> >+ ANA_POL_CIR_STATE,
> >+ pol_ix);
>
> I understand that you want to wrap all 5 calls in the same way. But
> still, pol_ix does not have to be on a separate line.
>
I will fix this and the folloeing unnecessary line wraps in v2.
>
> >+
> >+ return 0;
> >+}
> >+
> >+int ocelot_port_policer_add(struct ocelot_port *port,
> >+ struct tcf_police *p)
> >+{
> >+ struct ocelot *ocelot = port->ocelot;
> >+ struct qos_policer_conf pp;
> >+
> >+ if (!p)
> >+ return -EINVAL;
> >+
> >+ netdev_dbg(port->dev,
>
> Unnecessary line wrap.
>
>
> >+ "result %d ewma_rate %u burst %lld mtu %u mtu_pktoks %lld\n",
> >+ p->params->tcfp_result,
> >+ p->params->tcfp_ewma_rate,
> >+ p->params->tcfp_burst,
> >+ p->params->tcfp_mtu,
> >+ p->params->tcfp_mtu_ptoks);
> >+
> >+ if (p->params->rate_present)
> >+ netdev_dbg(port->dev,
>
> Again, no need to wrap.
>
>
> >+ "rate: rate %llu mult %u over %u link %u shift %u\n",
> >+ p->params->rate.rate_bytes_ps,
> >+ p->params->rate.mult,
> >+ p->params->rate.overhead,
> >+ p->params->rate.linklayer,
> >+ p->params->rate.shift);
> >+
> >+ if (p->params->peak_present)
> >+ netdev_dbg(port->dev,
>
> Again, no need to wrap.
>
>
> >+ "peak: rate %llu mult %u over %u link %u shift %u\n",
> >+ p->params->peak.rate_bytes_ps,
> >+ p->params->peak.mult,
> >+ p->params->peak.overhead,
> >+ p->params->peak.linklayer,
> >+ p->params->peak.shift);
> >+
> >+ memset(&pp, 0, sizeof(pp));
> >+
> >+ if (p->params->tcfp_ewma_rate) {
> >+ netdev_err(port->dev, "tcfp_ewma_rate is not supported\n");
> >+ return -EOPNOTSUPP;
> >+ }
> >+ if (p->params->peak_present) {
> >+ netdev_err(port->dev, "peakrate is not supported\n");
> >+ return -EOPNOTSUPP;
> >+ }
> >+ if (!p->params->rate_present) {
> >+ netdev_err(port->dev, "rate not specified\n");
> >+ return -EINVAL;
> >+ }
> >+ if (p->params->rate.overhead) {
> >+ pp.mode = MSCC_QOS_RATE_MODE_LINE;
> >+ pp.ipg = p->params->rate.overhead;
> >+ } else {
> >+ pp.mode = MSCC_QOS_RATE_MODE_DATA;
> >+ }
> >+
> >+ pp.pir = (u32)div_u64(p->params->rate.rate_bytes_ps, 1000) * 8;
> >+ pp.pbs = tc_calc_xmitsize(p->params->rate.rate_bytes_ps,
> >+ PSCHED_NS2TICKS(p->params->tcfp_burst));
> >+ netdev_dbg(port->dev,
>
> Again, no need to wrap.
>
>
> >+ "%s: port %u pir %u kbps, pbs %u bytes, ipg %u bytes\n",
> >+ __func__, port->chip_port, pp.pir, pp.pbs, pp.ipg);
> >+
> >+ MSCC_RC(qos_policer_conf_set(port, POL_IX_PORT + port->chip_port, &pp));
> >+
> >+ ocelot_rmw_gix(ocelot,
> >+ ANA_PORT_POL_CFG_PORT_POL_ENA |
> >+ ANA_PORT_POL_CFG_POL_ORDER(POL_ORDER),
> >+ ANA_PORT_POL_CFG_PORT_POL_ENA |
> >+ ANA_PORT_POL_CFG_POL_ORDER_M,
> >+ ANA_PORT_POL_CFG,
> >+ port->chip_port);
> >+
> >+ return 0;
> >+}
> >+
> >+int ocelot_port_policer_del(struct ocelot_port *port)
> >+{
> >+ struct ocelot *ocelot = port->ocelot;
> >+ struct qos_policer_conf pp;
> >+
> >+ netdev_dbg(port->dev, "%s: port %u\n", __func__, port->chip_port);
> >+
> >+ memset(&pp, 0, sizeof(pp));
> >+ pp.mode = MSCC_QOS_RATE_MODE_DISABLED;
> >+
> >+ MSCC_RC(qos_policer_conf_set(port, POL_IX_PORT + port->chip_port, &pp));
> >+
> >+ ocelot_rmw_gix(ocelot, 0 |
> >+ ANA_PORT_POL_CFG_POL_ORDER(POL_ORDER),
> >+ ANA_PORT_POL_CFG_PORT_POL_ENA |
> >+ ANA_PORT_POL_CFG_POL_ORDER_M,
> >+ ANA_PORT_POL_CFG,
> >+ port->chip_port);
> >+
> >+ return 0;
> >+}
> >diff --git a/drivers/net/ethernet/mscc/ocelot_police.h b/drivers/net/ethernet/mscc/ocelot_police.h
> >new file mode 100644
> >index 000000000000..bc4dc34c684e
> >--- /dev/null
> >+++ b/drivers/net/ethernet/mscc/ocelot_police.h
> >@@ -0,0 +1,16 @@
> >+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
> >+/* Microsemi Ocelot Switch driver
> >+ *
> >+ * Copyright (c) 2019 Microsemi Corporation
> >+ */
> >+
> >+#ifndef _MSCC_OCELOT_POLICE_H_
> >+#define _MSCC_OCELOT_POLICE_H_
> >+
> >+#include <net/tc_act/tc_police.h>
> >+#include "ocelot.h"
> >+
> >+int ocelot_port_policer_add(struct ocelot_port *port, struct tcf_police *p);
> >+int ocelot_port_policer_del(struct ocelot_port *port);
> >+
> >+#endif /* _MSCC_OCELOT_POLICE_H_ */
> >diff --git a/drivers/net/ethernet/mscc/ocelot_tc.c b/drivers/net/ethernet/mscc/ocelot_tc.c
> >new file mode 100644
> >index 000000000000..97b0a7bf5d06
> >--- /dev/null
> >+++ b/drivers/net/ethernet/mscc/ocelot_tc.c
> >@@ -0,0 +1,151 @@
> >+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
> >+/* Microsemi Ocelot Switch TC driver
> >+ *
> >+ * Copyright (c) 2019 Microsemi Corporation
> >+ */
> >+
> >+#include "ocelot_tc.h"
> >+#include "ocelot_police.h"
> >+#include <net/pkt_cls.h>
> >+
> >+static int ocelot_setup_tc_cls_matchall(struct ocelot_port *port,
> >+ struct tc_cls_matchall_offload *f,
> >+ bool ingress)
> >+{
> >+ const struct tc_action *a;
> >+ int err;
> >+
> >+ netdev_dbg(port->dev,
>
> Again, no need to wrap.
>
>
> >+ "%s: port %u cookie %lu\n",
> >+ __func__, port->chip_port, f->cookie);
> >+ switch (f->command) {
> >+ case TC_CLSMATCHALL_REPLACE:
> >+ if (!tcf_exts_has_one_action(f->exts)) {
> >+ netdev_err(port->dev, "only one action is supported\n");
> >+ return -EOPNOTSUPP;
> >+ }
> >+
> >+ a = tcf_exts_first_action(f->exts);
> >+
> >+ if (is_tcf_police(a)) {
> >+ if (!ingress)
> >+ return -EOPNOTSUPP;
> >+
> >+ if (port->tc.police_id &&
> >+ port->tc.police_id != f->cookie) {
> >+ netdev_warn(port->dev,
>
> Again, no need to wrap.
>
>
> >+ "Only one policer per port is supported\n");
> >+ return -EEXIST;
> >+ }
> >+
> >+ err = ocelot_port_policer_add(port, to_police(a));
> >+ if (err) {
> >+ netdev_err(port->dev, "Could not add policer\n");
> >+ return err;
> >+ }
> >+ port->tc.police_id = f->cookie;
> >+ return 0;
> >+ } else {
> >+ return -EOPNOTSUPP;
> >+ }
> >+ case TC_CLSMATCHALL_DESTROY:
> >+ if (port->tc.police_id != f->cookie)
> >+ return -ENOENT;
> >+
> >+ err = ocelot_port_policer_del(port);
> >+ if (err) {
> >+ netdev_err(port->dev, "Could not delete policer\n");
> >+ return err;
> >+ }
> >+ port->tc.police_id = 0;
> >+ return 0;
> >+ default:
> >+ return -EOPNOTSUPP;
> >+ }
> >+}
> >+
> >+static int ocelot_setup_tc_block_cb(enum tc_setup_type type,
> >+ void *type_data,
> >+ void *cb_priv, bool ingress)
> >+{
> >+ struct ocelot_port *port = cb_priv;
> >+
> >+ if (!tc_cls_can_offload_and_chain0(port->dev, type_data))
> >+ return -EOPNOTSUPP;
> >+
> >+ switch (type) {
> >+ case TC_SETUP_CLSMATCHALL:
> >+ netdev_dbg(port->dev, "tc_block_cb: TC_SETUP_CLSMATCHALL %s\n",
> >+ ingress ? "ingress" : "egress");
> >+
> >+ return ocelot_setup_tc_cls_matchall(port, type_data, ingress);
> >+ case TC_SETUP_CLSFLOWER:
> >+ netdev_dbg(port->dev, "tc_block_cb: TC_SETUP_CLSFLOWER %s\n",
> >+ ingress ? "ingress" : "egress");
> >+
> >+ return -EOPNOTSUPP;
> >+ default:
> >+ netdev_dbg(port->dev, "tc_block_cb: type %d %s\n",
> >+ type,
> >+ ingress ? "ingress" : "egress");
> >+
> >+ return -EOPNOTSUPP;
> >+ }
> >+}
> >+
> >+static int ocelot_setup_tc_block_cb_ig(enum tc_setup_type type,
> >+ void *type_data,
> >+ void *cb_priv)
> >+{
> >+ return ocelot_setup_tc_block_cb(type, type_data,
>
> Again, no need to wrap.
>
>
> >+ cb_priv, true);
> >+}
> >+
> >+static int ocelot_setup_tc_block_cb_eg(enum tc_setup_type type,
> >+ void *type_data,
> >+ void *cb_priv)
> >+{
> >+ return ocelot_setup_tc_block_cb(type, type_data,
>
> Again, no need to wrap.
>
>
> >+ cb_priv, false);
> >+}
> >+
> >+static int ocelot_setup_tc_block(struct ocelot_port *port,
> >+ struct tc_block_offload *f)
> >+{
> >+ tc_setup_cb_t *cb;
> >+
> >+ netdev_dbg(port->dev, "tc_block command %d, binder_type %d\n",
> >+ f->command, f->binder_type);
> >+
> >+ if (f->binder_type == TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
> >+ cb = ocelot_setup_tc_block_cb_ig;
> >+ else if (f->binder_type == TCF_BLOCK_BINDER_TYPE_CLSACT_EGRESS)
> >+ cb = ocelot_setup_tc_block_cb_eg;
> >+ else
> >+ return -EOPNOTSUPP;
> >+
> >+ switch (f->command) {
> >+ case TC_BLOCK_BIND:
> >+ return tcf_block_cb_register(f->block, cb, port,
> >+ port, f->extack);
> >+ case TC_BLOCK_UNBIND:
> >+ tcf_block_cb_unregister(f->block, cb, port);
> >+ return 0;
> >+ default:
> >+ return -EOPNOTSUPP;
> >+ }
> >+}
> >+
> >+int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type,
> >+ void *type_data)
> >+{
> >+ struct ocelot_port *port = netdev_priv(dev);
> >+
> >+ switch (type) {
> >+ case TC_SETUP_BLOCK:
> >+ return ocelot_setup_tc_block(port, type_data);
> >+ default:
> >+ return -EOPNOTSUPP;
> >+ }
> >+ return 0;
> >+}
> >diff --git a/drivers/net/ethernet/mscc/ocelot_tc.h b/drivers/net/ethernet/mscc/ocelot_tc.h
> >new file mode 100644
> >index 000000000000..c905b98b6b4c
> >--- /dev/null
> >+++ b/drivers/net/ethernet/mscc/ocelot_tc.h
> >@@ -0,0 +1,19 @@
> >+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
> >+/* Microsemi Ocelot Switch driver
> >+ *
> >+ * Copyright (c) 2019 Microsemi Corporation
> >+ */
> >+
> >+#ifndef _MSCC_OCELOT_TC_H_
> >+#define _MSCC_OCELOT_TC_H_
> >+
> >+#include <linux/netdevice.h>
> >+
> >+struct ocelot_port_tc {
> >+ unsigned long police_id;
> >+};
> >+
> >+int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type,
> >+ void *type_data);
> >+
> >+#endif /* _MSCC_OCELOT_TC_H_ */
> >--
> >2.17.1
> >
>
--
Joergen Andreasen, Microchip
Thu, May 02, 2019 at 11:40:28AM CEST, [email protected] wrote:
>Hardware offload of port policers are now supported via the tc command.
>Supported police parameters are: rate, burst and overhead.
Joergen, please see:
[PATCH net-next 00/13] net: act_police offload support
That patchset is also pushing flow intermediate representation for this,
so I believe that you should base this patch on top of that.
Hi Jiri,
The 05/04/2019 15:07, Jiri Pirko wrote:
> External E-Mail
>
>
> Thu, May 02, 2019 at 11:40:28AM CEST, [email protected] wrote:
> >Hardware offload of port policers are now supported via the tc command.
> >Supported police parameters are: rate, burst and overhead.
>
> Joergen, please see:
> [PATCH net-next 00/13] net: act_police offload support
> That patchset is also pushing flow intermediate representation for this,
> so I believe that you should base this patch on top of that.
>
I will base my patches on top of that.
--
Joergen Andreasen, Microchip
This patch series enables hardware offload of ingress port policing
on the MSCC ocelot board.
Changes v1 -> v2:
v2 now consists of only one patch: "[PATCH net-next v2 1/1] net: mscc: ocelot:
Implement port policers via tc command".
The patch, "[PATCH net-next 00/13] net: act_police offload support", from
Jakub Kicinski, removed the need for this patch:
"[PATCH net-next 1/3] net/sched: act_police: move police parameters".
Alexandre Belloni asked me to remove patch,
"[PATCH net-next 3/3] MIPS: generic: Add police related options to ocelot_defconfig",
from the series and instead send it through the MIPS tree and I will do that.
The remaining patch is now the only patch in this series and all suggested changes
have been incorporated.
Joergen Andreasen (1):
net: mscc: ocelot: Implement port policers via tc command
drivers/net/ethernet/mscc/Makefile | 2 +-
drivers/net/ethernet/mscc/ocelot.c | 6 +-
drivers/net/ethernet/mscc/ocelot.h | 3 +
drivers/net/ethernet/mscc/ocelot_police.c | 227 ++++++++++++++++++++++
drivers/net/ethernet/mscc/ocelot_police.h | 22 +++
drivers/net/ethernet/mscc/ocelot_tc.c | 164 ++++++++++++++++
drivers/net/ethernet/mscc/ocelot_tc.h | 19 ++
7 files changed, 440 insertions(+), 3 deletions(-)
create mode 100644 drivers/net/ethernet/mscc/ocelot_police.c
create mode 100644 drivers/net/ethernet/mscc/ocelot_police.h
create mode 100644 drivers/net/ethernet/mscc/ocelot_tc.c
create mode 100644 drivers/net/ethernet/mscc/ocelot_tc.h
--
2.17.1
Hardware offload of matchall classifier and police action are now
supported via the tc command.
Supported police parameters are: rate and burst.
Example:
Add:
tc qdisc add dev eth3 handle ffff: ingress
tc filter add dev eth3 parent ffff: prio 1 handle 2 \
matchall skip_sw \
action police rate 100Mbit burst 10000
Show:
tc -s -d qdisc show dev eth3
tc -s -d filter show dev eth3 ingress
Delete:
tc filter del dev eth3 parent ffff: prio 1
tc qdisc del dev eth3 handle ffff: ingress
Signed-off-by: Joergen Andreasen <[email protected]>
---
drivers/net/ethernet/mscc/Makefile | 2 +-
drivers/net/ethernet/mscc/ocelot.c | 6 +-
drivers/net/ethernet/mscc/ocelot.h | 3 +
drivers/net/ethernet/mscc/ocelot_police.c | 227 ++++++++++++++++++++++
drivers/net/ethernet/mscc/ocelot_police.h | 22 +++
drivers/net/ethernet/mscc/ocelot_tc.c | 164 ++++++++++++++++
drivers/net/ethernet/mscc/ocelot_tc.h | 19 ++
7 files changed, 440 insertions(+), 3 deletions(-)
create mode 100644 drivers/net/ethernet/mscc/ocelot_police.c
create mode 100644 drivers/net/ethernet/mscc/ocelot_police.h
create mode 100644 drivers/net/ethernet/mscc/ocelot_tc.c
create mode 100644 drivers/net/ethernet/mscc/ocelot_tc.h
diff --git a/drivers/net/ethernet/mscc/Makefile b/drivers/net/ethernet/mscc/Makefile
index cb52a3b128ae..5e694dc1f7f8 100644
--- a/drivers/net/ethernet/mscc/Makefile
+++ b/drivers/net/ethernet/mscc/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: (GPL-2.0 OR MIT)
obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot_common.o
mscc_ocelot_common-y := ocelot.o ocelot_io.o
-mscc_ocelot_common-y += ocelot_regs.o
+mscc_ocelot_common-y += ocelot_regs.o ocelot_tc.o ocelot_police.o
obj-$(CONFIG_MSCC_OCELOT_SWITCH_OCELOT) += ocelot_board.o
diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
index d715ef4fc92f..3ec7864d9dc8 100644
--- a/drivers/net/ethernet/mscc/ocelot.c
+++ b/drivers/net/ethernet/mscc/ocelot.c
@@ -943,6 +943,7 @@ static const struct net_device_ops ocelot_port_netdev_ops = {
.ndo_vlan_rx_kill_vid = ocelot_vlan_rx_kill_vid,
.ndo_set_features = ocelot_set_features,
.ndo_get_port_parent_id = ocelot_get_port_parent_id,
+ .ndo_setup_tc = ocelot_setup_tc,
};
static void ocelot_get_strings(struct net_device *netdev, u32 sset, u8 *data)
@@ -1663,8 +1664,9 @@ int ocelot_probe_port(struct ocelot *ocelot, u8 port,
dev->netdev_ops = &ocelot_port_netdev_ops;
dev->ethtool_ops = &ocelot_ethtool_ops;
- dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXFCS;
- dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+ dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXFCS |
+ NETIF_F_HW_TC;
+ dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_TC;
memcpy(dev->dev_addr, ocelot->base_mac, ETH_ALEN);
dev->dev_addr[ETH_ALEN - 1] += port;
diff --git a/drivers/net/ethernet/mscc/ocelot.h b/drivers/net/ethernet/mscc/ocelot.h
index ba3b3380b4d0..9514979fa075 100644
--- a/drivers/net/ethernet/mscc/ocelot.h
+++ b/drivers/net/ethernet/mscc/ocelot.h
@@ -22,6 +22,7 @@
#include "ocelot_rew.h"
#include "ocelot_sys.h"
#include "ocelot_qs.h"
+#include "ocelot_tc.h"
#define PGID_AGGR 64
#define PGID_SRC 80
@@ -458,6 +459,8 @@ struct ocelot_port {
phy_interface_t phy_mode;
struct phy *serdes;
+
+ struct ocelot_port_tc tc;
};
u32 __ocelot_read_ix(struct ocelot *ocelot, u32 reg, u32 offset);
diff --git a/drivers/net/ethernet/mscc/ocelot_police.c b/drivers/net/ethernet/mscc/ocelot_police.c
new file mode 100644
index 000000000000..701e82dd749a
--- /dev/null
+++ b/drivers/net/ethernet/mscc/ocelot_police.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/* Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2019 Microsemi Corporation
+ */
+
+#include "ocelot_police.h"
+
+enum mscc_qos_rate_mode {
+ MSCC_QOS_RATE_MODE_DISABLED, /* Policer/shaper disabled */
+ MSCC_QOS_RATE_MODE_LINE, /* Measure line rate in kbps incl. IPG */
+ MSCC_QOS_RATE_MODE_DATA, /* Measures data rate in kbps excl. IPG */
+ MSCC_QOS_RATE_MODE_FRAME, /* Measures frame rate in fps */
+ __MSCC_QOS_RATE_MODE_END,
+ NUM_MSCC_QOS_RATE_MODE = __MSCC_QOS_RATE_MODE_END,
+ MSCC_QOS_RATE_MODE_MAX = __MSCC_QOS_RATE_MODE_END - 1,
+};
+
+/* Types for ANA:POL[0-192]:POL_MODE_CFG.FRM_MODE */
+#define POL_MODE_LINERATE 0 /* Incl IPG. Unit: 33 1/3 kbps, 4096 bytes */
+#define POL_MODE_DATARATE 1 /* Excl IPG. Unit: 33 1/3 kbps, 4096 bytes */
+#define POL_MODE_FRMRATE_HI 2 /* Unit: 33 1/3 fps, 32.8 frames */
+#define POL_MODE_FRMRATE_LO 3 /* Unit: 1/3 fps, 0.3 frames */
+
+/* Policer indexes */
+#define POL_IX_PORT 0 /* 0-11 : Port policers */
+#define POL_IX_QUEUE 32 /* 32-127 : Queue policers */
+
+/* Default policer order */
+#define POL_ORDER 0x1d3 /* Ocelot policer order: Serial (QoS -> Port -> VCAP) */
+
+struct qos_policer_conf {
+ enum mscc_qos_rate_mode mode;
+ bool dlb; /* Enable DLB (dual leaky bucket mode */
+ bool cf; /* Coupling flag (ignored in SLB mode) */
+ u32 cir; /* CIR in kbps/fps (ignored in SLB mode) */
+ u32 cbs; /* CBS in bytes/frames (ignored in SLB mode) */
+ u32 pir; /* PIR in kbps/fps */
+ u32 pbs; /* PBS in bytes/frames */
+ u8 ipg; /* Size of IPG when MSCC_QOS_RATE_MODE_LINE is chosen */
+};
+
+static int qos_policer_conf_set(struct ocelot_port *port, u32 pol_ix,
+ struct qos_policer_conf *conf)
+{
+ u32 cf = 0, cir_ena = 0, frm_mode = POL_MODE_LINERATE;
+ u32 cir = 0, cbs = 0, pir = 0, pbs = 0;
+ bool cir_discard = 0, pir_discard = 0;
+ struct ocelot *ocelot = port->ocelot;
+ u32 pbs_max = 0, cbs_max = 0;
+ u8 ipg = 20;
+ u32 value;
+
+ pir = conf->pir;
+ pbs = conf->pbs;
+
+ switch (conf->mode) {
+ case MSCC_QOS_RATE_MODE_LINE:
+ case MSCC_QOS_RATE_MODE_DATA:
+ if (conf->mode == MSCC_QOS_RATE_MODE_LINE) {
+ frm_mode = POL_MODE_LINERATE;
+ ipg = min_t(u8, GENMASK(4, 0), conf->ipg);
+ } else {
+ frm_mode = POL_MODE_DATARATE;
+ }
+ if (conf->dlb) {
+ cir_ena = 1;
+ cir = conf->cir;
+ cbs = conf->cbs;
+ if (cir == 0 && cbs == 0) {
+ /* Discard cir frames */
+ cir_discard = 1;
+ } else {
+ cir = DIV_ROUND_UP(cir, 100);
+ cir *= 3; /* 33 1/3 kbps */
+ cbs = DIV_ROUND_UP(cbs, 4096);
+ cbs = (cbs ? cbs : 1); /* No zero burst size */
+ cbs_max = 60; /* Limit burst size */
+ cf = conf->cf;
+ if (cf)
+ pir += conf->cir;
+ }
+ }
+ if (pir == 0 && pbs == 0) {
+ /* Discard PIR frames */
+ pir_discard = 1;
+ } else {
+ pir = DIV_ROUND_UP(pir, 100);
+ pir *= 3; /* 33 1/3 kbps */
+ pbs = DIV_ROUND_UP(pbs, 4096);
+ pbs = (pbs ? pbs : 1); /* No zero burst size */
+ pbs_max = 60; /* Limit burst size */
+ }
+ break;
+ case MSCC_QOS_RATE_MODE_FRAME:
+ if (pir >= 100) {
+ frm_mode = POL_MODE_FRMRATE_HI;
+ pir = DIV_ROUND_UP(pir, 100);
+ pir *= 3; /* 33 1/3 fps */
+ pbs = (pbs * 10) / 328; /* 32.8 frames */
+ pbs = (pbs ? pbs : 1); /* No zero burst size */
+ pbs_max = GENMASK(6, 0); /* Limit burst size */
+ } else {
+ frm_mode = POL_MODE_FRMRATE_LO;
+ if (pir == 0 && pbs == 0) {
+ /* Discard all frames */
+ pir_discard = 1;
+ cir_discard = 1;
+ } else {
+ pir *= 3; /* 1/3 fps */
+ pbs = (pbs * 10) / 3; /* 0.3 frames */
+ pbs = (pbs ? pbs : 1); /* No zero burst size */
+ pbs_max = 61; /* Limit burst size */
+ }
+ }
+ break;
+ default: /* MSCC_QOS_RATE_MODE_DISABLED */
+ /* Disable policer using maximum rate and zero burst */
+ pir = GENMASK(15, 0);
+ pbs = 0;
+ break;
+ }
+
+ /* Check limits */
+ if (pir > GENMASK(15, 0)) {
+ netdev_err(port->dev, "Invalid pir\n");
+ return -EINVAL;
+ }
+
+ if (cir > GENMASK(15, 0)) {
+ netdev_err(port->dev, "Invalid cir\n");
+ return -EINVAL;
+ }
+
+ if (pbs > pbs_max) {
+ netdev_err(port->dev, "Invalid pbs\n");
+ return -EINVAL;
+ }
+
+ if (cbs > cbs_max) {
+ netdev_err(port->dev, "Invalid cbs\n");
+ return -EINVAL;
+ }
+
+ value = (ANA_POL_MODE_CFG_IPG_SIZE(ipg) |
+ ANA_POL_MODE_CFG_FRM_MODE(frm_mode) |
+ (cf ? ANA_POL_MODE_CFG_DLB_COUPLED : 0) |
+ (cir_ena ? ANA_POL_MODE_CFG_CIR_ENA : 0) |
+ ANA_POL_MODE_CFG_OVERSHOOT_ENA);
+
+ ocelot_write_gix(ocelot, value, ANA_POL_MODE_CFG, pol_ix);
+
+ ocelot_write_gix(ocelot,
+ ANA_POL_PIR_CFG_PIR_RATE(pir) |
+ ANA_POL_PIR_CFG_PIR_BURST(pbs),
+ ANA_POL_PIR_CFG, pol_ix);
+
+ ocelot_write_gix(ocelot,
+ (pir_discard ? GENMASK(22, 0) : 0),
+ ANA_POL_PIR_STATE, pol_ix);
+
+ ocelot_write_gix(ocelot,
+ ANA_POL_CIR_CFG_CIR_RATE(cir) |
+ ANA_POL_CIR_CFG_CIR_BURST(cbs),
+ ANA_POL_CIR_CFG, pol_ix);
+
+ ocelot_write_gix(ocelot,
+ (cir_discard ? GENMASK(22, 0) : 0),
+ ANA_POL_CIR_STATE, pol_ix);
+
+ return 0;
+}
+
+int ocelot_port_policer_add(struct ocelot_port *port,
+ struct ocelot_policer *pol)
+{
+ struct ocelot *ocelot = port->ocelot;
+ struct qos_policer_conf pp = { 0 };
+ int err;
+
+ if (!pol)
+ return -EINVAL;
+
+ pp.mode = MSCC_QOS_RATE_MODE_DATA;
+ pp.pir = pol->rate;
+ pp.pbs = pol->burst;
+
+ netdev_dbg(port->dev,
+ "%s: port %u pir %u kbps, pbs %u bytes\n",
+ __func__, port->chip_port, pp.pir, pp.pbs);
+
+ err = qos_policer_conf_set(port, POL_IX_PORT + port->chip_port, &pp);
+ if (err)
+ return err;
+
+ ocelot_rmw_gix(ocelot,
+ ANA_PORT_POL_CFG_PORT_POL_ENA |
+ ANA_PORT_POL_CFG_POL_ORDER(POL_ORDER),
+ ANA_PORT_POL_CFG_PORT_POL_ENA |
+ ANA_PORT_POL_CFG_POL_ORDER_M,
+ ANA_PORT_POL_CFG, port->chip_port);
+
+ return 0;
+}
+
+int ocelot_port_policer_del(struct ocelot_port *port)
+{
+ struct ocelot *ocelot = port->ocelot;
+ struct qos_policer_conf pp = { 0 };
+ int err;
+
+ netdev_dbg(port->dev, "%s: port %u\n", __func__, port->chip_port);
+
+ pp.mode = MSCC_QOS_RATE_MODE_DISABLED;
+
+ err = qos_policer_conf_set(port, POL_IX_PORT + port->chip_port, &pp);
+ if (err)
+ return err;
+
+ ocelot_rmw_gix(ocelot,
+ ANA_PORT_POL_CFG_POL_ORDER(POL_ORDER),
+ ANA_PORT_POL_CFG_PORT_POL_ENA |
+ ANA_PORT_POL_CFG_POL_ORDER_M,
+ ANA_PORT_POL_CFG, port->chip_port);
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/mscc/ocelot_police.h b/drivers/net/ethernet/mscc/ocelot_police.h
new file mode 100644
index 000000000000..d1137f79efda
--- /dev/null
+++ b/drivers/net/ethernet/mscc/ocelot_police.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/* Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2019 Microsemi Corporation
+ */
+
+#ifndef _MSCC_OCELOT_POLICE_H_
+#define _MSCC_OCELOT_POLICE_H_
+
+#include "ocelot.h"
+
+struct ocelot_policer {
+ u32 rate; /* kilobit per second */
+ u32 burst; /* bytes */
+};
+
+int ocelot_port_policer_add(struct ocelot_port *port,
+ struct ocelot_policer *pol);
+
+int ocelot_port_policer_del(struct ocelot_port *port);
+
+#endif /* _MSCC_OCELOT_POLICE_H_ */
diff --git a/drivers/net/ethernet/mscc/ocelot_tc.c b/drivers/net/ethernet/mscc/ocelot_tc.c
new file mode 100644
index 000000000000..2412e0dbc267
--- /dev/null
+++ b/drivers/net/ethernet/mscc/ocelot_tc.c
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/* Microsemi Ocelot Switch TC driver
+ *
+ * Copyright (c) 2019 Microsemi Corporation
+ */
+
+#include "ocelot_tc.h"
+#include "ocelot_police.h"
+#include <net/pkt_cls.h>
+
+static int ocelot_setup_tc_cls_matchall(struct ocelot_port *port,
+ struct tc_cls_matchall_offload *f,
+ bool ingress)
+{
+ struct netlink_ext_ack *extack = f->common.extack;
+ struct ocelot_policer pol = { 0 };
+ struct flow_action_entry *action;
+ int err;
+
+ netdev_dbg(port->dev, "%s: port %u command %d cookie %lu\n",
+ __func__, port->chip_port, f->command, f->cookie);
+
+ if (!ingress) {
+ NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported");
+ return -EOPNOTSUPP;
+ }
+
+ switch (f->command) {
+ case TC_CLSMATCHALL_REPLACE:
+ if (!flow_offload_has_one_action(&f->rule->action)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Only one action is supported");
+ return -EOPNOTSUPP;
+ }
+
+ action = &f->rule->action.entries[0];
+
+ if (action->id != FLOW_ACTION_POLICE) {
+ NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
+ return -EOPNOTSUPP;
+ }
+
+ if (port->tc.police_id && port->tc.police_id != f->cookie) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Only one policer per port is supported\n");
+ return -EEXIST;
+ }
+
+ pol.rate = (u32)div_u64(action->police.rate_bytes_ps, 1000) * 8;
+ pol.burst = (u32)div_u64(action->police.rate_bytes_ps *
+ PSCHED_NS2TICKS(action->police.burst),
+ PSCHED_TICKS_PER_SEC);
+
+ err = ocelot_port_policer_add(port, &pol);
+ if (err) {
+ NL_SET_ERR_MSG_MOD(extack, "Could not add policer\n");
+ return err;
+ }
+
+ port->tc.police_id = f->cookie;
+ return 0;
+ case TC_CLSMATCHALL_DESTROY:
+ if (port->tc.police_id != f->cookie)
+ return -ENOENT;
+
+ err = ocelot_port_policer_del(port);
+ if (err) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Could not delete policer\n");
+ return err;
+ }
+ port->tc.police_id = 0;
+ return 0;
+ case TC_CLSMATCHALL_STATS: /* fall through */
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ocelot_setup_tc_block_cb(enum tc_setup_type type,
+ void *type_data,
+ void *cb_priv, bool ingress)
+{
+ struct ocelot_port *port = cb_priv;
+
+ if (!tc_cls_can_offload_and_chain0(port->dev, type_data))
+ return -EOPNOTSUPP;
+
+ switch (type) {
+ case TC_SETUP_CLSMATCHALL:
+ netdev_dbg(port->dev, "tc_block_cb: TC_SETUP_CLSMATCHALL %s\n",
+ ingress ? "ingress" : "egress");
+
+ return ocelot_setup_tc_cls_matchall(port, type_data, ingress);
+ case TC_SETUP_CLSFLOWER:
+ netdev_dbg(port->dev, "tc_block_cb: TC_SETUP_CLSFLOWER %s\n",
+ ingress ? "ingress" : "egress");
+
+ return -EOPNOTSUPP;
+ default:
+ netdev_dbg(port->dev, "tc_block_cb: type %d %s\n",
+ type,
+ ingress ? "ingress" : "egress");
+
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ocelot_setup_tc_block_cb_ig(enum tc_setup_type type,
+ void *type_data,
+ void *cb_priv)
+{
+ return ocelot_setup_tc_block_cb(type, type_data,
+ cb_priv, true);
+}
+
+static int ocelot_setup_tc_block_cb_eg(enum tc_setup_type type,
+ void *type_data,
+ void *cb_priv)
+{
+ return ocelot_setup_tc_block_cb(type, type_data,
+ cb_priv, false);
+}
+
+static int ocelot_setup_tc_block(struct ocelot_port *port,
+ struct tc_block_offload *f)
+{
+ tc_setup_cb_t *cb;
+
+ netdev_dbg(port->dev, "tc_block command %d, binder_type %d\n",
+ f->command, f->binder_type);
+
+ if (f->binder_type == TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
+ cb = ocelot_setup_tc_block_cb_ig;
+ else if (f->binder_type == TCF_BLOCK_BINDER_TYPE_CLSACT_EGRESS)
+ cb = ocelot_setup_tc_block_cb_eg;
+ else
+ return -EOPNOTSUPP;
+
+ switch (f->command) {
+ case TC_BLOCK_BIND:
+ return tcf_block_cb_register(f->block, cb, port,
+ port, f->extack);
+ case TC_BLOCK_UNBIND:
+ tcf_block_cb_unregister(f->block, cb, port);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type,
+ void *type_data)
+{
+ struct ocelot_port *port = netdev_priv(dev);
+
+ switch (type) {
+ case TC_SETUP_BLOCK:
+ return ocelot_setup_tc_block(port, type_data);
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
diff --git a/drivers/net/ethernet/mscc/ocelot_tc.h b/drivers/net/ethernet/mscc/ocelot_tc.h
new file mode 100644
index 000000000000..c905b98b6b4c
--- /dev/null
+++ b/drivers/net/ethernet/mscc/ocelot_tc.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/* Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2019 Microsemi Corporation
+ */
+
+#ifndef _MSCC_OCELOT_TC_H_
+#define _MSCC_OCELOT_TC_H_
+
+#include <linux/netdevice.h>
+
+struct ocelot_port_tc {
+ unsigned long police_id;
+};
+
+int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type,
+ void *type_data);
+
+#endif /* _MSCC_OCELOT_TC_H_ */
--
2.17.1
On Thu, 23 May 2019 12:49:39 +0200, Joergen Andreasen wrote:
> Hardware offload of matchall classifier and police action are now
> supported via the tc command.
> Supported police parameters are: rate and burst.
>
> Example:
>
> Add:
> tc qdisc add dev eth3 handle ffff: ingress
> tc filter add dev eth3 parent ffff: prio 1 handle 2 \
> matchall skip_sw \
> action police rate 100Mbit burst 10000
>
> Show:
> tc -s -d qdisc show dev eth3
> tc -s -d filter show dev eth3 ingress
>
> Delete:
> tc filter del dev eth3 parent ffff: prio 1
> tc qdisc del dev eth3 handle ffff: ingress
>
> Signed-off-by: Joergen Andreasen <[email protected]>
> diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
> index d715ef4fc92f..3ec7864d9dc8 100644
> --- a/drivers/net/ethernet/mscc/ocelot.c
> +++ b/drivers/net/ethernet/mscc/ocelot.c
> @@ -943,6 +943,7 @@ static const struct net_device_ops ocelot_port_netdev_ops = {
> .ndo_vlan_rx_kill_vid = ocelot_vlan_rx_kill_vid,
> .ndo_set_features = ocelot_set_features,
> .ndo_get_port_parent_id = ocelot_get_port_parent_id,
> + .ndo_setup_tc = ocelot_setup_tc,
> };
>
> static void ocelot_get_strings(struct net_device *netdev, u32 sset, u8 *data)
> @@ -1663,8 +1664,9 @@ int ocelot_probe_port(struct ocelot *ocelot, u8 port,
> dev->netdev_ops = &ocelot_port_netdev_ops;
> dev->ethtool_ops = &ocelot_ethtool_ops;
>
> - dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXFCS;
> - dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
> + dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXFCS |
> + NETIF_F_HW_TC;
> + dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_TC;
>
> memcpy(dev->dev_addr, ocelot->base_mac, ETH_ALEN);
> dev->dev_addr[ETH_ALEN - 1] += port;
You need to add a check in set_features to make sure nobody clears the
NETIF_F_TC flag while something is offloaded, otherwise you will miss
the REMOVE callback (it will bounce from the
tc_cls_can_offload_and_chain0() check).
> diff --git a/drivers/net/ethernet/mscc/ocelot_tc.c b/drivers/net/ethernet/mscc/ocelot_tc.c
> new file mode 100644
> index 000000000000..2412e0dbc267
> --- /dev/null
> +++ b/drivers/net/ethernet/mscc/ocelot_tc.c
> @@ -0,0 +1,164 @@
> +// SPDX-License-Identifier: (GPL-2.0 OR MIT)
> +/* Microsemi Ocelot Switch TC driver
> + *
> + * Copyright (c) 2019 Microsemi Corporation
> + */
> +
> +#include "ocelot_tc.h"
> +#include "ocelot_police.h"
> +#include <net/pkt_cls.h>
> +
> +static int ocelot_setup_tc_cls_matchall(struct ocelot_port *port,
> + struct tc_cls_matchall_offload *f,
> + bool ingress)
> +{
> + struct netlink_ext_ack *extack = f->common.extack;
> + struct ocelot_policer pol = { 0 };
> + struct flow_action_entry *action;
> + int err;
> +
> + netdev_dbg(port->dev, "%s: port %u command %d cookie %lu\n",
> + __func__, port->chip_port, f->command, f->cookie);
> +
> + if (!ingress) {
> + NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported");
> + return -EOPNOTSUPP;
> + }
> +
> + switch (f->command) {
> + case TC_CLSMATCHALL_REPLACE:
> + if (!flow_offload_has_one_action(&f->rule->action)) {
> + NL_SET_ERR_MSG_MOD(extack,
> + "Only one action is supported");
> + return -EOPNOTSUPP;
> + }
> +
> + action = &f->rule->action.entries[0];
> +
> + if (action->id != FLOW_ACTION_POLICE) {
> + NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
> + return -EOPNOTSUPP;
> + }
Please also reject the offload if block is shared, as HW policer state
cannot be shared between ports, the way it is in SW. You have to save
whether the block is shared or not at bind time, see:
d6787147e15d ("net/sched: remove block pointer from common offload structure")
> + if (port->tc.police_id && port->tc.police_id != f->cookie) {
> + NL_SET_ERR_MSG_MOD(extack,
> + "Only one policer per port is supported\n");
> + return -EEXIST;
> + }
> +
> + pol.rate = (u32)div_u64(action->police.rate_bytes_ps, 1000) * 8;
> + pol.burst = (u32)div_u64(action->police.rate_bytes_ps *
> + PSCHED_NS2TICKS(action->police.burst),
> + PSCHED_TICKS_PER_SEC);
> +
> + err = ocelot_port_policer_add(port, &pol);
> + if (err) {
> + NL_SET_ERR_MSG_MOD(extack, "Could not add policer\n");
> + return err;
> + }
> +
> + port->tc.police_id = f->cookie;
> + return 0;
> + case TC_CLSMATCHALL_DESTROY:
> + if (port->tc.police_id != f->cookie)
> + return -ENOENT;
> +
> + err = ocelot_port_policer_del(port);
> + if (err) {
> + NL_SET_ERR_MSG_MOD(extack,
> + "Could not delete policer\n");
> + return err;
> + }
> + port->tc.police_id = 0;
> + return 0;
> + case TC_CLSMATCHALL_STATS: /* fall through */
> + default:
> + return -EOPNOTSUPP;
> + }
> +}
Hi Jakub,
The 05/23/2019 11:56, Jakub Kicinski wrote:
> External E-Mail
>
>
> On Thu, 23 May 2019 12:49:39 +0200, Joergen Andreasen wrote:
> > Hardware offload of matchall classifier and police action are now
> > supported via the tc command.
> > Supported police parameters are: rate and burst.
> >
> > Example:
> >
> > Add:
> > tc qdisc add dev eth3 handle ffff: ingress
> > tc filter add dev eth3 parent ffff: prio 1 handle 2 \
> > matchall skip_sw \
> > action police rate 100Mbit burst 10000
> >
> > Show:
> > tc -s -d qdisc show dev eth3
> > tc -s -d filter show dev eth3 ingress
> >
> > Delete:
> > tc filter del dev eth3 parent ffff: prio 1
> > tc qdisc del dev eth3 handle ffff: ingress
> >
> > Signed-off-by: Joergen Andreasen <[email protected]>
>
> > diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
> > index d715ef4fc92f..3ec7864d9dc8 100644
> > --- a/drivers/net/ethernet/mscc/ocelot.c
> > +++ b/drivers/net/ethernet/mscc/ocelot.c
> > @@ -943,6 +943,7 @@ static const struct net_device_ops ocelot_port_netdev_ops = {
> > .ndo_vlan_rx_kill_vid = ocelot_vlan_rx_kill_vid,
> > .ndo_set_features = ocelot_set_features,
> > .ndo_get_port_parent_id = ocelot_get_port_parent_id,
> > + .ndo_setup_tc = ocelot_setup_tc,
> > };
> >
> > static void ocelot_get_strings(struct net_device *netdev, u32 sset, u8 *data)
> > @@ -1663,8 +1664,9 @@ int ocelot_probe_port(struct ocelot *ocelot, u8 port,
> > dev->netdev_ops = &ocelot_port_netdev_ops;
> > dev->ethtool_ops = &ocelot_ethtool_ops;
> >
> > - dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXFCS;
> > - dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
> > + dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXFCS |
> > + NETIF_F_HW_TC;
> > + dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_TC;
> >
> > memcpy(dev->dev_addr, ocelot->base_mac, ETH_ALEN);
> > dev->dev_addr[ETH_ALEN - 1] += port;
>
> You need to add a check in set_features to make sure nobody clears the
> NETIF_F_TC flag while something is offloaded, otherwise you will miss
> the REMOVE callback (it will bounce from the
> tc_cls_can_offload_and_chain0() check).
I will add this check in v3
>
> > diff --git a/drivers/net/ethernet/mscc/ocelot_tc.c b/drivers/net/ethernet/mscc/ocelot_tc.c
> > new file mode 100644
> > index 000000000000..2412e0dbc267
> > --- /dev/null
> > +++ b/drivers/net/ethernet/mscc/ocelot_tc.c
> > @@ -0,0 +1,164 @@
> > +// SPDX-License-Identifier: (GPL-2.0 OR MIT)
> > +/* Microsemi Ocelot Switch TC driver
> > + *
> > + * Copyright (c) 2019 Microsemi Corporation
> > + */
> > +
> > +#include "ocelot_tc.h"
> > +#include "ocelot_police.h"
> > +#include <net/pkt_cls.h>
> > +
> > +static int ocelot_setup_tc_cls_matchall(struct ocelot_port *port,
> > + struct tc_cls_matchall_offload *f,
> > + bool ingress)
> > +{
> > + struct netlink_ext_ack *extack = f->common.extack;
> > + struct ocelot_policer pol = { 0 };
> > + struct flow_action_entry *action;
> > + int err;
> > +
> > + netdev_dbg(port->dev, "%s: port %u command %d cookie %lu\n",
> > + __func__, port->chip_port, f->command, f->cookie);
> > +
> > + if (!ingress) {
> > + NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported");
> > + return -EOPNOTSUPP;
> > + }
> > +
> > + switch (f->command) {
> > + case TC_CLSMATCHALL_REPLACE:
> > + if (!flow_offload_has_one_action(&f->rule->action)) {
> > + NL_SET_ERR_MSG_MOD(extack,
> > + "Only one action is supported");
> > + return -EOPNOTSUPP;
> > + }
> > +
> > + action = &f->rule->action.entries[0];
> > +
> > + if (action->id != FLOW_ACTION_POLICE) {
> > + NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
> > + return -EOPNOTSUPP;
> > + }
>
> Please also reject the offload if block is shared, as HW policer state
> cannot be shared between ports, the way it is in SW. You have to save
> whether the block is shared or not at bind time, see:
>
> d6787147e15d ("net/sched: remove block pointer from common offload structure")
I will fix this in v3.
>
> > + if (port->tc.police_id && port->tc.police_id != f->cookie) {
> > + NL_SET_ERR_MSG_MOD(extack,
> > + "Only one policer per port is supported\n");
> > + return -EEXIST;
> > + }
> > +
> > + pol.rate = (u32)div_u64(action->police.rate_bytes_ps, 1000) * 8;
> > + pol.burst = (u32)div_u64(action->police.rate_bytes_ps *
> > + PSCHED_NS2TICKS(action->police.burst),
> > + PSCHED_TICKS_PER_SEC);
> > +
> > + err = ocelot_port_policer_add(port, &pol);
> > + if (err) {
> > + NL_SET_ERR_MSG_MOD(extack, "Could not add policer\n");
> > + return err;
> > + }
> > +
> > + port->tc.police_id = f->cookie;
> > + return 0;
> > + case TC_CLSMATCHALL_DESTROY:
> > + if (port->tc.police_id != f->cookie)
> > + return -ENOENT;
> > +
> > + err = ocelot_port_policer_del(port);
> > + if (err) {
> > + NL_SET_ERR_MSG_MOD(extack,
> > + "Could not delete policer\n");
> > + return err;
> > + }
> > + port->tc.police_id = 0;
> > + return 0;
> > + case TC_CLSMATCHALL_STATS: /* fall through */
> > + default:
> > + return -EOPNOTSUPP;
> > + }
> > +}
>
--
Joergen Andreasen, Microchip
This patch series enables hardware offload of ingress port policing
on the MSCC ocelot board.
Changes v2 -> v3:
v3 now incorporates the following changes suggested by Jakub Kicinski:
- Add a check in ndo_set_features() in order to prevent users to clear the
NETIF_F_HW_TC flag while offload is active.
- Reject the offload if the block is shared.
Changes v1 -> v2:
v2 now consists of only one patch: "[PATCH net-next v2 1/1] net: mscc:
ocelot: Implement port policers via tc command".
The patch, "[PATCH net-next 00/13] net: act_police offload support", from
Jakub Kicinski, removed the need for this patch:
"[PATCH net-next 1/3] net/sched: act_police: move police parameters".
Alexandre Belloni asked me to remove patch,
"[PATCH net-next 3/3] MIPS: generic: Add police related options to
ocelot_defconfig", from the series and instead send it through the MIPS
tree and I will do that.
The remaining patch is now the only patch in this series and all suggested
changes have been incorporated.
Joergen Andreasen (1):
net: mscc: ocelot: Implement port policers via tc command
drivers/net/ethernet/mscc/Makefile | 2 +-
drivers/net/ethernet/mscc/ocelot.c | 13 +-
drivers/net/ethernet/mscc/ocelot.h | 3 +
drivers/net/ethernet/mscc/ocelot_police.c | 227 ++++++++++++++++++++++
drivers/net/ethernet/mscc/ocelot_police.h | 22 +++
drivers/net/ethernet/mscc/ocelot_tc.c | 174 +++++++++++++++++
drivers/net/ethernet/mscc/ocelot_tc.h | 22 +++
7 files changed, 460 insertions(+), 3 deletions(-)
create mode 100644 drivers/net/ethernet/mscc/ocelot_police.c
create mode 100644 drivers/net/ethernet/mscc/ocelot_police.h
create mode 100644 drivers/net/ethernet/mscc/ocelot_tc.c
create mode 100644 drivers/net/ethernet/mscc/ocelot_tc.h
--
2.17.1
Hardware offload of matchall classifier and police action are now
supported via the tc command.
Supported police parameters are: rate and burst.
Example:
Add:
tc qdisc add dev eth3 handle ffff: ingress
tc filter add dev eth3 parent ffff: prio 1 handle 2 \
matchall skip_sw \
action police rate 100Mbit burst 10000
Show:
tc -s -d qdisc show dev eth3
tc -s -d filter show dev eth3 ingress
Delete:
tc filter del dev eth3 parent ffff: prio 1
tc qdisc del dev eth3 handle ffff: ingress
Signed-off-by: Joergen Andreasen <[email protected]>
---
drivers/net/ethernet/mscc/Makefile | 2 +-
drivers/net/ethernet/mscc/ocelot.c | 13 +-
drivers/net/ethernet/mscc/ocelot.h | 3 +
drivers/net/ethernet/mscc/ocelot_police.c | 227 ++++++++++++++++++++++
drivers/net/ethernet/mscc/ocelot_police.h | 22 +++
drivers/net/ethernet/mscc/ocelot_tc.c | 174 +++++++++++++++++
drivers/net/ethernet/mscc/ocelot_tc.h | 22 +++
7 files changed, 460 insertions(+), 3 deletions(-)
create mode 100644 drivers/net/ethernet/mscc/ocelot_police.c
create mode 100644 drivers/net/ethernet/mscc/ocelot_police.h
create mode 100644 drivers/net/ethernet/mscc/ocelot_tc.c
create mode 100644 drivers/net/ethernet/mscc/ocelot_tc.h
diff --git a/drivers/net/ethernet/mscc/Makefile b/drivers/net/ethernet/mscc/Makefile
index cb52a3b128ae..5e694dc1f7f8 100644
--- a/drivers/net/ethernet/mscc/Makefile
+++ b/drivers/net/ethernet/mscc/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: (GPL-2.0 OR MIT)
obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot_common.o
mscc_ocelot_common-y := ocelot.o ocelot_io.o
-mscc_ocelot_common-y += ocelot_regs.o
+mscc_ocelot_common-y += ocelot_regs.o ocelot_tc.o ocelot_police.o
obj-$(CONFIG_MSCC_OCELOT_SWITCH_OCELOT) += ocelot_board.o
diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
index d715ef4fc92f..ab7d9eba6a32 100644
--- a/drivers/net/ethernet/mscc/ocelot.c
+++ b/drivers/net/ethernet/mscc/ocelot.c
@@ -910,6 +910,13 @@ static int ocelot_set_features(struct net_device *dev,
struct ocelot_port *port = netdev_priv(dev);
netdev_features_t changed = dev->features ^ features;
+ if ((dev->features & NETIF_F_HW_TC) > (features & NETIF_F_HW_TC) &&
+ port->tc.offload_cnt) {
+ netdev_err(dev,
+ "Cannot disable HW TC offload while offloads active\n");
+ return -EBUSY;
+ }
+
if (changed & NETIF_F_HW_VLAN_CTAG_FILTER)
ocelot_vlan_mode(port, features);
@@ -943,6 +950,7 @@ static const struct net_device_ops ocelot_port_netdev_ops = {
.ndo_vlan_rx_kill_vid = ocelot_vlan_rx_kill_vid,
.ndo_set_features = ocelot_set_features,
.ndo_get_port_parent_id = ocelot_get_port_parent_id,
+ .ndo_setup_tc = ocelot_setup_tc,
};
static void ocelot_get_strings(struct net_device *netdev, u32 sset, u8 *data)
@@ -1663,8 +1671,9 @@ int ocelot_probe_port(struct ocelot *ocelot, u8 port,
dev->netdev_ops = &ocelot_port_netdev_ops;
dev->ethtool_ops = &ocelot_ethtool_ops;
- dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXFCS;
- dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+ dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXFCS |
+ NETIF_F_HW_TC;
+ dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_TC;
memcpy(dev->dev_addr, ocelot->base_mac, ETH_ALEN);
dev->dev_addr[ETH_ALEN - 1] += port;
diff --git a/drivers/net/ethernet/mscc/ocelot.h b/drivers/net/ethernet/mscc/ocelot.h
index ba3b3380b4d0..9514979fa075 100644
--- a/drivers/net/ethernet/mscc/ocelot.h
+++ b/drivers/net/ethernet/mscc/ocelot.h
@@ -22,6 +22,7 @@
#include "ocelot_rew.h"
#include "ocelot_sys.h"
#include "ocelot_qs.h"
+#include "ocelot_tc.h"
#define PGID_AGGR 64
#define PGID_SRC 80
@@ -458,6 +459,8 @@ struct ocelot_port {
phy_interface_t phy_mode;
struct phy *serdes;
+
+ struct ocelot_port_tc tc;
};
u32 __ocelot_read_ix(struct ocelot *ocelot, u32 reg, u32 offset);
diff --git a/drivers/net/ethernet/mscc/ocelot_police.c b/drivers/net/ethernet/mscc/ocelot_police.c
new file mode 100644
index 000000000000..701e82dd749a
--- /dev/null
+++ b/drivers/net/ethernet/mscc/ocelot_police.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/* Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2019 Microsemi Corporation
+ */
+
+#include "ocelot_police.h"
+
+enum mscc_qos_rate_mode {
+ MSCC_QOS_RATE_MODE_DISABLED, /* Policer/shaper disabled */
+ MSCC_QOS_RATE_MODE_LINE, /* Measure line rate in kbps incl. IPG */
+ MSCC_QOS_RATE_MODE_DATA, /* Measures data rate in kbps excl. IPG */
+ MSCC_QOS_RATE_MODE_FRAME, /* Measures frame rate in fps */
+ __MSCC_QOS_RATE_MODE_END,
+ NUM_MSCC_QOS_RATE_MODE = __MSCC_QOS_RATE_MODE_END,
+ MSCC_QOS_RATE_MODE_MAX = __MSCC_QOS_RATE_MODE_END - 1,
+};
+
+/* Types for ANA:POL[0-192]:POL_MODE_CFG.FRM_MODE */
+#define POL_MODE_LINERATE 0 /* Incl IPG. Unit: 33 1/3 kbps, 4096 bytes */
+#define POL_MODE_DATARATE 1 /* Excl IPG. Unit: 33 1/3 kbps, 4096 bytes */
+#define POL_MODE_FRMRATE_HI 2 /* Unit: 33 1/3 fps, 32.8 frames */
+#define POL_MODE_FRMRATE_LO 3 /* Unit: 1/3 fps, 0.3 frames */
+
+/* Policer indexes */
+#define POL_IX_PORT 0 /* 0-11 : Port policers */
+#define POL_IX_QUEUE 32 /* 32-127 : Queue policers */
+
+/* Default policer order */
+#define POL_ORDER 0x1d3 /* Ocelot policer order: Serial (QoS -> Port -> VCAP) */
+
+struct qos_policer_conf {
+ enum mscc_qos_rate_mode mode;
+ bool dlb; /* Enable DLB (dual leaky bucket mode */
+ bool cf; /* Coupling flag (ignored in SLB mode) */
+ u32 cir; /* CIR in kbps/fps (ignored in SLB mode) */
+ u32 cbs; /* CBS in bytes/frames (ignored in SLB mode) */
+ u32 pir; /* PIR in kbps/fps */
+ u32 pbs; /* PBS in bytes/frames */
+ u8 ipg; /* Size of IPG when MSCC_QOS_RATE_MODE_LINE is chosen */
+};
+
+static int qos_policer_conf_set(struct ocelot_port *port, u32 pol_ix,
+ struct qos_policer_conf *conf)
+{
+ u32 cf = 0, cir_ena = 0, frm_mode = POL_MODE_LINERATE;
+ u32 cir = 0, cbs = 0, pir = 0, pbs = 0;
+ bool cir_discard = 0, pir_discard = 0;
+ struct ocelot *ocelot = port->ocelot;
+ u32 pbs_max = 0, cbs_max = 0;
+ u8 ipg = 20;
+ u32 value;
+
+ pir = conf->pir;
+ pbs = conf->pbs;
+
+ switch (conf->mode) {
+ case MSCC_QOS_RATE_MODE_LINE:
+ case MSCC_QOS_RATE_MODE_DATA:
+ if (conf->mode == MSCC_QOS_RATE_MODE_LINE) {
+ frm_mode = POL_MODE_LINERATE;
+ ipg = min_t(u8, GENMASK(4, 0), conf->ipg);
+ } else {
+ frm_mode = POL_MODE_DATARATE;
+ }
+ if (conf->dlb) {
+ cir_ena = 1;
+ cir = conf->cir;
+ cbs = conf->cbs;
+ if (cir == 0 && cbs == 0) {
+ /* Discard cir frames */
+ cir_discard = 1;
+ } else {
+ cir = DIV_ROUND_UP(cir, 100);
+ cir *= 3; /* 33 1/3 kbps */
+ cbs = DIV_ROUND_UP(cbs, 4096);
+ cbs = (cbs ? cbs : 1); /* No zero burst size */
+ cbs_max = 60; /* Limit burst size */
+ cf = conf->cf;
+ if (cf)
+ pir += conf->cir;
+ }
+ }
+ if (pir == 0 && pbs == 0) {
+ /* Discard PIR frames */
+ pir_discard = 1;
+ } else {
+ pir = DIV_ROUND_UP(pir, 100);
+ pir *= 3; /* 33 1/3 kbps */
+ pbs = DIV_ROUND_UP(pbs, 4096);
+ pbs = (pbs ? pbs : 1); /* No zero burst size */
+ pbs_max = 60; /* Limit burst size */
+ }
+ break;
+ case MSCC_QOS_RATE_MODE_FRAME:
+ if (pir >= 100) {
+ frm_mode = POL_MODE_FRMRATE_HI;
+ pir = DIV_ROUND_UP(pir, 100);
+ pir *= 3; /* 33 1/3 fps */
+ pbs = (pbs * 10) / 328; /* 32.8 frames */
+ pbs = (pbs ? pbs : 1); /* No zero burst size */
+ pbs_max = GENMASK(6, 0); /* Limit burst size */
+ } else {
+ frm_mode = POL_MODE_FRMRATE_LO;
+ if (pir == 0 && pbs == 0) {
+ /* Discard all frames */
+ pir_discard = 1;
+ cir_discard = 1;
+ } else {
+ pir *= 3; /* 1/3 fps */
+ pbs = (pbs * 10) / 3; /* 0.3 frames */
+ pbs = (pbs ? pbs : 1); /* No zero burst size */
+ pbs_max = 61; /* Limit burst size */
+ }
+ }
+ break;
+ default: /* MSCC_QOS_RATE_MODE_DISABLED */
+ /* Disable policer using maximum rate and zero burst */
+ pir = GENMASK(15, 0);
+ pbs = 0;
+ break;
+ }
+
+ /* Check limits */
+ if (pir > GENMASK(15, 0)) {
+ netdev_err(port->dev, "Invalid pir\n");
+ return -EINVAL;
+ }
+
+ if (cir > GENMASK(15, 0)) {
+ netdev_err(port->dev, "Invalid cir\n");
+ return -EINVAL;
+ }
+
+ if (pbs > pbs_max) {
+ netdev_err(port->dev, "Invalid pbs\n");
+ return -EINVAL;
+ }
+
+ if (cbs > cbs_max) {
+ netdev_err(port->dev, "Invalid cbs\n");
+ return -EINVAL;
+ }
+
+ value = (ANA_POL_MODE_CFG_IPG_SIZE(ipg) |
+ ANA_POL_MODE_CFG_FRM_MODE(frm_mode) |
+ (cf ? ANA_POL_MODE_CFG_DLB_COUPLED : 0) |
+ (cir_ena ? ANA_POL_MODE_CFG_CIR_ENA : 0) |
+ ANA_POL_MODE_CFG_OVERSHOOT_ENA);
+
+ ocelot_write_gix(ocelot, value, ANA_POL_MODE_CFG, pol_ix);
+
+ ocelot_write_gix(ocelot,
+ ANA_POL_PIR_CFG_PIR_RATE(pir) |
+ ANA_POL_PIR_CFG_PIR_BURST(pbs),
+ ANA_POL_PIR_CFG, pol_ix);
+
+ ocelot_write_gix(ocelot,
+ (pir_discard ? GENMASK(22, 0) : 0),
+ ANA_POL_PIR_STATE, pol_ix);
+
+ ocelot_write_gix(ocelot,
+ ANA_POL_CIR_CFG_CIR_RATE(cir) |
+ ANA_POL_CIR_CFG_CIR_BURST(cbs),
+ ANA_POL_CIR_CFG, pol_ix);
+
+ ocelot_write_gix(ocelot,
+ (cir_discard ? GENMASK(22, 0) : 0),
+ ANA_POL_CIR_STATE, pol_ix);
+
+ return 0;
+}
+
+int ocelot_port_policer_add(struct ocelot_port *port,
+ struct ocelot_policer *pol)
+{
+ struct ocelot *ocelot = port->ocelot;
+ struct qos_policer_conf pp = { 0 };
+ int err;
+
+ if (!pol)
+ return -EINVAL;
+
+ pp.mode = MSCC_QOS_RATE_MODE_DATA;
+ pp.pir = pol->rate;
+ pp.pbs = pol->burst;
+
+ netdev_dbg(port->dev,
+ "%s: port %u pir %u kbps, pbs %u bytes\n",
+ __func__, port->chip_port, pp.pir, pp.pbs);
+
+ err = qos_policer_conf_set(port, POL_IX_PORT + port->chip_port, &pp);
+ if (err)
+ return err;
+
+ ocelot_rmw_gix(ocelot,
+ ANA_PORT_POL_CFG_PORT_POL_ENA |
+ ANA_PORT_POL_CFG_POL_ORDER(POL_ORDER),
+ ANA_PORT_POL_CFG_PORT_POL_ENA |
+ ANA_PORT_POL_CFG_POL_ORDER_M,
+ ANA_PORT_POL_CFG, port->chip_port);
+
+ return 0;
+}
+
+int ocelot_port_policer_del(struct ocelot_port *port)
+{
+ struct ocelot *ocelot = port->ocelot;
+ struct qos_policer_conf pp = { 0 };
+ int err;
+
+ netdev_dbg(port->dev, "%s: port %u\n", __func__, port->chip_port);
+
+ pp.mode = MSCC_QOS_RATE_MODE_DISABLED;
+
+ err = qos_policer_conf_set(port, POL_IX_PORT + port->chip_port, &pp);
+ if (err)
+ return err;
+
+ ocelot_rmw_gix(ocelot,
+ ANA_PORT_POL_CFG_POL_ORDER(POL_ORDER),
+ ANA_PORT_POL_CFG_PORT_POL_ENA |
+ ANA_PORT_POL_CFG_POL_ORDER_M,
+ ANA_PORT_POL_CFG, port->chip_port);
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/mscc/ocelot_police.h b/drivers/net/ethernet/mscc/ocelot_police.h
new file mode 100644
index 000000000000..d1137f79efda
--- /dev/null
+++ b/drivers/net/ethernet/mscc/ocelot_police.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/* Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2019 Microsemi Corporation
+ */
+
+#ifndef _MSCC_OCELOT_POLICE_H_
+#define _MSCC_OCELOT_POLICE_H_
+
+#include "ocelot.h"
+
+struct ocelot_policer {
+ u32 rate; /* kilobit per second */
+ u32 burst; /* bytes */
+};
+
+int ocelot_port_policer_add(struct ocelot_port *port,
+ struct ocelot_policer *pol);
+
+int ocelot_port_policer_del(struct ocelot_port *port);
+
+#endif /* _MSCC_OCELOT_POLICE_H_ */
diff --git a/drivers/net/ethernet/mscc/ocelot_tc.c b/drivers/net/ethernet/mscc/ocelot_tc.c
new file mode 100644
index 000000000000..a0eaadccfecc
--- /dev/null
+++ b/drivers/net/ethernet/mscc/ocelot_tc.c
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/* Microsemi Ocelot Switch TC driver
+ *
+ * Copyright (c) 2019 Microsemi Corporation
+ */
+
+#include "ocelot_tc.h"
+#include "ocelot_police.h"
+#include <net/pkt_cls.h>
+
+static int ocelot_setup_tc_cls_matchall(struct ocelot_port *port,
+ struct tc_cls_matchall_offload *f,
+ bool ingress)
+{
+ struct netlink_ext_ack *extack = f->common.extack;
+ struct ocelot_policer pol = { 0 };
+ struct flow_action_entry *action;
+ int err;
+
+ netdev_dbg(port->dev, "%s: port %u command %d cookie %lu\n",
+ __func__, port->chip_port, f->command, f->cookie);
+
+ if (!ingress) {
+ NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported");
+ return -EOPNOTSUPP;
+ }
+
+ switch (f->command) {
+ case TC_CLSMATCHALL_REPLACE:
+ if (!flow_offload_has_one_action(&f->rule->action)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Only one action is supported");
+ return -EOPNOTSUPP;
+ }
+
+ if (port->tc.block_shared) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Rate limit is not supported on shared blocks");
+ return -EOPNOTSUPP;
+ }
+
+ action = &f->rule->action.entries[0];
+
+ if (action->id != FLOW_ACTION_POLICE) {
+ NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
+ return -EOPNOTSUPP;
+ }
+
+ if (port->tc.police_id && port->tc.police_id != f->cookie) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Only one policer per port is supported\n");
+ return -EEXIST;
+ }
+
+ pol.rate = (u32)div_u64(action->police.rate_bytes_ps, 1000) * 8;
+ pol.burst = (u32)div_u64(action->police.rate_bytes_ps *
+ PSCHED_NS2TICKS(action->police.burst),
+ PSCHED_TICKS_PER_SEC);
+
+ err = ocelot_port_policer_add(port, &pol);
+ if (err) {
+ NL_SET_ERR_MSG_MOD(extack, "Could not add policer\n");
+ return err;
+ }
+
+ port->tc.police_id = f->cookie;
+ port->tc.offload_cnt++;
+ return 0;
+ case TC_CLSMATCHALL_DESTROY:
+ if (port->tc.police_id != f->cookie)
+ return -ENOENT;
+
+ err = ocelot_port_policer_del(port);
+ if (err) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Could not delete policer\n");
+ return err;
+ }
+ port->tc.police_id = 0;
+ port->tc.offload_cnt--;
+ return 0;
+ case TC_CLSMATCHALL_STATS: /* fall through */
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ocelot_setup_tc_block_cb(enum tc_setup_type type,
+ void *type_data,
+ void *cb_priv, bool ingress)
+{
+ struct ocelot_port *port = cb_priv;
+
+ if (!tc_cls_can_offload_and_chain0(port->dev, type_data))
+ return -EOPNOTSUPP;
+
+ switch (type) {
+ case TC_SETUP_CLSMATCHALL:
+ netdev_dbg(port->dev, "tc_block_cb: TC_SETUP_CLSMATCHALL %s\n",
+ ingress ? "ingress" : "egress");
+
+ return ocelot_setup_tc_cls_matchall(port, type_data, ingress);
+ case TC_SETUP_CLSFLOWER:
+ netdev_dbg(port->dev, "tc_block_cb: TC_SETUP_CLSFLOWER %s\n",
+ ingress ? "ingress" : "egress");
+
+ return -EOPNOTSUPP;
+ default:
+ netdev_dbg(port->dev, "tc_block_cb: type %d %s\n",
+ type,
+ ingress ? "ingress" : "egress");
+
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ocelot_setup_tc_block_cb_ig(enum tc_setup_type type,
+ void *type_data,
+ void *cb_priv)
+{
+ return ocelot_setup_tc_block_cb(type, type_data,
+ cb_priv, true);
+}
+
+static int ocelot_setup_tc_block_cb_eg(enum tc_setup_type type,
+ void *type_data,
+ void *cb_priv)
+{
+ return ocelot_setup_tc_block_cb(type, type_data,
+ cb_priv, false);
+}
+
+static int ocelot_setup_tc_block(struct ocelot_port *port,
+ struct tc_block_offload *f)
+{
+ tc_setup_cb_t *cb;
+
+ netdev_dbg(port->dev, "tc_block command %d, binder_type %d\n",
+ f->command, f->binder_type);
+
+ if (f->binder_type == TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS) {
+ cb = ocelot_setup_tc_block_cb_ig;
+ port->tc.block_shared = tcf_block_shared(f->block);
+ } else if (f->binder_type == TCF_BLOCK_BINDER_TYPE_CLSACT_EGRESS) {
+ cb = ocelot_setup_tc_block_cb_eg;
+ } else {
+ return -EOPNOTSUPP;
+ }
+
+ switch (f->command) {
+ case TC_BLOCK_BIND:
+ return tcf_block_cb_register(f->block, cb, port,
+ port, f->extack);
+ case TC_BLOCK_UNBIND:
+ tcf_block_cb_unregister(f->block, cb, port);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type,
+ void *type_data)
+{
+ struct ocelot_port *port = netdev_priv(dev);
+
+ switch (type) {
+ case TC_SETUP_BLOCK:
+ return ocelot_setup_tc_block(port, type_data);
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
diff --git a/drivers/net/ethernet/mscc/ocelot_tc.h b/drivers/net/ethernet/mscc/ocelot_tc.h
new file mode 100644
index 000000000000..61757c2250a6
--- /dev/null
+++ b/drivers/net/ethernet/mscc/ocelot_tc.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/* Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2019 Microsemi Corporation
+ */
+
+#ifndef _MSCC_OCELOT_TC_H_
+#define _MSCC_OCELOT_TC_H_
+
+#include <linux/netdevice.h>
+
+struct ocelot_port_tc {
+ bool block_shared;
+ unsigned long offload_cnt;
+
+ unsigned long police_id;
+};
+
+int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type,
+ void *type_data);
+
+#endif /* _MSCC_OCELOT_TC_H_ */
--
2.17.1
On Tue, 28 May 2019 14:49:17 +0200, Joergen Andreasen wrote:
> Hardware offload of matchall classifier and police action are now
> supported via the tc command.
> Supported police parameters are: rate and burst.
>
> Example:
>
> Add:
> tc qdisc add dev eth3 handle ffff: ingress
> tc filter add dev eth3 parent ffff: prio 1 handle 2 \
> matchall skip_sw \
> action police rate 100Mbit burst 10000
>
> Show:
> tc -s -d qdisc show dev eth3
> tc -s -d filter show dev eth3 ingress
>
> Delete:
> tc filter del dev eth3 parent ffff: prio 1
> tc qdisc del dev eth3 handle ffff: ingress
>
> Signed-off-by: Joergen Andreasen <[email protected]>
Looks reasonable :)
Acked-by: Jakub Kicinski <[email protected]>
From: Joergen Andreasen <[email protected]>
Date: Tue, 28 May 2019 14:49:17 +0200
> Hardware offload of matchall classifier and police action are now
> supported via the tc command.
> Supported police parameters are: rate and burst.
>
> Example:
>
> Add:
> tc qdisc add dev eth3 handle ffff: ingress
> tc filter add dev eth3 parent ffff: prio 1 handle 2 \
> matchall skip_sw \
> action police rate 100Mbit burst 10000
>
> Show:
> tc -s -d qdisc show dev eth3
> tc -s -d filter show dev eth3 ingress
>
> Delete:
> tc filter del dev eth3 parent ffff: prio 1
> tc qdisc del dev eth3 handle ffff: ingress
>
> Signed-off-by: Joergen Andreasen <[email protected]>
Applied.