aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/rt2x00/rt2500pci.c
diff options
context:
space:
mode:
authorHelmut Schaa <helmut.schaa@googlemail.com>2011-08-06 07:13:48 -0400
committerJohn W. Linville <linville@tuxdriver.com>2011-08-09 15:52:08 -0400
commitabc11994112bf7441519e35f51c29ff5de5b0d4d (patch)
tree88b11da5cdec1a7a10ff7a388d29b94902c69653 /drivers/net/wireless/rt2x00/rt2500pci.c
parentc3ccb3341ec05444c8374d1829edc5157fc94853 (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.c39
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, &reg); 738 rt2x00pci_register_read(rt2x00dev, CSR14, &reg);
744 rt2x00_set_field32(&reg, CSR14_TSF_COUNT, 1); 739 rt2x00_set_field32(&reg, CSR14_TSF_COUNT, 1);
745 rt2x00_set_field32(&reg, CSR14_TBCN, 1); 740 rt2x00_set_field32(&reg, 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, &reg); 1133 rt2x00pci_register_read(rt2x00dev, CSR7, &reg);
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, &reg); 1475 rt2x00pci_register_read(rt2x00dev, CSR8, &reg);
1485 rt2x00_set_field32(&reg, CSR8_TXDONE_TXRING, 0); 1476 rt2x00_set_field32(&reg, CSR8_TXDONE_TXRING, 0);
1486 rt2x00_set_field32(&reg, CSR8_TXDONE_ATIMRING, 0); 1477 rt2x00_set_field32(&reg, CSR8_TXDONE_ATIMRING, 0);
1487 rt2x00_set_field32(&reg, CSR8_TXDONE_PRIORING, 0); 1478 rt2x00_set_field32(&reg, 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
1493static void rt2500pci_tbtt_tasklet(unsigned long data) 1485static 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
1500static void rt2500pci_rxdone_tasklet(unsigned long data) 1493static 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