summaryrefslogtreecommitdiff
path: root/net/mac80211
diff options
context:
space:
mode:
authorJohn W. Linville <linville@tuxdriver.com>2014-01-01 15:39:56 -0500
committerJohn W. Linville <linville@tuxdriver.com>2014-01-01 15:39:56 -0500
commitad86c55bac643a745f480d26689d153ec0f38b04 (patch)
tree8ae3e20d2f3619021393166fe1f50a8ca00c1c33 /net/mac80211
parent21eb218989523b7bee28900aaec9f9296b70fa27 (diff)
parent39731b78b4afebb7501c05b68e2443a1b250b41c (diff)
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next into for-davem
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/cfg.c30
-rw-r--r--net/mac80211/driver-ops.h32
-rw-r--r--net/mac80211/ibss.c74
-rw-r--r--net/mac80211/ieee80211_i.h21
-rw-r--r--net/mac80211/iface.c49
-rw-r--r--net/mac80211/key.c60
-rw-r--r--net/mac80211/key.h3
-rw-r--r--net/mac80211/main.c8
-rw-r--r--net/mac80211/mesh.c5
-rw-r--r--net/mac80211/mesh_sync.c9
-rw-r--r--net/mac80211/mlme.c2
-rw-r--r--net/mac80211/pm.c3
-rw-r--r--net/mac80211/rx.c33
-rw-r--r--net/mac80211/scan.c80
-rw-r--r--net/mac80211/sta_info.c118
-rw-r--r--net/mac80211/sta_info.h30
-rw-r--r--net/mac80211/trace.h58
-rw-r--r--net/mac80211/tx.c12
-rw-r--r--net/mac80211/util.c34
19 files changed, 306 insertions, 355 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index f80e8c4c6bcd..ac185286842d 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -301,9 +301,10 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
if (!sta)
goto out;
- if (pairwise)
+ if (pairwise && key_idx < NUM_DEFAULT_KEYS)
key = rcu_dereference(sta->ptk[key_idx]);
- else if (key_idx < NUM_DEFAULT_KEYS)
+ else if (!pairwise &&
+ key_idx < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS)
key = rcu_dereference(sta->gtk[key_idx]);
} else
key = rcu_dereference(sdata->keys[key_idx]);
@@ -873,8 +874,8 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
return 0;
}
-int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
- struct cfg80211_beacon_data *params)
+static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_beacon_data *params)
{
struct beacon_data *new, *old;
int new_head_len, new_tail_len;
@@ -1097,17 +1098,8 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
if (old_probe_resp)
kfree_rcu(old_probe_resp, rcu_head);
- list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
- sta_info_flush_defer(vlan);
- sta_info_flush_defer(sdata);
- synchronize_net();
- rcu_barrier();
- list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
- sta_info_flush_cleanup(vlan);
- ieee80211_free_keys(vlan);
- }
- sta_info_flush_cleanup(sdata);
- ieee80211_free_keys(sdata);
+ __sta_info_flush(sdata, true);
+ ieee80211_free_keys(sdata, true);
sdata->vif.bss_conf.enable_beacon = false;
sdata->vif.bss_conf.ssid_len = 0;
@@ -2587,8 +2579,8 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
int j;
sdata->rc_rateidx_mask[i] = mask->control[i].legacy;
- memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].mcs,
- sizeof(mask->control[i].mcs));
+ memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].ht_mcs,
+ sizeof(mask->control[i].ht_mcs));
sdata->rc_has_mcs_mask[i] = false;
if (!sband)
@@ -3047,8 +3039,8 @@ unlock:
sdata_unlock(sdata);
}
-static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
- struct cfg80211_csa_settings *params)
+int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_csa_settings *params)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 5d03c47c0a4c..ef8b385eff04 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -242,22 +242,6 @@ static inline u64 drv_prepare_multicast(struct ieee80211_local *local,
return ret;
}
-static inline void drv_set_multicast_list(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- struct netdev_hw_addr_list *mc_list)
-{
- bool allmulti = sdata->flags & IEEE80211_SDATA_ALLMULTI;
-
- trace_drv_set_multicast_list(local, sdata, mc_list->count);
-
- check_sdata_in_driver(sdata);
-
- if (local->ops->set_multicast_list)
- local->ops->set_multicast_list(&local->hw, &sdata->vif,
- allmulti, mc_list);
- trace_drv_return_void(local);
-}
-
static inline void drv_configure_filter(struct ieee80211_local *local,
unsigned int changed_flags,
unsigned int *total_flags,
@@ -550,6 +534,22 @@ static inline void drv_sta_remove_debugfs(struct ieee80211_local *local,
}
#endif
+static inline void drv_sta_pre_rcu_remove(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta)
+{
+ might_sleep();
+
+ sdata = get_bss_sdata(sdata);
+ check_sdata_in_driver(sdata);
+
+ trace_drv_sta_pre_rcu_remove(local, sdata, &sta->sta);
+ if (local->ops->sta_pre_rcu_remove)
+ local->ops->sta_pre_rcu_remove(&local->hw, &sdata->vif,
+ &sta->sta);
+ trace_drv_return_void(local);
+}
+
static inline __must_check
int drv_sta_state(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 2eda7b13124a..d6ba841437b6 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -522,7 +522,7 @@ int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
if (csa_settings)
ieee80211_send_action_csa(sdata, csa_settings);
- ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
+ return BSS_CHANGED_BEACON;
out:
return ret;
}
@@ -534,7 +534,8 @@ int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata)
int err;
u16 capability;
- sdata_lock(sdata);
+ sdata_assert_lock(sdata);
+
/* update cfg80211 bss information with the new channel */
if (!is_zero_ether_addr(ifibss->bssid)) {
capability = WLAN_CAPABILITY_IBSS;
@@ -559,10 +560,12 @@ int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata)
/* generate the beacon */
err = ieee80211_ibss_csa_beacon(sdata, NULL);
- sdata_unlock(sdata);
if (err < 0)
return err;
+ if (err)
+ ieee80211_bss_info_change_notify(sdata, err);
+
return 0;
}
@@ -753,12 +756,16 @@ static void ieee80211_csa_connection_drop_work(struct work_struct *work)
container_of(work, struct ieee80211_sub_if_data,
u.ibss.csa_connection_drop_work);
+ sdata_lock(sdata);
+
ieee80211_ibss_disconnect(sdata);
synchronize_rcu();
skb_queue_purge(&sdata->skb_queue);
/* trigger a scan to find another IBSS network to join */
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
+
+ sdata_unlock(sdata);
}
static void ieee80211_ibss_csa_mark_radar(struct ieee80211_sub_if_data *sdata)
@@ -784,18 +791,10 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
struct cfg80211_csa_settings params;
struct ieee80211_csa_ie csa_ie;
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
- struct ieee80211_chanctx_conf *chanctx_conf;
- struct ieee80211_chanctx *chanctx;
enum nl80211_channel_type ch_type;
- int err, num_chanctx;
+ int err;
u32 sta_flags;
- if (sdata->vif.csa_active)
- return true;
-
- if (!sdata->vif.bss_conf.ibss_joined)
- return false;
-
sta_flags = IEEE80211_STA_DISABLE_VHT;
switch (ifibss->chandef.width) {
case NL80211_CHAN_WIDTH_5:
@@ -830,9 +829,6 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
params.count = csa_ie.count;
params.chandef = csa_ie.chandef;
- if (ifibss->chandef.chan->band != params.chandef.chan->band)
- goto disconnect;
-
switch (ifibss->chandef.width) {
case NL80211_CHAN_WIDTH_20_NOHT:
case NL80211_CHAN_WIDTH_20:
@@ -888,28 +884,12 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
params.radar_required = true;
}
- rcu_read_lock();
- chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
- if (!chanctx_conf) {
- rcu_read_unlock();
- goto disconnect;
- }
-
- /* don't handle for multi-VIF cases */
- chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
- if (chanctx->refcount > 1) {
- rcu_read_unlock();
- goto disconnect;
- }
- num_chanctx = 0;
- list_for_each_entry_rcu(chanctx, &sdata->local->chanctx_list, list)
- num_chanctx++;
-
- if (num_chanctx > 1) {
- rcu_read_unlock();
- goto disconnect;
+ if (cfg80211_chandef_identical(&params.chandef,
+ &sdata->vif.bss_conf.chandef)) {
+ ibss_dbg(sdata,
+ "received csa with an identical chandef, ignoring\n");
+ return true;
}
- rcu_read_unlock();
/* all checks done, now perform the channel switch. */
ibss_dbg(sdata,
@@ -918,19 +898,9 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
params.block_tx = !!csa_ie.mode;
- ieee80211_ibss_csa_beacon(sdata, &params);
- sdata->csa_radar_required = params.radar_required;
-
- if (params.block_tx)
- ieee80211_stop_queues_by_reason(&sdata->local->hw,
- IEEE80211_MAX_QUEUE_MAP,
- IEEE80211_QUEUE_STOP_REASON_CSA);
-
- sdata->csa_chandef = params.chandef;
- sdata->vif.csa_active = true;
-
- ieee80211_bss_info_change_notify(sdata, err);
- drv_channel_switch_beacon(sdata, &params.chandef);
+ if (ieee80211_channel_switch(sdata->local->hw.wiphy, sdata->dev,
+ &params))
+ goto disconnect;
ieee80211_ibss_csa_mark_radar(sdata);
@@ -966,7 +936,8 @@ ieee80211_rx_mgmt_spectrum_mgmt(struct ieee80211_sub_if_data *sdata,
if (len < required_len)
return;
- ieee80211_ibss_process_chanswitch(sdata, elems, false);
+ if (!sdata->vif.csa_active)
+ ieee80211_ibss_process_chanswitch(sdata, elems, false);
}
static void ieee80211_rx_mgmt_deauth_ibss(struct ieee80211_sub_if_data *sdata,
@@ -1147,7 +1118,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
goto put_bss;
/* process channel switch */
- if (ieee80211_ibss_process_chanswitch(sdata, elems, true))
+ if (sdata->vif.csa_active ||
+ ieee80211_ibss_process_chanswitch(sdata, elems, true))
goto put_bss;
/* same BSSID */
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index ed5bf8b4b5c2..fb5dbcb79a12 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -232,6 +232,7 @@ struct ieee80211_rx_data {
struct beacon_data {
u8 *head, *tail;
int head_len, tail_len;
+ struct ieee80211_meshconf_ie *meshconf;
struct rcu_head rcu_head;
};
@@ -540,7 +541,10 @@ struct ieee80211_mesh_sync_ops {
struct ieee80211_mgmt *mgmt,
struct ieee802_11_elems *elems,
struct ieee80211_rx_status *rx_status);
- void (*adjust_tbtt)(struct ieee80211_sub_if_data *sdata);
+
+ /* should be called with beacon_data under RCU read lock */
+ void (*adjust_tbtt)(struct ieee80211_sub_if_data *sdata,
+ struct beacon_data *beacon);
/* add other framework functions here */
};
@@ -614,6 +618,9 @@ struct ieee80211_if_mesh {
bool chsw_init;
u8 chsw_ttl;
u16 pre_value;
+
+ /* offset from skb->data while building IE */
+ int meshconf_offset;
};
#ifdef CONFIG_MAC80211_MESH
@@ -776,10 +783,6 @@ struct ieee80211_sub_if_data {
u32 mntr_flags;
} u;
- spinlock_t cleanup_stations_lock;
- struct list_head cleanup_stations;
- struct work_struct cleanup_stations_wk;
-
#ifdef CONFIG_MAC80211_DEBUGFS
struct {
struct dentry *subdir_stations;
@@ -1117,6 +1120,7 @@ struct ieee80211_local {
struct work_struct sched_scan_stopped_work;
struct ieee80211_sub_if_data __rcu *sched_scan_sdata;
+ struct cfg80211_sched_scan_request *sched_scan_req;
unsigned long leave_oper_channel_time;
enum mac80211_scan_state next_scan_state;
@@ -1425,6 +1429,9 @@ void ieee80211_rx_bss_put(struct ieee80211_local *local,
struct ieee80211_bss *bss);
/* scheduled scan handling */
+int
+__ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_sched_scan_request *req);
int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
struct cfg80211_sched_scan_request *req);
int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata);
@@ -1443,6 +1450,8 @@ void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc);
/* channel switch handling */
void ieee80211_csa_finalize_work(struct work_struct *work);
+int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_csa_settings *params);
/* interface handling */
int ieee80211_iface_init(void);
@@ -1465,8 +1474,6 @@ void ieee80211_del_virtual_monitor(struct ieee80211_local *local);
bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
-int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
- struct cfg80211_beacon_data *params);
static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
{
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index a4f98123d0bf..d624ed49a7d9 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -786,10 +786,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
* This is relevant only in WDS mode, in all other modes we've
* already removed all stations when disconnecting or similar,
* so warn otherwise.
- *
- * We call sta_info_flush_cleanup() later, to combine RCU waits.
*/
- flushed = sta_info_flush_defer(sdata);
+ flushed = sta_info_flush(sdata);
WARN_ON_ONCE((sdata->vif.type != NL80211_IFTYPE_WDS && flushed > 0) ||
(sdata->vif.type == NL80211_IFTYPE_WDS && flushed != 1));
@@ -891,23 +889,15 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
cancel_work_sync(&sdata->work);
/*
* When we get here, the interface is marked down.
+ * Free the remaining keys, if there are any
+ * (shouldn't be, except maybe in WDS mode?)
*
- * sta_info_flush_cleanup() requires rcu_barrier()
- * first to wait for the station call_rcu() calls
- * to complete, and we also need synchronize_rcu()
- * to wait for the RX path in case it is using the
- * interface and enqueuing frames at this very time on
+ * Force the key freeing to always synchronize_net()
+ * to wait for the RX path in case it is using this
+ * interface enqueuing frames * at this very time on
* another CPU.
*/
- synchronize_rcu();
- rcu_barrier();
- sta_info_flush_cleanup(sdata);
-
- /*
- * Free all remaining keys, there shouldn't be any,
- * except maybe in WDS mode?
- */
- ieee80211_free_keys(sdata);
+ ieee80211_free_keys(sdata, true);
/* fall through */
case NL80211_IFTYPE_AP:
@@ -1018,17 +1008,6 @@ static void ieee80211_set_multicast_list(struct net_device *dev)
atomic_dec(&local->iff_promiscs);
sdata->flags ^= IEEE80211_SDATA_PROMISC;
}
-
- /*
- * TODO: If somebody needs this on AP interfaces,
- * it can be enabled easily but multicast
- * addresses from VLANs need to be synced.
- */
- if (sdata->vif.type != NL80211_IFTYPE_MONITOR &&
- sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
- sdata->vif.type != NL80211_IFTYPE_AP)
- drv_set_multicast_list(local, sdata, &dev->mc);
-
spin_lock_bh(&local->filter_lock);
__hw_addr_sync(&local->mc_list, &dev->mc, dev->addr_len);
spin_unlock_bh(&local->filter_lock);
@@ -1044,7 +1023,7 @@ static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata)
int i;
/* free extra data */
- ieee80211_free_keys(sdata);
+ ieee80211_free_keys(sdata, false);
ieee80211_debugfs_remove_netdev(sdata);
@@ -1578,15 +1557,6 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local,
mutex_unlock(&local->iflist_mtx);
}
-static void ieee80211_cleanup_sdata_stas_wk(struct work_struct *wk)
-{
- struct ieee80211_sub_if_data *sdata;
-
- sdata = container_of(wk, struct ieee80211_sub_if_data, cleanup_stations_wk);
-
- ieee80211_cleanup_sdata_stas(sdata);
-}
-
int ieee80211_if_add(struct ieee80211_local *local, const char *name,
struct wireless_dev **new_wdev, enum nl80211_iftype type,
struct vif_params *params)
@@ -1659,9 +1629,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
INIT_LIST_HEAD(&sdata->key_list);
- spin_lock_init(&sdata->cleanup_stations_lock);
- INIT_LIST_HEAD(&sdata->cleanup_stations);
- INIT_WORK(&sdata->cleanup_stations_wk, ieee80211_cleanup_sdata_stas_wk);
INIT_DELAYED_WORK(&sdata->dfs_cac_timer_work,
ieee80211_dfs_cac_timer_work);
INIT_DELAYED_WORK(&sdata->dec_tailroom_needed_wk,
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index e568d98167d0..6ff65a1ebaa9 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -589,14 +589,10 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_iter_keys);
-void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata)
+static void ieee80211_free_keys_iface(struct ieee80211_sub_if_data *sdata,
+ struct list_head *keys)
{
struct ieee80211_key *key, *tmp;
- LIST_HEAD(keys);
-
- cancel_delayed_work_sync(&sdata->dec_tailroom_needed_wk);
-
- mutex_lock(&sdata->local->key_mtx);
sdata->crypto_tx_tailroom_needed_cnt -=
sdata->crypto_tx_tailroom_pending_dec;
@@ -608,28 +604,51 @@ void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata)
ieee80211_key_replace(key->sdata, key->sta,
key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
key, NULL);
- list_add_tail(&key->list, &keys);
+ list_add_tail(&key->list, keys);
}
ieee80211_debugfs_key_update_default(sdata);
+}
- if (!list_empty(&keys)) {
- synchronize_net();
- list_for_each_entry_safe(key, tmp, &keys, list)
- __ieee80211_key_destroy(key, false);
+void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata,
+ bool force_synchronize)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_sub_if_data *vlan;
+ struct ieee80211_key *key, *tmp;
+ LIST_HEAD(keys);
+
+ cancel_delayed_work_sync(&sdata->dec_tailroom_needed_wk);
+
+ mutex_lock(&local->key_mtx);
+
+ ieee80211_free_keys_iface(sdata, &keys);
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP) {
+ list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+ ieee80211_free_keys_iface(vlan, &keys);
}
+ if (!list_empty(&keys) || force_synchronize)
+ synchronize_net();
+ list_for_each_entry_safe(key, tmp, &keys, list)
+ __ieee80211_key_destroy(key, false);
+
WARN_ON_ONCE(sdata->crypto_tx_tailroom_needed_cnt ||
sdata->crypto_tx_tailroom_pending_dec);
+ if (sdata->vif.type == NL80211_IFTYPE_AP) {
+ list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+ WARN_ON_ONCE(vlan->crypto_tx_tailroom_needed_cnt ||
+ vlan->crypto_tx_tailroom_pending_dec);
+ }
- mutex_unlock(&sdata->local->key_mtx);
+ mutex_unlock(&local->key_mtx);
}
void ieee80211_free_sta_keys(struct ieee80211_local *local,
struct sta_info *sta)
{
- struct ieee80211_key *key, *tmp;
- LIST_HEAD(keys);
+ struct ieee80211_key *key;
int i;
mutex_lock(&local->key_mtx);
@@ -640,7 +659,7 @@ void ieee80211_free_sta_keys(struct ieee80211_local *local,
ieee80211_key_replace(key->sdata, key->sta,
key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
key, NULL);
- list_add(&key->list, &keys);
+ __ieee80211_key_destroy(key, true);
}
for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
@@ -650,17 +669,8 @@ void ieee80211_free_sta_keys(struct ieee80211_local *local,
ieee80211_key_replace(key->sdata, key->sta,
key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
key, NULL);
- list_add(&key->list, &keys);
- }
-
- /*
- * NB: the station code relies on this being
- * done even if there aren't any keys
- */
- synchronize_net();
-
- list_for_each_entry_safe(key, tmp, &keys, list)
__ieee80211_key_destroy(key, true);
+ }
mutex_unlock(&local->key_mtx);
}
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index 0aebb889caba..19db68663d75 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -136,7 +136,8 @@ void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx,
bool uni, bool multi);
void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata,
int idx);
-void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata);
+void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata,
+ bool force_synchronize);
void ieee80211_free_sta_keys(struct ieee80211_local *local,
struct sta_info *sta);
void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata);
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index fa34cd2344b9..2bd5b552b2f6 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -250,12 +250,8 @@ static void ieee80211_restart_work(struct work_struct *work)
/* wait for scan work complete */
flush_workqueue(local->workqueue);
- mutex_lock(&local->mtx);
- WARN(test_bit(SCAN_HW_SCANNING, &local->scanning) ||
- rcu_dereference_protected(local->sched_scan_sdata,
- lockdep_is_held(&local->mtx)),
- "%s called with hardware scan in progress\n", __func__);
- mutex_unlock(&local->mtx);
+ WARN(test_bit(SCAN_HW_SCANNING, &local->scanning),
+ "%s called with hardware scan in progress\n", __func__);
rtnl_lock();
ieee80211_scan_cancel(local);
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 89df62b2b689..5a74b249ba35 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -259,6 +259,9 @@ int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata,
*pos++ = WLAN_EID_MESH_CONFIG;
*pos++ = meshconf_len;
+ /* save a pointer for quick updates in pre-tbtt */
+ ifmsh->meshconf_offset = pos - skb->data;
+
/* Active path selection protocol ID */
*pos++ = ifmsh->mesh_pp_id;
/* Active path selection metric ID */
@@ -723,6 +726,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
bcn->tail_len = skb->len;
memcpy(bcn->tail, skb->data, bcn->tail_len);
+ bcn->meshconf = (struct ieee80211_meshconf_ie *)
+ (bcn->tail + ifmsh->meshconf_offset);
dev_kfree_skb(skb);
rcu_assign_pointer(ifmsh->beacon, bcn);
diff --git a/net/mac80211/mesh_sync.c b/net/mac80211/mesh_sync.c
index d1cf2d553499..2bc5dc25d5ad 100644
--- a/net/mac80211/mesh_sync.c
+++ b/net/mac80211/mesh_sync.c
@@ -164,12 +164,15 @@ no_sync:
rcu_read_unlock();
}
-static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata)
+static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata,
+ struct beacon_data *beacon)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+ u8 cap;
WARN_ON(ifmsh->mesh_sp_id != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET);
BUG_ON(!rcu_read_lock_held());
+ cap = beacon->meshconf->meshconf_cap;
spin_lock_bh(&ifmsh->sync_offset_lock);
@@ -194,6 +197,10 @@ static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata)
ifmsh->adjusting_tbtt = false;
}
spin_unlock_bh(&ifmsh->sync_offset_lock);
+
+ beacon->meshconf->meshconf_cap = ifmsh->adjusting_tbtt ?
+ IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING | cap :
+ ~IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING & cap;
}
static const struct sync_method sync_methods[] = {
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 900ead344f5b..9c2c7ee2cc30 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1698,7 +1698,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
memset(ifmgd->bssid, 0, ETH_ALEN);
/* remove AP and TDLS peers */
- sta_info_flush_defer(sdata);
+ sta_info_flush(sdata);
/* finally reset all BSS / config parameters */
changed |= ieee80211_reset_erp_info(sdata);
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index 340126204343..af64fb8e8add 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -37,9 +37,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
IEEE80211_MAX_QUEUE_MAP,
IEEE80211_QUEUE_STOP_REASON_SUSPEND);
- /* flush out all packets and station cleanup call_rcu()s */
+ /* flush out all packets */
synchronize_net();
- rcu_barrier();
ieee80211_flush_queues(local, NULL);
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 2dfa75522733..5a2afe9583a8 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1963,20 +1963,17 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
}
}
- if (skb) {
- int align __maybe_unused;
-
#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
- /*
- * 'align' will only take the values 0 or 2 here
- * since all frames are required to be aligned
- * to 2-byte boundaries when being passed to
- * mac80211; the code here works just as well if
- * that isn't true, but mac80211 assumes it can
- * access fields as 2-byte aligned (e.g. for
- * compare_ether_addr)
+ if (skb) {
+ /* 'align' will only take the values 0 or 2 here since all
+ * frames are required to be aligned to 2-byte boundaries
+ * when being passed to mac80211; the code here works just
+ * as well if that isn't true, but mac80211 assumes it can
+ * access fields as 2-byte aligned (e.g. for ether_addr_equal)
*/
- align = ((unsigned long)(skb->data + sizeof(struct ethhdr))) & 3;
+ int align;
+
+ align = (unsigned long)(skb->data + sizeof(struct ethhdr)) & 3;
if (align) {
if (WARN_ON(skb_headroom(skb) < 3)) {
dev_kfree_skb(skb);
@@ -1989,14 +1986,14 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
skb_set_tail_pointer(skb, len);
}
}
+ }
#endif
- if (skb) {
- /* deliver to local stack */
- skb->protocol = eth_type_trans(skb, dev);
- memset(skb->cb, 0, sizeof(skb->cb));
- netif_receive_skb(skb);
- }
+ if (skb) {
+ /* deliver to local stack */
+ skb->protocol = eth_type_trans(skb, dev);
+ memset(skb->cb, 0, sizeof(skb->cb));
+ netif_receive_skb(skb);
}
if (xmit_skb) {
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 4d73c46df862..88c81616f8f7 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -271,10 +271,11 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
return true;
}
-static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
- bool was_hw_scan)
+static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
{
struct ieee80211_local *local = hw_to_local(hw);
+ bool hw_scan = local->ops->hw_scan;
+ bool was_scanning = local->scanning;
lockdep_assert_held(&local->mtx);
@@ -290,7 +291,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
if (WARN_ON(!local->scan_req))
return;
- if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) {
+ if (hw_scan && !aborted && ieee80211_prep_hw_scan(local)) {
int rc;
rc = drv_hw_scan(local,
@@ -316,7 +317,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
/* Set power back to normal operating levels. */
ieee80211_hw_config(local, 0);
- if (!was_hw_scan) {
+ if (!hw_scan) {
ieee80211_configure_filter(local);
drv_sw_scan_complete(local);
ieee80211_offchannel_return(local);
@@ -327,7 +328,8 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
ieee80211_mlme_notify_scan_completed(local);
ieee80211_ibss_notify_scan_completed(local);
ieee80211_mesh_notify_scan_completed(local);
- ieee80211_start_next_roc(local);
+ if (was_scanning)
+ ieee80211_start_next_roc(local);
}
void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
@@ -747,7 +749,7 @@ void ieee80211_scan_work(struct work_struct *work)
container_of(work, struct ieee80211_local, scan_work.work);
struct ieee80211_sub_if_data *sdata;
unsigned long next_delay = 0;
- bool aborted, hw_scan;
+ bool aborted;
mutex_lock(&local->mtx);
@@ -786,14 +788,6 @@ void ieee80211_scan_work(struct work_struct *work)
}
/*
- * Avoid re-scheduling when the sdata is going away.
- */
- if (!ieee80211_sdata_running(sdata)) {
- aborted = true;
- goto out_complete;
- }
-
- /*
* as long as no delay is required advance immediately
* without scheduling a new work
*/
@@ -834,8 +828,7 @@ void ieee80211_scan_work(struct work_struct *work)
goto out;
out_complete:
- hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning);
- __ieee80211_scan_completed(&local->hw, aborted, hw_scan);
+ __ieee80211_scan_completed(&local->hw, aborted);
out:
mutex_unlock(&local->mtx);
}
@@ -973,13 +966,13 @@ void ieee80211_scan_cancel(struct ieee80211_local *local)
*/
cancel_delayed_work(&local->scan_work);
/* and clean up */
- __ieee80211_scan_completed(&local->hw, true, false);
+ __ieee80211_scan_completed(&local->hw, true);
out:
mutex_unlock(&local->mtx);
}
-int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
- struct cfg80211_sched_scan_request *req)
+int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_sched_scan_request *req)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_sched_scan_ies sched_scan_ies = {};
@@ -989,17 +982,10 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
iebufsz = 2 + IEEE80211_MAX_SSID_LEN +
local->scan_ies_len + req->ie_len;
- mutex_lock(&local->mtx);
-
- if (rcu_access_pointer(local->sched_scan_sdata)) {
- ret = -EBUSY;
- goto out;
- }
+ lockdep_assert_held(&local->mtx);
- if (!local->ops->sched_scan_start) {
- ret = -ENOTSUPP;
- goto out;
- }
+ if (!local->ops->sched_scan_start)
+ return -ENOTSUPP;
for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
if (!local->hw.wiphy->bands[i])
@@ -1020,13 +1006,39 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
}
ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);
- if (ret == 0)
+ if (ret == 0) {
rcu_assign_pointer(local->sched_scan_sdata, sdata);
+ local->sched_scan_req = req;
+ }
out_free:
while (i > 0)
kfree(sched_scan_ies.ie[--i]);
-out:
+
+ if (ret) {
+ /* Clean in case of failure after HW restart or upon resume. */
+ rcu_assign_pointer(local->sched_scan_sdata, NULL);
+ local->sched_scan_req = NULL;
+ }
+
+ return ret;
+}
+
+int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_sched_scan_request *req)
+{
+ struct ieee80211_local *local = sdata->local;
+ int ret;
+
+ mutex_lock(&local->mtx);
+
+ if (rcu_access_pointer(local->sched_scan_sdata)) {
+ mutex_unlock(&local->mtx);
+ return -EBUSY;
+ }
+
+ ret = __ieee80211_request_sched_scan_start(sdata, req);
+
mutex_unlock(&local->mtx);
return ret;
}
@@ -1043,6 +1055,9 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
goto out;
}
+ /* We don't want to restart sched scan anymore. */
+ local->sched_scan_req = NULL;
+
if (rcu_access_pointer(local->sched_scan_sdata))
drv_sched_scan_stop(local, sdata);
@@ -1077,6 +1092,9 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work)
rcu_assign_pointer(local->sched_scan_sdata, NULL);
+ /* If sched scan was aborted by the driver. */
+ local->sched_scan_req = NULL;
+
mutex_unlock(&local->mtx);
cfg80211_sched_scan_stopped(local->hw.wiphy);
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 8ed97f76c3cf..4576ba0ff221 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -99,23 +99,6 @@ static void cleanup_single_sta(struct sta_info *sta)
struct ieee80211_local *local = sdata->local;
struct ps_data *ps;
- /*
- * At this point, when being called as call_rcu callback,
- * neither mac80211 nor the driver can reference this
- * sta struct any more except by still existing timers
- * associated with this station that we clean up below.
- *
- * Note though that this still uses the sdata and even
- * calls the driver in AP and mesh mode, so interfaces
- * of those types mush use call sta_info_flush_cleanup()
- * (typically via sta_info_flush()) before deconfiguring
- * the driver.
- *
- * In station mode, nothing happens here so it doesn't
- * have to (and doesn't) do that, this is intentional to
- * speed up roaming.
- */
-
if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
@@ -160,37 +143,6 @@ static void cleanup_single_sta(struct sta_info *sta)
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;
-
- 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 */
struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,
const u8 *addr)
@@ -842,7 +794,7 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
return have_buffered;
}
-int __must_check __sta_info_destroy(struct sta_info *sta)
+static int __must_check __sta_info_destroy_part1(struct sta_info *sta)
{
struct ieee80211_local *local;
struct ieee80211_sub_if_data *sdata;
@@ -868,12 +820,35 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
ieee80211_sta_tear_down_BA_sessions(sta, AGG_STOP_DESTROY_STA);
ret = sta_info_hash_del(local, sta);
- if (ret)
+ if (WARN_ON(ret))
return ret;
list_del_rcu(&sta->list);
- /* this always calls synchronize_net() */
+ drv_sta_pre_rcu_remove(local, sta->sdata, sta);
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
+ rcu_access_pointer(sdata->u.vlan.sta) == sta)
+ RCU_INIT_POINTER(sdata->u.vlan.sta, NULL);
+
+ return 0;
+}
+
+static void __sta_info_destroy_part2(struct sta_info *sta)
+{
+ struct ieee80211_local *local = sta->local;
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ int ret;
+
+ /*
+ * NOTE: This assumes at least synchronize_net() was done
+ * after _part1 and before _part2!
+ */
+
+ might_sleep();
+ lockdep_assert_held(&local->sta_mtx);
+
+ /* now keys can no longer be reached */
ieee80211_free_sta_keys(local, sta);
sta->dead = true;
@@ -881,9 +856,6 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
local->num_sta--;
local->sta_generation++;
- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
- RCU_INIT_POINTER(sdata->u.vlan.sta, NULL);
-
while (sta->sta_state > IEEE80211_STA_NONE) {
ret = sta_info_move_state(sta, sta->sta_state - 1);
if (ret) {
@@ -906,7 +878,19 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
ieee80211_sta_debugfs_remove(sta);
ieee80211_recalc_min_chandef(sdata);
- call_rcu(&sta->rcu_head, free_sta_rcu);
+ cleanup_single_sta(sta);
+}
+
+int __must_check __sta_info_destroy(struct sta_info *sta)
+{
+ int err = __sta_info_destroy_part1(sta);
+
+ if (err)
+ return err;
+
+ synchronize_net();
+
+ __sta_info_destroy_part2(sta);
return 0;
}
@@ -976,32 +960,38 @@ void sta_info_stop(struct ieee80211_local *local)
}
-int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata)
+int __sta_info_flush(struct ieee80211_sub_if_data *sdata, bool vlans)
{
struct ieee80211_local *local = sdata->local;
struct sta_info *sta, *tmp;
+ LIST_HEAD(free_list);
int ret = 0;
might_sleep();
+ WARN_ON(vlans && sdata->vif.type != NL80211_IFTYPE_AP);
+ WARN_ON(vlans && !sdata->bss);
+
mutex_lock(&local->sta_mtx);
list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
- if (sdata == sta->sdata) {
- WARN_ON(__sta_info_destroy(sta));
+ if (sdata == sta->sdata ||
+ (vlans && sdata->bss == sta->sdata->bss)) {
+ if (!WARN_ON(__sta_info_destroy_part1(sta)))
+ list_add(&sta->free_list, &free_list);
ret++;
}
}
+
+ if (!list_empty(&free_list)) {
+ synchronize_net();
+ list_for_each_entry_safe(sta, tmp, &free_list, free_list)
+ __sta_info_destroy_part2(sta);
+ }
mutex_unlock(&local->sta_mtx);
return ret;
}
-void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata)
-{
- ieee80211_cleanup_sdata_stas(sdata);
- cancel_work_sync(&sdata->cleanup_stations_wk);
-}
-
void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
unsigned long exp_time)
{
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 0218caf5c14a..d77ff7090630 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -247,6 +247,7 @@ struct ieee80211_tx_latency_stat {
* mac80211 is communicating with.
*
* @list: global linked list entry
+ * @free_list: list entry for keeping track of stations to free
* @hnext: hash table linked list pointer
* @local: pointer to the global information
* @sdata: virtual interface this station belongs to
@@ -329,7 +330,7 @@ struct ieee80211_tx_latency_stat {
*/
struct sta_info {
/* General information, mostly static */
- struct list_head list;
+ struct list_head list, free_list;
struct rcu_head rcu_head;
struct sta_info __rcu *hnext;
struct ieee80211_local *local;
@@ -605,21 +606,6 @@ void sta_info_recalc_tim(struct sta_info *sta);
void sta_info_init(struct ieee80211_local *local);
void sta_info_stop(struct ieee80211_local *local);
-int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata);
-
-/**
- * sta_info_flush_cleanup - flush the sta_info cleanup queue
- * @sdata: the interface
- *
- * Flushes the sta_info cleanup queue for a given interface;
- * this is necessary before the interface is removed or, for
- * AP/mesh interfaces, before it is deconfigured.
- *
- * Note an rcu_barrier() must precede the function, after all
- * stations have been flushed/removed to ensure the call_rcu()
- * calls that add stations to the cleanup queue have completed.
- */
-void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata);
/**
* sta_info_flush - flush matching STA entries from the STA table
@@ -627,15 +613,13 @@ void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata);
* Returns the number of removed STA entries.
*
* @sdata: sdata to remove all stations from
+ * @vlans: if the given interface is an AP interface, also flush VLANs
*/
+int __sta_info_flush(struct ieee80211_sub_if_data *sdata, bool vlans);
+
static inline int sta_info_flush(struct ieee80211_sub_if_data *sdata)
{
- int ret = sta_info_flush_defer(sdata);
-
- rcu_barrier();
- sta_info_flush_cleanup(sdata);
-
- return ret;
+ return __sta_info_flush(sdata, false);
}
void sta_set_rate_info_tx(struct sta_info *sta,
@@ -651,6 +635,4 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta);
void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta);
void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta);
-void ieee80211_cleanup_sdata_stas(struct ieee80211_sub_if_data *sdata);
-
#endif /* STA_INFO_H */
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index e9ccf22f6dd9..3a669d7ec7ad 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -443,30 +443,6 @@ TRACE_EVENT(drv_prepare_multicast,
)
);
-TRACE_EVENT(drv_set_multicast_list,
- TP_PROTO(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata, int mc_count),
-
- TP_ARGS(local, sdata, mc_count),
-
- TP_STRUCT__entry(
- LOCAL_ENTRY
- __field(bool, allmulti)
- __field(int, mc_count)
- ),
-
- TP_fast_assign(
- LOCAL_ASSIGN;
- __entry->allmulti = sdata->flags & IEEE80211_SDATA_ALLMULTI;
- __entry->mc_count = mc_count;
- ),
-
- TP_printk(
- LOCAL_PR_FMT " configure mc filter, count=%d, allmulti=%d",
- LOCAL_PR_ARG, __entry->mc_count, __entry->allmulti
- )
-);
-
TRACE_EVENT(drv_configure_filter,
TP_PROTO(struct ieee80211_local *local,
unsigned int changed_flags,
@@ -790,7 +766,7 @@ TRACE_EVENT(drv_sta_rc_update,
)
);
-TRACE_EVENT(drv_sta_add,
+DECLARE_EVENT_CLASS(sta_event,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta *sta),
@@ -815,29 +791,25 @@ TRACE_EVENT(drv_sta_add,
)
);
-TRACE_EVENT(drv_sta_remove,
+DEFINE_EVENT(sta_event, drv_sta_add,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta *sta),
+ TP_ARGS(local, sdata, sta)
+);
- TP_ARGS(local, sdata, sta),
-
- TP_STRUCT__entry(
- LOCAL_ENTRY
- VIF_ENTRY
- STA_ENTRY
- ),
-
- TP_fast_assign(
- LOCAL_ASSIGN;
- VIF_ASSIGN;
- STA_ASSIGN;
- ),
+DEFINE_EVENT(sta_event, drv_sta_remove,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta),
+ TP_ARGS(local, sdata, sta)
+);
- TP_printk(
- LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT,
- LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG
- )
+DEFINE_EVENT(sta_event, drv_sta_pre_rcu_remove,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta),
+ TP_ARGS(local, sdata, sta)
);
TRACE_EVENT(drv_conf_tx,
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 6d59e21cdb9f..2f0e176e7989 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2549,7 +2549,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
*/
skb = dev_alloc_skb(local->tx_headroom +
beacon->head_len +
- beacon->tail_len + 256);
+ beacon->tail_len + 256 +
+ local->hw.extra_beacon_tailroom);
if (!skb)
goto out;
@@ -2581,7 +2582,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
ieee80211_update_csa(sdata, presp);
- skb = dev_alloc_skb(local->tx_headroom + presp->head_len);
+ skb = dev_alloc_skb(local->tx_headroom + presp->head_len +
+ local->hw.extra_beacon_tailroom);
if (!skb)
goto out;
skb_reserve(skb, local->tx_headroom);
@@ -2602,13 +2604,13 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
ieee80211_update_csa(sdata, bcn);
if (ifmsh->sync_ops)
- ifmsh->sync_ops->adjust_tbtt(
- sdata);
+ ifmsh->sync_ops->adjust_tbtt(sdata, bcn);
skb = dev_alloc_skb(local->tx_headroom +
bcn->head_len +
256 + /* TIM IE */
- bcn->tail_len);
+ bcn->tail_len +
+ local->hw.extra_beacon_tailroom);
if (!skb)
goto out;
skb_reserve(skb, local->tx_headroom);
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 875e172c001c..591b46b72462 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -642,6 +642,17 @@ void ieee80211_iterate_active_interfaces_rtnl(
}
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl);
+struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+
+ if (!ieee80211_sdata_running(sdata) ||
+ !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
+ return NULL;
+ return &sdata->vif;
+}
+EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif);
+
/*
* Nothing should have been stuffed into the workqueue during
* the suspend->resume cycle. If this WARN is seen then there
@@ -1451,6 +1462,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
struct sta_info *sta;
int res, i;
bool reconfig_due_to_wowlan = false;
+ struct ieee80211_sub_if_data *sched_scan_sdata;
+ bool sched_scan_stopped = false;
#ifdef CONFIG_PM
if (local->suspended)
@@ -1754,6 +1767,27 @@ int ieee80211_reconfig(struct ieee80211_local *local)
#else
WARN_ON(1);
#endif
+
+ /*
+ * Reconfigure sched scan if it was interrupted by FW restart or
+ * suspend.
+ */
+ mutex_lock(&local->mtx);
+ sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata,
+ lockdep_is_held(&local->mtx));
+ if (sched_scan_sdata && local->sched_scan_req)
+ /*
+ * Sched scan stopped, but we don't want to report it. Instead,
+ * we're trying to reschedule.
+ */
+ if (__ieee80211_request_sched_scan_start(sched_scan_sdata,
+ local->sched_scan_req))
+ sched_scan_stopped = true;
+ mutex_unlock(&local->mtx);
+
+ if (sched_scan_stopped)
+ cfg80211_sched_scan_stopped(local->hw.wiphy);
+
return 0;
}