aboutsummaryrefslogtreecommitdiff
path: root/net/mac80211
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/agg-rx.c3
-rw-r--r--net/mac80211/agg-tx.c59
-rw-r--r--net/mac80211/cfg.c135
-rw-r--r--net/mac80211/debugfs.c2
-rw-r--r--net/mac80211/debugfs_key.c21
-rw-r--r--net/mac80211/driver-ops.h56
-rw-r--r--net/mac80211/driver-trace.h228
-rw-r--r--net/mac80211/ht.c27
-rw-r--r--net/mac80211/ibss.c11
-rw-r--r--net/mac80211/ieee80211_i.h41
-rw-r--r--net/mac80211/iface.c3
-rw-r--r--net/mac80211/key.c30
-rw-r--r--net/mac80211/key.h4
-rw-r--r--net/mac80211/main.c37
-rw-r--r--net/mac80211/mesh.c47
-rw-r--r--net/mac80211/mesh.h6
-rw-r--r--net/mac80211/mesh_hwmp.c38
-rw-r--r--net/mac80211/mesh_pathtbl.c123
-rw-r--r--net/mac80211/mesh_plink.c83
-rw-r--r--net/mac80211/mlme.c22
-rw-r--r--net/mac80211/pm.c13
-rw-r--r--net/mac80211/rc80211_minstrel.c4
-rw-r--r--net/mac80211/rc80211_minstrel_ht.c27
-rw-r--r--net/mac80211/rx.c17
-rw-r--r--net/mac80211/scan.c122
-rw-r--r--net/mac80211/sta_info.c19
-rw-r--r--net/mac80211/sta_info.h50
-rw-r--r--net/mac80211/tx.c10
-rw-r--r--net/mac80211/util.c19
29 files changed, 835 insertions, 422 deletions
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index 0c9d0c07eae..9c0d76cdca9 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -63,7 +63,8 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
lockdep_assert_held(&sta->ampdu_mlme.mtx);
- tid_rx = sta->ampdu_mlme.tid_rx[tid];
+ tid_rx = rcu_dereference_protected(sta->ampdu_mlme.tid_rx[tid],
+ lockdep_is_held(&sta->ampdu_mlme.mtx));
if (!tid_rx)
return;
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index 63d852cb4ca..cd5125f77cc 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -136,6 +136,14 @@ void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u1
ieee80211_tx_skb(sdata, skb);
}
+void ieee80211_assign_tid_tx(struct sta_info *sta, int tid,
+ struct tid_ampdu_tx *tid_tx)
+{
+ lockdep_assert_held(&sta->ampdu_mlme.mtx);
+ lockdep_assert_held(&sta->lock);
+ rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx);
+}
+
static void kfree_tid_tx(struct rcu_head *rcu_head)
{
struct tid_ampdu_tx *tid_tx =
@@ -149,19 +157,22 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
bool tx)
{
struct ieee80211_local *local = sta->local;
- struct tid_ampdu_tx *tid_tx = sta->ampdu_mlme.tid_tx[tid];
+ struct tid_ampdu_tx *tid_tx;
int ret;
lockdep_assert_held(&sta->ampdu_mlme.mtx);
- if (!tid_tx)
- return -ENOENT;
-
spin_lock_bh(&sta->lock);
+ tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
+ if (!tid_tx) {
+ spin_unlock_bh(&sta->lock);
+ return -ENOENT;
+ }
+
if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) {
/* not even started yet! */
- rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], NULL);
+ ieee80211_assign_tid_tx(sta, tid, NULL);
spin_unlock_bh(&sta->lock);
call_rcu(&tid_tx->rcu_head, kfree_tid_tx);
return 0;
@@ -283,13 +294,13 @@ ieee80211_wake_queue_agg(struct ieee80211_local *local, int tid)
void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
{
- struct tid_ampdu_tx *tid_tx = sta->ampdu_mlme.tid_tx[tid];
+ struct tid_ampdu_tx *tid_tx;
struct ieee80211_local *local = sta->local;
struct ieee80211_sub_if_data *sdata = sta->sdata;
u16 start_seq_num;
int ret;
- lockdep_assert_held(&sta->ampdu_mlme.mtx);
+ tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
/*
* While we're asking the driver about the aggregation,
@@ -318,7 +329,7 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
" tid %d\n", tid);
#endif
spin_lock_bh(&sta->lock);
- rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], NULL);
+ ieee80211_assign_tid_tx(sta, tid, NULL);
spin_unlock_bh(&sta->lock);
ieee80211_wake_queue_agg(local, tid);
@@ -396,9 +407,9 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
goto err_unlock_sta;
}
- tid_tx = sta->ampdu_mlme.tid_tx[tid];
+ tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
/* check if the TID is not in aggregation flow already */
- if (tid_tx) {
+ if (tid_tx || sta->ampdu_mlme.tid_start_tx[tid]) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "BA request denied - session is not "
"idle on tid %u\n", tid);
@@ -433,8 +444,11 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
sta->ampdu_mlme.dialog_token_allocator++;
tid_tx->dialog_token = sta->ampdu_mlme.dialog_token_allocator;
- /* finally, assign it to the array */
- rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx);
+ /*
+ * Finally, assign it to the start array; the work item will
+ * collect it and move it to the normal array.
+ */
+ sta->ampdu_mlme.tid_start_tx[tid] = tid_tx;
ieee80211_queue_work(&local->hw, &sta->ampdu_mlme.work);
@@ -480,16 +494,19 @@ ieee80211_agg_splice_finish(struct ieee80211_local *local, u16 tid)
static void ieee80211_agg_tx_operational(struct ieee80211_local *local,
struct sta_info *sta, u16 tid)
{
+ struct tid_ampdu_tx *tid_tx;
+
lockdep_assert_held(&sta->ampdu_mlme.mtx);
+ tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
+
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Aggregation is on for tid %d\n", tid);
#endif
drv_ampdu_action(local, sta->sdata,
IEEE80211_AMPDU_TX_OPERATIONAL,
- &sta->sta, tid, NULL,
- sta->ampdu_mlme.tid_tx[tid]->buf_size);
+ &sta->sta, tid, NULL, tid_tx->buf_size);
/*
* synchronize with TX path, while splicing the TX path
@@ -497,13 +514,13 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local,
*/
spin_lock_bh(&sta->lock);
- ieee80211_agg_splice_packets(local, sta->ampdu_mlme.tid_tx[tid], tid);
+ ieee80211_agg_splice_packets(local, tid_tx, tid);
/*
* Now mark as operational. This will be visible
* in the TX path, and lets it go lock-free in
* the common case.
*/
- set_bit(HT_AGG_STATE_OPERATIONAL, &sta->ampdu_mlme.tid_tx[tid]->state);
+ set_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state);
ieee80211_agg_splice_finish(local, tid);
spin_unlock_bh(&sta->lock);
@@ -537,7 +554,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid)
}
mutex_lock(&sta->ampdu_mlme.mtx);
- tid_tx = sta->ampdu_mlme.tid_tx[tid];
+ tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
if (WARN_ON(!tid_tx)) {
#ifdef CONFIG_MAC80211_HT_DEBUG
@@ -615,7 +632,7 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)
return -EINVAL;
spin_lock_bh(&sta->lock);
- tid_tx = sta->ampdu_mlme.tid_tx[tid];
+ tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
if (!tid_tx) {
ret = -ENOENT;
@@ -671,7 +688,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid)
mutex_lock(&sta->ampdu_mlme.mtx);
spin_lock_bh(&sta->lock);
- tid_tx = sta->ampdu_mlme.tid_tx[tid];
+ tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
if (!tid_tx || !test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) {
#ifdef CONFIG_MAC80211_HT_DEBUG
@@ -697,7 +714,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid)
ieee80211_agg_splice_packets(local, tid_tx, tid);
/* future packets must not find the tid_tx struct any more */
- rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], NULL);
+ ieee80211_assign_tid_tx(sta, tid, NULL);
ieee80211_agg_splice_finish(local, tid);
@@ -752,7 +769,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
mutex_lock(&sta->ampdu_mlme.mtx);
- tid_tx = sta->ampdu_mlme.tid_tx[tid];
+ tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
if (!tid_tx)
goto out;
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 12d52cec951..be70c70d3f5 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -136,7 +136,10 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
mutex_lock(&sdata->local->sta_mtx);
if (mac_addr) {
- sta = sta_info_get_bss(sdata, mac_addr);
+ if (ieee80211_vif_is_mesh(&sdata->vif))
+ sta = sta_info_get(sdata, mac_addr);
+ else
+ sta = sta_info_get_bss(sdata, mac_addr);
if (!sta) {
ieee80211_key_free(sdata->local, key);
err = -ENOENT;
@@ -157,13 +160,14 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
u8 key_idx, bool pairwise, const u8 *mac_addr)
{
- struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
+ struct ieee80211_key *key = NULL;
int ret;
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- mutex_lock(&sdata->local->sta_mtx);
+ mutex_lock(&local->sta_mtx);
+ mutex_lock(&local->key_mtx);
if (mac_addr) {
ret = -ENOENT;
@@ -172,33 +176,24 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
if (!sta)
goto out_unlock;
- if (pairwise) {
- if (sta->ptk) {
- ieee80211_key_free(sdata->local, sta->ptk);
- ret = 0;
- }
- } else {
- if (sta->gtk[key_idx]) {
- ieee80211_key_free(sdata->local,
- sta->gtk[key_idx]);
- ret = 0;
- }
- }
-
- goto out_unlock;
- }
+ if (pairwise)
+ key = key_mtx_dereference(local, sta->ptk);
+ else
+ key = key_mtx_dereference(local, sta->gtk[key_idx]);
+ } else
+ key = key_mtx_dereference(local, sdata->keys[key_idx]);
- if (!sdata->keys[key_idx]) {
+ if (!key) {
ret = -ENOENT;
goto out_unlock;
}
- ieee80211_key_free(sdata->local, sdata->keys[key_idx]);
- WARN_ON(sdata->keys[key_idx]);
+ __ieee80211_key_free(key);
ret = 0;
out_unlock:
- mutex_unlock(&sdata->local->sta_mtx);
+ mutex_unlock(&local->key_mtx);
+ mutex_unlock(&local->sta_mtx);
return ret;
}
@@ -228,11 +223,11 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
goto out;
if (pairwise)
- key = sta->ptk;
+ key = rcu_dereference(sta->ptk);
else if (key_idx < NUM_DEFAULT_KEYS)
- key = sta->gtk[key_idx];
+ key = rcu_dereference(sta->gtk[key_idx]);
} else
- key = sdata->keys[key_idx];
+ key = rcu_dereference(sdata->keys[key_idx]);
if (!key)
goto out;
@@ -468,7 +463,7 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata,
int size;
int err = -EINVAL;
- old = sdata->u.ap.beacon;
+ old = rtnl_dereference(sdata->u.ap.beacon);
/* head must not be zero-length */
if (params->head && !params->head_len)
@@ -563,8 +558,7 @@ static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev,
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- old = sdata->u.ap.beacon;
-
+ old = rtnl_dereference(sdata->u.ap.beacon);
if (old)
return -EALREADY;
@@ -579,8 +573,7 @@ static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev,
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- old = sdata->u.ap.beacon;
-
+ old = rtnl_dereference(sdata->u.ap.beacon);
if (!old)
return -ENOENT;
@@ -594,8 +587,7 @@ static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev)
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- old = sdata->u.ap.beacon;
-
+ old = rtnl_dereference(sdata->u.ap.beacon);
if (!old)
return -ENOENT;
@@ -734,15 +726,29 @@ static void sta_apply_parameters(struct ieee80211_local *local,
params->ht_capa,
&sta->sta.ht_cap);
- if (ieee80211_vif_is_mesh(&sdata->vif) && params->plink_action) {
- switch (params->plink_action) {
- case PLINK_ACTION_OPEN:
- mesh_plink_open(sta);
- break;
- case PLINK_ACTION_BLOCK:
- mesh_plink_block(sta);
- break;
- }
+ if (ieee80211_vif_is_mesh(&sdata->vif)) {
+#ifdef CONFIG_MAC80211_MESH
+ if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED)
+ switch (params->plink_state) {
+ case NL80211_PLINK_LISTEN:
+ case NL80211_PLINK_ESTAB:
+ case NL80211_PLINK_BLOCKED:
+ sta->plink_state = params->plink_state;
+ break;
+ default:
+ /* nothing */
+ break;
+ }
+ else
+ switch (params->plink_action) {
+ case PLINK_ACTION_OPEN:
+ mesh_plink_open(sta);
+ break;
+ case PLINK_ACTION_BLOCK:
+ mesh_plink_block(sta);
+ break;
+ }
+#endif
}
}
@@ -943,8 +949,10 @@ static int ieee80211_change_mpath(struct wiphy *wiphy,
static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
struct mpath_info *pinfo)
{
- if (mpath->next_hop)
- memcpy(next_hop, mpath->next_hop->sta.addr, ETH_ALEN);
+ struct sta_info *next_hop_sta = rcu_dereference(mpath->next_hop);
+
+ if (next_hop_sta)
+ memcpy(next_hop, next_hop_sta->sta.addr, ETH_ALEN);
else
memset(next_hop, 0, ETH_ALEN);
@@ -1064,7 +1072,11 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh,
memcpy(ifmsh->mesh_id, setup->mesh_id, ifmsh->mesh_id_len);
ifmsh->mesh_pp_id = setup->path_sel_proto;
ifmsh->mesh_pm_id = setup->path_metric;
- ifmsh->is_secure = setup->is_secure;
+ ifmsh->security = IEEE80211_MESH_SEC_NONE;
+ if (setup->is_authenticated)
+ ifmsh->security |= IEEE80211_MESH_SEC_AUTHED;
+ if (setup->is_secure)
+ ifmsh->security |= IEEE80211_MESH_SEC_SECURED;
return 0;
}
@@ -1297,9 +1309,10 @@ static int ieee80211_set_channel(struct wiphy *wiphy,
}
#ifdef CONFIG_PM
-static int ieee80211_suspend(struct wiphy *wiphy)
+static int ieee80211_suspend(struct wiphy *wiphy,
+ struct cfg80211_wowlan *wowlan)
{
- return __ieee80211_suspend(wiphy_priv(wiphy));
+ return __ieee80211_suspend(wiphy_priv(wiphy), wowlan);
}
static int ieee80211_resume(struct wiphy *wiphy)
@@ -1342,6 +1355,30 @@ static int ieee80211_scan(struct wiphy *wiphy,
return ieee80211_request_scan(sdata, req);
}
+static int
+ieee80211_sched_scan_start(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_sched_scan_request *req)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (!sdata->local->ops->sched_scan_start)
+ return -EOPNOTSUPP;
+
+ return ieee80211_request_sched_scan_start(sdata, req);
+}
+
+static int
+ieee80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (!sdata->local->ops->sched_scan_stop)
+ return -EOPNOTSUPP;
+
+ return ieee80211_request_sched_scan_stop(sdata);
+}
+
static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_auth_request *req)
{
@@ -2083,6 +2120,8 @@ struct cfg80211_ops mac80211_config_ops = {
.suspend = ieee80211_suspend,
.resume = ieee80211_resume,
.scan = ieee80211_scan,
+ .sched_scan_start = ieee80211_sched_scan_start,
+ .sched_scan_stop = ieee80211_sched_scan_stop,
.auth = ieee80211_auth,
.assoc = ieee80211_assoc,
.deauth = ieee80211_deauth,
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 0a602dbfdb2..186e02f7cc3 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -135,7 +135,7 @@ static ssize_t reset_write(struct file *file, const char __user *user_buf,
struct ieee80211_local *local = file->private_data;
rtnl_lock();
- __ieee80211_suspend(&local->hw);
+ __ieee80211_suspend(&local->hw, NULL);
__ieee80211_resume(&local->hw);
rtnl_unlock();
diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c
index f7ef3477c24..33c58b85c91 100644
--- a/net/mac80211/debugfs_key.c
+++ b/net/mac80211/debugfs_key.c
@@ -241,16 +241,12 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key)
if (!key->debugfs.dir)
return;
- rcu_read_lock();
- sta = rcu_dereference(key->sta);
- if (sta)
+ sta = key->sta;
+ if (sta) {
sprintf(buf, "../../stations/%pM", sta->sta.addr);
- rcu_read_unlock();
-
- /* using sta as a boolean is fine outside RCU lock */
- if (sta)
key->debugfs.stalink =
debugfs_create_symlink("station", key->debugfs.dir, buf);
+ }
DEBUGFS_ADD(keylen);
DEBUGFS_ADD(flags);
@@ -286,7 +282,8 @@ void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata)
lockdep_assert_held(&sdata->local->key_mtx);
if (sdata->default_unicast_key) {
- key = sdata->default_unicast_key;
+ key = key_mtx_dereference(sdata->local,
+ sdata->default_unicast_key);
sprintf(buf, "../keys/%d", key->debugfs.cnt);
sdata->debugfs.default_unicast_key =
debugfs_create_symlink("default_unicast_key",
@@ -297,7 +294,8 @@ void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata)
}
if (sdata->default_multicast_key) {
- key = sdata->default_multicast_key;
+ key = key_mtx_dereference(sdata->local,
+ sdata->default_multicast_key);
sprintf(buf, "../keys/%d", key->debugfs.cnt);
sdata->debugfs.default_multicast_key =
debugfs_create_symlink("default_multicast_key",
@@ -316,9 +314,8 @@ void ieee80211_debugfs_key_add_mgmt_default(struct ieee80211_sub_if_data *sdata)
if (!sdata->debugfs.dir)
return;
- /* this is running under the key lock */
-
- key = sdata->default_mgmt_key;
+ key = key_mtx_dereference(sdata->local,
+ sdata->default_mgmt_key);
if (key) {
sprintf(buf, "../keys/%d", key->debugfs.cnt);
sdata->debugfs.default_mgmt_key =
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 2ddb56e5b51..eebf7a67daf 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -41,6 +41,33 @@ static inline void drv_stop(struct ieee80211_local *local)
local->started = false;
}
+#ifdef CONFIG_PM
+static inline int drv_suspend(struct ieee80211_local *local,
+ struct cfg80211_wowlan *wowlan)
+{
+ int ret;
+
+ might_sleep();
+
+ trace_drv_suspend(local);
+ ret = local->ops->suspend(&local->hw, wowlan);
+ trace_drv_return_int(local, ret);
+ return ret;
+}
+
+static inline int drv_resume(struct ieee80211_local *local)
+{
+ int ret;
+
+ might_sleep();
+
+ trace_drv_resume(local);
+ ret = local->ops->resume(&local->hw);
+ trace_drv_return_int(local, ret);
+ return ret;
+}
+#endif
+
static inline int drv_add_interface(struct ieee80211_local *local,
struct ieee80211_vif *vif)
{
@@ -185,12 +212,39 @@ static inline int drv_hw_scan(struct ieee80211_local *local,
might_sleep();
- trace_drv_hw_scan(local, sdata, req);
+ trace_drv_hw_scan(local, sdata);
ret = local->ops->hw_scan(&local->hw, &sdata->vif, req);
trace_drv_return_int(local, ret);
return ret;
}
+static inline int
+drv_sched_scan_start(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_sched_scan_request *req,
+ struct ieee80211_sched_scan_ies *ies)
+{
+ int ret;
+
+ might_sleep();
+
+ trace_drv_sched_scan_start(local, sdata);
+ ret = local->ops->sched_scan_start(&local->hw, &sdata->vif,
+ req, ies);
+ trace_drv_return_int(local, ret);
+ return ret;
+}
+
+static inline void drv_sched_scan_stop(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
+{
+ might_sleep();
+
+ trace_drv_sched_scan_stop(local, sdata);
+ local->ops->sched_scan_stop(&local->hw, &sdata->vif);
+ trace_drv_return_void(local);
+}
+
static inline void drv_sw_scan_start(struct ieee80211_local *local)
{
might_sleep();
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h
index 191e834ec46..ed9edcbd9aa 100644
--- a/net/mac80211/driver-trace.h
+++ b/net/mac80211/driver-trace.h
@@ -55,6 +55,70 @@ DECLARE_EVENT_CLASS(local_only_evt,
TP_printk(LOCAL_PR_FMT, LOCAL_PR_ARG)
);
+DECLARE_EVENT_CLASS(local_sdata_addr_evt,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata),
+ TP_ARGS(local, sdata),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ __array(char, addr, 6)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ memcpy(__entry->addr, sdata->vif.addr, 6);
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT " addr:%pM",
+ LOCAL_PR_ARG, VIF_PR_ARG, __entry->addr
+ )
+);
+
+DECLARE_EVENT_CLASS(local_u32_evt,
+ TP_PROTO(struct ieee80211_local *local, u32 value),
+ TP_ARGS(local, value),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __field(u32, value)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ __entry->value = value;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT " value:%d",
+ LOCAL_PR_ARG, __entry->value
+ )
+);
+
+DECLARE_EVENT_CLASS(local_sdata_evt,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata),
+ TP_ARGS(local, sdata),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT,
+ LOCAL_PR_ARG, VIF_PR_ARG
+ )
+);
+
DEFINE_EVENT(local_only_evt, drv_return_void,
TP_PROTO(struct ieee80211_local *local),
TP_ARGS(local)
@@ -108,33 +172,25 @@ DEFINE_EVENT(local_only_evt, drv_start,
TP_ARGS(local)
);
+DEFINE_EVENT(local_only_evt, drv_suspend,
+ TP_PROTO(struct ieee80211_local *local),
+ TP_ARGS(local)
+);
+
+DEFINE_EVENT(local_only_evt, drv_resume,
+ TP_PROTO(struct ieee80211_local *local),
+ TP_ARGS(local)
+);
+
DEFINE_EVENT(local_only_evt, drv_stop,
TP_PROTO(struct ieee80211_local *local),
TP_ARGS(local)
);
-TRACE_EVENT(drv_add_interface,
+DEFINE_EVENT(local_sdata_addr_evt, drv_add_interface,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata),
-
- TP_ARGS(local, sdata),
-
- TP_STRUCT__entry(
- LOCAL_ENTRY
- VIF_ENTRY
- __array(char, addr, 6)
- ),
-
- TP_fast_assign(
- LOCAL_ASSIGN;
- VIF_ASSIGN;
- memcpy(__entry->addr, sdata->vif.addr, 6);
- ),
-
- TP_printk(
- LOCAL_PR_FMT VIF_PR_FMT " addr:%pM",
- LOCAL_PR_ARG, VIF_PR_ARG, __entry->addr
- )
+ TP_ARGS(local, sdata)
);
TRACE_EVENT(drv_change_interface,
@@ -165,27 +221,10 @@ TRACE_EVENT(drv_change_interface,
)
);
-TRACE_EVENT(drv_remove_interface,
- TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata),
-
- TP_ARGS(local, sdata),
-
- TP_STRUCT__entry(
- LOCAL_ENTRY
- VIF_ENTRY
- __array(char, addr, 6)
- ),
-
- TP_fast_assign(
- LOCAL_ASSIGN;
- VIF_ASSIGN;
- memcpy(__entry->addr, sdata->vif.addr, 6);
- ),
-
- TP_printk(
- LOCAL_PR_FMT VIF_PR_FMT " addr:%pM",
- LOCAL_PR_ARG, VIF_PR_ARG, __entry->addr
- )
+DEFINE_EVENT(local_sdata_addr_evt, drv_remove_interface,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata),
+ TP_ARGS(local, sdata)
);
TRACE_EVENT(drv_config,
@@ -415,27 +454,22 @@ TRACE_EVENT(drv_update_tkip_key,
)
);
-TRACE_EVENT(drv_hw_scan,
+DEFINE_EVENT(local_sdata_evt, drv_hw_scan,
TP_PROTO(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- struct cfg80211_scan_request *req),
-
- TP_ARGS(local, sdata, req),
-
- TP_STRUCT__entry(
- LOCAL_ENTRY
- VIF_ENTRY
- ),
+ struct ieee80211_sub_if_data *sdata),
+ TP_ARGS(local, sdata)
+);
- TP_fast_assign(
- LOCAL_ASSIGN;
- VIF_ASSIGN;
- ),
+DEFINE_EVENT(local_sdata_evt, drv_sched_scan_start,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata),
+ TP_ARGS(local, sdata)
+);
- TP_printk(
- LOCAL_PR_FMT VIF_PR_FMT,
- LOCAL_PR_ARG,VIF_PR_ARG
- )
+DEFINE_EVENT(local_sdata_evt, drv_sched_scan_stop,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata),
+ TP_ARGS(local, sdata)
);
DEFINE_EVENT(local_only_evt, drv_sw_scan_start,
@@ -504,46 +538,14 @@ TRACE_EVENT(drv_get_tkip_seq,
)
);
-TRACE_EVENT(drv_set_frag_threshold,
+DEFINE_EVENT(local_u32_evt, drv_set_frag_threshold,
TP_PROTO(struct ieee80211_local *local, u32 value),
-
- TP_ARGS(local, value),
-
- TP_STRUCT__entry(
- LOCAL_ENTRY
- __field(u32, value)
- ),
-
- TP_fast_assign(
- LOCAL_ASSIGN;
- __entry->value = value;
- ),
-
- TP_printk(
- LOCAL_PR_FMT " value:%d",
- LOCAL_PR_ARG, __entry->value
- )
+ TP_ARGS(local, value)
);
-TRACE_EVENT(drv_set_rts_threshold,
+DEFINE_EVENT(local_u32_evt, drv_set_rts_threshold,
TP_PROTO(struct ieee80211_local *local, u32 value),
-
- TP_ARGS(local, value),
-
- TP_STRUCT__entry(
- LOCAL_ENTRY
- __field(u32, value)
- ),
-
- TP_fast_assign(
- LOCAL_ASSIGN;
- __entry->value = value;
- ),
-
- TP_printk(
- LOCAL_PR_FMT " value:%d",
- LOCAL_PR_ARG, __entry->value
- )
+ TP_ARGS(local, value)
);
TRACE_EVENT(drv_set_coverage_class,
@@ -1194,6 +1196,42 @@ TRACE_EVENT(api_scan_completed,
)
);
+TRACE_EVENT(api_sched_scan_results,
+ TP_PROTO(struct ieee80211_local *local),
+
+ TP_ARGS(local),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT, LOCAL_PR_ARG
+ )
+);
+
+TRACE_EVENT(api_sched_scan_stopped,
+ TP_PROTO(struct ieee80211_local *local),
+
+ TP_ARGS(local),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT, LOCAL_PR_ARG
+ )
+);
+
TRACE_EVENT(api_sta_block_awake,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sta *sta, bool block),
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index b9e4b9bd217..591add22bcc 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -140,14 +140,29 @@ void ieee80211_ba_session_work(struct work_struct *work)
sta, tid, WLAN_BACK_RECIPIENT,
WLAN_REASON_QSTA_TIMEOUT, true);
- tid_tx = sta->ampdu_mlme.tid_tx[tid];
- if (!tid_tx)
- continue;
+ tid_tx = sta->ampdu_mlme.tid_start_tx[tid];
+ if (tid_tx) {
+ /*
+ * Assign it over to the normal tid_tx array
+ * where it "goes live".
+ */
+ spin_lock_bh(&sta->lock);
+
+ sta->ampdu_mlme.tid_start_tx[tid] = NULL;
+ /* could there be a race? */
+ if (sta->ampdu_mlme.tid_tx[tid])
+ kfree(tid_tx);
+ else
+ ieee80211_assign_tid_tx(sta, tid, tid_tx);
+ spin_unlock_bh(&sta->lock);
- if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state))
ieee80211_tx_ba_session_handle_start(sta, tid);
- else if (test_and_clear_bit(HT_AGG_STATE_WANT_STOP,
- &tid_tx->state))
+ continue;
+ }
+
+ tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
+ if (tid_tx && test_and_clear_bit(HT_AGG_STATE_WANT_STOP,
+ &tid_tx->state))
___ieee80211_stop_tx_ba_session(sta, tid,
WLAN_BACK_INITIATOR,
true);
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index b81860c9469..421eaa6b0c2 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -662,12 +662,16 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
int tx_last_beacon, len = req->len;
struct sk_buff *skb;
struct ieee80211_mgmt *resp;
+ struct sk_buff *presp;
u8 *pos, *end;
lockdep_assert_held(&ifibss->mtx);
+ presp = rcu_dereference_protected(ifibss->presp,
+ lockdep_is_held(&ifibss->mtx));
+
if (ifibss->state != IEEE80211_IBSS_MLME_JOINED ||
- len < 24 + 2 || !ifibss->presp)
+ len < 24 + 2 || !presp)
return;
tx_last_beacon = drv_tx_last_beacon(local);
@@ -705,7 +709,7 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
}
/* Reply with ProbeResp */
- skb = skb_copy(ifibss->presp, GFP_KERNEL);
+ skb = skb_copy(presp, GFP_KERNEL);
if (!skb)
return;
@@ -985,7 +989,8 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
/* remove beacon */
kfree(sdata->u.ibss.ie);
- skb = sdata->u.ibss.presp;
+ skb = rcu_dereference_protected(sdata->u.ibss.presp,
+ lockdep_is_held(&sdata->u.ibss.mtx));
rcu_assign_pointer(sdata->u.ibss.presp, NULL);
sdata->vif.bss_conf.ibss_joined = false;
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 027c0467d7a..2025af52b19 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -214,7 +214,7 @@ struct beacon_data {
};
struct ieee80211_if_ap {
- struct beacon_data *beacon;
+ struct beacon_data __rcu *beacon;
struct list_head vlans;
@@ -237,7 +237,7 @@ struct ieee80211_if_vlan {
struct list_head list;
/* used for all tx if the VLAN is configured to 4-addr mode */
- struct sta_info *sta;
+ struct sta_info __rcu *sta;
};
struct mesh_stats {
@@ -442,7 +442,8 @@ struct ieee80211_if_ibss {
unsigned long ibss_join_req;
/* probe response/beacon for IBSS */
- struct sk_buff *presp, *skb;
+ struct sk_buff __rcu *presp;
+ struct sk_buff *skb;
enum {
IEEE80211_IBSS_MLME_SEARCH,
@@ -490,7 +491,11 @@ struct ieee80211_if_mesh {
bool accepting_plinks;
const u8 *ie;
u8 ie_len;
- bool is_secure;
+ enum {
+ IEEE80211_MESH_SEC_NONE = 0x0,
+ IEEE80211_MESH_SEC_AUTHED = 0x1,
+ IEEE80211_MESH_SEC_SECURED = 0x2,
+ } security;
};
#ifdef CONFIG_MAC80211_MESH
@@ -563,9 +568,10 @@ struct ieee80211_sub_if_data {
struct ieee80211_fragment_entry fragments[IEEE80211_FRAGMENT_MAX];
unsigned int fragment_next;
- struct ieee80211_key *keys[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS];
- struct ieee80211_key *default_unicast_key, *default_multicast_key;
- struct ieee80211_key *default_mgmt_key;
+ struct ieee80211_key __rcu *keys[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS];
+ struct ieee80211_key __rcu *default_unicast_key;
+ struct ieee80211_key __rcu *default_multicast_key;
+ struct ieee80211_key __rcu *default_mgmt_key;
u16 sequence_number;
__be16 control_port_protocol;
@@ -764,6 +770,9 @@ struct ieee80211_local {
/* device is started */
bool started;
+ /* wowlan is enabled -- don't reconfig on resume */
+ bool wowlan;
+
int tx_headroom; /* required headroom for hardware/radiotap */
/* count for keys needing tailroom space allocation */
@@ -798,7 +807,7 @@ struct ieee80211_local {
spinlock_t sta_lock;
unsigned long num_sta;
struct list_head sta_list, sta_pending_list;
- struct sta_info *sta_hash[STA_HASH_SIZE];
+ struct sta_info __rcu *sta_hash[STA_HASH_SIZE];
struct timer_list sta_cleanup;
struct work_struct sta_finish_work;
int sta_generation;
@@ -840,6 +849,10 @@ struct ieee80211_local {
int scan_channel_idx;
int scan_ies_len;
+ bool sched_scanning;
+ struct ieee80211_sched_scan_ies sched_scan_ies;
+ struct work_struct sched_scan_stopped_work;
+
unsigned long leave_oper_channel_time;
enum mac80211_scan_state next_scan_state;
struct delayed_work scan_work;
@@ -1147,6 +1160,12 @@ ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq,
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_stop(struct ieee80211_sub_if_data *sdata);
+void ieee80211_sched_scan_stopped_work(struct work_struct *work);
+
/* off-channel helpers */
bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local);
void ieee80211_offchannel_enable_all_ps(struct ieee80211_local *local,
@@ -1250,7 +1269,8 @@ int ieee80211_reconfig(struct ieee80211_local *local);
void ieee80211_stop_device(struct ieee80211_local *local);
#ifdef CONFIG_PM
-int __ieee80211_suspend(struct ieee80211_hw *hw);
+int __ieee80211_suspend(struct ieee80211_hw *hw,
+ struct cfg80211_wowlan *wowlan);
static inline int __ieee80211_resume(struct ieee80211_hw *hw)
{
@@ -1263,7 +1283,8 @@ static inline int __ieee80211_resume(struct ieee80211_hw *hw)
return ieee80211_reconfig(hw_to_local(hw));
}
#else
-static inline int __ieee80211_suspend(struct ieee80211_hw *hw)
+static inline int __ieee80211_suspend(struct ieee80211_hw *hw,
+ struct cfg80211_wowlan *wowlan)
{
return 0;
}
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 80c29d626aa..7dfbe71dc63 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -449,7 +449,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
/* APs need special treatment */
if (sdata->vif.type == NL80211_IFTYPE_AP) {
struct ieee80211_sub_if_data *vlan, *tmpsdata;
- struct beacon_data *old_beacon = sdata->u.ap.beacon;
+ struct beacon_data *old_beacon =
+ rtnl_dereference(sdata->u.ap.beacon);
/* sdata_running will return false, so this will disable */
ieee80211_bss_info_change_notify(sdata,
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index b510721e3b3..31afd712930 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -195,7 +195,7 @@ static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata,
assert_key_lock(sdata->local);
if (idx >= 0 && idx < NUM_DEFAULT_KEYS)
- key = sdata->keys[idx];
+ key = key_mtx_dereference(sdata->local, sdata->keys[idx]);
if (uni)
rcu_assign_pointer(sdata->default_unicast_key, key);
@@ -222,7 +222,7 @@ __ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata, int idx)
if (idx >= NUM_DEFAULT_KEYS &&
idx < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS)
- key = sdata->keys[idx];
+ key = key_mtx_dereference(sdata->local, sdata->keys[idx]);
rcu_assign_pointer(sdata->default_mgmt_key, key);
@@ -266,9 +266,15 @@ static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
else
idx = new->conf.keyidx;
- defunikey = old && sdata->default_unicast_key == old;
- defmultikey = old && sdata->default_multicast_key == old;
- defmgmtkey = old && sdata->default_mgmt_key == old;
+ defunikey = old &&
+ old == key_mtx_dereference(sdata->local,
+ sdata->default_unicast_key);
+ defmultikey = old &&
+ old == key_mtx_dereference(sdata->local,
+ sdata->default_multicast_key);
+ defmgmtkey = old &&
+ old == key_mtx_dereference(sdata->local,
+ sdata->default_mgmt_key);
if (defunikey && !new)
__ieee80211_set_default_key(sdata, -1, true, false);
@@ -451,11 +457,11 @@ int ieee80211_key_link(struct ieee80211_key *key,
mutex_lock(&sdata->local->key_mtx);
if (sta && pairwise)
- old_key = sta->ptk;
+ old_key = key_mtx_dereference(sdata->local, sta->ptk);
else if (sta)
- old_key = sta->gtk[idx];
+ old_key = key_mtx_dereference(sdata->local, sta->gtk[idx]);
else
- old_key = sdata->keys[idx];
+ old_key = key_mtx_dereference(sdata->local, sdata->keys[idx]);
__ieee80211_key_replace(sdata, sta, pairwise, old_key, key);
__ieee80211_key_destroy(old_key);
@@ -471,8 +477,11 @@ int ieee80211_key_link(struct ieee80211_key *key,
return ret;
}
-static void __ieee80211_key_free(struct ieee80211_key *key)
+void __ieee80211_key_free(struct ieee80211_key *key)
{
+ if (!key)
+ return;
+
/*
* Replace key with nothingness if it was ever used.
*/
@@ -486,9 +495,6 @@ static void __ieee80211_key_free(struct ieee80211_key *key)
void ieee80211_key_free(struct ieee80211_local *local,
struct ieee80211_key *key)
{
- if (!key)
- return;
-
mutex_lock(&local->key_mtx);
__ieee80211_key_free(key);
mutex_unlock(&local->key_mtx);
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index 4ddbe27eb57..d801d535133 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -135,6 +135,7 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
int __must_check ieee80211_key_link(struct ieee80211_key *key,
struct ieee80211_sub_if_data *sdata,
struct sta_info *sta);
+void __ieee80211_key_free(struct ieee80211_key *key);
void ieee80211_key_free(struct ieee80211_local *local,
struct ieee80211_key *key);
void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx,
@@ -145,4 +146,7 @@ void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata);
void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata);
void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata);
+#define key_mtx_dereference(local, ref) \
+ rcu_dereference_protected(ref, lockdep_is_held(&((local)->key_mtx)))
+
#endif /* IEEE80211_KEY_H */
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 61877662e8f..0d7b08db8e5 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -358,7 +358,8 @@ static void ieee80211_restart_work(struct work_struct *work)
flush_workqueue(local->workqueue);
mutex_lock(&local->mtx);
- WARN(test_bit(SCAN_HW_SCANNING, &local->scanning),
+ WARN(test_bit(SCAN_HW_SCANNING, &local->scanning) ||
+ local->sched_scanning,
"%s called with hardware scan in progress\n", __func__);
mutex_unlock(&local->mtx);
@@ -580,8 +581,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
wiphy->flags |= WIPHY_FLAG_NETNS_OK |
WIPHY_FLAG_4ADDR_AP |
- WIPHY_FLAG_4ADDR_STATION |
- WIPHY_FLAG_SUPPORTS_SEPARATE_DEFAULT_KEYS;
+ WIPHY_FLAG_4ADDR_STATION;
if (!ops->set_key)
wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
@@ -652,6 +652,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
setup_timer(&local->dynamic_ps_timer,
ieee80211_dynamic_ps_timer, (unsigned long) local);
+ INIT_WORK(&local->sched_scan_stopped_work,
+ ieee80211_sched_scan_stopped_work);
+
sta_info_init(local);
for (i = 0; i < IEEE80211_MAX_QUEUES; i++) {
@@ -682,7 +685,7 @@ EXPORT_SYMBOL(ieee80211_alloc_hw);
int ieee80211_register_hw(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
- int result;
+ int result, i;
enum ieee80211_band band;
int channels, max_bitrates;
bool supp_ht;
@@ -697,6 +700,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
WLAN_CIPHER_SUITE_AES_CMAC
};
+ if ((hw->wiphy->wowlan.flags || hw->wiphy->wowlan.n_patterns)
+#ifdef CONFIG_PM
+ && (!local->ops->suspend || !local->ops->resume)
+#endif
+ )
+ return -EINVAL;
+
if (hw->max_report_rates == 0)
hw->max_report_rates = hw->max_rates;
@@ -733,11 +743,19 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
return -ENOMEM;
/* if low-level driver supports AP, we also support VLAN */
- if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_AP))
- local->hw.wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN);
+ if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) {
+ hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN);
+ hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_AP_VLAN);
+ }
/* mac80211 always supports monitor */
- local->hw.wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
+ hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
+ hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR);
+
+ /* mac80211 doesn't support more than 1 channel */
+ for (i = 0; i < hw->wiphy->n_iface_combinations; i++)
+ if (hw->wiphy->iface_combinations[i].num_different_channels > 1)
+ return -EINVAL;
#ifndef CONFIG_MAC80211_MESH
/* mesh depends on Kconfig, but drivers should set it if they want */
@@ -827,6 +845,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
if (!local->ops->remain_on_channel)
local->hw.wiphy->max_remain_on_channel_duration = 5000;
+ if (local->ops->sched_scan_start)
+ local->hw.wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
+
result = wiphy_register(local->hw.wiphy);
if (result < 0)
goto fail_wiphy_register;
@@ -850,8 +871,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
* and we need some headroom for passing the frame to monitor
* interfaces, but never both at the same time.
*/
+#ifndef __CHECKER__
BUILD_BUG_ON(IEEE80211_TX_STATUS_HEADROOM !=
sizeof(struct ieee80211_tx_status_rtap_hdr));
+#endif
local->tx_headroom = max_t(unsigned int , local->hw.extra_tx_headroom,
sizeof(struct ieee80211_tx_status_rtap_hdr));
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index c1299e24954..29e9980c8e6 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -287,49 +287,6 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
}
}
-u32 mesh_table_hash(u8 *addr, struct ieee80211_sub_if_data *sdata, struct mesh_table *tbl)
-{
- /* Use last four bytes of hw addr and interface index as hash index */
- return jhash_2words(*(u32 *)(addr+2), sdata->dev->ifindex, tbl->hash_rnd)
- & tbl->hash_mask;
-}
-
-struct mesh_table *mesh_table_alloc(int size_order)
-{
- int i;
- struct mesh_table *newtbl;
-
- newtbl = kmalloc(sizeof(struct mesh_table), GFP_KERNEL);
- if (!newtbl)
- return NULL;
-
- newtbl->hash_buckets = kzalloc(sizeof(struct hlist_head) *
- (1 << size_order), GFP_KERNEL);
-
- if (!newtbl->hash_buckets) {
- kfree(newtbl);
- return NULL;
- }
-
- newtbl->hashwlock = kmalloc(sizeof(spinlock_t) *
- (1 << size_order), GFP_KERNEL);
- if (!newtbl->hashwlock) {
- kfree(newtbl->hash_buckets);
- kfree(newtbl);
- return NULL;
- }
-
- newtbl->size_order = size_order;
- newtbl->hash_mask = (1 << size_order) - 1;
- atomic_set(&newtbl->entries, 0);
- get_random_bytes(&newtbl->hash_rnd,
- sizeof(newtbl->hash_rnd));
- for (i = 0; i <= newtbl->hash_mask; i++)
- spin_lock_init(&newtbl->hashwlock[i]);
-
- return newtbl;
-}
-
static void ieee80211_mesh_path_timer(unsigned long data)
{
@@ -574,7 +531,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
&elems);
/* ignore beacons from secure mesh peers if our security is off */
- if (elems.rsn_len && !sdata->u.mesh.is_secure)
+ if (elems.rsn_len && sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE)
return;
if (elems.ds_params && elems.ds_params_len == 1)
@@ -600,7 +557,7 @@ static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
struct ieee80211_rx_status *rx_status)
{
switch (mgmt->u.action.category) {
- case WLAN_CATEGORY_MESH_PLINK:
+ case WLAN_CATEGORY_MESH_ACTION:
mesh_rx_plink_frame(sdata, mgmt, len, rx_status);
break;
case WLAN_CATEGORY_MESH_PATH_SEL:
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 10acf1cc808..e7c5fddb480 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -92,7 +92,7 @@ struct mesh_path {
u8 dst[ETH_ALEN];
u8 mpp[ETH_ALEN]; /* used for MPP or MAP */
struct ieee80211_sub_if_data *sdata;
- struct sta_info *next_hop;
+ struct sta_info __rcu *next_hop;
struct timer_list timer;
struct sk_buff_head frame_queue;
struct rcu_head rcu;
@@ -240,12 +240,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
/* Private interfaces */
/* Mesh tables */
-struct mesh_table *mesh_table_alloc(int size_order);
-void mesh_table_free(struct mesh_table *tbl, bool free_leafs);
void mesh_mpath_table_grow(void);
void mesh_mpp_table_grow(void);
-u32 mesh_table_hash(u8 *addr, struct ieee80211_sub_if_data *sdata,
- struct mesh_table *tbl);
/* Mesh paths */
int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn, __le16 target_rcode,
const u8 *ra, struct ieee80211_sub_if_data *sdata);
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index e57f2e728cf..2b18053070c 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -391,7 +391,6 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
(mpath->flags & MESH_PATH_SN_VALID)) {
if (SN_GT(mpath->sn, orig_sn) ||
(mpath->sn == orig_sn &&
- action == MPATH_PREQ &&
new_metric >= mpath->metric)) {
process = false;
fresh_info = false;
@@ -561,6 +560,14 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
}
+static inline struct sta_info *
+next_hop_deref_protected(struct mesh_path *mpath)
+{
+ return rcu_dereference_protected(mpath->next_hop,
+ lockdep_is_held(&mpath->state_lock));
+}
+
+
static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
u8 *prep_elem, u32 metric)
@@ -600,7 +607,7 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
spin_unlock_bh(&mpath->state_lock);
goto fail;
}
- memcpy(next_hop, mpath->next_hop->sta.addr, ETH_ALEN);
+ memcpy(next_hop, next_hop_deref_protected(mpath)->sta.addr, ETH_ALEN);
spin_unlock_bh(&mpath->state_lock);
--ttl;
flags = PREP_IE_FLAGS(prep_elem);
@@ -652,7 +659,8 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata,
if (mpath) {
spin_lock_bh(&mpath->state_lock);
if (mpath->flags & MESH_PATH_ACTIVE &&
- memcmp(ta, mpath->next_hop->sta.addr, ETH_ALEN) == 0 &&
+ memcmp(ta, next_hop_deref_protected(mpath)->sta.addr,
+ ETH_ALEN) == 0 &&
(!(mpath->flags & MESH_PATH_SN_VALID) ||
SN_GT(target_sn, mpath->sn))) {
mpath->flags &= ~MESH_PATH_ACTIVE;
@@ -914,6 +922,7 @@ int mesh_nexthop_lookup(struct sk_buff *skb,
{
struct sk_buff *skb_to_free = NULL;
struct mesh_path *mpath;
+ struct sta_info *next_hop;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
u8 *target_addr = hdr->addr3;
int err = 0;
@@ -941,7 +950,11 @@ int mesh_nexthop_lookup(struct sk_buff *skb,
mesh_queue_preq(mpath,
PREQ_Q_F_START | PREQ_Q_F_REFRESH);
}
- memcpy(hdr->addr1, mpath->next_hop->sta.addr, ETH_ALEN);
+ next_hop = rcu_dereference(mpath->next_hop);
+ if (next_hop)
+ memcpy(hdr->addr1, next_hop->sta.addr, ETH_ALEN);
+ else
+ err = -ENOENT;
} else {
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
if (!(mpath->flags & MESH_PATH_RESOLVING)) {
@@ -967,20 +980,11 @@ endlookup:
void mesh_path_timer(unsigned long data)
{
- struct ieee80211_sub_if_data *sdata;
- struct mesh_path *mpath;
-
- rcu_read_lock();
- mpath = (struct mesh_path *) data;
- mpath = rcu_dereference(mpath);
- if (!mpath)
- goto endmpathtimer;
- sdata = mpath->sdata;
+ struct mesh_path *mpath = (void *) data;
+ struct ieee80211_sub_if_data *sdata = mpath->sdata;
- if (sdata->local->quiescing) {
- rcu_read_unlock();
+ if (sdata->local->quiescing)
return;
- }
spin_lock_bh(&mpath->state_lock);
if (mpath->flags & MESH_PATH_RESOLVED ||
@@ -997,8 +1001,6 @@ void mesh_path_timer(unsigned long data)
}
spin_unlock_bh(&mpath->state_lock);
-endmpathtimer:
- rcu_read_unlock();
}
void
diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c
index 35c715adaae..83ce48e3191 100644
--- a/net/mac80211/mesh_pathtbl.c
+++ b/net/mac80211/mesh_pathtbl.c
@@ -40,6 +40,50 @@ static struct mesh_table *mesh_paths;
static struct mesh_table *mpp_paths; /* Store paths for MPP&MAP */
int mesh_paths_generation;
+
+/* This lock will have the grow table function as writer and add / delete nodes
+ * as readers. When reading the table (i.e. doing lookups) we are well protected
+ * by RCU
+ */
+static DEFINE_RWLOCK(pathtbl_resize_lock);
+
+
+static struct mesh_table *mesh_table_alloc(int size_order)
+{
+ int i;
+ struct mesh_table *newtbl;
+
+ newtbl = kmalloc(sizeof(struct mesh_table), GFP_KERNEL);
+ if (!newtbl)
+ return NULL;
+
+ newtbl->hash_buckets = kzalloc(sizeof(struct hlist_head) *
+ (1 << size_order), GFP_KERNEL);
+
+ if (!newtbl->hash_buckets) {
+ kfree(newtbl);
+ return NULL;
+ }
+
+ newtbl->hashwlock = kmalloc(sizeof(spinlock_t) *
+ (1 << size_order), GFP_KERNEL);
+ if (!newtbl->hashwlock) {
+ kfree(newtbl->hash_buckets);
+ kfree(newtbl);
+ return NULL;
+ }
+
+ newtbl->size_order = size_order;
+ newtbl->hash_mask = (1 << size_order) - 1;
+ atomic_set(&newtbl->entries, 0);
+ get_random_bytes(&newtbl->hash_rnd,
+ sizeof(newtbl->hash_rnd));
+ for (i = 0; i <= newtbl->hash_mask; i++)
+ spin_lock_init(&newtbl->hashwlock[i]);
+
+ return newtbl;
+}
+
static void __mesh_table_free(struct mesh_table *tbl)
{
kfree(tbl->hash_buckets);
@@ -47,7 +91,7 @@ static void __mesh_table_free(struct mesh_table *tbl)
kfree(tbl);
}
-void mesh_table_free(struct mesh_table *tbl, bool free_leafs)
+static void mesh_table_free(struct mesh_table *tbl, bool free_leafs)
{
struct hlist_head *mesh_hash;
struct hlist_node *p, *q;
@@ -55,18 +99,18 @@ void mesh_table_free(struct mesh_table *tbl, bool free_leafs)
mesh_hash = tbl->hash_buckets;
for (i = 0; i <= tbl->hash_mask; i++) {
- spin_lock(&tbl->hashwlock[i]);
+ spin_lock_bh(&tbl->hashwlock[i]);
hlist_for_each_safe(p, q, &mesh_hash[i]) {
tbl->free_node(p, free_leafs);
atomic_dec(&tbl->entries);
}
- spin_unlock(&tbl->hashwlock[i]);
+ spin_unlock_bh(&tbl->hashwlock[i]);
}
__mesh_table_free(tbl);
}
static int mesh_table_grow(struct mesh_table *oldtbl,
- struct mesh_table *newtbl)
+ struct mesh_table *newtbl)
{
struct hlist_head *oldhash;
struct hlist_node *p, *q;
@@ -76,7 +120,6 @@ static int mesh_table_grow(struct mesh_table *oldtbl,
< oldtbl->mean_chain_len * (oldtbl->hash_mask + 1))
return -EAGAIN;
-
newtbl->free_node = oldtbl->free_node;
newtbl->mean_chain_len = oldtbl->mean_chain_len;
newtbl->copy_node = oldtbl->copy_node;
@@ -98,12 +141,14 @@ errcopy:
return -ENOMEM;
}
+static u32 mesh_table_hash(u8 *addr, struct ieee80211_sub_if_data *sdata,
+ struct mesh_table *tbl)
+{
+ /* Use last four bytes of hw addr and interface index as hash index */
+ return jhash_2words(*(u32 *)(addr+2), sdata->dev->ifindex, tbl->hash_rnd)
+ & tbl->hash_mask;
+}
-/* This lock will have the grow table function as writer and add / delete nodes
- * as readers. When reading the table (i.e. doing lookups) we are well protected
- * by RCU
- */
-static DEFINE_RWLOCK(pathtbl_resize_lock);
/**
*
@@ -275,7 +320,7 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata)
if (!new_node)
goto err_node_alloc;
- read_lock(&pathtbl_resize_lock);
+ read_lock_bh(&pathtbl_resize_lock);
memcpy(new_mpath->dst, dst, ETH_ALEN);
new_mpath->sdata = sdata;
new_mpath->flags = 0;
@@ -290,7 +335,7 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata)
hash_idx = mesh_table_hash(dst, sdata, mesh_paths);
bucket = &mesh_paths->hash_buckets[hash_idx];
- spin_lock(&mesh_paths->hashwlock[hash_idx]);
+ spin_lock_bh(&mesh_paths->hashwlock[hash_idx]);
err = -EEXIST;
hlist_for_each_entry(node, n, bucket, list) {
@@ -306,8 +351,8 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata)
mesh_paths_generation++;
- spin_unlock(&mesh_paths->hashwlock[hash_idx]);
- read_unlock(&pathtbl_resize_lock);
+ spin_unlock_bh(&mesh_paths->hashwlock[hash_idx]);
+ read_unlock_bh(&pathtbl_resize_lock);
if (grow) {
set_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags);
ieee80211_queue_work(&local->hw, &sdata->work);
@@ -315,8 +360,8 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata)
return 0;
err_exists:
- spin_unlock(&mesh_paths->hashwlock[hash_idx]);
- read_unlock(&pathtbl_resize_lock);
+ spin_unlock_bh(&mesh_paths->hashwlock[hash_idx]);
+ read_unlock_bh(&pathtbl_resize_lock);
kfree(new_node);
err_node_alloc:
kfree(new_mpath);
@@ -329,18 +374,21 @@ void mesh_mpath_table_grow(void)
{
struct mesh_table *oldtbl, *newtbl;
- newtbl = mesh_table_alloc(mesh_paths->size_order + 1);
+ rcu_read_lock();
+ newtbl = mesh_table_alloc(rcu_dereference(mesh_paths)->size_order + 1);
if (!newtbl)
return;
- write_lock(&pathtbl_resize_lock);
+ write_lock_bh(&pathtbl_resize_lock);
oldtbl = mesh_paths;
if (mesh_table_grow(mesh_paths, newtbl) < 0) {
+ rcu_read_unlock();
__mesh_table_free(newtbl);
- write_unlock(&pathtbl_resize_lock);
+ write_unlock_bh(&pathtbl_resize_lock);
return;
}
+ rcu_read_unlock();
rcu_assign_pointer(mesh_paths, newtbl);
- write_unlock(&pathtbl_resize_lock);
+ write_unlock_bh(&pathtbl_resize_lock);
synchronize_rcu();
mesh_table_free(oldtbl, false);
@@ -350,18 +398,21 @@ void mesh_mpp_table_grow(void)
{
struct mesh_table *oldtbl, *newtbl;
- newtbl = mesh_table_alloc(mpp_paths->size_order + 1);
+ rcu_read_lock();
+ newtbl = mesh_table_alloc(rcu_dereference(mpp_paths)->size_order + 1);
if (!newtbl)
return;
- write_lock(&pathtbl_resize_lock);
+ write_lock_bh(&pathtbl_resize_lock);
oldtbl = mpp_paths;
if (mesh_table_grow(mpp_paths, newtbl) < 0) {
+ rcu_read_unlock();
__mesh_table_free(newtbl);
- write_unlock(&pathtbl_resize_lock);
+ write_unlock_bh(&pathtbl_resize_lock);
return;
}
+ rcu_read_unlock();
rcu_assign_pointer(mpp_paths, newtbl);
- write_unlock(&pathtbl_resize_lock);
+ write_unlock_bh(&pathtbl_resize_lock);
synchronize_rcu();
mesh_table_free(oldtbl, false);
@@ -395,7 +446,7 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata)
if (!new_node)
goto err_node_alloc;
- read_lock(&pathtbl_resize_lock);
+ read_lock_bh(&pathtbl_resize_lock);
memcpy(new_mpath->dst, dst, ETH_ALEN);
memcpy(new_mpath->mpp, mpp, ETH_ALEN);
new_mpath->sdata = sdata;
@@ -408,7 +459,7 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata)
hash_idx = mesh_table_hash(dst, sdata, mpp_paths);
bucket = &mpp_paths->hash_buckets[hash_idx];
- spin_lock(&mpp_paths->hashwlock[hash_idx]);
+ spin_lock_bh(&mpp_paths->hashwlock[hash_idx]);
err = -EEXIST;
hlist_for_each_entry(node, n, bucket, list) {
@@ -422,8 +473,8 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata)
mpp_paths->mean_chain_len * (mpp_paths->hash_mask + 1))
grow = 1;
- spin_unlock(&mpp_paths->hashwlock[hash_idx]);
- read_unlock(&pathtbl_resize_lock);
+ spin_unlock_bh(&mpp_paths->hashwlock[hash_idx]);
+ read_unlock_bh(&pathtbl_resize_lock);
if (grow) {
set_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags);
ieee80211_queue_work(&local->hw, &sdata->work);
@@ -431,8 +482,8 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata)
return 0;
err_exists:
- spin_unlock(&mpp_paths->hashwlock[hash_idx]);
- read_unlock(&pathtbl_resize_lock);
+ spin_unlock_bh(&mpp_paths->hashwlock[hash_idx]);
+ read_unlock_bh(&pathtbl_resize_lock);
kfree(new_node);
err_node_alloc:
kfree(new_mpath);
@@ -545,11 +596,11 @@ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata)
int hash_idx;
int err = 0;
- read_lock(&pathtbl_resize_lock);
+ read_lock_bh(&pathtbl_resize_lock);
hash_idx = mesh_table_hash(addr, sdata, mesh_paths);
bucket = &mesh_paths->hash_buckets[hash_idx];
- spin_lock(&mesh_paths->hashwlock[hash_idx]);
+ spin_lock_bh(&mesh_paths->hashwlock[hash_idx]);
hlist_for_each_entry(node, n, bucket, list) {
mpath = node->mpath;
if (mpath->sdata == sdata &&
@@ -567,8 +618,8 @@ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata)
err = -ENXIO;
enddel:
mesh_paths_generation++;
- spin_unlock(&mesh_paths->hashwlock[hash_idx]);
- read_unlock(&pathtbl_resize_lock);
+ spin_unlock_bh(&mesh_paths->hashwlock[hash_idx]);
+ read_unlock_bh(&pathtbl_resize_lock);
return err;
}
@@ -720,7 +771,7 @@ void mesh_path_expire(struct ieee80211_sub_if_data *sdata)
struct hlist_node *p;
int i;
- read_lock(&pathtbl_resize_lock);
+ read_lock_bh(&pathtbl_resize_lock);
for_each_mesh_entry(mesh_paths, p, node, i) {
if (node->mpath->sdata != sdata)
continue;
@@ -735,7 +786,7 @@ void mesh_path_expire(struct ieee80211_sub_if_data *sdata)
} else
spin_unlock_bh(&mpath->state_lock);
}
- read_unlock(&pathtbl_resize_lock);
+ read_unlock_bh(&pathtbl_resize_lock);
}
void mesh_pathtbl_unregister(void)
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 84e5b056af0..f4adc091788 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -43,7 +43,7 @@
#define dot11MeshMaxPeerLinks(s) (s->u.mesh.mshcfg.dot11MeshMaxPeerLinks)
enum plink_frame_type {
- PLINK_OPEN = 0,
+ PLINK_OPEN = 1,
PLINK_CONFIRM,
PLINK_CLOSE
};
@@ -83,7 +83,7 @@ void mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata)
*/
static inline void mesh_plink_fsm_restart(struct sta_info *sta)
{
- sta->plink_state = PLINK_LISTEN;
+ sta->plink_state = NL80211_PLINK_LISTEN;
sta->llid = sta->plid = sta->reason = 0;
sta->plink_retries = 0;
}
@@ -126,11 +126,11 @@ static bool __mesh_plink_deactivate(struct sta_info *sta)
struct ieee80211_sub_if_data *sdata = sta->sdata;
bool deactivated = false;
- if (sta->plink_state == PLINK_ESTAB) {
+ if (sta->plink_state == NL80211_PLINK_ESTAB) {
mesh_plink_dec_estab_count(sdata);
deactivated = true;
}
- sta->plink_state = PLINK_BLOCKED;
+ sta->plink_state = NL80211_PLINK_BLOCKED;
mesh_path_flush_by_nexthop(sta);
return deactivated;
@@ -181,8 +181,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
IEEE80211_STYPE_ACTION);
memcpy(mgmt->da, da, ETH_ALEN);
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
- /* BSSID is left zeroed, wildcard value */
- mgmt->u.action.category = WLAN_CATEGORY_MESH_PLINK;
+ memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
+ mgmt->u.action.category = WLAN_CATEGORY_MESH_ACTION;
mgmt->u.action.u.plink_action.action_code = action;
if (action == PLINK_CLOSE)
@@ -251,7 +251,7 @@ void mesh_neighbour_update(u8 *hw_addr, u32 rates,
rcu_read_unlock();
/* Userspace handles peer allocation when security is enabled
* */
- if (sdata->u.mesh.is_secure)
+ if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED)
cfg80211_notify_new_peer_candidate(sdata->dev, hw_addr,
elems->ie_start, elems->total_len,
GFP_KERNEL);
@@ -268,7 +268,7 @@ void mesh_neighbour_update(u8 *hw_addr, u32 rates,
sta->last_rx = jiffies;
sta->sta.supp_rates[local->hw.conf.channel->band] = rates;
if (mesh_peer_accepts_plinks(elems) &&
- sta->plink_state == PLINK_LISTEN &&
+ sta->plink_state == NL80211_PLINK_LISTEN &&
sdata->u.mesh.accepting_plinks &&
sdata->u.mesh.mshcfg.auto_open_plinks)
mesh_plink_open(sta);
@@ -308,8 +308,8 @@ static void mesh_plink_timer(unsigned long data)
sdata = sta->sdata;
switch (sta->plink_state) {
- case PLINK_OPN_RCVD:
- case PLINK_OPN_SNT:
+ case NL80211_PLINK_OPN_RCVD:
+ case NL80211_PLINK_OPN_SNT:
/* retry timer */
if (sta->plink_retries < dot11MeshMaxRetries(sdata)) {
u32 rand;
@@ -328,17 +328,17 @@ static void mesh_plink_timer(unsigned long data)
}
reason = cpu_to_le16(MESH_MAX_RETRIES);
/* fall through on else */
- case PLINK_CNF_RCVD:
+ case NL80211_PLINK_CNF_RCVD:
/* confirm timer */
if (!reason)
reason = cpu_to_le16(MESH_CONFIRM_TIMEOUT);
- sta->plink_state = PLINK_HOLDING;
+ sta->plink_state = NL80211_PLINK_HOLDING;
mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
spin_unlock_bh(&sta->lock);
mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, plid,
reason);
break;
- case PLINK_HOLDING:
+ case NL80211_PLINK_HOLDING:
/* holding timer */
del_timer(&sta->plink_timer);
mesh_plink_fsm_restart(sta);
@@ -386,11 +386,11 @@ int mesh_plink_open(struct sta_info *sta)
spin_lock_bh(&sta->lock);
get_random_bytes(&llid, 2);
sta->llid = llid;
- if (sta->plink_state != PLINK_LISTEN) {
+ if (sta->plink_state != NL80211_PLINK_LISTEN) {
spin_unlock_bh(&sta->lock);
return -EBUSY;
}
- sta->plink_state = PLINK_OPN_SNT;
+ sta->plink_state = NL80211_PLINK_OPN_SNT;
mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata));
spin_unlock_bh(&sta->lock);
mpl_dbg("Mesh plink: starting establishment with %pM\n",
@@ -407,7 +407,7 @@ void mesh_plink_block(struct sta_info *sta)
spin_lock_bh(&sta->lock);
deactivated = __mesh_plink_deactivate(sta);
- sta->plink_state = PLINK_BLOCKED;
+ sta->plink_state = NL80211_PLINK_BLOCKED;
spin_unlock_bh(&sta->lock);
if (deactivated)
@@ -430,13 +430,13 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
__le16 plid, llid, reason;
#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
static const char *mplstates[] = {
- [PLINK_LISTEN] = "LISTEN",
- [PLINK_OPN_SNT] = "OPN-SNT",
- [PLINK_OPN_RCVD] = "OPN-RCVD",
- [PLINK_CNF_RCVD] = "CNF_RCVD",
- [PLINK_ESTAB] = "ESTAB",
- [PLINK_HOLDING] = "HOLDING",
- [PLINK_BLOCKED] = "BLOCKED"
+ [NL80211_PLINK_LISTEN] = "LISTEN",
+ [NL80211_PLINK_OPN_SNT] = "OPN-SNT",
+ [NL80211_PLINK_OPN_RCVD] = "OPN-RCVD",
+ [NL80211_PLINK_CNF_RCVD] = "CNF_RCVD",
+ [NL80211_PLINK_ESTAB] = "ESTAB",
+ [NL80211_PLINK_HOLDING] = "HOLDING",
+ [NL80211_PLINK_BLOCKED] = "BLOCKED"
};
#endif
@@ -460,7 +460,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
mpl_dbg("Mesh plink: missing necessary peer link ie\n");
return;
}
- if (elems.rsn_len && !sdata->u.mesh.is_secure) {
+ if (elems.rsn_len &&
+ sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) {
mpl_dbg("Mesh plink: can't establish link with secure peer\n");
return;
}
@@ -501,7 +502,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
return;
}
- if (sta && sta->plink_state == PLINK_BLOCKED) {
+ if (sta && sta->plink_state == NL80211_PLINK_BLOCKED) {
rcu_read_unlock();
return;
}
@@ -571,7 +572,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
event = CNF_ACPT;
break;
case PLINK_CLOSE:
- if (sta->plink_state == PLINK_ESTAB)
+ if (sta->plink_state == NL80211_PLINK_ESTAB)
/* Do not check for llid or plid. This does not
* follow the standard but since multiple plinks
* per sta are not supported, it is necessary in
@@ -606,14 +607,14 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
reason = 0;
switch (sta->plink_state) {
/* spin_unlock as soon as state is updated at each case */
- case PLINK_LISTEN:
+ case NL80211_PLINK_LISTEN:
switch (event) {
case CLS_ACPT:
mesh_plink_fsm_restart(sta);
spin_unlock_bh(&sta->lock);
break;
case OPN_ACPT:
- sta->plink_state = PLINK_OPN_RCVD;
+ sta->plink_state = NL80211_PLINK_OPN_RCVD;
sta->plid = plid;
get_random_bytes(&llid, 2);
sta->llid = llid;
@@ -630,7 +631,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
}
break;
- case PLINK_OPN_SNT:
+ case NL80211_PLINK_OPN_SNT:
switch (event) {
case OPN_RJCT:
case CNF_RJCT:
@@ -639,7 +640,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
if (!reason)
reason = cpu_to_le16(MESH_CLOSE_RCVD);
sta->reason = reason;
- sta->plink_state = PLINK_HOLDING;
+ sta->plink_state = NL80211_PLINK_HOLDING;
if (!mod_plink_timer(sta,
dot11MeshHoldingTimeout(sdata)))
sta->ignore_plink_timer = true;
@@ -651,7 +652,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
break;
case OPN_ACPT:
/* retry timer is left untouched */
- sta->plink_state = PLINK_OPN_RCVD;
+ sta->plink_state = NL80211_PLINK_OPN_RCVD;
sta->plid = plid;
llid = sta->llid;
spin_unlock_bh(&sta->lock);
@@ -659,7 +660,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
plid, 0);
break;
case CNF_ACPT:
- sta->plink_state = PLINK_CNF_RCVD;
+ sta->plink_state = NL80211_PLINK_CNF_RCVD;
if (!mod_plink_timer(sta,
dot11MeshConfirmTimeout(sdata)))
sta->ignore_plink_timer = true;
@@ -672,7 +673,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
}
break;
- case PLINK_OPN_RCVD:
+ case NL80211_PLINK_OPN_RCVD:
switch (event) {
case OPN_RJCT:
case CNF_RJCT:
@@ -681,7 +682,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
if (!reason)
reason = cpu_to_le16(MESH_CLOSE_RCVD);
sta->reason = reason;
- sta->plink_state = PLINK_HOLDING;
+ sta->plink_state = NL80211_PLINK_HOLDING;
if (!mod_plink_timer(sta,
dot11MeshHoldingTimeout(sdata)))
sta->ignore_plink_timer = true;
@@ -699,7 +700,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
break;
case CNF_ACPT:
del_timer(&sta->plink_timer);
- sta->plink_state = PLINK_ESTAB;
+ sta->plink_state = NL80211_PLINK_ESTAB;
spin_unlock_bh(&sta->lock);
mesh_plink_inc_estab_count(sdata);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
@@ -712,7 +713,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
}
break;
- case PLINK_CNF_RCVD:
+ case NL80211_PLINK_CNF_RCVD:
switch (event) {
case OPN_RJCT:
case CNF_RJCT:
@@ -721,7 +722,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
if (!reason)
reason = cpu_to_le16(MESH_CLOSE_RCVD);
sta->reason = reason;
- sta->plink_state = PLINK_HOLDING;
+ sta->plink_state = NL80211_PLINK_HOLDING;
if (!mod_plink_timer(sta,
dot11MeshHoldingTimeout(sdata)))
sta->ignore_plink_timer = true;
@@ -733,7 +734,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
break;
case OPN_ACPT:
del_timer(&sta->plink_timer);
- sta->plink_state = PLINK_ESTAB;
+ sta->plink_state = NL80211_PLINK_ESTAB;
spin_unlock_bh(&sta->lock);
mesh_plink_inc_estab_count(sdata);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
@@ -748,13 +749,13 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
}
break;
- case PLINK_ESTAB:
+ case NL80211_PLINK_ESTAB:
switch (event) {
case CLS_ACPT:
reason = cpu_to_le16(MESH_CLOSE_RCVD);
sta->reason = reason;
deactivated = __mesh_plink_deactivate(sta);
- sta->plink_state = PLINK_HOLDING;
+ sta->plink_state = NL80211_PLINK_HOLDING;
llid = sta->llid;
mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
spin_unlock_bh(&sta->lock);
@@ -774,7 +775,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
break;
}
break;
- case PLINK_HOLDING:
+ case NL80211_PLINK_HOLDING:
switch (event) {
case CLS_ACPT:
if (del_timer(&sta->plink_timer))
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index a41f234bd48..4f6b2675e41 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -750,6 +750,8 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
dynamic_ps_enable_work);
struct ieee80211_sub_if_data *sdata = local->ps_sdata;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ unsigned long flags;
+ int q;
/* can only happen when PS was just disabled anyway */
if (!sdata)
@@ -758,6 +760,24 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
if (local->hw.conf.flags & IEEE80211_CONF_PS)
return;
+ /*
+ * transmission can be stopped by others which leads to
+ * dynamic_ps_timer expiry. Postpond the ps timer if it
+ * is not the actual idle state.
+ */
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+ for (q = 0; q < local->hw.queues; q++) {
+ if (local->queue_stop_reasons[q]) {
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock,
+ flags);
+ mod_timer(&local->dynamic_ps_timer, jiffies +
+ msecs_to_jiffies(
+ local->hw.conf.dynamic_ps_timeout));
+ return;
+ }
+ }
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+
if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
(!(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED))) {
netif_tx_stop_all_queues(sdata->dev);
@@ -781,7 +801,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
}
- netif_tx_start_all_queues(sdata->dev);
+ netif_tx_wake_all_queues(sdata->dev);
}
void ieee80211_dynamic_ps_timer(unsigned long data)
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index 04246171088..730778a2c90 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -6,7 +6,7 @@
#include "driver-ops.h"
#include "led.h"
-int __ieee80211_suspend(struct ieee80211_hw *hw)
+int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata;
@@ -47,6 +47,16 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
cancel_work_sync(&local->dynamic_ps_enable_work);
del_timer_sync(&local->dynamic_ps_timer);
+ local->wowlan = wowlan && local->open_count;
+ if (local->wowlan) {
+ int err = drv_suspend(local, wowlan);
+ if (err) {
+ local->quiescing = false;
+ return err;
+ }
+ goto suspend;
+ }
+
/* disable keys */
list_for_each_entry(sdata, &local->interfaces, list)
ieee80211_disable_keys(sdata);
@@ -104,6 +114,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
if (local->open_count)
ieee80211_stop_device(local);
+ suspend:
local->suspended = true;
/* need suspended to be visible before quiescing is false */
barrier();
diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c
index 778c604d793..8adac67395f 100644
--- a/net/mac80211/rc80211_minstrel.c
+++ b/net/mac80211/rc80211_minstrel.c
@@ -417,8 +417,8 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
tx_time_single = mr->ack_time + mr->perfect_tx_time;
/* contention window */
- tx_time_single += t_slot + min(cw, mp->cw_max);
- cw = (cw << 1) | 1;
+ tx_time_single += (t_slot * cw) >> 1;
+ cw = min((cw << 1) | 1, mp->cw_max);
tx_time += tx_time_single;
tx_time_cts += tx_time_single + mi->sp_ack_dur;
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index c06aa3ac6b9..333b5118be6 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -464,6 +464,7 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
const struct mcs_group *group;
unsigned int tx_time, tx_time_rtscts, tx_time_data;
unsigned int cw = mp->cw_min;
+ unsigned int ctime = 0;
unsigned int t_slot = 9; /* FIXME */
unsigned int ampdu_len = MINSTREL_TRUNC(mi->avg_ampdu_len);
@@ -480,13 +481,27 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];
tx_time_data = group->duration[index % MCS_GROUP_RATES] * ampdu_len;
- tx_time = 2 * (t_slot + mi->overhead + tx_time_data);
- tx_time_rtscts = 2 * (t_slot + mi->overhead_rtscts + tx_time_data);
+
+ /* Contention time for first 2 tries */
+ ctime = (t_slot * cw) >> 1;
+ cw = min((cw << 1) | 1, mp->cw_max);
+ ctime += (t_slot * cw) >> 1;
+ cw = min((cw << 1) | 1, mp->cw_max);
+
+ /* Total TX time for data and Contention after first 2 tries */
+ tx_time = ctime + 2 * (mi->overhead + tx_time_data);
+ tx_time_rtscts = ctime + 2 * (mi->overhead_rtscts + tx_time_data);
+
+ /* See how many more tries we can fit inside segment size */
do {
- cw = (cw << 1) | 1;
- cw = min(cw, mp->cw_max);
- tx_time += cw + t_slot + mi->overhead;
- tx_time_rtscts += cw + t_slot + mi->overhead_rtscts;
+ /* Contention time for this try */
+ ctime = (t_slot * cw) >> 1;
+ cw = min((cw << 1) | 1, mp->cw_max);
+
+ /* Total TX time after this try */
+ tx_time += ctime + mi->overhead + tx_time_data;
+ tx_time_rtscts += ctime + mi->overhead_rtscts + tx_time_data;
+
if (tx_time_rtscts < mp->segment_size)
mr->retry_count_rtscts++;
} while ((tx_time < mp->segment_size) &&
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 13a6697651a..7fa8c6be7bf 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -404,11 +404,13 @@ ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx)
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
struct sk_buff *skb = rx->skb;
- if (likely(!(status->rx_flags & IEEE80211_RX_IN_SCAN)))
+ if (likely(!(status->rx_flags & IEEE80211_RX_IN_SCAN) &&
+ !local->sched_scanning))
return RX_CONTINUE;
if (test_bit(SCAN_HW_SCANNING, &local->scanning) ||
- test_bit(SCAN_SW_SCANNING, &local->scanning))
+ test_bit(SCAN_SW_SCANNING, &local->scanning) ||
+ local->sched_scanning)
return ieee80211_scan_rx(rx->sdata, skb);
/* scanning finished during invoking of handlers */
@@ -488,15 +490,18 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
* establisment frame, beacon or probe, drop the frame.
*/
- if (!rx->sta || sta_plink_state(rx->sta) != PLINK_ESTAB) {
+ if (!rx->sta || sta_plink_state(rx->sta) != NL80211_PLINK_ESTAB) {
struct ieee80211_mgmt *mgmt;
if (!ieee80211_is_mgmt(hdr->frame_control))
return RX_DROP_MONITOR;
if (ieee80211_is_action(hdr->frame_control)) {
+ u8 category;
mgmt = (struct ieee80211_mgmt *)hdr;
- if (mgmt->u.action.category != WLAN_CATEGORY_MESH_PLINK)
+ category = mgmt->u.action.category;
+ if (category != WLAN_CATEGORY_MESH_ACTION &&
+ category != WLAN_CATEGORY_SELF_PROTECTED)
return RX_DROP_MONITOR;
return RX_CONTINUE;
}
@@ -1778,7 +1783,7 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr,
rx->sdata->vif.type,
- rx->local->hw.extra_tx_headroom);
+ rx->local->hw.extra_tx_headroom, true);
while (!skb_queue_empty(&frame_list)) {
rx->skb = __skb_dequeue(&frame_list);
@@ -2205,7 +2210,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
goto handled;
}
break;
- case WLAN_CATEGORY_MESH_PLINK:
+ case WLAN_CATEGORY_MESH_ACTION:
if (!ieee80211_vif_is_mesh(&sdata->vif))
break;
goto queue;
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 489b6ad200d..d20046b5d8f 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -15,6 +15,7 @@
#include <linux/if_arp.h>
#include <linux/rtnetlink.h>
#include <linux/pm_qos_params.h>
+#include <linux/slab.h>
#include <net/sch_generic.h>
#include <linux/slab.h>
#include <net/mac80211.h>
@@ -170,7 +171,7 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
return RX_CONTINUE;
if (skb->len < 24)
- return RX_DROP_MONITOR;
+ return RX_CONTINUE;
presp = ieee80211_is_probe_resp(fc);
if (presp) {
@@ -850,3 +851,122 @@ void ieee80211_scan_cancel(struct ieee80211_local *local)
}
mutex_unlock(&local->mtx);
}
+
+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, i;
+
+ mutex_lock(&sdata->local->mtx);
+
+ if (local->sched_scanning) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ if (!local->ops->sched_scan_start) {
+ ret = -ENOTSUPP;
+ goto out;
+ }
+
+ for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
+ local->sched_scan_ies.ie[i] = kzalloc(2 +
+ IEEE80211_MAX_SSID_LEN +
+ local->scan_ies_len,
+ GFP_KERNEL);
+ if (!local->sched_scan_ies.ie[i]) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ local->sched_scan_ies.len[i] =
+ ieee80211_build_preq_ies(local,
+ local->sched_scan_ies.ie[i],
+ req->ie, req->ie_len, i,
+ (u32) -1, 0);
+ }
+
+ ret = drv_sched_scan_start(local, sdata, req,
+ &local->sched_scan_ies);
+ if (ret == 0) {
+ local->sched_scanning = true;
+ goto out;
+ }
+
+out_free:
+ while (i > 0)
+ kfree(local->sched_scan_ies.ie[--i]);
+out:
+ mutex_unlock(&sdata->local->mtx);
+ return ret;
+}
+
+int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ int ret = 0, i;
+
+ mutex_lock(&sdata->local->mtx);
+
+ if (!local->ops->sched_scan_stop) {
+ ret = -ENOTSUPP;
+ goto out;
+ }
+
+ if (local->sched_scanning) {
+ for (i = 0; i < IEEE80211_NUM_BANDS; i++)
+ kfree(local->sched_scan_ies.ie[i]);
+
+ drv_sched_scan_stop(local, sdata);
+ local->sched_scanning = false;
+ }
+out:
+ mutex_unlock(&sdata->local->mtx);
+
+ return ret;
+}
+
+void ieee80211_sched_scan_results(struct ieee80211_hw *hw)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+
+ trace_api_sched_scan_results(local);
+
+ cfg80211_sched_scan_results(hw->wiphy);
+}
+EXPORT_SYMBOL(ieee80211_sched_scan_results);
+
+void ieee80211_sched_scan_stopped_work(struct work_struct *work)
+{
+ struct ieee80211_local *local =
+ container_of(work, struct ieee80211_local,
+ sched_scan_stopped_work);
+ int i;
+
+ mutex_lock(&local->mtx);
+
+ if (!local->sched_scanning) {
+ mutex_unlock(&local->mtx);
+ return;
+ }
+
+ for (i = 0; i < IEEE80211_NUM_BANDS; i++)
+ kfree(local->sched_scan_ies.ie[i]);
+
+ local->sched_scanning = false;
+
+ mutex_unlock(&local->mtx);
+
+ cfg80211_sched_scan_stopped(local->hw.wiphy);
+}
+
+void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+
+ trace_api_sched_scan_stopped(local);
+
+ ieee80211_queue_work(&local->hw, &local->sched_scan_stopped_work);
+}
+EXPORT_SYMBOL(ieee80211_sched_scan_stopped);
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index d9e6e81ff6b..b83870bf60f 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -67,7 +67,8 @@ static int sta_info_hash_del(struct ieee80211_local *local,
{
struct sta_info *s;
- s = local->sta_hash[STA_HASH(sta->sta.addr)];
+ s = rcu_dereference_protected(local->sta_hash[STA_HASH(sta->sta.addr)],
+ lockdep_is_held(&local->sta_lock));
if (!s)
return -ENOENT;
if (s == sta) {
@@ -76,9 +77,11 @@ static int sta_info_hash_del(struct ieee80211_local *local,
return 0;
}
- while (s->hnext && s->hnext != sta)
- s = s->hnext;
- if (s->hnext) {
+ while (rcu_access_pointer(s->hnext) &&
+ rcu_access_pointer(s->hnext) != sta)
+ s = rcu_dereference_protected(s->hnext,
+ lockdep_is_held(&local->sta_lock));
+ if (rcu_access_pointer(s->hnext)) {
rcu_assign_pointer(s->hnext, sta->hnext);
return 0;
}
@@ -274,7 +277,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
#ifdef CONFIG_MAC80211_MESH
- sta->plink_state = PLINK_LISTEN;
+ sta->plink_state = NL80211_PLINK_LISTEN;
init_timer(&sta->plink_timer);
#endif
@@ -652,10 +655,12 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
if (ret)
return ret;
+ mutex_lock(&local->key_mtx);
for (i = 0; i < NUM_DEFAULT_KEYS; i++)
- ieee80211_key_free(local, sta->gtk[i]);
+ __ieee80211_key_free(key_mtx_dereference(local, sta->gtk[i]));
if (sta->ptk)
- ieee80211_key_free(local, sta->ptk);
+ __ieee80211_key_free(key_mtx_dereference(local, sta->ptk));
+ mutex_unlock(&local->key_mtx);
sta->dead = true;
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index aa0adcbf3a9..c6ae8718bd5 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -152,6 +152,7 @@ struct tid_ampdu_rx {
*
* @tid_rx: aggregation info for Rx per TID -- RCU protected
* @tid_tx: aggregation info for Tx per TID
+ * @tid_start_tx: sessions where start was requested
* @addba_req_num: number of times addBA request has been sent.
* @dialog_token_allocator: dialog token enumerator for each new session;
* @work: work struct for starting/stopping aggregation
@@ -163,40 +164,18 @@ struct tid_ampdu_rx {
struct sta_ampdu_mlme {
struct mutex mtx;
/* rx */
- struct tid_ampdu_rx *tid_rx[STA_TID_NUM];
+ struct tid_ampdu_rx __rcu *tid_rx[STA_TID_NUM];
unsigned long tid_rx_timer_expired[BITS_TO_LONGS(STA_TID_NUM)];
/* tx */
struct work_struct work;
- struct tid_ampdu_tx *tid_tx[STA_TID_NUM];
+ struct tid_ampdu_tx __rcu *tid_tx[STA_TID_NUM];
+ struct tid_ampdu_tx *tid_start_tx[STA_TID_NUM];
u8 addba_req_num[STA_TID_NUM];
u8 dialog_token_allocator;
};
/**
- * enum plink_state - state of a mesh peer link finite state machine
- *
- * @PLINK_LISTEN: initial state, considered the implicit state of non existent
- * mesh peer links
- * @PLINK_OPN_SNT: mesh plink open frame has been sent to this mesh peer
- * @PLINK_OPN_RCVD: mesh plink open frame has been received from this mesh peer
- * @PLINK_CNF_RCVD: mesh plink confirm frame has been received from this mesh
- * peer
- * @PLINK_ESTAB: mesh peer link is established
- * @PLINK_HOLDING: mesh peer link is being closed or cancelled
- * @PLINK_BLOCKED: all frames transmitted from this mesh plink are discarded
- */
-enum plink_state {
- PLINK_LISTEN,
- PLINK_OPN_SNT,
- PLINK_OPN_RCVD,
- PLINK_CNF_RCVD,
- PLINK_ESTAB,
- PLINK_HOLDING,
- PLINK_BLOCKED
-};
-
-/**
* struct sta_info - STA information
*
* This structure collects information about a station that
@@ -264,11 +243,11 @@ enum plink_state {
struct sta_info {
/* General information, mostly static */
struct list_head list;
- struct sta_info *hnext;
+ struct sta_info __rcu *hnext;
struct ieee80211_local *local;
struct ieee80211_sub_if_data *sdata;
- struct ieee80211_key *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS];
- struct ieee80211_key *ptk;
+ struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS];
+ struct ieee80211_key __rcu *ptk;
struct rate_control_ref *rate_ctrl;
void *rate_ctrl_priv;
spinlock_t lock;
@@ -339,7 +318,7 @@ struct sta_info {
u8 plink_retries;
bool ignore_plink_timer;
bool plink_timer_was_running;
- enum plink_state plink_state;
+ enum nl80211_plink_state plink_state;
u32 plink_timeout;
struct timer_list plink_timer;
#endif
@@ -357,12 +336,12 @@ struct sta_info {
struct ieee80211_sta sta;
};
-static inline enum plink_state sta_plink_state(struct sta_info *sta)
+static inline enum nl80211_plink_state sta_plink_state(struct sta_info *sta)
{
#ifdef CONFIG_MAC80211_MESH
return sta->plink_state;
#endif
- return PLINK_LISTEN;
+ return NL80211_PLINK_LISTEN;
}
static inline void set_sta_flags(struct sta_info *sta, const u32 flags)
@@ -421,7 +400,16 @@ static inline u32 get_sta_flags(struct sta_info *sta)
return ret;
}
+void ieee80211_assign_tid_tx(struct sta_info *sta, int tid,
+ struct tid_ampdu_tx *tid_tx);
+static inline struct tid_ampdu_tx *
+rcu_dereference_protected_tid_tx(struct sta_info *sta, int tid)
+{
+ return rcu_dereference_protected(sta->ampdu_mlme.tid_tx[tid],
+ lockdep_is_held(&sta->lock) ||
+ lockdep_is_held(&sta->ampdu_mlme.mtx));
+}
#define STA_HASH_SIZE 256
#define STA_HASH(sta) (sta[5])
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index e3e3aa173af..6eeaaa2bbaf 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1147,7 +1147,7 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx,
* packet pass through because splicing the frames
* back is already done.
*/
- tid_tx = tx->sta->ampdu_mlme.tid_tx[tid];
+ tid_tx = rcu_dereference_protected_tid_tx(tx->sta, tid);
if (!tid_tx) {
/* do nothing, let packet pass through */
@@ -1751,6 +1751,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
ret = NETDEV_TX_OK;
goto fail;
}
+ rcu_read_lock();
if (!is_multicast_ether_addr(skb->data))
mppath = mpp_path_lookup(skb->data, sdata);
@@ -1765,13 +1766,13 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
!(mppath && compare_ether_addr(mppath->mpp, skb->data))) {
hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc,
skb->data, skb->data + ETH_ALEN);
+ rcu_read_unlock();
meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr,
sdata, NULL, NULL);
} else {
int is_mesh_mcast = 1;
const u8 *mesh_da;
- rcu_read_lock();
if (is_multicast_ether_addr(skb->data))
/* DA TA mSA AE:SA */
mesh_da = skb->data;
@@ -2534,8 +2535,9 @@ void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
skb_set_network_header(skb, 0);
skb_set_transport_header(skb, 0);
- /* send all internal mgmt frames on VO */
- skb_set_queue_mapping(skb, 0);
+ /* Send all internal mgmt frames on VO. Accordingly set TID to 7. */
+ skb_set_queue_mapping(skb, IEEE80211_AC_VO);
+ skb->priority = 7;
/*
* The other path calling ieee80211_xmit is from the tasklet,
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index ef0560a2346..d3fe2d23748 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1125,9 +1125,27 @@ int ieee80211_reconfig(struct ieee80211_local *local)
struct sta_info *sta;
int res;
+#ifdef CONFIG_PM
if (local->suspended)
local->resuming = true;
+ if (local->wowlan) {
+ local->wowlan = false;
+ res = drv_resume(local);
+ if (res < 0) {
+ local->resuming = false;
+ return res;
+ }
+ if (res == 0)
+ goto wake_up;
+ WARN_ON(res > 1);
+ /*
+ * res is 1, which means the driver requested
+ * to go through a regular reset on wakeup.
+ */
+ }
+#endif
+
/* restart hardware */
if (local->open_count) {
/*
@@ -1258,6 +1276,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
if (ieee80211_sdata_running(sdata))
ieee80211_enable_keys(sdata);
+ wake_up:
ieee80211_wake_queues_by_reason(hw,
IEEE80211_QUEUE_STOP_REASON_SUSPEND);