diff options
author | Gertjan van Wingerde <gwingerde@gmail.com> | 2011-11-12 13:10:44 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-11-26 12:09:54 -0500 |
commit | 11885cd854b27c1dcf35ab44d4899e8d20e08290 (patch) | |
tree | 9146d37546311d225bde73ccddf12bec243ebeb3 /drivers/net/wireless/rt2x00/rt2x00dev.c | |
parent | 78724db116c88705db2d6acbe66c56bed90fc991 (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.c | 22 |
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 | ||
452 | static 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 | |||
452 | static void rt2x00lib_rxdone_check_ps(struct rt2x00_dev *rt2x00dev, | 469 | static 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 | ||
503 | static int rt2x00lib_rxdone_read_signal(struct rt2x00_dev *rt2x00dev, | 519 | static 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); |