[PATCH net v4 2/3] net: dsa: motorcomm: Resolve the flower action list to one ACL verdict

From: David Yang

Date: Tue Nov 10 2026 - 05:57:03 EST


The hardware resolves one forward decision per ACL rule, but the
translation accumulated every action, so a later action silently changed
the verdict of the ones before it: an `action drop action trap` filter
with skip_sw trapped packets to the CPU instead of dropping them.

Resolve the list against its first terminal action, and ask for software
assistance if any action follows the redirect action.

Fixes: 8eb9e7520b17 ("net: dsa: yt921x: Add ACL support")
Signed-off-by: David Yang <mmyangfl@xxxxxxxxx>
---
drivers/net/dsa/motorcomm/tc.c | 72 +++++++++++++++++-----------------
1 file changed, 37 insertions(+), 35 deletions(-)

diff --git a/drivers/net/dsa/motorcomm/tc.c b/drivers/net/dsa/motorcomm/tc.c
index 90b017a11a1b..429345ad1eda 100644
--- a/drivers/net/dsa/motorcomm/tc.c
+++ b/drivers/net/dsa/motorcomm/tc.c
@@ -1030,55 +1030,54 @@ yt921x_acl_rule_ext_parse_flow_action(struct yt921x_acl_rule_ext *ruleext,
const struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
const struct flow_action *flow_action = &rule->action;
struct netlink_ext_ack *extack = cls->common.extack;
- enum flow_action_id redir_act = NUM_FLOW_ACTIONS;
const struct flow_action_entry *act;
u32 *action = ruleext->r.action;
bool seen_priority = false;
bool seen_police = false;
+ bool terminal = false;
+ bool redir = false;
const char *reason;
unsigned int i;
int res;

memset(action, 0, 3 * sizeof(*action));
- flow_action_for_each(i, act, flow_action)
+ flow_action_for_each(i, act, flow_action) {
+ /* The flow action entry carries no mirred control, so there
+ * is no way to tell whether software would continue past a
+ * redirect and apply the actions after it. Leave the list to
+ * software instead of guessing a hardware verdict.
+ */
+ if (redir) {
+ reason = "Action after redirect";
+ goto fallback;
+ }
+
switch (act->id) {
case FLOW_ACTION_ACCEPT:
+ action[2] |= YT921X_ACL_ACTc_FWD_EN |
+ YT921X_ACL_ACTc_FWD_FWD;
+ terminal = true;
+ break;
case FLOW_ACTION_DROP:
- case FLOW_ACTION_REDIRECT:
- if (redir_act != NUM_FLOW_ACTIONS &&
- redir_act != act->id) {
- reason = "Different redirect actions";
+ action[2] |= YT921X_ACL_ACTc_FWD_EN |
+ YT921X_ACL_ACTc_FWD_REDIR;
+ terminal = true;
+ break;
+ case FLOW_ACTION_REDIRECT: {
+ struct dsa_port *to_dp;
+
+ to_dp = dsa_port_from_netdev(act->dev);
+ if (IS_ERR(to_dp) || to_dp->ds != &priv->ds) {
+ reason = "Redirect to non-local port";
goto fallback;
}
- redir_act = act->id;
-
- switch (act->id) {
- case FLOW_ACTION_ACCEPT:
- action[2] |= YT921X_ACL_ACTc_FWD_EN |
- YT921X_ACL_ACTc_FWD_FWD;
- break;
- case FLOW_ACTION_DROP:
- action[2] |= YT921X_ACL_ACTc_FWD_EN |
- YT921X_ACL_ACTc_FWD_REDIR;
- break;
- case FLOW_ACTION_REDIRECT: {
- struct dsa_port *to_dp;

- to_dp = dsa_port_from_netdev(act->dev);
- if (IS_ERR(to_dp) || to_dp->ds != &priv->ds) {
- reason = "Redirect to non-local port";
- goto fallback;
- }
-
- action[2] |= YT921X_ACL_ACTc_FWD_EN |
- YT921X_ACL_ACTc_FWD_REDIR |
- YT921X_ACL_ACTc_FWD_REDIR_DPORTn(to_dp->index);
- break;
- }
- default:
- break;
- }
+ action[2] |= YT921X_ACL_ACTc_FWD_EN |
+ YT921X_ACL_ACTc_FWD_REDIR |
+ YT921X_ACL_ACTc_FWD_REDIR_DPORTn(to_dp->index);
+ redir = true;
break;
+ }
case FLOW_ACTION_PRIORITY:
if (seen_priority) {
action[0] &= ~YT921X_ACL_ACTa_PRIO_EN;
@@ -1132,15 +1131,18 @@ yt921x_acl_rule_ext_parse_flow_action(struct yt921x_acl_rule_ext *ruleext,
}
fallthrough;
case FLOW_ACTION_TRAP:
- redir_act = FLOW_ACTION_TRAP;
-
action[2] &= ~YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M &
~YT921X_ACL_ACTc_FWD_M;
action[2] |= YT921X_ACL_ACTc_FWD_EN |
YT921X_ACL_ACTc_FWD_TRAP;
+ terminal = true;
break;
}

+ if (terminal)
+ break;
+ }
+
ruleext->r.sw_assisted = !cls->common.skip_sw;
return 0;
}
--
2.53.0