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 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> Cc: <stable@vger.kernel.org> Acked-by: Ivo van Doorn <IvDoorn@gmail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
c18b7806e4
commit
ed66ba472a
|
@ -943,6 +943,7 @@ struct rt2x00_dev {
|
||||||
* Powersaving work
|
* Powersaving work
|
||||||
*/
|
*/
|
||||||
struct delayed_work autowakeup_work;
|
struct delayed_work autowakeup_work;
|
||||||
|
struct work_struct sleep_work;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Data queue arrays for RX, TX, Beacon and ATIM.
|
* 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;
|
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))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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,
|
||||||
|
IEEE80211_CONF_CHANGE_PS);
|
||||||
|
}
|
||||||
|
|
||||||
static void rt2x00lib_rxdone_check_ps(struct rt2x00_dev *rt2x00dev,
|
static void rt2x00lib_rxdone_check_ps(struct rt2x00_dev *rt2x00dev,
|
||||||
struct sk_buff *skb,
|
struct sk_buff *skb,
|
||||||
struct rxdone_entry_desc *rxdesc)
|
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);
|
cam |= (tim_ie->bitmap_ctrl & 0x01);
|
||||||
|
|
||||||
if (!cam && !test_bit(CONFIG_POWERSAVING, &rt2x00dev->flags))
|
if (!cam && !test_bit(CONFIG_POWERSAVING, &rt2x00dev->flags))
|
||||||
rt2x00lib_config(rt2x00dev, &rt2x00dev->hw->conf,
|
queue_work(rt2x00dev->workqueue, &rt2x00dev->sleep_work);
|
||||||
IEEE80211_CONF_CHANGE_PS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rt2x00lib_rxdone_read_signal(struct rt2x00_dev *rt2x00dev,
|
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_WORK(&rt2x00dev->intf_work, rt2x00lib_intf_scheduled);
|
||||||
INIT_DELAYED_WORK(&rt2x00dev->autowakeup_work, rt2x00lib_autowakeup);
|
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.
|
* Let the driver probe the device to detect the capabilities.
|
||||||
|
@ -1197,6 +1214,7 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev)
|
||||||
*/
|
*/
|
||||||
cancel_work_sync(&rt2x00dev->intf_work);
|
cancel_work_sync(&rt2x00dev->intf_work);
|
||||||
cancel_delayed_work_sync(&rt2x00dev->autowakeup_work);
|
cancel_delayed_work_sync(&rt2x00dev->autowakeup_work);
|
||||||
|
cancel_work_sync(&rt2x00dev->sleep_work);
|
||||||
if (rt2x00_is_usb(rt2x00dev)) {
|
if (rt2x00_is_usb(rt2x00dev)) {
|
||||||
del_timer_sync(&rt2x00dev->txstatus_timer);
|
del_timer_sync(&rt2x00dev->txstatus_timer);
|
||||||
cancel_work_sync(&rt2x00dev->rxdone_work);
|
cancel_work_sync(&rt2x00dev->rxdone_work);
|
||||||
|
|
Loading…
Reference in New Issue