iwlagn: use mutex for aggregation

Now that the ampdu_action callback can sleep,
we can use the mutex to properly protect the
aggregation data, and return useful errors if
they should happen.

Also, add some sleep and mutex debugging so
we won't call any of the functions that now
require being able to sleep and/or the mutex
to be held in an invalid context.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Acked-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
This commit is contained in:
Johannes Berg 2010-06-16 03:30:27 -07:00 committed by Reinette Chatre
parent 543708be32
commit 4620fefa59
5 changed files with 45 additions and 27 deletions

View File

@ -1785,6 +1785,7 @@ static int iwl4965_txq_agg_enable(struct iwl_priv *priv, int txq_id,
{ {
unsigned long flags; unsigned long flags;
u16 ra_tid; u16 ra_tid;
int ret;
if ((IWL49_FIRST_AMPDU_QUEUE > txq_id) || if ((IWL49_FIRST_AMPDU_QUEUE > txq_id) ||
(IWL49_FIRST_AMPDU_QUEUE + priv->cfg->num_of_ampdu_queues (IWL49_FIRST_AMPDU_QUEUE + priv->cfg->num_of_ampdu_queues
@ -1800,7 +1801,9 @@ static int iwl4965_txq_agg_enable(struct iwl_priv *priv, int txq_id,
ra_tid = BUILD_RAxTID(sta_id, tid); ra_tid = BUILD_RAxTID(sta_id, tid);
/* Modify device's station table to Tx this TID */ /* Modify device's station table to Tx this TID */
iwl_sta_tx_modify_enable_tid(priv, sta_id, tid); ret = iwl_sta_tx_modify_enable_tid(priv, sta_id, tid);
if (ret)
return ret;
spin_lock_irqsave(&priv->lock, flags); spin_lock_irqsave(&priv->lock, flags);

View File

@ -233,6 +233,7 @@ int iwlagn_txq_agg_enable(struct iwl_priv *priv, int txq_id,
{ {
unsigned long flags; unsigned long flags;
u16 ra_tid; u16 ra_tid;
int ret;
if ((IWLAGN_FIRST_AMPDU_QUEUE > txq_id) || if ((IWLAGN_FIRST_AMPDU_QUEUE > txq_id) ||
(IWLAGN_FIRST_AMPDU_QUEUE + priv->cfg->num_of_ampdu_queues (IWLAGN_FIRST_AMPDU_QUEUE + priv->cfg->num_of_ampdu_queues
@ -248,7 +249,9 @@ int iwlagn_txq_agg_enable(struct iwl_priv *priv, int txq_id,
ra_tid = BUILD_RAxTID(sta_id, tid); ra_tid = BUILD_RAxTID(sta_id, tid);
/* Modify device's station table to Tx this TID */ /* Modify device's station table to Tx this TID */
iwl_sta_tx_modify_enable_tid(priv, sta_id, tid); ret = iwl_sta_tx_modify_enable_tid(priv, sta_id, tid);
if (ret)
return ret;
spin_lock_irqsave(&priv->lock, flags); spin_lock_irqsave(&priv->lock, flags);

View File

@ -3374,7 +3374,7 @@ static int iwl_mac_ampdu_action(struct ieee80211_hw *hw,
struct ieee80211_sta *sta, u16 tid, u16 *ssn) struct ieee80211_sta *sta, u16 tid, u16 *ssn)
{ {
struct iwl_priv *priv = hw->priv; struct iwl_priv *priv = hw->priv;
int ret; int ret = -EINVAL;
IWL_DEBUG_HT(priv, "A-MPDU action on addr %pM tid %d\n", IWL_DEBUG_HT(priv, "A-MPDU action on addr %pM tid %d\n",
sta->addr, tid); sta->addr, tid);
@ -3382,17 +3382,19 @@ static int iwl_mac_ampdu_action(struct ieee80211_hw *hw,
if (!(priv->cfg->sku & IWL_SKU_N)) if (!(priv->cfg->sku & IWL_SKU_N))
return -EACCES; return -EACCES;
mutex_lock(&priv->mutex);
switch (action) { switch (action) {
case IEEE80211_AMPDU_RX_START: case IEEE80211_AMPDU_RX_START:
IWL_DEBUG_HT(priv, "start Rx\n"); IWL_DEBUG_HT(priv, "start Rx\n");
return iwl_sta_rx_agg_start(priv, sta, tid, *ssn); ret = iwl_sta_rx_agg_start(priv, sta, tid, *ssn);
break;
case IEEE80211_AMPDU_RX_STOP: case IEEE80211_AMPDU_RX_STOP:
IWL_DEBUG_HT(priv, "stop Rx\n"); IWL_DEBUG_HT(priv, "stop Rx\n");
ret = iwl_sta_rx_agg_stop(priv, sta, tid); ret = iwl_sta_rx_agg_stop(priv, sta, tid);
if (test_bit(STATUS_EXIT_PENDING, &priv->status)) if (test_bit(STATUS_EXIT_PENDING, &priv->status))
return 0; ret = 0;
else break;
return ret;
case IEEE80211_AMPDU_TX_START: case IEEE80211_AMPDU_TX_START:
IWL_DEBUG_HT(priv, "start Tx\n"); IWL_DEBUG_HT(priv, "start Tx\n");
ret = iwlagn_tx_agg_start(priv, vif, sta, tid, ssn); ret = iwlagn_tx_agg_start(priv, vif, sta, tid, ssn);
@ -3401,7 +3403,7 @@ static int iwl_mac_ampdu_action(struct ieee80211_hw *hw,
IWL_DEBUG_HT(priv, "priv->_agn.agg_tids_count = %u\n", IWL_DEBUG_HT(priv, "priv->_agn.agg_tids_count = %u\n",
priv->_agn.agg_tids_count); priv->_agn.agg_tids_count);
} }
return ret; break;
case IEEE80211_AMPDU_TX_STOP: case IEEE80211_AMPDU_TX_STOP:
IWL_DEBUG_HT(priv, "stop Tx\n"); IWL_DEBUG_HT(priv, "stop Tx\n");
ret = iwlagn_tx_agg_stop(priv, vif, sta, tid); ret = iwlagn_tx_agg_stop(priv, vif, sta, tid);
@ -3411,18 +3413,15 @@ static int iwl_mac_ampdu_action(struct ieee80211_hw *hw,
priv->_agn.agg_tids_count); priv->_agn.agg_tids_count);
} }
if (test_bit(STATUS_EXIT_PENDING, &priv->status)) if (test_bit(STATUS_EXIT_PENDING, &priv->status))
return 0; ret = 0;
else break;
return ret;
case IEEE80211_AMPDU_TX_OPERATIONAL: case IEEE80211_AMPDU_TX_OPERATIONAL:
/* do nothing */ /* do nothing, return value ignored */
return -EOPNOTSUPP;
default:
IWL_DEBUG_HT(priv, "unknown\n");
return -EINVAL;
break; break;
} }
return 0; mutex_unlock(&priv->mutex);
return ret;
} }
static void iwl_mac_sta_notify(struct ieee80211_hw *hw, static void iwl_mac_sta_notify(struct ieee80211_hw *hw,

View File

@ -30,6 +30,7 @@
#include <net/mac80211.h> #include <net/mac80211.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/lockdep.h>
#include "iwl-dev.h" #include "iwl-dev.h"
#include "iwl-core.h" #include "iwl-core.h"
@ -145,8 +146,10 @@ int iwl_send_add_sta(struct iwl_priv *priv,
if (flags & CMD_ASYNC) if (flags & CMD_ASYNC)
cmd.callback = iwl_add_sta_callback; cmd.callback = iwl_add_sta_callback;
else else {
cmd.flags |= CMD_WANT_SKB; cmd.flags |= CMD_WANT_SKB;
might_sleep();
}
cmd.len = priv->cfg->ops->utils->build_addsta_hcmd(sta, data); cmd.len = priv->cfg->ops->utils->build_addsta_hcmd(sta, data);
ret = iwl_send_cmd(priv, &cmd); ret = iwl_send_cmd(priv, &cmd);
@ -1268,17 +1271,22 @@ EXPORT_SYMBOL_GPL(iwl_dealloc_bcast_station);
/** /**
* iwl_sta_tx_modify_enable_tid - Enable Tx for this TID in station table * iwl_sta_tx_modify_enable_tid - Enable Tx for this TID in station table
*/ */
void iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid) int iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid)
{ {
unsigned long flags; unsigned long flags;
struct iwl_addsta_cmd sta_cmd;
lockdep_assert_held(&priv->mutex);
/* Remove "disable" flag, to enable Tx for this TID */ /* Remove "disable" flag, to enable Tx for this TID */
spin_lock_irqsave(&priv->sta_lock, flags); spin_lock_irqsave(&priv->sta_lock, flags);
priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_TID_DISABLE_TX; priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_TID_DISABLE_TX;
priv->stations[sta_id].sta.tid_disable_tx &= cpu_to_le16(~(1 << tid)); priv->stations[sta_id].sta.tid_disable_tx &= cpu_to_le16(~(1 << tid));
priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC); memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
spin_unlock_irqrestore(&priv->sta_lock, flags); spin_unlock_irqrestore(&priv->sta_lock, flags);
return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
} }
EXPORT_SYMBOL(iwl_sta_tx_modify_enable_tid); EXPORT_SYMBOL(iwl_sta_tx_modify_enable_tid);
@ -1287,6 +1295,9 @@ int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta,
{ {
unsigned long flags; unsigned long flags;
int sta_id; int sta_id;
struct iwl_addsta_cmd sta_cmd;
lockdep_assert_held(&priv->mutex);
sta_id = iwl_sta_id(sta); sta_id = iwl_sta_id(sta);
if (sta_id == IWL_INVALID_STATION) if (sta_id == IWL_INVALID_STATION)
@ -1298,10 +1309,10 @@ int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta,
priv->stations[sta_id].sta.add_immediate_ba_tid = (u8)tid; priv->stations[sta_id].sta.add_immediate_ba_tid = (u8)tid;
priv->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn); priv->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn);
priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
spin_unlock_irqrestore(&priv->sta_lock, flags); spin_unlock_irqrestore(&priv->sta_lock, flags);
return iwl_send_add_sta(priv, &priv->stations[sta_id].sta, return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
CMD_ASYNC);
} }
EXPORT_SYMBOL(iwl_sta_rx_agg_start); EXPORT_SYMBOL(iwl_sta_rx_agg_start);
@ -1309,7 +1320,10 @@ int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta,
int tid) int tid)
{ {
unsigned long flags; unsigned long flags;
int sta_id, ret; int sta_id;
struct iwl_addsta_cmd sta_cmd;
lockdep_assert_held(&priv->mutex);
sta_id = iwl_sta_id(sta); sta_id = iwl_sta_id(sta);
if (sta_id == IWL_INVALID_STATION) { if (sta_id == IWL_INVALID_STATION) {
@ -1322,11 +1336,10 @@ int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta,
priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK; priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK;
priv->stations[sta_id].sta.remove_immediate_ba_tid = (u8)tid; priv->stations[sta_id].sta.remove_immediate_ba_tid = (u8)tid;
priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
ret = iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC); memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
spin_unlock_irqrestore(&priv->sta_lock, flags); spin_unlock_irqrestore(&priv->sta_lock, flags);
return ret; return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
} }
EXPORT_SYMBOL(iwl_sta_rx_agg_stop); EXPORT_SYMBOL(iwl_sta_rx_agg_stop);

View File

@ -73,7 +73,7 @@ int iwl_remove_station(struct iwl_priv *priv, const u8 sta_id,
const u8 *addr); const u8 *addr);
int iwl_mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int iwl_mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta); struct ieee80211_sta *sta);
void iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid); int iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid);
int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta, int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta,
int tid, u16 ssn); int tid, u16 ssn);
int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta, int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta,