Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

sfc: de-indirect TSO handling

Remove the tx_queue->handle_tso function pointer, and just use
tx_queue->tso_version to decide which function to call, thus removing
an indirect call from the fast path.
Instead of passing a tso_v2 flag to efx_mcdi_tx_init(), set the desired
tx_queue->tso_version before calling it.
In efx_mcdi_tx_init(), report back failure to obtain a TSOv2 context by
setting tx_queue->tso_version to 0, which will cause the TX path to
use the GSO-based fallback.

Signed-off-by: Edward Cree <ecree@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Edward Cree and committed by
David S. Miller
1679c72c fe0c4060

+46 -37
+14 -21
drivers/net/ethernet/sfc/ef10.c
··· 2175 2175 2176 2176 /* Add Firmware-Assisted TSO v2 option descriptors to a queue. 2177 2177 */ 2178 - static int efx_ef10_tx_tso_desc(struct efx_tx_queue *tx_queue, 2179 - struct sk_buff *skb, 2180 - bool *data_mapped) 2178 + int efx_ef10_tx_tso_desc(struct efx_tx_queue *tx_queue, struct sk_buff *skb, 2179 + bool *data_mapped) 2181 2180 { 2182 2181 struct efx_tx_buffer *buffer; 2183 2182 struct tcphdr *tcp; ··· 2265 2266 struct efx_channel *channel = tx_queue->channel; 2266 2267 struct efx_nic *efx = tx_queue->efx; 2267 2268 struct efx_ef10_nic_data *nic_data; 2268 - bool tso_v2 = false; 2269 2269 efx_qword_t *txd; 2270 2270 int rc; 2271 2271 ··· 2287 2289 * TSOv2 cannot be used with Hardware timestamping, and is never needed 2288 2290 * for XDP tx. 2289 2291 */ 2290 - if ((csum_offload || inner_csum) && (nic_data->datapath_caps2 & 2291 - (1 << MC_CMD_GET_CAPABILITIES_V2_OUT_TX_TSO_V2_LBN)) && 2292 - !tx_queue->timestamping && !tx_queue->xdp_tx) { 2293 - tso_v2 = true; 2294 - netif_dbg(efx, hw, efx->net_dev, "Using TSOv2 for channel %u\n", 2295 - channel->channel); 2292 + if (efx_has_cap(efx, TX_TSO_V2)) { 2293 + if ((csum_offload || inner_csum) && 2294 + !tx_queue->timestamping && !tx_queue->xdp_tx) { 2295 + tx_queue->tso_version = 2; 2296 + netif_dbg(efx, hw, efx->net_dev, "Using TSOv2 for channel %u\n", 2297 + channel->channel); 2298 + } 2299 + } else if (efx_has_cap(efx, TX_TSO)) { 2300 + tx_queue->tso_version = 1; 2296 2301 } 2297 2302 2298 - rc = efx_mcdi_tx_init(tx_queue, tso_v2); 2303 + rc = efx_mcdi_tx_init(tx_queue); 2299 2304 if (rc) 2300 2305 goto fail; 2301 2306 ··· 2316 2315 ESF_DZ_TX_OPTION_TYPE, 2317 2316 ESE_DZ_TX_OPTION_DESC_CRC_CSUM, 2318 2317 ESF_DZ_TX_OPTION_UDP_TCP_CSUM, csum_offload, 2319 - ESF_DZ_TX_OPTION_IP_CSUM, csum_offload && !tso_v2, 2318 + ESF_DZ_TX_OPTION_IP_CSUM, csum_offload && tx_queue->tso_version != 2, 2320 2319 ESF_DZ_TX_OPTION_INNER_UDP_TCP_CSUM, inner_csum, 2321 - ESF_DZ_TX_OPTION_INNER_IP_CSUM, inner_csum && !tso_v2, 2320 + ESF_DZ_TX_OPTION_INNER_IP_CSUM, inner_csum && tx_queue->tso_version != 2, 2322 2321 ESF_DZ_TX_TIMESTAMP, tx_queue->timestamping); 2323 2322 tx_queue->write_count = 1; 2324 - 2325 - if (tso_v2) { 2326 - tx_queue->handle_tso = efx_ef10_tx_tso_desc; 2327 - tx_queue->tso_version = 2; 2328 - } else if (nic_data->datapath_caps & 2329 - (1 << MC_CMD_GET_CAPABILITIES_OUT_TX_TSO_LBN)) { 2330 - tx_queue->tso_version = 1; 2331 - } 2332 2323 2333 2324 wmb(); 2334 2325 efx_ef10_push_tx_desc(tx_queue, txd);
+8 -1
drivers/net/ethernet/sfc/ef100_tx.c
··· 37 37 tx_queue->channel->channel - 38 38 tx_queue->efx->tx_channel_offset); 39 39 40 - if (efx_mcdi_tx_init(tx_queue, false)) 40 + /* This value is purely documentational; as EF100 never passes through 41 + * the switch statement in tx.c:__efx_enqueue_skb(), that switch does 42 + * not handle case 3. EF100's TSOv3 descriptors are generated by 43 + * ef100_make_tso_desc(). 44 + * Meanwhile, all efx_mcdi_tx_init() cares about is that it's not 2. 45 + */ 46 + tx_queue->tso_version = 3; 47 + if (efx_mcdi_tx_init(tx_queue)) 41 48 netdev_WARN(tx_queue->efx->net_dev, 42 49 "failed to initialise TXQ %d\n", tx_queue->queue); 43 50 }
+2
drivers/net/ethernet/sfc/farch.c
··· 415 415 FFE_BZ_TX_PACE_OFF : 416 416 FFE_BZ_TX_PACE_RESERVED); 417 417 efx_writeo_table(efx, &reg, FR_BZ_TX_PACE_TBL, tx_queue->queue); 418 + 419 + tx_queue->tso_version = 1; 418 420 } 419 421 420 422 static void efx_farch_flush_tx_queue(struct efx_tx_queue *tx_queue)
+4 -2
drivers/net/ethernet/sfc/mcdi_functions.c
··· 160 160 outbuf, outlen, rc); 161 161 } 162 162 163 - int efx_mcdi_tx_init(struct efx_tx_queue *tx_queue, bool tso_v2) 163 + int efx_mcdi_tx_init(struct efx_tx_queue *tx_queue) 164 164 { 165 165 MCDI_DECLARE_BUF(inbuf, MC_CMD_INIT_TXQ_IN_LEN(EFX_MAX_DMAQ_SIZE * 8 / 166 166 EFX_BUF_SIZE)); ··· 195 195 inlen = MC_CMD_INIT_TXQ_IN_LEN(entries); 196 196 197 197 do { 198 + bool tso_v2 = tx_queue->tso_version == 2; 199 + 198 200 /* TSOv2 implies IP header checksum offload for TSO frames, 199 201 * so we can safely disable IP header checksum offload for 200 202 * everything else. If we don't have TSOv2, then we have to ··· 219 217 NULL, 0, NULL); 220 218 if (rc == -ENOSPC && tso_v2) { 221 219 /* Retry without TSOv2 if we're short on contexts. */ 222 - tso_v2 = false; 220 + tx_queue->tso_version = 0; 223 221 netif_warn(efx, probe, efx->net_dev, 224 222 "TSOv2 context not available to segment in " 225 223 "hardware. TCP performance may be reduced.\n"
+1 -1
drivers/net/ethernet/sfc/mcdi_functions.h
··· 19 19 int efx_mcdi_ev_init(struct efx_channel *channel, bool v1_cut_thru, bool v2); 20 20 void efx_mcdi_ev_remove(struct efx_channel *channel); 21 21 void efx_mcdi_ev_fini(struct efx_channel *channel); 22 - int efx_mcdi_tx_init(struct efx_tx_queue *tx_queue, bool tso_v2); 22 + int efx_mcdi_tx_init(struct efx_tx_queue *tx_queue); 23 23 void efx_mcdi_tx_remove(struct efx_tx_queue *tx_queue); 24 24 void efx_mcdi_tx_fini(struct efx_tx_queue *tx_queue); 25 25 int efx_mcdi_rx_probe(struct efx_rx_queue *rx_queue);
-5
drivers/net/ethernet/sfc/net_driver.h
··· 208 208 * @initialised: Has hardware queue been initialised? 209 209 * @timestamping: Is timestamping enabled for this channel? 210 210 * @xdp_tx: Is this an XDP tx queue? 211 - * @handle_tso: TSO xmit preparation handler. Sets up the TSO metadata and 212 - * may also map tx data, depending on the nature of the TSO implementation. 213 211 * @read_count: Current read pointer. 214 212 * This is the number of buffers that have been removed from both rings. 215 213 * @old_write_count: The value of @write_count when last checked. ··· 269 271 bool initialised; 270 272 bool timestamping; 271 273 bool xdp_tx; 272 - 273 - /* Function pointers used in the fast path. */ 274 - int (*handle_tso)(struct efx_tx_queue*, struct sk_buff*, bool *); 275 274 276 275 /* Members used mainly on the completion path */ 277 276 unsigned int read_count ____cacheline_aligned_in_smp;
+4
drivers/net/ethernet/sfc/nic.h
··· 297 297 u64 licensed_features; 298 298 }; 299 299 300 + /* TSOv2 */ 301 + int efx_ef10_tx_tso_desc(struct efx_tx_queue *tx_queue, struct sk_buff *skb, 302 + bool *data_mapped); 303 + 300 304 int efx_init_sriov(void); 301 305 void efx_fini_sriov(void); 302 306
+12 -2
drivers/net/ethernet/sfc/tx.c
··· 338 338 * size limit. 339 339 */ 340 340 if (segments) { 341 - EFX_WARN_ON_ONCE_PARANOID(!tx_queue->handle_tso); 342 - rc = tx_queue->handle_tso(tx_queue, skb, &data_mapped); 341 + switch (tx_queue->tso_version) { 342 + case 1: 343 + rc = efx_enqueue_skb_tso(tx_queue, skb, &data_mapped); 344 + break; 345 + case 2: 346 + rc = efx_ef10_tx_tso_desc(tx_queue, skb, &data_mapped); 347 + break; 348 + case 0: /* No TSO on this queue, SW fallback needed */ 349 + default: 350 + rc = -EINVAL; 351 + break; 352 + } 343 353 if (rc == -EINVAL) { 344 354 rc = efx_tx_tso_fallback(tx_queue, skb); 345 355 tx_queue->tso_fallbacks++;
+1 -5
drivers/net/ethernet/sfc/tx_common.c
··· 86 86 tx_queue->completed_timestamp_minor = 0; 87 87 88 88 tx_queue->xdp_tx = efx_channel_is_xdp_tx(tx_queue->channel); 89 - 90 - /* Set up default function pointers. These may get replaced by 91 - * efx_nic_init_tx() based off NIC/queue capabilities. 92 - */ 93 - tx_queue->handle_tso = efx_enqueue_skb_tso; 89 + tx_queue->tso_version = 0; 94 90 95 91 /* Set up TX descriptor ring */ 96 92 efx_nic_init_tx(tx_queue);