From 439c4be98318751415a8f3690e4fa0fb62474a72 Mon Sep 17 00:00:00 2001 From: Pieter Jansen van Vuuren Date: Thu, 24 Aug 2023 12:28:37 +0100 Subject: sfc: introduce ethernet pedit set action infrastructure Introduce the initial ethernet pedit set action infrastructure in preparation for adding mac src and dst pedit action offloads. Co-developed-by: Edward Cree Signed-off-by: Edward Cree Signed-off-by: Pieter Jansen van Vuuren Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/mae.c | 83 ++++++++++++++++++++++++++++++++++++++++-- drivers/net/ethernet/sfc/mae.h | 4 ++ drivers/net/ethernet/sfc/tc.c | 70 +++++++++++++++++++++++++++++++++++ drivers/net/ethernet/sfc/tc.h | 56 ++++++++++++++++++++++++---- 4 files changed, 202 insertions(+), 11 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/sfc/mae.c b/drivers/net/ethernet/sfc/mae.c index 3b8780c76b6e..a7ad7ab8c5f4 100644 --- a/drivers/net/ethernet/sfc/mae.c +++ b/drivers/net/ethernet/sfc/mae.c @@ -1219,6 +1219,71 @@ fail: return rc; } +/** + * efx_mae_allocate_pedit_mac() - allocate pedit MAC address in HW. + * @efx: NIC we're installing a pedit MAC address on + * @ped: pedit MAC action to be installed + * + * Attempts to install @ped in HW and populates its id with an index of this + * entry in the firmware MAC address table on success. + * + * Return: negative value on error, 0 in success. + */ +int efx_mae_allocate_pedit_mac(struct efx_nic *efx, + struct efx_tc_mac_pedit_action *ped) +{ + MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_LEN); + MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_MAC_ADDR_ALLOC_IN_LEN); + size_t outlen; + int rc; + + BUILD_BUG_ON(MC_CMD_MAE_MAC_ADDR_ALLOC_IN_MAC_ADDR_LEN != + sizeof(ped->h_addr)); + memcpy(MCDI_PTR(inbuf, MAE_MAC_ADDR_ALLOC_IN_MAC_ADDR), ped->h_addr, + sizeof(ped->h_addr)); + rc = efx_mcdi_rpc(efx, MC_CMD_MAE_MAC_ADDR_ALLOC, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlen); + if (rc) + return rc; + if (outlen < sizeof(outbuf)) + return -EIO; + ped->fw_id = MCDI_DWORD(outbuf, MAE_MAC_ADDR_ALLOC_OUT_MAC_ID); + return 0; +} + +/** + * efx_mae_free_pedit_mac() - free pedit MAC address in HW. + * @efx: NIC we're installing a pedit MAC address on + * @ped: pedit MAC action that needs to be freed + * + * Frees @ped in HW, check that firmware did not free a different one and clears + * the id (which denotes the index of the entry in the MAC address table). + */ +void efx_mae_free_pedit_mac(struct efx_nic *efx, + struct efx_tc_mac_pedit_action *ped) +{ + MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_MAC_ADDR_FREE_OUT_LEN(1)); + MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_MAC_ADDR_FREE_IN_LEN(1)); + size_t outlen; + int rc; + + MCDI_SET_DWORD(inbuf, MAE_MAC_ADDR_FREE_IN_MAC_ID, ped->fw_id); + rc = efx_mcdi_rpc(efx, MC_CMD_MAE_MAC_ADDR_FREE, inbuf, + sizeof(inbuf), outbuf, sizeof(outbuf), &outlen); + if (rc || outlen < sizeof(outbuf)) + return; + /* FW freed a different ID than we asked for, should also never happen. + * Warn because it means we've now got a different idea to the FW of + * what MAC addresses exist, which could cause mayhem later. + */ + if (WARN_ON(MCDI_DWORD(outbuf, MAE_MAC_ADDR_FREE_OUT_FREED_MAC_ID) != ped->fw_id)) + return; + /* We're probably about to free @ped, but let's just make sure its + * fw_id is blatted so that it won't look valid if it leaks out. + */ + ped->fw_id = MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL; +} + int efx_mae_alloc_action_set(struct efx_nic *efx, struct efx_tc_action_set *act) { MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_ALLOC_OUT_LEN); @@ -1231,10 +1296,20 @@ int efx_mae_alloc_action_set(struct efx_nic *efx, struct efx_tc_action_set *act) MAE_ACTION_SET_ALLOC_IN_VLAN_POP, act->vlan_pop, MAE_ACTION_SET_ALLOC_IN_DECAP, act->decap); - MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_SRC_MAC_ID, - MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL); - MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_DST_MAC_ID, - MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL); + if (act->src_mac) + MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_SRC_MAC_ID, + act->src_mac->fw_id); + else + MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_SRC_MAC_ID, + MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL); + + if (act->dst_mac) + MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_DST_MAC_ID, + act->dst_mac->fw_id); + else + MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_DST_MAC_ID, + MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL); + if (act->count && !WARN_ON(!act->count->cnt)) MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_ID, act->count->cnt->fw_id); diff --git a/drivers/net/ethernet/sfc/mae.h b/drivers/net/ethernet/sfc/mae.h index e88e80574f15..8df30bc4f3ba 100644 --- a/drivers/net/ethernet/sfc/mae.h +++ b/drivers/net/ethernet/sfc/mae.h @@ -103,6 +103,10 @@ int efx_mae_update_encap_md(struct efx_nic *efx, int efx_mae_free_encap_md(struct efx_nic *efx, struct efx_tc_encap_action *encap); +int efx_mae_allocate_pedit_mac(struct efx_nic *efx, + struct efx_tc_mac_pedit_action *ped); +void efx_mae_free_pedit_mac(struct efx_nic *efx, + struct efx_tc_mac_pedit_action *ped); int efx_mae_alloc_action_set(struct efx_nic *efx, struct efx_tc_action_set *act); int efx_mae_free_action_set(struct efx_nic *efx, u32 fw_id); diff --git a/drivers/net/ethernet/sfc/tc.c b/drivers/net/ethernet/sfc/tc.c index 039180c61c83..8a9fc2f47514 100644 --- a/drivers/net/ethernet/sfc/tc.c +++ b/drivers/net/ethernet/sfc/tc.c @@ -86,6 +86,12 @@ s64 efx_tc_flower_external_mport(struct efx_nic *efx, struct efx_rep *efv) return mport; } +static const struct rhashtable_params efx_tc_mac_ht_params = { + .key_len = offsetofend(struct efx_tc_mac_pedit_action, h_addr), + .key_offset = 0, + .head_offset = offsetof(struct efx_tc_mac_pedit_action, linkage), +}; + static const struct rhashtable_params efx_tc_encap_match_ht_params = { .key_len = offsetof(struct efx_tc_encap_match, linkage), .key_offset = 0, @@ -110,6 +116,56 @@ 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, + unsigned char h_addr[ETH_ALEN], + struct netlink_ext_ack *extack) +{ + struct efx_tc_mac_pedit_action *ped, *old; + int rc; + + ped = kzalloc(sizeof(*ped), GFP_USER); + if (!ped) + return ERR_PTR(-ENOMEM); + memcpy(ped->h_addr, h_addr, ETH_ALEN); + old = rhashtable_lookup_get_insert_fast(&efx->tc->mac_ht, + &ped->linkage, + efx_tc_mac_ht_params); + if (old) { + /* don't need our new entry */ + kfree(ped); + if (!refcount_inc_not_zero(&old->ref)) + return ERR_PTR(-EAGAIN); + /* existing entry found, ref taken */ + return old; + } + + rc = efx_mae_allocate_pedit_mac(efx, ped); + if (rc < 0) { + NL_SET_ERR_MSG_MOD(extack, "Failed to store pedit MAC address in hw"); + goto out_remove; + } + + /* ref and return */ + refcount_set(&ped->ref, 1); + return ped; +out_remove: + rhashtable_remove_fast(&efx->tc->mac_ht, &ped->linkage, + efx_tc_mac_ht_params); + kfree(ped); + return ERR_PTR(rc); +} + +static void __maybe_unused efx_tc_flower_put_mac(struct efx_nic *efx, + struct efx_tc_mac_pedit_action *ped) +{ + if (!refcount_dec_and_test(&ped->ref)) + return; /* still in use */ + rhashtable_remove_fast(&efx->tc->mac_ht, &ped->linkage, + efx_tc_mac_ht_params); + efx_mae_free_pedit_mac(efx, ped); + kfree(ped); +} + static void efx_tc_free_action_set(struct efx_nic *efx, struct efx_tc_action_set *act, bool in_hw) { @@ -2156,6 +2212,14 @@ static void efx_tc_lhs_free(void *ptr, void *arg) kfree(rule); } +static void efx_tc_mac_free(void *ptr, void *__unused) +{ + struct efx_tc_mac_pedit_action *ped = ptr; + + WARN_ON(refcount_read(&ped->ref)); + kfree(ped); +} + static void efx_tc_flow_free(void *ptr, void *arg) { struct efx_tc_flow_rule *rule = ptr; @@ -2196,6 +2260,9 @@ int efx_init_struct_tc(struct efx_nic *efx) rc = efx_tc_init_counters(efx); if (rc < 0) goto fail_counters; + rc = rhashtable_init(&efx->tc->mac_ht, &efx_tc_mac_ht_params); + if (rc < 0) + goto fail_mac_ht; rc = rhashtable_init(&efx->tc->encap_match_ht, &efx_tc_encap_match_ht_params); if (rc < 0) goto fail_encap_match_ht; @@ -2233,6 +2300,8 @@ fail_lhs_rule_ht: fail_match_action_ht: rhashtable_destroy(&efx->tc->encap_match_ht); fail_encap_match_ht: + rhashtable_destroy(&efx->tc->mac_ht); +fail_mac_ht: efx_tc_destroy_counters(efx); fail_counters: efx_tc_destroy_encap_actions(efx); @@ -2268,6 +2337,7 @@ void efx_fini_struct_tc(struct efx_nic *efx) rhashtable_free_and_destroy(&efx->tc->recirc_ht, efx_tc_recirc_free, efx); WARN_ON(!ida_is_empty(&efx->tc->recirc_ida)); ida_destroy(&efx->tc->recirc_ida); + rhashtable_free_and_destroy(&efx->tc->mac_ht, efx_tc_mac_free, NULL); efx_tc_fini_counters(efx); efx_tc_fini_encap_actions(efx); mutex_unlock(&efx->tc->mutex); diff --git a/drivers/net/ethernet/sfc/tc.h b/drivers/net/ethernet/sfc/tc.h index 40d2c803fca8..7b6a6a3d8e4c 100644 --- a/drivers/net/ethernet/sfc/tc.h +++ b/drivers/net/ethernet/sfc/tc.h @@ -18,6 +18,23 @@ #define IS_ALL_ONES(v) (!(typeof (v))~(v)) +/** + * struct efx_tc_mac_pedit_action - mac pedit action fields + * + * @h_addr: mac address field of ethernet header + * @linkage: rhashtable reference + * @ref: reference count + * @fw_id: index of this entry in firmware MAC address table + * + * MAC address edits are indirected through a table in the hardware + */ +struct efx_tc_mac_pedit_action { + u8 h_addr[ETH_ALEN]; + struct rhash_head linkage; + refcount_t ref; + u32 fw_id; /* index of this entry in firmware MAC address table */ +}; + static inline bool efx_ipv6_addr_all_ones(struct in6_addr *addr) { return !memchr_inv(addr, 0xff, sizeof(*addr)); @@ -25,20 +42,43 @@ static inline bool efx_ipv6_addr_all_ones(struct in6_addr *addr) struct efx_tc_encap_action; /* see tc_encap_actions.h */ +/** + * struct efx_tc_action_set - collection of tc action fields + * + * @vlan_push: the number of vlan headers to push + * @vlan_pop: the number of vlan headers to pop + * @decap: used to indicate a tunnel header decapsulation should take place + * @deliver: used to indicate a deliver action should take place + * @vlan_tci: tci fields for vlan push actions + * @vlan_proto: ethernet types for vlan push actions + * @count: counter mapping + * @encap_md: encap entry in tc_encap_ht table + * @encap_user: linked list of encap users (encap_md->users) + * @user: owning action-set-list. Only populated if @encap_md is; used by efx_tc_update_encap() fallback handling + * @count_user: linked list of counter users (counter->users) + * @dest_mport: destination mport + * @src_mac: source mac entry in tc_mac_ht table + * @dst_mac: destination mac entry in tc_mac_ht table + * @fw_id: index of this entry in firmware actions table + * @list: linked list of tc actions + * + */ struct efx_tc_action_set { u16 vlan_push:2; u16 vlan_pop:2; u16 decap:1; u16 deliver:1; - __be16 vlan_tci[2]; /* TCIs for vlan_push */ - __be16 vlan_proto[2]; /* Ethertypes for vlan_push */ + __be16 vlan_tci[2]; + __be16 vlan_proto[2]; struct efx_tc_counter_index *count; - struct efx_tc_encap_action *encap_md; /* entry in tc_encap_ht table */ - struct list_head encap_user; /* entry on encap_md->users list */ - struct efx_tc_action_set_list *user; /* Only populated if encap_md */ - struct list_head count_user; /* entry on counter->users list, if encap */ + struct efx_tc_encap_action *encap_md; + struct list_head encap_user; + struct efx_tc_action_set_list *user; + struct list_head count_user; u32 dest_mport; - u32 fw_id; /* index of this entry in firmware actions table */ + struct efx_tc_mac_pedit_action *src_mac; + struct efx_tc_mac_pedit_action *dst_mac; + u32 fw_id; struct list_head list; }; @@ -220,6 +260,7 @@ struct efx_tc_table_ct { /* TABLE_ID_CONNTRACK_TABLE */ * @counter_ht: Hashtable of TC counters (FW IDs and counter values) * @counter_id_ht: Hashtable mapping TC counter cookies to counters * @encap_ht: Hashtable of TC encap actions + * @mac_ht: Hashtable of MAC address entries (for pedits) * @encap_match_ht: Hashtable of TC encap matches * @match_action_ht: Hashtable of TC match-action rules * @lhs_rule_ht: Hashtable of TC left-hand (act ct & goto chain) rules @@ -257,6 +298,7 @@ struct efx_tc_state { struct rhashtable counter_ht; struct rhashtable counter_id_ht; struct rhashtable encap_ht; + struct rhashtable mac_ht; struct rhashtable encap_match_ht; struct rhashtable match_action_ht; struct rhashtable lhs_rule_ht; -- cgit v1.2.3