* [PATCH iwl-next v2 0/2] ice: fix TC flower filter priority violations
@ 2026-09-16 14:41 Petr Oros
2026-09-16 14:41 ` [PATCH iwl-next v2 1/2] ice: fix TC flower filters matching more than the ip_proto key Petr Oros
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Petr Oros @ 2026-09-16 14:41 UTC (permalink / raw)
To: netdev
Cc: Petr Oros, Tony Nguyen, Przemek Kitszel, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Priyalee Kushwaha, Kiran Patil, Wojciech Drewek,
Michal Swiatkowski, Aleksandr Loktionov, Simon Horman,
intel-wired-lan, linux-kernel
Two fixes for TC flower offload in the legacy switch mode. Both
address the same user visible failure, all traffic on a port silently
disappears after installing a filter chain that mixes offloadable and
non offloadable filters.
The first fix covers filters matching on ip_proto alone. The driver
never programmed the protocol lookup, so a filter like "ip_proto udp
action drop" was installed in hardware as a match on eth_type ipv4 and
dropped every IPv4 packet.
The second fix covers the interaction between software only filters and
offloaded drop filters. The E810 switch gives drop rules absolute
precedence over forwarding rules regardless of recipe priority, so a
lower priority drop offloaded into hardware overrides any higher
priority filter that stayed in software. The driver now tracks filters
it could not offload and refuses to offload a drop filter that would
bypass one of them, keeping the drop functional in software instead.
Tested on E810 with the OS default and comms DDP packages, including
the original reproducer from the report, L2TPv3 pass and drop chains
with and without session ID matching, and a shared block bound to two
PF ports.
---
v2:
- track one entry per cookie and filter_dev pair so a shared block
bound to multiple devices is protected on every binding, and the
delete path drops one entry per DESTROY invocation (sashiko review
via Simon Horman)
- collect Reviewed-by from Aleksandr Loktionov on patch 1
v1: https://lore.kernel.org/all/20260910154824.3603687-1-poros@redhat.com/
Petr Oros (2):
ice: fix TC flower filters matching more than the ip_proto key
ice: don't offload drop filters that bypass higher priority filters
drivers/net/ethernet/intel/ice/ice.h | 1 +
drivers/net/ethernet/intel/ice/ice_main.c | 2 +-
drivers/net/ethernet/intel/ice/ice_repr.c | 2 +-
drivers/net/ethernet/intel/ice/ice_tc_lib.c | 196 +++++++++++++++++++-
drivers/net/ethernet/intel/ice/ice_tc_lib.h | 24 ++-
5 files changed, 212 insertions(+), 13 deletions(-)
--
2.55.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH iwl-next v2 1/2] ice: fix TC flower filters matching more than the ip_proto key 2026-09-16 14:41 [PATCH iwl-next v2 0/2] ice: fix TC flower filter priority violations Petr Oros @ 2026-09-16 14:41 ` Petr Oros 2026-10-12 13:55 ` Holda, Patryk 2026-09-16 14:41 ` [PATCH iwl-next v2 2/2] ice: don't offload drop filters that bypass higher priority filters Petr Oros 2026-09-17 12:38 ` [PATCH iwl-next v2 0/2] ice: fix TC flower filter priority violations Simon Horman 2 siblings, 1 reply; 6+ messages in thread From: Petr Oros @ 2026-09-16 14:41 UTC (permalink / raw) To: netdev Cc: Petr Oros, Aleksandr Loktionov, Tony Nguyen, Przemek Kitszel, Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Priyalee Kushwaha, Kiran Patil, Wojciech Drewek, Michal Swiatkowski, Simon Horman, intel-wired-lan, linux-kernel ice_parse_cls_flower() stores the ip_proto key from the flow rule but never programs a matching lookup unless the filter also matches on L4 ports or the L2TPv3 session ID. A filter like: tc filter add dev $pf ingress protocol ip flower skip_sw \ ip_proto udp action drop is silently programmed into the hardware as a match on eth_type ipv4 alone and drops every IPv4 packet, not just UDP. Program the IP protocol match through the protocol field of the IPv4 header lookup and the next header field of the IPv6 header lookup, the same lookups that are already used for ToS and TTL. The OS default and comms DDP packages provide no profile that extracts the IPv6 next header word, so the IPv6 rule programming currently fails with "Required profiles not found" and the filter falls back to software evaluation instead of over-matching, and the offload starts working with a DDP package that can extract it. Note that the lookup matches the next header byte of the base IPv6 header, so packets carrying extension headers are not matched in hardware and fall back to software evaluation, which under-matches only for skip_sw filters. GTP tunnel and PPPoE filters rewrite the parsed ethertype, the IP header lookups are not available there, so reject an unconsumed ip_proto for them instead of silently widening the match. Filters where ip_proto is implied by an L4 ports or L2TPv3 session ID lookup are not affected. Based on an earlier unapplied patch from Michal Swiatkowski that implemented the IPv4 part [1]. Link: https://lore.kernel.org/intel-wired-lan/20240222123956.2393-3-michal.swiatkowski@linux.intel.com/ [1] Fixes: 0d08a441fb1a ("ice: ndo_setup_tc implementation for PF") Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com> Signed-off-by: Petr Oros <poros@redhat.com> --- v2: - no code changes, collect Reviewed-by from Aleksandr Loktionov v1: https://lore.kernel.org/all/20260910154824.3603687-2-poros@redhat.com/ drivers/net/ethernet/intel/ice/ice_tc_lib.c | 37 +++++++++++++++++++-- drivers/net/ethernet/intel/ice/ice_tc_lib.h | 1 + 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_tc_lib.c b/drivers/net/ethernet/intel/ice/ice_tc_lib.c index d20357c0412731..fbd8cbad150a98 100644 --- a/drivers/net/ethernet/intel/ice/ice_tc_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_tc_lib.c @@ -78,7 +78,8 @@ static int ice_tc_count_lkups(u32 flags, struct ice_tc_flower_fltr *fltr) ICE_TC_FLWR_FIELD_DEST_IPV6 | ICE_TC_FLWR_FIELD_SRC_IPV6)) lkups_cnt++; - if (flags & (ICE_TC_FLWR_FIELD_IP_TOS | ICE_TC_FLWR_FIELD_IP_TTL)) + if (flags & (ICE_TC_FLWR_FIELD_IP_TOS | ICE_TC_FLWR_FIELD_IP_TTL | + ICE_TC_FLWR_FIELD_IP_PROTO)) lkups_cnt++; /* are L2TPv3 options specified? */ @@ -552,7 +553,8 @@ ice_tc_fill_rules(struct ice_hw *hw, u32 flags, } if (headers->l2_key.n_proto == htons(ETH_P_IP) && - (flags & (ICE_TC_FLWR_FIELD_IP_TOS | ICE_TC_FLWR_FIELD_IP_TTL))) { + (flags & (ICE_TC_FLWR_FIELD_IP_TOS | ICE_TC_FLWR_FIELD_IP_TTL | + ICE_TC_FLWR_FIELD_IP_PROTO))) { list[i].type = ice_proto_type_from_ipv4(inner); if (flags & ICE_TC_FLWR_FIELD_IP_TOS) { @@ -567,11 +569,19 @@ ice_tc_fill_rules(struct ice_hw *hw, u32 flags, headers->l3_mask.ttl; } + if (flags & ICE_TC_FLWR_FIELD_IP_PROTO) { + list[i].h_u.ipv4_hdr.protocol = + headers->l3_key.ip_proto; + list[i].m_u.ipv4_hdr.protocol = + headers->l3_mask.ip_proto; + } + i++; } if (headers->l2_key.n_proto == htons(ETH_P_IPV6) && - (flags & (ICE_TC_FLWR_FIELD_IP_TOS | ICE_TC_FLWR_FIELD_IP_TTL))) { + (flags & (ICE_TC_FLWR_FIELD_IP_TOS | ICE_TC_FLWR_FIELD_IP_TTL | + ICE_TC_FLWR_FIELD_IP_PROTO))) { struct ice_ipv6_hdr *hdr_h, *hdr_m; hdr_h = &list[i].h_u.ipv6_hdr; @@ -592,6 +602,11 @@ ice_tc_fill_rules(struct ice_hw *hw, u32 flags, hdr_m->hop_limit = headers->l3_mask.ttl; } + if (flags & ICE_TC_FLWR_FIELD_IP_PROTO) { + hdr_h->next_hdr = headers->l3_key.ip_proto; + hdr_m->next_hdr = headers->l3_mask.ip_proto; + } + i++; } @@ -1737,6 +1752,9 @@ ice_parse_cls_flower(struct net_device *filter_dev, struct ice_vsi *vsi, headers->l2_key.n_proto = cpu_to_be16(n_proto_key); headers->l2_mask.n_proto = cpu_to_be16(n_proto_mask); headers->l3_key.ip_proto = match.key->ip_proto; + headers->l3_mask.ip_proto = match.mask->ip_proto; + if (match.mask->ip_proto) + fltr->flags |= ICE_TC_FLWR_FIELD_IP_PROTO; } if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { @@ -1910,6 +1928,19 @@ ice_parse_cls_flower(struct net_device *filter_dev, struct ice_vsi *vsi, } } + if (fltr->flags & (ICE_TC_FLWR_FIELD_DEST_L4_PORT | + ICE_TC_FLWR_FIELD_SRC_L4_PORT | + ICE_TC_FLWR_FIELD_L2TPV3_SESSID)) + fltr->flags &= ~ICE_TC_FLWR_FIELD_IP_PROTO; + + if ((fltr->flags & ICE_TC_FLWR_FIELD_IP_PROTO) && + headers->l2_key.n_proto != htons(ETH_P_IP) && + headers->l2_key.n_proto != htons(ETH_P_IPV6)) { + NL_SET_ERR_MSG_MOD(fltr->extack, + "IP protocol match is not supported with GTP or PPPoE"); + return -EOPNOTSUPP; + } + /* Ingress filter on representor results in an egress filter in HW * and vice versa */ diff --git a/drivers/net/ethernet/intel/ice/ice_tc_lib.h b/drivers/net/ethernet/intel/ice/ice_tc_lib.h index 8a3ab2f22af9ba..752af65e70b7bf 100644 --- a/drivers/net/ethernet/intel/ice/ice_tc_lib.h +++ b/drivers/net/ethernet/intel/ice/ice_tc_lib.h @@ -38,6 +38,7 @@ #define ICE_TC_FLWR_FIELD_CVLAN_PRIO BIT(28) #define ICE_TC_FLWR_FIELD_VLAN_TPID BIT(29) #define ICE_TC_FLWR_FIELD_PFCP_OPTS BIT(30) +#define ICE_TC_FLWR_FIELD_IP_PROTO BIT(31) #define ICE_TC_FLOWER_MASK_32 0xFFFFFFFF -- 2.55.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* RE: [PATCH iwl-next v2 1/2] ice: fix TC flower filters matching more than the ip_proto key 2026-09-16 14:41 ` [PATCH iwl-next v2 1/2] ice: fix TC flower filters matching more than the ip_proto key Petr Oros @ 2026-10-12 13:55 ` Holda, Patryk 0 siblings, 0 replies; 6+ messages in thread From: Holda, Patryk @ 2026-10-12 13:55 UTC (permalink / raw) To: Oros, Petr, netdev@vger.kernel.org Cc: Oros, Petr, Loktionov, Aleksandr, Nguyen, Anthony L, Kitszel, Przemyslaw, Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Kushwaha, Priyalee, Kiran Patil, Drewek, Wojciech, Michal Swiatkowski, Simon Horman, intel-wired-lan@lists.osuosl.org, linux-kernel@vger.kernel.org > -----Original Message----- > From: Petr Oros <poros@redhat.com> > Sent: Wednesday, September 16, 2026 4:41 PM > To: netdev@vger.kernel.org > Cc: Oros, Petr <poros@redhat.com>; Loktionov, Aleksandr > <aleksandr.loktionov@intel.com>; Nguyen, Anthony L > <anthony.l.nguyen@intel.com>; Kitszel, Przemyslaw > <przemyslaw.kitszel@intel.com>; Andrew Lunn <andrew+netdev@lunn.ch>; > David S. Miller <davem@davemloft.net>; Eric Dumazet > <edumazet@google.com>; Jakub Kicinski <kuba@kernel.org>; Paolo Abeni > <pabeni@redhat.com>; Kushwaha, Priyalee <priyalee.kushwaha@intel.com>; > Kiran Patil <kiran.patil@intel.com>; Drewek, Wojciech > <wojciech.drewek@intel.com>; Michal Swiatkowski > <michal.swiatkowski@linux.intel.com>; Simon Horman <horms@kernel.org>; > intel-wired-lan@lists.osuosl.org; linux-kernel@vger.kernel.org > Subject: [PATCH iwl-next v2 1/2] ice: fix TC flower filters matching more than > the ip_proto key > > ice_parse_cls_flower() stores the ip_proto key from the flow rule but never > programs a matching lookup unless the filter also matches on L4 ports or the > L2TPv3 session ID. A filter like: > > tc filter add dev $pf ingress protocol ip flower skip_sw \ > ip_proto udp action drop > > is silently programmed into the hardware as a match on eth_type ipv4 alone > and drops every IPv4 packet, not just UDP. > > Program the IP protocol match through the protocol field of the IPv4 header > lookup and the next header field of the IPv6 header lookup, the same lookups > that are already used for ToS and TTL. The OS default and comms DDP > packages provide no profile that extracts the IPv6 next header word, so the > IPv6 rule programming currently fails with "Required profiles not found" and > the filter falls back to software evaluation instead of over-matching, and the > offload starts working with a DDP package that can extract it. Note that the > lookup matches the next header byte of the base IPv6 header, so packets > carrying extension headers are not matched in hardware and fall back to > software evaluation, which under-matches only for skip_sw filters. > > GTP tunnel and PPPoE filters rewrite the parsed ethertype, the IP header > lookups are not available there, so reject an unconsumed ip_proto for them > instead of silently widening the match. Filters where ip_proto is implied by an > L4 ports or L2TPv3 session ID lookup are not affected. Based on an earlier > unapplied patch from Michal Swiatkowski that implemented the IPv4 part [1]. > > Link: https://lore.kernel.org/intel-wired-lan/20240222123956.2393-3- > michal.swiatkowski@linux.intel.com/ [1] > Fixes: 0d08a441fb1a ("ice: ndo_setup_tc implementation for PF") > Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com> > Signed-off-by: Petr Oros <poros@redhat.com> > --- > v2: > - no code changes, collect Reviewed-by from Aleksandr Loktionov > > v1: https://lore.kernel.org/all/20260910154824.3603687-2- > poros@redhat.com/ > drivers/net/ethernet/intel/ice/ice_tc_lib.c | 37 +++++++++++++++++++-- > drivers/net/ethernet/intel/ice/ice_tc_lib.h | 1 + > 2 files changed, 35 insertions(+), 3 deletions(-) > > diff --git a/drivers/net/ethernet/intel/ice/ice_tc_lib.c > b/drivers/net/ethernet/intel/ice/ice_tc_lib.c > index d20357c0412731..fbd8cbad150a98 100644 > --- a/drivers/net/ethernet/intel/ice/ice_tc_lib.c > +++ b/drivers/net/ethernet/intel/ice/ice_tc_lib.c > @@ -78,7 +78,8 @@ static int ice_tc_count_lkups(u32 flags, struct > ice_tc_flower_fltr *fltr) > ICE_TC_FLWR_FIELD_DEST_IPV6 | > ICE_TC_FLWR_FIELD_SRC_IPV6)) > lkups_cnt++; > > - if (flags & (ICE_TC_FLWR_FIELD_IP_TOS | > ICE_TC_FLWR_FIELD_IP_TTL)) > + if (flags & (ICE_TC_FLWR_FIELD_IP_TOS | ICE_TC_FLWR_FIELD_IP_TTL > | > + ICE_TC_FLWR_FIELD_IP_PROTO)) > lkups_cnt++; > > /* are L2TPv3 options specified? */ > @@ -552,7 +553,8 @@ ice_tc_fill_rules(struct ice_hw *hw, u32 flags, > } > > if (headers->l2_key.n_proto == htons(ETH_P_IP) && > - (flags & (ICE_TC_FLWR_FIELD_IP_TOS | > ICE_TC_FLWR_FIELD_IP_TTL))) { > + (flags & (ICE_TC_FLWR_FIELD_IP_TOS | ICE_TC_FLWR_FIELD_IP_TTL > | > + ICE_TC_FLWR_FIELD_IP_PROTO))) { > list[i].type = ice_proto_type_from_ipv4(inner); > > if (flags & ICE_TC_FLWR_FIELD_IP_TOS) { @@ -567,11 > +569,19 @@ ice_tc_fill_rules(struct ice_hw *hw, u32 flags, > headers->l3_mask.ttl; > } > > + if (flags & ICE_TC_FLWR_FIELD_IP_PROTO) { > + list[i].h_u.ipv4_hdr.protocol = > + headers->l3_key.ip_proto; > + list[i].m_u.ipv4_hdr.protocol = > + headers->l3_mask.ip_proto; > + } > + > i++; > } > > if (headers->l2_key.n_proto == htons(ETH_P_IPV6) && > - (flags & (ICE_TC_FLWR_FIELD_IP_TOS | > ICE_TC_FLWR_FIELD_IP_TTL))) { > + (flags & (ICE_TC_FLWR_FIELD_IP_TOS | ICE_TC_FLWR_FIELD_IP_TTL > | > + ICE_TC_FLWR_FIELD_IP_PROTO))) { > struct ice_ipv6_hdr *hdr_h, *hdr_m; > > hdr_h = &list[i].h_u.ipv6_hdr; > @@ -592,6 +602,11 @@ ice_tc_fill_rules(struct ice_hw *hw, u32 flags, > hdr_m->hop_limit = headers->l3_mask.ttl; > } > > + if (flags & ICE_TC_FLWR_FIELD_IP_PROTO) { > + hdr_h->next_hdr = headers->l3_key.ip_proto; > + hdr_m->next_hdr = headers->l3_mask.ip_proto; > + } > + > i++; > } > > @@ -1737,6 +1752,9 @@ ice_parse_cls_flower(struct net_device *filter_dev, > struct ice_vsi *vsi, > headers->l2_key.n_proto = cpu_to_be16(n_proto_key); > headers->l2_mask.n_proto = cpu_to_be16(n_proto_mask); > headers->l3_key.ip_proto = match.key->ip_proto; > + headers->l3_mask.ip_proto = match.mask->ip_proto; > + if (match.mask->ip_proto) > + fltr->flags |= ICE_TC_FLWR_FIELD_IP_PROTO; > } > > if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { > @@ -1910,6 +1928,19 @@ ice_parse_cls_flower(struct net_device > *filter_dev, struct ice_vsi *vsi, > } > } > > + if (fltr->flags & (ICE_TC_FLWR_FIELD_DEST_L4_PORT | > + ICE_TC_FLWR_FIELD_SRC_L4_PORT | > + ICE_TC_FLWR_FIELD_L2TPV3_SESSID)) > + fltr->flags &= ~ICE_TC_FLWR_FIELD_IP_PROTO; > + > + if ((fltr->flags & ICE_TC_FLWR_FIELD_IP_PROTO) && > + headers->l2_key.n_proto != htons(ETH_P_IP) && > + headers->l2_key.n_proto != htons(ETH_P_IPV6)) { > + NL_SET_ERR_MSG_MOD(fltr->extack, > + "IP protocol match is not supported with > GTP or PPPoE"); > + return -EOPNOTSUPP; > + } > + > /* Ingress filter on representor results in an egress filter in HW > * and vice versa > */ > diff --git a/drivers/net/ethernet/intel/ice/ice_tc_lib.h > b/drivers/net/ethernet/intel/ice/ice_tc_lib.h > index 8a3ab2f22af9ba..752af65e70b7bf 100644 > --- a/drivers/net/ethernet/intel/ice/ice_tc_lib.h > +++ b/drivers/net/ethernet/intel/ice/ice_tc_lib.h > @@ -38,6 +38,7 @@ > #define ICE_TC_FLWR_FIELD_CVLAN_PRIO BIT(28) > #define ICE_TC_FLWR_FIELD_VLAN_TPID BIT(29) > #define ICE_TC_FLWR_FIELD_PFCP_OPTS BIT(30) > +#define ICE_TC_FLWR_FIELD_IP_PROTO BIT(31) > > #define ICE_TC_FLOWER_MASK_32 0xFFFFFFFF > > -- > 2.55.0 Tested-by: Patryk Holda <patryk.holda@intel.com> ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH iwl-next v2 2/2] ice: don't offload drop filters that bypass higher priority filters 2026-09-16 14:41 [PATCH iwl-next v2 0/2] ice: fix TC flower filter priority violations Petr Oros 2026-09-16 14:41 ` [PATCH iwl-next v2 1/2] ice: fix TC flower filters matching more than the ip_proto key Petr Oros @ 2026-09-16 14:41 ` Petr Oros 2026-10-12 13:54 ` Holda, Patryk 2026-09-17 12:38 ` [PATCH iwl-next v2 0/2] ice: fix TC flower filter priority violations Simon Horman 2 siblings, 1 reply; 6+ messages in thread From: Petr Oros @ 2026-09-16 14:41 UTC (permalink / raw) To: netdev Cc: Petr Oros, Tony Nguyen, Przemek Kitszel, Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Priyalee Kushwaha, Kiran Patil, Wojciech Drewek, Michal Swiatkowski, Aleksandr Loktionov, Simon Horman, intel-wired-lan, linux-kernel The E810 switch gives drop rules absolute precedence over forwarding rules regardless of recipe priority (verified on E810 in every combination of installation order and match specificity). TC however requires filters to be evaluated in priority order, so when a higher priority filter stays software-only because its match or action cannot be offloaded while a lower priority drop filter is offloaded, the hardware drops packets the higher priority filter should have seen: tc filter add dev $pf ingress prio 1 protocol ip flower \ ip_proto l2tp action pass tc filter add dev $pf ingress prio 2 protocol ip flower action drop The pass filter is rejected (unsupported action), the drop filter is offloaded and the hardware drops all IPv4 traffic including the L2TP packets the prio 1 filter should accept. Track filters that were presented to the driver but not offloaded and refuse to offload a drop filter when a higher priority filter on the same device block and direction is software-only or is offloaded with a forwarding action, which the hardware drop would equally override. The refused drop filter keeps working in software. Filters are compared only within one device block since TC priorities are not ordered across blocks, and a shared block bound to multiple devices is tracked once per binding. Filters with skip_sw bypass the check because the user explicitly requested hardware-only operation and refusing would fail the filter add entirely, and the tracking applies to the legacy switch mode only, switchdev pipelines manage rule priorities themselves. The check does not analyze match overlap, so it is conservative, and it runs only at drop offload time. A conflicting filter installed after a drop filter was already offloaded cannot un-offload it, and filters the driver never saw (added while hw-tc-offload was off) are invisible to it. Installing filters in priority order with offload enabled avoids both. Fixes: 0d08a441fb1a ("ice: ndo_setup_tc implementation for PF") Signed-off-by: Petr Oros <poros@redhat.com> --- v2: - track one entry per cookie and filter_dev pair instead of one per cookie, so a shared block bound to multiple devices of one PF is protected on every binding and each DESTROY invocation removes its own entry instead of returning -EINVAL for the later bindings (sashiko review via Simon Horman) - pass the filter device to ice_del_cls_flower() for the lookup above v1: https://lore.kernel.org/all/20260910154824.3603687-3-poros@redhat.com/ drivers/net/ethernet/intel/ice/ice.h | 1 + drivers/net/ethernet/intel/ice/ice_main.c | 2 +- drivers/net/ethernet/intel/ice/ice_repr.c | 2 +- drivers/net/ethernet/intel/ice/ice_tc_lib.c | 159 +++++++++++++++++++- drivers/net/ethernet/intel/ice/ice_tc_lib.h | 23 ++- 5 files changed, 177 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h index db3c7015c56c43..6c596e5a315175 100644 --- a/drivers/net/ethernet/intel/ice/ice.h +++ b/drivers/net/ethernet/intel/ice/ice.h @@ -643,6 +643,7 @@ struct ice_pf { */ u16 num_dmac_chnl_fltrs; struct hlist_head tc_flower_fltr_list; + struct hlist_head tc_sw_fltr_list; u64 supported_rxdids; diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index 3b76eb58666c07..3e6976cbb45c22 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -8392,7 +8392,7 @@ ice_setup_tc_cls_flower(struct ice_netdev_priv *np, case FLOW_CLS_REPLACE: return ice_add_cls_flower(filter_dev, vsi, cls_flower, ingress); case FLOW_CLS_DESTROY: - return ice_del_cls_flower(vsi, cls_flower); + return ice_del_cls_flower(filter_dev, vsi, cls_flower); default: return -EINVAL; } diff --git a/drivers/net/ethernet/intel/ice/ice_repr.c b/drivers/net/ethernet/intel/ice/ice_repr.c index 096566c697f444..312a1bda460035 100644 --- a/drivers/net/ethernet/intel/ice/ice_repr.c +++ b/drivers/net/ethernet/intel/ice/ice_repr.c @@ -223,7 +223,7 @@ ice_repr_setup_tc_cls_flower(struct ice_repr *repr, return ice_add_cls_flower(repr->netdev, repr->src_vsi, flower, true); case FLOW_CLS_DESTROY: - return ice_del_cls_flower(repr->src_vsi, flower); + return ice_del_cls_flower(repr->netdev, repr->src_vsi, flower); default: return -EINVAL; } diff --git a/drivers/net/ethernet/intel/ice/ice_tc_lib.c b/drivers/net/ethernet/intel/ice/ice_tc_lib.c index fbd8cbad150a98..fec6970a74cfbf 100644 --- a/drivers/net/ethernet/intel/ice/ice_tc_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_tc_lib.c @@ -2269,6 +2269,117 @@ ice_find_tc_flower_fltr(struct ice_pf *pf, unsigned long cookie) return NULL; } +/** + * ice_tc_fltr_is_drop - check if a filter carries a drop action + * @cls_flower: offload request describing the filter + * + * Return: true if any action of the filter is a drop, false otherwise. + */ +static bool ice_tc_fltr_is_drop(struct flow_cls_offload *cls_flower) +{ + struct flow_rule *rule = flow_cls_offload_flow_rule(cls_flower); + struct flow_action_entry *act; + int i; + + if (cls_flower->classid) + return false; + + flow_action_for_each(i, act, &rule->action) + if (act->id == FLOW_ACTION_DROP) + return true; + + return false; +} + +/** + * ice_tc_untrack_sw_fltr - forget a tracked software-only filter + * @pf: pointer to PF structure + * @filter_dev: device the filter was requested on + * @cookie: unique filter identifier from the offload request + * + * Return: true if the filter was tracked, false otherwise. + */ +static bool ice_tc_untrack_sw_fltr(struct ice_pf *pf, + struct net_device *filter_dev, + unsigned long cookie) +{ + struct ice_tc_sw_fltr *sw_fltr; + + hlist_for_each_entry(sw_fltr, &pf->tc_sw_fltr_list, node) { + if (sw_fltr->cookie != cookie || + sw_fltr->filter_dev != filter_dev) + continue; + + hlist_del(&sw_fltr->node); + kfree(sw_fltr); + return true; + } + + return false; +} + +/** + * ice_tc_track_sw_fltr - remember a filter that was not offloaded + * @pf: pointer to PF structure + * @filter_dev: device the filter was requested on + * @cls_flower: offload request describing the filter + * @direction: block direction the filter was requested for + */ +static void ice_tc_track_sw_fltr(struct ice_pf *pf, + struct net_device *filter_dev, + struct flow_cls_offload *cls_flower, + enum ice_eswitch_fltr_direction direction) +{ + struct ice_tc_sw_fltr *sw_fltr; + + hlist_for_each_entry(sw_fltr, &pf->tc_sw_fltr_list, node) + if (sw_fltr->cookie == cls_flower->cookie && + sw_fltr->filter_dev == filter_dev) + return; + + sw_fltr = kzalloc_obj(*sw_fltr); + if (!sw_fltr) + return; + + sw_fltr->cookie = cls_flower->cookie; + sw_fltr->filter_dev = filter_dev; + sw_fltr->prio = cls_flower->common.prio; + sw_fltr->direction = direction; + sw_fltr->is_drop = ice_tc_fltr_is_drop(cls_flower); + hlist_add_head(&sw_fltr->node, &pf->tc_sw_fltr_list); +} + +/** + * ice_tc_drop_bypasses_fltr - check if a drop rule would bypass a filter + * @pf: pointer to PF structure + * @filter_dev: device the drop filter is requested on + * @prio: TC priority of the drop filter + * @direction: block direction of the drop filter + * + * Return: true if such a filter exists, false otherwise. + */ +static bool +ice_tc_drop_bypasses_fltr(struct ice_pf *pf, struct net_device *filter_dev, + u32 prio, enum ice_eswitch_fltr_direction direction) +{ + struct ice_tc_flower_fltr *fltr; + struct ice_tc_sw_fltr *sw_fltr; + + hlist_for_each_entry(sw_fltr, &pf->tc_sw_fltr_list, node) + if (sw_fltr->filter_dev == filter_dev && + sw_fltr->direction == direction && sw_fltr->prio < prio && + !sw_fltr->is_drop) + return true; + + hlist_for_each_entry(fltr, &pf->tc_flower_fltr_list, tc_flower_node) + if (fltr->filter_dev == filter_dev && + fltr->direction == direction && fltr->prio < prio && + fltr->action.fltr_act != ICE_DROP_PACKET) + return true; + + return false; +} + /** * ice_add_cls_flower - add TC flower filters * @netdev: Pointer to filter device @@ -2284,14 +2395,24 @@ int ice_add_cls_flower(struct net_device *netdev, struct ice_vsi *vsi, { struct netlink_ext_ack *extack = cls_flower->common.extack; struct net_device *vsi_netdev = vsi->netdev; + enum ice_eswitch_fltr_direction direction; struct ice_tc_flower_fltr *fltr; struct ice_pf *pf = vsi->back; + bool track_sw_fltrs; int err; - if (ice_is_reset_in_progress(pf->state)) - return -EBUSY; - if (test_bit(ICE_FLAG_FW_LLDP_AGENT, pf->flags)) - return -EINVAL; + direction = ingress ? ICE_ESWITCH_FLTR_INGRESS : + ICE_ESWITCH_FLTR_EGRESS; + track_sw_fltrs = !ice_is_eswitch_mode_switchdev(pf); + + if (ice_is_reset_in_progress(pf->state)) { + err = -EBUSY; + goto track_sw; + } + if (test_bit(ICE_FLAG_FW_LLDP_AGENT, pf->flags)) { + err = -EINVAL; + goto track_sw; + } if (ice_is_port_repr_netdev(netdev)) vsi_netdev = netdev; @@ -2304,7 +2425,8 @@ int ice_add_cls_flower(struct net_device *netdev, struct ice_vsi *vsi, */ if (netdev == vsi_netdev) NL_SET_ERR_MSG_MOD(extack, "can't apply TC flower filters, turn ON hw-tc-offload and try again"); - return -EINVAL; + err = -EINVAL; + goto track_sw; } /* avoid duplicate entries, if exists - return error */ @@ -2314,28 +2436,51 @@ int ice_add_cls_flower(struct net_device *netdev, struct ice_vsi *vsi, return -EEXIST; } + if (track_sw_fltrs && !cls_flower->common.skip_sw && + ice_tc_fltr_is_drop(cls_flower) && + ice_tc_drop_bypasses_fltr(pf, netdev, cls_flower->common.prio, + direction)) { + NL_SET_ERR_MSG_MOD(extack, + "Drop filter not offloaded because it would bypass a higher priority filter"); + err = -EOPNOTSUPP; + goto track_sw; + } + /* prep and add TC-flower filter in HW */ err = ice_add_tc_fltr(netdev, vsi, cls_flower, &fltr, ingress); if (err) - return err; + goto track_sw; + + fltr->filter_dev = netdev; + fltr->prio = cls_flower->common.prio; /* add filter into an ordered list */ hlist_add_head(&fltr->tc_flower_node, &pf->tc_flower_fltr_list); return 0; + +track_sw: + if (track_sw_fltrs && !cls_flower->common.skip_sw) + ice_tc_track_sw_fltr(pf, netdev, cls_flower, direction); + return err; } /** * ice_del_cls_flower - delete TC flower filters + * @netdev: Pointer to filter device * @vsi: Pointer to VSI * @cls_flower: Pointer to struct flow_cls_offload */ int -ice_del_cls_flower(struct ice_vsi *vsi, struct flow_cls_offload *cls_flower) +ice_del_cls_flower(struct net_device *netdev, struct ice_vsi *vsi, + struct flow_cls_offload *cls_flower) { struct ice_tc_flower_fltr *fltr; struct ice_pf *pf = vsi->back; int err; + if (ice_tc_untrack_sw_fltr(pf, netdev, cls_flower->cookie)) + return 0; + /* find filter */ fltr = ice_find_tc_flower_fltr(pf, cls_flower->cookie); if (!fltr) { diff --git a/drivers/net/ethernet/intel/ice/ice_tc_lib.h b/drivers/net/ethernet/intel/ice/ice_tc_lib.h index 752af65e70b7bf..47444b7dadd257 100644 --- a/drivers/net/ethernet/intel/ice/ice_tc_lib.h +++ b/drivers/net/ethernet/intel/ice/ice_tc_lib.h @@ -139,12 +139,33 @@ enum ice_eswitch_fltr_direction { ICE_ESWITCH_FLTR_EGRESS, }; +/** + * struct ice_tc_sw_fltr - filter presented to the driver but not offloaded + * @node: node in the pf->tc_sw_fltr_list + * @cookie: unique filter identifier from the offload request + * @filter_dev: device the filter was requested on + * @prio: TC priority, lower value is evaluated first + * @direction: block direction the filter was requested for + * @is_drop: the filter carries a drop action + */ +struct ice_tc_sw_fltr { + struct hlist_node node; + unsigned long cookie; + struct net_device *filter_dev; + u32 prio; + enum ice_eswitch_fltr_direction direction; + bool is_drop; +}; + struct ice_tc_flower_fltr { struct hlist_node tc_flower_node; /* cookie becomes filter_rule_id if rule is added successfully */ unsigned long cookie; + struct net_device *filter_dev; + u32 prio; + /* add_adv_rule returns information like recipe ID, rule_id. Store * those values since they are needed to remove advanced rule */ @@ -214,7 +235,7 @@ static inline int ice_chnl_dmac_fltr_cnt(struct ice_pf *pf) struct ice_vsi *ice_locate_vsi_using_queue(struct ice_vsi *vsi, int queue); int ice_add_cls_flower(struct net_device *netdev, struct ice_vsi *vsi, struct flow_cls_offload *cls_flower, bool ingress); -int ice_del_cls_flower(struct ice_vsi *vsi, +int ice_del_cls_flower(struct net_device *netdev, struct ice_vsi *vsi, struct flow_cls_offload *cls_flower); void ice_replay_tc_fltrs(struct ice_pf *pf); bool ice_is_tunnel_supported(struct net_device *dev); -- 2.55.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* RE: [PATCH iwl-next v2 2/2] ice: don't offload drop filters that bypass higher priority filters 2026-09-16 14:41 ` [PATCH iwl-next v2 2/2] ice: don't offload drop filters that bypass higher priority filters Petr Oros @ 2026-10-12 13:54 ` Holda, Patryk 0 siblings, 0 replies; 6+ messages in thread From: Holda, Patryk @ 2026-10-12 13:54 UTC (permalink / raw) To: Oros, Petr, netdev@vger.kernel.org Cc: Oros, Petr, Nguyen, Anthony L, Kitszel, Przemyslaw, Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Kushwaha, Priyalee, Kiran Patil, Drewek, Wojciech, Michal Swiatkowski, Loktionov, Aleksandr, Simon Horman, intel-wired-lan@lists.osuosl.org, linux-kernel@vger.kernel.org > -----Original Message----- > From: Petr Oros <poros@redhat.com> > Sent: Wednesday, September 16, 2026 4:42 PM > To: netdev@vger.kernel.org > Cc: Oros, Petr <poros@redhat.com>; Nguyen, Anthony L > <anthony.l.nguyen@intel.com>; Kitszel, Przemyslaw > <przemyslaw.kitszel@intel.com>; Andrew Lunn <andrew+netdev@lunn.ch>; > David S. Miller <davem@davemloft.net>; Eric Dumazet > <edumazet@google.com>; Jakub Kicinski <kuba@kernel.org>; Paolo Abeni > <pabeni@redhat.com>; Kushwaha, Priyalee <priyalee.kushwaha@intel.com>; > Kiran Patil <kiran.patil@intel.com>; Drewek, Wojciech > <wojciech.drewek@intel.com>; Michal Swiatkowski > <michal.swiatkowski@linux.intel.com>; Loktionov, Aleksandr > <aleksandr.loktionov@intel.com>; Simon Horman <horms@kernel.org>; intel- > wired-lan@lists.osuosl.org; linux-kernel@vger.kernel.org > Subject: [PATCH iwl-next v2 2/2] ice: don't offload drop filters that bypass > higher priority filters > > The E810 switch gives drop rules absolute precedence over forwarding rules > regardless of recipe priority (verified on E810 in every combination of > installation order and match specificity). TC however requires filters to be > evaluated in priority order, so when a higher priority filter stays software-only > because its match or action cannot be offloaded while a lower priority drop > filter is offloaded, the hardware drops packets the higher priority filter should > have seen: > > tc filter add dev $pf ingress prio 1 protocol ip flower \ > ip_proto l2tp action pass > tc filter add dev $pf ingress prio 2 protocol ip flower action drop > > The pass filter is rejected (unsupported action), the drop filter is offloaded and > the hardware drops all IPv4 traffic including the L2TP packets the prio 1 filter > should accept. > > Track filters that were presented to the driver but not offloaded and refuse to > offload a drop filter when a higher priority filter on the same device block and > direction is software-only or is offloaded with a forwarding action, which the > hardware drop would equally override. The refused drop filter keeps working > in software. Filters are compared only within one device block since TC > priorities are not ordered across blocks, and a shared block bound to multiple > devices is tracked once per binding. Filters with skip_sw bypass the check > because the user explicitly requested hardware-only operation and refusing > would fail the filter add entirely, and the tracking applies to the legacy switch > mode only, switchdev pipelines manage rule priorities themselves. > > The check does not analyze match overlap, so it is conservative, and it runs > only at drop offload time. A conflicting filter installed after a drop filter was > already offloaded cannot un-offload it, and filters the driver never saw (added > while hw-tc-offload was off) are invisible to it. Installing filters in priority order > with offload enabled avoids both. > > Fixes: 0d08a441fb1a ("ice: ndo_setup_tc implementation for PF") > Signed-off-by: Petr Oros <poros@redhat.com> > --- > v2: > - track one entry per cookie and filter_dev pair instead of one per > cookie, so a shared block bound to multiple devices of one PF is > protected on every binding and each DESTROY invocation removes its > own entry instead of returning -EINVAL for the later bindings > (sashiko review via Simon Horman) > - pass the filter device to ice_del_cls_flower() for the lookup above > > v1: https://lore.kernel.org/all/20260910154824.3603687-3- > poros@redhat.com/ > drivers/net/ethernet/intel/ice/ice.h | 1 + > drivers/net/ethernet/intel/ice/ice_main.c | 2 +- > drivers/net/ethernet/intel/ice/ice_repr.c | 2 +- > drivers/net/ethernet/intel/ice/ice_tc_lib.c | 159 +++++++++++++++++++- > drivers/net/ethernet/intel/ice/ice_tc_lib.h | 23 ++- > 5 files changed, 177 insertions(+), 10 deletions(-) > > diff --git a/drivers/net/ethernet/intel/ice/ice.h > b/drivers/net/ethernet/intel/ice/ice.h > index db3c7015c56c43..6c596e5a315175 100644 > --- a/drivers/net/ethernet/intel/ice/ice.h > +++ b/drivers/net/ethernet/intel/ice/ice.h > @@ -643,6 +643,7 @@ struct ice_pf { > */ > u16 num_dmac_chnl_fltrs; > struct hlist_head tc_flower_fltr_list; > + struct hlist_head tc_sw_fltr_list; > > u64 supported_rxdids; > > diff --git a/drivers/net/ethernet/intel/ice/ice_main.c > b/drivers/net/ethernet/intel/ice/ice_main.c > index 3b76eb58666c07..3e6976cbb45c22 100644 > --- a/drivers/net/ethernet/intel/ice/ice_main.c > +++ b/drivers/net/ethernet/intel/ice/ice_main.c > @@ -8392,7 +8392,7 @@ ice_setup_tc_cls_flower(struct ice_netdev_priv > *np, > case FLOW_CLS_REPLACE: > return ice_add_cls_flower(filter_dev, vsi, cls_flower, ingress); > case FLOW_CLS_DESTROY: > - return ice_del_cls_flower(vsi, cls_flower); > + return ice_del_cls_flower(filter_dev, vsi, cls_flower); > default: > return -EINVAL; > } > diff --git a/drivers/net/ethernet/intel/ice/ice_repr.c > b/drivers/net/ethernet/intel/ice/ice_repr.c > index 096566c697f444..312a1bda460035 100644 > --- a/drivers/net/ethernet/intel/ice/ice_repr.c > +++ b/drivers/net/ethernet/intel/ice/ice_repr.c > @@ -223,7 +223,7 @@ ice_repr_setup_tc_cls_flower(struct ice_repr *repr, > return ice_add_cls_flower(repr->netdev, repr->src_vsi, flower, > true); > case FLOW_CLS_DESTROY: > - return ice_del_cls_flower(repr->src_vsi, flower); > + return ice_del_cls_flower(repr->netdev, repr->src_vsi, flower); > default: > return -EINVAL; > } > diff --git a/drivers/net/ethernet/intel/ice/ice_tc_lib.c > b/drivers/net/ethernet/intel/ice/ice_tc_lib.c > index fbd8cbad150a98..fec6970a74cfbf 100644 > --- a/drivers/net/ethernet/intel/ice/ice_tc_lib.c > +++ b/drivers/net/ethernet/intel/ice/ice_tc_lib.c > @@ -2269,6 +2269,117 @@ ice_find_tc_flower_fltr(struct ice_pf *pf, > unsigned long cookie) > return NULL; > } > > +/** > + * ice_tc_fltr_is_drop - check if a filter carries a drop action > + * @cls_flower: offload request describing the filter > + * > + * Return: true if any action of the filter is a drop, false otherwise. > + */ > +static bool ice_tc_fltr_is_drop(struct flow_cls_offload *cls_flower) { > + struct flow_rule *rule = flow_cls_offload_flow_rule(cls_flower); > + struct flow_action_entry *act; > + int i; > + > + if (cls_flower->classid) > + return false; > + > + flow_action_for_each(i, act, &rule->action) > + if (act->id == FLOW_ACTION_DROP) > + return true; > + > + return false; > +} > + > +/** > + * ice_tc_untrack_sw_fltr - forget a tracked software-only filter > + * @pf: pointer to PF structure > + * @filter_dev: device the filter was requested on > + * @cookie: unique filter identifier from the offload request > + * > + * Return: true if the filter was tracked, false otherwise. > + */ > +static bool ice_tc_untrack_sw_fltr(struct ice_pf *pf, > + struct net_device *filter_dev, > + unsigned long cookie) > +{ > + struct ice_tc_sw_fltr *sw_fltr; > + > + hlist_for_each_entry(sw_fltr, &pf->tc_sw_fltr_list, node) { > + if (sw_fltr->cookie != cookie || > + sw_fltr->filter_dev != filter_dev) > + continue; > + > + hlist_del(&sw_fltr->node); > + kfree(sw_fltr); > + return true; > + } > + > + return false; > +} > + > +/** > + * ice_tc_track_sw_fltr - remember a filter that was not offloaded > + * @pf: pointer to PF structure > + * @filter_dev: device the filter was requested on > + * @cls_flower: offload request describing the filter > + * @direction: block direction the filter was requested for */ static > +void ice_tc_track_sw_fltr(struct ice_pf *pf, > + struct net_device *filter_dev, > + struct flow_cls_offload *cls_flower, > + enum ice_eswitch_fltr_direction direction) { > + struct ice_tc_sw_fltr *sw_fltr; > + > + hlist_for_each_entry(sw_fltr, &pf->tc_sw_fltr_list, node) > + if (sw_fltr->cookie == cls_flower->cookie && > + sw_fltr->filter_dev == filter_dev) > + return; > + > + sw_fltr = kzalloc_obj(*sw_fltr); > + if (!sw_fltr) > + return; > + > + sw_fltr->cookie = cls_flower->cookie; > + sw_fltr->filter_dev = filter_dev; > + sw_fltr->prio = cls_flower->common.prio; > + sw_fltr->direction = direction; > + sw_fltr->is_drop = ice_tc_fltr_is_drop(cls_flower); > + hlist_add_head(&sw_fltr->node, &pf->tc_sw_fltr_list); } > + > +/** > + * ice_tc_drop_bypasses_fltr - check if a drop rule would bypass a > +filter > + * @pf: pointer to PF structure > + * @filter_dev: device the drop filter is requested on > + * @prio: TC priority of the drop filter > + * @direction: block direction of the drop filter > + * > + * Return: true if such a filter exists, false otherwise. > + */ > +static bool > +ice_tc_drop_bypasses_fltr(struct ice_pf *pf, struct net_device *filter_dev, > + u32 prio, enum ice_eswitch_fltr_direction direction) { > + struct ice_tc_flower_fltr *fltr; > + struct ice_tc_sw_fltr *sw_fltr; > + > + hlist_for_each_entry(sw_fltr, &pf->tc_sw_fltr_list, node) > + if (sw_fltr->filter_dev == filter_dev && > + sw_fltr->direction == direction && sw_fltr->prio < prio && > + !sw_fltr->is_drop) > + return true; > + > + hlist_for_each_entry(fltr, &pf->tc_flower_fltr_list, tc_flower_node) > + if (fltr->filter_dev == filter_dev && > + fltr->direction == direction && fltr->prio < prio && > + fltr->action.fltr_act != ICE_DROP_PACKET) > + return true; > + > + return false; > +} > + > /** > * ice_add_cls_flower - add TC flower filters > * @netdev: Pointer to filter device > @@ -2284,14 +2395,24 @@ int ice_add_cls_flower(struct net_device > *netdev, struct ice_vsi *vsi, { > struct netlink_ext_ack *extack = cls_flower->common.extack; > struct net_device *vsi_netdev = vsi->netdev; > + enum ice_eswitch_fltr_direction direction; > struct ice_tc_flower_fltr *fltr; > struct ice_pf *pf = vsi->back; > + bool track_sw_fltrs; > int err; > > - if (ice_is_reset_in_progress(pf->state)) > - return -EBUSY; > - if (test_bit(ICE_FLAG_FW_LLDP_AGENT, pf->flags)) > - return -EINVAL; > + direction = ingress ? ICE_ESWITCH_FLTR_INGRESS : > + ICE_ESWITCH_FLTR_EGRESS; > + track_sw_fltrs = !ice_is_eswitch_mode_switchdev(pf); > + > + if (ice_is_reset_in_progress(pf->state)) { > + err = -EBUSY; > + goto track_sw; > + } > + if (test_bit(ICE_FLAG_FW_LLDP_AGENT, pf->flags)) { > + err = -EINVAL; > + goto track_sw; > + } > > if (ice_is_port_repr_netdev(netdev)) > vsi_netdev = netdev; > @@ -2304,7 +2425,8 @@ int ice_add_cls_flower(struct net_device *netdev, > struct ice_vsi *vsi, > */ > if (netdev == vsi_netdev) > NL_SET_ERR_MSG_MOD(extack, "can't apply TC > flower filters, turn ON hw-tc-offload and try again"); > - return -EINVAL; > + err = -EINVAL; > + goto track_sw; > } > > /* avoid duplicate entries, if exists - return error */ @@ -2314,28 > +2436,51 @@ int ice_add_cls_flower(struct net_device *netdev, struct ice_vsi > *vsi, > return -EEXIST; > } > > + if (track_sw_fltrs && !cls_flower->common.skip_sw && > + ice_tc_fltr_is_drop(cls_flower) && > + ice_tc_drop_bypasses_fltr(pf, netdev, cls_flower->common.prio, > + direction)) { > + NL_SET_ERR_MSG_MOD(extack, > + "Drop filter not offloaded because it would > bypass a higher priority filter"); > + err = -EOPNOTSUPP; > + goto track_sw; > + } > + > /* prep and add TC-flower filter in HW */ > err = ice_add_tc_fltr(netdev, vsi, cls_flower, &fltr, ingress); > if (err) > - return err; > + goto track_sw; > + > + fltr->filter_dev = netdev; > + fltr->prio = cls_flower->common.prio; > > /* add filter into an ordered list */ > hlist_add_head(&fltr->tc_flower_node, &pf->tc_flower_fltr_list); > return 0; > + > +track_sw: > + if (track_sw_fltrs && !cls_flower->common.skip_sw) > + ice_tc_track_sw_fltr(pf, netdev, cls_flower, direction); > + return err; > } > > /** > * ice_del_cls_flower - delete TC flower filters > + * @netdev: Pointer to filter device > * @vsi: Pointer to VSI > * @cls_flower: Pointer to struct flow_cls_offload > */ > int > -ice_del_cls_flower(struct ice_vsi *vsi, struct flow_cls_offload *cls_flower) > +ice_del_cls_flower(struct net_device *netdev, struct ice_vsi *vsi, > + struct flow_cls_offload *cls_flower) > { > struct ice_tc_flower_fltr *fltr; > struct ice_pf *pf = vsi->back; > int err; > > + if (ice_tc_untrack_sw_fltr(pf, netdev, cls_flower->cookie)) > + return 0; > + > /* find filter */ > fltr = ice_find_tc_flower_fltr(pf, cls_flower->cookie); > if (!fltr) { > diff --git a/drivers/net/ethernet/intel/ice/ice_tc_lib.h > b/drivers/net/ethernet/intel/ice/ice_tc_lib.h > index 752af65e70b7bf..47444b7dadd257 100644 > --- a/drivers/net/ethernet/intel/ice/ice_tc_lib.h > +++ b/drivers/net/ethernet/intel/ice/ice_tc_lib.h > @@ -139,12 +139,33 @@ enum ice_eswitch_fltr_direction { > ICE_ESWITCH_FLTR_EGRESS, > }; > > +/** > + * struct ice_tc_sw_fltr - filter presented to the driver but not > +offloaded > + * @node: node in the pf->tc_sw_fltr_list > + * @cookie: unique filter identifier from the offload request > + * @filter_dev: device the filter was requested on > + * @prio: TC priority, lower value is evaluated first > + * @direction: block direction the filter was requested for > + * @is_drop: the filter carries a drop action */ struct ice_tc_sw_fltr > +{ > + struct hlist_node node; > + unsigned long cookie; > + struct net_device *filter_dev; > + u32 prio; > + enum ice_eswitch_fltr_direction direction; > + bool is_drop; > +}; > + > struct ice_tc_flower_fltr { > struct hlist_node tc_flower_node; > > /* cookie becomes filter_rule_id if rule is added successfully */ > unsigned long cookie; > > + struct net_device *filter_dev; > + u32 prio; > + > /* add_adv_rule returns information like recipe ID, rule_id. Store > * those values since they are needed to remove advanced rule > */ > @@ -214,7 +235,7 @@ static inline int ice_chnl_dmac_fltr_cnt(struct ice_pf > *pf) struct ice_vsi *ice_locate_vsi_using_queue(struct ice_vsi *vsi, int queue); > int ice_add_cls_flower(struct net_device *netdev, struct ice_vsi *vsi, > struct flow_cls_offload *cls_flower, bool ingress); -int > ice_del_cls_flower(struct ice_vsi *vsi, > +int ice_del_cls_flower(struct net_device *netdev, struct ice_vsi *vsi, > struct flow_cls_offload *cls_flower); void > ice_replay_tc_fltrs(struct ice_pf *pf); bool ice_is_tunnel_supported(struct > net_device *dev); > -- > 2.55.0 Tested-by: Patryk Holda <patryk.holda@intel.com> ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH iwl-next v2 0/2] ice: fix TC flower filter priority violations 2026-09-16 14:41 [PATCH iwl-next v2 0/2] ice: fix TC flower filter priority violations Petr Oros 2026-09-16 14:41 ` [PATCH iwl-next v2 1/2] ice: fix TC flower filters matching more than the ip_proto key Petr Oros 2026-09-16 14:41 ` [PATCH iwl-next v2 2/2] ice: don't offload drop filters that bypass higher priority filters Petr Oros @ 2026-09-17 12:38 ` Simon Horman 2 siblings, 0 replies; 6+ messages in thread From: Simon Horman @ 2026-09-17 12:38 UTC (permalink / raw) To: Petr Oros Cc: netdev, Tony Nguyen, Przemek Kitszel, Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Priyalee Kushwaha, Kiran Patil, Wojciech Drewek, Michal Swiatkowski, Aleksandr Loktionov, intel-wired-lan, linux-kernel On Wed, Sep 16, 2026 at 04:41:28PM +0200, Petr Oros wrote: > Two fixes for TC flower offload in the legacy switch mode. Both > address the same user visible failure, all traffic on a port silently > disappears after installing a filter chain that mixes offloadable and > non offloadable filters. > > The first fix covers filters matching on ip_proto alone. The driver > never programmed the protocol lookup, so a filter like "ip_proto udp > action drop" was installed in hardware as a match on eth_type ipv4 and > dropped every IPv4 packet. > > The second fix covers the interaction between software only filters and > offloaded drop filters. The E810 switch gives drop rules absolute > precedence over forwarding rules regardless of recipe priority, so a > lower priority drop offloaded into hardware overrides any higher > priority filter that stayed in software. The driver now tracks filters > it could not offload and refuses to offload a drop filter that would > bypass one of them, keeping the drop functional in software instead. > > Tested on E810 with the OS default and comms DDP packages, including > the original reproducer from the report, L2TPv3 pass and drop chains > with and without session ID matching, and a shared block bound to two > PF ports. > > --- > v2: > - track one entry per cookie and filter_dev pair so a shared block > bound to multiple devices is protected on every binding, and the > delete path drops one entry per DESTROY invocation (sashiko review > via Simon Horman) > - collect Reviewed-by from Aleksandr Loktionov on patch 1 > > v1: https://lore.kernel.org/all/20260910154824.3603687-1-poros@redhat.com/ Thanks for the update. For the series: Reviewed-by: Simon Horman <horms@kernel.org> ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-10-12 13:55 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-09-16 14:41 [PATCH iwl-next v2 0/2] ice: fix TC flower filter priority violations Petr Oros 2026-09-16 14:41 ` [PATCH iwl-next v2 1/2] ice: fix TC flower filters matching more than the ip_proto key Petr Oros 2026-10-12 13:55 ` Holda, Patryk 2026-09-16 14:41 ` [PATCH iwl-next v2 2/2] ice: don't offload drop filters that bypass higher priority filters Petr Oros 2026-10-12 13:54 ` Holda, Patryk 2026-09-17 12:38 ` [PATCH iwl-next v2 0/2] ice: fix TC flower filter priority violations Simon Horman
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.