aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/rt2x00/rt2x00dev.c
diff options
context:
space:
mode:
authorGertjan van Wingerde <gwingerde@gmail.com>2011-11-12 13:10:44 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2011-11-26 12:09:54 -0500
commit11885cd854b27c1dcf35ab44d4899e8d20e08290 (patch)
tree9146d37546311d225bde73ccddf12bec243ebeb3 /drivers/net/wireless/rt2x00/rt2x00dev.c
parent78724db116c88705db2d6acbe66c56bed90fc991 (diff)
rt2x00: Fix sleep-while-atomic bug in powersaving code.
commit ed66ba472a742cd8df37d7072804b2111cdb1014 upstream. The generic powersaving code that determines after reception of a frame whether the device should go back to sleep or whether is could stay awake was calling rt2x00lib_config directly from RX tasklet context. On a number of the devices this call can actually sleep, due to having to confirm that the sleeping commands have been executed successfully. Fix this by moving the call to rt2x00lib_config to a workqueue call. This fixes bug https://bugzilla.redhat.com/show_bug.cgi?id=731672 Tested-by: Tomas Trnka <tomastrnka@gmx.com> Signed-off-by: Gertjan van Wingerde <gwingerde@gmail.com> Acked-by: Ivo van Doorn <IvDoorn@gmail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/net/wireless/rt2x00/rt2x00dev.c')
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00dev.c22
1 files changed, 20 insertions, 2 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index 939821b4af2..dffaa8f45f1 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -449,6 +449,23 @@ static u8 *rt2x00lib_find_ie(u8 *data, unsigned int len, u8 ie)
449 return NULL; 449 return NULL;
450} 450}
451 451
452static void rt2x00lib_sleep(struct work_struct *work)
453{
454 struct rt2x00_dev *rt2x00dev =
455 container_of(work, struct rt2x00_dev, sleep_work);
456
457 if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
458 return;
459
460 /*
461 * Check again is powersaving is enabled, to prevent races from delayed
462 * work execution.
463 */
464 if (!test_bit(CONFIG_POWERSAVING, &rt2x00dev->flags))
465 rt2x00lib_config(rt2x00dev, &rt2x00dev->hw->conf,
466 IEEE80211_CONF_CHANGE_PS);
467}
468
452static void rt2x00lib_rxdone_check_ps(struct rt2x00_dev *rt2x00dev, 469static void rt2x00lib_rxdone_check_ps(struct rt2x00_dev *rt2x00dev,
453 struct sk_buff *skb, 470 struct sk_buff *skb,
454 struct rxdone_entry_desc *rxdesc) 471 struct rxdone_entry_desc *rxdesc)
@@ -496,8 +513,7 @@ static void rt2x00lib_rxdone_check_ps(struct rt2x00_dev *rt2x00dev,
496 cam |= (tim_ie->bitmap_ctrl & 0x01); 513 cam |= (tim_ie->bitmap_ctrl & 0x01);
497 514
498 if (!cam && !test_bit(CONFIG_POWERSAVING, &rt2x00dev->flags)) 515 if (!cam && !test_bit(CONFIG_POWERSAVING, &rt2x00dev->flags))
499 rt2x00lib_config(rt2x00dev, &rt2x00dev->hw->conf, 516 queue_work(rt2x00dev->workqueue, &rt2x00dev->sleep_work);
500 IEEE80211_CONF_CHANGE_PS);
501} 517}
502 518
503static int rt2x00lib_rxdone_read_signal(struct rt2x00_dev *rt2x00dev, 519static int rt2x00lib_rxdone_read_signal(struct rt2x00_dev *rt2x00dev,
@@ -1108,6 +1124,7 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
1108 1124
1109 INIT_WORK(&rt2x00dev->intf_work, rt2x00lib_intf_scheduled); 1125 INIT_WORK(&rt2x00dev->intf_work, rt2x00lib_intf_scheduled);
1110 INIT_DELAYED_WORK(&rt2x00dev->autowakeup_work, rt2x00lib_autowakeup); 1126 INIT_DELAYED_WORK(&rt2x00dev->autowakeup_work, rt2x00lib_autowakeup);
1127 INIT_WORK(&rt2x00dev->sleep_work, rt2x00lib_sleep);
1111 1128
1112 /* 1129 /*
1113 * Let the driver probe the device to detect the capabilities. 1130 * Let the driver probe the device to detect the capabilities.
@@ -1164,6 +1181,7 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev)
1164 */ 1181 */
1165 cancel_work_sync(&rt2x00dev->intf_work); 1182 cancel_work_sync(&rt2x00dev->intf_work);
1166 cancel_delayed_work_sync(&rt2x00dev->autowakeup_work); 1183 cancel_delayed_work_sync(&rt2x00dev->autowakeup_work);
1184 cancel_work_sync(&rt2x00dev->sleep_work);
1167 if (rt2x00_is_usb(rt2x00dev)) { 1185 if (rt2x00_is_usb(rt2x00dev)) {
1168 del_timer_sync(&rt2x00dev->txstatus_timer); 1186 del_timer_sync(&rt2x00dev->txstatus_timer);
1169 cancel_work_sync(&rt2x00dev->rxdone_work); 1187 cancel_work_sync(&rt2x00dev->rxdone_work);