[PATCH net v2 2/3] net: dsa: motorcomm: Resolve the flower action list to one ACL verdict
From: David Yang
Date: Tue Nov 03 2026 - 05:09:01 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. Consecutive
redirects merge into the destination port mask, since a flow action
entry carries no mirred control verdict to tell whether software would
have continued; an action asking for a second decision is rejected when
skip_sw is set and left to the software path otherwise.
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 8835907366bf..5d2db04f3385 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;
const char *reason = NULL;
bool seen_police = false;
+ bool terminal = false;
+ bool redir = false;
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) {
+ /* Only another redirect, or a drop, can follow a redirect: a
+ * drop is an empty destination set, so the software verdict is
+ * the same whichever control the redirect carries.
+ */
+ if (redir && act->id != FLOW_ACTION_REDIRECT &&
+ act->id != FLOW_ACTION_DROP) {
+ 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