There is a metadata flag telling whether a packet is fragmented. It
covers both ipv4 and ipv6 fragments. Use it to offload the "not
fragmented" case.

Offloading the fragmented case isn't that simple, as it needs changes
in the training packets. For ipv4 a bit has to be set in the header,
but for ipv6 an additional header is needed. A packet fragmented like
that results in an incorrect parser result for L4 matching. Because of
that, leave only support for no_frag.

The flag is set for a fragment in either the outer or the inner header
and there is no way to tell the two apart. Reject the match on
tunneled filters then, so that there is always one IP header.
PFCP is excluded from that, as it is considered non-tunneled and its
headers aren't swapped.

Non-tunneled filters are programmed with ICE_NON_TUN, so encapsulated
traffic is matched by tunnel profiles only and can't reach these rules.

Example tc command:
$ tc filter add dev ens786f1r1 ingress protocol ip prio 1 flower \
    src_mac 0a:7a:c1:6b:8e:1c \
    dst_mac be:9d:68:58:51:d8 \
    ip_flags nofrag \
    skip_sw \
    action mirred egress redirect dev ens786f1r0

OVS in default mode is adding this kind of filter, so without this
match the ip flows weren't offloaded to HW. With this change, running
iperf traffic between OVS bridge ports results in offloading all
packets.

Assisted-by: Copilot:claude-opus-5
Signed-off-by: Michal Swiatkowski <[email protected]>
---
 .../ethernet/intel/ice/ice_protocol_type.h    |  1 +
 drivers/net/ethernet/intel/ice/ice_switch.c   |  7 ++++
 drivers/net/ethernet/intel/ice/ice_switch.h   |  1 +
 drivers/net/ethernet/intel/ice/ice_tc_lib.c   | 35 +++++++++++++++++--
 drivers/net/ethernet/intel/ice/ice_tc_lib.h   |  1 +
 5 files changed, 43 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/intel/ice/ice_protocol_type.h 
b/drivers/net/ethernet/intel/ice/ice_protocol_type.h
index 725167d557a8..3a3b66a8ec1a 100644
--- a/drivers/net/ethernet/intel/ice/ice_protocol_type.h
+++ b/drivers/net/ethernet/intel/ice/ice_protocol_type.h
@@ -356,6 +356,7 @@ struct ice_nvgre_hdr {
 #define ICE_PKT_TUNNEL_MAC     BIT(6)
 #define ICE_PKT_TUNNEL_VLAN    BIT(7)
 #define ICE_PKT_TUNNEL_MASK    (ICE_PKT_TUNNEL_MAC | ICE_PKT_TUNNEL_VLAN)
+#define ICE_PKT_IS_FRAG                BIT(8)
 
 /* MDID 22
  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
diff --git a/drivers/net/ethernet/intel/ice/ice_switch.c 
b/drivers/net/ethernet/intel/ice/ice_switch.c
index 4daee252e2a6..060a271be0f5 100644
--- a/drivers/net/ethernet/intel/ice/ice_switch.c
+++ b/drivers/net/ethernet/intel/ice/ice_switch.c
@@ -6009,6 +6009,13 @@ void ice_rule_add_direction_metadata(struct 
ice_adv_lkup_elem *lkup)
                cpu_to_be16(ICE_PKT_FROM_NETWORK);
 }
 
+void ice_rule_add_nofrag_metadata(struct ice_adv_lkup_elem *lkup)
+{
+       lkup->type = ICE_HW_METADATA;
+       lkup->m_u.metadata.flags[ICE_PKT_FLAGS_MDID21] |=
+               cpu_to_be16(ICE_PKT_IS_FRAG);
+}
+
 void ice_rule_add_vlan_metadata(struct ice_adv_lkup_elem *lkup)
 {
        lkup->type = ICE_HW_METADATA;
diff --git a/drivers/net/ethernet/intel/ice/ice_switch.h 
b/drivers/net/ethernet/intel/ice/ice_switch.h
index 671d7a5f359f..b282c476d6b2 100644
--- a/drivers/net/ethernet/intel/ice/ice_switch.h
+++ b/drivers/net/ethernet/intel/ice/ice_switch.h
@@ -347,6 +347,7 @@ int ice_share_res(struct ice_hw *hw, u16 type, u8 shared, 
u16 res_id);
 /* Switch/bridge related commands */
 void ice_rule_add_tunnel_metadata(struct ice_adv_lkup_elem *lkup);
 void ice_rule_add_direction_metadata(struct ice_adv_lkup_elem *lkup);
+void ice_rule_add_nofrag_metadata(struct ice_adv_lkup_elem *lkup);
 void ice_rule_add_vlan_metadata(struct ice_adv_lkup_elem *lkup);
 void ice_rule_add_src_vsi_metadata(struct ice_adv_lkup_elem *lkup);
 int
diff --git a/drivers/net/ethernet/intel/ice/ice_tc_lib.c 
b/drivers/net/ethernet/intel/ice/ice_tc_lib.c
index 560d939b2008..d90a9b52cd5b 100644
--- a/drivers/net/ethernet/intel/ice/ice_tc_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_tc_lib.c
@@ -24,6 +24,7 @@ static int ice_tc_count_lkups(u64 flags, struct 
ice_tc_flower_fltr *fltr)
         * - Direction flag (always present)
         * - ICE_TC_FLWR_FIELD_VLAN_TPID (present if specified)
         * - Tunnel flag (present if tunnel)
+        * - ICE_TC_FLWR_FIELD_NOFRAG (present if specified)
         */
        if (fltr->direction == ICE_ESWITCH_FLTR_EGRESS)
                lkups_cnt++;
@@ -385,6 +386,9 @@ ice_tc_fill_rules(struct ice_hw *hw, u64 flags,
        /* Always add direction metadata */
        ice_rule_add_direction_metadata(&list[ICE_TC_METADATA_LKUP_IDX]);
 
+       if (flags & ICE_TC_FLWR_FIELD_NOFRAG)
+               ice_rule_add_nofrag_metadata(&list[ICE_TC_METADATA_LKUP_IDX]);
+
        if (tc_fltr->direction == ICE_ESWITCH_FLTR_EGRESS) {
                ice_rule_add_src_vsi_metadata(&list[i]);
                i++;
@@ -1876,9 +1880,36 @@ ice_parse_cls_flower(struct net_device *filter_dev, 
struct ice_vsi *vsi,
 
                addr_type = match.key->addr_type;
 
-               if (flow_rule_has_control_flags(match.mask->flags,
-                                               fltr->extack))
+               if (!flow_rule_is_supp_control_flags(FLOW_DIS_IS_FRAGMENT,
+                                                    match.mask->flags,
+                                                    fltr->extack))
                        return -EOPNOTSUPP;
+
+               if (match.mask->flags & FLOW_DIS_IS_FRAGMENT) {
+                       /* HW can only match on the "not fragmented" metadata
+                        * flag, there is no way to match fragments.
+                        */
+                       if (match.key->flags & FLOW_DIS_IS_FRAGMENT) {
+                               NL_SET_ERR_MSG_MOD(fltr->extack,
+                                                  "Match on fragmented packets 
is not supported");
+                               return -EOPNOTSUPP;
+                       }
+
+                       /* The metadata flag is set for a fragment in either
+                        * the outer or the inner header, without a way to
+                        * tell which one. Allow the match only when there is
+                        * one IP header and one ip_flags, so the meaning is
+                        * unambiguous. PFCP is considered non-tunneled.
+                        */
+                       if (fltr->tunnel_type != TNL_LAST &&
+                           fltr->tunnel_type != TNL_PFCP) {
+                               NL_SET_ERR_MSG_MOD(fltr->extack,
+                                                  "Match on fragmentation of 
tunneled packets is not supported");
+                               return -EOPNOTSUPP;
+                       }
+
+                       fltr->flags |= ICE_TC_FLWR_FIELD_NOFRAG;
+               }
        }
 
        if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
diff --git a/drivers/net/ethernet/intel/ice/ice_tc_lib.h 
b/drivers/net/ethernet/intel/ice/ice_tc_lib.h
index 234cc9bafc41..326358cc5884 100644
--- a/drivers/net/ethernet/intel/ice/ice_tc_lib.h
+++ b/drivers/net/ethernet/intel/ice/ice_tc_lib.h
@@ -39,6 +39,7 @@
 #define ICE_TC_FLWR_FIELD_VLAN_TPID            BIT_ULL(29)
 #define ICE_TC_FLWR_FIELD_PFCP_OPTS            BIT_ULL(30)
 #define ICE_TC_FLWR_FIELD_IP_PROTO             BIT_ULL(31)
+#define ICE_TC_FLWR_FIELD_NOFRAG               BIT_ULL(32)
 
 #define ICE_TC_FLOWER_MASK_32   0xFFFFFFFF
 
-- 
2.49.0

Reply via email to