Commit f8d59f7826aa73c5e7682fbed6db38020635d466
Committed by
Jeff Garzik
1 parent
d53f706da8
Exists in
master
and in
7 other branches
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 <bruce.w.allan@intel.com> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com> Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Showing 3 changed files with 164 additions and 11 deletions Side-by-side Diff
drivers/net/e1000e/defines.h
... | ... | @@ -389,7 +389,7 @@ |
389 | 389 | |
390 | 390 | /* Interrupt Cause Set */ |
391 | 391 | #define E1000_ICS_LSC E1000_ICR_LSC /* Link Status Change */ |
392 | -#define E1000_ICS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ | |
392 | +#define E1000_ICS_RXSEQ E1000_ICR_RXSEQ /* Rx sequence error */ | |
393 | 393 | #define E1000_ICS_RXDMT0 E1000_ICR_RXDMT0 /* Rx desc min. threshold */ |
394 | 394 | |
395 | 395 | /* Transmit Descriptor Control */ |
drivers/net/e1000e/e1000.h
... | ... | @@ -326,6 +326,7 @@ |
326 | 326 | #define FLAG_RX_CSUM_ENABLED (1 << 28) |
327 | 327 | #define FLAG_TSO_FORCE (1 << 29) |
328 | 328 | #define FLAG_RX_RESTART_NOW (1 << 30) |
329 | +#define FLAG_MSI_TEST_FAILED (1 << 31) | |
329 | 330 | |
330 | 331 | #define E1000_RX_DESC_PS(R, i) \ |
331 | 332 | (&(((union e1000_rx_desc_packet_split *)((R).desc))[i])) |
drivers/net/e1000e/netdev.c
... | ... | @@ -1236,26 +1236,36 @@ |
1236 | 1236 | return IRQ_HANDLED; |
1237 | 1237 | } |
1238 | 1238 | |
1239 | +/** | |
1240 | + * e1000_request_irq - initialize interrupts | |
1241 | + * | |
1242 | + * Attempts to configure interrupts using the best available | |
1243 | + * capabilities of the hardware and kernel. | |
1244 | + **/ | |
1239 | 1245 | static int e1000_request_irq(struct e1000_adapter *adapter) |
1240 | 1246 | { |
1241 | 1247 | struct net_device *netdev = adapter->netdev; |
1242 | - irq_handler_t handler = e1000_intr; | |
1243 | 1248 | int irq_flags = IRQF_SHARED; |
1244 | 1249 | int err; |
1245 | 1250 | |
1246 | - if (!pci_enable_msi(adapter->pdev)) { | |
1247 | - adapter->flags |= FLAG_MSI_ENABLED; | |
1248 | - handler = e1000_intr_msi; | |
1249 | - irq_flags = 0; | |
1251 | + if (!(adapter->flags & FLAG_MSI_TEST_FAILED)) { | |
1252 | + err = pci_enable_msi(adapter->pdev); | |
1253 | + if (!err) { | |
1254 | + adapter->flags |= FLAG_MSI_ENABLED; | |
1255 | + irq_flags = 0; | |
1256 | + } | |
1250 | 1257 | } |
1251 | 1258 | |
1252 | - err = request_irq(adapter->pdev->irq, handler, irq_flags, netdev->name, | |
1253 | - netdev); | |
1259 | + err = request_irq(adapter->pdev->irq, | |
1260 | + ((adapter->flags & FLAG_MSI_ENABLED) ? | |
1261 | + &e1000_intr_msi : &e1000_intr), | |
1262 | + irq_flags, netdev->name, netdev); | |
1254 | 1263 | if (err) { |
1255 | - e_err("Unable to allocate %s interrupt (return: %d)\n", | |
1256 | - adapter->flags & FLAG_MSI_ENABLED ? "MSI":"INTx", err); | |
1257 | - if (adapter->flags & FLAG_MSI_ENABLED) | |
1264 | + if (adapter->flags & FLAG_MSI_ENABLED) { | |
1258 | 1265 | pci_disable_msi(adapter->pdev); |
1266 | + adapter->flags &= ~FLAG_MSI_ENABLED; | |
1267 | + } | |
1268 | + e_err("Unable to allocate interrupt, Error: %d\n", err); | |
1259 | 1269 | } |
1260 | 1270 | |
1261 | 1271 | return err; |
... | ... | @@ -2595,6 +2605,135 @@ |
2595 | 2605 | } |
2596 | 2606 | |
2597 | 2607 | /** |
2608 | + * e1000_intr_msi_test - Interrupt Handler | |
2609 | + * @irq: interrupt number | |
2610 | + * @data: pointer to a network interface device structure | |
2611 | + **/ | |
2612 | +static irqreturn_t e1000_intr_msi_test(int irq, void *data) | |
2613 | +{ | |
2614 | + struct net_device *netdev = data; | |
2615 | + struct e1000_adapter *adapter = netdev_priv(netdev); | |
2616 | + struct e1000_hw *hw = &adapter->hw; | |
2617 | + u32 icr = er32(ICR); | |
2618 | + | |
2619 | + e_dbg("%s: icr is %08X\n", netdev->name, icr); | |
2620 | + if (icr & E1000_ICR_RXSEQ) { | |
2621 | + adapter->flags &= ~FLAG_MSI_TEST_FAILED; | |
2622 | + wmb(); | |
2623 | + } | |
2624 | + | |
2625 | + return IRQ_HANDLED; | |
2626 | +} | |
2627 | + | |
2628 | +/** | |
2629 | + * e1000_test_msi_interrupt - Returns 0 for successful test | |
2630 | + * @adapter: board private struct | |
2631 | + * | |
2632 | + * code flow taken from tg3.c | |
2633 | + **/ | |
2634 | +static int e1000_test_msi_interrupt(struct e1000_adapter *adapter) | |
2635 | +{ | |
2636 | + struct net_device *netdev = adapter->netdev; | |
2637 | + struct e1000_hw *hw = &adapter->hw; | |
2638 | + int err; | |
2639 | + | |
2640 | + /* poll_enable hasn't been called yet, so don't need disable */ | |
2641 | + /* clear any pending events */ | |
2642 | + er32(ICR); | |
2643 | + | |
2644 | + /* free the real vector and request a test handler */ | |
2645 | + e1000_free_irq(adapter); | |
2646 | + | |
2647 | + /* Assume that the test fails, if it succeeds then the test | |
2648 | + * MSI irq handler will unset this flag */ | |
2649 | + adapter->flags |= FLAG_MSI_TEST_FAILED; | |
2650 | + | |
2651 | + err = pci_enable_msi(adapter->pdev); | |
2652 | + if (err) | |
2653 | + goto msi_test_failed; | |
2654 | + | |
2655 | + err = request_irq(adapter->pdev->irq, &e1000_intr_msi_test, 0, | |
2656 | + netdev->name, netdev); | |
2657 | + if (err) { | |
2658 | + pci_disable_msi(adapter->pdev); | |
2659 | + goto msi_test_failed; | |
2660 | + } | |
2661 | + | |
2662 | + wmb(); | |
2663 | + | |
2664 | + e1000_irq_enable(adapter); | |
2665 | + | |
2666 | + /* fire an unusual interrupt on the test handler */ | |
2667 | + ew32(ICS, E1000_ICS_RXSEQ); | |
2668 | + e1e_flush(); | |
2669 | + msleep(50); | |
2670 | + | |
2671 | + e1000_irq_disable(adapter); | |
2672 | + | |
2673 | + rmb(); | |
2674 | + | |
2675 | + if (adapter->flags & FLAG_MSI_TEST_FAILED) { | |
2676 | + err = -EIO; | |
2677 | + e_info("MSI interrupt test failed!\n"); | |
2678 | + } | |
2679 | + | |
2680 | + free_irq(adapter->pdev->irq, netdev); | |
2681 | + pci_disable_msi(adapter->pdev); | |
2682 | + | |
2683 | + if (err == -EIO) | |
2684 | + goto msi_test_failed; | |
2685 | + | |
2686 | + /* okay so the test worked, restore settings */ | |
2687 | + e_dbg("%s: MSI interrupt test succeeded!\n", netdev->name); | |
2688 | +msi_test_failed: | |
2689 | + /* restore the original vector, even if it failed */ | |
2690 | + e1000_request_irq(adapter); | |
2691 | + return err; | |
2692 | +} | |
2693 | + | |
2694 | +/** | |
2695 | + * e1000_test_msi - Returns 0 if MSI test succeeds or INTx mode is restored | |
2696 | + * @adapter: board private struct | |
2697 | + * | |
2698 | + * code flow taken from tg3.c, called with e1000 interrupts disabled. | |
2699 | + **/ | |
2700 | +static int e1000_test_msi(struct e1000_adapter *adapter) | |
2701 | +{ | |
2702 | + int err; | |
2703 | + u16 pci_cmd; | |
2704 | + | |
2705 | + if (!(adapter->flags & FLAG_MSI_ENABLED)) | |
2706 | + return 0; | |
2707 | + | |
2708 | + /* disable SERR in case the MSI write causes a master abort */ | |
2709 | + pci_read_config_word(adapter->pdev, PCI_COMMAND, &pci_cmd); | |
2710 | + pci_write_config_word(adapter->pdev, PCI_COMMAND, | |
2711 | + pci_cmd & ~PCI_COMMAND_SERR); | |
2712 | + | |
2713 | + err = e1000_test_msi_interrupt(adapter); | |
2714 | + | |
2715 | + /* restore previous setting of command word */ | |
2716 | + pci_write_config_word(adapter->pdev, PCI_COMMAND, pci_cmd); | |
2717 | + | |
2718 | + /* success ! */ | |
2719 | + if (!err) | |
2720 | + return 0; | |
2721 | + | |
2722 | + /* EIO means MSI test failed */ | |
2723 | + if (err != -EIO) | |
2724 | + return err; | |
2725 | + | |
2726 | + /* back to INTx mode */ | |
2727 | + e_warn("MSI interrupt test failed, using legacy interrupt.\n"); | |
2728 | + | |
2729 | + e1000_free_irq(adapter); | |
2730 | + | |
2731 | + err = e1000_request_irq(adapter); | |
2732 | + | |
2733 | + return err; | |
2734 | +} | |
2735 | + | |
2736 | +/** | |
2598 | 2737 | * e1000_open - Called when a network interface is made active |
2599 | 2738 | * @netdev: network interface device structure |
2600 | 2739 | * |
... | ... | @@ -2651,6 +2790,19 @@ |
2651 | 2790 | err = e1000_request_irq(adapter); |
2652 | 2791 | if (err) |
2653 | 2792 | goto err_req_irq; |
2793 | + | |
2794 | + /* | |
2795 | + * Work around PCIe errata with MSI interrupts causing some chipsets to | |
2796 | + * ignore e1000e MSI messages, which means we need to test our MSI | |
2797 | + * interrupt now | |
2798 | + */ | |
2799 | + { | |
2800 | + err = e1000_test_msi(adapter); | |
2801 | + if (err) { | |
2802 | + e_err("Interrupt allocation failed\n"); | |
2803 | + goto err_req_irq; | |
2804 | + } | |
2805 | + } | |
2654 | 2806 | |
2655 | 2807 | /* From here on the code is the same as e1000e_up() */ |
2656 | 2808 | clear_bit(__E1000_DOWN, &adapter->state); |