[PATCH] CONFIG_NET_HW_FLOWCONTROL support for e1000

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




Hi,
you'll find here a patch I've made to add the CONFIG_NET_HW_FLOWCONTROL
support to the e1000 driver (against version 5.2.16).
Without CONFIG_NET_HW_FLOWCONTROL, the system will collapse when you load
it at Gigabit speed with small frames (only a few fps rate).
Thanks to CONFIG_NET_HW_FLOWCONTROL, it can keep a very good rate (200k
fps forwarding small packets in my test system) even with 100% load.

This patch has been made against the 5.2.16 Intel driver downloaded from
their web site. Patching the driver incorporated into recent kernels may
require some modifications.



diff -u -r e1000-5.2.16/src/e1000.h e1000-5.2.16_fc/src/e1000.h
--- e1000-5.2.16/src/e1000.h  Sat Aug  9 01:46:40 2003
+++ e1000-5.2.16_fc/src/e1000.h    Wed Sep 24 09:14:18 2003
@@ -255,5 +255,9 @@
 #endif

     uint32_t pci_state[16];
+
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+    int fc_bit;
+#endif
 };
 #endif /* _E1000_H_ */
diff -u -r e1000-5.2.16/src/e1000_main.c e1000-5.2.16_fc/src/e1000_main.c
--- e1000-5.2.16/src/e1000_main.c  Sat Aug  9 01:46:40 2003
+++ e1000-5.2.16_fc/src/e1000_main.c     Fri Sep 26 15:25:25 2003
@@ -142,6 +142,10 @@
 static void e1000_update_stats(struct e1000_adapter *adapter);
 static inline void e1000_irq_disable(struct e1000_adapter *adapter);
 static inline void e1000_irq_enable(struct e1000_adapter *adapter);
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+static inline void e1000_irq_disable_rx(struct e1000_adapter *adapter);
+static inline void e1000_irq_enable_rx(struct e1000_adapter *adapter);
+#endif
 static irqreturn_t e1000_intr(int irq, void *data, struct pt_regs *regs);
 static boolean_t e1000_clean_tx_irq(struct e1000_adapter *adapter);
 #ifdef CONFIG_E1000_NAPI
@@ -284,6 +288,7 @@
     struct net_device *netdev = adapter->netdev;

     e1000_irq_disable(adapter);
+
     free_irq(netdev->irq, netdev);
     del_timer_sync(&adapter->tx_fifo_stall_timer);
     del_timer_sync(&adapter->watchdog_timer);
@@ -675,6 +680,18 @@
     return 0;
 }

+#ifdef CONFIG_NET_HW_FLOWCONTROL
+static void e1000_xon(struct net_device *netdev)
+{
+    struct e1000_adapter *adapter = netdev->priv;
+
+    clear_bit(adapter->fc_bit, &netdev_fc_xoff);
+    if (netif_running(netdev)) {
+         e1000_irq_enable_rx(adapter);
+    }
+}
+#endif
+
 /**
  * e1000_open - Called when a network interface is made active
  * @netdev: network interface device structure
@@ -693,6 +710,10 @@
 {
     struct e1000_adapter *adapter = netdev->priv;

+#ifdef CONFIG_NET_HW_FLOWCONTROL
+    adapter->fc_bit = netdev_register_fc(netdev, e1000_xon);
+#endif
+
     /* allocate transmit descriptors */

     if(e1000_setup_tx_resources(adapter))
@@ -735,6 +756,15 @@
 {
     struct e1000_adapter *adapter = netdev->priv;

+#ifdef CONFIG_NET_HW_FLOWCONTROL
+    if (adapter->fc_bit) {
+         int bit = adapter->fc_bit;
+
+         adapter->fc_bit = 0;
+         netdev_unregister_fc(bit);
+    }
+#endif
+
     e1000_down(adapter);

     e1000_free_tx_resources(adapter);
@@ -2067,6 +2097,26 @@
     }
 }

+#ifdef CONFIG_NET_HW_FLOWCONTROL
+static inline void
+e1000_irq_disable_rx(struct e1000_adapter *adapter)
+{
+    if (atomic_read(&adapter->irq_sem)==0) {
+         E1000_WRITE_REG(&adapter->hw, IMC, E1000_IMC_RXT0);
+         E1000_WRITE_FLUSH(&adapter->hw);
+    }
+}
+
+static inline void
+e1000_irq_enable_rx(struct e1000_adapter *adapter)
+{
+    if (atomic_read(&adapter->irq_sem)==0) {
+         E1000_WRITE_REG(&adapter->hw, IMS, E1000_IMS_RXT0);
+         E1000_WRITE_FLUSH(&adapter->hw);
+    }
+}
+#endif //CONFIG_NET_HW_FLOWCONTROL
+
 /**
  * e1000_intr - Interrupt Handler
  * @irq: interrupt number
@@ -2082,7 +2132,8 @@
     uint32_t icr = E1000_READ_REG(&adapter->hw, ICR);
 #ifndef CONFIG_E1000_NAPI
     unsigned int i;
-#endif
+#endif
+    int rx_res, tx_res;

     if(!icr)
          return IRQ_NONE;  /* Not our interrupt */
@@ -2104,10 +2155,19 @@
          __netif_rx_schedule(netdev);
     }
 #else
-    for(i = 0; i < E1000_MAX_INTR; i++)
-         if(!e1000_clean_rx_irq(adapter) &
-            !e1000_clean_tx_irq(adapter))
+    for(i = 0; i < E1000_MAX_INTR; i++) {
+         rx_res = tx_res = 0;
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+         if ( (!adapter->fc_bit) ||
+              (!test_bit(adapter->fc_bit, &netdev_fc_xoff)))
+#endif
+              rx_res = e1000_clean_rx_irq(adapter);
+
+         tx_res = e1000_clean_tx_irq(adapter);
+
+         if (!rx_res && !tx_res)
               break;
+    }
 #endif
 #ifdef E1000_COUNT_ICR
     adapter->icr_txdw += icr & 0x01;
@@ -2249,6 +2309,7 @@
     uint8_t last_byte;
     unsigned int i;
     boolean_t cleaned = FALSE;
+    int netif_rx_result = NET_RX_SUCCESS;

     i = rx_ring->next_to_clean;
     rx_desc = E1000_RX_DESC(*rx_ring, i);
@@ -2263,8 +2324,6 @@
          (*work_done)++;
 #endif

-         cleaned = TRUE;
-
          pci_unmap_single(pdev,
                           buffer_info->dma,
                           buffer_info->length,
@@ -2273,16 +2332,29 @@
          skb = buffer_info->skb;
          length = le16_to_cpu(rx_desc->length);

+#ifdef CONFIG_NET_HW_FLOWCONTROL
+         if (atomic_read(&netdev_dropping)) {
+              dev_kfree_skb_irq(skb);
+              buffer_info->skb = NULL;
+              rx_desc->status = 0;
+
+              goto e1000_throttle;
+         }
+#endif
+
+         cleaned = TRUE;
+
          if(!(rx_desc->status & E1000_RXD_STAT_EOP)) {

               /* All receives must fit into a single buffer */

               E1000_DBG("Receive packet consumed multiple buffers\n");
-
+
               dev_kfree_skb_irq(skb);
-              rx_desc->status = 0;
               buffer_info->skb = NULL;

+              rx_desc->status = 0;
+
               if(++i == rx_ring->count) i = 0;

               rx_desc = E1000_RX_DESC(*rx_ring, i);
@@ -2344,16 +2416,27 @@
                    le16_to_cpu(rx_desc->special &
                         E1000_RXD_SPC_VLAN_MASK));
          } else {
-              netif_rx(skb);
+              netif_rx_result = netif_rx(skb);
          }
 #else
-         netif_rx(skb);
+         netif_rx_result = netif_rx(skb);
 #endif
 #endif /* CONFIG_E1000_NAPI */
          netdev->last_rx = jiffies;

          rx_desc->status = 0;
          buffer_info->skb = NULL;
+
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+         if (atomic_read(&netdev_dropping)) {
+e1000_throttle:
+              if (adapter->fc_bit) {
+                   e1000_irq_disable_rx(adapter);
+                   set_bit(adapter->fc_bit, &netdev_fc_xoff);
+              }
+              cleaned = FALSE;
+         }
+#endif

          if(++i == rx_ring->count) i = 0;



---
Daniel FAGES
ARKOON Network Security    http://www.arkoon.net


-
: send the line "unsubscribe linux-net" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux 802.1Q VLAN]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Git]     [Bugtraq]     [Yosemite News and Information]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux PCI]     [Linux Admin]     [Samba]

  Powered by Linux