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