linux/drivers/net/wireless
Johannes Berg f0f74a0e65 iwlwifi: fix PS disable status race
iwlwifi internally needs to keep track of whether PS
is enabled in the firmware or not. To do this, it keeps
a bit in the status flags, called STATUS_POWER_PMI.

The code to set this bit looks as follows:

static int iwl_set_power(struct iwl_priv *priv, void *cmd)
{
	return iwl_send_cmd_pdu_async(priv, POWER_TABLE_CMD,
				      sizeof(struct iwl_powertable_cmd),
				      cmd, NULL);
}

int iwl_power_update_mode(...)
{
	[...]
	if (final_mode != IWL_POWER_MODE_CAM)
		set_bit(STATUS_POWER_PMI, &priv->status);

	iwl_update_power_cmd(priv, &cmd, final_mode);
	cmd.keep_alive_beacons = 0;

	if (final_mode == IWL_POWER_INDEX_5)
		cmd.flags |= IWL_POWER_FAST_PD;

	ret = iwl_set_power(priv, &cmd);

	if (final_mode == IWL_POWER_MODE_CAM)
		clear_bit(STATUS_POWER_PMI, &priv->status);
	else
		set_bit(STATUS_POWER_PMI, &priv->status);

	if (priv->cfg->ops->lib->update_chain_flags && update_chains)
		priv->cfg->ops->lib->update_chain_flags(priv);
	[...]
}

Now, this bit really needs to track what the _firmware_
thinks, not what the driver thinks. Therefore, there is
a race condition here -- the driver sets the bit before
it knows that the async command sent to the card in the
iwl_set_power function has been processed. As a result,
the call to update_chain_flags() may think that the card
has been woken up (PMI bit cleared) while in reality it
hasn't processed the async POWER_TABLE_CMD yet.

This leads to bugs -- any commands the update_chain_flags
function sends can get stuck and subsequent commands also
fail.

The fix is almost trivial: since there's no reason to send
an async command here (in fact, there almost never should
be since many mac80211 callbacks can sleep) just make the
function wait for the card to process the command and then
return and clear the PMI bit.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Acked-by: Mohamed Abbas <mohamed.abbas@intel.com>
Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-05-11 15:23:58 -04:00
..
ath ath9k: remove redundant AR9285 checks 2009-05-06 15:15:06 -04:00
b43 mac80211: unify config_interface and bss_info_changed 2009-05-06 15:14:36 -04:00
b43legacy mac80211: unify config_interface and bss_info_changed 2009-05-06 15:14:36 -04:00
hostap wireless: remove some (bogus?) 'may be used uninitialized' warnings 2009-04-24 15:41:41 -04:00
ipw2x00 Wireless: remove driver_data direct access of struct device 2009-05-06 15:15:01 -04:00
iwlwifi iwlwifi: fix PS disable status race 2009-05-11 15:23:58 -04:00
libertas libertas: fix format warning 2009-04-24 15:41:37 -04:00
libertas_tf mac80211: unify config_interface and bss_info_changed 2009-05-06 15:14:36 -04:00
orinoco
p54 p54: call p54_wake_free_queues on every p54_free_skb and p54_rx_frame_sent 2009-05-06 15:15:05 -04:00
prism54
rt2x00 rt2x00: Fix chipset detection for rt73usb 2009-05-11 15:23:56 -04:00
rtl818x mac80211: unify config_interface and bss_info_changed 2009-05-06 15:14:36 -04:00
wl12xx wireless: WL12XX should depend on GENERIC_HARDIRQS 2009-05-11 15:23:54 -04:00
zd1211rw mac80211: unify config_interface and bss_info_changed 2009-05-06 15:14:36 -04:00
adm8211.c mac80211: unify config_interface and bss_info_changed 2009-05-06 15:14:36 -04:00
adm8211.h
airo.c
airo.h
airo_cs.c
arlan-main.c
arlan-proc.c
arlan.h
at76c50x-usb.c mac80211: unify config_interface and bss_info_changed 2009-05-06 15:14:36 -04:00
at76c50x-usb.h
atmel.c
atmel.h
atmel_cs.c Wireless: remove driver_data direct access of struct device 2009-05-06 15:15:01 -04:00
atmel_pci.c
i82586.h
i82593.h
Kconfig wl12xx: add driver 2009-05-06 15:14:54 -04:00
mac80211_hwsim.c mac80211: move HT operation mode BSS info 2009-05-11 15:23:57 -04:00
Makefile wl12xx: add driver 2009-05-06 15:14:54 -04:00
mwl8k.c mac80211: move HT operation mode BSS info 2009-05-11 15:23:57 -04:00
netwave_cs.c
ray_cs.c
ray_cs.h
rayctl.h
rndis_wlan.c rndis_wlan: fix initialization order for workqueue&workers 2009-04-28 15:59:48 -04:00
strip.c
wavelan.c
wavelan.h
wavelan.p.h
wavelan_cs.c
wavelan_cs.h
wavelan_cs.p.h
wl3501.h
wl3501_cs.c
zd1201.c
zd1201.h