sfc: Use separate hardware TX queues to select checksum generation
authorBen Hutchings <bhutchings@solarflare.com>
Mon, 1 Sep 2008 11:44:59 +0000 (12:44 +0100)
committerJeff Garzik <jgarzik@redhat.com>
Wed, 3 Sep 2008 13:53:42 +0000 (09:53 -0400)
Checksum generation is an attribute of our hardware TX queues, not TX
descriptors.  We previously used a single queue and turned checksum
generation on or off as requested through ethtool.  However, this can
result in regenerating checksums in raw packets that should not be
modified.  We now create 2 hardware TX queues with checksum generation
on or off.  They are presented to the net core as one queue since it
does not know how to select between them.

The self-test verifies that a bad checksum is unaltered on the queue
with checksum generation off.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
drivers/net/sfc/efx.c
drivers/net/sfc/ethtool.c
drivers/net/sfc/falcon.c
drivers/net/sfc/net_driver.h
drivers/net/sfc/selftest.c
drivers/net/sfc/selftest.h
drivers/net/sfc/tx.c

index 42539802b7ae0c93f24c4b77ba669a23e208669f..2a2300571cbfa21f277c53025ead15e8c59a5dea 100644 (file)
@@ -923,22 +923,13 @@ static void efx_select_used(struct efx_nic *efx)
        struct efx_rx_queue *rx_queue;
        int i;
 
-       /* TX queues.  One per port per channel with TX capability
-        * (more than one per port won't work on Linux, due to out
-        *  of order issues... but will be fine on Solaris)
-        */
-       tx_queue = &efx->tx_queue[0];
-
-       /* Perform this for each channel with TX capabilities.
-        * At the moment, we only support a single TX queue
-        */
-       tx_queue->used = 1;
-       if ((!EFX_INT_MODE_USE_MSI(efx)) && separate_tx_and_rx_channels)
-               tx_queue->channel = &efx->channel[1];
-       else
-               tx_queue->channel = &efx->channel[0];
-       tx_queue->channel->used_flags |= EFX_USED_BY_TX;
-       tx_queue++;
+       efx_for_each_tx_queue(tx_queue, efx) {
+               if (!EFX_INT_MODE_USE_MSI(efx) && separate_tx_and_rx_channels)
+                       tx_queue->channel = &efx->channel[1];
+               else
+                       tx_queue->channel = &efx->channel[0];
+               tx_queue->channel->used_flags |= EFX_USED_BY_TX;
+       }
 
        /* RX queues.  Each has a dedicated channel. */
        for (i = 0; i < EFX_MAX_RX_QUEUES; i++) {
@@ -1881,7 +1872,7 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type,
                channel->evqnum = i;
                channel->work_pending = 0;
        }
-       for (i = 0; i < EFX_MAX_TX_QUEUES; i++) {
+       for (i = 0; i < EFX_TX_QUEUE_COUNT; i++) {
                tx_queue = &efx->tx_queue[i];
                tx_queue->efx = efx;
                tx_queue->queue = i;
index 8a15be6548d23d3a70e3c8573cb3917f9cf107b7..ccd82f12456cffee86902dfe88d3d7df4faff751 100644 (file)
@@ -32,8 +32,6 @@ const char *efx_loopback_mode_names[] = {
        [LOOPBACK_NETWORK]      = "NETWORK",
 };
 
-static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable);
-
 struct ethtool_string {
        char name[ETH_GSTRING_LEN];
 };
@@ -442,45 +440,6 @@ 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)
-{
-       struct efx_nic *efx = netdev_priv(net_dev);
-       int rc;
-
-       rc = ethtool_op_set_tx_csum(net_dev, enable);
-       if (rc)
-               return rc;
-
-       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;
-}
-
 static int efx_ethtool_set_rx_csum(struct net_device *net_dev, u32 enable)
 {
        struct efx_nic *efx = netdev_priv(net_dev);
@@ -701,11 +660,11 @@ struct ethtool_ops efx_ethtool_ops = {
        .get_rx_csum            = efx_ethtool_get_rx_csum,
        .set_rx_csum            = efx_ethtool_set_rx_csum,
        .get_tx_csum            = ethtool_op_get_tx_csum,
-       .set_tx_csum            = efx_ethtool_set_tx_csum,
+       .set_tx_csum            = ethtool_op_set_tx_csum,
        .get_sg                 = ethtool_op_get_sg,
        .set_sg                 = ethtool_op_set_sg,
        .get_tso                = ethtool_op_get_tso,
-       .set_tso                = efx_ethtool_set_tso,
+       .set_tso                = ethtool_op_set_tso,
        .get_flags              = ethtool_op_get_flags,
        .set_flags              = ethtool_op_set_flags,
        .self_test_count        = efx_ethtool_self_test_count,
index c7aa2f61cb1c6b6968f0be01a6968024df6a97da..b73f1ea8bd582abc4d2229192819cec5f5a50ee3 100644 (file)
@@ -474,9 +474,9 @@ int falcon_init_tx(struct efx_tx_queue *tx_queue)
                              TX_NON_IP_DROP_DIS_B0, 1);
 
        if (falcon_rev(efx) >= FALCON_REV_B0) {
-               int csum = !(efx->net_dev->features & NETIF_F_IP_CSUM);
-               EFX_SET_OWORD_FIELD(tx_desc_ptr, TX_IP_CHKSM_DIS_B0, csum);
-               EFX_SET_OWORD_FIELD(tx_desc_ptr, TX_TCP_CHKSM_DIS_B0, csum);
+               int csum = tx_queue->queue == EFX_TX_QUEUE_OFFLOAD_CSUM;
+               EFX_SET_OWORD_FIELD(tx_desc_ptr, TX_IP_CHKSM_DIS_B0, !csum);
+               EFX_SET_OWORD_FIELD(tx_desc_ptr, TX_TCP_CHKSM_DIS_B0, !csum);
        }
 
        falcon_write_table(efx, &tx_desc_ptr, efx->type->txd_ptr_tbl_base,
@@ -485,10 +485,11 @@ int falcon_init_tx(struct efx_tx_queue *tx_queue)
        if (falcon_rev(efx) < FALCON_REV_B0) {
                efx_oword_t reg;
 
-               BUG_ON(tx_queue->queue >= 128); /* HW limit */
+               /* Only 128 bits in this register */
+               BUILD_BUG_ON(EFX_TX_QUEUE_COUNT >= 128);
 
                falcon_read(efx, &reg, TX_CHKSM_CFG_REG_KER_A1);
-               if (efx->net_dev->features & NETIF_F_IP_CSUM)
+               if (tx_queue->queue == EFX_TX_QUEUE_OFFLOAD_CSUM)
                        clear_bit_le(tx_queue->queue, (void *)&reg);
                else
                        set_bit_le(tx_queue->queue, (void *)&reg);
index 628f25e32918377efee5fbf15aa32fbb580104d3..f539e2e0da1bcaff546a059acbbfe2fc3f070971 100644 (file)
@@ -88,9 +88,12 @@ do {if (net_ratelimit()) EFX_LOG(efx, fmt, ##args); } while (0)
  **************************************************************************/
 
 #define EFX_MAX_CHANNELS 32
-#define EFX_MAX_TX_QUEUES 1
 #define EFX_MAX_RX_QUEUES EFX_MAX_CHANNELS
 
+#define EFX_TX_QUEUE_OFFLOAD_CSUM      0
+#define EFX_TX_QUEUE_NO_CSUM           1
+#define EFX_TX_QUEUE_COUNT             2
+
 /**
  * struct efx_special_buffer - An Efx special buffer
  * @addr: CPU base address of the buffer
@@ -156,7 +159,6 @@ struct efx_tx_buffer {
  *
  * @efx: The associated Efx NIC
  * @queue: DMA queue number
- * @used: Queue is used by net driver
  * @channel: The associated channel
  * @buffer: The software buffer ring
  * @txd: The hardware descriptor ring
@@ -188,7 +190,6 @@ struct efx_tx_queue {
        /* Members which don't change on the fast path */
        struct efx_nic *efx ____cacheline_aligned_in_smp;
        int queue;
-       int used;
        struct efx_channel *channel;
        struct efx_nic *nic;
        struct efx_tx_buffer *buffer;
@@ -699,7 +700,7 @@ struct efx_nic {
        enum nic_state state;
        enum reset_type reset_pending;
 
-       struct efx_tx_queue tx_queue[EFX_MAX_TX_QUEUES];
+       struct efx_tx_queue tx_queue[EFX_TX_QUEUE_COUNT];
        struct efx_rx_queue rx_queue[EFX_MAX_RX_QUEUES];
        struct efx_channel channel[EFX_MAX_CHANNELS];
 
@@ -840,19 +841,15 @@ struct efx_nic_type {
 /* Iterate over all used TX queues */
 #define efx_for_each_tx_queue(_tx_queue, _efx)                         \
        for (_tx_queue = &_efx->tx_queue[0];                            \
-            _tx_queue < &_efx->tx_queue[EFX_MAX_TX_QUEUES];            \
-            _tx_queue++)                                               \
-               if (!_tx_queue->used)                                   \
-                       continue;                                       \
-               else
+            _tx_queue < &_efx->tx_queue[EFX_TX_QUEUE_COUNT];           \
+            _tx_queue++)
 
 /* Iterate over all TX queues belonging to a channel */
 #define efx_for_each_channel_tx_queue(_tx_queue, _channel)             \
        for (_tx_queue = &_channel->efx->tx_queue[0];                   \
-            _tx_queue < &_channel->efx->tx_queue[EFX_MAX_TX_QUEUES];   \
+            _tx_queue < &_channel->efx->tx_queue[EFX_TX_QUEUE_COUNT];  \
             _tx_queue++)                                               \
-               if ((!_tx_queue->used) ||                               \
-                   (_tx_queue->channel != _channel))                   \
+               if (_tx_queue->channel != _channel)                     \
                        continue;                                       \
                else
 
index 3b2de9fe7f27ee6607a3cdc17cf8a443285287c5..0a4778629178fd77e0b26c999162dbb3c026ab7f 100644 (file)
@@ -63,6 +63,10 @@ struct efx_selftest_state {
        int flush;
        int packet_count;
        struct sk_buff **skbs;
+
+       /* Checksums are being offloaded */
+       int offload_csum;
+
        atomic_t rx_good;
        atomic_t rx_bad;
        struct efx_loopback_payload payload;
@@ -292,8 +296,9 @@ void efx_loopback_rx_packet(struct efx_nic *efx,
        
        received = (struct efx_loopback_payload *) buf_ptr;
        received->ip.saddr = payload->ip.saddr;
-       received->ip.check = payload->ip.check;
-       
+       if (state->offload_csum)
+               received->ip.check = payload->ip.check;
+
        /* Check that header exists */
        if (pkt_len < sizeof(received->header)) {
                EFX_ERR(efx, "saw runt RX packet (length %d) in %s loopback "
@@ -634,6 +639,8 @@ static int efx_test_loopbacks(struct efx_nic *efx,
 
                /* Test every TX queue */
                efx_for_each_tx_queue(tx_queue, efx) {
+                       state->offload_csum = (tx_queue->queue ==
+                                              EFX_TX_QUEUE_OFFLOAD_CSUM);
                        rc |= efx_test_loopback(tx_queue,
                                                &tests->loopback[mode]);
                        if (rc)
index f6999c2b622d603eabcd77a3f5f8fc0350201489..cd59f00e28212d190072fae0a8bf1e9cdf1ba53a 100644 (file)
@@ -18,8 +18,8 @@
  */
 
 struct efx_loopback_self_tests {
-       int tx_sent[EFX_MAX_TX_QUEUES];
-       int tx_done[EFX_MAX_TX_QUEUES];
+       int tx_sent[EFX_TX_QUEUE_COUNT];
+       int tx_done[EFX_TX_QUEUE_COUNT];
        int rx_good;
        int rx_bad;
 };
index 2a09101f67eb83e9045b080f9950539c497d1e7f..e5e0bab313eb210983e59eaf50b3c285e3e2324c 100644 (file)
@@ -368,7 +368,14 @@ inline int efx_xmit(struct efx_nic *efx,
 int efx_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev)
 {
        struct efx_nic *efx = netdev_priv(net_dev);
-       return efx_xmit(efx, &efx->tx_queue[0], skb);
+       struct efx_tx_queue *tx_queue;
+
+       if (likely(skb->ip_summed == CHECKSUM_PARTIAL))
+               tx_queue = &efx->tx_queue[EFX_TX_QUEUE_OFFLOAD_CSUM];
+       else
+               tx_queue = &efx->tx_queue[EFX_TX_QUEUE_NO_CSUM];
+
+       return efx_xmit(efx, tx_queue, skb);
 }
 
 void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index)
@@ -412,26 +419,21 @@ int efx_probe_tx_queue(struct efx_tx_queue *tx_queue)
        /* Allocate software ring */
        txq_size = (efx->type->txd_ring_mask + 1) * sizeof(*tx_queue->buffer);
        tx_queue->buffer = kzalloc(txq_size, GFP_KERNEL);
-       if (!tx_queue->buffer) {
-               rc = -ENOMEM;
-               goto fail1;
-       }
+       if (!tx_queue->buffer)
+               return -ENOMEM;
        for (i = 0; i <= efx->type->txd_ring_mask; ++i)
                tx_queue->buffer[i].continuation = 1;
 
        /* Allocate hardware ring */
        rc = falcon_probe_tx(tx_queue);
        if (rc)
-               goto fail2;
+               goto fail;
 
        return 0;
 
- fail2:
+ fail:
        kfree(tx_queue->buffer);
        tx_queue->buffer = NULL;
- fail1:
-       tx_queue->used = 0;
-
        return rc;
 }
 
@@ -494,7 +496,6 @@ void efx_remove_tx_queue(struct efx_tx_queue *tx_queue)
 
        kfree(tx_queue->buffer);
        tx_queue->buffer = NULL;
-       tx_queue->used = 0;
 }