diff options
author | Dean Nelson <dnelson@redhat.com> | 2010-06-29 14:12:05 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-06-30 02:09:18 -0400 |
commit | 36f2407fe52c55566221f8c68c8fb808abffd2f5 (patch) | |
tree | 6de3622f8c40351e801015389d3809224055a6b8 | |
parent | 6c057573f21db0ef85f78318875269a2159af35c (diff) |
e1000e: don't inadvertently re-set INTX_DISABLE
Should e1000_test_msi() fail to see an msi interrupt, it attempts to
fallback to legacy INTx interrupts. But an error in the code may prevent
this from happening correctly.
Before calling e1000_test_msi_interrupt(), e1000_test_msi() disables SERR
by clearing the SERR bit from the just read PCI_COMMAND bits as it writes
them back out.
Upon return from calling e1000_test_msi_interrupt(), it re-enables SERR
by writing out the version of PCI_COMMAND it had previously read.
The problem with this is that e1000_test_msi_interrupt() calls
pci_disable_msi(), which eventually ends up in pci_intx(). And because
pci_intx() was called with enable set to 1, the INTX_DISABLE bit gets
cleared from PCI_COMMAND, which is what we want. But when we get back to
e1000_test_msi(), the INTX_DISABLE bit gets inadvertently re-set because
of the attempt by e1000_test_msi() to re-enable SERR.
The solution is to have e1000_test_msi() re-read the PCI_COMMAND bits as
part of its attempt to re-enable SERR.
During debugging/testing of this issue I found that not all the systems
I ran on had the SERR bit set to begin with. And on some of the systems
the same could be said for the INTX_DISABLE bit. Needless to say these
latter systems didn't have a problem falling back to legacy INTx
interrupts with the code as is.
Signed-off-by: Dean Nelson <dnelson@redhat.com>
CC: stable@kernel.org
Tested-by: Emil Tantilov <emil.s.tantilov@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/e1000e/netdev.c | 13 |
1 files changed, 9 insertions, 4 deletions
diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index 71592ed2e686..3e53ca723435 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c | |||
@@ -3439,13 +3439,18 @@ static int e1000_test_msi(struct e1000_adapter *adapter) | |||
3439 | 3439 | ||
3440 | /* disable SERR in case the MSI write causes a master abort */ | 3440 | /* disable SERR in case the MSI write causes a master abort */ |
3441 | pci_read_config_word(adapter->pdev, PCI_COMMAND, &pci_cmd); | 3441 | pci_read_config_word(adapter->pdev, PCI_COMMAND, &pci_cmd); |
3442 | pci_write_config_word(adapter->pdev, PCI_COMMAND, | 3442 | if (pci_cmd & PCI_COMMAND_SERR) |
3443 | pci_cmd & ~PCI_COMMAND_SERR); | 3443 | pci_write_config_word(adapter->pdev, PCI_COMMAND, |
3444 | pci_cmd & ~PCI_COMMAND_SERR); | ||
3444 | 3445 | ||
3445 | err = e1000_test_msi_interrupt(adapter); | 3446 | err = e1000_test_msi_interrupt(adapter); |
3446 | 3447 | ||
3447 | /* restore previous setting of command word */ | 3448 | /* re-enable SERR */ |
3448 | pci_write_config_word(adapter->pdev, PCI_COMMAND, pci_cmd); | 3449 | if (pci_cmd & PCI_COMMAND_SERR) { |
3450 | pci_read_config_word(adapter->pdev, PCI_COMMAND, &pci_cmd); | ||
3451 | pci_cmd |= PCI_COMMAND_SERR; | ||
3452 | pci_write_config_word(adapter->pdev, PCI_COMMAND, pci_cmd); | ||
3453 | } | ||
3449 | 3454 | ||
3450 | /* success ! */ | 3455 | /* success ! */ |
3451 | if (!err) | 3456 | if (!err) |