[PATCH] e1000: Fix mulitple queues
authorJeff Kirsher <jeffrey.t.kirsher@intel.com>
Fri, 13 Jan 2006 00:50:41 +0000 (16:50 -0800)
committerJeff Garzik <jgarzik@pobox.com>
Tue, 17 Jan 2006 12:40:11 +0000 (07:40 -0500)
Fixed stats when using multiple queues.
When multiple queues are enabled, log a message in syslog.
Fixed memory allocation for multiple queues.

Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: Jesse Brandeburg <jesse.brandeburg@intel.com>
Signed-off-by: John Ronciak <john.ronciak@intel.com>
Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
drivers/net/e1000/e1000.h
drivers/net/e1000/e1000_ethtool.c
drivers/net/e1000/e1000_main.c

index 5940f7a..0a084e9 100644 (file)
 #include <linux/mii.h>
 #include <linux/ethtool.h>
 #include <linux/if_vlan.h>
-#ifdef CONFIG_E1000_MQ
-#include <linux/cpu.h>
-#include <linux/smp.h>
-#endif
 
 #define BAR_0          0
 #define BAR_1          1
 struct e1000_adapter;
 
 #include "e1000_hw.h"
+#ifdef CONFIG_E1000_MQ
+#include <linux/cpu.h>
+#include <linux/smp.h>
+#endif
 
 #ifdef DBG
 #define E1000_DBG(args...) printk(KERN_DEBUG "e1000: " args)
@@ -169,6 +169,13 @@ struct e1000_buffer {
        uint16_t next_to_watch;
 };
 
+#ifdef CONFIG_E1000_MQ
+struct e1000_queue_stats {
+       uint64_t packets;
+       uint64_t bytes;
+};
+#endif
+
 struct e1000_ps_page { struct page *ps_page[PS_PAGE_BUFFERS]; };
 struct e1000_ps_page_dma { uint64_t ps_page_dma[PS_PAGE_BUFFERS]; };
 
@@ -194,6 +201,9 @@ struct e1000_tx_ring {
 
        boolean_t last_tx_tso;
 
+#ifdef CONFIG_E1000_MQ
+       struct e1000_queue_stats tx_stats;
+#endif
 };
 
 struct e1000_rx_ring {
@@ -223,6 +233,9 @@ struct e1000_rx_ring {
 
        uint16_t rdh;
        uint16_t rdt;
+#ifdef CONFIG_E1000_MQ
+       struct e1000_queue_stats rx_stats;
+#endif
 };
 
 #define E1000_DESC_UNUSED(R) \
@@ -255,6 +268,9 @@ struct e1000_adapter {
        uint16_t link_speed;
        uint16_t link_duplex;
        spinlock_t stats_lock;
+#ifdef CONFIG_E1000_NAPI
+       spinlock_t tx_queue_lock;
+#endif
        atomic_t irq_sem;
        struct work_struct tx_timeout_task;
        struct work_struct watchdog_task;
@@ -302,7 +318,7 @@ struct e1000_adapter {
 #ifdef CONFIG_E1000_MQ
        struct net_device **cpu_netdev;     /* per-cpu */
        struct call_async_data_struct rx_sched_call_data;
-       int cpu_for_queue[4];
+       cpumask_t cpumask;
 #endif
        int num_tx_queues;
        int num_rx_queues;
index fa9a465..ffdf76b 100644 (file)
@@ -96,8 +96,18 @@ static const struct e1000_stats e1000_gstrings_stats[] = {
        { "rx_header_split", E1000_STAT(rx_hdr_split) },
        { "alloc_rx_buff_failed", E1000_STAT(alloc_rx_buff_failed) },
 };
-#define E1000_STATS_LEN        \
+
+#ifdef CONFIG_E1000_MQ
+#define E1000_QUEUE_STATS_LEN \
+       (((struct e1000_adapter *)netdev->priv)->num_tx_queues + \
+        ((struct e1000_adapter *)netdev->priv)->num_rx_queues) \
+       * (sizeof(struct e1000_queue_stats) / sizeof(uint64_t))
+#else
+#define E1000_QUEUE_STATS_LEN 0
+#endif
+#define E1000_GLOBAL_STATS_LEN \
        sizeof(e1000_gstrings_stats) / sizeof(struct e1000_stats)
+#define E1000_STATS_LEN (E1000_GLOBAL_STATS_LEN + E1000_QUEUE_STATS_LEN)
 static const char e1000_gstrings_test[][ETH_GSTRING_LEN] = {
        "Register test  (offline)", "Eeprom test    (offline)",
        "Interrupt test (offline)", "Loopback test  (offline)",
@@ -1746,19 +1756,43 @@ e1000_get_ethtool_stats(struct net_device *netdev,
                struct ethtool_stats *stats, uint64_t *data)
 {
        struct e1000_adapter *adapter = netdev_priv(netdev);
+#ifdef CONFIG_E1000_MQ
+       uint64_t *queue_stat;
+       int stat_count = sizeof(struct e1000_queue_stats) / sizeof(uint64_t);
+       int j, k;
+#endif
        int i;
 
        e1000_update_stats(adapter);
-       for(i = 0; i < E1000_STATS_LEN; i++) {
-               char *p = (char *)adapter+e1000_gstrings_stats[i].stat_offset;  
-               data[i] = (e1000_gstrings_stats[i].sizeof_stat == 
+       for (i = 0; i < E1000_GLOBAL_STATS_LEN; i++) {
+               char *p = (char *)adapter+e1000_gstrings_stats[i].stat_offset;
+               data[i] = (e1000_gstrings_stats[i].sizeof_stat ==
                        sizeof(uint64_t)) ? *(uint64_t *)p : *(uint32_t *)p;
        }
+#ifdef CONFIG_E1000_MQ
+       for (j = 0; j < adapter->num_tx_queues; j++) {
+               queue_stat = (uint64_t *)&adapter->tx_ring[j].tx_stats;
+               for (k = 0; k < stat_count; k++)
+                       data[i + k] = queue_stat[k];
+               i += k;
+       }
+       for (j = 0; j < adapter->num_rx_queues; j++) {
+               queue_stat = (uint64_t *)&adapter->rx_ring[j].rx_stats;
+               for (k = 0; k < stat_count; k++)
+                       data[i + k] = queue_stat[k];
+               i += k;
+       }
+#endif
+/*     BUG_ON(i != E1000_STATS_LEN); */
 }
 
 static void 
 e1000_get_strings(struct net_device *netdev, uint32_t stringset, uint8_t *data)
 {
+#ifdef CONFIG_E1000_MQ
+       struct e1000_adapter *adapter = netdev_priv(netdev);
+#endif
+       uint8_t *p = data;
        int i;
 
        switch(stringset) {
@@ -1767,11 +1801,26 @@ e1000_get_strings(struct net_device *netdev, uint32_t stringset, uint8_t *data)
                        E1000_TEST_LEN*ETH_GSTRING_LEN);
                break;
        case ETH_SS_STATS:
-               for (i=0; i < E1000_STATS_LEN; i++) {
-                       memcpy(data + i * ETH_GSTRING_LEN, 
-                       e1000_gstrings_stats[i].stat_string,
-                       ETH_GSTRING_LEN);
+               for (i = 0; i < E1000_GLOBAL_STATS_LEN; i++) {
+                       memcpy(p, e1000_gstrings_stats[i].stat_string,
+                              ETH_GSTRING_LEN);
+                       p += ETH_GSTRING_LEN;
+               }
+#ifdef CONFIG_E1000_MQ
+               for (i = 0; i < adapter->num_tx_queues; i++) {
+                       sprintf(p, "tx_queue_%u_packets", i);
+                       p += ETH_GSTRING_LEN;
+                       sprintf(p, "tx_queue_%u_bytes", i);
+                       p += ETH_GSTRING_LEN;
                }
+               for (i = 0; i < adapter->num_rx_queues; i++) {
+                       sprintf(p, "rx_queue_%u_packets", i);
+                       p += ETH_GSTRING_LEN;
+                       sprintf(p, "rx_queue_%u_bytes", i);
+                       p += ETH_GSTRING_LEN;
+               }
+#endif
+/*             BUG_ON(p - data != E1000_STATS_LEN * ETH_GSTRING_LEN); */
                break;
        }
 }
index 540c856..4016264 100644 (file)
@@ -433,6 +433,12 @@ e1000_up(struct e1000_adapter *adapter)
                return err;
        }
 
+#ifdef CONFIG_E1000_MQ
+       e1000_setup_queue_mapping(adapter);
+#endif
+
+       adapter->tx_queue_len = netdev->tx_queue_len;
+
        mod_timer(&adapter->watchdog_timer, jiffies);
 
 #ifdef CONFIG_E1000_NAPI
@@ -467,6 +473,7 @@ e1000_down(struct e1000_adapter *adapter)
 #ifdef CONFIG_E1000_NAPI
        netif_poll_disable(netdev);
 #endif
+       netdev->tx_queue_len = adapter->tx_queue_len;
        adapter->link_speed = 0;
        adapter->link_duplex = 0;
        netif_carrier_off(netdev);
@@ -989,6 +996,15 @@ e1000_sw_init(struct e1000_adapter *adapter)
        }
        adapter->num_rx_queues = min(adapter->num_rx_queues, num_online_cpus());
        adapter->num_tx_queues = min(adapter->num_tx_queues, num_online_cpus());
+       DPRINTK(DRV, INFO, "Multiqueue Enabled: Rx Queue count = %u %s\n",
+               adapter->num_rx_queues,
+               ((adapter->num_rx_queues == 1)
+                ? ((num_online_cpus() > 1)
+                       ? "(due to unsupported feature in current adapter)"
+                       : "(due to unsupported system configuration)")
+                : ""));
+       DPRINTK(DRV, INFO, "Multiqueue Enabled: Tx Queue count = %u\n",
+               adapter->num_tx_queues);
 #else
        adapter->num_tx_queues = 1;
        adapter->num_rx_queues = 1;
@@ -1007,10 +1023,7 @@ e1000_sw_init(struct e1000_adapter *adapter)
                dev_hold(&adapter->polling_netdev[i]);
                set_bit(__LINK_STATE_START, &adapter->polling_netdev[i].state);
        }
-#endif
-
-#ifdef CONFIG_E1000_MQ
-       e1000_setup_queue_mapping(adapter);
+       spin_lock_init(&adapter->tx_queue_lock);
 #endif
 
        atomic_set(&adapter->irq_sem, 1);
@@ -1058,6 +1071,14 @@ e1000_alloc_queues(struct e1000_adapter *adapter)
        memset(adapter->polling_netdev, 0, size);
 #endif
 
+#ifdef CONFIG_E1000_MQ
+       adapter->rx_sched_call_data.func = e1000_rx_schedule;
+       adapter->rx_sched_call_data.info = adapter->netdev;
+
+       adapter->cpu_netdev = alloc_percpu(struct net_device *);
+       adapter->cpu_tx_ring = alloc_percpu(struct e1000_tx_ring *);
+#endif
+
        return E1000_SUCCESS;
 }
 
@@ -1084,7 +1105,8 @@ e1000_setup_queue_mapping(struct e1000_adapter *adapter)
                 */
                if (i < adapter->num_rx_queues) {
                        *per_cpu_ptr(adapter->cpu_netdev, cpu) = &adapter->polling_netdev[i];
-                       adapter->cpu_for_queue[i] = cpu;
+                       adapter->rx_ring[i].cpu = cpu;
+                       cpu_set(cpu, adapter->cpumask);
                } else
                        *per_cpu_ptr(adapter->cpu_netdev, cpu) = NULL;
 
@@ -3337,6 +3359,10 @@ e1000_clean_tx_irq(struct e1000_adapter *adapter,
                        if(unlikely(++i == tx_ring->count)) i = 0;
                }
 
+#ifdef CONFIG_E1000_MQ
+               tx_ring->tx_stats.packets++;
+#endif
+
                eop = tx_ring->buffer_info[i].next_to_watch;
                eop_desc = E1000_TX_DESC(*tx_ring, eop);
        }
@@ -3365,6 +3391,7 @@ e1000_clean_tx_irq(struct e1000_adapter *adapter,
                        eop = tx_ring->buffer_info[i].next_to_watch;
                        eop_desc = E1000_TX_DESC(*tx_ring, eop);
                        DPRINTK(DRV, ERR, "Detected Tx Unit Hang\n"
+                                       "  Tx Queue             <%lu>\n"
                                        "  TDH                  <%x>\n"
                                        "  TDT                  <%x>\n"
                                        "  next_to_use          <%x>\n"
@@ -3375,6 +3402,8 @@ e1000_clean_tx_irq(struct e1000_adapter *adapter,
                                        "  next_to_watch        <%x>\n"
                                        "  jiffies              <%lx>\n"
                                        "  next_to_watch.status <%x>\n",
+                               (unsigned long)((tx_ring - adapter->tx_ring) /
+                                       sizeof(struct e1000_tx_ring)),
                                readl(adapter->hw.hw_addr + tx_ring->tdh),
                                readl(adapter->hw.hw_addr + tx_ring->tdt),
                                tx_ring->next_to_use,
@@ -3541,6 +3570,10 @@ e1000_clean_rx_irq(struct e1000_adapter *adapter,
                }
 #endif /* CONFIG_E1000_NAPI */
                netdev->last_rx = jiffies;
+#ifdef CONFIG_E1000_MQ
+               rx_ring->rx_stats.packets++;
+               rx_ring->rx_stats.bytes += length;
+#endif
 
 next_desc:
                rx_desc->status = 0;
@@ -3671,6 +3704,10 @@ e1000_clean_rx_irq_ps(struct e1000_adapter *adapter,
                }
 #endif /* CONFIG_E1000_NAPI */
                netdev->last_rx = jiffies;
+#ifdef CONFIG_E1000_MQ
+               rx_ring->rx_stats.packets++;
+               rx_ring->rx_stats.bytes += length;
+#endif
 
 next_desc:
                rx_desc->wb.middle.status_error &= ~0xFF;