LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* [PATCH net-next 0/3] Add hw offload of TC police on MSCC ocelot
@ 2019-05-02  9:40 Joergen Andreasen
  2019-05-02  9:40 ` [PATCH net-next 1/3] net/sched: act_police: move police parameters into separate header file Joergen Andreasen
                   ` (4 more replies)
  0 siblings, 5 replies; 21+ messages in thread
From: Joergen Andreasen @ 2019-05-02  9:40 UTC (permalink / raw)
  To: netdev
  Cc: Joergen Andreasen, Jamal Hadi Salim, Cong Wang, Jiri Pirko,
	Alexandre Belloni, Microchip Linux Driver Support,
	David S. Miller, Ralf Baechle, Paul Burton, James Hogan,
	linux-mips, linux-kernel

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


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH net-next 1/3] net/sched: act_police: move police parameters into separate header file
  2019-05-02  9:40 [PATCH net-next 0/3] Add hw offload of TC police on MSCC ocelot Joergen Andreasen
@ 2019-05-02  9:40 ` Joergen Andreasen
  2019-05-02 20:38   ` Jiri Pirko
  2019-05-02  9:40 ` [PATCH net-next 2/3] net: mscc: ocelot: Implement port policers via tc command Joergen Andreasen
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 21+ messages in thread
From: Joergen Andreasen @ 2019-05-02  9:40 UTC (permalink / raw)
  To: netdev
  Cc: Joergen Andreasen, Jamal Hadi Salim, Cong Wang, Jiri Pirko,
	Alexandre Belloni, Microchip Linux Driver Support,
	David S. Miller, Ralf Baechle, Paul Burton, James Hogan,
	linux-mips, linux-kernel

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 <joergen.andreasen@microchip.com>
---
 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


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH net-next 2/3] net: mscc: ocelot: Implement port policers via tc command
  2019-05-02  9:40 [PATCH net-next 0/3] Add hw offload of TC police on MSCC ocelot Joergen Andreasen
  2019-05-02  9:40 ` [PATCH net-next 1/3] net/sched: act_police: move police parameters into separate header file Joergen Andreasen
@ 2019-05-02  9:40 ` Joergen Andreasen
  2019-05-02 12:32   ` Andrew Lunn
                     ` (2 more replies)
  2019-05-02  9:40 ` [PATCH net-next 3/3] MIPS: generic: Add police related options to ocelot_defconfig Joergen Andreasen
                   ` (2 subsequent siblings)
  4 siblings, 3 replies; 21+ messages in thread
From: Joergen Andreasen @ 2019-05-02  9:40 UTC (permalink / raw)
  To: netdev
  Cc: Joergen Andreasen, Jamal Hadi Salim, Cong Wang, Jiri Pirko,
	Alexandre Belloni, Microchip Linux Driver Support,
	David S. Miller, Ralf Baechle, Paul Burton, James Hogan,
	linux-mips, linux-kernel

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 <joergen.andreasen@microchip.com>
---
 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


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH net-next 3/3] MIPS: generic: Add police related options to ocelot_defconfig
  2019-05-02  9:40 [PATCH net-next 0/3] Add hw offload of TC police on MSCC ocelot Joergen Andreasen
  2019-05-02  9:40 ` [PATCH net-next 1/3] net/sched: act_police: move police parameters into separate header file Joergen Andreasen
  2019-05-02  9:40 ` [PATCH net-next 2/3] net: mscc: ocelot: Implement port policers via tc command Joergen Andreasen
@ 2019-05-02  9:40 ` Joergen Andreasen
  2019-05-02 16:27   ` Alexandre Belloni
  2019-05-23 10:49 ` [PATCH net-next v2 0/1] Add hw offload of TC police on MSCC ocelot Joergen Andreasen
  2019-05-28 12:49 ` [PATCH net-next v3 0/1] Add hw offload of TC police on MSCC ocelot Joergen Andreasen
  4 siblings, 1 reply; 21+ messages in thread
From: Joergen Andreasen @ 2019-05-02  9:40 UTC (permalink / raw)
  To: netdev
  Cc: Joergen Andreasen, Jamal Hadi Salim, Cong Wang, Jiri Pirko,
	Alexandre Belloni, Microchip Linux Driver Support,
	David S. Miller, Ralf Baechle, Paul Burton, James Hogan,
	linux-mips, linux-kernel

Add default support for ingress qdisc, matchall classification
and police action on MSCC Ocelot.

Signed-off-by: Joergen Andreasen <joergen.andreasen@microchip.com>
---
 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


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH net-next 2/3] net: mscc: ocelot: Implement port policers via tc command
  2019-05-02  9:40 ` [PATCH net-next 2/3] net: mscc: ocelot: Implement port policers via tc command Joergen Andreasen
@ 2019-05-02 12:32   ` Andrew Lunn
  2019-05-03 11:23     ` Joergen Andreasen
  2019-05-02 20:36   ` Jiri Pirko
  2019-05-04 13:07   ` Jiri Pirko
  2 siblings, 1 reply; 21+ messages in thread
From: Andrew Lunn @ 2019-05-02 12:32 UTC (permalink / raw)
  To: Joergen Andreasen
  Cc: netdev, Jamal Hadi Salim, Cong Wang, Jiri Pirko,
	Alexandre Belloni, Microchip Linux Driver Support,
	David S. Miller, Ralf Baechle, Paul Burton, James Hogan,
	linux-mips, linux-kernel

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

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH net-next 3/3] MIPS: generic: Add police related options to ocelot_defconfig
  2019-05-02  9:40 ` [PATCH net-next 3/3] MIPS: generic: Add police related options to ocelot_defconfig Joergen Andreasen
@ 2019-05-02 16:27   ` Alexandre Belloni
  2019-05-03 10:47     ` Joergen Andreasen
  0 siblings, 1 reply; 21+ messages in thread
From: Alexandre Belloni @ 2019-05-02 16:27 UTC (permalink / raw)
  To: Joergen Andreasen
  Cc: netdev, Jamal Hadi Salim, Cong Wang, Jiri Pirko,
	Microchip Linux Driver Support, David S. Miller, Ralf Baechle,
	Paul Burton, James Hogan, linux-mips, linux-kernel

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 <joergen.andreasen@microchip.com>
> ---
>  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

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH net-next 2/3] net: mscc: ocelot: Implement port policers via tc command
  2019-05-02  9:40 ` [PATCH net-next 2/3] net: mscc: ocelot: Implement port policers via tc command Joergen Andreasen
  2019-05-02 12:32   ` Andrew Lunn
@ 2019-05-02 20:36   ` Jiri Pirko
  2019-05-03 11:38     ` Joergen Andreasen
  2019-05-04 13:07   ` Jiri Pirko
  2 siblings, 1 reply; 21+ messages in thread
From: Jiri Pirko @ 2019-05-02 20:36 UTC (permalink / raw)
  To: Joergen Andreasen
  Cc: netdev, Jamal Hadi Salim, Cong Wang, Alexandre Belloni,
	Microchip Linux Driver Support, David S. Miller, Ralf Baechle,
	Paul Burton, James Hogan, linux-mips, linux-kernel

Thu, May 02, 2019 at 11:40:28AM CEST, joergen.andreasen@microchip.com 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 <joergen.andreasen@microchip.com>
>---
> 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
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH net-next 1/3] net/sched: act_police: move police parameters into separate header file
  2019-05-02  9:40 ` [PATCH net-next 1/3] net/sched: act_police: move police parameters into separate header file Joergen Andreasen
@ 2019-05-02 20:38   ` Jiri Pirko
  0 siblings, 0 replies; 21+ messages in thread
From: Jiri Pirko @ 2019-05-02 20:38 UTC (permalink / raw)
  To: Joergen Andreasen
  Cc: netdev, Jamal Hadi Salim, Cong Wang, Alexandre Belloni,
	Microchip Linux Driver Support, David S. Miller, Ralf Baechle,
	Paul Burton, James Hogan, linux-mips, linux-kernel

Thu, May 02, 2019 at 11:40:27AM CEST, joergen.andreasen@microchip.com 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 <joergen.andreasen@microchip.com>

Acked-by: Jiri Pirko <jiri@mellanox.com>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH net-next 3/3] MIPS: generic: Add police related options to ocelot_defconfig
  2019-05-02 16:27   ` Alexandre Belloni
@ 2019-05-03 10:47     ` Joergen Andreasen
  0 siblings, 0 replies; 21+ messages in thread
From: Joergen Andreasen @ 2019-05-03 10:47 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: netdev, Jamal Hadi Salim, Cong Wang, Jiri Pirko,
	Microchip Linux Driver Support, David S. Miller, Ralf Baechle,
	Paul Burton, James Hogan, linux-mips, linux-kernel,
	Joergen Andreasen

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 <joergen.andreasen@microchip.com>
> > ---
> >  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

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH net-next 2/3] net: mscc: ocelot: Implement port policers via tc command
  2019-05-02 12:32   ` Andrew Lunn
@ 2019-05-03 11:23     ` Joergen Andreasen
  0 siblings, 0 replies; 21+ messages in thread
From: Joergen Andreasen @ 2019-05-03 11:23 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: netdev, Jamal Hadi Salim, Cong Wang, Jiri Pirko,
	Alexandre Belloni, Microchip Linux Driver Support,
	David S. Miller, Ralf Baechle, Paul Burton, James Hogan,
	linux-mips, linux-kernel, Joergen Andreasen

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

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH net-next 2/3] net: mscc: ocelot: Implement port policers via tc command
  2019-05-02 20:36   ` Jiri Pirko
@ 2019-05-03 11:38     ` Joergen Andreasen
  0 siblings, 0 replies; 21+ messages in thread
From: Joergen Andreasen @ 2019-05-03 11:38 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: netdev, Jamal Hadi Salim, Cong Wang, Alexandre Belloni,
	Microchip Linux Driver Support, David S. Miller, Ralf Baechle,
	Paul Burton, James Hogan, linux-mips, linux-kernel,
	Joergen Andreasen

Hi Jiri,

The 05/02/2019 22:36, Jiri Pirko wrote:
> External E-Mail
> 
> 
> Thu, May 02, 2019 at 11:40:28AM CEST, joergen.andreasen@microchip.com 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 <joergen.andreasen@microchip.com>
> >---
> > 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

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH net-next 2/3] net: mscc: ocelot: Implement port policers via tc command
  2019-05-02  9:40 ` [PATCH net-next 2/3] net: mscc: ocelot: Implement port policers via tc command Joergen Andreasen
  2019-05-02 12:32   ` Andrew Lunn
  2019-05-02 20:36   ` Jiri Pirko
@ 2019-05-04 13:07   ` Jiri Pirko
  2019-05-07  8:30     ` Joergen Andreasen
  2 siblings, 1 reply; 21+ messages in thread
From: Jiri Pirko @ 2019-05-04 13:07 UTC (permalink / raw)
  To: Joergen Andreasen
  Cc: netdev, Jamal Hadi Salim, Cong Wang, Alexandre Belloni,
	Microchip Linux Driver Support, David S. Miller, Ralf Baechle,
	Paul Burton, James Hogan, linux-mips, linux-kernel,
	Jakub Kicinski, pieter.jansenvanvuuren

Thu, May 02, 2019 at 11:40:28AM CEST, joergen.andreasen@microchip.com 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.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH net-next 2/3] net: mscc: ocelot: Implement port policers via tc command
  2019-05-04 13:07   ` Jiri Pirko
@ 2019-05-07  8:30     ` Joergen Andreasen
  0 siblings, 0 replies; 21+ messages in thread
From: Joergen Andreasen @ 2019-05-07  8:30 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: netdev, Jamal Hadi Salim, Cong Wang, Alexandre Belloni,
	Microchip Linux Driver Support, David S. Miller, Ralf Baechle,
	Paul Burton, James Hogan, linux-mips, linux-kernel,
	Jakub Kicinski, pieter.jansenvanvuuren

Hi Jiri,

The 05/04/2019 15:07, Jiri Pirko wrote:
> External E-Mail
> 
> 
> Thu, May 02, 2019 at 11:40:28AM CEST, joergen.andreasen@microchip.com 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

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH net-next v2 0/1] Add hw offload of TC police on MSCC ocelot
  2019-05-02  9:40 [PATCH net-next 0/3] Add hw offload of TC police on MSCC ocelot Joergen Andreasen
                   ` (2 preceding siblings ...)
  2019-05-02  9:40 ` [PATCH net-next 3/3] MIPS: generic: Add police related options to ocelot_defconfig Joergen Andreasen
@ 2019-05-23 10:49 ` Joergen Andreasen
  2019-05-23 10:49   ` [PATCH net-next v2 1/1] net: mscc: ocelot: Implement port policers via tc command Joergen Andreasen
  2019-05-28 12:49 ` [PATCH net-next v3 0/1] Add hw offload of TC police on MSCC ocelot Joergen Andreasen
  4 siblings, 1 reply; 21+ messages in thread
From: Joergen Andreasen @ 2019-05-23 10:49 UTC (permalink / raw)
  To: netdev
  Cc: Joergen Andreasen, Microchip Linux Driver Support,
	Alexandre Belloni, David S. Miller, linux-kernel

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


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH net-next v2 1/1] net: mscc: ocelot: Implement port policers via tc command
  2019-05-23 10:49 ` [PATCH net-next v2 0/1] Add hw offload of TC police on MSCC ocelot Joergen Andreasen
@ 2019-05-23 10:49   ` Joergen Andreasen
  2019-05-23 18:56     ` Jakub Kicinski
  0 siblings, 1 reply; 21+ messages in thread
From: Joergen Andreasen @ 2019-05-23 10:49 UTC (permalink / raw)
  To: netdev
  Cc: Joergen Andreasen, Microchip Linux Driver Support,
	Alexandre Belloni, David S. Miller, linux-kernel

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 <joergen.andreasen@microchip.com>
---
 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


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH net-next v2 1/1] net: mscc: ocelot: Implement port policers via tc command
  2019-05-23 10:49   ` [PATCH net-next v2 1/1] net: mscc: ocelot: Implement port policers via tc command Joergen Andreasen
@ 2019-05-23 18:56     ` Jakub Kicinski
  2019-05-24 11:40       ` Joergen Andreasen
  0 siblings, 1 reply; 21+ messages in thread
From: Jakub Kicinski @ 2019-05-23 18:56 UTC (permalink / raw)
  To: Joergen Andreasen
  Cc: netdev, Microchip Linux Driver Support, Alexandre Belloni,
	David S. Miller, linux-kernel

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 <joergen.andreasen@microchip.com>

> 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;
> +	}
> +}

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH net-next v2 1/1] net: mscc: ocelot: Implement port policers via tc command
  2019-05-23 18:56     ` Jakub Kicinski
@ 2019-05-24 11:40       ` Joergen Andreasen
  0 siblings, 0 replies; 21+ messages in thread
From: Joergen Andreasen @ 2019-05-24 11:40 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: netdev, Microchip Linux Driver Support, Alexandre Belloni,
	David S. Miller, linux-kernel

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 <joergen.andreasen@microchip.com>
> 
> > 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

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH net-next v3 0/1] Add hw offload of TC police on MSCC ocelot
  2019-05-02  9:40 [PATCH net-next 0/3] Add hw offload of TC police on MSCC ocelot Joergen Andreasen
                   ` (3 preceding siblings ...)
  2019-05-23 10:49 ` [PATCH net-next v2 0/1] Add hw offload of TC police on MSCC ocelot Joergen Andreasen
@ 2019-05-28 12:49 ` Joergen Andreasen
  2019-05-28 12:49   ` [PATCH net-next v3 1/1] net: mscc: ocelot: Implement port policers via tc command Joergen Andreasen
  4 siblings, 1 reply; 21+ messages in thread
From: Joergen Andreasen @ 2019-05-28 12:49 UTC (permalink / raw)
  To: netdev
  Cc: Joergen Andreasen, Microchip Linux Driver Support,
	Alexandre Belloni, David S. Miller, linux-kernel

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


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH net-next v3 1/1] net: mscc: ocelot: Implement port policers via tc command
  2019-05-28 12:49 ` [PATCH net-next v3 0/1] Add hw offload of TC police on MSCC ocelot Joergen Andreasen
@ 2019-05-28 12:49   ` Joergen Andreasen
  2019-05-30  4:38     ` David Miller
  0 siblings, 1 reply; 21+ messages in thread
From: Joergen Andreasen @ 2019-05-28 12:49 UTC (permalink / raw)
  To: netdev
  Cc: Joergen Andreasen, Microchip Linux Driver Support,
	Alexandre Belloni, David S. Miller, linux-kernel

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 <joergen.andreasen@microchip.com>
---
 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


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH net-next v3 1/1] net: mscc: ocelot: Implement port policers via tc command
  2019-05-28 12:49   ` [PATCH net-next v3 1/1] net: mscc: ocelot: Implement port policers via tc command Joergen Andreasen
@ 2019-05-30  4:38     ` David Miller
  0 siblings, 0 replies; 21+ messages in thread
From: David Miller @ 2019-05-30  4:38 UTC (permalink / raw)
  To: joergen.andreasen; +Cc: netdev, UNGLinuxDriver, alexandre.belloni, linux-kernel

From: Joergen Andreasen <joergen.andreasen@microchip.com>
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 <joergen.andreasen@microchip.com>

Applied.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH net-next v3 1/1] net: mscc: ocelot: Implement port policers via tc command
@ 2019-05-28 20:15 Jakub Kicinski
  0 siblings, 0 replies; 21+ messages in thread
From: Jakub Kicinski @ 2019-05-28 20:15 UTC (permalink / raw)
  To: Joergen Andreasen
  Cc: Linux Netdev List, Microchip Linux Driver Support,
	Alexandre Belloni, David S. Miller, LKML

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 <joergen.andreasen@microchip.com>

Looks reasonable :)

Acked-by: Jakub Kicinski <jakub.kicinski@netronome.com>

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2019-05-30  4:38 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-05-02  9:40 [PATCH net-next 0/3] Add hw offload of TC police on MSCC ocelot Joergen Andreasen
2019-05-02  9:40 ` [PATCH net-next 1/3] net/sched: act_police: move police parameters into separate header file Joergen Andreasen
2019-05-02 20:38   ` Jiri Pirko
2019-05-02  9:40 ` [PATCH net-next 2/3] net: mscc: ocelot: Implement port policers via tc command Joergen Andreasen
2019-05-02 12:32   ` Andrew Lunn
2019-05-03 11:23     ` Joergen Andreasen
2019-05-02 20:36   ` Jiri Pirko
2019-05-03 11:38     ` Joergen Andreasen
2019-05-04 13:07   ` Jiri Pirko
2019-05-07  8:30     ` Joergen Andreasen
2019-05-02  9:40 ` [PATCH net-next 3/3] MIPS: generic: Add police related options to ocelot_defconfig Joergen Andreasen
2019-05-02 16:27   ` Alexandre Belloni
2019-05-03 10:47     ` Joergen Andreasen
2019-05-23 10:49 ` [PATCH net-next v2 0/1] Add hw offload of TC police on MSCC ocelot Joergen Andreasen
2019-05-23 10:49   ` [PATCH net-next v2 1/1] net: mscc: ocelot: Implement port policers via tc command Joergen Andreasen
2019-05-23 18:56     ` Jakub Kicinski
2019-05-24 11:40       ` Joergen Andreasen
2019-05-28 12:49 ` [PATCH net-next v3 0/1] Add hw offload of TC police on MSCC ocelot Joergen Andreasen
2019-05-28 12:49   ` [PATCH net-next v3 1/1] net: mscc: ocelot: Implement port policers via tc command Joergen Andreasen
2019-05-30  4:38     ` David Miller
2019-05-28 20:15 Jakub Kicinski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).