[PATCH net-next 6/6] net: dsa: motorcomm: Add broadcast/multicast policers via tc police
WARM3d
From: David Yang <mmyangfl@gmail.com>
Date: 2026-09-26 21:18:42
Also in:
lkml
Subsystem:
networking drivers, networking [dsa], the rest · Maintainers:
Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Andrew Lunn, Vladimir Oltean, Linus Torvalds
Recognize the following tc flower filters
tc filter add dev lan1 ingress protocol all flower skip_sw \
dst_mac ff:ff:ff:ff:ff:ff action police rate 8mbit burst 64k
(and dst_mac 01:00:00:00:00:00/01:00:00:00:00:00)
and map it onto dedicated per-port storm control, following the sja1105
broadcast/multicast policer approach.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/chip.c | 11 ++
drivers/net/dsa/motorcomm/chip.h | 9 ++
drivers/net/dsa/motorcomm/tc.c | 175 ++++++++++++++++++++++++++++++-
drivers/net/dsa/motorcomm/tc.h | 17 +++
4 files changed, 209 insertions(+), 3 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index 0405f1441f2b..fb2514e01f91 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c@@ -2676,6 +2676,12 @@ static int yt921x_chip_setup_tc(struct yt921x_priv *priv) return res; priv->port_shape_slot_ns = ctrl * op_ns; + ctrl = YT921X_STORM_SLOT_DEF; + res = yt921x_reg_write(priv, YT921X_STORM_SLOT, ctrl); + if (res) + return res; + priv->storm_slot_ns = ctrl * 10 * NSEC_PER_USEC; + return 0; }
@@ -2781,6 +2787,11 @@ static int yt921x_chip_setup(struct yt921x_priv *priv) if (res) return res; + res = yt921x_reg_write(priv, YT921X_STORM_UNK_MCAST, + YT921X_STORM_UNK_MCAST_PORTS_M); + if (res) + return res; + return 0; }
diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h
index d7f2b22ae504..4b2a688663df 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h@@ -567,6 +567,12 @@ struct yt921x_mib { u64 tx_oam; }; +enum yt921x_storm_type { + YT921X_STORM_BCAST, + YT921X_STORM_MCAST, + YT921X_STORM_NUM +}; + struct yt921x_port { unsigned char index;
@@ -586,6 +592,8 @@ struct yt921x_port { unsigned short acl_cnt; + unsigned long storm_tags[YT921X_STORM_NUM]; + #if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS) unsigned char led_duty; unsigned short led_cycle;
@@ -625,6 +633,7 @@ struct yt921x_priv { const struct yt921x_info *info; unsigned int meter_slot_ns; unsigned int port_shape_slot_ns; + unsigned int storm_slot_ns; /* cache of dsa_cpu_ports(ds) */ u16 cpu_ports_mask; unsigned char cycle_ns;
diff --git a/drivers/net/dsa/motorcomm/tc.c b/drivers/net/dsa/motorcomm/tc.c
index 05d4a5957af9..3a0c6e0fbc52 100644
--- a/drivers/net/dsa/motorcomm/tc.c
+++ b/drivers/net/dsa/motorcomm/tc.c@@ -182,6 +182,41 @@ yt921x_marker_tfm_shape(struct yt921x_marker *marker, u64 rate, u64 burst, priv, port, extack); } +static int +yt921x_marker_tfm_storm(struct yt921x_marker *marker, + const struct flow_action_police *police, + struct yt921x_priv *priv, int port) +{ + unsigned int slot_ns = priv->storm_slot_ns; + bool pkt_mode = !!police->rate_pkt_ps; + u64 burst; + u64 rate; + u32 cbs; + u32 cir; + + rate = pkt_mode ? police->rate_pkt_ps : police->rate_bytes_ps; + burst = pkt_mode ? police->burst_pkt : police->burst; + if (pkt_mode) { + rate *= 1024; + burst *= 1024; + } + + /* 1 token = 1 rate */ + cir = div_u64(slot_ns * rate, NSEC_PER_SEC); + if (cir > YT921X_STORM_CIR_MAX) + return -ERANGE; + cbs = div_u64(slot_ns * burst, NSEC_PER_SEC); + if (cbs > YT921X_STORM_CBS_MAX) + return -ERANGE; + + *marker = (typeof(*marker)){ + .cir = cir, + .cbs = cbs, + .pkt_mode = pkt_mode, + }; + return 0; +} + static int yt921x_police_validate(const struct flow_action_police *police, const struct flow_action *action,
@@ -550,6 +585,12 @@ struct yt921x_acl_rule_ext { struct yt921x_acl_rule r; struct yt921x_marker marker; + struct yt921x_marker marker_storm; + + bool non_storm:1; + bool marker_storm_set:1; + bool storm_type_set:1; + unsigned char storm_type; }; static int
@@ -597,6 +638,8 @@ yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext, entry->key[0] |= ntohl(match.key->dst); entry->mask[0] |= ntohl(match.mask->dst); + + ruleext->non_storm = true; } if (match.mask->src) {
@@ -607,6 +650,8 @@ yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext, entry->key[0] |= ntohl(match.key->src); entry->mask[0] |= ntohl(match.mask->src); + + ruleext->non_storm = true; } }
@@ -626,6 +671,8 @@ yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext, entry->key[0] |= ntohl(match.key->dst.s6_addr32[i]); entry->mask[0] |= ntohl(match.mask->dst.s6_addr32[i]); + + ruleext->non_storm = true; } for (unsigned int i = 0; i < 4; i++) {
@@ -639,6 +686,8 @@ yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext, entry->key[0] |= ntohl(match.key->src.s6_addr32[i]); entry->mask[0] |= ntohl(match.mask->src.s6_addr32[i]); + + ruleext->non_storm = true; } }
@@ -661,6 +710,8 @@ yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext, ntohs(match.key->src); entry->mask[0] |= (ntohs(match.mask->dst) << 16) | ntohs(match.mask->src); + + ruleext->non_storm = true; } if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS_RANGE)) {
@@ -684,21 +735,40 @@ yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext, entry->key[0] |= ntohs(match.key->tp_min.dst) << 16; entry->key[1] |= YT921X_ACL_KEYb_L4_DPORT_RANGE_EN; entry->mask[0] |= ntohs(match.key->tp_max.dst) << 16; + + ruleext->non_storm = true; } if (match.mask->tp.src) { entry->key[0] |= ntohs(match.key->tp_min.src); entry->key[1] |= YT921X_ACL_KEYb_L4_SPORT_RANGE_EN; entry->mask[0] |= ntohs(match.key->tp_max.src); + + ruleext->non_storm = true; } } if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { + static const u8 mcast_addr[ETH_ALEN] = {0x01}; struct flow_match_eth_addrs match; u32 mask; flow_rule_match_eth_addrs(rule, &match); + if (!ruleext->non_storm) { + if (is_broadcast_ether_addr(match.mask->dst) && + is_broadcast_ether_addr(match.key->dst)) { + ruleext->storm_type = YT921X_STORM_BCAST; + ruleext->storm_type_set = true; + } else if (ether_addr_equal(match.mask->dst, mcast_addr) && + is_multicast_ether_addr(match.key->dst)) { + ruleext->storm_type = YT921X_STORM_MCAST; + ruleext->storm_type_set = true; + } else { + ruleext->non_storm = true; + } + } + mask = ethaddr_hi4_to_u32(match.mask->dst); if (mask) { entry = yt921x_acl_entries_new(entries, &size,
@@ -781,6 +851,8 @@ yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext, entry->key[0] |= ntohs(match.key->n_proto); entry->mask[0] |= ntohs(match.mask->n_proto); } + + ruleext->non_storm = true; } if (match.mask->ip_proto) {
@@ -820,6 +892,8 @@ yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext, entry->key[0] |= YT921X_ACL_BINa_MISC_IP_PROTO(match.key->ip_proto); entry->mask[0] |= YT921X_ACL_BINa_MISC_IP_PROTO(match.mask->ip_proto); } + + ruleext->non_storm = true; } }
@@ -839,6 +913,8 @@ yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext, set); if (!size) goto err; + + ruleext->non_storm = true; } if (match.mask->flags & FLOW_DIS_FIRST_FRAG) { bool set = match.key->flags & FLOW_DIS_FIRST_FRAG;
@@ -847,6 +923,8 @@ yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext, set); if (!size) goto err; + + ruleext->non_storm = true; } }
@@ -869,6 +947,8 @@ yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext, entry->key[0] |= YT921X_ACL_BINa_MISC_TOS(match.key->tos); entry->mask[0] |= YT921X_ACL_BINa_MISC_TOS(match.mask->tos); + + ruleext->non_storm = true; } }
@@ -889,6 +969,8 @@ yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext, entry->key[0] |= YT921X_ACL_BINa_MISC_TCP_FLAGS(ntohs(match.key->flags)); entry->mask[0] |= YT921X_ACL_BINa_MISC_TCP_FLAGS(ntohs(match.mask->flags)); + + ruleext->non_storm = true; } }
@@ -923,7 +1005,10 @@ yt921x_acl_rule_ext_parse_flow_action(struct yt921x_acl_rule_ext *ruleext, int res; memset(action, 0, 3 * sizeof(*action)); - flow_action_for_each(i, act, flow_action) + flow_action_for_each(i, act, flow_action) { + if (act->id != FLOW_ACTION_POLICE) + ruleext->non_storm = true; + switch (act->id) { case FLOW_ACTION_ACCEPT: case FLOW_ACTION_DROP:
@@ -996,6 +1081,16 @@ yt921x_acl_rule_ext_parse_flow_action(struct yt921x_acl_rule_ext *ruleext, if (res) return res; + if (!ruleext->non_storm) { + res = yt921x_marker_tfm_storm(&ruleext->marker_storm, + police, priv, + port); + if (!res) + ruleext->marker_storm_set = true; + else + ruleext->non_storm = true; + } + res = yt921x_marker_tfm_police(&ruleext->marker, police, 0, priv, port, extack); if (res)
@@ -1022,6 +1117,7 @@ yt921x_acl_rule_ext_parse_flow_action(struct yt921x_acl_rule_ext *ruleext, YT921X_ACL_ACTc_FWD_TRAP; break; } + } ruleext->r.sw_assisted = !cls->common.skip_sw; return 0;
@@ -1056,6 +1152,9 @@ yt921x_acl_rule_ext_parse_flow(struct yt921x_acl_rule_ext *ruleext, int port, return -EOPNOTSUPP; } + ruleext->non_storm = false; + ruleext->marker_storm_set = false; + ruleext->storm_type_set = false; res = yt921x_acl_rule_ext_parse_flow_action(ruleext, cls, priv, port); if (res) return res;
@@ -1488,6 +1587,76 @@ yt921x_acl_add(struct yt921x_priv *priv, return res; } +static int +yt921x_storm_del(struct yt921x_priv *priv, int port, unsigned int type) +{ + struct yt921x_port *pp = &priv->ports[port]; + int res; + + res = yt921x_reg_write(priv, YT921X_STORM_CTRLnm(type, port), 0); + /* the kernel never rolls back on failure */ + + pp->storm_tags[type] = 0; + + return res; +} + +static int +yt921x_storm_add(struct yt921x_priv *priv, int port, unsigned long cookie, + unsigned int type, const struct yt921x_marker *marker, + struct netlink_ext_ack *extack) +{ + struct yt921x_port *pp = &priv->ports[port]; + u32 ctrl; + int res; + + if (pp->storm_tags[type]) + return -ENOSPC; + + ctrl = YT921X_STORM_CTRL_EN | YT921X_STORM_CTRL_CBS(marker->cbs) | + YT921X_STORM_CTRL_CIR(marker->cir); + if (marker->pkt_mode) + ctrl |= YT921X_STORM_CTRL_PKT_MODE; + res = yt921x_reg_write(priv, YT921X_STORM_CTRLnm(type, port), ctrl); + if (res) + return res; + + pp->storm_tags[type] = cookie; + return 0; +} + +static int +yt921x_flower_del(struct yt921x_priv *priv, int port, unsigned long cookie) +{ + struct yt921x_port *pp = &priv->ports[port]; + + for (unsigned int type = YT921X_STORM_BCAST; type < YT921X_STORM_NUM; + type++) + if (pp->storm_tags[type] == cookie) + return yt921x_storm_del(priv, port, type); + + return yt921x_acl_del(priv, TC_SETUP_CLSFLOWER, cookie); +} + +static int +yt921x_flower_add(struct yt921x_priv *priv, int port, + const struct yt921x_acl_rule_ext *ruleext, + struct netlink_ext_ack *extack) +{ + int res; + + if (!ruleext->non_storm && ruleext->marker_storm_set && + ruleext->storm_type_set) { + res = yt921x_storm_add(priv, port, ruleext->r.tag, + ruleext->storm_type, + &ruleext->marker_storm, extack); + if (res != -ENOSPC) + return res; + } + + return yt921x_acl_add(priv, ruleext, extack); +} + int yt921x_dsa_cls_flower_stats(struct dsa_switch *ds, int port, struct flow_cls_offload *cls, bool ingress)
@@ -1511,7 +1680,7 @@ yt921x_dsa_cls_flower_del(struct dsa_switch *ds, int port, int res; mutex_lock(&priv->reg_lock); - res = yt921x_acl_del(priv, TC_SETUP_CLSFLOWER, cls->cookie); + res = yt921x_flower_del(priv, port, cls->cookie); mutex_unlock(&priv->reg_lock); return res;
@@ -1532,7 +1701,7 @@ yt921x_dsa_cls_flower_add(struct dsa_switch *ds, int port, return res; mutex_lock(&priv->reg_lock); - res = yt921x_acl_add(priv, &ruleext, extack); + res = yt921x_flower_add(priv, port, &ruleext, extack); mutex_unlock(&priv->reg_lock); return res;
diff --git a/drivers/net/dsa/motorcomm/tc.h b/drivers/net/dsa/motorcomm/tc.h
index f2ea0ac32f7e..a9107f97a1b9 100644
--- a/drivers/net/dsa/motorcomm/tc.h
+++ b/drivers/net/dsa/motorcomm/tc.h@@ -263,12 +263,26 @@ enum yt921x_l4_type { #define YT921X_PORTn_RATE(port) (0x220000 + 4 * (port)) #define YT921X_PORT_RATE_GAP_VALUE GENMASK(4, 0) /* default 20 */ +#define YT921X_STORM_SLOT 0x220100 +#define YT921X_STORM_SLOT_SLOT_M GENMASK(11, 0) #define YT921X_METER_SLOT 0x220104 #define YT921X_METER_SLOT_SLOT_M GENMASK(11, 0) #define YT921X_PORTn_METER(port) (0x220108 + 4 * (port)) #define YT921X_PORT_METER_EN BIT(4) #define YT921X_PORT_METER_ID_M GENMASK(3, 0) #define YT921X_PORT_METER_ID(x) FIELD_PREP(YT921X_PORT_METER_ID_M, (x)) +#define YT921X_STORM_UNK_MCAST 0x220140 +#define YT921X_STORM_UNK_MCAST_PORTS_M GENMASK(10, 0) +#define YT921X_STORM_UNK_MCAST_PORT(x) BIT(x) +#define YT921X_STORM_CTRLnm(type, port) (0x220200 + 4 * (11 * (type) + (port))) +#define YT921X_STORM_CTRL_CIR_M GENMASK(31, 13) +#define YT921X_STORM_CTRL_CIR(x) FIELD_PREP(YT921X_STORM_CTRL_CIR_M, (x)) +#define YT921X_STORM_CTRL_CBS_M GENMASK(12, 3) +#define YT921X_STORM_CTRL_CBS(x) FIELD_PREP(YT921X_STORM_CTRL_CBS_M, (x)) +#define YT921X_STORM_CTRL_BYTE_MODE_INCLUDE_GAP BIT(2) /* +GAP_VALUE bytes each packet */ +#define YT921X_STORM_CTRL_PKT_MODE BIT(1) /* 0: byte rate mode */ +#define YT921X_STORM_CTRL_EN BIT(0) +#define YT921X_STORM_STATnm(type, port) (0x220400 + 4 * (11 * (type) + (port))) #define YT921X_METERn_CTRL(x) (0x220800 + 0x10 * (x)) #define YT921X_METER_CTRLc_METER_EN BIT(14) #define YT921X_METER_CTRLc_TOKEN_OVERFLOW_EN BIT(13) /* RFC4115: yellow use unused green bw */
@@ -339,6 +353,9 @@ enum yt921x_l4_type { #define YT921X_SHAPE_UNIT_MAX ((1 << 3) - 1) #define YT921X_SHAPE_CIR_MAX ((1 << 18) - 1) #define YT921X_SHAPE_CBS_MAX ((1 << 14) - 1) +#define YT921X_STORM_SLOT_DEF 100 +#define YT921X_STORM_CIR_MAX ((1 << 19) - 1) +#define YT921X_STORM_CBS_MAX ((1 << 10) - 1) #define YT921X_ACL_PRIO_NUM 512 #define YT921X_UDF_NUM 8
--
2.53.0