Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6f213ff1 authored by Stanislaw Gruszka's avatar Stanislaw Gruszka Committed by John W. Linville
Browse files

iwlagn: fix channel switch locking



We use priv->mutex to avoid race conditions between iwl_chswitch_done()
and iwlagn_mac_channel_switch(), when marking channel switch in
progress. But iwl_chswitch_done() can be called in atomic context
from iwl_rx_csa() or with mutex already taken from iwlagn_commit_rxon().

These bugs were introduced by:

commit 79d07325
Author: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Date:   Thu May 6 08:54:11 2010 -0700

    iwlwifi: support channel switch offload in driver

To fix remove mutex from iwl_chswitch_done() and use atomic bitops for
marking channel switch pending.

Also remove iwl2030_hw_channel_switch() since 2000 series adapters are
2.4GHz only devices.

Cc: stable@kernel.org # 2.6.36+
Signed-off-by: default avatarStanislaw Gruszka <sgruszka@redhat.com>
Acked-by: default avatarWey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent a99168ee
Loading
Loading
Loading
Loading
+0 −74
Original line number Original line Diff line number Diff line
@@ -177,79 +177,6 @@ static int iwl2000_hw_set_hw_params(struct iwl_priv *priv)
	return 0;
	return 0;
}
}


static int iwl2030_hw_channel_switch(struct iwl_priv *priv,
                                    struct ieee80211_channel_switch *ch_switch)
{
	/*
	 * MULTI-FIXME
	 * See iwl_mac_channel_switch.
	 */
	struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
	struct iwl6000_channel_switch_cmd cmd;
	const struct iwl_channel_info *ch_info;
	u32 switch_time_in_usec, ucode_switch_time;
	u16 ch;
	u32 tsf_low;
	u8 switch_count;
	u16 beacon_interval = le16_to_cpu(ctx->timing.beacon_interval);
	struct ieee80211_vif *vif = ctx->vif;
	struct iwl_host_cmd hcmd = {
		.id = REPLY_CHANNEL_SWITCH,
		.len = { sizeof(cmd), },
		.flags = CMD_SYNC,
		.data = { &cmd, },
	};

	cmd.band = priv->band == IEEE80211_BAND_2GHZ;
	ch = ch_switch->channel->hw_value;
	IWL_DEBUG_11H(priv, "channel switch from %u to %u\n",
		ctx->active.channel, ch);
	cmd.channel = cpu_to_le16(ch);
	cmd.rxon_flags = ctx->staging.flags;
	cmd.rxon_filter_flags = ctx->staging.filter_flags;
	switch_count = ch_switch->count;
	tsf_low = ch_switch->timestamp & 0x0ffffffff;
	/*
	 * calculate the ucode channel switch time
	 * adding TSF as one of the factor for when to switch
	 */
	if ((priv->ucode_beacon_time > tsf_low) && beacon_interval) {
		if (switch_count > ((priv->ucode_beacon_time - tsf_low) /
		    beacon_interval)) {
			switch_count -= (priv->ucode_beacon_time -
				tsf_low) / beacon_interval;
		} else
			switch_count = 0;
	}
	if (switch_count <= 1)
		cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time);
	else {
		switch_time_in_usec =
			vif->bss_conf.beacon_int * switch_count * TIME_UNIT;
		ucode_switch_time = iwl_usecs_to_beacons(priv,
						switch_time_in_usec,
						beacon_interval);
		cmd.switch_time = iwl_add_beacon_time(priv,
						priv->ucode_beacon_time,
						ucode_switch_time,
						beacon_interval);
	}
	IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n",
		      cmd.switch_time);
	ch_info = iwl_get_channel_info(priv, priv->band, ch);
	if (ch_info)
		cmd.expect_beacon = is_channel_radar(ch_info);
	else {
		IWL_ERR(priv, "invalid channel switch from %u to %u\n",
			ctx->active.channel, ch);
		return -EFAULT;
	}
	priv->switch_rxon.channel = cmd.channel;
	priv->switch_rxon.switch_in_progress = true;

	return iwl_send_cmd_sync(priv, &hcmd);
}

static struct iwl_lib_ops iwl2000_lib = {
static struct iwl_lib_ops iwl2000_lib = {
	.set_hw_params = iwl2000_hw_set_hw_params,
	.set_hw_params = iwl2000_hw_set_hw_params,
	.rx_handler_setup = iwlagn_rx_handler_setup,
	.rx_handler_setup = iwlagn_rx_handler_setup,
@@ -258,7 +185,6 @@ static struct iwl_lib_ops iwl2000_lib = {
	.is_valid_rtc_data_addr = iwlagn_hw_valid_rtc_data_addr,
	.is_valid_rtc_data_addr = iwlagn_hw_valid_rtc_data_addr,
	.send_tx_power = iwlagn_send_tx_power,
	.send_tx_power = iwlagn_send_tx_power,
	.update_chain_flags = iwl_update_chain_flags,
	.update_chain_flags = iwl_update_chain_flags,
	.set_channel_switch = iwl2030_hw_channel_switch,
	.apm_ops = {
	.apm_ops = {
		.init = iwl_apm_init,
		.init = iwl_apm_init,
		.config = iwl2000_nic_config,
		.config = iwl2000_nic_config,
+0 −2
Original line number Original line Diff line number Diff line
@@ -331,8 +331,6 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv,
			ctx->active.channel, ch);
			ctx->active.channel, ch);
		return -EFAULT;
		return -EFAULT;
	}
	}
	priv->switch_rxon.channel = cmd.channel;
	priv->switch_rxon.switch_in_progress = true;


	return iwl_send_cmd_sync(priv, &hcmd);
	return iwl_send_cmd_sync(priv, &hcmd);
}
}
+0 −2
Original line number Original line Diff line number Diff line
@@ -270,8 +270,6 @@ static int iwl6000_hw_channel_switch(struct iwl_priv *priv,
			ctx->active.channel, ch);
			ctx->active.channel, ch);
		return -EFAULT;
		return -EFAULT;
	}
	}
	priv->switch_rxon.channel = cmd.channel;
	priv->switch_rxon.switch_in_progress = true;


	return iwl_send_cmd_sync(priv, &hcmd);
	return iwl_send_cmd_sync(priv, &hcmd);
}
}
+3 −3
Original line number Original line Diff line number Diff line
@@ -342,10 +342,10 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
	 * receive commit_rxon request
	 * receive commit_rxon request
	 * abort any previous channel switch if still in process
	 * abort any previous channel switch if still in process
	 */
	 */
	if (priv->switch_rxon.switch_in_progress &&
	if (test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status) &&
	    (priv->switch_rxon.channel != ctx->staging.channel)) {
	    (priv->switch_channel != ctx->staging.channel)) {
		IWL_DEBUG_11H(priv, "abort channel switch on %d\n",
		IWL_DEBUG_11H(priv, "abort channel switch on %d\n",
		      le16_to_cpu(priv->switch_rxon.channel));
			      le16_to_cpu(priv->switch_channel));
		iwl_chswitch_done(priv, false);
		iwl_chswitch_done(priv, false);
	}
	}


+10 −9
Original line number Original line Diff line number Diff line
@@ -2843,16 +2843,13 @@ static void iwlagn_mac_channel_switch(struct ieee80211_hw *hw,
		goto out;
		goto out;


	if (test_bit(STATUS_EXIT_PENDING, &priv->status) ||
	if (test_bit(STATUS_EXIT_PENDING, &priv->status) ||
	    test_bit(STATUS_SCANNING, &priv->status))
	    test_bit(STATUS_SCANNING, &priv->status) ||
	    test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status))
		goto out;
		goto out;


	if (!iwl_is_associated_ctx(ctx))
	if (!iwl_is_associated_ctx(ctx))
		goto out;
		goto out;


	/* channel switch in progress */
	if (priv->switch_rxon.switch_in_progress == true)
		goto out;

	if (priv->cfg->ops->lib->set_channel_switch) {
	if (priv->cfg->ops->lib->set_channel_switch) {


		ch = channel->hw_value;
		ch = channel->hw_value;
@@ -2901,15 +2898,19 @@ static void iwlagn_mac_channel_switch(struct ieee80211_hw *hw,
			 * at this point, staging_rxon has the
			 * at this point, staging_rxon has the
			 * configuration for channel switch
			 * configuration for channel switch
			 */
			 */
			set_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status);
			priv->switch_channel = cpu_to_le16(ch);
			if (priv->cfg->ops->lib->set_channel_switch(priv,
			if (priv->cfg->ops->lib->set_channel_switch(priv,
								    ch_switch))
								    ch_switch)) {
				priv->switch_rxon.switch_in_progress = false;
				clear_bit(STATUS_CHANNEL_SWITCH_PENDING,
					  &priv->status);
				priv->switch_channel = 0;
				ieee80211_chswitch_done(ctx->vif, false);
			}
		}
		}
	}
	}
out:
out:
	mutex_unlock(&priv->mutex);
	mutex_unlock(&priv->mutex);
	if (!priv->switch_rxon.switch_in_progress)
		ieee80211_chswitch_done(ctx->vif, false);
	IWL_DEBUG_MAC80211(priv, "leave\n");
	IWL_DEBUG_MAC80211(priv, "leave\n");
}
}


Loading