2020-01-08 16:14:00 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
/****************************************************************************
|
|
|
|
* Driver for Solarflare network controllers and boards
|
|
|
|
* Copyright 2018 Solarflare Communications Inc.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License version 2 as published
|
|
|
|
* by the Free Software Foundation, incorporated herein by reference.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "net_driver.h"
|
|
|
|
#include "efx.h"
|
2020-06-30 13:12:49 +01:00
|
|
|
#include "nic_common.h"
|
2020-01-08 16:14:00 +00:00
|
|
|
#include "tx_common.h"
|
2023-06-08 19:17:37 +00:00
|
|
|
#include <net/gso.h>
|
2020-01-08 16:14:00 +00:00
|
|
|
|
|
|
|
static unsigned int efx_tx_cb_page_count(struct efx_tx_queue *tx_queue)
|
|
|
|
{
|
|
|
|
return DIV_ROUND_UP(tx_queue->ptr_mask + 1,
|
|
|
|
PAGE_SIZE >> EFX_TX_CB_ORDER);
|
|
|
|
}
|
|
|
|
|
|
|
|
int efx_probe_tx_queue(struct efx_tx_queue *tx_queue)
|
|
|
|
{
|
|
|
|
struct efx_nic *efx = tx_queue->efx;
|
|
|
|
unsigned int entries;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
/* Create the smallest power-of-two aligned ring */
|
|
|
|
entries = max(roundup_pow_of_two(efx->txq_entries), EFX_MIN_DMAQ_SIZE);
|
|
|
|
EFX_WARN_ON_PARANOID(entries > EFX_MAX_DMAQ_SIZE);
|
|
|
|
tx_queue->ptr_mask = entries - 1;
|
|
|
|
|
|
|
|
netif_dbg(efx, probe, efx->net_dev,
|
|
|
|
"creating TX queue %d size %#x mask %#x\n",
|
|
|
|
tx_queue->queue, efx->txq_entries, tx_queue->ptr_mask);
|
|
|
|
|
|
|
|
/* Allocate software ring */
|
|
|
|
tx_queue->buffer = kcalloc(entries, sizeof(*tx_queue->buffer),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!tx_queue->buffer)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
tx_queue->cb_page = kcalloc(efx_tx_cb_page_count(tx_queue),
|
|
|
|
sizeof(tx_queue->cb_page[0]), GFP_KERNEL);
|
|
|
|
if (!tx_queue->cb_page) {
|
|
|
|
rc = -ENOMEM;
|
|
|
|
goto fail1;
|
|
|
|
}
|
|
|
|
|
2020-09-11 23:39:02 +01:00
|
|
|
/* Allocate hardware ring, determine TXQ type */
|
2020-01-08 16:14:00 +00:00
|
|
|
rc = efx_nic_probe_tx(tx_queue);
|
|
|
|
if (rc)
|
|
|
|
goto fail2;
|
|
|
|
|
2020-09-11 23:39:02 +01:00
|
|
|
tx_queue->channel->tx_queue_by_type[tx_queue->type] = tx_queue;
|
2020-01-08 16:14:00 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail2:
|
|
|
|
kfree(tx_queue->cb_page);
|
|
|
|
tx_queue->cb_page = NULL;
|
|
|
|
fail1:
|
|
|
|
kfree(tx_queue->buffer);
|
|
|
|
tx_queue->buffer = NULL;
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void efx_init_tx_queue(struct efx_tx_queue *tx_queue)
|
|
|
|
{
|
|
|
|
struct efx_nic *efx = tx_queue->efx;
|
|
|
|
|
|
|
|
netif_dbg(efx, drv, efx->net_dev,
|
|
|
|
"initialising TX queue %d\n", tx_queue->queue);
|
|
|
|
|
|
|
|
tx_queue->insert_count = 0;
|
2020-08-03 21:34:00 +01:00
|
|
|
tx_queue->notify_count = 0;
|
2020-01-08 16:14:00 +00:00
|
|
|
tx_queue->write_count = 0;
|
|
|
|
tx_queue->packet_write_count = 0;
|
|
|
|
tx_queue->old_write_count = 0;
|
|
|
|
tx_queue->read_count = 0;
|
|
|
|
tx_queue->old_read_count = 0;
|
|
|
|
tx_queue->empty_read_count = 0 | EFX_EMPTY_COUNT_VALID;
|
2020-09-03 22:34:15 +01:00
|
|
|
tx_queue->xmit_pending = false;
|
2020-01-08 16:14:00 +00:00
|
|
|
tx_queue->timestamping = (efx_ptp_use_mac_tx_timestamps(efx) &&
|
|
|
|
tx_queue->channel == efx_ptp_channel(efx));
|
|
|
|
tx_queue->completed_timestamp_major = 0;
|
|
|
|
tx_queue->completed_timestamp_minor = 0;
|
|
|
|
|
|
|
|
tx_queue->xdp_tx = efx_channel_is_xdp_tx(tx_queue->channel);
|
2020-09-11 23:40:03 +01:00
|
|
|
tx_queue->tso_version = 0;
|
2020-01-08 16:14:00 +00:00
|
|
|
|
|
|
|
/* Set up TX descriptor ring */
|
|
|
|
efx_nic_init_tx(tx_queue);
|
|
|
|
|
|
|
|
tx_queue->initialised = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void efx_fini_tx_queue(struct efx_tx_queue *tx_queue)
|
|
|
|
{
|
|
|
|
struct efx_tx_buffer *buffer;
|
|
|
|
|
|
|
|
netif_dbg(tx_queue->efx, drv, tx_queue->efx->net_dev,
|
|
|
|
"shutting down TX queue %d\n", tx_queue->queue);
|
|
|
|
|
2022-04-05 08:45:44 +00:00
|
|
|
tx_queue->initialised = false;
|
|
|
|
|
2020-01-08 16:14:00 +00:00
|
|
|
if (!tx_queue->buffer)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Free any buffers left in the ring */
|
|
|
|
while (tx_queue->read_count != tx_queue->write_count) {
|
|
|
|
unsigned int pkts_compl = 0, bytes_compl = 0;
|
2022-07-20 19:33:47 +01:00
|
|
|
unsigned int efv_pkts_compl = 0;
|
2020-01-08 16:14:00 +00:00
|
|
|
|
|
|
|
buffer = &tx_queue->buffer[tx_queue->read_count & tx_queue->ptr_mask];
|
2022-07-20 19:33:47 +01:00
|
|
|
efx_dequeue_buffer(tx_queue, buffer, &pkts_compl, &bytes_compl,
|
|
|
|
&efv_pkts_compl);
|
2020-01-08 16:14:00 +00:00
|
|
|
|
|
|
|
++tx_queue->read_count;
|
|
|
|
}
|
2020-09-03 22:34:15 +01:00
|
|
|
tx_queue->xmit_pending = false;
|
2020-01-08 16:14:00 +00:00
|
|
|
netdev_tx_reset_queue(tx_queue->core_txq);
|
|
|
|
}
|
|
|
|
|
|
|
|
void efx_remove_tx_queue(struct efx_tx_queue *tx_queue)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!tx_queue->buffer)
|
|
|
|
return;
|
|
|
|
|
|
|
|
netif_dbg(tx_queue->efx, drv, tx_queue->efx->net_dev,
|
|
|
|
"destroying TX queue %d\n", tx_queue->queue);
|
|
|
|
efx_nic_remove_tx(tx_queue);
|
|
|
|
|
|
|
|
if (tx_queue->cb_page) {
|
|
|
|
for (i = 0; i < efx_tx_cb_page_count(tx_queue); i++)
|
|
|
|
efx_nic_free_buffer(tx_queue->efx,
|
|
|
|
&tx_queue->cb_page[i]);
|
|
|
|
kfree(tx_queue->cb_page);
|
|
|
|
tx_queue->cb_page = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
kfree(tx_queue->buffer);
|
|
|
|
tx_queue->buffer = NULL;
|
2020-09-11 23:39:02 +01:00
|
|
|
tx_queue->channel->tx_queue_by_type[tx_queue->type] = NULL;
|
2020-01-08 16:14:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void efx_dequeue_buffer(struct efx_tx_queue *tx_queue,
|
|
|
|
struct efx_tx_buffer *buffer,
|
|
|
|
unsigned int *pkts_compl,
|
2022-07-20 19:33:47 +01:00
|
|
|
unsigned int *bytes_compl,
|
|
|
|
unsigned int *efv_pkts_compl)
|
2020-01-08 16:14:00 +00:00
|
|
|
{
|
|
|
|
if (buffer->unmap_len) {
|
|
|
|
struct device *dma_dev = &tx_queue->efx->pci_dev->dev;
|
|
|
|
dma_addr_t unmap_addr = buffer->dma_addr - buffer->dma_offset;
|
|
|
|
|
|
|
|
if (buffer->flags & EFX_TX_BUF_MAP_SINGLE)
|
|
|
|
dma_unmap_single(dma_dev, unmap_addr, buffer->unmap_len,
|
|
|
|
DMA_TO_DEVICE);
|
|
|
|
else
|
|
|
|
dma_unmap_page(dma_dev, unmap_addr, buffer->unmap_len,
|
|
|
|
DMA_TO_DEVICE);
|
|
|
|
buffer->unmap_len = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buffer->flags & EFX_TX_BUF_SKB) {
|
|
|
|
struct sk_buff *skb = (struct sk_buff *)buffer->skb;
|
|
|
|
|
2022-07-20 19:33:47 +01:00
|
|
|
if (unlikely(buffer->flags & EFX_TX_BUF_EFV)) {
|
|
|
|
EFX_WARN_ON_PARANOID(!efv_pkts_compl);
|
|
|
|
(*efv_pkts_compl)++;
|
|
|
|
} else {
|
|
|
|
EFX_WARN_ON_PARANOID(!pkts_compl || !bytes_compl);
|
|
|
|
(*pkts_compl)++;
|
|
|
|
(*bytes_compl) += skb->len;
|
|
|
|
}
|
|
|
|
|
2020-01-08 16:14:00 +00:00
|
|
|
if (tx_queue->timestamping &&
|
|
|
|
(tx_queue->completed_timestamp_major ||
|
|
|
|
tx_queue->completed_timestamp_minor)) {
|
|
|
|
struct skb_shared_hwtstamps hwtstamp;
|
|
|
|
|
|
|
|
hwtstamp.hwtstamp =
|
|
|
|
efx_ptp_nic_to_kernel_time(tx_queue);
|
|
|
|
skb_tstamp_tx(skb, &hwtstamp);
|
|
|
|
|
|
|
|
tx_queue->completed_timestamp_major = 0;
|
|
|
|
tx_queue->completed_timestamp_minor = 0;
|
|
|
|
}
|
|
|
|
dev_consume_skb_any((struct sk_buff *)buffer->skb);
|
|
|
|
netif_vdbg(tx_queue->efx, tx_done, tx_queue->efx->net_dev,
|
|
|
|
"TX queue %d transmission id %x complete\n",
|
|
|
|
tx_queue->queue, tx_queue->read_count);
|
|
|
|
} else if (buffer->flags & EFX_TX_BUF_XDP) {
|
|
|
|
xdp_return_frame_rx_napi(buffer->xdpf);
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer->len = 0;
|
|
|
|
buffer->flags = 0;
|
|
|
|
}
|
|
|
|
|
2020-01-10 13:26:46 +00:00
|
|
|
/* Remove packets from the TX queue
|
|
|
|
*
|
|
|
|
* This removes packets from the TX queue, up to and including the
|
|
|
|
* specified index.
|
|
|
|
*/
|
|
|
|
static void efx_dequeue_buffers(struct efx_tx_queue *tx_queue,
|
|
|
|
unsigned int index,
|
|
|
|
unsigned int *pkts_compl,
|
2022-07-20 19:33:47 +01:00
|
|
|
unsigned int *bytes_compl,
|
|
|
|
unsigned int *efv_pkts_compl)
|
2020-01-10 13:26:46 +00:00
|
|
|
{
|
|
|
|
struct efx_nic *efx = tx_queue->efx;
|
|
|
|
unsigned int stop_index, read_ptr;
|
|
|
|
|
|
|
|
stop_index = (index + 1) & tx_queue->ptr_mask;
|
|
|
|
read_ptr = tx_queue->read_count & tx_queue->ptr_mask;
|
|
|
|
|
|
|
|
while (read_ptr != stop_index) {
|
|
|
|
struct efx_tx_buffer *buffer = &tx_queue->buffer[read_ptr];
|
|
|
|
|
2020-03-05 11:38:45 +00:00
|
|
|
if (!efx_tx_buffer_in_use(buffer)) {
|
2020-01-10 13:26:46 +00:00
|
|
|
netif_err(efx, tx_err, efx->net_dev,
|
2020-03-05 11:38:45 +00:00
|
|
|
"TX queue %d spurious TX completion id %d\n",
|
2020-01-10 13:26:46 +00:00
|
|
|
tx_queue->queue, read_ptr);
|
|
|
|
efx_schedule_reset(efx, RESET_TYPE_TX_SKIP);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-07-20 19:33:47 +01:00
|
|
|
efx_dequeue_buffer(tx_queue, buffer, pkts_compl, bytes_compl,
|
|
|
|
efv_pkts_compl);
|
2020-01-10 13:26:46 +00:00
|
|
|
|
|
|
|
++tx_queue->read_count;
|
|
|
|
read_ptr = tx_queue->read_count & tx_queue->ptr_mask;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-05 11:38:45 +00:00
|
|
|
void efx_xmit_done_check_empty(struct efx_tx_queue *tx_queue)
|
|
|
|
{
|
|
|
|
if ((int)(tx_queue->read_count - tx_queue->old_write_count) >= 0) {
|
|
|
|
tx_queue->old_write_count = READ_ONCE(tx_queue->write_count);
|
|
|
|
if (tx_queue->read_count == tx_queue->old_write_count) {
|
|
|
|
/* Ensure that read_count is flushed. */
|
|
|
|
smp_mb();
|
|
|
|
tx_queue->empty_read_count =
|
|
|
|
tx_queue->read_count | EFX_EMPTY_COUNT_VALID;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
sfc: use budget for TX completions
When running workloads heavy unbalanced towards TX (high TX, low RX
traffic), sfc driver can retain the CPU during too long times. Although
in many cases this is not enough to be visible, it can affect
performance and system responsiveness.
A way to reproduce it is to use a debug kernel and run some parallel
netperf TX tests. In some systems, this will lead to this message being
logged:
kernel:watchdog: BUG: soft lockup - CPU#12 stuck for 22s!
The reason is that sfc driver doesn't account any NAPI budget for the TX
completion events work. With high-TX/low-RX traffic, this makes that the
CPU is held for long time for NAPI poll.
Documentations says "drivers can process completions for any number of Tx
packets but should only process up to budget number of Rx packets".
However, many drivers do limit the amount of TX completions that they
process in a single NAPI poll.
In the same way, this patch adds a limit for the TX work in sfc. With
the patch applied, the watchdog warning never appears.
Tested with netperf in different combinations: single process / parallel
processes, TCP / UDP and different sizes of UDP messages. Repeated the
tests before and after the patch, without any noticeable difference in
network or CPU performance.
Test hardware:
Intel(R) Xeon(R) CPU E5-1620 v4 @ 3.50GHz (4 cores, 2 threads/core)
Solarflare Communications XtremeScale X2522-25G Network Adapter
Fixes: 5227ecccea2d ("sfc: remove tx and MCDI handling from NAPI budget consideration")
Fixes: d19a53721863 ("sfc_ef100: TX path for EF100 NICs")
Reported-by: Fei Liu <feliu@redhat.com>
Signed-off-by: Íñigo Huguet <ihuguet@redhat.com>
Acked-by: Martin Habets <habetsm.xilinx@gmail.com>
Link: https://lore.kernel.org/r/20230615084929.10506-1-ihuguet@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2023-06-15 10:49:29 +02:00
|
|
|
int efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index)
|
2020-01-10 13:26:46 +00:00
|
|
|
{
|
|
|
|
unsigned int fill_level, pkts_compl = 0, bytes_compl = 0;
|
2022-07-20 19:33:47 +01:00
|
|
|
unsigned int efv_pkts_compl = 0;
|
2020-01-10 13:26:46 +00:00
|
|
|
struct efx_nic *efx = tx_queue->efx;
|
|
|
|
|
|
|
|
EFX_WARN_ON_ONCE_PARANOID(index > tx_queue->ptr_mask);
|
|
|
|
|
2022-07-20 19:33:47 +01:00
|
|
|
efx_dequeue_buffers(tx_queue, index, &pkts_compl, &bytes_compl,
|
|
|
|
&efv_pkts_compl);
|
2020-01-10 13:26:46 +00:00
|
|
|
tx_queue->pkts_compl += pkts_compl;
|
|
|
|
tx_queue->bytes_compl += bytes_compl;
|
|
|
|
|
2022-07-20 19:33:47 +01:00
|
|
|
if (pkts_compl + efv_pkts_compl > 1)
|
2020-01-10 13:26:46 +00:00
|
|
|
++tx_queue->merge_events;
|
|
|
|
|
|
|
|
/* See if we need to restart the netif queue. This memory
|
|
|
|
* barrier ensures that we write read_count (inside
|
|
|
|
* efx_dequeue_buffers()) before reading the queue status.
|
|
|
|
*/
|
|
|
|
smp_mb();
|
|
|
|
if (unlikely(netif_tx_queue_stopped(tx_queue->core_txq)) &&
|
|
|
|
likely(efx->port_enabled) &&
|
|
|
|
likely(netif_device_present(efx->net_dev))) {
|
2020-09-03 22:35:13 +01:00
|
|
|
fill_level = efx_channel_tx_fill_level(tx_queue->channel);
|
2020-01-10 13:26:46 +00:00
|
|
|
if (fill_level <= efx->txq_wake_thresh)
|
|
|
|
netif_tx_wake_queue(tx_queue->core_txq);
|
|
|
|
}
|
|
|
|
|
2020-03-05 11:38:45 +00:00
|
|
|
efx_xmit_done_check_empty(tx_queue);
|
sfc: use budget for TX completions
When running workloads heavy unbalanced towards TX (high TX, low RX
traffic), sfc driver can retain the CPU during too long times. Although
in many cases this is not enough to be visible, it can affect
performance and system responsiveness.
A way to reproduce it is to use a debug kernel and run some parallel
netperf TX tests. In some systems, this will lead to this message being
logged:
kernel:watchdog: BUG: soft lockup - CPU#12 stuck for 22s!
The reason is that sfc driver doesn't account any NAPI budget for the TX
completion events work. With high-TX/low-RX traffic, this makes that the
CPU is held for long time for NAPI poll.
Documentations says "drivers can process completions for any number of Tx
packets but should only process up to budget number of Rx packets".
However, many drivers do limit the amount of TX completions that they
process in a single NAPI poll.
In the same way, this patch adds a limit for the TX work in sfc. With
the patch applied, the watchdog warning never appears.
Tested with netperf in different combinations: single process / parallel
processes, TCP / UDP and different sizes of UDP messages. Repeated the
tests before and after the patch, without any noticeable difference in
network or CPU performance.
Test hardware:
Intel(R) Xeon(R) CPU E5-1620 v4 @ 3.50GHz (4 cores, 2 threads/core)
Solarflare Communications XtremeScale X2522-25G Network Adapter
Fixes: 5227ecccea2d ("sfc: remove tx and MCDI handling from NAPI budget consideration")
Fixes: d19a53721863 ("sfc_ef100: TX path for EF100 NICs")
Reported-by: Fei Liu <feliu@redhat.com>
Signed-off-by: Íñigo Huguet <ihuguet@redhat.com>
Acked-by: Martin Habets <habetsm.xilinx@gmail.com>
Link: https://lore.kernel.org/r/20230615084929.10506-1-ihuguet@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2023-06-15 10:49:29 +02:00
|
|
|
|
|
|
|
return pkts_compl + efv_pkts_compl;
|
2020-01-10 13:26:46 +00:00
|
|
|
}
|
|
|
|
|
2020-01-10 13:27:50 +00:00
|
|
|
/* Remove buffers put into a tx_queue for the current packet.
|
|
|
|
* None of the buffers must have an skb attached.
|
|
|
|
*/
|
|
|
|
void efx_enqueue_unwind(struct efx_tx_queue *tx_queue,
|
|
|
|
unsigned int insert_count)
|
|
|
|
{
|
2022-07-20 19:33:47 +01:00
|
|
|
unsigned int efv_pkts_compl = 0;
|
2020-01-10 13:27:50 +00:00
|
|
|
struct efx_tx_buffer *buffer;
|
|
|
|
unsigned int bytes_compl = 0;
|
|
|
|
unsigned int pkts_compl = 0;
|
|
|
|
|
|
|
|
/* Work backwards until we hit the original insert pointer value */
|
|
|
|
while (tx_queue->insert_count != insert_count) {
|
|
|
|
--tx_queue->insert_count;
|
|
|
|
buffer = __efx_tx_queue_get_insert_buffer(tx_queue);
|
2022-07-20 19:33:47 +01:00
|
|
|
efx_dequeue_buffer(tx_queue, buffer, &pkts_compl, &bytes_compl,
|
|
|
|
&efv_pkts_compl);
|
2020-01-10 13:27:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-08 16:14:00 +00:00
|
|
|
struct efx_tx_buffer *efx_tx_map_chunk(struct efx_tx_queue *tx_queue,
|
|
|
|
dma_addr_t dma_addr, size_t len)
|
|
|
|
{
|
|
|
|
const struct efx_nic_type *nic_type = tx_queue->efx->type;
|
|
|
|
struct efx_tx_buffer *buffer;
|
|
|
|
unsigned int dma_len;
|
|
|
|
|
|
|
|
/* Map the fragment taking account of NIC-dependent DMA limits. */
|
|
|
|
do {
|
|
|
|
buffer = efx_tx_queue_get_insert_buffer(tx_queue);
|
2020-07-02 17:30:23 +01:00
|
|
|
|
|
|
|
if (nic_type->tx_limit_len)
|
|
|
|
dma_len = nic_type->tx_limit_len(tx_queue, dma_addr, len);
|
|
|
|
else
|
|
|
|
dma_len = len;
|
2020-01-08 16:14:00 +00:00
|
|
|
|
|
|
|
buffer->len = dma_len;
|
|
|
|
buffer->dma_addr = dma_addr;
|
|
|
|
buffer->flags = EFX_TX_BUF_CONT;
|
|
|
|
len -= dma_len;
|
|
|
|
dma_addr += dma_len;
|
|
|
|
++tx_queue->insert_count;
|
|
|
|
} while (len);
|
|
|
|
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
2020-06-30 13:13:15 +01:00
|
|
|
int efx_tx_tso_header_length(struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
size_t header_len;
|
|
|
|
|
|
|
|
if (skb->encapsulation)
|
2024-02-29 09:39:07 +00:00
|
|
|
header_len = skb_inner_transport_offset(skb) +
|
2020-06-30 13:13:15 +01:00
|
|
|
(inner_tcp_hdr(skb)->doff << 2u);
|
|
|
|
else
|
2024-02-29 09:39:07 +00:00
|
|
|
header_len = skb_transport_offset(skb) +
|
2020-06-30 13:13:15 +01:00
|
|
|
(tcp_hdr(skb)->doff << 2u);
|
|
|
|
return header_len;
|
|
|
|
}
|
|
|
|
|
2020-01-08 16:14:00 +00:00
|
|
|
/* Map all data from an SKB for DMA and create descriptors on the queue. */
|
|
|
|
int efx_tx_map_data(struct efx_tx_queue *tx_queue, struct sk_buff *skb,
|
|
|
|
unsigned int segment_count)
|
|
|
|
{
|
|
|
|
struct efx_nic *efx = tx_queue->efx;
|
|
|
|
struct device *dma_dev = &efx->pci_dev->dev;
|
|
|
|
unsigned int frag_index, nr_frags;
|
|
|
|
dma_addr_t dma_addr, unmap_addr;
|
|
|
|
unsigned short dma_flags;
|
|
|
|
size_t len, unmap_len;
|
|
|
|
|
|
|
|
nr_frags = skb_shinfo(skb)->nr_frags;
|
|
|
|
frag_index = 0;
|
|
|
|
|
|
|
|
/* Map header data. */
|
|
|
|
len = skb_headlen(skb);
|
|
|
|
dma_addr = dma_map_single(dma_dev, skb->data, len, DMA_TO_DEVICE);
|
|
|
|
dma_flags = EFX_TX_BUF_MAP_SINGLE;
|
|
|
|
unmap_len = len;
|
|
|
|
unmap_addr = dma_addr;
|
|
|
|
|
|
|
|
if (unlikely(dma_mapping_error(dma_dev, dma_addr)))
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
if (segment_count) {
|
|
|
|
/* For TSO we need to put the header in to a separate
|
|
|
|
* descriptor. Map this separately if necessary.
|
|
|
|
*/
|
2020-06-30 13:13:15 +01:00
|
|
|
size_t header_len = efx_tx_tso_header_length(skb);
|
2020-01-08 16:14:00 +00:00
|
|
|
|
|
|
|
if (header_len != len) {
|
|
|
|
tx_queue->tso_long_headers++;
|
|
|
|
efx_tx_map_chunk(tx_queue, dma_addr, header_len);
|
|
|
|
len -= header_len;
|
|
|
|
dma_addr += header_len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add descriptors for each fragment. */
|
|
|
|
do {
|
|
|
|
struct efx_tx_buffer *buffer;
|
|
|
|
skb_frag_t *fragment;
|
|
|
|
|
|
|
|
buffer = efx_tx_map_chunk(tx_queue, dma_addr, len);
|
|
|
|
|
|
|
|
/* The final descriptor for a fragment is responsible for
|
|
|
|
* unmapping the whole fragment.
|
|
|
|
*/
|
|
|
|
buffer->flags = EFX_TX_BUF_CONT | dma_flags;
|
|
|
|
buffer->unmap_len = unmap_len;
|
|
|
|
buffer->dma_offset = buffer->dma_addr - unmap_addr;
|
|
|
|
|
|
|
|
if (frag_index >= nr_frags) {
|
|
|
|
/* Store SKB details with the final buffer for
|
|
|
|
* the completion.
|
|
|
|
*/
|
|
|
|
buffer->skb = skb;
|
|
|
|
buffer->flags = EFX_TX_BUF_SKB | dma_flags;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Move on to the next fragment. */
|
|
|
|
fragment = &skb_shinfo(skb)->frags[frag_index++];
|
|
|
|
len = skb_frag_size(fragment);
|
|
|
|
dma_addr = skb_frag_dma_map(dma_dev, fragment, 0, len,
|
|
|
|
DMA_TO_DEVICE);
|
|
|
|
dma_flags = 0;
|
|
|
|
unmap_len = len;
|
|
|
|
unmap_addr = dma_addr;
|
|
|
|
|
|
|
|
if (unlikely(dma_mapping_error(dma_dev, dma_addr)))
|
|
|
|
return -EIO;
|
|
|
|
} while (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int efx_tx_max_skb_descs(struct efx_nic *efx)
|
|
|
|
{
|
|
|
|
/* Header and payload descriptor for each output segment, plus
|
|
|
|
* one for every input fragment boundary within a segment
|
|
|
|
*/
|
|
|
|
unsigned int max_descs = EFX_TSO_MAX_SEGS * 2 + MAX_SKB_FRAGS;
|
|
|
|
|
|
|
|
/* Possibly one more per segment for option descriptors */
|
|
|
|
if (efx_nic_rev(efx) >= EFX_REV_HUNT_A0)
|
|
|
|
max_descs += EFX_TSO_MAX_SEGS;
|
|
|
|
|
|
|
|
/* Possibly more for PCIe page boundaries within input fragments */
|
|
|
|
if (PAGE_SIZE > EFX_PAGE_SIZE)
|
|
|
|
max_descs += max_t(unsigned int, MAX_SKB_FRAGS,
|
2022-05-13 11:33:57 -07:00
|
|
|
DIV_ROUND_UP(GSO_LEGACY_MAX_SIZE,
|
|
|
|
EFX_PAGE_SIZE));
|
2020-01-08 16:14:00 +00:00
|
|
|
|
|
|
|
return max_descs;
|
|
|
|
}
|
2020-06-30 13:12:17 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Fallback to software TSO.
|
|
|
|
*
|
|
|
|
* This is used if we are unable to send a GSO packet through hardware TSO.
|
|
|
|
* This should only ever happen due to per-queue restrictions - unsupported
|
|
|
|
* packets should first be filtered by the feature flags.
|
|
|
|
*
|
|
|
|
* Returns 0 on success, error code otherwise.
|
|
|
|
*/
|
|
|
|
int efx_tx_tso_fallback(struct efx_tx_queue *tx_queue, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct sk_buff *segments, *next;
|
|
|
|
|
|
|
|
segments = skb_gso_segment(skb, 0);
|
|
|
|
if (IS_ERR(segments))
|
|
|
|
return PTR_ERR(segments);
|
|
|
|
|
|
|
|
dev_consume_skb_any(skb);
|
|
|
|
|
|
|
|
skb_list_walk_safe(segments, skb, next) {
|
|
|
|
skb_mark_not_on_list(skb);
|
|
|
|
efx_enqueue_skb(tx_queue, skb);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|