]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ice: support egress drop rules on PF
authorLarysa Zaremba <larysa.zaremba@intel.com>
Fri, 14 Feb 2025 08:50:39 +0000 (09:50 +0100)
committerTony Nguyen <anthony.l.nguyen@intel.com>
Fri, 11 Apr 2025 16:47:43 +0000 (09:47 -0700)
tc clsact qdisc allows us to add offloaded egress rules with commands such
as the following one:

tc filter add dev <ifname> egress protocol lldp flower skip_sw action drop

Support the egress rule drop action when added to PF, with a few caveats:
* in switchdev mode, all PF traffic has to go uplink with an exception for
  LLDP that can be delegated to a single VSI at a time
* in legacy mode, we cannot delegate LLDP functionality to another VSI, so
  such packets from PF should not be blocked.

Also, simplify the rule direction logic, it was previously derived from
actions, but actually can be inherited from the tc block (and flipped in
case of port representors).

Reviewed-by: Michal Swiatkowski <michal.swiatkowski@linux.intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Tested-by: Rafal Romanowski <rafal.romanowski@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
drivers/net/ethernet/intel/ice/ice_eswitch.c
drivers/net/ethernet/intel/ice/ice_main.c
drivers/net/ethernet/intel/ice/ice_repr.c
drivers/net/ethernet/intel/ice/ice_tc_lib.c
drivers/net/ethernet/intel/ice/ice_tc_lib.h
drivers/net/ethernet/intel/ice/ice_txrx.c

index ed21d7f55ac11b9fd573d02fb5ae95167eaf734d..206a7d839aeffcee32438d4b037d16c761283858 100644 (file)
@@ -245,6 +245,10 @@ ice_eswitch_set_target_vsi(struct sk_buff *skb,
        u64 cd_cmd, dst_vsi;
 
        if (!dst) {
+               struct ethhdr *eth = (struct ethhdr *)skb_mac_header(skb);
+
+               if (unlikely(eth->h_proto == htons(ETH_P_LLDP)))
+                       return;
                cd_cmd = ICE_TX_CTX_DESC_SWTCH_UPLINK << ICE_TXD_CTX_QW1_CMD_S;
                off->cd_qw1 |= (cd_cmd | ICE_TX_DESC_DTYPE_CTX);
        } else {
index d390157b59fe1873ddab78323cdc0bbaea6ad0c5..1fbe13ee93a8d721607a645acc437f0fe6af52cf 100644 (file)
@@ -8330,11 +8330,16 @@ void ice_tx_timeout(struct net_device *netdev, unsigned int txqueue)
  * @np: net device to configure
  * @filter_dev: device on which filter is added
  * @cls_flower: offload data
+ * @ingress: if the rule is added to an ingress block
+ *
+ * Return: 0 if the flower was successfully added or deleted,
+ *        negative error code otherwise.
  */
 static int
 ice_setup_tc_cls_flower(struct ice_netdev_priv *np,
                        struct net_device *filter_dev,
-                       struct flow_cls_offload *cls_flower)
+                       struct flow_cls_offload *cls_flower,
+                       bool ingress)
 {
        struct ice_vsi *vsi = np->vsi;
 
@@ -8343,7 +8348,7 @@ ice_setup_tc_cls_flower(struct ice_netdev_priv *np,
 
        switch (cls_flower->command) {
        case FLOW_CLS_REPLACE:
-               return ice_add_cls_flower(filter_dev, vsi, cls_flower);
+               return ice_add_cls_flower(filter_dev, vsi, cls_flower, ingress);
        case FLOW_CLS_DESTROY:
                return ice_del_cls_flower(vsi, cls_flower);
        default:
@@ -8352,20 +8357,46 @@ ice_setup_tc_cls_flower(struct ice_netdev_priv *np,
 }
 
 /**
- * ice_setup_tc_block_cb - callback handler registered for TC block
+ * ice_setup_tc_block_cb_ingress - callback handler for ingress TC block
  * @type: TC SETUP type
  * @type_data: TC flower offload data that contains user input
  * @cb_priv: netdev private data
+ *
+ * Return: 0 if the setup was successful, negative error code otherwise.
  */
 static int
-ice_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
+ice_setup_tc_block_cb_ingress(enum tc_setup_type type, void *type_data,
+                             void *cb_priv)
 {
        struct ice_netdev_priv *np = cb_priv;
 
        switch (type) {
        case TC_SETUP_CLSFLOWER:
                return ice_setup_tc_cls_flower(np, np->vsi->netdev,
-                                              type_data);
+                                              type_data, true);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+/**
+ * ice_setup_tc_block_cb_egress - callback handler for egress TC block
+ * @type: TC SETUP type
+ * @type_data: TC flower offload data that contains user input
+ * @cb_priv: netdev private data
+ *
+ * Return: 0 if the setup was successful, negative error code otherwise.
+ */
+static int
+ice_setup_tc_block_cb_egress(enum tc_setup_type type, void *type_data,
+                            void *cb_priv)
+{
+       struct ice_netdev_priv *np = cb_priv;
+
+       switch (type) {
+       case TC_SETUP_CLSFLOWER:
+               return ice_setup_tc_cls_flower(np, np->vsi->netdev,
+                                              type_data, false);
        default:
                return -EOPNOTSUPP;
        }
@@ -9310,16 +9341,32 @@ ice_setup_tc(struct net_device *netdev, enum tc_setup_type type,
             void *type_data)
 {
        struct ice_netdev_priv *np = netdev_priv(netdev);
+       enum flow_block_binder_type binder_type;
        struct ice_pf *pf = np->vsi->back;
+       flow_setup_cb_t *flower_handler;
        bool locked = false;
        int err;
 
        switch (type) {
        case TC_SETUP_BLOCK:
+               binder_type =
+                       ((struct flow_block_offload *)type_data)->binder_type;
+
+               switch (binder_type) {
+               case FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS:
+                       flower_handler = ice_setup_tc_block_cb_ingress;
+                       break;
+               case FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS:
+                       flower_handler = ice_setup_tc_block_cb_egress;
+                       break;
+               default:
+                       return -EOPNOTSUPP;
+               }
+
                return flow_block_cb_setup_simple(type_data,
                                                  &ice_block_cb_list,
-                                                 ice_setup_tc_block_cb,
-                                                 np, np, true);
+                                                 flower_handler,
+                                                 np, np, false);
        case TC_SETUP_QDISC_MQPRIO:
                if (ice_is_eswitch_mode_switchdev(pf)) {
                        netdev_err(netdev, "TC MQPRIO offload not supported, switchdev is enabled\n");
@@ -9380,7 +9427,7 @@ ice_indr_setup_block_cb(enum tc_setup_type type, void *type_data,
        case TC_SETUP_CLSFLOWER:
                return ice_setup_tc_cls_flower(np, priv->netdev,
                                               (struct flow_cls_offload *)
-                                              type_data);
+                                              type_data, false);
        default:
                return -EOPNOTSUPP;
        }
index fb7a1b9a4313919574b08da9fbd0877010c08dd9..f81bf60f8365185146f6b05a2e803f92499284f5 100644 (file)
@@ -219,7 +219,8 @@ ice_repr_setup_tc_cls_flower(struct ice_repr *repr,
 {
        switch (flower->command) {
        case FLOW_CLS_REPLACE:
-               return ice_add_cls_flower(repr->netdev, repr->src_vsi, flower);
+               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);
        default:
index d8d28d74222a635bb302ede1c50d59c7084f1cbf..229cd29ff92a463ed573bf29fc0ae675ac372c6f 100644 (file)
@@ -681,26 +681,26 @@ static int ice_tc_setup_action(struct net_device *filter_dev,
        fltr->action.fltr_act = action;
 
        if (ice_is_port_repr_netdev(filter_dev) &&
-           ice_is_port_repr_netdev(target_dev)) {
+           ice_is_port_repr_netdev(target_dev) &&
+           fltr->direction == ICE_ESWITCH_FLTR_EGRESS) {
                repr = ice_netdev_to_repr(target_dev);
 
                fltr->dest_vsi = repr->src_vsi;
-               fltr->direction = ICE_ESWITCH_FLTR_EGRESS;
        } else if (ice_is_port_repr_netdev(filter_dev) &&
-                  ice_tc_is_dev_uplink(target_dev)) {
+                  ice_tc_is_dev_uplink(target_dev) &&
+                  fltr->direction == ICE_ESWITCH_FLTR_EGRESS) {
                repr = ice_netdev_to_repr(filter_dev);
 
                fltr->dest_vsi = repr->src_vsi->back->eswitch.uplink_vsi;
-               fltr->direction = ICE_ESWITCH_FLTR_EGRESS;
        } else if (ice_tc_is_dev_uplink(filter_dev) &&
-                  ice_is_port_repr_netdev(target_dev)) {
+                  ice_is_port_repr_netdev(target_dev) &&
+                  fltr->direction == ICE_ESWITCH_FLTR_INGRESS) {
                repr = ice_netdev_to_repr(target_dev);
 
                fltr->dest_vsi = repr->src_vsi;
-               fltr->direction = ICE_ESWITCH_FLTR_INGRESS;
        } else {
                NL_SET_ERR_MSG_MOD(fltr->extack,
-                                  "Unsupported netdevice in switchdev mode");
+                                  "The action is not supported for this netdevice");
                return -EINVAL;
        }
 
@@ -713,13 +713,11 @@ ice_tc_setup_drop_action(struct net_device *filter_dev,
 {
        fltr->action.fltr_act = ICE_DROP_PACKET;
 
-       if (ice_is_port_repr_netdev(filter_dev)) {
-               fltr->direction = ICE_ESWITCH_FLTR_EGRESS;
-       } else if (ice_tc_is_dev_uplink(filter_dev)) {
-               fltr->direction = ICE_ESWITCH_FLTR_INGRESS;
-       } else {
+       if (!ice_tc_is_dev_uplink(filter_dev) &&
+           !(ice_is_port_repr_netdev(filter_dev) &&
+             fltr->direction == ICE_ESWITCH_FLTR_INGRESS)) {
                NL_SET_ERR_MSG_MOD(fltr->extack,
-                                  "Unsupported netdevice in switchdev mode");
+                                  "The action is not supported for this netdevice");
                return -EINVAL;
        }
 
@@ -809,6 +807,11 @@ ice_eswitch_add_tc_fltr(struct ice_vsi *vsi, struct ice_tc_flower_fltr *fltr)
                rule_info.sw_act.flag |= ICE_FLTR_RX;
                rule_info.sw_act.src = hw->pf_id;
                rule_info.flags_info.act = ICE_SINGLE_ACT_LB_ENABLE;
+       } else if (fltr->direction == ICE_ESWITCH_FLTR_EGRESS &&
+                  !fltr->dest_vsi && vsi == vsi->back->eswitch.uplink_vsi) {
+               /* PF to Uplink */
+               rule_info.sw_act.flag |= ICE_FLTR_TX;
+               rule_info.sw_act.src = vsi->idx;
        } else if (fltr->direction == ICE_ESWITCH_FLTR_EGRESS &&
                   fltr->dest_vsi == vsi->back->eswitch.uplink_vsi) {
                /* VF to Uplink */
@@ -1051,8 +1054,13 @@ ice_add_tc_flower_adv_fltr(struct ice_vsi *vsi,
                        tc_fltr->action.fwd.q.hw_queue, lkups_cnt);
                break;
        case ICE_DROP_PACKET:
-               rule_info.sw_act.flag |= ICE_FLTR_RX;
-               rule_info.sw_act.src = hw->pf_id;
+               if (tc_fltr->direction == ICE_ESWITCH_FLTR_EGRESS) {
+                       rule_info.sw_act.flag |= ICE_FLTR_TX;
+                       rule_info.sw_act.src = vsi->idx;
+               } else {
+                       rule_info.sw_act.flag |= ICE_FLTR_RX;
+                       rule_info.sw_act.src = hw->pf_id;
+               }
                rule_info.priority = ICE_SWITCH_FLTR_PRIO_VSI;
                break;
        default:
@@ -1458,11 +1466,16 @@ ice_parse_tunnel_attr(struct net_device *dev, struct flow_rule *rule,
  * @filter_dev: Pointer to device on which filter is being added
  * @f: Pointer to struct flow_cls_offload
  * @fltr: Pointer to filter structure
+ * @ingress: if the rule is added to an ingress block
+ *
+ * Return: 0 if the flower was parsed successfully, -EINVAL if the flower
+ *        cannot be parsed, -EOPNOTSUPP if such filter cannot be configured
+ *        for the given VSI.
  */
 static int
 ice_parse_cls_flower(struct net_device *filter_dev, struct ice_vsi *vsi,
                     struct flow_cls_offload *f,
-                    struct ice_tc_flower_fltr *fltr)
+                    struct ice_tc_flower_fltr *fltr, bool ingress)
 {
        struct ice_tc_flower_lyr_2_4_hdrs *headers = &fltr->outer_headers;
        struct flow_rule *rule = flow_cls_offload_flow_rule(f);
@@ -1546,6 +1559,20 @@ ice_parse_cls_flower(struct net_device *filter_dev, struct ice_vsi *vsi,
                        fltr->flags |= ICE_TC_FLWR_FIELD_ETH_TYPE_ID;
                }
 
+               if (!ingress) {
+                       bool switchdev =
+                               ice_is_eswitch_mode_switchdev(vsi->back);
+
+                       if (switchdev != (n_proto_key == ETH_P_LLDP)) {
+                               NL_SET_ERR_MSG_FMT_MOD(fltr->extack,
+                                                      "%sLLDP filtering is not supported on egress in %s mode",
+                                                      switchdev ? "Non-" : "",
+                                                      switchdev ? "switchdev" :
+                                                                  "legacy");
+                               return -EOPNOTSUPP;
+                       }
+               }
+
                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;
@@ -1721,6 +1748,14 @@ ice_parse_cls_flower(struct net_device *filter_dev, struct ice_vsi *vsi,
                        return -EINVAL;
                }
        }
+
+       /* Ingress filter on representor results in an egress filter in HW
+        * and vice versa
+        */
+       ingress = ice_is_port_repr_netdev(filter_dev) ? !ingress : ingress;
+       fltr->direction = ingress ? ICE_ESWITCH_FLTR_INGRESS :
+                                   ICE_ESWITCH_FLTR_EGRESS;
+
        return 0;
 }
 
@@ -1970,14 +2005,18 @@ static int ice_del_tc_fltr(struct ice_vsi *vsi, struct ice_tc_flower_fltr *fltr)
  * @vsi: Pointer to VSI
  * @f: Pointer to flower offload structure
  * @__fltr: Pointer to struct ice_tc_flower_fltr
+ * @ingress: if the rule is added to an ingress block
  *
  * This function parses TC-flower input fields, parses action,
  * and adds a filter.
+ *
+ * Return: 0 if the filter was successfully added,
+ *        negative error code otherwise.
  */
 static int
 ice_add_tc_fltr(struct net_device *netdev, struct ice_vsi *vsi,
                struct flow_cls_offload *f,
-               struct ice_tc_flower_fltr **__fltr)
+               struct ice_tc_flower_fltr **__fltr, bool ingress)
 {
        struct ice_tc_flower_fltr *fltr;
        int err;
@@ -1994,7 +2033,7 @@ ice_add_tc_fltr(struct net_device *netdev, struct ice_vsi *vsi,
        fltr->src_vsi = vsi;
        INIT_HLIST_NODE(&fltr->tc_flower_node);
 
-       err = ice_parse_cls_flower(netdev, vsi, f, fltr);
+       err = ice_parse_cls_flower(netdev, vsi, f, fltr, ingress);
        if (err < 0)
                goto err;
 
@@ -2037,10 +2076,13 @@ ice_find_tc_flower_fltr(struct ice_pf *pf, unsigned long cookie)
  * @netdev: Pointer to filter device
  * @vsi: Pointer to VSI
  * @cls_flower: Pointer to flower offload structure
+ * @ingress: if the rule is added to an ingress block
+ *
+ * Return: 0 if the flower was successfully added,
+ *        negative error code otherwise.
  */
-int
-ice_add_cls_flower(struct net_device *netdev, struct ice_vsi *vsi,
-                  struct flow_cls_offload *cls_flower)
+int ice_add_cls_flower(struct net_device *netdev, struct ice_vsi *vsi,
+                      struct flow_cls_offload *cls_flower, bool ingress)
 {
        struct netlink_ext_ack *extack = cls_flower->common.extack;
        struct net_device *vsi_netdev = vsi->netdev;
@@ -2075,7 +2117,7 @@ ice_add_cls_flower(struct net_device *netdev, struct ice_vsi *vsi,
        }
 
        /* prep and add TC-flower filter in HW */
-       err = ice_add_tc_fltr(netdev, vsi, cls_flower, &fltr);
+       err = ice_add_tc_fltr(netdev, vsi, cls_flower, &fltr, ingress);
        if (err)
                return err;
 
index d84f153517ec5c5a513b7e427d8c633c322a4c41..df9f90f793b9965908ff9a565fbebdf56c88844b 100644 (file)
@@ -211,11 +211,10 @@ 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);
-int
-ice_del_cls_flower(struct ice_vsi *vsi, struct flow_cls_offload *cls_flower);
+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,
+                      struct flow_cls_offload *cls_flower);
 void ice_replay_tc_fltrs(struct ice_pf *pf);
 bool ice_is_tunnel_supported(struct net_device *dev);
 
index 1e4f6f6ee449c96ed1afe6fa4c677a7b2caf53e8..0e5107fe62ad5b7ac594ea71a192a8fbaa932f1e 100644 (file)
@@ -2440,19 +2440,20 @@ ice_xmit_frame_ring(struct sk_buff *skb, struct ice_tx_ring *tx_ring)
 
        /* allow CONTROL frames egress from main VSI if FW LLDP disabled */
        eth = (struct ethhdr *)skb_mac_header(skb);
-       if (unlikely((skb->priority == TC_PRIO_CONTROL ||
-                     eth->h_proto == htons(ETH_P_LLDP)) &&
-                    vsi->type == ICE_VSI_PF &&
-                    vsi->port_info->qos_cfg.is_sw_lldp))
-               offload.cd_qw1 |= (u64)(ICE_TX_DESC_DTYPE_CTX |
-                                       ICE_TX_CTX_DESC_SWTCH_UPLINK <<
-                                       ICE_TXD_CTX_QW1_CMD_S);
 
-       ice_tstamp(tx_ring, skb, first, &offload);
        if ((ice_is_switchdev_running(vsi->back) ||
             ice_lag_is_switchdev_running(vsi->back)) &&
            vsi->type != ICE_VSI_SF)
                ice_eswitch_set_target_vsi(skb, &offload);
+       else if (unlikely((skb->priority == TC_PRIO_CONTROL ||
+                          eth->h_proto == htons(ETH_P_LLDP)) &&
+                          vsi->type == ICE_VSI_PF &&
+                          vsi->port_info->qos_cfg.is_sw_lldp))
+               offload.cd_qw1 |= (u64)(ICE_TX_DESC_DTYPE_CTX |
+                                       ICE_TX_CTX_DESC_SWTCH_UPLINK <<
+                                       ICE_TXD_CTX_QW1_CMD_S);
+
+       ice_tstamp(tx_ring, skb, first, &offload);
 
        if (offload.cd_qw1 & ICE_TX_DESC_DTYPE_CTX) {
                struct ice_tx_ctx_desc *cdesc;