iwlwifi: fix PS disable status race
authorJohannes Berg <johannes@sipsolutions.net>
Fri, 8 May 2009 20:44:37 +0000 (13:44 -0700)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 11 May 2009 19:23:58 +0000 (15:23 -0400)
commitf0f74a0e65a69d4cc579e60f76a30b38ad7dbe06
treef2410b5c6c8384d77c6eeec96d0d456ef7773176
parent19cc10870ece942d287241937944c237130f50f4
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>
drivers/net/wireless/iwlwifi/iwl-power.c