summaryrefslogtreecommitdiffstats
path: root/net/mac80211/sta_info.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/sta_info.c')
-rw-r--r--net/mac80211/sta_info.c126
1 files changed, 99 insertions, 27 deletions
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 0a4e4c04db89..ca9fde198188 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -91,13 +91,13 @@ static int sta_info_hash_del(struct ieee80211_local *local,
return -ENOENT;
}
-static void free_sta_work(struct work_struct *wk)
+static void cleanup_single_sta(struct sta_info *sta)
{
- struct sta_info *sta = container_of(wk, struct sta_info, free_sta_wk);
int ac, i;
struct tid_ampdu_tx *tid_tx;
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
+ struct ps_data *ps;
/*
* At this point, when being called as call_rcu callback,
@@ -107,18 +107,22 @@ static void free_sta_work(struct work_struct *wk)
*/
if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
- BUG_ON(!sdata->bss);
+ if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
+ sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ ps = &sdata->bss->ps;
+ else
+ return;
clear_sta_flag(sta, WLAN_STA_PS_STA);
- atomic_dec(&sdata->bss->num_sta_ps);
+ atomic_dec(&ps->num_sta_ps);
sta_info_recalc_tim(sta);
}
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]);
- __skb_queue_purge(&sta->ps_tx_buf[ac]);
- __skb_queue_purge(&sta->tx_filtered[ac]);
+ ieee80211_purge_tx_queue(&local->hw, &sta->ps_tx_buf[ac]);
+ ieee80211_purge_tx_queue(&local->hw, &sta->tx_filtered[ac]);
}
#ifdef CONFIG_MAC80211_MESH
@@ -137,22 +141,46 @@ static void free_sta_work(struct work_struct *wk)
* drivers have to handle aggregation stop being requested, followed
* directly by station destruction.
*/
- for (i = 0; i < STA_TID_NUM; i++) {
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]);
if (!tid_tx)
continue;
- __skb_queue_purge(&tid_tx->pending);
+ ieee80211_purge_tx_queue(&local->hw, &tid_tx->pending);
kfree(tid_tx);
}
sta_info_free(local, sta);
}
+void ieee80211_cleanup_sdata_stas(struct ieee80211_sub_if_data *sdata)
+{
+ struct sta_info *sta;
+
+ spin_lock_bh(&sdata->cleanup_stations_lock);
+ while (!list_empty(&sdata->cleanup_stations)) {
+ sta = list_first_entry(&sdata->cleanup_stations,
+ struct sta_info, list);
+ list_del(&sta->list);
+ spin_unlock_bh(&sdata->cleanup_stations_lock);
+
+ cleanup_single_sta(sta);
+
+ spin_lock_bh(&sdata->cleanup_stations_lock);
+ }
+
+ spin_unlock_bh(&sdata->cleanup_stations_lock);
+}
+
static void free_sta_rcu(struct rcu_head *h)
{
struct sta_info *sta = container_of(h, struct sta_info, rcu_head);
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
- ieee80211_queue_work(&sta->local->hw, &sta->free_sta_wk);
+ spin_lock(&sdata->cleanup_stations_lock);
+ list_add_tail(&sta->list, &sdata->cleanup_stations);
+ spin_unlock(&sdata->cleanup_stations_lock);
+
+ ieee80211_queue_work(&sdata->local->hw, &sdata->cleanup_stations_wk);
}
/* protected by RCU */
@@ -305,7 +333,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
spin_lock_init(&sta->lock);
INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
- INIT_WORK(&sta->free_sta_wk, free_sta_work);
INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
mutex_init(&sta->ampdu_mlme.mtx);
@@ -325,7 +352,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
return NULL;
}
- for (i = 0; i < STA_TID_NUM; i++) {
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
/*
* timer_to_tid must be initialized with identity mapping
* to enable session_timer's data differentiation. See
@@ -338,7 +365,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
skb_queue_head_init(&sta->tx_filtered[i]);
}
- for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++)
sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX);
sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
@@ -502,22 +529,22 @@ int sta_info_insert(struct sta_info *sta)
return err;
}
-static inline void __bss_tim_set(struct ieee80211_if_ap *bss, u16 aid)
+static inline void __bss_tim_set(u8 *tim, u16 id)
{
/*
* This format has been mandated by the IEEE specifications,
* so this line may not be changed to use the __set_bit() format.
*/
- bss->tim[aid / 8] |= (1 << (aid % 8));
+ tim[id / 8] |= (1 << (id % 8));
}
-static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, u16 aid)
+static inline void __bss_tim_clear(u8 *tim, u16 id)
{
/*
* This format has been mandated by the IEEE specifications,
* so this line may not be changed to use the __clear_bit() format.
*/
- bss->tim[aid / 8] &= ~(1 << (aid % 8));
+ tim[id / 8] &= ~(1 << (id % 8));
}
static unsigned long ieee80211_tids_for_ac(int ac)
@@ -541,14 +568,23 @@ static unsigned long ieee80211_tids_for_ac(int ac)
void sta_info_recalc_tim(struct sta_info *sta)
{
struct ieee80211_local *local = sta->local;
- struct ieee80211_if_ap *bss = sta->sdata->bss;
+ struct ps_data *ps;
unsigned long flags;
bool indicate_tim = false;
u8 ignore_for_tim = sta->sta.uapsd_queues;
int ac;
+ u16 id;
+
+ if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
+ sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+ if (WARN_ON_ONCE(!sta->sdata->bss))
+ return;
- if (WARN_ON_ONCE(!sta->sdata->bss))
+ ps = &sta->sdata->bss->ps;
+ id = sta->sta.aid;
+ } else {
return;
+ }
/* No need to do anything if the driver does all */
if (local->hw.flags & IEEE80211_HW_AP_LINK_PS)
@@ -587,9 +623,9 @@ void sta_info_recalc_tim(struct sta_info *sta)
spin_lock_irqsave(&local->tim_lock, flags);
if (indicate_tim)
- __bss_tim_set(bss, sta->sta.aid);
+ __bss_tim_set(ps->tim, id);
else
- __bss_tim_clear(bss, sta->sta.aid);
+ __bss_tim_clear(ps->tim, id);
if (local->ops->set_tim) {
local->tim_in_locked_section = true;
@@ -848,7 +884,7 @@ void sta_info_init(struct ieee80211_local *local)
void sta_info_stop(struct ieee80211_local *local)
{
- del_timer(&local->sta_cleanup);
+ del_timer_sync(&local->sta_cleanup);
sta_info_flush(local, NULL);
}
@@ -877,6 +913,20 @@ int sta_info_flush(struct ieee80211_local *local,
}
mutex_unlock(&local->sta_mtx);
+ rcu_barrier();
+
+ if (sdata) {
+ ieee80211_cleanup_sdata_stas(sdata);
+ cancel_work_sync(&sdata->cleanup_stations_wk);
+ } else {
+ mutex_lock(&local->iflist_mtx);
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ ieee80211_cleanup_sdata_stas(sdata);
+ cancel_work_sync(&sdata->cleanup_stations_wk);
+ }
+ mutex_unlock(&local->iflist_mtx);
+ }
+
return ret;
}
@@ -893,8 +943,8 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
continue;
if (time_after(jiffies, sta->last_rx + exp_time)) {
- ibss_dbg(sdata, "expiring inactive STA %pM\n",
- sta->sta.addr);
+ sta_dbg(sta->sdata, "expiring inactive STA %pM\n",
+ sta->sta.addr);
WARN_ON(__sta_info_destroy(sta));
}
}
@@ -948,10 +998,17 @@ static void clear_sta_ps_flags(void *_sta)
{
struct sta_info *sta = _sta;
struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct ps_data *ps;
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP ||
+ sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ ps = &sdata->bss->ps;
+ else
+ return;
clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
if (test_and_clear_sta_flag(sta, WLAN_STA_PS_STA))
- atomic_dec(&sdata->bss->num_sta_ps);
+ atomic_dec(&ps->num_sta_ps);
}
/* powersave support code */
@@ -961,10 +1018,11 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
struct ieee80211_local *local = sdata->local;
struct sk_buff_head pending;
int filtered = 0, buffered = 0, ac;
+ unsigned long flags;
clear_sta_flag(sta, WLAN_STA_SP);
- BUILD_BUG_ON(BITS_TO_LONGS(STA_TID_NUM) > 1);
+ BUILD_BUG_ON(BITS_TO_LONGS(IEEE80211_NUM_TIDS) > 1);
sta->driver_buffered_tids = 0;
if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS))
@@ -976,12 +1034,16 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
int count = skb_queue_len(&pending), tmp;
+ spin_lock_irqsave(&sta->tx_filtered[ac].lock, flags);
skb_queue_splice_tail_init(&sta->tx_filtered[ac], &pending);
+ spin_unlock_irqrestore(&sta->tx_filtered[ac].lock, flags);
tmp = skb_queue_len(&pending);
filtered += tmp - count;
count = tmp;
+ spin_lock_irqsave(&sta->ps_tx_buf[ac].lock, flags);
skb_queue_splice_tail_init(&sta->ps_tx_buf[ac], &pending);
+ spin_unlock_irqrestore(&sta->ps_tx_buf[ac].lock, flags);
tmp = skb_queue_len(&pending);
buffered += tmp - count;
}
@@ -1008,6 +1070,7 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
__le16 fc;
bool qos = test_sta_flag(sta, WLAN_STA_WME);
struct ieee80211_tx_info *info;
+ struct ieee80211_chanctx_conf *chanctx_conf;
if (qos) {
fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
@@ -1057,7 +1120,16 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
drv_allow_buffered_frames(local, sta, BIT(tid), 1, reason, false);
- ieee80211_xmit(sdata, skb);
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON(!chanctx_conf)) {
+ rcu_read_unlock();
+ kfree_skb(skb);
+ return;
+ }
+
+ ieee80211_xmit(sdata, skb, chanctx_conf->def.chan->band);
+ rcu_read_unlock();
}
static void
@@ -1338,7 +1410,7 @@ void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta,
{
struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
- if (WARN_ON(tid >= STA_TID_NUM))
+ if (WARN_ON(tid >= IEEE80211_NUM_TIDS))
return;
if (buffered)
OpenPOWER on IntegriCloud