Skip to content

Commit e5fbd97

Browse files
jco-xilinxdavem330
authored andcommitted
sfc: configure UDP tunnel offload ports
Implement ndo_udp_tunnel_{add,del} to update the NIC's list of VXLAN and GENEVE UDP ports. Also reset the port list to empty on driver load and on driver unload, with appropriate flag set on the unload case. These port numbers are used for RX inner checksum offload, and in future will also be used for TX inner checksum offload and encapsulated TSO. Signed-off-by: Edward Cree <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent d4e8547 commit e5fbd97

File tree

7 files changed

+397
-1
lines changed

7 files changed

+397
-1
lines changed

drivers/net/ethernet/sfc/ef10.c

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ static int efx_ef10_filter_add_vlan(struct efx_nic *efx, u16 vid);
132132
static void efx_ef10_filter_del_vlan_internal(struct efx_nic *efx,
133133
struct efx_ef10_filter_vlan *vlan);
134134
static void efx_ef10_filter_del_vlan(struct efx_nic *efx, u16 vid);
135+
static int efx_ef10_set_udp_tnl_ports(struct efx_nic *efx, bool unloading);
135136

136137
static int efx_ef10_get_warm_boot_count(struct efx_nic *efx)
137138
{
@@ -624,6 +625,8 @@ static int efx_ef10_probe(struct efx_nic *efx)
624625
if (rc)
625626
goto fail2;
626627

628+
mutex_init(&nic_data->udp_tunnels_lock);
629+
627630
/* Reset (most) configuration for this function */
628631
rc = efx_mcdi_reset(efx, RESET_TYPE_ALL);
629632
if (rc)
@@ -712,6 +715,14 @@ static int efx_ef10_probe(struct efx_nic *efx)
712715
fail4:
713716
device_remove_file(&efx->pci_dev->dev, &dev_attr_link_control_flag);
714717
fail3:
718+
efx_mcdi_detach(efx);
719+
720+
mutex_lock(&nic_data->udp_tunnels_lock);
721+
memset(nic_data->udp_tunnels, 0, sizeof(nic_data->udp_tunnels));
722+
(void)efx_ef10_set_udp_tnl_ports(efx, true);
723+
mutex_unlock(&nic_data->udp_tunnels_lock);
724+
mutex_destroy(&nic_data->udp_tunnels_lock);
725+
715726
efx_mcdi_fini(efx);
716727
fail2:
717728
efx_nic_free_buffer(efx, &nic_data->mcdi_buf);
@@ -981,6 +992,15 @@ static void efx_ef10_remove(struct efx_nic *efx)
981992
device_remove_file(&efx->pci_dev->dev, &dev_attr_primary_flag);
982993
device_remove_file(&efx->pci_dev->dev, &dev_attr_link_control_flag);
983994

995+
efx_mcdi_detach(efx);
996+
997+
memset(nic_data->udp_tunnels, 0, sizeof(nic_data->udp_tunnels));
998+
mutex_lock(&nic_data->udp_tunnels_lock);
999+
(void)efx_ef10_set_udp_tnl_ports(efx, true);
1000+
mutex_unlock(&nic_data->udp_tunnels_lock);
1001+
1002+
mutex_destroy(&nic_data->udp_tunnels_lock);
1003+
9841004
efx_mcdi_fini(efx);
9851005
efx_nic_free_buffer(efx, &nic_data->mcdi_buf);
9861006
kfree(nic_data);
@@ -6066,6 +6086,271 @@ static int efx_ef10_vlan_rx_kill_vid(struct efx_nic *efx, __be16 proto, u16 vid)
60666086
return efx_ef10_del_vlan(efx, vid);
60676087
}
60686088

6089+
/* We rely on the MCDI wiping out our TX rings if it made any changes to the
6090+
* ports table, ensuring that any TSO descriptors that were made on a now-
6091+
* removed tunnel port will be blown away and won't break things when we try
6092+
* to transmit them using the new ports table.
6093+
*/
6094+
static int efx_ef10_set_udp_tnl_ports(struct efx_nic *efx, bool unloading)
6095+
{
6096+
struct efx_ef10_nic_data *nic_data = efx->nic_data;
6097+
MCDI_DECLARE_BUF(inbuf, MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_LENMAX);
6098+
MCDI_DECLARE_BUF(outbuf, MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT_LEN);
6099+
bool will_reset = false;
6100+
size_t num_entries = 0;
6101+
size_t inlen, outlen;
6102+
size_t i;
6103+
int rc;
6104+
efx_dword_t flags_and_num_entries;
6105+
6106+
WARN_ON(!mutex_is_locked(&nic_data->udp_tunnels_lock));
6107+
6108+
nic_data->udp_tunnels_dirty = false;
6109+
6110+
if (!(nic_data->datapath_caps &
6111+
(1 << MC_CMD_GET_CAPABILITIES_OUT_VXLAN_NVGRE_LBN))) {
6112+
netif_device_attach(efx->net_dev);
6113+
return 0;
6114+
}
6115+
6116+
BUILD_BUG_ON(ARRAY_SIZE(nic_data->udp_tunnels) >
6117+
MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_ENTRIES_MAXNUM);
6118+
6119+
for (i = 0; i < ARRAY_SIZE(nic_data->udp_tunnels); ++i) {
6120+
if (nic_data->udp_tunnels[i].count &&
6121+
nic_data->udp_tunnels[i].port) {
6122+
efx_dword_t entry;
6123+
6124+
EFX_POPULATE_DWORD_2(entry,
6125+
TUNNEL_ENCAP_UDP_PORT_ENTRY_UDP_PORT,
6126+
ntohs(nic_data->udp_tunnels[i].port),
6127+
TUNNEL_ENCAP_UDP_PORT_ENTRY_PROTOCOL,
6128+
nic_data->udp_tunnels[i].type);
6129+
*_MCDI_ARRAY_DWORD(inbuf,
6130+
SET_TUNNEL_ENCAP_UDP_PORTS_IN_ENTRIES,
6131+
num_entries++) = entry;
6132+
}
6133+
}
6134+
6135+
BUILD_BUG_ON((MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_NUM_ENTRIES_OFST -
6136+
MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_FLAGS_OFST) * 8 !=
6137+
EFX_WORD_1_LBN);
6138+
BUILD_BUG_ON(MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_NUM_ENTRIES_LEN * 8 !=
6139+
EFX_WORD_1_WIDTH);
6140+
EFX_POPULATE_DWORD_2(flags_and_num_entries,
6141+
MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_UNLOADING,
6142+
!!unloading,
6143+
EFX_WORD_1, num_entries);
6144+
*_MCDI_DWORD(inbuf, SET_TUNNEL_ENCAP_UDP_PORTS_IN_FLAGS) =
6145+
flags_and_num_entries;
6146+
6147+
inlen = MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_IN_LEN(num_entries);
6148+
6149+
rc = efx_mcdi_rpc_quiet(efx, MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS,
6150+
inbuf, inlen, outbuf, sizeof(outbuf), &outlen);
6151+
if (rc == -EIO) {
6152+
/* Most likely the MC rebooted due to another function also
6153+
* setting its tunnel port list. Mark the tunnel port list as
6154+
* dirty, so it will be pushed upon coming up from the reboot.
6155+
*/
6156+
nic_data->udp_tunnels_dirty = true;
6157+
return 0;
6158+
}
6159+
6160+
if (rc) {
6161+
/* expected not available on unprivileged functions */
6162+
if (rc != -EPERM)
6163+
netif_warn(efx, drv, efx->net_dev,
6164+
"Unable to set UDP tunnel ports; rc=%d.\n", rc);
6165+
} else if (MCDI_DWORD(outbuf, SET_TUNNEL_ENCAP_UDP_PORTS_OUT_FLAGS) &
6166+
(1 << MC_CMD_SET_TUNNEL_ENCAP_UDP_PORTS_OUT_RESETTING_LBN)) {
6167+
netif_info(efx, drv, efx->net_dev,
6168+
"Rebooting MC due to UDP tunnel port list change\n");
6169+
will_reset = true;
6170+
if (unloading)
6171+
/* Delay for the MC reset to complete. This will make
6172+
* unloading other functions a bit smoother. This is a
6173+
* race, but the other unload will work whichever way
6174+
* it goes, this just avoids an unnecessary error
6175+
* message.
6176+
*/
6177+
msleep(100);
6178+
}
6179+
if (!will_reset && !unloading) {
6180+
/* The caller will have detached, relying on the MC reset to
6181+
* trigger a re-attach. Since there won't be an MC reset, we
6182+
* have to do the attach ourselves.
6183+
*/
6184+
netif_device_attach(efx->net_dev);
6185+
}
6186+
6187+
return rc;
6188+
}
6189+
6190+
static int efx_ef10_udp_tnl_push_ports(struct efx_nic *efx)
6191+
{
6192+
struct efx_ef10_nic_data *nic_data = efx->nic_data;
6193+
int rc = 0;
6194+
6195+
mutex_lock(&nic_data->udp_tunnels_lock);
6196+
if (nic_data->udp_tunnels_dirty) {
6197+
/* Make sure all TX are stopped while we modify the table, else
6198+
* we might race against an efx_features_check().
6199+
*/
6200+
efx_device_detach_sync(efx);
6201+
rc = efx_ef10_set_udp_tnl_ports(efx, false);
6202+
}
6203+
mutex_unlock(&nic_data->udp_tunnels_lock);
6204+
return rc;
6205+
}
6206+
6207+
static struct efx_udp_tunnel *__efx_ef10_udp_tnl_lookup_port(struct efx_nic *efx,
6208+
__be16 port)
6209+
{
6210+
struct efx_ef10_nic_data *nic_data = efx->nic_data;
6211+
size_t i;
6212+
6213+
for (i = 0; i < ARRAY_SIZE(nic_data->udp_tunnels); ++i) {
6214+
if (!nic_data->udp_tunnels[i].count)
6215+
continue;
6216+
if (nic_data->udp_tunnels[i].port == port)
6217+
return &nic_data->udp_tunnels[i];
6218+
}
6219+
return NULL;
6220+
}
6221+
6222+
static int efx_ef10_udp_tnl_add_port(struct efx_nic *efx,
6223+
struct efx_udp_tunnel tnl)
6224+
{
6225+
struct efx_ef10_nic_data *nic_data = efx->nic_data;
6226+
struct efx_udp_tunnel *match;
6227+
char typebuf[8];
6228+
size_t i;
6229+
int rc;
6230+
6231+
if (!(nic_data->datapath_caps &
6232+
(1 << MC_CMD_GET_CAPABILITIES_OUT_VXLAN_NVGRE_LBN)))
6233+
return 0;
6234+
6235+
efx_get_udp_tunnel_type_name(tnl.type, typebuf, sizeof(typebuf));
6236+
netif_dbg(efx, drv, efx->net_dev, "Adding UDP tunnel (%s) port %d\n",
6237+
typebuf, ntohs(tnl.port));
6238+
6239+
mutex_lock(&nic_data->udp_tunnels_lock);
6240+
/* Make sure all TX are stopped while we add to the table, else we
6241+
* might race against an efx_features_check().
6242+
*/
6243+
efx_device_detach_sync(efx);
6244+
6245+
match = __efx_ef10_udp_tnl_lookup_port(efx, tnl.port);
6246+
if (match != NULL) {
6247+
if (match->type == tnl.type) {
6248+
netif_dbg(efx, drv, efx->net_dev,
6249+
"Referencing existing tunnel entry\n");
6250+
match->count++;
6251+
/* No need to cause an MCDI update */
6252+
rc = 0;
6253+
goto unlock_out;
6254+
}
6255+
efx_get_udp_tunnel_type_name(match->type,
6256+
typebuf, sizeof(typebuf));
6257+
netif_dbg(efx, drv, efx->net_dev,
6258+
"UDP port %d is already in use by %s\n",
6259+
ntohs(tnl.port), typebuf);
6260+
rc = -EEXIST;
6261+
goto unlock_out;
6262+
}
6263+
6264+
for (i = 0; i < ARRAY_SIZE(nic_data->udp_tunnels); ++i)
6265+
if (!nic_data->udp_tunnels[i].count) {
6266+
nic_data->udp_tunnels[i] = tnl;
6267+
nic_data->udp_tunnels[i].count = 1;
6268+
rc = efx_ef10_set_udp_tnl_ports(efx, false);
6269+
goto unlock_out;
6270+
}
6271+
6272+
netif_dbg(efx, drv, efx->net_dev,
6273+
"Unable to add UDP tunnel (%s) port %d; insufficient resources.\n",
6274+
typebuf, ntohs(tnl.port));
6275+
6276+
rc = -ENOMEM;
6277+
6278+
unlock_out:
6279+
mutex_unlock(&nic_data->udp_tunnels_lock);
6280+
return rc;
6281+
}
6282+
6283+
/* Called under the TX lock with the TX queue running, hence no-one can be
6284+
* in the middle of updating the UDP tunnels table. However, they could
6285+
* have tried and failed the MCDI, in which case they'll have set the dirty
6286+
* flag before dropping their locks.
6287+
*/
6288+
static bool efx_ef10_udp_tnl_has_port(struct efx_nic *efx, __be16 port)
6289+
{
6290+
struct efx_ef10_nic_data *nic_data = efx->nic_data;
6291+
6292+
if (!(nic_data->datapath_caps &
6293+
(1 << MC_CMD_GET_CAPABILITIES_OUT_VXLAN_NVGRE_LBN)))
6294+
return false;
6295+
6296+
if (nic_data->udp_tunnels_dirty)
6297+
/* SW table may not match HW state, so just assume we can't
6298+
* use any UDP tunnel offloads.
6299+
*/
6300+
return false;
6301+
6302+
return __efx_ef10_udp_tnl_lookup_port(efx, port) != NULL;
6303+
}
6304+
6305+
static int efx_ef10_udp_tnl_del_port(struct efx_nic *efx,
6306+
struct efx_udp_tunnel tnl)
6307+
{
6308+
struct efx_ef10_nic_data *nic_data = efx->nic_data;
6309+
struct efx_udp_tunnel *match;
6310+
char typebuf[8];
6311+
int rc;
6312+
6313+
if (!(nic_data->datapath_caps &
6314+
(1 << MC_CMD_GET_CAPABILITIES_OUT_VXLAN_NVGRE_LBN)))
6315+
return 0;
6316+
6317+
efx_get_udp_tunnel_type_name(tnl.type, typebuf, sizeof(typebuf));
6318+
netif_dbg(efx, drv, efx->net_dev, "Removing UDP tunnel (%s) port %d\n",
6319+
typebuf, ntohs(tnl.port));
6320+
6321+
mutex_lock(&nic_data->udp_tunnels_lock);
6322+
/* Make sure all TX are stopped while we remove from the table, else we
6323+
* might race against an efx_features_check().
6324+
*/
6325+
efx_device_detach_sync(efx);
6326+
6327+
match = __efx_ef10_udp_tnl_lookup_port(efx, tnl.port);
6328+
if (match != NULL) {
6329+
if (match->type == tnl.type) {
6330+
if (--match->count) {
6331+
/* Port is still in use, so nothing to do */
6332+
netif_dbg(efx, drv, efx->net_dev,
6333+
"UDP tunnel port %d remains active\n",
6334+
ntohs(tnl.port));
6335+
rc = 0;
6336+
goto out_unlock;
6337+
}
6338+
rc = efx_ef10_set_udp_tnl_ports(efx, false);
6339+
goto out_unlock;
6340+
}
6341+
efx_get_udp_tunnel_type_name(match->type,
6342+
typebuf, sizeof(typebuf));
6343+
netif_warn(efx, drv, efx->net_dev,
6344+
"UDP port %d is actually in use by %s, not removing\n",
6345+
ntohs(tnl.port), typebuf);
6346+
}
6347+
rc = -ENOENT;
6348+
6349+
out_unlock:
6350+
mutex_unlock(&nic_data->udp_tunnels_lock);
6351+
return rc;
6352+
}
6353+
60696354
#define EF10_OFFLOAD_FEATURES \
60706355
(NETIF_F_IP_CSUM | \
60716356
NETIF_F_HW_VLAN_CTAG_FILTER | \
@@ -6269,6 +6554,10 @@ const struct efx_nic_type efx_hunt_a0_nic_type = {
62696554
.ptp_set_ts_config = efx_ef10_ptp_set_ts_config,
62706555
.vlan_rx_add_vid = efx_ef10_vlan_rx_add_vid,
62716556
.vlan_rx_kill_vid = efx_ef10_vlan_rx_kill_vid,
6557+
.udp_tnl_push_ports = efx_ef10_udp_tnl_push_ports,
6558+
.udp_tnl_add_port = efx_ef10_udp_tnl_add_port,
6559+
.udp_tnl_has_port = efx_ef10_udp_tnl_has_port,
6560+
.udp_tnl_del_port = efx_ef10_udp_tnl_del_port,
62726561
#ifdef CONFIG_SFC_SRIOV
62736562
.sriov_configure = efx_ef10_sriov_configure,
62746563
.sriov_init = efx_ef10_sriov_init,

0 commit comments

Comments
 (0)