diff options
author | Helmut Schaa <helmut.schaa@googlemail.com> | 2011-08-06 07:13:48 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-08-09 15:52:08 -0400 |
commit | abc11994112bf7441519e35f51c29ff5de5b0d4d (patch) | |
tree | 88b11da5cdec1a7a10ff7a388d29b94902c69653 /drivers/net/wireless/rt2x00/rt2500pci.c | |
parent | c3ccb3341ec05444c8374d1829edc5157fc94853 (diff) |
rt2x00: Fix PCI interrupt processing race on SMP systems
When toggle_irq is called for PCI devices to disable device interrupts
it used tasklet_disable to wait for a possibly running tasklet to finish.
However, on SMP systems the tasklet might still be scheduled on another CPU.
Instead, use tasklet_kill to ensure that all scheduled tasklets are finished
before returning from toggle_irq.
Furthermore, it was possible that a tasklet reenabled its interrupt even
though interrupts have been disabled already. Fix this by checking the
DEVICE_STATE_ENABLED_RADIO flag before reenabling single interrupts
during tasklet processing.
While at it also enable/kill the TBTT and PRETBTT tasklets in the
toggle_irq callback and only use tasklet_kill in stop_queue to wait
for a currently scheduled beacon update before returning.
Signed-off-by: Helmut Schaa <helmut.schaa@googlemail.com>
Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/rt2x00/rt2500pci.c')
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2500pci.c | 39 |
1 files changed, 16 insertions, 23 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c index c288d951c034..b46c3b8866fa 100644 --- a/drivers/net/wireless/rt2x00/rt2500pci.c +++ b/drivers/net/wireless/rt2x00/rt2500pci.c | |||
@@ -735,11 +735,6 @@ static void rt2500pci_start_queue(struct data_queue *queue) | |||
735 | rt2x00pci_register_write(rt2x00dev, RXCSR0, reg); | 735 | rt2x00pci_register_write(rt2x00dev, RXCSR0, reg); |
736 | break; | 736 | break; |
737 | case QID_BEACON: | 737 | case QID_BEACON: |
738 | /* | ||
739 | * Allow the tbtt tasklet to be scheduled. | ||
740 | */ | ||
741 | tasklet_enable(&rt2x00dev->tbtt_tasklet); | ||
742 | |||
743 | rt2x00pci_register_read(rt2x00dev, CSR14, ®); | 738 | rt2x00pci_register_read(rt2x00dev, CSR14, ®); |
744 | rt2x00_set_field32(®, CSR14_TSF_COUNT, 1); | 739 | rt2x00_set_field32(®, CSR14_TSF_COUNT, 1); |
745 | rt2x00_set_field32(®, CSR14_TBCN, 1); | 740 | rt2x00_set_field32(®, CSR14_TBCN, 1); |
@@ -805,7 +800,7 @@ static void rt2500pci_stop_queue(struct data_queue *queue) | |||
805 | /* | 800 | /* |
806 | * Wait for possibly running tbtt tasklets. | 801 | * Wait for possibly running tbtt tasklets. |
807 | */ | 802 | */ |
808 | tasklet_disable(&rt2x00dev->tbtt_tasklet); | 803 | tasklet_kill(&rt2x00dev->tbtt_tasklet); |
809 | break; | 804 | break; |
810 | default: | 805 | default: |
811 | break; | 806 | break; |
@@ -1137,12 +1132,6 @@ static void rt2500pci_toggle_irq(struct rt2x00_dev *rt2x00dev, | |||
1137 | if (state == STATE_RADIO_IRQ_ON) { | 1132 | if (state == STATE_RADIO_IRQ_ON) { |
1138 | rt2x00pci_register_read(rt2x00dev, CSR7, ®); | 1133 | rt2x00pci_register_read(rt2x00dev, CSR7, ®); |
1139 | rt2x00pci_register_write(rt2x00dev, CSR7, reg); | 1134 | rt2x00pci_register_write(rt2x00dev, CSR7, reg); |
1140 | |||
1141 | /* | ||
1142 | * Enable tasklets. | ||
1143 | */ | ||
1144 | tasklet_enable(&rt2x00dev->txstatus_tasklet); | ||
1145 | tasklet_enable(&rt2x00dev->rxdone_tasklet); | ||
1146 | } | 1135 | } |
1147 | 1136 | ||
1148 | /* | 1137 | /* |
@@ -1165,8 +1154,9 @@ static void rt2500pci_toggle_irq(struct rt2x00_dev *rt2x00dev, | |||
1165 | /* | 1154 | /* |
1166 | * Ensure that all tasklets are finished. | 1155 | * Ensure that all tasklets are finished. |
1167 | */ | 1156 | */ |
1168 | tasklet_disable(&rt2x00dev->txstatus_tasklet); | 1157 | tasklet_kill(&rt2x00dev->txstatus_tasklet); |
1169 | tasklet_disable(&rt2x00dev->rxdone_tasklet); | 1158 | tasklet_kill(&rt2x00dev->rxdone_tasklet); |
1159 | tasklet_kill(&rt2x00dev->tbtt_tasklet); | ||
1170 | } | 1160 | } |
1171 | } | 1161 | } |
1172 | 1162 | ||
@@ -1479,22 +1469,25 @@ static void rt2500pci_txstatus_tasklet(unsigned long data) | |||
1479 | /* | 1469 | /* |
1480 | * Enable all TXDONE interrupts again. | 1470 | * Enable all TXDONE interrupts again. |
1481 | */ | 1471 | */ |
1482 | spin_lock_irq(&rt2x00dev->irqmask_lock); | 1472 | if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) { |
1473 | spin_lock_irq(&rt2x00dev->irqmask_lock); | ||
1483 | 1474 | ||
1484 | rt2x00pci_register_read(rt2x00dev, CSR8, ®); | 1475 | rt2x00pci_register_read(rt2x00dev, CSR8, ®); |
1485 | rt2x00_set_field32(®, CSR8_TXDONE_TXRING, 0); | 1476 | rt2x00_set_field32(®, CSR8_TXDONE_TXRING, 0); |
1486 | rt2x00_set_field32(®, CSR8_TXDONE_ATIMRING, 0); | 1477 | rt2x00_set_field32(®, CSR8_TXDONE_ATIMRING, 0); |
1487 | rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, 0); | 1478 | rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, 0); |
1488 | rt2x00pci_register_write(rt2x00dev, CSR8, reg); | 1479 | rt2x00pci_register_write(rt2x00dev, CSR8, reg); |
1489 | 1480 | ||
1490 | spin_unlock_irq(&rt2x00dev->irqmask_lock); | 1481 | spin_unlock_irq(&rt2x00dev->irqmask_lock); |
1482 | } | ||
1491 | } | 1483 | } |
1492 | 1484 | ||
1493 | static void rt2500pci_tbtt_tasklet(unsigned long data) | 1485 | static void rt2500pci_tbtt_tasklet(unsigned long data) |
1494 | { | 1486 | { |
1495 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | 1487 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; |
1496 | rt2x00lib_beacondone(rt2x00dev); | 1488 | rt2x00lib_beacondone(rt2x00dev); |
1497 | rt2500pci_enable_interrupt(rt2x00dev, CSR8_TBCN_EXPIRE); | 1489 | if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) |
1490 | rt2500pci_enable_interrupt(rt2x00dev, CSR8_TBCN_EXPIRE); | ||
1498 | } | 1491 | } |
1499 | 1492 | ||
1500 | static void rt2500pci_rxdone_tasklet(unsigned long data) | 1493 | static void rt2500pci_rxdone_tasklet(unsigned long data) |
@@ -1502,7 +1495,7 @@ static void rt2500pci_rxdone_tasklet(unsigned long data) | |||
1502 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | 1495 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; |
1503 | if (rt2x00pci_rxdone(rt2x00dev)) | 1496 | if (rt2x00pci_rxdone(rt2x00dev)) |
1504 | tasklet_schedule(&rt2x00dev->rxdone_tasklet); | 1497 | tasklet_schedule(&rt2x00dev->rxdone_tasklet); |
1505 | else | 1498 | else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) |
1506 | rt2500pci_enable_interrupt(rt2x00dev, CSR8_RXDONE); | 1499 | rt2500pci_enable_interrupt(rt2x00dev, CSR8_RXDONE); |
1507 | } | 1500 | } |
1508 | 1501 | ||