sfc: add mac source and destination pedit action offload
authorPieter Jansen van Vuuren <pieter.jansen-van-vuuren@amd.com>
Thu, 24 Aug 2023 11:28:38 +0000 (12:28 +0100)
committerDavid S. Miller <davem@davemloft.net>
Sun, 27 Aug 2023 05:56:54 +0000 (06:56 +0100)
Introduce the first pedit set offload functionality for the sfc driver.
In addition to this, add offload functionality for both mac source and
destination pedit set actions.

Co-developed-by: Edward Cree <ecree.xilinx@gmail.com>
Signed-off-by: Edward Cree <ecree.xilinx@gmail.com>
Signed-off-by: Pieter Jansen van Vuuren <pieter.jansen-van-vuuren@amd.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/sfc/tc.c

index 8a9fc2f475145461e82ab7d89f5552d2ac2d921b..b9d7c4bb7e696380de5a78e4ed80cfdf1e4c7492 100644 (file)
@@ -116,7 +116,7 @@ static const struct rhashtable_params efx_tc_recirc_ht_params = {
        .head_offset    = offsetof(struct efx_tc_recirc_id, linkage),
 };
 
-static struct efx_tc_mac_pedit_action __maybe_unused *efx_tc_flower_get_mac(struct efx_nic *efx,
+static struct efx_tc_mac_pedit_action *efx_tc_flower_get_mac(struct efx_nic *efx,
                                                             unsigned char h_addr[ETH_ALEN],
                                                             struct netlink_ext_ack *extack)
 {
@@ -155,7 +155,7 @@ out_remove:
        return ERR_PTR(rc);
 }
 
-static void __maybe_unused efx_tc_flower_put_mac(struct efx_nic *efx,
+static void efx_tc_flower_put_mac(struct efx_nic *efx,
                                  struct efx_tc_mac_pedit_action *ped)
 {
        if (!refcount_dec_and_test(&ped->ref))
@@ -191,6 +191,10 @@ static void efx_tc_free_action_set(struct efx_nic *efx,
                list_del(&act->encap_user);
                efx_tc_flower_release_encap_md(efx, act->encap_md);
        }
+       if (act->src_mac)
+               efx_tc_flower_put_mac(efx, act->src_mac);
+       if (act->dst_mac)
+               efx_tc_flower_put_mac(efx, act->dst_mac);
        kfree(act);
 }
 
@@ -753,6 +757,7 @@ static const char *efx_tc_encap_type_name(enum efx_encap_type typ)
 /* For details of action order constraints refer to SF-123102-TC-1ยง12.6.1 */
 enum efx_tc_action_order {
        EFX_TC_AO_DECAP,
+       EFX_TC_AO_PEDIT_MAC_ADDRS,
        EFX_TC_AO_VLAN_POP,
        EFX_TC_AO_VLAN_PUSH,
        EFX_TC_AO_COUNT,
@@ -767,6 +772,11 @@ static bool efx_tc_flower_action_order_ok(const struct efx_tc_action_set *act,
        case EFX_TC_AO_DECAP:
                if (act->decap)
                        return false;
+               /* PEDIT_MAC_ADDRS must not happen before DECAP, though it
+                * can wait until much later
+                */
+               if (act->dst_mac || act->src_mac)
+                       return false;
                fallthrough;
        case EFX_TC_AO_VLAN_POP:
                if (act->vlan_pop >= 2)
@@ -786,6 +796,7 @@ static bool efx_tc_flower_action_order_ok(const struct efx_tc_action_set *act,
                if (act->count)
                        return false;
                fallthrough;
+       case EFX_TC_AO_PEDIT_MAC_ADDRS:
        case EFX_TC_AO_ENCAP:
                if (act->encap_md)
                        return false;
@@ -956,6 +967,191 @@ static void efx_tc_flower_release_lhs_actions(struct efx_nic *efx,
                efx_tc_flower_put_counter_index(efx, act->count);
 }
 
+/**
+ * struct efx_tc_mangler_state - accumulates 32-bit pedits into fields
+ *
+ * @dst_mac_32:        dst_mac[0:3] has been populated
+ * @dst_mac_16:        dst_mac[4:5] has been populated
+ * @src_mac_16:        src_mac[0:1] has been populated
+ * @src_mac_32:        src_mac[2:5] has been populated
+ * @dst_mac:   h_dest field of ethhdr
+ * @src_mac:   h_source field of ethhdr
+ *
+ * Since FLOW_ACTION_MANGLE comes in 32-bit chunks that do not
+ * necessarily equate to whole fields of the packet header, this
+ * structure is used to hold the cumulative effect of the partial
+ * field pedits that have been processed so far.
+ */
+struct efx_tc_mangler_state {
+       u8 dst_mac_32:1; /* eth->h_dest[0:3] */
+       u8 dst_mac_16:1; /* eth->h_dest[4:5] */
+       u8 src_mac_16:1; /* eth->h_source[0:1] */
+       u8 src_mac_32:1; /* eth->h_source[2:5] */
+       unsigned char dst_mac[ETH_ALEN];
+       unsigned char src_mac[ETH_ALEN];
+};
+
+/** efx_tc_complete_mac_mangle() - pull complete field pedits out of @mung
+ * @efx:       NIC we're installing a flow rule on
+ * @act:       action set (cursor) to update
+ * @mung:      accumulated partial mangles
+ * @extack:    netlink extended ack for reporting errors
+ *
+ * Check @mung to find any combinations of partial mangles that can be
+ * combined into a complete packet field edit, add that edit to @act,
+ * and consume the partial mangles from @mung.
+ */
+
+static int efx_tc_complete_mac_mangle(struct efx_nic *efx,
+                                     struct efx_tc_action_set *act,
+                                     struct efx_tc_mangler_state *mung,
+                                     struct netlink_ext_ack *extack)
+{
+       struct efx_tc_mac_pedit_action *ped;
+
+       if (mung->dst_mac_32 && mung->dst_mac_16) {
+               ped = efx_tc_flower_get_mac(efx, mung->dst_mac, extack);
+               if (IS_ERR(ped))
+                       return PTR_ERR(ped);
+
+               /* Check that we have not already populated dst_mac */
+               if (act->dst_mac)
+                       efx_tc_flower_put_mac(efx, act->dst_mac);
+
+               act->dst_mac = ped;
+
+               /* consume the incomplete state */
+               mung->dst_mac_32 = 0;
+               mung->dst_mac_16 = 0;
+       }
+       if (mung->src_mac_16 && mung->src_mac_32) {
+               ped = efx_tc_flower_get_mac(efx, mung->src_mac, extack);
+               if (IS_ERR(ped))
+                       return PTR_ERR(ped);
+
+               /* Check that we have not already populated src_mac */
+               if (act->src_mac)
+                       efx_tc_flower_put_mac(efx, act->src_mac);
+
+               act->src_mac = ped;
+
+               /* consume the incomplete state */
+               mung->src_mac_32 = 0;
+               mung->src_mac_16 = 0;
+       }
+       return 0;
+}
+
+/**
+ * efx_tc_mangle() - handle a single 32-bit (or less) pedit
+ * @efx:       NIC we're installing a flow rule on
+ * @act:       action set (cursor) to update
+ * @fa:                FLOW_ACTION_MANGLE action metadata
+ * @mung:      accumulator for partial mangles
+ * @extack:    netlink extended ack for reporting errors
+ *
+ * Identify the fields written by a FLOW_ACTION_MANGLE, and record
+ * the partial mangle state in @mung.  If this mangle completes an
+ * earlier partial mangle, consume and apply to @act by calling
+ * efx_tc_complete_mac_mangle().
+ */
+
+static int efx_tc_mangle(struct efx_nic *efx, struct efx_tc_action_set *act,
+                        const struct flow_action_entry *fa,
+                        struct efx_tc_mangler_state *mung,
+                        struct netlink_ext_ack *extack)
+{
+       __le32 mac32;
+       __le16 mac16;
+
+       switch (fa->mangle.htype) {
+       case FLOW_ACT_MANGLE_HDR_TYPE_ETH:
+               BUILD_BUG_ON(offsetof(struct ethhdr, h_dest) != 0);
+               BUILD_BUG_ON(offsetof(struct ethhdr, h_source) != 6);
+               if (!efx_tc_flower_action_order_ok(act, EFX_TC_AO_PEDIT_MAC_ADDRS)) {
+                       NL_SET_ERR_MSG_MOD(extack,
+                                          "Pedit mangle mac action violates action order");
+                       return -EOPNOTSUPP;
+               }
+               switch (fa->mangle.offset) {
+               case 0:
+                       if (fa->mangle.mask) {
+                               NL_SET_ERR_MSG_FMT_MOD(extack,
+                                                      "Unsupported: mask (%#x) of eth.dst32 mangle",
+                                                      fa->mangle.mask);
+                               return -EOPNOTSUPP;
+                       }
+                       /* Ethernet address is little-endian */
+                       mac32 = cpu_to_le32(fa->mangle.val);
+                       memcpy(mung->dst_mac, &mac32, sizeof(mac32));
+                       mung->dst_mac_32 = 1;
+                       return efx_tc_complete_mac_mangle(efx, act, mung, extack);
+               case 4:
+                       if (fa->mangle.mask == 0xffff) {
+                               mac16 = cpu_to_le16(fa->mangle.val >> 16);
+                               memcpy(mung->src_mac, &mac16, sizeof(mac16));
+                               mung->src_mac_16 = 1;
+                       } else if (fa->mangle.mask == 0xffff0000) {
+                               mac16 = cpu_to_le16((u16)fa->mangle.val);
+                               memcpy(mung->dst_mac + 4, &mac16, sizeof(mac16));
+                               mung->dst_mac_16 = 1;
+                       } else {
+                               NL_SET_ERR_MSG_FMT_MOD(extack,
+                                                      "Unsupported: mask (%#x) of eth+4 mangle is not high or low 16b",
+                                                      fa->mangle.mask);
+                               return -EOPNOTSUPP;
+                       }
+                       return efx_tc_complete_mac_mangle(efx, act, mung, extack);
+               case 8:
+                       if (fa->mangle.mask) {
+                               NL_SET_ERR_MSG_FMT_MOD(extack,
+                                                      "Unsupported: mask (%#x) of eth.src32 mangle",
+                                                      fa->mangle.mask);
+                               return -EOPNOTSUPP;
+                       }
+                       mac32 = cpu_to_le32(fa->mangle.val);
+                       memcpy(mung->src_mac + 2, &mac32, sizeof(mac32));
+                       mung->src_mac_32 = 1;
+                       return efx_tc_complete_mac_mangle(efx, act, mung, extack);
+               default:
+                       NL_SET_ERR_MSG_FMT_MOD(extack, "Unsupported: mangle eth+%u %x/%x",
+                                              fa->mangle.offset, fa->mangle.val, fa->mangle.mask);
+                       return -EOPNOTSUPP;
+               }
+               break;
+       default:
+               NL_SET_ERR_MSG_FMT_MOD(extack, "Unhandled mangle htype %u for action rule",
+                                      fa->mangle.htype);
+               return -EOPNOTSUPP;
+       }
+       return 0;
+}
+
+/**
+ * efx_tc_incomplete_mangle() - check for leftover partial pedits
+ * @mung:      accumulator for partial mangles
+ * @extack:    netlink extended ack for reporting errors
+ *
+ * Since the MAE can only overwrite whole fields, any partial
+ * field mangle left over on reaching packet delivery (mirred or
+ * end of TC actions) cannot be offloaded.  Check for any such
+ * and reject them with -%EOPNOTSUPP.
+ */
+
+static int efx_tc_incomplete_mangle(struct efx_tc_mangler_state *mung,
+                                   struct netlink_ext_ack *extack)
+{
+       if (mung->dst_mac_32 || mung->dst_mac_16) {
+               NL_SET_ERR_MSG_MOD(extack, "Incomplete pedit of destination MAC address");
+               return -EOPNOTSUPP;
+       }
+       if (mung->src_mac_16 || mung->src_mac_32) {
+               NL_SET_ERR_MSG_MOD(extack, "Incomplete pedit of source MAC address");
+               return -EOPNOTSUPP;
+       }
+       return 0;
+}
+
 static int efx_tc_flower_replace_foreign(struct efx_nic *efx,
                                         struct net_device *net_dev,
                                         struct flow_cls_offload *tc)
@@ -1351,6 +1547,7 @@ static int efx_tc_flower_replace(struct efx_nic *efx,
        struct netlink_ext_ack *extack = tc->common.extack;
        const struct ip_tunnel_info *encap_info = NULL;
        struct efx_tc_flow_rule *rule = NULL, *old;
+       struct efx_tc_mangler_state mung = {};
        struct efx_tc_action_set *act = NULL;
        const struct flow_action_entry *fa;
        struct efx_rep *from_efv, *to_efv;
@@ -1687,6 +1884,11 @@ static int efx_tc_flower_replace(struct efx_nic *efx,
                        act->vlan_proto[act->vlan_push] = fa->vlan.proto;
                        act->vlan_push++;
                        break;
+               case FLOW_ACTION_MANGLE:
+                       rc = efx_tc_mangle(efx, act, fa, &mung, extack);
+                       if (rc < 0)
+                               goto release;
+                       break;
                case FLOW_ACTION_TUNNEL_ENCAP:
                        if (encap_info) {
                                /* Can't specify encap multiple times.
@@ -1726,6 +1928,9 @@ static int efx_tc_flower_replace(struct efx_nic *efx,
                }
        }
 
+       rc = efx_tc_incomplete_mangle(&mung, extack);
+       if (rc < 0)
+               goto release;
        if (act) {
                /* Not shot/redirected, so deliver to default dest */
                if (from_efv == EFX_EFV_PF)