From 808ff676c46d8e259c2cb38579f94f8e6ae38009 Mon Sep 17 00:00:00 2001 From: Bruce Allan Date: Fri, 8 Aug 2008 18:35:56 -0700 Subject: e1000e: Use skb_copy_to_linear_data_offset introduced in 2.6.22 The e1000e driver was based on a version of e1000 prior to acme's introduction of skb_copy_to_linear_data_offset, and was submitted after acme went through and coverted all the drivers to use it. Signed-off-by: Bruce Allan Signed-off-by: Jeff Kirsher Signed-off-by: Jeff Garzik --- drivers/net/e1000e/netdev.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/net/e1000e/netdev.c') diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index 05b0b2f9c54..efa944509a3 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -510,9 +510,12 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter, netdev_alloc_skb(netdev, length + NET_IP_ALIGN); if (new_skb) { skb_reserve(new_skb, NET_IP_ALIGN); - memcpy(new_skb->data - NET_IP_ALIGN, - skb->data - NET_IP_ALIGN, - length + NET_IP_ALIGN); + skb_copy_to_linear_data_offset(new_skb, + -NET_IP_ALIGN, + (skb->data - + NET_IP_ALIGN), + (length + + NET_IP_ALIGN)); /* save the skb in buffer_info as good */ buffer_info->skb = skb; skb = new_skb; -- cgit v1.2.3 From 10f1b492135620a87970cad9f80439249dcce299 Mon Sep 17 00:00:00 2001 From: Bruce Allan Date: Fri, 8 Aug 2008 18:36:01 -0700 Subject: e1000e: Increase Tx timeout factor for 10Mbps Prevent Tx hangs from happening on 10Mb flood ping by increasing the timeout factor. Signed-off-by: Bruce Allan Signed-off-by: Jeff Kirsher Signed-off-by: Jeff Garzik --- drivers/net/e1000e/netdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/e1000e/netdev.c') diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index efa944509a3..4573c7b9cbd 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -3058,7 +3058,7 @@ static void e1000_watchdog_task(struct work_struct *work) case SPEED_10: txb2b = 0; netdev->tx_queue_len = 10; - adapter->tx_timeout_factor = 14; + adapter->tx_timeout_factor = 16; break; case SPEED_100: txb2b = 0; -- cgit v1.2.3 From d53f706da808e6d93dc60b1910233a309009939f Mon Sep 17 00:00:00 2001 From: Bruce Allan Date: Fri, 8 Aug 2008 18:36:06 -0700 Subject: e1000e: increase minimum frame size allowed Setting an MTU value below 68 was disabling the network connection and would not reconnect until the driver was reloaded. Prevent changing the MTU to anything below 68. Signed-off-by: Bruce Allan Signed-off-by: Jeff Kirsher Signed-off-by: Jeff Garzik --- drivers/net/e1000e/netdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/e1000e/netdev.c') diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index 4573c7b9cbd..b8b26cf78de 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -3724,7 +3724,7 @@ static int e1000_change_mtu(struct net_device *netdev, int new_mtu) struct e1000_adapter *adapter = netdev_priv(netdev); int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN; - if ((max_frame < ETH_ZLEN + ETH_FCS_LEN) || + if ((new_mtu < ETH_ZLEN + ETH_FCS_LEN + VLAN_HLEN) || (max_frame > MAX_JUMBO_FRAME_SIZE)) { e_err("Invalid MTU setting\n"); return -EINVAL; -- cgit v1.2.3 From f8d59f7826aa73c5e7682fbed6db38020635d466 Mon Sep 17 00:00:00 2001 From: Bruce Allan Date: Fri, 8 Aug 2008 18:36:11 -0700 Subject: e1000e: test for unusable MSI support Some systems do not like 82571/2 use of 16-bit MSI messages and some other systems claim to support MSI, but neither really works. Setup a test MSI handler to detect whether or not MSI is working properly, and if not, fallback to legacy INTx interrupts. Signed-off-by: Bruce Allan Signed-off-by: Jeff Kirsher Signed-off-by: Jeff Garzik --- drivers/net/e1000e/netdev.c | 172 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 162 insertions(+), 10 deletions(-) (limited to 'drivers/net/e1000e/netdev.c') diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index b8b26cf78de..d266510c8a9 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -1236,26 +1236,36 @@ static irqreturn_t e1000_intr(int irq, void *data) return IRQ_HANDLED; } +/** + * e1000_request_irq - initialize interrupts + * + * Attempts to configure interrupts using the best available + * capabilities of the hardware and kernel. + **/ static int e1000_request_irq(struct e1000_adapter *adapter) { struct net_device *netdev = adapter->netdev; - irq_handler_t handler = e1000_intr; int irq_flags = IRQF_SHARED; int err; - if (!pci_enable_msi(adapter->pdev)) { - adapter->flags |= FLAG_MSI_ENABLED; - handler = e1000_intr_msi; - irq_flags = 0; + if (!(adapter->flags & FLAG_MSI_TEST_FAILED)) { + err = pci_enable_msi(adapter->pdev); + if (!err) { + adapter->flags |= FLAG_MSI_ENABLED; + irq_flags = 0; + } } - err = request_irq(adapter->pdev->irq, handler, irq_flags, netdev->name, - netdev); + err = request_irq(adapter->pdev->irq, + ((adapter->flags & FLAG_MSI_ENABLED) ? + &e1000_intr_msi : &e1000_intr), + irq_flags, netdev->name, netdev); if (err) { - e_err("Unable to allocate %s interrupt (return: %d)\n", - adapter->flags & FLAG_MSI_ENABLED ? "MSI":"INTx", err); - if (adapter->flags & FLAG_MSI_ENABLED) + if (adapter->flags & FLAG_MSI_ENABLED) { pci_disable_msi(adapter->pdev); + adapter->flags &= ~FLAG_MSI_ENABLED; + } + e_err("Unable to allocate interrupt, Error: %d\n", err); } return err; @@ -2594,6 +2604,135 @@ err: return -ENOMEM; } +/** + * e1000_intr_msi_test - Interrupt Handler + * @irq: interrupt number + * @data: pointer to a network interface device structure + **/ +static irqreturn_t e1000_intr_msi_test(int irq, void *data) +{ + struct net_device *netdev = data; + struct e1000_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; + u32 icr = er32(ICR); + + e_dbg("%s: icr is %08X\n", netdev->name, icr); + if (icr & E1000_ICR_RXSEQ) { + adapter->flags &= ~FLAG_MSI_TEST_FAILED; + wmb(); + } + + return IRQ_HANDLED; +} + +/** + * e1000_test_msi_interrupt - Returns 0 for successful test + * @adapter: board private struct + * + * code flow taken from tg3.c + **/ +static int e1000_test_msi_interrupt(struct e1000_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + struct e1000_hw *hw = &adapter->hw; + int err; + + /* poll_enable hasn't been called yet, so don't need disable */ + /* clear any pending events */ + er32(ICR); + + /* free the real vector and request a test handler */ + e1000_free_irq(adapter); + + /* Assume that the test fails, if it succeeds then the test + * MSI irq handler will unset this flag */ + adapter->flags |= FLAG_MSI_TEST_FAILED; + + err = pci_enable_msi(adapter->pdev); + if (err) + goto msi_test_failed; + + err = request_irq(adapter->pdev->irq, &e1000_intr_msi_test, 0, + netdev->name, netdev); + if (err) { + pci_disable_msi(adapter->pdev); + goto msi_test_failed; + } + + wmb(); + + e1000_irq_enable(adapter); + + /* fire an unusual interrupt on the test handler */ + ew32(ICS, E1000_ICS_RXSEQ); + e1e_flush(); + msleep(50); + + e1000_irq_disable(adapter); + + rmb(); + + if (adapter->flags & FLAG_MSI_TEST_FAILED) { + err = -EIO; + e_info("MSI interrupt test failed!\n"); + } + + free_irq(adapter->pdev->irq, netdev); + pci_disable_msi(adapter->pdev); + + if (err == -EIO) + goto msi_test_failed; + + /* okay so the test worked, restore settings */ + e_dbg("%s: MSI interrupt test succeeded!\n", netdev->name); +msi_test_failed: + /* restore the original vector, even if it failed */ + e1000_request_irq(adapter); + return err; +} + +/** + * e1000_test_msi - Returns 0 if MSI test succeeds or INTx mode is restored + * @adapter: board private struct + * + * code flow taken from tg3.c, called with e1000 interrupts disabled. + **/ +static int e1000_test_msi(struct e1000_adapter *adapter) +{ + int err; + u16 pci_cmd; + + if (!(adapter->flags & FLAG_MSI_ENABLED)) + return 0; + + /* disable SERR in case the MSI write causes a master abort */ + pci_read_config_word(adapter->pdev, PCI_COMMAND, &pci_cmd); + pci_write_config_word(adapter->pdev, PCI_COMMAND, + pci_cmd & ~PCI_COMMAND_SERR); + + err = e1000_test_msi_interrupt(adapter); + + /* restore previous setting of command word */ + pci_write_config_word(adapter->pdev, PCI_COMMAND, pci_cmd); + + /* success ! */ + if (!err) + return 0; + + /* EIO means MSI test failed */ + if (err != -EIO) + return err; + + /* back to INTx mode */ + e_warn("MSI interrupt test failed, using legacy interrupt.\n"); + + e1000_free_irq(adapter); + + err = e1000_request_irq(adapter); + + return err; +} + /** * e1000_open - Called when a network interface is made active * @netdev: network interface device structure @@ -2652,6 +2791,19 @@ static int e1000_open(struct net_device *netdev) if (err) goto err_req_irq; + /* + * Work around PCIe errata with MSI interrupts causing some chipsets to + * ignore e1000e MSI messages, which means we need to test our MSI + * interrupt now + */ + { + err = e1000_test_msi(adapter); + if (err) { + e_err("Interrupt allocation failed\n"); + goto err_req_irq; + } + } + /* From here on the code is the same as e1000e_up() */ clear_bit(__E1000_DOWN, &adapter->state); -- cgit v1.2.3