diff options
| author | John Greene <jogreene@redhat.com> | 2012-12-19 04:47:48 -0500 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2012-12-19 17:30:59 -0500 |
| commit | 83c34fd00d0c3989466e95808bf12af9bf87e383 (patch) | |
| tree | 477e1bde4443e5708f4c1c7885c9c552dcd7d4d6 /drivers/net/ethernet | |
| parent | f8b840344cbf4fa7212223b436adfb7559ca0e1e (diff) | |
8139cp: Prevent dev_close/cp_interrupt race on MTU change
commit: cb64edb6b89491edfdbae52ba7db9a8b8391d339 upstream
Above commit may introduce a race between cp_interrupt and dev_close
/ change MTU / dev_open up state. Changes cp_interrupt to tolerate
this. Change spin_locking in cp_interrupt to avoid possible
but unobserved race.
Reported-by: "Francois Romieu" <romieu@fr.zoreil.com>
Tested on virtual hardware, Tx MTU size up to 4096, max tx payload
was ping -s 4068 for MTU of 4096. No real hardware, need test
assist.
Signed-off-by: "John Greene" <jogreene@redhat.com>
CC: "David S. Miller" <davem@davemloft.net>
CC: "David Woodhouse" <David.Woodhouse@intel.com>
Tested-by: David Woodhouse <David.Woodhouse@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet')
| -rw-r--r-- | drivers/net/ethernet/realtek/8139cp.c | 18 |
1 files changed, 11 insertions, 7 deletions
diff --git a/drivers/net/ethernet/realtek/8139cp.c b/drivers/net/ethernet/realtek/8139cp.c index cb6fc5a743c..5ac93323a40 100644 --- a/drivers/net/ethernet/realtek/8139cp.c +++ b/drivers/net/ethernet/realtek/8139cp.c | |||
| @@ -577,28 +577,30 @@ static irqreturn_t cp_interrupt (int irq, void *dev_instance) | |||
| 577 | { | 577 | { |
| 578 | struct net_device *dev = dev_instance; | 578 | struct net_device *dev = dev_instance; |
| 579 | struct cp_private *cp; | 579 | struct cp_private *cp; |
| 580 | int handled = 0; | ||
| 580 | u16 status; | 581 | u16 status; |
| 581 | 582 | ||
| 582 | if (unlikely(dev == NULL)) | 583 | if (unlikely(dev == NULL)) |
| 583 | return IRQ_NONE; | 584 | return IRQ_NONE; |
| 584 | cp = netdev_priv(dev); | 585 | cp = netdev_priv(dev); |
| 585 | 586 | ||
| 587 | spin_lock(&cp->lock); | ||
| 588 | |||
| 586 | status = cpr16(IntrStatus); | 589 | status = cpr16(IntrStatus); |
| 587 | if (!status || (status == 0xFFFF)) | 590 | if (!status || (status == 0xFFFF)) |
| 588 | return IRQ_NONE; | 591 | goto out_unlock; |
| 592 | |||
| 593 | handled = 1; | ||
| 589 | 594 | ||
| 590 | netif_dbg(cp, intr, dev, "intr, status %04x cmd %02x cpcmd %04x\n", | 595 | netif_dbg(cp, intr, dev, "intr, status %04x cmd %02x cpcmd %04x\n", |
| 591 | status, cpr8(Cmd), cpr16(CpCmd)); | 596 | status, cpr8(Cmd), cpr16(CpCmd)); |
| 592 | 597 | ||
| 593 | cpw16(IntrStatus, status & ~cp_rx_intr_mask); | 598 | cpw16(IntrStatus, status & ~cp_rx_intr_mask); |
| 594 | 599 | ||
| 595 | spin_lock(&cp->lock); | ||
| 596 | |||
| 597 | /* close possible race's with dev_close */ | 600 | /* close possible race's with dev_close */ |
| 598 | if (unlikely(!netif_running(dev))) { | 601 | if (unlikely(!netif_running(dev))) { |
| 599 | cpw16(IntrMask, 0); | 602 | cpw16(IntrMask, 0); |
| 600 | spin_unlock(&cp->lock); | 603 | goto out_unlock; |
| 601 | return IRQ_HANDLED; | ||
| 602 | } | 604 | } |
| 603 | 605 | ||
| 604 | if (status & (RxOK | RxErr | RxEmpty | RxFIFOOvr)) | 606 | if (status & (RxOK | RxErr | RxEmpty | RxFIFOOvr)) |
| @@ -612,7 +614,6 @@ static irqreturn_t cp_interrupt (int irq, void *dev_instance) | |||
| 612 | if (status & LinkChg) | 614 | if (status & LinkChg) |
| 613 | mii_check_media(&cp->mii_if, netif_msg_link(cp), false); | 615 | mii_check_media(&cp->mii_if, netif_msg_link(cp), false); |
| 614 | 616 | ||
| 615 | spin_unlock(&cp->lock); | ||
| 616 | 617 | ||
| 617 | if (status & PciErr) { | 618 | if (status & PciErr) { |
| 618 | u16 pci_status; | 619 | u16 pci_status; |
| @@ -625,7 +626,10 @@ static irqreturn_t cp_interrupt (int irq, void *dev_instance) | |||
| 625 | /* TODO: reset hardware */ | 626 | /* TODO: reset hardware */ |
| 626 | } | 627 | } |
| 627 | 628 | ||
| 628 | return IRQ_HANDLED; | 629 | out_unlock: |
| 630 | spin_unlock(&cp->lock); | ||
| 631 | |||
| 632 | return IRQ_RETVAL(handled); | ||
| 629 | } | 633 | } |
| 630 | 634 | ||
| 631 | #ifdef CONFIG_NET_POLL_CONTROLLER | 635 | #ifdef CONFIG_NET_POLL_CONTROLLER |
