Commit ed66ba47 authored by Gertjan van Wingerde's avatar Gertjan van Wingerde Committed by John W. Linville
Browse files

rt2x00: Fix sleep-while-atomic bug in powersaving code.

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

Tested-by: default avatarTomas Trnka <>
Signed-off-by: default avatarGertjan van Wingerde <>
Cc: <>
Acked-by: default avatarIvo van Doorn <>
Signed-off-by: default avatarJohn W. Linville <>
parent c18b7806
......@@ -943,6 +943,7 @@ struct rt2x00_dev {
* Powersaving work
struct delayed_work autowakeup_work;
struct work_struct sleep_work;
* Data queue arrays for RX, TX, Beacon and ATIM.
......@@ -465,6 +465,23 @@ static u8 *rt2x00lib_find_ie(u8 *data, unsigned int len, u8 ie)
return NULL;
static void rt2x00lib_sleep(struct work_struct *work)
struct rt2x00_dev *rt2x00dev =
container_of(work, struct rt2x00_dev, sleep_work);
if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
* Check again is powersaving is enabled, to prevent races from delayed
* work execution.
if (!test_bit(CONFIG_POWERSAVING, &rt2x00dev->flags))
rt2x00lib_config(rt2x00dev, &rt2x00dev->hw->conf,
static void rt2x00lib_rxdone_check_ps(struct rt2x00_dev *rt2x00dev,
struct sk_buff *skb,
struct rxdone_entry_desc *rxdesc)
......@@ -512,8 +529,7 @@ static void rt2x00lib_rxdone_check_ps(struct rt2x00_dev *rt2x00dev,
cam |= (tim_ie->bitmap_ctrl & 0x01);
if (!cam && !test_bit(CONFIG_POWERSAVING, &rt2x00dev->flags))
rt2x00lib_config(rt2x00dev, &rt2x00dev->hw->conf,
queue_work(rt2x00dev->workqueue, &rt2x00dev->sleep_work);
static int rt2x00lib_rxdone_read_signal(struct rt2x00_dev *rt2x00dev,
......@@ -1141,6 +1157,7 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
INIT_WORK(&rt2x00dev->intf_work, rt2x00lib_intf_scheduled);
INIT_DELAYED_WORK(&rt2x00dev->autowakeup_work, rt2x00lib_autowakeup);
INIT_WORK(&rt2x00dev->sleep_work, rt2x00lib_sleep);
* Let the driver probe the device to detect the capabilities.
......@@ -1197,6 +1214,7 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev)
if (rt2x00_is_usb(rt2x00dev)) {
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment