sfc: TC encap actions offload
Subsystems (3)
cover sfc: TC encap actions offload
From: Edward Cree <[email protected]>
This series adds support for offloading TC tunnel_key set actions to the
EF100 driver, supporting VxLAN and GENEVE tunnels over IPv4 or IPv6.
Edward Cree (6):
sfc: add fallback action-set-lists for TC offload
sfc: some plumbing towards TC encap action offload
sfc: add function to atomically update a rule in the MAE
sfc: MAE functions to create/update/delete encap headers
sfc: neighbour lookup for TC encap action offload
sfc: generate encap headers for TC offload
drivers/net/ethernet/sfc/Makefile | 3 +- drivers/net/ethernet/sfc/ef100_netdev.c | 34 + drivers/net/ethernet/sfc/mae.c | 113 ++- drivers/net/ethernet/sfc/mae.h | 8 + drivers/net/ethernet/sfc/net_driver.h | 3 + drivers/net/ethernet/sfc/tc.c | 182 ++++- drivers/net/ethernet/sfc/tc.h | 23 + drivers/net/ethernet/sfc/tc_bindings.c | 13 + drivers/net/ethernet/sfc/tc_bindings.h | 2 + drivers/net/ethernet/sfc/tc_encap_actions.c | 742 ++++++++++++++++++++ drivers/net/ethernet/sfc/tc_encap_actions.h | 99 +++ 11 files changed, 1214 insertions(+), 8 deletions(-) create mode 100644 drivers/net/ethernet/sfc/tc_encap_actions.c create mode 100644 drivers/net/ethernet/sfc/tc_encap_actions.h
PATCH 1/6 sfc: add fallback action-set-lists for TC offload
From: Edward Cree <[email protected]>
When offloading a TC encap action, the action information for the
hardware might not be "ready": if there's currently no neighbour entry available for the destination address, we can't construct the Ethernet header to prepend to the packet. In this case, we still offload the flow rule, but with its action-set-list ID pointing at a "fallback" action which simply delivers the packet to its default destination (as though no flow rule had matched), thus allowing software TC to handle it. Later, when we receive a neighbouring update that allows us to construct the encap header, the rule will become "ready" and we will update its action-set-list ID in hardware to point at the actual offloaded actions.
This patch sets up these fallback ASLs, but does not yet use them.
--- drivers/net/ethernet/sfc/tc.c | 68 +++++++++++++++++++++++++++++++++++ drivers/net/ethernet/sfc/tc.h | 9 +++++ 2 files changed, 77 insertions(+)
drivers/net/ethernet/sfc/tc.c +68 -0
@@ -1391,6 +1391,58 @@ void efx_tc_deconfigure_default_rule(struct efx_nic *efx, rule->fw_id =MC_CMD_MAE_ACTION_RULE_INSERT_OUT_ACTION_RULE_ID_NULL ; } +static int efx_tc_configure_fallback_acts (struct efx_nic *efx,u32 eg_port, +struct efx_tc_action_set_list *acts) +{ +struct efx_tc_action_set *act; +int rc; + + act =kzalloc (sizeof (*act),GFP_KERNEL ); +if (!act) +return -ENOMEM ; + act->deliver =1 ; + act->dest_mport = eg_port; + rc =efx_mae_alloc_action_set (efx, act); +if (rc) + goto fail1; +EFX_WARN_ON_PARANOID (!list_empty (&acts->list)); +list_add_tail (&act->list, &acts->list); + rc =efx_mae_alloc_action_set_list (efx, acts); +if (rc) + goto fail2; +return 0 ; +fail2: +list_del (&act->list); +efx_mae_free_action_set (efx, act->fw_id); +fail1: +kfree (act); +return rc; +} + +static int efx_tc_configure_fallback_acts_pf (struct efx_nic *efx) +{ +struct efx_tc_action_set_list *acts = &efx->tc->facts.pf; +u32 eg_port; + +efx_mae_mport_uplink (efx, &eg_port); +return efx_tc_configure_fallback_acts (efx, eg_port, acts); +} + +static int efx_tc_configure_fallback_acts_reps (struct efx_nic *efx) +{ +struct efx_tc_action_set_list *acts = &efx->tc->facts.reps; +u32 eg_port; + +efx_mae_mport_mport (efx, efx->tc->reps_mport_id, &eg_port); +return efx_tc_configure_fallback_acts (efx, eg_port, acts); +} + +static void efx_tc_deconfigure_fallback_acts (struct efx_nic *efx, +struct efx_tc_action_set_list *acts) +{ +efx_tc_free_action_set_list (efx, acts, true); +} +static int efx_tc_configure_rep_mport (struct efx_nic *efx) {u32 rep_mport_label;
@@ -1481,6 +1533,12 @@ int efx_init_tc(struct efx_nic *efx)if (rc)return rc; rc =efx_tc_configure_rep_mport (efx); +if (rc) +return rc; + rc =efx_tc_configure_fallback_acts_pf (efx); +if (rc) +return rc; + rc =efx_tc_configure_fallback_acts_reps (efx);if (rc)return rc; efx->tc->up = true;
@@ -1500,6 +1558,8 @@ void efx_fini_tc(struct efx_nic *efx)efx_tc_deconfigure_rep_mport (efx);efx_tc_deconfigure_default_rule (efx, &efx->tc->dflt.pf);efx_tc_deconfigure_default_rule (efx, &efx->tc->dflt.wire); +efx_tc_deconfigure_fallback_acts (efx, &efx->tc->facts.pf); +efx_tc_deconfigure_fallback_acts (efx, &efx->tc->facts.reps); efx->tc->up = false; }
@@ -1564,6 +1624,10 @@ int efx_init_struct_tc(struct efx_nic *efx) efx->tc->dflt.pf.fw_id =MC_CMD_MAE_ACTION_RULE_INSERT_OUT_ACTION_RULE_ID_NULL ;INIT_LIST_HEAD (&efx->tc->dflt.wire.acts.list); efx->tc->dflt.wire.fw_id =MC_CMD_MAE_ACTION_RULE_INSERT_OUT_ACTION_RULE_ID_NULL ; +INIT_LIST_HEAD (&efx->tc->facts.pf.list); + efx->tc->facts.pf.fw_id =MC_CMD_MAE_ACTION_SET_ALLOC_OUT_ACTION_SET_ID_NULL ; +INIT_LIST_HEAD (&efx->tc->facts.reps.list); + efx->tc->facts.reps.fw_id =MC_CMD_MAE_ACTION_SET_ALLOC_OUT_ACTION_SET_ID_NULL ; efx->extra_channel_type[EFX_EXTRA_CHANNEL_TC ] = &efx_tc_channel_type;return 0 ; fail_match_action_ht:
@@ -1589,6 +1653,10 @@ void efx_fini_struct_tc(struct efx_nic *efx) MC_CMD_MAE_ACTION_RULE_INSERT_OUT_ACTION_RULE_ID_NULL);EFX_WARN_ON_PARANOID (efx->tc->dflt.wire.fw_id !=MC_CMD_MAE_ACTION_RULE_INSERT_OUT_ACTION_RULE_ID_NULL ); +EFX_WARN_ON_PARANOID (efx->tc->facts.pf.fw_id != +MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL ); +EFX_WARN_ON_PARANOID (efx->tc->facts.reps.fw_id != +MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL );rhashtable_free_and_destroy (&efx->tc->match_action_ht, efx_tc_flow_free, efx); rhashtable_free_and_destroy(&efx->tc->encap_match_ht,
drivers/net/ethernet/sfc/tc.h +9 -0
@@ -160,6 +160,11 @@ enum efx_tc_rule_prios {* %EFX_TC_PRIO_DFLT. Named by *ingress* port * @dflt.pf: rule for traffic ingressing from PF (egresses to wire) * @dflt.wire: rule for traffic ingressing from wire (egresses to PF) +* @facts: Fallback action-set-lists for unready rules. Named by *egress* port +* @facts.pf: action-set-list for unready rules on PF netdev, hence applying to +* traffic from wire, and egressing to PF +* @facts.reps: action-set-list for unready rules on representors, hence +* applying to traffic from representees, and egressing to the reps mport * @up: have TC datastructures been set up? */ struct efx_tc_state {
@@ -180,6 +185,10 @@ struct efx_tc_state {struct efx_tc_flow_rule pf;struct efx_tc_flow_rule wire; } dflt; +struct { +struct efx_tc_action_set_list pf; +struct efx_tc_action_set_list reps; + } facts;bool up; };
Re: [PATCH net-next 1/6] sfc: add fallback action-set-lists for TC offload
Quoted 15 lines · On 05/06/2023 20:17, [email protected] wrote:
From: Edward Cree <[email protected]>
When offloading a TC encap action, the action information for the
hardware might not be "ready": if there's currently no neighbour entry available for the destination address, we can't construct the Ethernet header to prepend to the packet. In this case, we still offload the flow rule, but with its action-set-list ID pointing at a "fallback" action which simply delivers the packet to its default destination (as though no flow rule had matched), thus allowing software TC to handle it. Later, when we receive a neighbouring update that allows us to construct the encap header, the rule will become "ready" and we will update its action-set-list ID in hardware to point at the actual offloaded actions.
This patch sets up these fallback ASLs, but does not yet use them.
Quoted 1 line
Signed-off-by Edward Cree <[email protected]>
PATCH 2/6 sfc: some plumbing towards TC encap action offload
In reply to [email protected]From: Edward Cree <[email protected]>
Create software objects to manage the metadata for encap actions that
can be attached to TC rules. However, since we don't yet have the neighbouring information (needed to generate the Ethernet header), all rules with encap actions are marked as "unready" and thus insert the fallback action into hardware rather than actually offloading the encapsulation action.
--- drivers/net/ethernet/sfc/Makefile | 3 +- drivers/net/ethernet/sfc/tc.c | 104 +++++++++++++++- drivers/net/ethernet/sfc/tc.h | 7 ++ drivers/net/ethernet/sfc/tc_encap_actions.c | 126 ++++++++++++++++++++ drivers/net/ethernet/sfc/tc_encap_actions.h | 47 ++++++++ 5 files changed, 284 insertions(+), 3 deletions(-) create mode 100644 drivers/net/ethernet/sfc/tc_encap_actions.c create mode 100644 drivers/net/ethernet/sfc/tc_encap_actions.h
drivers/net/ethernet/sfc/Makefile +2 -1
@@ -10,7 +10,8 @@ sfc-y += efx.o efx_common.o efx_channels.o nic.o \ efx_devlink.o sfc-$(CONFIG_SFC_MTD) += mtd.o sfc-$(CONFIG_SFC_SRIOV) += sriov.o ef10_sriov.o ef100_sriov.o ef100_rep.o \ - mae.o tc.o tc_bindings.o tc_counters.o + mae.o tc.o tc_bindings.o tc_counters.o \ + tc_encap_actions.o obj-$(CONFIG_SFC) += sfc.o
drivers/net/ethernet/sfc/tc.c +102 -2
180 lines not on this page · raw message
drivers/net/ethernet/sfc/tc.h +7 -0
@@ -25,6 +25,8 @@ static inline bool efx_ipv6_addr_all_ones(struct in6_addr *addr) }#endif +struct efx_tc_encap_action ;/* see tc_encap_actions.h */ +struct efx_tc_action_set {u16 vlan_push:2 ;u16 vlan_pop:2 ;
@@ -33,6 +35,7 @@ struct efx_tc_action_set {__be16 vlan_tci[2 ];/* TCIs for vlan_push */ __be16 vlan_proto[2 ];/* Ethertypes for vlan_push */ struct efx_tc_counter_index *count; +struct efx_tc_encap_action *encap_md;/* entry in tc_encap_ht table */ u32 dest_mport;u32 fw_id;/* index of this entry in firmware actions table */ struct list_head list;
@@ -127,6 +130,7 @@ struct efx_tc_flow_rule {struct rhash_head linkage;struct efx_tc_match match;struct efx_tc_action_set_list acts; +struct efx_tc_action_set_list *fallback;/* what to use when unready? */ u32 fw_id; };
@@ -144,6 +148,7 @@ enum efx_tc_rule_prios {* @mutex: Used to serialise operations on TC hashtables * @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 * @encap_match_ht: Hashtable of TC encap matches * @match_action_ht: Hashtable of TC match-action rules * @reps_mport_id: MAE port allocated for representor RX
@@ -173,6 +178,7 @@ struct efx_tc_state {struct mutex mutex;struct rhashtable counter_ht;struct rhashtable counter_id_ht; +struct rhashtable encap_ht;struct rhashtable encap_match_ht;struct rhashtable match_action_ht;u32 reps_mport_id, reps_mport_vport_id;
@@ -194,6 +200,7 @@ struct efx_tc_state {struct efx_rep ; +enum efx_encap_type efx_tc_indr_netdev_type (struct net_device *net_dev);int efx_tc_configure_default_rule_rep (struct efx_rep *efv);void efx_tc_deconfigure_default_rule (struct efx_nic *efx,struct efx_tc_flow_rule *rule);
drivers/net/ethernet/sfc/tc_encap_actions.c new file +126 -0
126 lines not on this page · raw message
drivers/net/ethernet/sfc/tc_encap_actions.h new file +47 -0
47 lines not on this page · raw message
Re: [PATCH net-next 2/6] sfc: some plumbing towards TC encap action offload
Quoted 10 lines · On 05/06/2023 20:17, [email protected] wrote:
From: Edward Cree <[email protected]>
Create software objects to manage the metadata for encap actions that
can be attached to TC rules. However, since we don't yet have the neighbouring information (needed to generate the Ethernet header), all rules with encap actions are marked as "unready" and thus insert the fallback action into hardware rather than actually offloading the encapsulation action.Signed-off-by Edward Cree <[email protected]>
PATCH 3/6 sfc: add function to atomically update a rule in the MAE
In reply to [email protected]From: Edward Cree <[email protected]>
efx_mae_update_rule() changes the action-set-list attached to an MAE
flow rule in the Action Rule Table.
We will use this when neighbouring updates change encap actions.
--- drivers/net/ethernet/sfc/mae.c | 23 +++++++++++++++++++++++ drivers/net/ethernet/sfc/mae.h | 1 + 2 files changed, 24 insertions(+)
drivers/net/ethernet/sfc/mae.c +23 -0
@@ -1229,6 +1229,29 @@ int efx_mae_insert_rule(struct efx_nic *efx, const struct efx_tc_match *match,return 0 ; } +int efx_mae_update_rule (struct efx_nic *efx,u32 acts_id,u32 id) +{ +MCDI_DECLARE_BUF (inbuf,MC_CMD_MAE_ACTION_RULE_UPDATE_IN_LEN ); +MCDI_DECLARE_STRUCT_PTR (response); + +BUILD_BUG_ON (MC_CMD_MAE_ACTION_RULE_UPDATE_OUT_LEN ); + response =_MCDI_DWORD (inbuf,MAE_ACTION_RULE_UPDATE_IN_RESPONSE ); + +MCDI_SET_DWORD (inbuf,MAE_ACTION_RULE_UPDATE_IN_AR_ID , id); +if (efx_mae_asl_id (acts_id)) { +MCDI_STRUCT_SET_DWORD (response,MAE_ACTION_RULE_RESPONSE_ASL_ID , acts_id); +MCDI_STRUCT_SET_DWORD (response,MAE_ACTION_RULE_RESPONSE_AS_ID , +MC_CMD_MAE_ACTION_SET_ALLOC_OUT_ACTION_SET_ID_NULL ); + }else { +/* We only had one AS, so we didn't wrap it in an ASL */ +MCDI_STRUCT_SET_DWORD (response,MAE_ACTION_RULE_RESPONSE_ASL_ID , +MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL ); +MCDI_STRUCT_SET_DWORD (response,MAE_ACTION_RULE_RESPONSE_AS_ID , acts_id); + } +return efx_mcdi_rpc (efx,MC_CMD_MAE_ACTION_RULE_UPDATE , inbuf,sizeof (inbuf), +NULL ,0 ,NULL ); +} +int efx_mae_delete_rule (struct efx_nic *efx,u32 id) {MCDI_DECLARE_BUF (outbuf,MC_CMD_MAE_ACTION_RULE_DELETE_OUT_LEN (1 ));
drivers/net/ethernet/sfc/mae.h +1 -0
@@ -105,6 +105,7 @@ int efx_mae_unregister_encap_match(struct efx_nic *efx,int efx_mae_insert_rule (struct efx_nic *efx,const struct efx_tc_match *match,u32 prio,u32 acts_id,u32 *id); +int efx_mae_update_rule (struct efx_nic *efx,u32 acts_id,u32 id);int efx_mae_delete_rule (struct efx_nic *efx,u32 id);int efx_init_mae (struct efx_nic *efx);
Re: [PATCH net-next 3/6] sfc: add function to atomically update a rule in the MAE
Quoted 7 lines · On 05/06/2023 20:17, [email protected] wrote:
From: Edward Cree <[email protected]>
efx_mae_update_rule()changes the action-set-list attached to an MAE
flow rule in the Action Rule Table.
We will use this when neighbouring updates change encap actions.Signed-off-by Edward Cree <[email protected]>
PATCH 4/6 sfc: MAE functions to create/update/delete encap headers
In reply to [email protected]From: Edward Cree <[email protected]>
Besides the raw header data, also pass the tunnel type, so that the
hardware knows it needs to update the IP Total Length and UDP Length fields (and corresponding checksums) for each packet.
Also, populate the ENCAP_HEADER_ID field in efx_mae_alloc_action_set()
with the fw_id returned from efx_mae_allocate_encap_md().
--- drivers/net/ethernet/sfc/mae.c | 90 +++++++++++++++++++++++++++++++++- drivers/net/ethernet/sfc/mae.h | 7 +++ 2 files changed, 95 insertions(+), 2 deletions(-)
drivers/net/ethernet/sfc/mae.c +88 -2
@@ -15,6 +15,7 @@#include "mcdi.h" #include "mcdi_pcol.h" #include "mcdi_pcol_mae.h" +#include "tc_encap_actions.h" int efx_mae_allocate_mport (struct efx_nic *efx,u32 *id,u32 *label) {
@@ -610,6 +611,87 @@ static int efx_mae_encap_type_to_mae_type(enum efx_encap_type type) } } +int efx_mae_allocate_encap_md (struct efx_nic *efx, +struct efx_tc_encap_action *encap) +{ +MCDI_DECLARE_BUF (inbuf,MC_CMD_MAE_ENCAP_HEADER_ALLOC_IN_LEN (EFX_TC_MAX_ENCAP_HDR )); +MCDI_DECLARE_BUF (outbuf,MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_LEN ); +size_t inlen, outlen; +int rc; + + rc =efx_mae_encap_type_to_mae_type (encap->type); +if (rc <0 ) +return rc; +MCDI_SET_DWORD (inbuf,MAE_ENCAP_HEADER_ALLOC_IN_ENCAP_TYPE , rc); + inlen =MC_CMD_MAE_ENCAP_HEADER_ALLOC_IN_LEN (encap->encap_hdr_len); +if (WARN_ON (inlen >sizeof (inbuf)))/* can't happen */ +return -EINVAL ; +memcpy (MCDI_PTR (inbuf,MAE_ENCAP_HEADER_ALLOC_IN_HDR_DATA ), + encap->encap_hdr, + encap->encap_hdr_len); + rc =efx_mcdi_rpc (efx,MC_CMD_MAE_ENCAP_HEADER_ALLOC , inbuf, + inlen, outbuf,sizeof (outbuf), &outlen); +if (rc) +return rc; +if (outlen <sizeof (outbuf)) +return -EIO ; + encap->fw_id =MCDI_DWORD (outbuf,MAE_ENCAP_HEADER_ALLOC_OUT_ENCAP_HEADER_ID ); +return 0 ; +} + +int efx_mae_update_encap_md (struct efx_nic *efx, +struct efx_tc_encap_action *encap) +{ +MCDI_DECLARE_BUF (inbuf,MC_CMD_MAE_ENCAP_HEADER_UPDATE_IN_LEN (EFX_TC_MAX_ENCAP_HDR )); +size_t inlen; +int rc; + + rc =efx_mae_encap_type_to_mae_type (encap->type); +if (rc <0 ) +return rc; +MCDI_SET_DWORD (inbuf,MAE_ENCAP_HEADER_UPDATE_IN_ENCAP_TYPE , rc); +MCDI_SET_DWORD (inbuf,MAE_ENCAP_HEADER_UPDATE_IN_EH_ID , + encap->fw_id); + inlen =MC_CMD_MAE_ENCAP_HEADER_UPDATE_IN_LEN (encap->encap_hdr_len); +if (WARN_ON (inlen >sizeof (inbuf)))/* can't happen */ +return -EINVAL ; +memcpy (MCDI_PTR (inbuf,MAE_ENCAP_HEADER_UPDATE_IN_HDR_DATA ), + encap->encap_hdr, + encap->encap_hdr_len); + +BUILD_BUG_ON (MC_CMD_MAE_ENCAP_HEADER_UPDATE_OUT_LEN !=0 ); +return efx_mcdi_rpc (efx,MC_CMD_MAE_ENCAP_HEADER_UPDATE , inbuf, + inlen,NULL ,0 ,NULL ); +} + +int efx_mae_free_encap_md (struct efx_nic *efx, +struct efx_tc_encap_action *encap) +{ +MCDI_DECLARE_BUF (outbuf,MC_CMD_MAE_ENCAP_HEADER_FREE_OUT_LEN (1 )); +MCDI_DECLARE_BUF (inbuf,MC_CMD_MAE_ENCAP_HEADER_FREE_IN_LEN (1 )); +size_t outlen; +int rc; + +MCDI_SET_DWORD (inbuf,MAE_ENCAP_HEADER_FREE_IN_EH_ID , encap->fw_id); + rc =efx_mcdi_rpc (efx,MC_CMD_MAE_ENCAP_HEADER_FREE , inbuf, +sizeof (inbuf), outbuf,sizeof (outbuf), &outlen); +if (rc) +return rc; +if (outlen <sizeof (outbuf)) +return -EIO ; +/* 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 encap_mds exist, which could cause mayhem later. +*/ +if (WARN_ON (MCDI_DWORD (outbuf,MAE_ENCAP_HEADER_FREE_OUT_FREED_EH_ID ) != encap->fw_id)) +return -EIO ; +/* We're probably about to free @encap, but let's just make sure its +* fw_id is blatted so that it won't look valid if it leaks out. +*/ + encap->fw_id =MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_ENCAP_HEADER_ID_NULL ; +return 0 ; +} +int efx_mae_lookup_mport (struct efx_nic *efx,u32 vf_idx,u32 *id) {struct ef100_nic_data *nic_data = efx->nic_data;
@@ -833,8 +915,12 @@ int efx_mae_alloc_action_set(struct efx_nic *efx, struct efx_tc_action_set *act)MCDI_SET_WORD_BE (inbuf,MAE_ACTION_SET_ALLOC_IN_VLAN1_PROTO_BE , act->vlan_proto[1 ]); } -MCDI_SET_DWORD (inbuf,MAE_ACTION_SET_ALLOC_IN_ENCAP_HEADER_ID , -MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_ENCAP_HEADER_ID_NULL ); +if (act->encap_md) +MCDI_SET_DWORD (inbuf,MAE_ACTION_SET_ALLOC_IN_ENCAP_HEADER_ID , + act->encap_md->fw_id); +else +MCDI_SET_DWORD (inbuf,MAE_ACTION_SET_ALLOC_IN_ENCAP_HEADER_ID , +MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_ENCAP_HEADER_ID_NULL );if (act->deliver)MCDI_SET_DWORD (inbuf,MAE_ACTION_SET_ALLOC_IN_DELIVER , act->dest_mport);
drivers/net/ethernet/sfc/mae.h +7 -0
@@ -90,6 +90,13 @@ int efx_mae_check_encap_type_supported(struct efx_nic *efx,int efx_mae_allocate_counter (struct efx_nic *efx,struct efx_tc_counter *cnt);int efx_mae_free_counter (struct efx_nic *efx,struct efx_tc_counter *cnt); +int efx_mae_allocate_encap_md (struct efx_nic *efx, +struct efx_tc_encap_action *encap); +int efx_mae_update_encap_md (struct efx_nic *efx, +struct efx_tc_encap_action *encap); +int efx_mae_free_encap_md (struct efx_nic *efx, +struct efx_tc_encap_action *encap); +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);
Re: [PATCH net-next 4/6] sfc: MAE functions to create/update/delete encap headers
Quoted 9 lines · On 05/06/2023 20:17, [email protected] wrote:
From: Edward Cree <[email protected]>
Besides the raw header data, also pass the tunnel type, so that the
hardware knows it needs to update the IP Total Length and UDP Length fields (and corresponding checksums) for each packet.
Also, populate theENCAP_HEADER_IDfield inefx_mae_alloc_action_set()
with thefw_idreturned fromefx_mae_allocate_encap_md().Signed-off-by Edward Cree <[email protected]>
PATCH 5/6 sfc: neighbour lookup for TC encap action offload
In reply to [email protected]From: Edward Cree <[email protected]>
For each neighbour we're interested in, create a struct efx_neigh_binder
object which has a list of all the encap_actions using it. When we receive a neighbouring update (through the netevent notifier), find the corresponding efx_neigh_binder and update all its users.
Since the actual generation of encap headers is still only a stub, the
resulting rules still get left on fallback actions.
--- drivers/net/ethernet/sfc/ef100_netdev.c | 34 ++ drivers/net/ethernet/sfc/net_driver.h | 3 + drivers/net/ethernet/sfc/tc.c | 12 +- drivers/net/ethernet/sfc/tc.h | 7 + drivers/net/ethernet/sfc/tc_bindings.c | 13 + drivers/net/ethernet/sfc/tc_bindings.h | 2 + drivers/net/ethernet/sfc/tc_encap_actions.c | 444 +++++++++++++++++++- drivers/net/ethernet/sfc/tc_encap_actions.h | 52 +++ 8 files changed, 561 insertions(+), 6 deletions(-)
drivers/net/ethernet/sfc/ef100_netdev.c +34 -0
@@ -24,6 +24,7 @@#include "rx_common.h" #include "ef100_sriov.h" #include "tc_bindings.h" +#include "tc_encap_actions.h" #include "efx_devlink.h" static void ef100_update_name (struct efx_nic *efx)
@@ -300,14 +301,38 @@ int ef100_netdev_event(struct notifier_block *this, { struct efx_nic *efx = container_of(this, struct efx_nic, netdev_notifier);struct net_device *net_dev =netdev_notifier_info_to_dev (ptr); +struct ef100_nic_data *nic_data = efx->nic_data; +int err;if (efx->net_dev == net_dev && (event ==NETDEV_CHANGENAME || event ==NETDEV_REGISTER ))ef100_update_name (efx); +if (!nic_data->grp_mae) +return NOTIFY_DONE ; + err =efx_tc_netdev_event (efx, event, net_dev); +if (err &NOTIFY_STOP_MASK ) +return err; +return NOTIFY_DONE ; } +static int ef100_netevent_event (struct notifier_block *this, +unsigned long event,void *ptr) +{ + struct efx_nic *efx = container_of(this, struct efx_nic, netevent_notifier); +struct ef100_nic_data *nic_data = efx->nic_data; +int err; + +if (!nic_data->grp_mae) +return NOTIFY_DONE ; + err =efx_tc_netevent_event (efx, event, ptr); +if (err &NOTIFY_STOP_MASK ) +return err; + +return NOTIFY_DONE ; +}; +static int ef100_register_netdev (struct efx_nic *efx) {struct net_device *net_dev = efx->net_dev;
@@ -367,6 +392,7 @@ void ef100_remove_netdev(struct efx_probe_data *probe_data)rtnl_unlock ();unregister_netdevice_notifier (&efx->netdev_notifier); +unregister_netevent_notifier (&efx->netevent_notifier);#if defined(CONFIG_SFC_SRIOV )if (!efx->type->is_vf)efx_ef100_pci_sriov_disable (efx, true);
@@ -487,6 +513,14 @@ int ef100_probe_netdev(struct efx_probe_data *probe_data) goto fail; } + efx->netevent_notifier.notifier_call = ef100_netevent_event; + rc =register_netevent_notifier (&efx->netevent_notifier); +if (rc) { +netif_err (efx, probe, efx->net_dev, +"Failed to register netevent notifier, rc=%d\n" , rc); + goto fail; + } +efx_probe_devlink_unlock (efx);return rc; fail:
drivers/net/ethernet/sfc/net_driver.h +3 -0
@@ -27,6 +27,7 @@#include <linux/mtd/mtd.h> #include <net/busy_poll.h> #include <net/xdp.h> +#include <net/netevent.h> #include "enum.h" #include "bitfield.h"
@@ -996,6 +997,7 @@ struct efx_mae;* @xdp_rxq_info_failed: Have any of the rx queues failed to initialise their * xdp_rxq_info structures? * @netdev_notifier: Netdevice notifier. +* @netevent_notifier: Netevent notifier (for neighbour updates). * @tc: state for TC offload (EF100). * @devlink: reference to devlink structure owned by this device * @dl_port: devlink port associated with the PF
@@ -1183,6 +1185,7 @@ struct efx_nic {bool xdp_rxq_info_failed;struct notifier_block netdev_notifier; +struct notifier_block netevent_notifier;struct efx_tc_state *tc;struct devlink *devlink;
drivers/net/ethernet/sfc/tc.c +8 -4
@@ -34,8 +34,8 @@ enum efx_encap_type efx_tc_indr_netdev_type(struct net_device *net_dev)* May return NULL for the PF (us), or an error pointer for a device that * isn't supported as a TC offload endpoint */ -static struct efx_rep *efx_tc_flower_lookup_efv (struct efx_nic *efx, -struct net_device *dev) +struct efx_rep *efx_tc_flower_lookup_efv (struct efx_nic *efx, +struct net_device *dev) {struct efx_rep *efv;
@@ -71,7 +71,7 @@ static s64 efx_tc_flower_internal_mport(struct efx_nic *efx, struct efx_rep *efv }/* Convert a driver-internal vport ID into an external device (wire or VF) */ -static s64 efx_tc_flower_external_mport (struct efx_nic *efx,struct efx_rep *efv) +s64 efx_tc_flower_external_mport (struct efx_nic *efx,struct efx_rep *efv) {u32 mport;
@@ -112,8 +112,10 @@ static void efx_tc_free_action_set(struct efx_nic *efx, }if (act->count)efx_tc_flower_put_counter_index (efx, act->count); -if (act->encap_md) +if (act->encap_md) { +list_del (&act->encap_user);efx_tc_flower_release_encap_md (efx, act->encap_md); + }kfree (act); }
@@ -1115,6 +1117,7 @@ static int efx_tc_flower_replace(struct efx_nic *efx, goto release; } act->encap_md = encap; +list_add_tail (&act->encap_user, &encap->users); act->dest_mport = encap->dest_mport; act->deliver =1 ; rc =efx_mae_alloc_action_set (efx, act);
@@ -1123,6 +1126,7 @@ static int efx_tc_flower_replace(struct efx_nic *efx, goto release; }list_add_tail (&act->list, &rule->acts.list); + act->user = &rule->acts; act =NULL ;if (fa->id ==FLOW_ACTION_REDIRECT )break ;/* end of the line */
drivers/net/ethernet/sfc/tc.h +7 -0
@@ -36,6 +36,8 @@ struct efx_tc_action_set {__be16 vlan_proto[2 ];/* Ethertypes for vlan_push */ 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 */ u32 dest_mport;u32 fw_id;/* index of this entry in firmware actions table */ struct list_head list;
@@ -151,6 +153,7 @@ enum efx_tc_rule_prios {* @encap_ht: Hashtable of TC encap actions * @encap_match_ht: Hashtable of TC encap matches * @match_action_ht: Hashtable of TC match-action rules +* @neigh_ht: Hashtable of neighbour watches (&struct efx_neigh_binder) * @reps_mport_id: MAE port allocated for representor RX * @reps_filter_uc: VNIC filter for representor unicast RX (promisc) * @reps_filter_mc: VNIC filter for representor multicast RX (allmulti)
@@ -181,6 +184,7 @@ struct efx_tc_state {struct rhashtable encap_ht;struct rhashtable encap_match_ht;struct rhashtable match_action_ht; +struct rhashtable neigh_ht;u32 reps_mport_id, reps_mport_vport_id;s32 reps_filter_uc, reps_filter_mc;bool flush_counters;
@@ -201,6 +205,9 @@ struct efx_tc_state {struct efx_rep ;enum efx_encap_type efx_tc_indr_netdev_type (struct net_device *net_dev); +struct efx_rep *efx_tc_flower_lookup_efv (struct efx_nic *efx, +struct net_device *dev); +s64 efx_tc_flower_external_mport (struct efx_nic *efx,struct efx_rep *efv);int efx_tc_configure_default_rule_rep (struct efx_rep *efv);void efx_tc_deconfigure_default_rule (struct efx_nic *efx,struct efx_tc_flow_rule *rule);
drivers/net/ethernet/sfc/tc_bindings.c +13 -0
@@ -10,6 +10,7 @@#include "tc_bindings.h" #include "tc.h" +#include "tc_encap_actions.h" struct efx_tc_block_binding {struct list_head list;
@@ -226,3 +227,15 @@ int efx_tc_setup(struct net_device *net_dev, enum tc_setup_type type,return -EOPNOTSUPP ; } + +int efx_tc_netdev_event (struct efx_nic *efx,unsigned long event, +struct net_device *net_dev) +{ +if (efx->type->is_vf) +return NOTIFY_DONE ; + +if (event ==NETDEV_UNREGISTER ) +efx_tc_unregister_egdev (efx, net_dev); + +return NOTIFY_OK ; +}
drivers/net/ethernet/sfc/tc_bindings.h +2 -0
@@ -26,4 +26,6 @@ int efx_tc_indr_setup_cb(struct net_device *net_dev, struct Qdisc *sch, void *cb_priv, enum tc_setup_type type, void *type_data, void *data, void (*cleanup)(struct flow_block_cb *block_cb)); +int efx_tc_netdev_event (struct efx_nic *efx,unsigned long event, +struct net_device *net_dev);#endif /* EFX_TC_BINDINGS_H */
drivers/net/ethernet/sfc/tc_encap_actions.c +442 -2
503 lines not on this page · raw message
drivers/net/ethernet/sfc/tc_encap_actions.h +52 -0
69 lines not on this page · raw message
Re: [PATCH net-next 5/6] sfc: neighbour lookup for TC encap action offload
Quoted 10 lines · On 05/06/2023 20:17, [email protected] wrote:
From: Edward Cree <[email protected]>
For each neighbour we're interested in, create a struct
efx_neigh_binder
object which has a list of all theencap_actionsusing it. When we receive a neighbouring update (through the netevent notifier), find the correspondingefx_neigh_binderand update all its users.
Since the actual generation of encap headers is still only a stub, the
resulting rules still get left on fallback actions.Signed-off-by Edward Cree <[email protected]>
Jakub Kicinski
maintainerRe: [PATCH net-next 5/6] sfc: neighbour lookup for TC encap action offload
In reply to [email protected]drivers/net/ethernet/sfc/tc_encap_actions.c int efx_tc_init_encap_actions(struct efx_nic *efx) · line 155
Quoted 1 line · On Mon, 5 Jun 2023 20:17:38 +0100 [email protected] wrote:
drivers/net/ethernet/sfc/tc_encap_actions.c +1
-0+dev_hold (neigh->egdev = dst->dev);
Please use the ref-tracker enabled helpers in new code. And the assignment on a separate line, please.
Edward Cree
maintainerRe: [PATCH net-next 5/6] sfc: neighbour lookup for TC encap action offload
Quoted 4 lines · Jakub Kicinski
drivers/net/ethernet/sfc/tc_encap_actions.c int efx_tc_init_encap_actions(struct efx_nic *efx) · line 155
Quoted 1 line · On Mon, 5 Jun 2023 20:17:38 +0100 [email protected] wrote:
drivers/net/ethernet/sfc/tc_encap_actions.c +1
-0+dev_hold (neigh->egdev = dst->dev);Please use the ref-tracker enabled helpers in new code.
Fair point. Guessing that applies to the netns reference as well? (Though looking back I honestly can't remember why we need to hold
the neigh->net reference for the life of efx_neigh_binder; but I presumably had some reason for it at the time and I'm leery of removing it now in case it's load-bearing. Chesterton's Fence and all that.)
Quoted 1 line
And the assignment on a separate line, please.
Will do.
-ed
Jakub Kicinski
maintainerRe: [PATCH net-next 5/6] sfc: neighbour lookup for TC encap action offload
Quoted 1 line · On Wed, 7 Jun 2023 21:51:35 +0100 Edward Cree wrote:
Fair point. Guessing that applies to the netns reference as well?
Yes. I should add that to the checker..
Quoted 5 lines
(Though looking back I honestly can't remember why we need to hold
theneigh->netreference for the life ofefx_neigh_binder; but I presumably had some reason for it at the time and I'm leery of removing it now in case it's load-bearing. Chesterton's Fence and all that.)
You don't seem to be doing much with the reference, just holding it. Your call.
Re: [PATCH net-next 5/6] sfc: neighbour lookup for TC encap action offload
In reply to [email protected]On Mon, Jun 05, 2023 at 08:17:38PM +0100, [email protected] wrote:
...
Quoted 76 lines
drivers/net/ethernet/sfc/tc_encap_actions.c +76
-0+static int efx_bind_neigh (struct efx_nic *efx, +struct efx_tc_encap_action *encap,struct net *net, +struct netlink_ext_ack *extack) +{ +struct efx_neigh_binder *neigh, *old; +struct flowi6 flow6 = {}; +struct flowi4 flow4 = {}; +int rc; + +/* GCC stupidly thinks that only values explicitly listed in the enum +* definition can _possibly_ be sensible case values, so without this +* cast it complains about the IPv6 versions. +*/ +switch ((int )encap->type) { +case EFX_ENCAP_TYPE_VXLAN : +case EFX_ENCAP_TYPE_GENEVE : + flow4.flowi4_proto =IPPROTO_UDP ; + flow4.fl4_dport = encap->key.tp_dst; + flow4.flowi4_tos = encap->key.tos; + flow4.daddr = encap->key.u.ipv4.dst; + flow4.saddr = encap->key.u.ipv4.src; +break ; +case EFX_ENCAP_TYPE_VXLAN |EFX_ENCAP_FLAG_IPV6 : +case EFX_ENCAP_TYPE_GENEVE |EFX_ENCAP_FLAG_IPV6 : + flow6.flowi6_proto =IPPROTO_UDP ; + flow6.fl6_dport = encap->key.tp_dst; + flow6.flowlabel =ip6_make_flowinfo (encap->key.tos, + encap->key.label); + flow6.daddr = encap->key.u.ipv6.dst; + flow6.saddr = encap->key.u.ipv6.src; +break ; +default : +NL_SET_ERR_MSG_FMT_MOD (extack,"Unsupported encap type %d" , + (int )encap->type); +return -EOPNOTSUPP ; + } + + neigh =kzalloc (sizeof (*neigh),GFP_KERNEL_ACCOUNT ); +if (!neigh) +return -ENOMEM ; + neigh->net =get_net (net); + neigh->dst_ip = flow4.daddr; + neigh->dst_ip6 = flow6.daddr; + + old =rhashtable_lookup_get_insert_fast (&efx->tc->neigh_ht, + &neigh->linkage, + efx_neigh_ht_params); +if (old) { +/* don't need our new entry */ +put_net (neigh->net); +kfree (neigh); +if (!refcount_inc_not_zero (&old->ref)) +return -EAGAIN ; +/* existing entry found, ref taken */ + neigh = old; + }else { +/* New entry. We need to initiate a lookup */ +struct neighbour *n; +struct rtable *rt; + +if (encap->type &EFX_ENCAP_FLAG_IPV6 ) { +#if IS_ENABLED (CONFIG_IPV6 ) +struct dst_entry *dst; + + dst = ipv6_stub->ipv6_dst_lookup_flow (net,NULL , &flow6, +NULL ); + rc =PTR_ERR_OR_ZERO (dst); +if (rc) { +NL_SET_ERR_MSG_MOD (extack,"Failed to lookup route for IPv6 encap" ); + goto out_free; + } +dev_hold (neigh->egdev = dst->dev); + neigh->ttl =ip6_dst_hoplimit (dst); + n =dst_neigh_lookup (dst, &flow6.daddr); +dst_release (dst); +#else
drivers/net/ethernet/sfc/tc_encap_actions.c int efx_tc_init_encap_actions(struct efx_nic *efx) · line 169
Quoted 10 lines
drivers/net/ethernet/sfc/tc_encap_actions.c +10
-0+/* We shouldn't ever get here, because if IPv6 isn't +* enabled how did someone create an IPv6 tunnel_key? +*/ + rc = -EOPNOTSUPP ; +NL_SET_ERR_MSG_MOD (extack,"No IPv6 support (neigh bind)" ); +#endif + }else { + rt =ip_route_output_key (net, &flow4); +if (IS_ERR_OR_NULL (rt)) { + rc =PTR_ERR (rt);
Hi Edward,
A minor nit from my side: perhaps this should use PTR_ERR_OR_ZERO().
Quoted 45 lines
drivers/net/ethernet/sfc/tc_encap_actions.c +45
-0+if (!rc) + rc = -EIO ; +NL_SET_ERR_MSG_MOD (extack,"Failed to lookup route for encap" ); + goto out_free; + } +dev_hold (neigh->egdev = rt->dst.dev); + neigh->ttl =ip4_dst_hoplimit (&rt->dst); + n =dst_neigh_lookup (&rt->dst, &flow4.daddr); +ip_rt_put (rt); + } +if (!n) { + rc = -ENETUNREACH ; +NL_SET_ERR_MSG_MOD (extack,"Failed to lookup neighbour for encap" ); +dev_put (neigh->egdev); + goto out_free; + } +refcount_set (&neigh->ref,1 ); +INIT_LIST_HEAD (&neigh->users); +read_lock_bh (&n->lock); +ether_addr_copy (neigh->ha, n->ha); + neigh->n_valid = n->nud_state &NUD_VALID ; +read_unlock_bh (&n->lock); +rwlock_init (&neigh->lock); +INIT_WORK (&neigh->work, efx_neigh_update); + neigh->efx = efx; + neigh->used = jiffies; +if (!neigh->n_valid) +/* Prod ARP to find us a neighbour */ +neigh_event_send (n,NULL ); +neigh_release (n); + } +/* Add us to this neigh */ + encap->neigh = neigh; +list_add_tail (&encap->list, &neigh->users); +return 0 ; + +out_free: +/* cleanup common to several error paths */ +rhashtable_remove_fast (&efx->tc->neigh_ht, &neigh->linkage, + efx_neigh_ht_params); +synchronize_rcu (); +put_net (net); +kfree (neigh); +return rc; +}
...
PATCH 6/6 sfc: generate encap headers for TC offload
In reply to [email protected]From: Edward Cree <[email protected]>
Support constructing VxLAN and GENEVE headers, on either IPv4 or IPv6,
using the neighbouring information obtained in encap->neigh to populate the Ethernet header.
Note that the ef100 hardware does not insert UDP checksums when
performing encap, so for IPv6 the remote endpoint will need to be configured with udp6zerocsumrx or equivalent.
--- drivers/net/ethernet/sfc/tc_encap_actions.c | 194 +++++++++++++++++++- 1 file changed, 185 insertions(+), 9 deletions(-)
drivers/net/ethernet/sfc/tc_encap_actions.c +185 -9
@@ -239,12 +239,183 @@ static void efx_release_neigh(struct efx_nic *efx,efx_free_neigh (neigh); } -static void efx_gen_encap_header (struct efx_tc_encap_action *encap) +static void efx_gen_tun_header_eth (struct efx_tc_encap_action *encap,u16 proto) { -/* stub for now */ - encap->n_valid = false; -memset (encap->encap_hdr,0 ,sizeof (encap->encap_hdr)); - encap->encap_hdr_len =ETH_HLEN ; +struct efx_neigh_binder *neigh = encap->neigh; +struct ethhdr *eth; + + encap->encap_hdr_len =sizeof (*eth); + eth = (struct ethhdr *)encap->encap_hdr; + +if (encap->neigh->n_valid) +ether_addr_copy (eth->h_dest, neigh->ha); +else +eth_zero_addr (eth->h_dest); +ether_addr_copy (eth->h_source, neigh->egdev->dev_addr); + eth->h_proto =htons (proto); +} + +static void efx_gen_tun_header_ipv4 (struct efx_tc_encap_action *encap,u8 ipproto,u8 len) +{ +struct efx_neigh_binder *neigh = encap->neigh; +struct ip_tunnel_key *key = &encap->key; +struct iphdr *ip; + + ip = (struct iphdr *)(encap->encap_hdr + encap->encap_hdr_len); + encap->encap_hdr_len +=sizeof (*ip); + + ip->daddr = key->u.ipv4.dst; + ip->saddr = key->u.ipv4.src; + ip->ttl = neigh->ttl; + ip->protocol = ipproto; + ip->version =0x4 ; + ip->ihl =0x5 ; + ip->tot_len =cpu_to_be16 (ip->ihl *4 + len); +ip_send_check (ip); +} + +#ifdef CONFIG_IPV6 +static void efx_gen_tun_header_ipv6 (struct efx_tc_encap_action *encap,u8 ipproto,u8 len) +{ +struct efx_neigh_binder *neigh = encap->neigh; +struct ip_tunnel_key *key = &encap->key; +struct ipv6hdr *ip; + + ip = (struct ipv6hdr *)(encap->encap_hdr + encap->encap_hdr_len); + encap->encap_hdr_len +=sizeof (*ip); + +ip6_flow_hdr (ip, key->tos, key->label); + ip->daddr = key->u.ipv6.dst; + ip->saddr = key->u.ipv6.src; + ip->hop_limit = neigh->ttl; + ip->nexthdr = ipproto; + ip->version =0x6 ; + ip->payload_len =cpu_to_be16 (len); +} +#endif + +static void efx_gen_tun_header_udp (struct efx_tc_encap_action *encap,u8 len) +{ +struct ip_tunnel_key *key = &encap->key; +struct udphdr *udp; + + udp = (struct udphdr *)(encap->encap_hdr + encap->encap_hdr_len); + encap->encap_hdr_len +=sizeof (*udp); + + udp->dest = key->tp_dst; + udp->len =cpu_to_be16 (sizeof (*udp) + len); +} + +static void efx_gen_tun_header_vxlan (struct efx_tc_encap_action *encap) +{ +struct ip_tunnel_key *key = &encap->key; +struct vxlanhdr *vxlan; + + vxlan = (struct vxlanhdr *)(encap->encap_hdr + encap->encap_hdr_len); + encap->encap_hdr_len +=sizeof (*vxlan); + + vxlan->vx_flags =VXLAN_HF_VNI ; + vxlan->vx_vni =vxlan_vni_field (tunnel_id_to_key32 (key->tun_id)); +} + +static void efx_gen_tun_header_geneve (struct efx_tc_encap_action *encap) +{ +struct ip_tunnel_key *key = &encap->key; +struct genevehdr *geneve; +u32 vni; + + geneve = (struct genevehdr *)(encap->encap_hdr + encap->encap_hdr_len); + encap->encap_hdr_len +=sizeof (*geneve); + + geneve->proto_type =htons (ETH_P_TEB ); +/* convert tun_id to host-endian so we can use host arithmetic to +* extract individual bytes. +*/ + vni =ntohl (tunnel_id_to_key32 (key->tun_id)); + geneve->vni[0 ] = vni >>16 ; + geneve->vni[1 ] = vni >>8 ; + geneve->vni[2 ] = vni; +} + +#define vxlan_header_l4_len (sizeof(struct udphdr) + sizeof(struct vxlanhdr)) +#define vxlan4_header_len (sizeof(struct ethhdr) + sizeof(struct iphdr) + vxlan_header_l4_len) +static void efx_gen_vxlan_header_ipv4 (struct efx_tc_encap_action *encap) +{ +BUILD_BUG_ON (sizeof (encap->encap_hdr) < vxlan4_header_len); +efx_gen_tun_header_eth (encap,ETH_P_IP ); +efx_gen_tun_header_ipv4 (encap,IPPROTO_UDP , vxlan_header_l4_len); +efx_gen_tun_header_udp (encap,sizeof (struct vxlanhdr )); +efx_gen_tun_header_vxlan (encap); +} + +#define geneve_header_l4_len (sizeof(struct udphdr) + sizeof(struct genevehdr)) +#define geneve4_header_len (sizeof(struct ethhdr) + sizeof(struct iphdr) + geneve_header_l4_len) +static void efx_gen_geneve_header_ipv4 (struct efx_tc_encap_action *encap) +{ +BUILD_BUG_ON (sizeof (encap->encap_hdr) < geneve4_header_len); +efx_gen_tun_header_eth (encap,ETH_P_IP ); +efx_gen_tun_header_ipv4 (encap,IPPROTO_UDP , geneve_header_l4_len); +efx_gen_tun_header_udp (encap,sizeof (struct genevehdr )); +efx_gen_tun_header_geneve (encap); +} + +#ifdef CONFIG_IPV6 +#define vxlan6_header_len (sizeof(struct ethhdr) + sizeof(struct ipv6hdr) + vxlan_header_l4_len) +static void efx_gen_vxlan_header_ipv6 (struct efx_tc_encap_action *encap) +{ +BUILD_BUG_ON (sizeof (encap->encap_hdr) < vxlan6_header_len); +efx_gen_tun_header_eth (encap,ETH_P_IPV6 ); +efx_gen_tun_header_ipv6 (encap,IPPROTO_UDP , vxlan_header_l4_len); +efx_gen_tun_header_udp (encap,sizeof (struct vxlanhdr )); +efx_gen_tun_header_vxlan (encap); +} + +#define geneve6_header_len (sizeof(struct ethhdr) + sizeof(struct ipv6hdr) + geneve_header_l4_len) +static void efx_gen_geneve_header_ipv6 (struct efx_tc_encap_action *encap) +{ +BUILD_BUG_ON (sizeof (encap->encap_hdr) < geneve6_header_len); +efx_gen_tun_header_eth (encap,ETH_P_IPV6 ); +efx_gen_tun_header_ipv6 (encap,IPPROTO_UDP , geneve_header_l4_len); +efx_gen_tun_header_udp (encap,sizeof (struct genevehdr )); +efx_gen_tun_header_geneve (encap); +} +#endif + +static void efx_gen_encap_header (struct efx_nic *efx, +struct efx_tc_encap_action *encap) +{ + encap->n_valid = encap->neigh->n_valid; + +/* GCC stupidly thinks that only values explicitly listed in the enum +* definition can _possibly_ be sensible case values, so without this +* cast it complains about the IPv6 versions. +*/ +switch ((int )encap->type) { +case EFX_ENCAP_TYPE_VXLAN : +efx_gen_vxlan_header_ipv4 (encap); +break ; +case EFX_ENCAP_TYPE_GENEVE : +efx_gen_geneve_header_ipv4 (encap); +break ; +#ifdef CONFIG_IPV6 +case EFX_ENCAP_TYPE_VXLAN |EFX_ENCAP_FLAG_IPV6 : +efx_gen_vxlan_header_ipv6 (encap); +break ; +case EFX_ENCAP_TYPE_GENEVE |EFX_ENCAP_FLAG_IPV6 : +efx_gen_geneve_header_ipv6 (encap); +break ; +#endif +default : +/* unhandled encap type, can't happen */ +if (net_ratelimit ()) +netif_err (efx, drv, efx->net_dev, +"Bogus encap type %d, can't generate\n" , + encap->type); + +/* Use fallback action. */ + encap->n_valid = false; +break ; + } } static void efx_tc_update_encap(struct efx_nic *efx,
@@ -278,14 +449,19 @@ static void efx_tc_update_encap(struct efx_nic *efx, } } +/* Make sure we don't leak arbitrary bytes on the wire; +* set an all-0s ethernet header. A successful call to +* efx_gen_encap_header() will overwrite this. +*/ +memset (encap->encap_hdr,0 ,sizeof (encap->encap_hdr)); + encap->encap_hdr_len =ETH_HLEN ; +if (encap->neigh) {read_lock_bh (&encap->neigh->lock); -efx_gen_encap_header (encap); +efx_gen_encap_header (efx, encap);read_unlock_bh (&encap->neigh->lock); }else { encap->n_valid = false; -memset (encap->encap_hdr,0 ,sizeof (encap->encap_hdr)); - encap->encap_hdr_len =ETH_HLEN ; } rc =efx_mae_update_encap_md (efx, encap);
@@ -482,7 +658,7 @@ struct efx_tc_encap_action *efx_tc_flower_create_encap_md( } encap->dest_mport = rc;read_lock_bh (&encap->neigh->lock); -efx_gen_encap_header (encap); +efx_gen_encap_header (efx, encap);read_unlock_bh (&encap->neigh->lock); rc =efx_mae_allocate_encap_md (efx, encap);
Re: [PATCH net-next 6/6] sfc: generate encap headers for TC offload
Quoted 196 lines · On 2023/6/6 3:17, [email protected] wrote:
From: Edward Cree <[email protected]>
Support constructing VxLAN and GENEVE headers, on either IPv4 or IPv6,
using the neighbouring information obtained inencap->neighto populate the Ethernet header.
Note that the ef100 hardware does not insert UDP checksums when
performing encap, so for IPv6 the remote endpoint will need to be configured with udp6zerocsumrx or equivalent.Signed-off-by Edward Cree <[email protected]>--- drivers/net/ethernet/sfc/tc_encap_actions.c | 194 +++++++++++++++++++- 1 file changed, 185 insertions(+), 9 deletions(-)drivers/net/ethernet/sfc/tc_encap_actions.c +168
-5@@ -239,12 +239,183 @@ static void efx_release_neigh(struct efx_nic *efx,efx_free_neigh (neigh); } -static void efx_gen_encap_header (struct efx_tc_encap_action *encap) +static void efx_gen_tun_header_eth (struct efx_tc_encap_action *encap,u16 proto) { -/* stub for now */ - encap->n_valid = false; -memset (encap->encap_hdr,0 ,sizeof (encap->encap_hdr)); - encap->encap_hdr_len =ETH_HLEN ; +struct efx_neigh_binder *neigh = encap->neigh; +struct ethhdr *eth; + + encap->encap_hdr_len =sizeof (*eth); + eth = (struct ethhdr *)encap->encap_hdr; + +if (encap->neigh->n_valid) +ether_addr_copy (eth->h_dest, neigh->ha); +else +eth_zero_addr (eth->h_dest); +ether_addr_copy (eth->h_source, neigh->egdev->dev_addr); + eth->h_proto =htons (proto); +} + +static void efx_gen_tun_header_ipv4 (struct efx_tc_encap_action *encap,u8 ipproto,u8 len) +{ +struct efx_neigh_binder *neigh = encap->neigh; +struct ip_tunnel_key *key = &encap->key; +struct iphdr *ip; + + ip = (struct iphdr *)(encap->encap_hdr + encap->encap_hdr_len); + encap->encap_hdr_len +=sizeof (*ip); + + ip->daddr = key->u.ipv4.dst; + ip->saddr = key->u.ipv4.src; + ip->ttl = neigh->ttl; + ip->protocol = ipproto; + ip->version =0x4 ; + ip->ihl =0x5 ; + ip->tot_len =cpu_to_be16 (ip->ihl *4 + len); +ip_send_check (ip); +} + +#ifdef CONFIG_IPV6 +static void efx_gen_tun_header_ipv6 (struct efx_tc_encap_action *encap,u8 ipproto,u8 len) +{ +struct efx_neigh_binder *neigh = encap->neigh; +struct ip_tunnel_key *key = &encap->key; +struct ipv6hdr *ip; + + ip = (struct ipv6hdr *)(encap->encap_hdr + encap->encap_hdr_len); + encap->encap_hdr_len +=sizeof (*ip); + +ip6_flow_hdr (ip, key->tos, key->label); + ip->daddr = key->u.ipv6.dst; + ip->saddr = key->u.ipv6.src; + ip->hop_limit = neigh->ttl; + ip->nexthdr = ipproto; + ip->version =0x6 ; + ip->payload_len =cpu_to_be16 (len); +} +#endif + +static void efx_gen_tun_header_udp (struct efx_tc_encap_action *encap,u8 len) +{ +struct ip_tunnel_key *key = &encap->key; +struct udphdr *udp; + + udp = (struct udphdr *)(encap->encap_hdr + encap->encap_hdr_len); + encap->encap_hdr_len +=sizeof (*udp); + + udp->dest = key->tp_dst; + udp->len =cpu_to_be16 (sizeof (*udp) + len); +} + +static void efx_gen_tun_header_vxlan (struct efx_tc_encap_action *encap) +{ +struct ip_tunnel_key *key = &encap->key; +struct vxlanhdr *vxlan; + + vxlan = (struct vxlanhdr *)(encap->encap_hdr + encap->encap_hdr_len); + encap->encap_hdr_len +=sizeof (*vxlan); + + vxlan->vx_flags =VXLAN_HF_VNI ; + vxlan->vx_vni =vxlan_vni_field (tunnel_id_to_key32 (key->tun_id)); +} + +static void efx_gen_tun_header_geneve (struct efx_tc_encap_action *encap) +{ +struct ip_tunnel_key *key = &encap->key; +struct genevehdr *geneve; +u32 vni; + + geneve = (struct genevehdr *)(encap->encap_hdr + encap->encap_hdr_len); + encap->encap_hdr_len +=sizeof (*geneve); + + geneve->proto_type =htons (ETH_P_TEB ); +/* convert tun_id to host-endian so we can use host arithmetic to +* extract individual bytes. +*/ + vni =ntohl (tunnel_id_to_key32 (key->tun_id)); + geneve->vni[0 ] = vni >>16 ; + geneve->vni[1 ] = vni >>8 ; + geneve->vni[2 ] = vni; +} + +#define vxlan_header_l4_len (sizeof(struct udphdr) + sizeof(struct vxlanhdr)) +#define vxlan4_header_len (sizeof(struct ethhdr) + sizeof(struct iphdr) + vxlan_header_l4_len) +static void efx_gen_vxlan_header_ipv4 (struct efx_tc_encap_action *encap) +{ +BUILD_BUG_ON (sizeof (encap->encap_hdr) < vxlan4_header_len); +efx_gen_tun_header_eth (encap,ETH_P_IP ); +efx_gen_tun_header_ipv4 (encap,IPPROTO_UDP , vxlan_header_l4_len); +efx_gen_tun_header_udp (encap,sizeof (struct vxlanhdr )); +efx_gen_tun_header_vxlan (encap); +} + +#define geneve_header_l4_len (sizeof(struct udphdr) + sizeof(struct genevehdr)) +#define geneve4_header_len (sizeof(struct ethhdr) + sizeof(struct iphdr) + geneve_header_l4_len) +static void efx_gen_geneve_header_ipv4 (struct efx_tc_encap_action *encap) +{ +BUILD_BUG_ON (sizeof (encap->encap_hdr) < geneve4_header_len); +efx_gen_tun_header_eth (encap,ETH_P_IP ); +efx_gen_tun_header_ipv4 (encap,IPPROTO_UDP , geneve_header_l4_len); +efx_gen_tun_header_udp (encap,sizeof (struct genevehdr )); +efx_gen_tun_header_geneve (encap); +} + +#ifdef CONFIG_IPV6 +#define vxlan6_header_len (sizeof(struct ethhdr) + sizeof(struct ipv6hdr) + vxlan_header_l4_len) +static void efx_gen_vxlan_header_ipv6 (struct efx_tc_encap_action *encap) +{ +BUILD_BUG_ON (sizeof (encap->encap_hdr) < vxlan6_header_len); +efx_gen_tun_header_eth (encap,ETH_P_IPV6 ); +efx_gen_tun_header_ipv6 (encap,IPPROTO_UDP , vxlan_header_l4_len); +efx_gen_tun_header_udp (encap,sizeof (struct vxlanhdr )); +efx_gen_tun_header_vxlan (encap); +} + +#define geneve6_header_len (sizeof(struct ethhdr) + sizeof(struct ipv6hdr) + geneve_header_l4_len) +static void efx_gen_geneve_header_ipv6 (struct efx_tc_encap_action *encap) +{ +BUILD_BUG_ON (sizeof (encap->encap_hdr) < geneve6_header_len); +efx_gen_tun_header_eth (encap,ETH_P_IPV6 ); +efx_gen_tun_header_ipv6 (encap,IPPROTO_UDP , geneve_header_l4_len); +efx_gen_tun_header_udp (encap,sizeof (struct genevehdr )); +efx_gen_tun_header_geneve (encap); +} +#endif + +static void efx_gen_encap_header (struct efx_nic *efx, +struct efx_tc_encap_action *encap) +{ + encap->n_valid = encap->neigh->n_valid; + +/* GCC stupidly thinks that only values explicitly listed in the enum +* definition can _possibly_ be sensible case values, so without this +* cast it complains about the IPv6 versions. +*/ +switch ((int )encap->type) { +case EFX_ENCAP_TYPE_VXLAN : +efx_gen_vxlan_header_ipv4 (encap); +break ; +case EFX_ENCAP_TYPE_GENEVE : +efx_gen_geneve_header_ipv4 (encap); +break ; +#ifdef CONFIG_IPV6 +case EFX_ENCAP_TYPE_VXLAN |EFX_ENCAP_FLAG_IPV6 : +efx_gen_vxlan_header_ipv6 (encap); +break ; +case EFX_ENCAP_TYPE_GENEVE |EFX_ENCAP_FLAG_IPV6 : +efx_gen_geneve_header_ipv6 (encap); +break ; +#endif +default : +/* unhandled encap type, can't happen */ +if (net_ratelimit ())
drivers/net/ethernet/sfc/tc_encap_actions.c static void efx_release_neigh(struct efx_nic *efx, · line 420
Quoted 10 lines
drivers/net/ethernet/sfc/tc_encap_actions.c +8
-0+netif_err (efx, drv, efx->net_dev, +"Bogus encap type %d, can't generate\n" , + encap->type); + +/* Use fallback action. */ + encap->n_valid = false; +break ; + } }
Hello Edward Cree,
Why do you need to refactor the efx_gen_encap_header function in the same series? I saw that patch 5 defined this function, and patch 6 refactored it, instead of writing it all at once?
Regards,
Hao Lan
Quoted 36 lines
drivers/net/ethernet/sfc/tc_encap_actions.c +9
-4static void efx_tc_update_encap(struct efx_nic *efx,@@ -278,14 +449,19 @@ static void efx_tc_update_encap(struct efx_nic *efx, } } +/* Make sure we don't leak arbitrary bytes on the wire; +* set an all-0s ethernet header. A successful call to +* efx_gen_encap_header() will overwrite this. +*/ +memset (encap->encap_hdr,0 ,sizeof (encap->encap_hdr)); + encap->encap_hdr_len =ETH_HLEN ; +if (encap->neigh) {read_lock_bh (&encap->neigh->lock); -efx_gen_encap_header (encap); +efx_gen_encap_header (efx, encap);read_unlock_bh (&encap->neigh->lock); }else { encap->n_valid = false; -memset (encap->encap_hdr,0 ,sizeof (encap->encap_hdr)); - encap->encap_hdr_len =ETH_HLEN ; } rc =efx_mae_update_encap_md (efx, encap);@@ -482,7 +658,7 @@ struct efx_tc_encap_action *efx_tc_flower_create_encap_md( } encap->dest_mport = rc;read_lock_bh (&encap->neigh->lock); -efx_gen_encap_header (encap); +efx_gen_encap_header (efx, encap);read_unlock_bh (&encap->neigh->lock); rc =efx_mae_allocate_encap_md (efx, encap);.
Edward Cree
maintainerRe: [PATCH net-next 6/6] sfc: generate encap headers for TC offload
Quoted 3 lines · Hao Lan
Why do you need to refactor the
efx_gen_encap_headerfunction in the same series? I saw that patch 5 defined this function, and patch 6 refactored it, instead of writing it all at once?
Patch 5 introduces it only as a stub, because the calling code needs
it to exist. Patch 6 then provides the implementation; this is not a refactoring.
They're in separate patches to split things up logically for review
and to assist future bisection. Patch 5 is already big and complex without this part.
(Also, please trim your quotes when replying to patches: you only
need to quote the part you're commenting on.)
-ed
Hao Lan
Reviewed-byRe: [PATCH net-next 6/6] sfc: generate encap headers for TC offload
Quoted 11 lines · Edward Cree
Quoted 3 lines · Hao Lan
Why do you need to refactor the
efx_gen_encap_headerfunction in the same series? I saw that patch 5 defined this function, and patch 6 refactored it, instead of writing it all at once?Patch 5 introduces it only as a stub, because the calling code needs
it to exist. Patch 6 then provides the implementation; this is not a refactoring.
They're in separate patches to split things up logically for review
and to assist future bisection. Patch 5 is already big and complex without this part.
Quoted 6 lines
(Also, please trim your quotes when replying to patches: you only
need to quote the part you're commenting on.)-ed
.
This is a great idea for committing complex code,
thank you.
Re: [PATCH net-next 6/6] sfc: generate encap headers for TC offload
In reply to [email protected]Quoted 10 lines · On 05/06/2023 20:17, [email protected] wrote:
From: Edward Cree <[email protected]>
Support constructing VxLAN and GENEVE headers, on either IPv4 or IPv6,
using the neighbouring information obtained inencap->neighto populate the Ethernet header.
Note that the ef100 hardware does not insert UDP checksums when
performing encap, so for IPv6 the remote endpoint will need to be configured with udp6zerocsumrx or equivalent.Signed-off-by Edward Cree <[email protected]>