[PATCH net-next v2 5/5] net: sparx5: Offload tc port policers via matchall
Daniel Machon
daniel.machon at microchip.com
Mon Nov 2 04:21:28 PST 2026
Offload a tc matchall filter with a police action to the port policer:
tc qdisc add dev <eth> clsact
tc filter add dev <eth> ingress matchall skip_sw \
action police rate <bps> burst <bytes> conform-exceed drop
The port policer meters all ingress frames on the port, so offload the
filter only on ingress, on chain 0, with protocol all, and on a block
that is not shared. Only drop is supported as the exceed action, and
only one policer per port. A police action shared between ports is a
single bucket in software, which the per-port policers cannot express,
so reject a police action that is already offloaded as a port policer on
another port. Report the pass and drop counts through the matchall
stats.
Signed-off-by: Daniel Machon <daniel.machon at microchip.com>
---
.../net/ethernet/microchip/sparx5/sparx5_main.h | 3 ++
.../net/ethernet/microchip/sparx5/sparx5_police.c | 27 +++++++++++++++
.../ethernet/microchip/sparx5/sparx5_tc_matchall.c | 39 ++++++++++++++++++++++
3 files changed, 69 insertions(+)
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
index 93df90a62a9c8..ad29db91ce91e 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
@@ -218,6 +218,8 @@ struct sparx5_port_policer {
struct flow_stats prev;
/* Port policers hold the client reference (cookie) */
unsigned long policer;
+ /* tc police action, the same for every filter bound to it */
+ unsigned long act_cookie;
};
struct sparx5_port_tc {
@@ -304,6 +306,7 @@ struct sparx5_mall_mirror_entry {
struct sparx5_mall_port_policer_entry {
struct flow_action_police police;
+ unsigned long act_cookie;
};
struct sparx5_mall_entry {
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_police.c b/drivers/net/ethernet/microchip/sparx5/sparx5_police.c
index 0fb1003e1b189..a635b95a7bd73 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_police.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_police.c
@@ -301,6 +301,25 @@ int sparx5_update_port_policer_stats(struct net_device *ndev,
return 0;
}
+static bool sparx5_port_policer_act_in_use(struct sparx5_port *port,
+ unsigned long act_cookie)
+{
+ struct sparx5 *sparx5 = port->sparx5;
+ struct sparx5_port *other;
+ int i;
+
+ for (i = 0; i < sparx5->data->consts->n_ports; i++) {
+ other = sparx5->ports[i];
+ if (!other || other == port)
+ continue;
+ if (other->tc.port_policer[0].policer &&
+ other->tc.port_policer[0].act_cookie == act_cookie)
+ return true;
+ }
+
+ return false;
+}
+
int sparx5_add_port_policer(struct sparx5_mall_entry *entry,
struct netlink_ext_ack *extack)
{
@@ -322,6 +341,13 @@ int sparx5_add_port_policer(struct sparx5_mall_entry *entry,
return -EOPNOTSUPP;
}
+ if (sparx5_port_policer_act_in_use(port,
+ entry->port_policer.act_cookie)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Police action shared between ports is not supported");
+ return -EOPNOTSUPP;
+ }
+
if (police->exceed.act_id != FLOW_ACTION_DROP) {
NL_SET_ERR_MSG_MOD(extack,
"Policer parameters are not supported");
@@ -388,6 +414,7 @@ int sparx5_add_port_policer(struct sparx5_mall_entry *entry,
return err;
port->tc.port_policer[idx].policer = entry->cookie;
+ port->tc.port_policer[idx].act_cookie = entry->port_policer.act_cookie;
return 0;
}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_matchall.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_matchall.c
index 146d7f28e8f3d..4961b49fe1831 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_matchall.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_matchall.c
@@ -43,6 +43,14 @@ sparx5_tc_matchall_parse_mirror_action(struct sparx5_mall_entry *entry,
entry->mirror.port = netdev_priv(action->dev);
}
+static void
+sparx5_tc_matchall_parse_port_policer_action(struct sparx5_mall_entry *entry,
+ struct flow_action_entry *action)
+{
+ entry->port_policer.police = action->police;
+ entry->port_policer.act_cookie = action->cookie;
+}
+
static int sparx5_tc_matchall_replace(struct net_device *ndev,
struct tc_cls_matchall_offload *tmo,
bool ingress)
@@ -98,6 +106,30 @@ static int sparx5_tc_matchall_replace(struct net_device *ndev,
}
/* Get baseline stats for this port */
sparx5_mirror_stats(mall_entry, &tmo->stats);
+ break;
+ case FLOW_ACTION_POLICE:
+ if (tmo->common.protocol != htons(ETH_P_ALL)) {
+ NL_SET_ERR_MSG_MOD(tmo->common.extack,
+ "Policer is only supported with protocol all");
+ kfree(mall_entry);
+ return -EOPNOTSUPP;
+ }
+
+ if (tmo->common.chain_index) {
+ NL_SET_ERR_MSG_MOD(tmo->common.extack,
+ "Policer is only supported on chain 0");
+ kfree(mall_entry);
+ return -EOPNOTSUPP;
+ }
+
+ sparx5_tc_matchall_parse_port_policer_action(mall_entry,
+ action);
+ err = sparx5_add_port_policer(mall_entry, tmo->common.extack);
+ if (err) {
+ kfree(mall_entry);
+ return err;
+ }
+
break;
case FLOW_ACTION_GOTO:
err = vcap_enable_lookups(sparx5->vcap_ctrl, ndev,
@@ -151,6 +183,11 @@ static int sparx5_tc_matchall_destroy(struct net_device *ndev,
if (entry->type == FLOW_ACTION_MIRRED) {
sparx5_mirror_del(entry);
+ } else if (entry->type == FLOW_ACTION_POLICE) {
+ err = sparx5_delete_port_policer(entry);
+ if (err)
+ NL_SET_ERR_MSG_MOD(tmo->common.extack,
+ "Could not delete port policer");
} else if (entry->type == FLOW_ACTION_GOTO) {
err = vcap_enable_lookups(sparx5->vcap_ctrl, ndev,
0, 0, tmo->cookie, false);
@@ -180,6 +217,8 @@ static int sparx5_tc_matchall_stats(struct net_device *ndev,
if (entry->type == FLOW_ACTION_MIRRED) {
sparx5_mirror_stats(entry, &tmo->stats);
+ } else if (entry->type == FLOW_ACTION_POLICE) {
+ sparx5_update_port_policer_stats(ndev, tmo);
} else {
NL_SET_ERR_MSG_MOD(tmo->common.extack, "Unsupported action");
return -EOPNOTSUPP;
--
2.34.1
More information about the linux-arm-kernel
mailing list