Commit b9b39b62 authored by Ben Hutchings's avatar Ben Hutchings Committed by Jeff Garzik

[netdrvr] sfc: Add TSO support

The SFC4000 controller does not have hardware support for TSO, and the
core GSO code incurs a high cost in allocating and freeing skbs.  This
TSO implementation uses lightweight packet header structures and is
substantially faster.
Signed-off-by: default avatarBen Hutchings <bhutchings@solarflare.com>
Signed-off-by: default avatarJeff Garzik <jgarzik@redhat.com>
parent 48cfb14f
...@@ -1873,6 +1873,7 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type, ...@@ -1873,6 +1873,7 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type,
tx_queue->queue = i; tx_queue->queue = i;
tx_queue->buffer = NULL; tx_queue->buffer = NULL;
tx_queue->channel = &efx->channel[0]; /* for safety */ tx_queue->channel = &efx->channel[0]; /* for safety */
tx_queue->tso_headers_free = NULL;
} }
for (i = 0; i < EFX_MAX_RX_QUEUES; i++) { for (i = 0; i < EFX_MAX_RX_QUEUES; i++) {
rx_queue = &efx->rx_queue[i]; rx_queue = &efx->rx_queue[i];
...@@ -2071,7 +2072,8 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev, ...@@ -2071,7 +2072,8 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev,
net_dev = alloc_etherdev(sizeof(*efx)); net_dev = alloc_etherdev(sizeof(*efx));
if (!net_dev) if (!net_dev)
return -ENOMEM; return -ENOMEM;
net_dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_HIGHDMA; net_dev->features |= (NETIF_F_IP_CSUM | NETIF_F_SG |
NETIF_F_HIGHDMA | NETIF_F_TSO);
if (lro) if (lro)
net_dev->features |= NETIF_F_LRO; net_dev->features |= NETIF_F_LRO;
efx = net_dev->priv; efx = net_dev->priv;
......
...@@ -272,6 +272,22 @@ static void efx_ethtool_get_stats(struct net_device *net_dev, ...@@ -272,6 +272,22 @@ static void efx_ethtool_get_stats(struct net_device *net_dev,
} }
} }
static int efx_ethtool_set_tso(struct net_device *net_dev, u32 enable)
{
int rc;
/* Our TSO requires TX checksumming, so force TX checksumming
* on when TSO is enabled.
*/
if (enable) {
rc = efx_ethtool_set_tx_csum(net_dev, 1);
if (rc)
return rc;
}
return ethtool_op_set_tso(net_dev, enable);
}
static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable) static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable)
{ {
struct efx_nic *efx = net_dev->priv; struct efx_nic *efx = net_dev->priv;
...@@ -283,6 +299,15 @@ static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable) ...@@ -283,6 +299,15 @@ static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable)
efx_flush_queues(efx); efx_flush_queues(efx);
/* Our TSO requires TX checksumming, so disable TSO when
* checksumming is disabled
*/
if (!enable) {
rc = efx_ethtool_set_tso(net_dev, 0);
if (rc)
return rc;
}
return 0; return 0;
} }
...@@ -451,6 +476,8 @@ struct ethtool_ops efx_ethtool_ops = { ...@@ -451,6 +476,8 @@ struct ethtool_ops efx_ethtool_ops = {
.set_tx_csum = efx_ethtool_set_tx_csum, .set_tx_csum = efx_ethtool_set_tx_csum,
.get_sg = ethtool_op_get_sg, .get_sg = ethtool_op_get_sg,
.set_sg = ethtool_op_set_sg, .set_sg = ethtool_op_set_sg,
.get_tso = ethtool_op_get_tso,
.set_tso = efx_ethtool_set_tso,
.get_flags = ethtool_op_get_flags, .get_flags = ethtool_op_get_flags,
.set_flags = ethtool_op_set_flags, .set_flags = ethtool_op_set_flags,
.get_strings = efx_ethtool_get_strings, .get_strings = efx_ethtool_get_strings,
......
...@@ -134,6 +134,8 @@ struct efx_special_buffer { ...@@ -134,6 +134,8 @@ struct efx_special_buffer {
* Set only on the final fragment of a packet; %NULL for all other * Set only on the final fragment of a packet; %NULL for all other
* fragments. When this fragment completes, then we can free this * fragments. When this fragment completes, then we can free this
* skb. * skb.
* @tsoh: The associated TSO header structure, or %NULL if this
* buffer is not a TSO header.
* @dma_addr: DMA address of the fragment. * @dma_addr: DMA address of the fragment.
* @len: Length of this fragment. * @len: Length of this fragment.
* This field is zero when the queue slot is empty. * This field is zero when the queue slot is empty.
...@@ -144,6 +146,7 @@ struct efx_special_buffer { ...@@ -144,6 +146,7 @@ struct efx_special_buffer {
*/ */
struct efx_tx_buffer { struct efx_tx_buffer {
const struct sk_buff *skb; const struct sk_buff *skb;
struct efx_tso_header *tsoh;
dma_addr_t dma_addr; dma_addr_t dma_addr;
unsigned short len; unsigned short len;
unsigned char continuation; unsigned char continuation;
...@@ -187,6 +190,13 @@ struct efx_tx_buffer { ...@@ -187,6 +190,13 @@ struct efx_tx_buffer {
* variable indicates that the queue is full. This is to * variable indicates that the queue is full. This is to
* avoid cache-line ping-pong between the xmit path and the * avoid cache-line ping-pong between the xmit path and the
* completion path. * completion path.
* @tso_headers_free: A list of TSO headers allocated for this TX queue
* that are not in use, and so available for new TSO sends. The list
* is protected by the TX queue lock.
* @tso_bursts: Number of times TSO xmit invoked by kernel
* @tso_long_headers: Number of packets with headers too long for standard
* blocks
* @tso_packets: Number of packets via the TSO xmit path
*/ */
struct efx_tx_queue { struct efx_tx_queue {
/* Members which don't change on the fast path */ /* Members which don't change on the fast path */
...@@ -206,6 +216,10 @@ struct efx_tx_queue { ...@@ -206,6 +216,10 @@ struct efx_tx_queue {
unsigned int insert_count ____cacheline_aligned_in_smp; unsigned int insert_count ____cacheline_aligned_in_smp;
unsigned int write_count; unsigned int write_count;
unsigned int old_read_count; unsigned int old_read_count;
struct efx_tso_header *tso_headers_free;
unsigned int tso_bursts;
unsigned int tso_long_headers;
unsigned int tso_packets;
}; };
/** /**
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment