summaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-04-15 09:00:47 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2015-04-15 09:00:47 -0700
commit6c373ca89399c5a3f7ef210ad8f63dc3437da345 (patch)
tree74d1ec65087df1da1021b43ac51acc1ee8601809 /net/mac80211
parentbb0fd7ab0986105765d11baa82e619c618a235aa (diff)
parent9f9151412dd7aae0e3f51a89ae4a1f8755fdb4d0 (diff)
downloadblackbird-obmc-linux-6c373ca89399c5a3f7ef210ad8f63dc3437da345.tar.gz
blackbird-obmc-linux-6c373ca89399c5a3f7ef210ad8f63dc3437da345.zip
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller: 1) Add BQL support to via-rhine, from Tino Reichardt. 2) Integrate SWITCHDEV layer support into the DSA layer, so DSA drivers can support hw switch offloading. From Floria Fainelli. 3) Allow 'ip address' commands to initiate multicast group join/leave, from Madhu Challa. 4) Many ipv4 FIB lookup optimizations from Alexander Duyck. 5) Support EBPF in cls_bpf classifier and act_bpf action, from Daniel Borkmann. 6) Remove the ugly compat support in ARP for ugly layers like ax25, rose, etc. And use this to clean up the neigh layer, then use it to implement MPLS support. All from Eric Biederman. 7) Support L3 forwarding offloading in switches, from Scott Feldman. 8) Collapse the LOCAL and MAIN ipv4 FIB tables when possible, to speed up route lookups even further. From Alexander Duyck. 9) Many improvements and bug fixes to the rhashtable implementation, from Herbert Xu and Thomas Graf. In particular, in the case where an rhashtable user bulk adds a large number of items into an empty table, we expand the table much more sanely. 10) Don't make the tcp_metrics hash table per-namespace, from Eric Biederman. 11) Extend EBPF to access SKB fields, from Alexei Starovoitov. 12) Split out new connection request sockets so that they can be established in the main hash table. Much less false sharing since hash lookups go direct to the request sockets instead of having to go first to the listener then to the request socks hashed underneath. From Eric Dumazet. 13) Add async I/O support for crytpo AF_ALG sockets, from Tadeusz Struk. 14) Support stable privacy address generation for RFC7217 in IPV6. From Hannes Frederic Sowa. 15) Hash network namespace into IP frag IDs, also from Hannes Frederic Sowa. 16) Convert PTP get/set methods to use 64-bit time, from Richard Cochran. * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1816 commits) fm10k: Bump driver version to 0.15.2 fm10k: corrected VF multicast update fm10k: mbx_update_max_size does not drop all oversized messages fm10k: reset head instead of calling update_max_size fm10k: renamed mbx_tx_dropped to mbx_tx_oversized fm10k: update xcast mode before synchronizing multicast addresses fm10k: start service timer on probe fm10k: fix function header comment fm10k: comment next_vf_mbx flow fm10k: don't handle mailbox events in iov_event path and always process mailbox fm10k: use separate workqueue for fm10k driver fm10k: Set PF queues to unlimited bandwidth during virtualization fm10k: expose tx_timeout_count as an ethtool stat fm10k: only increment tx_timeout_count in Tx hang path fm10k: remove extraneous "Reset interface" message fm10k: separate PF only stats so that VF does not display them fm10k: use hw->mac.max_queues for stats fm10k: only show actual queues, not the maximum in hardware fm10k: allow creation of VLAN on default vid fm10k: fix unused warnings ...
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/aes_ccm.c12
-rw-r--r--net/mac80211/aes_gcm.c12
-rw-r--r--net/mac80211/aes_gmac.c4
-rw-r--r--net/mac80211/agg-rx.c8
-rw-r--r--net/mac80211/agg-tx.c58
-rw-r--r--net/mac80211/cfg.c99
-rw-r--r--net/mac80211/debugfs.c170
-rw-r--r--net/mac80211/debugfs_netdev.c2
-rw-r--r--net/mac80211/debugfs_sta.c134
-rw-r--r--net/mac80211/driver-ops.h24
-rw-r--r--net/mac80211/ht.c2
-rw-r--r--net/mac80211/ibss.c343
-rw-r--r--net/mac80211/ieee80211_i.h67
-rw-r--r--net/mac80211/iface.c27
-rw-r--r--net/mac80211/key.c1
-rw-r--r--net/mac80211/key.h2
-rw-r--r--net/mac80211/main.c16
-rw-r--r--net/mac80211/mesh.c5
-rw-r--r--net/mac80211/mesh_plink.c10
-rw-r--r--net/mac80211/mlme.c236
-rw-r--r--net/mac80211/pm.c28
-rw-r--r--net/mac80211/rc80211_minstrel.c125
-rw-r--r--net/mac80211/rc80211_minstrel.h49
-rw-r--r--net/mac80211/rc80211_minstrel_debugfs.c125
-rw-r--r--net/mac80211/rc80211_minstrel_ht.c223
-rw-r--r--net/mac80211/rc80211_minstrel_ht.h5
-rw-r--r--net/mac80211/rc80211_minstrel_ht_debugfs.c216
-rw-r--r--net/mac80211/rx.c39
-rw-r--r--net/mac80211/scan.c25
-rw-r--r--net/mac80211/sta_info.c226
-rw-r--r--net/mac80211/sta_info.h62
-rw-r--r--net/mac80211/status.c82
-rw-r--r--net/mac80211/tdls.c155
-rw-r--r--net/mac80211/trace.h45
-rw-r--r--net/mac80211/tx.c381
-rw-r--r--net/mac80211/util.c190
-rw-r--r--net/mac80211/vht.c4
-rw-r--r--net/mac80211/wpa.c13
38 files changed, 1928 insertions, 1297 deletions
diff --git a/net/mac80211/aes_ccm.c b/net/mac80211/aes_ccm.c
index 7869bb40acaa..208df7c0b6ea 100644
--- a/net/mac80211/aes_ccm.c
+++ b/net/mac80211/aes_ccm.c
@@ -85,11 +85,15 @@ struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[],
return tfm;
err = crypto_aead_setkey(tfm, key, key_len);
- if (!err)
- err = crypto_aead_setauthsize(tfm, mic_len);
- if (!err)
- return tfm;
+ if (err)
+ goto free_aead;
+ err = crypto_aead_setauthsize(tfm, mic_len);
+ if (err)
+ goto free_aead;
+
+ return tfm;
+free_aead:
crypto_free_aead(tfm);
return ERR_PTR(err);
}
diff --git a/net/mac80211/aes_gcm.c b/net/mac80211/aes_gcm.c
index c2bf6698d738..fd278bbe1b0d 100644
--- a/net/mac80211/aes_gcm.c
+++ b/net/mac80211/aes_gcm.c
@@ -80,11 +80,15 @@ struct crypto_aead *ieee80211_aes_gcm_key_setup_encrypt(const u8 key[],
return tfm;
err = crypto_aead_setkey(tfm, key, key_len);
- if (!err)
- err = crypto_aead_setauthsize(tfm, IEEE80211_GCMP_MIC_LEN);
- if (!err)
- return tfm;
+ if (err)
+ goto free_aead;
+ err = crypto_aead_setauthsize(tfm, IEEE80211_GCMP_MIC_LEN);
+ if (err)
+ goto free_aead;
+
+ return tfm;
+free_aead:
crypto_free_aead(tfm);
return ERR_PTR(err);
}
diff --git a/net/mac80211/aes_gmac.c b/net/mac80211/aes_gmac.c
index 1c72edcb0083..f1321b7d6506 100644
--- a/net/mac80211/aes_gmac.c
+++ b/net/mac80211/aes_gmac.c
@@ -70,9 +70,9 @@ struct crypto_aead *ieee80211_aes_gmac_key_setup(const u8 key[],
err = crypto_aead_setkey(tfm, key, key_len);
if (!err)
- return tfm;
- if (!err)
err = crypto_aead_setauthsize(tfm, GMAC_MIC_LEN);
+ if (!err)
+ return tfm;
crypto_free_aead(tfm);
return ERR_PTR(err);
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index 7702978a4c99..5c564a68fb50 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -238,6 +238,14 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
int i, ret = -EOPNOTSUPP;
u16 status = WLAN_STATUS_REQUEST_DECLINED;
+ if (!sta->sta.ht_cap.ht_supported) {
+ ht_dbg(sta->sdata,
+ "STA %pM erroneously requests BA session on tid %d w/o QoS\n",
+ sta->sta.addr, tid);
+ /* send a response anyway, it's an error case if we get here */
+ goto end_no_lock;
+ }
+
if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) {
ht_dbg(sta->sdata,
"Suspend in progress - Denying ADDBA request (%pM tid %d)\n",
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index a360c15cc978..cce9d425c718 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -188,6 +188,43 @@ ieee80211_wake_queue_agg(struct ieee80211_sub_if_data *sdata, int tid)
__release(agg_queue);
}
+static void
+ieee80211_agg_stop_txq(struct sta_info *sta, int tid)
+{
+ struct ieee80211_txq *txq = sta->sta.txq[tid];
+ struct txq_info *txqi;
+
+ if (!txq)
+ return;
+
+ txqi = to_txq_info(txq);
+
+ /* Lock here to protect against further seqno updates on dequeue */
+ spin_lock_bh(&txqi->queue.lock);
+ set_bit(IEEE80211_TXQ_STOP, &txqi->flags);
+ spin_unlock_bh(&txqi->queue.lock);
+}
+
+static void
+ieee80211_agg_start_txq(struct sta_info *sta, int tid, bool enable)
+{
+ struct ieee80211_txq *txq = sta->sta.txq[tid];
+ struct txq_info *txqi;
+
+ if (!txq)
+ return;
+
+ txqi = to_txq_info(txq);
+
+ if (enable)
+ set_bit(IEEE80211_TXQ_AMPDU, &txqi->flags);
+ else
+ clear_bit(IEEE80211_TXQ_AMPDU, &txqi->flags);
+
+ clear_bit(IEEE80211_TXQ_STOP, &txqi->flags);
+ drv_wake_tx_queue(sta->sdata->local, txqi);
+}
+
/*
* splice packets from the STA's pending to the local pending,
* requires a call to ieee80211_agg_splice_finish later
@@ -247,6 +284,7 @@ static void ieee80211_remove_tid_tx(struct sta_info *sta, int tid)
ieee80211_assign_tid_tx(sta, tid, NULL);
ieee80211_agg_splice_finish(sta->sdata, tid);
+ ieee80211_agg_start_txq(sta, tid, false);
kfree_rcu(tid_tx, rcu_head);
}
@@ -418,6 +456,8 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
*/
clear_bit(HT_AGG_STATE_WANT_START, &tid_tx->state);
+ ieee80211_agg_stop_txq(sta, tid);
+
/*
* Make sure no packets are being processed. This ensures that
* we have a valid starting sequence number and that in-flight
@@ -440,6 +480,8 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
ieee80211_agg_splice_finish(sdata, tid);
spin_unlock_bh(&sta->lock);
+ ieee80211_agg_start_txq(sta, tid, false);
+
kfree_rcu(tid_tx, rcu_head);
return;
}
@@ -509,11 +551,14 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
struct tid_ampdu_tx *tid_tx;
int ret = 0;
+ trace_api_start_tx_ba_session(pubsta, tid);
+
if (WARN(sta->reserved_tid == tid,
"Requested to start BA session on reserved tid=%d", tid))
return -EINVAL;
- trace_api_start_tx_ba_session(pubsta, tid);
+ if (!pubsta->ht_cap.ht_supported)
+ return -EINVAL;
if (WARN_ON_ONCE(!local->ops->ampdu_action))
return -EINVAL;
@@ -666,6 +711,8 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local,
ieee80211_agg_splice_finish(sta->sdata, tid);
spin_unlock_bh(&sta->lock);
+
+ ieee80211_agg_start_txq(sta, tid, true);
}
void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid)
@@ -793,6 +840,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid)
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
struct tid_ampdu_tx *tid_tx;
+ bool send_delba = false;
trace_api_stop_tx_ba_cb(sdata, ra, tid);
@@ -824,13 +872,17 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid)
}
if (tid_tx->stop_initiator == WLAN_BACK_INITIATOR && tid_tx->tx_stop)
- ieee80211_send_delba(sta->sdata, ra, tid,
- WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
+ send_delba = true;
ieee80211_remove_tid_tx(sta, tid);
unlock_sta:
spin_unlock_bh(&sta->lock);
+
+ if (send_delba)
+ ieee80211_send_delba(sdata, ra, tid,
+ WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
+
mutex_unlock(&sta->ampdu_mlme.mtx);
unlock:
mutex_unlock(&local->sta_mtx);
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index dd4ff36c557a..265e42721a66 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -24,6 +24,7 @@
static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
const char *name,
+ unsigned char name_assign_type,
enum nl80211_iftype type,
u32 *flags,
struct vif_params *params)
@@ -33,7 +34,7 @@ static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
struct ieee80211_sub_if_data *sdata;
int err;
- err = ieee80211_if_add(local, name, &wdev, type, params);
+ err = ieee80211_if_add(local, name, name_assign_type, &wdev, type, params);
if (err)
return ERR_PTR(err);
@@ -977,6 +978,14 @@ static int sta_apply_auth_flags(struct ieee80211_local *local,
if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
set & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
!test_sta_flag(sta, WLAN_STA_ASSOC)) {
+ /*
+ * When peer becomes associated, init rate control as
+ * well. Some drivers require rate control initialized
+ * before drv_sta_state() is called.
+ */
+ if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
+ rate_control_rate_init(sta);
+
ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
if (ret)
return ret;
@@ -1050,6 +1059,10 @@ static int sta_apply_parameters(struct ieee80211_local *local,
}
}
+ if (mask & BIT(NL80211_STA_FLAG_WME) &&
+ local->hw.queues >= IEEE80211_NUM_ACS)
+ sta->sta.wme = set & BIT(NL80211_STA_FLAG_WME);
+
/* auth flags will be set later for TDLS stations */
if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
ret = sta_apply_auth_flags(local, sta, mask, set);
@@ -1064,10 +1077,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,
clear_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE);
}
- if (mask & BIT(NL80211_STA_FLAG_WME))
- sta->sta.wme = set & BIT(NL80211_STA_FLAG_WME);
-
if (mask & BIT(NL80211_STA_FLAG_MFP)) {
+ sta->sta.mfp = !!(set & BIT(NL80211_STA_FLAG_MFP));
if (set & BIT(NL80211_STA_FLAG_MFP))
set_sta_flag(sta, WLAN_STA_MFP);
else
@@ -1377,11 +1388,6 @@ static int ieee80211_change_station(struct wiphy *wiphy,
if (err)
goto out_err;
- /* When peer becomes authorized, init rate control as well */
- if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
- test_sta_flag(sta, WLAN_STA_AUTHORIZED))
- rate_control_rate_init(sta);
-
mutex_unlock(&local->sta_mtx);
if ((sdata->vif.type == NL80211_IFTYPE_AP ||
@@ -1488,7 +1494,7 @@ static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
if (next_hop_sta)
memcpy(next_hop, next_hop_sta->sta.addr, ETH_ALEN);
else
- memset(next_hop, 0, ETH_ALEN);
+ eth_zero_addr(next_hop);
memset(pinfo, 0, sizeof(*pinfo));
@@ -2273,7 +2279,6 @@ int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
{
struct sta_info *sta;
enum ieee80211_smps_mode old_req;
- int i;
if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP))
return -EINVAL;
@@ -2297,52 +2302,44 @@ int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
}
ht_dbg(sdata,
- "SMSP %d requested in AP mode, sending Action frame to %d stations\n",
+ "SMPS %d requested in AP mode, sending Action frame to %d stations\n",
smps_mode, atomic_read(&sdata->u.ap.num_mcast_sta));
mutex_lock(&sdata->local->sta_mtx);
- for (i = 0; i < STA_HASH_SIZE; i++) {
- for (sta = rcu_dereference_protected(sdata->local->sta_hash[i],
- lockdep_is_held(&sdata->local->sta_mtx));
- sta;
- sta = rcu_dereference_protected(sta->hnext,
- lockdep_is_held(&sdata->local->sta_mtx))) {
- /*
- * Only stations associated to our AP and
- * associated VLANs
- */
- if (sta->sdata->bss != &sdata->u.ap)
- continue;
+ list_for_each_entry(sta, &sdata->local->sta_list, list) {
+ /*
+ * Only stations associated to our AP and
+ * associated VLANs
+ */
+ if (sta->sdata->bss != &sdata->u.ap)
+ continue;
- /* This station doesn't support MIMO - skip it */
- if (sta_info_tx_streams(sta) == 1)
- continue;
+ /* This station doesn't support MIMO - skip it */
+ if (sta_info_tx_streams(sta) == 1)
+ continue;
- /*
- * Don't wake up a STA just to send the action frame
- * unless we are getting more restrictive.
- */
- if (test_sta_flag(sta, WLAN_STA_PS_STA) &&
- !ieee80211_smps_is_restrictive(sta->known_smps_mode,
- smps_mode)) {
- ht_dbg(sdata,
- "Won't send SMPS to sleeping STA %pM\n",
- sta->sta.addr);
- continue;
- }
+ /*
+ * Don't wake up a STA just to send the action frame
+ * unless we are getting more restrictive.
+ */
+ if (test_sta_flag(sta, WLAN_STA_PS_STA) &&
+ !ieee80211_smps_is_restrictive(sta->known_smps_mode,
+ smps_mode)) {
+ ht_dbg(sdata, "Won't send SMPS to sleeping STA %pM\n",
+ sta->sta.addr);
+ continue;
+ }
- /*
- * If the STA is not authorized, wait until it gets
- * authorized and the action frame will be sent then.
- */
- if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
- continue;
+ /*
+ * If the STA is not authorized, wait until it gets
+ * authorized and the action frame will be sent then.
+ */
+ if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
+ continue;
- ht_dbg(sdata, "Sending SMPS to %pM\n", sta->sta.addr);
- ieee80211_send_smps_action(sdata, smps_mode,
- sta->sta.addr,
- sdata->vif.bss_conf.bssid);
- }
+ ht_dbg(sdata, "Sending SMPS to %pM\n", sta->sta.addr);
+ ieee80211_send_smps_action(sdata, smps_mode, sta->sta.addr,
+ sdata->vif.bss_conf.bssid);
}
mutex_unlock(&sdata->local->sta_mtx);
@@ -3581,7 +3578,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
nullfunc->qos_ctrl = cpu_to_le16(7);
local_bh_disable();
- ieee80211_xmit(sdata, skb);
+ ieee80211_xmit(sdata, sta, skb);
local_bh_enable();
rcu_read_unlock();
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index eeb0bbd69d98..23813ebb349c 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -18,172 +18,6 @@
#define DEBUGFS_FORMAT_BUFFER_SIZE 100
-#define TX_LATENCY_BIN_DELIMTER_C ','
-#define TX_LATENCY_BIN_DELIMTER_S ","
-#define TX_LATENCY_BINS_DISABLED "enable(bins disabled)\n"
-#define TX_LATENCY_DISABLED "disable\n"
-
-
-/*
- * Display if Tx latency statistics & bins are enabled/disabled
- */
-static ssize_t sta_tx_latency_stat_read(struct file *file,
- char __user *userbuf,
- size_t count, loff_t *ppos)
-{
- struct ieee80211_local *local = file->private_data;
- struct ieee80211_tx_latency_bin_ranges *tx_latency;
- char *buf;
- int bufsz, i, ret;
- int pos = 0;
-
- rcu_read_lock();
-
- tx_latency = rcu_dereference(local->tx_latency);
-
- if (tx_latency && tx_latency->n_ranges) {
- bufsz = tx_latency->n_ranges * 15;
- buf = kzalloc(bufsz, GFP_ATOMIC);
- if (!buf)
- goto err;
-
- for (i = 0; i < tx_latency->n_ranges; i++)
- pos += scnprintf(buf + pos, bufsz - pos, "%d,",
- tx_latency->ranges[i]);
- pos += scnprintf(buf + pos, bufsz - pos, "\n");
- } else if (tx_latency) {
- bufsz = sizeof(TX_LATENCY_BINS_DISABLED) + 1;
- buf = kzalloc(bufsz, GFP_ATOMIC);
- if (!buf)
- goto err;
-
- pos += scnprintf(buf + pos, bufsz - pos, "%s\n",
- TX_LATENCY_BINS_DISABLED);
- } else {
- bufsz = sizeof(TX_LATENCY_DISABLED) + 1;
- buf = kzalloc(bufsz, GFP_ATOMIC);
- if (!buf)
- goto err;
-
- pos += scnprintf(buf + pos, bufsz - pos, "%s\n",
- TX_LATENCY_DISABLED);
- }
-
- rcu_read_unlock();
-
- ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
- kfree(buf);
-
- return ret;
-err:
- rcu_read_unlock();
- return -ENOMEM;
-}
-
-/*
- * Receive input from user regarding Tx latency statistics
- * The input should indicate if Tx latency statistics and bins are
- * enabled/disabled.
- * If bins are enabled input should indicate the amount of different bins and
- * their ranges. Each bin will count how many Tx frames transmitted within the
- * appropriate latency.
- * Legal input is:
- * a) "enable(bins disabled)" - to enable only general statistics
- * b) "a,b,c,d,...z" - to enable general statistics and bins, where all are
- * numbers and a < b < c < d.. < z
- * c) "disable" - disable all statistics
- * NOTE: must configure Tx latency statistics bins before stations connected.
- */
-
-static ssize_t sta_tx_latency_stat_write(struct file *file,
- const char __user *userbuf,
- size_t count, loff_t *ppos)
-{
- struct ieee80211_local *local = file->private_data;
- char buf[128] = {};
- char *bins = buf;
- char *token;
- int buf_size, i, alloc_size;
- int prev_bin = 0;
- int n_ranges = 0;
- int ret = count;
- struct ieee80211_tx_latency_bin_ranges *tx_latency;
-
- if (sizeof(buf) <= count)
- return -EINVAL;
- buf_size = count;
- if (copy_from_user(buf, userbuf, buf_size))
- return -EFAULT;
-
- mutex_lock(&local->sta_mtx);
-
- /* cannot change config once we have stations */
- if (local->num_sta)
- goto unlock;
-
- tx_latency =
- rcu_dereference_protected(local->tx_latency,
- lockdep_is_held(&local->sta_mtx));
-
- /* disable Tx statistics */
- if (!strcmp(buf, TX_LATENCY_DISABLED)) {
- if (!tx_latency)
- goto unlock;
- RCU_INIT_POINTER(local->tx_latency, NULL);
- synchronize_rcu();
- kfree(tx_latency);
- goto unlock;
- }
-
- /* Tx latency already enabled */
- if (tx_latency)
- goto unlock;
-
- if (strcmp(TX_LATENCY_BINS_DISABLED, buf)) {
- /* check how many bins and between what ranges user requested */
- token = buf;
- while (*token != '\0') {
- if (*token == TX_LATENCY_BIN_DELIMTER_C)
- n_ranges++;
- token++;
- }
- n_ranges++;
- }
-
- alloc_size = sizeof(struct ieee80211_tx_latency_bin_ranges) +
- n_ranges * sizeof(u32);
- tx_latency = kzalloc(alloc_size, GFP_ATOMIC);
- if (!tx_latency) {
- ret = -ENOMEM;
- goto unlock;
- }
- tx_latency->n_ranges = n_ranges;
- for (i = 0; i < n_ranges; i++) { /* setting bin ranges */
- token = strsep(&bins, TX_LATENCY_BIN_DELIMTER_S);
- sscanf(token, "%d", &tx_latency->ranges[i]);
- /* bins values should be in ascending order */
- if (prev_bin >= tx_latency->ranges[i]) {
- ret = -EINVAL;
- kfree(tx_latency);
- goto unlock;
- }
- prev_bin = tx_latency->ranges[i];
- }
- rcu_assign_pointer(local->tx_latency, tx_latency);
-
-unlock:
- mutex_unlock(&local->sta_mtx);
-
- return ret;
-}
-
-static const struct file_operations stats_tx_latency_ops = {
- .write = sta_tx_latency_stat_write,
- .read = sta_tx_latency_stat_read,
- .open = simple_open,
- .llseek = generic_file_llseek,
-};
-
int mac80211_format_buffer(char __user *userbuf, size_t count,
loff_t *ppos, char *fmt, ...)
{
@@ -440,8 +274,6 @@ void debugfs_hw_add(struct ieee80211_local *local)
#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
DEBUGFS_STATS_ADD(tx_handlers_drop, local->tx_handlers_drop);
DEBUGFS_STATS_ADD(tx_handlers_queued, local->tx_handlers_queued);
- DEBUGFS_STATS_ADD(tx_handlers_drop_unencrypted,
- local->tx_handlers_drop_unencrypted);
DEBUGFS_STATS_ADD(tx_handlers_drop_fragment,
local->tx_handlers_drop_fragment);
DEBUGFS_STATS_ADD(tx_handlers_drop_wep,
@@ -475,6 +307,4 @@ void debugfs_hw_add(struct ieee80211_local *local)
DEBUGFS_DEVSTATS_ADD(dot11RTSFailureCount);
DEBUGFS_DEVSTATS_ADD(dot11FCSErrorCount);
DEBUGFS_DEVSTATS_ADD(dot11RTSSuccessCount);
-
- DEBUGFS_DEVSTATS_ADD(tx_latency);
}
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index c68896adfa96..29236e832e44 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -177,7 +177,6 @@ static ssize_t ieee80211_if_write_##name(struct file *file, \
IEEE80211_IF_FILE_R(name)
/* common attributes */
-IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC);
IEEE80211_IF_FILE(rc_rateidx_mask_2ghz, rc_rateidx_mask[IEEE80211_BAND_2GHZ],
HEX);
IEEE80211_IF_FILE(rc_rateidx_mask_5ghz, rc_rateidx_mask[IEEE80211_BAND_5GHZ],
@@ -562,7 +561,6 @@ IEEE80211_IF_FILE(dot11MeshAwakeWindowDuration,
static void add_common_files(struct ieee80211_sub_if_data *sdata)
{
- DEBUGFS_ADD(drop_unencrypted);
DEBUGFS_ADD(rc_rateidx_mask_2ghz);
DEBUGFS_ADD(rc_rateidx_mask_5ghz);
DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 94c70091bbd7..252859e90e8a 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -39,13 +39,6 @@ static const struct file_operations sta_ ##name## _ops = { \
.llseek = generic_file_llseek, \
}
-#define STA_OPS_W(name) \
-static const struct file_operations sta_ ##name## _ops = { \
- .write = sta_##name##_write, \
- .open = simple_open, \
- .llseek = generic_file_llseek, \
-}
-
#define STA_OPS_RW(name) \
static const struct file_operations sta_ ##name## _ops = { \
.read = sta_##name##_read, \
@@ -398,131 +391,6 @@ static ssize_t sta_last_rx_rate_read(struct file *file, char __user *userbuf,
}
STA_OPS(last_rx_rate);
-static int
-sta_tx_latency_stat_header(struct ieee80211_tx_latency_bin_ranges *tx_latency,
- char *buf, int pos, int bufsz)
-{
- int i;
- int range_count = tx_latency->n_ranges;
- u32 *bin_ranges = tx_latency->ranges;
-
- pos += scnprintf(buf + pos, bufsz - pos,
- "Station\t\t\tTID\tMax\tAvg");
- if (range_count) {
- pos += scnprintf(buf + pos, bufsz - pos,
- "\t<=%d", bin_ranges[0]);
- for (i = 0; i < range_count - 1; i++)
- pos += scnprintf(buf + pos, bufsz - pos, "\t%d-%d",
- bin_ranges[i], bin_ranges[i+1]);
- pos += scnprintf(buf + pos, bufsz - pos,
- "\t%d<", bin_ranges[range_count - 1]);
- }
-
- pos += scnprintf(buf + pos, bufsz - pos, "\n");
-
- return pos;
-}
-
-static int
-sta_tx_latency_stat_table(struct ieee80211_tx_latency_bin_ranges *tx_lat_range,
- struct ieee80211_tx_latency_stat *tx_lat,
- char *buf, int pos, int bufsz, int tid)
-{
- u32 avg = 0;
- int j;
- int bin_count = tx_lat->bin_count;
-
- pos += scnprintf(buf + pos, bufsz - pos, "\t\t\t%d", tid);
- /* make sure you don't divide in 0 */
- if (tx_lat->counter)
- avg = tx_lat->sum / tx_lat->counter;
-
- pos += scnprintf(buf + pos, bufsz - pos, "\t%d\t%d",
- tx_lat->max, avg);
-
- if (tx_lat_range->n_ranges && tx_lat->bins)
- for (j = 0; j < bin_count; j++)
- pos += scnprintf(buf + pos, bufsz - pos,
- "\t%d", tx_lat->bins[j]);
- pos += scnprintf(buf + pos, bufsz - pos, "\n");
-
- return pos;
-}
-
-/*
- * Output Tx latency statistics station && restart all statistics information
- */
-static ssize_t sta_tx_latency_stat_read(struct file *file,
- char __user *userbuf,
- size_t count, loff_t *ppos)
-{
- struct sta_info *sta = file->private_data;
- struct ieee80211_local *local = sta->local;
- struct ieee80211_tx_latency_bin_ranges *tx_latency;
- char *buf;
- int bufsz, ret, i;
- int pos = 0;
-
- bufsz = 20 * IEEE80211_NUM_TIDS *
- sizeof(struct ieee80211_tx_latency_stat);
- buf = kzalloc(bufsz, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- rcu_read_lock();
-
- tx_latency = rcu_dereference(local->tx_latency);
-
- if (!sta->tx_lat) {
- pos += scnprintf(buf + pos, bufsz - pos,
- "Tx latency statistics are not enabled\n");
- goto unlock;
- }
-
- pos = sta_tx_latency_stat_header(tx_latency, buf, pos, bufsz);
-
- pos += scnprintf(buf + pos, bufsz - pos, "%pM\n", sta->sta.addr);
- for (i = 0; i < IEEE80211_NUM_TIDS; i++)
- pos = sta_tx_latency_stat_table(tx_latency, &sta->tx_lat[i],
- buf, pos, bufsz, i);
-unlock:
- rcu_read_unlock();
-
- ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
- kfree(buf);
-
- return ret;
-}
-STA_OPS(tx_latency_stat);
-
-static ssize_t sta_tx_latency_stat_reset_write(struct file *file,
- const char __user *userbuf,
- size_t count, loff_t *ppos)
-{
- u32 *bins;
- int bin_count;
- struct sta_info *sta = file->private_data;
- int i;
-
- if (!sta->tx_lat)
- return -EINVAL;
-
- for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
- bins = sta->tx_lat[i].bins;
- bin_count = sta->tx_lat[i].bin_count;
-
- sta->tx_lat[i].max = 0;
- sta->tx_lat[i].sum = 0;
- sta->tx_lat[i].counter = 0;
-
- if (bin_count)
- memset(bins, 0, bin_count * sizeof(u32));
- }
-
- return count;
-}
-STA_OPS_W(tx_latency_stat_reset);
-
#define DEBUGFS_ADD(name) \
debugfs_create_file(#name, 0400, \
sta->debugfs.dir, sta, &sta_ ##name## _ops);
@@ -576,8 +444,6 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
DEBUGFS_ADD(last_ack_signal);
DEBUGFS_ADD(current_tx_rate);
DEBUGFS_ADD(last_rx_rate);
- DEBUGFS_ADD(tx_latency_stat);
- DEBUGFS_ADD(tx_latency_stat_reset);
DEBUGFS_ADD_COUNTER(rx_packets, rx_packets);
DEBUGFS_ADD_COUNTER(tx_packets, tx_packets);
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index fdeda17b8dd2..26e1ca8a474a 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -941,13 +941,13 @@ static inline void drv_set_rekey_data(struct ieee80211_local *local,
trace_drv_return_void(local);
}
-static inline void drv_rssi_callback(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- const enum ieee80211_rssi_event event)
+static inline void drv_event_callback(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ const struct ieee80211_event *event)
{
- trace_drv_rssi_callback(local, sdata, event);
- if (local->ops->rssi_callback)
- local->ops->rssi_callback(&local->hw, &sdata->vif, event);
+ trace_drv_event_callback(local, sdata, event);
+ if (local->ops->event_callback)
+ local->ops->event_callback(&local->hw, &sdata->vif, event);
trace_drv_return_void(local);
}
@@ -1367,4 +1367,16 @@ drv_tdls_recv_channel_switch(struct ieee80211_local *local,
trace_drv_return_void(local);
}
+static inline void drv_wake_tx_queue(struct ieee80211_local *local,
+ struct txq_info *txq)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->txq.vif);
+
+ if (!check_sdata_in_driver(sdata))
+ return;
+
+ trace_drv_wake_tx_queue(local, sdata, txq);
+ local->ops->wake_tx_queue(&local->hw, &txq->txq);
+}
+
#endif /* __MAC80211_DRIVER_OPS */
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index ff630be2ca75..7a76ce639d58 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -252,8 +252,6 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
break;
}
- if (bw != sta->sta.bandwidth)
- changed = true;
sta->sta.bandwidth = bw;
sta->cur_max_bandwidth =
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index b606b53a49a7..bfef1b215050 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -188,6 +188,16 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
*/
pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap,
chandef, 0);
+
+ /* add VHT capability and information IEs */
+ if (chandef->width != NL80211_CHAN_WIDTH_20 &&
+ chandef->width != NL80211_CHAN_WIDTH_40 &&
+ sband->vht_cap.vht_supported) {
+ pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap,
+ sband->vht_cap.cap);
+ pos = ieee80211_ie_build_vht_oper(pos, &sband->vht_cap,
+ chandef);
+ }
}
if (local->hw.queues >= IEEE80211_NUM_ACS)
@@ -249,8 +259,6 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
if (presp)
kfree_rcu(presp, rcu_head);
- sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
-
/* make a copy of the chandef, it could be modified below. */
chandef = *req_chandef;
chan = chandef.chan;
@@ -417,6 +425,11 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
NL80211_CHAN_WIDTH_20_NOHT);
chandef.width = sdata->u.ibss.chandef.width;
break;
+ case NL80211_CHAN_WIDTH_80:
+ case NL80211_CHAN_WIDTH_160:
+ chandef = sdata->u.ibss.chandef;
+ chandef.chan = cbss->channel;
+ break;
default:
/* fall back to 20 MHz for unsupported modes */
cfg80211_chandef_create(&chandef, cbss->channel,
@@ -470,22 +483,19 @@ int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
struct beacon_data *presp, *old_presp;
struct cfg80211_bss *cbss;
const struct cfg80211_bss_ies *ies;
- u16 capability;
+ u16 capability = 0;
u64 tsf;
int ret = 0;
sdata_assert_lock(sdata);
- capability = WLAN_CAPABILITY_IBSS;
-
if (ifibss->privacy)
- capability |= WLAN_CAPABILITY_PRIVACY;
+ capability = WLAN_CAPABILITY_PRIVACY;
cbss = cfg80211_get_bss(sdata->local->hw.wiphy, ifibss->chandef.chan,
ifibss->bssid, ifibss->ssid,
- ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
- WLAN_CAPABILITY_PRIVACY,
- capability);
+ ifibss->ssid_len, IEEE80211_BSS_TYPE_IBSS,
+ IEEE80211_PRIVACY(ifibss->privacy));
if (WARN_ON(!cbss)) {
ret = -EINVAL;
@@ -525,23 +535,17 @@ int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata)
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct cfg80211_bss *cbss;
int err, changed = 0;
- u16 capability;
sdata_assert_lock(sdata);
/* update cfg80211 bss information with the new channel */
if (!is_zero_ether_addr(ifibss->bssid)) {
- capability = WLAN_CAPABILITY_IBSS;
-
- if (ifibss->privacy)
- capability |= WLAN_CAPABILITY_PRIVACY;
-
cbss = cfg80211_get_bss(sdata->local->hw.wiphy,
ifibss->chandef.chan,
ifibss->bssid, ifibss->ssid,
- ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
- WLAN_CAPABILITY_PRIVACY,
- capability);
+ ifibss->ssid_len,
+ IEEE80211_BSS_TYPE_IBSS,
+ IEEE80211_PRIVACY(ifibss->privacy));
/* XXX: should not really modify cfg80211 data */
if (cbss) {
cbss->channel = sdata->csa_chandef.chan;
@@ -682,19 +686,13 @@ static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata)
struct cfg80211_bss *cbss;
struct beacon_data *presp;
struct sta_info *sta;
- u16 capability;
if (!is_zero_ether_addr(ifibss->bssid)) {
- capability = WLAN_CAPABILITY_IBSS;
-
- if (ifibss->privacy)
- capability |= WLAN_CAPABILITY_PRIVACY;
-
cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan,
ifibss->bssid, ifibss->ssid,
- ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
- WLAN_CAPABILITY_PRIVACY,
- capability);
+ ifibss->ssid_len,
+ IEEE80211_BSS_TYPE_IBSS,
+ IEEE80211_PRIVACY(ifibss->privacy));
if (cbss) {
cfg80211_unlink_bss(local->hw.wiphy, cbss);
@@ -980,110 +978,140 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
mgmt->sa, sdata->u.ibss.bssid, NULL, 0, 0, 0);
}
-static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt, size_t len,
- struct ieee80211_rx_status *rx_status,
- struct ieee802_11_elems *elems)
+static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt, size_t len,
+ struct ieee80211_rx_status *rx_status,
+ struct ieee802_11_elems *elems,
+ struct ieee80211_channel *channel)
{
- struct ieee80211_local *local = sdata->local;
- struct cfg80211_bss *cbss;
- struct ieee80211_bss *bss;
struct sta_info *sta;
- struct ieee80211_channel *channel;
- u64 beacon_timestamp, rx_timestamp;
- u32 supp_rates = 0;
enum ieee80211_band band = rx_status->band;
enum nl80211_bss_scan_width scan_width;
+ struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
bool rates_updated = false;
+ u32 supp_rates = 0;
- channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq);
- if (!channel)
+ if (sdata->vif.type != NL80211_IFTYPE_ADHOC)
return;
- if (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
- ether_addr_equal(mgmt->bssid, sdata->u.ibss.bssid)) {
+ if (!ether_addr_equal(mgmt->bssid, sdata->u.ibss.bssid))
+ return;
- rcu_read_lock();
- sta = sta_info_get(sdata, mgmt->sa);
-
- if (elems->supp_rates) {
- supp_rates = ieee80211_sta_get_rates(sdata, elems,
- band, NULL);
- if (sta) {
- u32 prev_rates;
-
- prev_rates = sta->sta.supp_rates[band];
- /* make sure mandatory rates are always added */
- scan_width = NL80211_BSS_CHAN_WIDTH_20;
- if (rx_status->flag & RX_FLAG_5MHZ)
- scan_width = NL80211_BSS_CHAN_WIDTH_5;
- if (rx_status->flag & RX_FLAG_10MHZ)
- scan_width = NL80211_BSS_CHAN_WIDTH_10;
-
- sta->sta.supp_rates[band] = supp_rates |
- ieee80211_mandatory_rates(sband,
- scan_width);
- if (sta->sta.supp_rates[band] != prev_rates) {
- ibss_dbg(sdata,
- "updated supp_rates set for %pM based on beacon/probe_resp (0x%x -> 0x%x)\n",
- sta->sta.addr, prev_rates,
- sta->sta.supp_rates[band]);
- rates_updated = true;
- }
- } else {
- rcu_read_unlock();
- sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid,
- mgmt->sa, supp_rates);
+ rcu_read_lock();
+ sta = sta_info_get(sdata, mgmt->sa);
+
+ if (elems->supp_rates) {
+ supp_rates = ieee80211_sta_get_rates(sdata, elems,
+ band, NULL);
+ if (sta) {
+ u32 prev_rates;
+
+ prev_rates = sta->sta.supp_rates[band];
+ /* make sure mandatory rates are always added */
+ scan_width = NL80211_BSS_CHAN_WIDTH_20;
+ if (rx_status->flag & RX_FLAG_5MHZ)
+ scan_width = NL80211_BSS_CHAN_WIDTH_5;
+ if (rx_status->flag & RX_FLAG_10MHZ)
+ scan_width = NL80211_BSS_CHAN_WIDTH_10;
+
+ sta->sta.supp_rates[band] = supp_rates |
+ ieee80211_mandatory_rates(sband, scan_width);
+ if (sta->sta.supp_rates[band] != prev_rates) {
+ ibss_dbg(sdata,
+ "updated supp_rates set for %pM based on beacon/probe_resp (0x%x -> 0x%x)\n",
+ sta->sta.addr, prev_rates,
+ sta->sta.supp_rates[band]);
+ rates_updated = true;
}
+ } else {
+ rcu_read_unlock();
+ sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid,
+ mgmt->sa, supp_rates);
}
+ }
- if (sta && elems->wmm_info)
- sta->sta.wme = true;
-
- if (sta && elems->ht_operation && elems->ht_cap_elem &&
- sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
- sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_5 &&
- sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_10) {
- /* we both use HT */
- struct ieee80211_ht_cap htcap_ie;
- struct cfg80211_chan_def chandef;
-
- ieee80211_ht_oper_to_chandef(channel,
- elems->ht_operation,
- &chandef);
-
- memcpy(&htcap_ie, elems->ht_cap_elem, sizeof(htcap_ie));
-
- /*
- * fall back to HT20 if we don't use or use
- * the other extension channel
- */
- if (chandef.center_freq1 !=
- sdata->u.ibss.chandef.center_freq1)
- htcap_ie.cap_info &=
- cpu_to_le16(~IEEE80211_HT_CAP_SUP_WIDTH_20_40);
-
- rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap(
- sdata, sband, &htcap_ie, sta);
+ if (sta && elems->wmm_info && local->hw.queues >= IEEE80211_NUM_ACS)
+ sta->sta.wme = true;
+
+ if (sta && elems->ht_operation && elems->ht_cap_elem &&
+ sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
+ sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_5 &&
+ sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_10) {
+ /* we both use HT */
+ struct ieee80211_ht_cap htcap_ie;
+ struct cfg80211_chan_def chandef;
+ enum ieee80211_sta_rx_bandwidth bw = sta->sta.bandwidth;
+
+ ieee80211_ht_oper_to_chandef(channel,
+ elems->ht_operation,
+ &chandef);
+
+ memcpy(&htcap_ie, elems->ht_cap_elem, sizeof(htcap_ie));
+ rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
+ &htcap_ie,
+ sta);
+
+ if (elems->vht_operation && elems->vht_cap_elem &&
+ sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_20 &&
+ sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_40) {
+ /* we both use VHT */
+ struct ieee80211_vht_cap cap_ie;
+ struct ieee80211_sta_vht_cap cap = sta->sta.vht_cap;
+
+ ieee80211_vht_oper_to_chandef(channel,
+ elems->vht_operation,
+ &chandef);
+ memcpy(&cap_ie, elems->vht_cap_elem, sizeof(cap_ie));
+ ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
+ &cap_ie, sta);
+ if (memcmp(&cap, &sta->sta.vht_cap, sizeof(cap)))
+ rates_updated |= true;
}
- if (sta && rates_updated) {
- u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED;
- u8 rx_nss = sta->sta.rx_nss;
+ if (bw != sta->sta.bandwidth)
+ rates_updated |= true;
- /* Force rx_nss recalculation */
- sta->sta.rx_nss = 0;
- rate_control_rate_init(sta);
- if (sta->sta.rx_nss != rx_nss)
- changed |= IEEE80211_RC_NSS_CHANGED;
+ if (!cfg80211_chandef_compatible(&sdata->u.ibss.chandef,
+ &chandef))
+ WARN_ON_ONCE(1);
+ }
- drv_sta_rc_update(local, sdata, &sta->sta, changed);
- }
+ if (sta && rates_updated) {
+ u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED;
+ u8 rx_nss = sta->sta.rx_nss;
- rcu_read_unlock();
+ /* Force rx_nss recalculation */
+ sta->sta.rx_nss = 0;
+ rate_control_rate_init(sta);
+ if (sta->sta.rx_nss != rx_nss)
+ changed |= IEEE80211_RC_NSS_CHANGED;
+
+ drv_sta_rc_update(local, sdata, &sta->sta, changed);
}
+ rcu_read_unlock();
+}
+
+static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt, size_t len,
+ struct ieee80211_rx_status *rx_status,
+ struct ieee802_11_elems *elems)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct cfg80211_bss *cbss;
+ struct ieee80211_bss *bss;
+ struct ieee80211_channel *channel;
+ u64 beacon_timestamp, rx_timestamp;
+ u32 supp_rates = 0;
+ enum ieee80211_band band = rx_status->band;
+
+ channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq);
+ if (!channel)
+ return;
+
+ ieee80211_update_sta_info(sdata, mgmt, len, rx_status, elems, channel);
+
bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
channel);
if (!bss)
@@ -1273,7 +1301,7 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
ieee80211_request_ibss_scan(sdata, ifibss->ssid, ifibss->ssid_len,
- NULL, scan_width);
+ NULL, 0, scan_width);
}
static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
@@ -1304,14 +1332,82 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
if (ifibss->privacy)
capability |= WLAN_CAPABILITY_PRIVACY;
- else
- sdata->drop_unencrypted = 0;
__ieee80211_sta_join_ibss(sdata, bssid, sdata->vif.bss_conf.beacon_int,
&ifibss->chandef, ifibss->basic_rates,
capability, 0, true);
}
+static unsigned ibss_setup_channels(struct wiphy *wiphy,
+ struct ieee80211_channel **channels,
+ unsigned int channels_max,
+ u32 center_freq, u32 width)
+{
+ struct ieee80211_channel *chan = NULL;
+ unsigned int n_chan = 0;
+ u32 start_freq, end_freq, freq;
+
+ if (width <= 20) {
+ start_freq = center_freq;
+ end_freq = center_freq;
+ } else {
+ start_freq = center_freq - width / 2 + 10;
+ end_freq = center_freq + width / 2 - 10;
+ }
+
+ for (freq = start_freq; freq <= end_freq; freq += 20) {
+ chan = ieee80211_get_channel(wiphy, freq);
+ if (!chan)
+ continue;
+ if (n_chan >= channels_max)
+ return n_chan;
+
+ channels[n_chan] = chan;
+ n_chan++;
+ }
+
+ return n_chan;
+}
+
+static unsigned int
+ieee80211_ibss_setup_scan_channels(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef,
+ struct ieee80211_channel **channels,
+ unsigned int channels_max)
+{
+ unsigned int n_chan = 0;
+ u32 width, cf1, cf2 = 0;
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_40:
+ width = 40;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ cf2 = chandef->center_freq2;
+ /* fall through */
+ case NL80211_CHAN_WIDTH_80:
+ width = 80;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ width = 160;
+ break;
+ default:
+ width = 20;
+ break;
+ }
+
+ cf1 = chandef->center_freq1;
+
+ n_chan = ibss_setup_channels(wiphy, channels, channels_max, cf1, width);
+
+ if (cf2)
+ n_chan += ibss_setup_channels(wiphy, &channels[n_chan],
+ channels_max - n_chan, cf2,
+ width);
+
+ return n_chan;
+}
+
/*
* This function is called with state == IEEE80211_IBSS_MLME_SEARCH
*/
@@ -1325,7 +1421,6 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
const u8 *bssid = NULL;
enum nl80211_bss_scan_width scan_width;
int active_ibss;
- u16 capability;
sdata_assert_lock(sdata);
@@ -1335,9 +1430,6 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
if (active_ibss)
return;
- capability = WLAN_CAPABILITY_IBSS;
- if (ifibss->privacy)
- capability |= WLAN_CAPABILITY_PRIVACY;
if (ifibss->fixed_bssid)
bssid = ifibss->bssid;
if (ifibss->fixed_channel)
@@ -1346,8 +1438,8 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
bssid = ifibss->bssid;
cbss = cfg80211_get_bss(local->hw.wiphy, chan, bssid,
ifibss->ssid, ifibss->ssid_len,
- WLAN_CAPABILITY_IBSS | WLAN_CAPABILITY_PRIVACY,
- capability);
+ IEEE80211_BSS_TYPE_IBSS,
+ IEEE80211_PRIVACY(ifibss->privacy));
if (cbss) {
struct ieee80211_bss *bss;
@@ -1381,11 +1473,18 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
/* Selected IBSS not found in current scan results - try to scan */
if (time_after(jiffies, ifibss->last_scan_completed +
IEEE80211_SCAN_INTERVAL)) {
+ struct ieee80211_channel *channels[8];
+ unsigned int num;
+
sdata_info(sdata, "Trigger new scan to find an IBSS to join\n");
+ num = ieee80211_ibss_setup_scan_channels(local->hw.wiphy,
+ &ifibss->chandef,
+ channels,
+ ARRAY_SIZE(channels));
scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
ieee80211_request_ibss_scan(sdata, ifibss->ssid,
- ifibss->ssid_len, chan,
+ ifibss->ssid_len, channels, num,
scan_width);
} else {
int interval = IEEE80211_SCAN_INTERVAL;
@@ -1742,7 +1841,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
ieee80211_ibss_disconnect(sdata);
ifibss->ssid_len = 0;
- memset(ifibss->bssid, 0, ETH_ALEN);
+ eth_zero_addr(ifibss->bssid);
/* remove beacon */
kfree(sdata->u.ibss.ie);
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 8d53d65bd2ab..ab46ab4a7249 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -26,6 +26,7 @@
#include <linux/etherdevice.h>
#include <linux/leds.h>
#include <linux/idr.h>
+#include <linux/rhashtable.h>
#include <net/ieee80211_radiotap.h>
#include <net/cfg80211.h>
#include <net/mac80211.h>
@@ -810,6 +811,19 @@ struct mac80211_qos_map {
struct rcu_head rcu_head;
};
+enum txq_info_flags {
+ IEEE80211_TXQ_STOP,
+ IEEE80211_TXQ_AMPDU,
+};
+
+struct txq_info {
+ struct sk_buff_head queue;
+ unsigned long flags;
+
+ /* keep last! */
+ struct ieee80211_txq txq;
+};
+
struct ieee80211_sub_if_data {
struct list_head list;
@@ -830,8 +844,6 @@ struct ieee80211_sub_if_data {
unsigned long state;
- int drop_unencrypted;
-
char name[IFNAMSIZ];
/* Fragment table for host-based reassembly */
@@ -854,6 +866,7 @@ struct ieee80211_sub_if_data {
bool control_port_no_encrypt;
int encrypt_headroom;
+ atomic_t txqs_len[IEEE80211_NUM_ACS];
struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];
struct mac80211_qos_map __rcu *qos_map;
@@ -1042,24 +1055,6 @@ struct tpt_led_trigger {
};
#endif
-/*
- * struct ieee80211_tx_latency_bin_ranges - Tx latency statistics bins ranges
- *
- * Measuring Tx latency statistics. Counts how many Tx frames transmitted in a
- * certain latency range (in Milliseconds). Each station that uses these
- * ranges will have bins to count the amount of frames received in that range.
- * The user can configure the ranges via debugfs.
- * If ranges is NULL then Tx latency statistics bins are disabled for all
- * stations.
- *
- * @n_ranges: number of ranges that are taken in account
- * @ranges: the ranges that the user requested or NULL if disabled.
- */
-struct ieee80211_tx_latency_bin_ranges {
- int n_ranges;
- u32 ranges[];
-};
-
/**
* mac80211 scan flags - currently active scan mode
*
@@ -1207,16 +1202,10 @@ struct ieee80211_local {
spinlock_t tim_lock;
unsigned long num_sta;
struct list_head sta_list;
- struct sta_info __rcu *sta_hash[STA_HASH_SIZE];
+ struct rhashtable sta_hash;
struct timer_list sta_cleanup;
int sta_generation;
- /*
- * Tx latency statistics parameters for all stations.
- * Can enable via debugfs (NULL when disabled).
- */
- struct ieee80211_tx_latency_bin_ranges __rcu *tx_latency;
-
struct sk_buff_head pending[IEEE80211_MAX_QUEUES];
struct tasklet_struct tx_pending_tasklet;
@@ -1298,7 +1287,6 @@ struct ieee80211_local {
/* TX/RX handler statistics */
unsigned int tx_handlers_drop;
unsigned int tx_handlers_queued;
- unsigned int tx_handlers_drop_unencrypted;
unsigned int tx_handlers_drop_fragment;
unsigned int tx_handlers_drop_wep;
unsigned int tx_handlers_drop_not_assoc;
@@ -1476,6 +1464,10 @@ static inline struct ieee80211_local *hw_to_local(
return container_of(hw, struct ieee80211_local, hw);
}
+static inline struct txq_info *to_txq_info(struct ieee80211_txq *txq)
+{
+ return container_of(txq, struct txq_info, txq);
+}
static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
{
@@ -1568,7 +1560,8 @@ int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata);
void ieee80211_scan_work(struct work_struct *work);
int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
const u8 *ssid, u8 ssid_len,
- struct ieee80211_channel *chan,
+ struct ieee80211_channel **channels,
+ unsigned int n_channels,
enum nl80211_bss_scan_width scan_width);
int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
struct cfg80211_scan_request *req);
@@ -1617,6 +1610,7 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
int ieee80211_iface_init(void);
void ieee80211_iface_exit(void);
int ieee80211_if_add(struct ieee80211_local *local, const char *name,
+ unsigned char name_assign_type,
struct wireless_dev **new_wdev, enum nl80211_iftype type,
struct vif_params *params);
int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
@@ -1784,7 +1778,8 @@ void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int ke
gfp_t gfp);
void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
bool bss_notify);
-void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb);
+void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta, struct sk_buff *skb);
void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, int tid,
@@ -1929,6 +1924,9 @@ static inline bool ieee80211_can_run_worker(struct ieee80211_local *local)
return true;
}
+void ieee80211_init_tx_queue(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta,
+ struct txq_info *txq, int tid);
void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
u16 transaction, u16 auth_alg, u16 status,
const u8 *extra, size_t extra_len, const u8 *bssid,
@@ -1967,10 +1965,6 @@ int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata);
void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata);
-size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
- const u8 *ids, int n_ids,
- const u8 *after_ric, int n_after_ric,
- size_t offset);
size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset);
u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
u16 cap);
@@ -1979,6 +1973,8 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
u16 prot_mode);
u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
u32 cap);
+u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
+ const struct cfg80211_chan_def *chandef);
int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
const struct ieee80211_supported_band *sband,
const u8 *srates, int srates_len, u32 *rates);
@@ -1994,6 +1990,9 @@ u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo);
void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
const struct ieee80211_ht_operation *ht_oper,
struct cfg80211_chan_def *chandef);
+void ieee80211_vht_oper_to_chandef(struct ieee80211_channel *control_chan,
+ const struct ieee80211_vht_operation *oper,
+ struct cfg80211_chan_def *chandef);
u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c);
int __must_check
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 81a27516813e..b4ac596a7cb7 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -969,6 +969,13 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
}
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+ if (sdata->vif.txq) {
+ struct txq_info *txqi = to_txq_info(sdata->vif.txq);
+
+ ieee80211_purge_tx_queue(&local->hw, &txqi->queue);
+ atomic_set(&sdata->txqs_len[txqi->txq.ac], 0);
+ }
+
if (local->open_count == 0)
ieee80211_clear_tx_pending(local);
@@ -1508,7 +1515,6 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
}
/* reset some values that shouldn't be kept across type changes */
- sdata->drop_unencrypted = 0;
if (type == NL80211_IFTYPE_STATION)
sdata->u.mgd.use_4addr = false;
@@ -1649,11 +1655,13 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local,
}
int ieee80211_if_add(struct ieee80211_local *local, const char *name,
+ unsigned char name_assign_type,
struct wireless_dev **new_wdev, enum nl80211_iftype type,
struct vif_params *params)
{
struct net_device *ndev = NULL;
struct ieee80211_sub_if_data *sdata = NULL;
+ struct txq_info *txqi;
int ret, i;
int txqs = 1;
@@ -1673,11 +1681,19 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
ieee80211_assign_perm_addr(local, wdev->address, type);
memcpy(sdata->vif.addr, wdev->address, ETH_ALEN);
} else {
+ int size = ALIGN(sizeof(*sdata) + local->hw.vif_data_size,
+ sizeof(void *));
+ int txq_size = 0;
+
+ if (local->ops->wake_tx_queue)
+ txq_size += sizeof(struct txq_info) +
+ local->hw.txq_data_size;
+
if (local->hw.queues >= IEEE80211_NUM_ACS)
txqs = IEEE80211_NUM_ACS;
- ndev = alloc_netdev_mqs(sizeof(*sdata) + local->hw.vif_data_size,
- name, NET_NAME_UNKNOWN,
+ ndev = alloc_netdev_mqs(size + txq_size,
+ name, name_assign_type,
ieee80211_if_setup, txqs, 1);
if (!ndev)
return -ENOMEM;
@@ -1711,6 +1727,11 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
memcpy(sdata->vif.addr, ndev->dev_addr, ETH_ALEN);
memcpy(sdata->name, ndev->name, IFNAMSIZ);
+ if (txq_size) {
+ txqi = netdev_priv(ndev) + size;
+ ieee80211_init_tx_queue(sdata, NULL, txqi, 0);
+ }
+
sdata->dev = ndev;
}
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 0825d76edcfc..2291cd730091 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -492,6 +492,7 @@ ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
for (j = 0; j < len; j++)
key->u.gen.rx_pn[i][j] =
seq[len - j - 1];
+ key->flags |= KEY_FLAG_CIPHER_SCHEME;
}
}
memcpy(key->conf.key, key_data, key_len);
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index d57a9915494f..c5a31835be0e 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -30,10 +30,12 @@ struct sta_info;
* @KEY_FLAG_UPLOADED_TO_HARDWARE: Indicates that this key is present
* in the hardware for TX crypto hardware acceleration.
* @KEY_FLAG_TAINTED: Key is tainted and packets should be dropped.
+ * @KEY_FLAG_CIPHER_SCHEME: This key is for a hardware cipher scheme
*/
enum ieee80211_internal_key_flags {
KEY_FLAG_UPLOADED_TO_HARDWARE = BIT(0),
KEY_FLAG_TAINTED = BIT(1),
+ KEY_FLAG_CIPHER_SCHEME = BIT(2),
};
enum ieee80211_internal_tkip_state {
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 5e09d354c5a5..df3051d96aff 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -557,6 +557,9 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
local = wiphy_priv(wiphy);
+ if (sta_info_init(local))
+ goto err_free;
+
local->hw.wiphy = wiphy;
local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN);
@@ -629,8 +632,6 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
spin_lock_init(&local->ack_status_lock);
idr_init(&local->ack_status_frames);
- sta_info_init(local);
-
for (i = 0; i < IEEE80211_MAX_QUEUES; i++) {
skb_queue_head_init(&local->pending[i]);
atomic_set(&local->agg_queue_stop[i], 0);
@@ -650,6 +651,9 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
ieee80211_roc_setup(local);
return &local->hw;
+ err_free:
+ wiphy_free(wiphy);
+ return NULL;
}
EXPORT_SYMBOL(ieee80211_alloc_hw_nm);
@@ -1035,6 +1039,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
local->dynamic_ps_forced_timeout = -1;
+ if (!local->hw.txq_ac_max_pending)
+ local->hw.txq_ac_max_pending = 64;
+
result = ieee80211_wep_init(local);
if (result < 0)
wiphy_debug(local->hw.wiphy, "Failed to initialize wep: %d\n",
@@ -1057,7 +1064,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
/* add one default STA interface if supported */
if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION) &&
!(hw->flags & IEEE80211_HW_NO_AUTO_VIF)) {
- result = ieee80211_if_add(local, "wlan%d", NULL,
+ result = ieee80211_if_add(local, "wlan%d", NET_NAME_ENUM, NULL,
NL80211_IFTYPE_STATION, NULL);
if (result)
wiphy_warn(local->hw.wiphy,
@@ -1173,7 +1180,6 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
destroy_workqueue(local->workqueue);
wiphy_unregister(local->hw.wiphy);
- sta_info_stop(local);
ieee80211_wep_free(local);
ieee80211_led_exit(local);
kfree(local->int_scan_req);
@@ -1201,8 +1207,6 @@ void ieee80211_free_hw(struct ieee80211_hw *hw)
ieee80211_free_ack_frame, NULL);
idr_destroy(&local->ack_status_frames);
- kfree(rcu_access_pointer(local->tx_latency));
-
sta_info_stop(local);
wiphy_free(local->hw.wiphy);
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 0c8b2a77d312..d4684242e78b 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -520,7 +520,7 @@ int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc,
} else {
*fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
/* RA TA DA SA */
- memset(hdr->addr1, 0, ETH_ALEN); /* RA is resolved later */
+ eth_zero_addr(hdr->addr1); /* RA is resolved later */
memcpy(hdr->addr2, meshsa, ETH_ALEN);
memcpy(hdr->addr3, meshda, ETH_ALEN);
memcpy(hdr->addr4, meshsa, ETH_ALEN);
@@ -574,7 +574,8 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata)
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
u32 changed;
- ieee80211_sta_expire(sdata, ifmsh->mshcfg.plink_timeout * HZ);
+ if (ifmsh->mshcfg.plink_timeout > 0)
+ ieee80211_sta_expire(sdata, ifmsh->mshcfg.plink_timeout * HZ);
mesh_path_expire(sdata);
changed = mesh_accept_plinks_update(sdata);
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index b488e1859b18..60d737f144e3 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -17,7 +17,7 @@
#define PLINK_GET_PLID(p) (p + 4)
#define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \
- jiffies + HZ * t / 1000))
+ jiffies + msecs_to_jiffies(t)))
enum plink_event {
PLINK_UNDEFINED,
@@ -382,6 +382,7 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_supported_band *sband;
u32 rates, basic_rates = 0, changed = 0;
+ enum ieee80211_sta_rx_bandwidth bw = sta->sta.bandwidth;
sband = local->hw.wiphy->bands[band];
rates = ieee80211_sta_get_rates(sdata, elems, band, &basic_rates);
@@ -401,6 +402,9 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
elems->ht_cap_elem, sta))
changed |= IEEE80211_RC_BW_CHANGED;
+ if (bw != sta->sta.bandwidth)
+ changed |= IEEE80211_RC_BW_CHANGED;
+
/* HT peer is operating 20MHz-only */
if (elems->ht_operation &&
!(elems->ht_operation->ht_param &
@@ -621,9 +625,9 @@ static void mesh_plink_timer(unsigned long data)
sta->llid, sta->plid, reason);
}
-static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)
+static inline void mesh_plink_timer_set(struct sta_info *sta, u32 timeout)
{
- sta->plink_timer.expires = jiffies + (HZ * timeout / 1000);
+ sta->plink_timer.expires = jiffies + msecs_to_jiffies(timeout);
sta->plink_timer.data = (unsigned long) sta;
sta->plink_timer.function = mesh_plink_timer;
sta->plink_timeout = timeout;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 142f66aece18..26053bf2faa8 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1168,11 +1168,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
if (!conf) {
sdata_info(sdata,
"no channel context assigned to vif?, disconnecting\n");
- ieee80211_queue_work(&local->hw,
- &ifmgd->csa_connection_drop_work);
- mutex_unlock(&local->chanctx_mtx);
- mutex_unlock(&local->mtx);
- return;
+ goto drop_connection;
}
chanctx = container_of(conf, struct ieee80211_chanctx, conf);
@@ -1181,11 +1177,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
!(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
sdata_info(sdata,
"driver doesn't support chan-switch with channel contexts\n");
- ieee80211_queue_work(&local->hw,
- &ifmgd->csa_connection_drop_work);
- mutex_unlock(&local->chanctx_mtx);
- mutex_unlock(&local->mtx);
- return;
+ goto drop_connection;
}
ch_switch.timestamp = timestamp;
@@ -1197,11 +1189,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
if (drv_pre_channel_switch(sdata, &ch_switch)) {
sdata_info(sdata,
"preparing for channel switch failed, disconnecting\n");
- ieee80211_queue_work(&local->hw,
- &ifmgd->csa_connection_drop_work);
- mutex_unlock(&local->chanctx_mtx);
- mutex_unlock(&local->mtx);
- return;
+ goto drop_connection;
}
res = ieee80211_vif_reserve_chanctx(sdata, &csa_ie.chandef,
@@ -1210,11 +1198,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
sdata_info(sdata,
"failed to reserve channel context for channel switch, disconnecting (err=%d)\n",
res);
- ieee80211_queue_work(&local->hw,
- &ifmgd->csa_connection_drop_work);
- mutex_unlock(&local->chanctx_mtx);
- mutex_unlock(&local->mtx);
- return;
+ goto drop_connection;
}
mutex_unlock(&local->chanctx_mtx);
@@ -1244,6 +1228,11 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
mod_timer(&ifmgd->chswitch_timer,
TU_TO_EXP_TIME((csa_ie.count - 1) *
cbss->beacon_interval));
+ return;
+ drop_connection:
+ ieee80211_queue_work(&local->hw, &ifmgd->csa_connection_drop_work);
+ mutex_unlock(&local->chanctx_mtx);
+ mutex_unlock(&local->mtx);
}
static bool
@@ -1359,15 +1348,15 @@ static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
*/
if (has_80211h_pwr &&
(!has_cisco_pwr || pwr_level_80211h <= pwr_level_cisco)) {
- sdata_info(sdata,
- "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
- pwr_level_80211h, chan_pwr, pwr_reduction_80211h,
- sdata->u.mgd.bssid);
+ sdata_dbg(sdata,
+ "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
+ pwr_level_80211h, chan_pwr, pwr_reduction_80211h,
+ sdata->u.mgd.bssid);
new_ap_level = pwr_level_80211h;
} else { /* has_cisco_pwr is always true here. */
- sdata_info(sdata,
- "Limiting TX power to %d dBm as advertised by %pM\n",
- pwr_level_cisco, sdata->u.mgd.bssid);
+ sdata_dbg(sdata,
+ "Limiting TX power to %d dBm as advertised by %pM\n",
+ pwr_level_cisco, sdata->u.mgd.bssid);
new_ap_level = pwr_level_cisco;
}
@@ -1633,9 +1622,6 @@ void ieee80211_dynamic_ps_timer(unsigned long data)
{
struct ieee80211_local *local = (void *) data;
- if (local->quiescing || local->suspended)
- return;
-
ieee80211_queue_work(&local->hw, &local->dynamic_ps_enable_work);
}
@@ -2045,7 +2031,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
ieee80211_flush_queues(local, sdata, false);
/* clear bssid only after building the needed mgmt frames */
- memset(ifmgd->bssid, 0, ETH_ALEN);
+ eth_zero_addr(ifmgd->bssid);
/* remove AP and TDLS peers */
sta_info_flush(sdata);
@@ -2260,7 +2246,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
else
ssid_len = ssid[1];
- ieee80211_send_probe_req(sdata, sdata->vif.addr, NULL,
+ ieee80211_send_probe_req(sdata, sdata->vif.addr, dst,
ssid + 2, ssid_len, NULL,
0, (u32) -1, true, 0,
ifmgd->associated->channel, false);
@@ -2372,6 +2358,24 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_ap_probereq_get);
+static void ieee80211_report_disconnect(struct ieee80211_sub_if_data *sdata,
+ const u8 *buf, size_t len, bool tx,
+ u16 reason)
+{
+ struct ieee80211_event event = {
+ .type = MLME_EVENT,
+ .u.mlme.data = tx ? DEAUTH_TX_EVENT : DEAUTH_RX_EVENT,
+ .u.mlme.reason = reason,
+ };
+
+ if (tx)
+ cfg80211_tx_mlme_mgmt(sdata->dev, buf, len);
+ else
+ cfg80211_rx_mlme_mgmt(sdata->dev, buf, len);
+
+ drv_event_callback(sdata->local, sdata, &event);
+}
+
static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
@@ -2397,8 +2401,9 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
}
mutex_unlock(&local->mtx);
- cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
- IEEE80211_DEAUTH_FRAME_LEN);
+ ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true,
+ WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
+
sdata_unlock(sdata);
}
@@ -2477,7 +2482,7 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata,
del_timer_sync(&sdata->u.mgd.timer);
sta_info_destroy_addr(sdata, auth_data->bss->bssid);
- memset(sdata->u.mgd.bssid, 0, ETH_ALEN);
+ eth_zero_addr(sdata->u.mgd.bssid);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
sdata->u.mgd.flags = 0;
mutex_lock(&sdata->local->mtx);
@@ -2522,6 +2527,10 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
u8 bssid[ETH_ALEN];
u16 auth_alg, auth_transaction, status_code;
struct sta_info *sta;
+ struct ieee80211_event event = {
+ .type = MLME_EVENT,
+ .u.mlme.data = AUTH_EVENT,
+ };
sdata_assert_lock(sdata);
@@ -2554,6 +2563,9 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
mgmt->sa, status_code);
ieee80211_destroy_auth_data(sdata, false);
cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
+ event.u.mlme.status = MLME_DENIED;
+ event.u.mlme.reason = status_code;
+ drv_event_callback(sdata->local, sdata, &event);
return;
}
@@ -2576,6 +2588,8 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
return;
}
+ event.u.mlme.status = MLME_SUCCESS;
+ drv_event_callback(sdata->local, sdata, &event);
sdata_info(sdata, "authenticated\n");
ifmgd->auth_data->done = true;
ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC;
@@ -2694,7 +2708,7 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
- cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
+ ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false, reason_code);
}
@@ -2720,7 +2734,7 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
- cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
+ ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false, reason_code);
}
static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
@@ -2790,7 +2804,7 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,
del_timer_sync(&sdata->u.mgd.timer);
sta_info_destroy_addr(sdata, assoc_data->bss->bssid);
- memset(sdata->u.mgd.bssid, 0, ETH_ALEN);
+ eth_zero_addr(sdata->u.mgd.bssid);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
sdata->u.mgd.flags = 0;
mutex_lock(&sdata->local->mtx);
@@ -2982,10 +2996,14 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
rate_control_rate_init(sta);
- if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED)
+ if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) {
set_sta_flag(sta, WLAN_STA_MFP);
+ sta->sta.mfp = true;
+ } else {
+ sta->sta.mfp = false;
+ }
- sta->sta.wme = elems.wmm_param;
+ sta->sta.wme = elems.wmm_param && local->hw.queues >= IEEE80211_NUM_ACS;
err = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
if (!err && !(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
@@ -3055,6 +3073,10 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
u8 *pos;
bool reassoc;
struct cfg80211_bss *bss;
+ struct ieee80211_event event = {
+ .type = MLME_EVENT,
+ .u.mlme.data = ASSOC_EVENT,
+ };
sdata_assert_lock(sdata);
@@ -3106,6 +3128,9 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
sdata_info(sdata, "%pM denied association (code=%d)\n",
mgmt->sa, status_code);
ieee80211_destroy_assoc_data(sdata, false);
+ event.u.mlme.status = MLME_DENIED;
+ event.u.mlme.reason = status_code;
+ drv_event_callback(sdata->local, sdata, &event);
} else {
if (!ieee80211_assoc_success(sdata, bss, mgmt, len)) {
/* oops -- internal error -- send timeout for now */
@@ -3113,6 +3138,8 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
cfg80211_assoc_timeout(sdata->dev, bss);
return;
}
+ event.u.mlme.status = MLME_SUCCESS;
+ drv_event_callback(sdata->local, sdata, &event);
sdata_info(sdata, "associated\n");
/*
@@ -3315,6 +3342,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) {
int sig = ifmgd->ave_beacon_signal;
int last_sig = ifmgd->last_ave_beacon_signal;
+ struct ieee80211_event event = {
+ .type = RSSI_EVENT,
+ };
/*
* if signal crosses either of the boundaries, invoke callback
@@ -3323,12 +3353,14 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
if (sig > ifmgd->rssi_max_thold &&
(last_sig <= ifmgd->rssi_min_thold || last_sig == 0)) {
ifmgd->last_ave_beacon_signal = sig;
- drv_rssi_callback(local, sdata, RSSI_EVENT_HIGH);
+ event.u.rssi.data = RSSI_EVENT_HIGH;
+ drv_event_callback(local, sdata, &event);
} else if (sig < ifmgd->rssi_min_thold &&
(last_sig >= ifmgd->rssi_max_thold ||
last_sig == 0)) {
ifmgd->last_ave_beacon_signal = sig;
- drv_rssi_callback(local, sdata, RSSI_EVENT_LOW);
+ event.u.rssi.data = RSSI_EVENT_LOW;
+ drv_event_callback(local, sdata, &event);
}
}
@@ -3433,6 +3465,26 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
if (ifmgd->csa_waiting_bcn)
ieee80211_chswitch_post_beacon(sdata);
+ /*
+ * Update beacon timing and dtim count on every beacon appearance. This
+ * will allow the driver to use the most updated values. Do it before
+ * comparing this one with last received beacon.
+ * IMPORTANT: These parameters would possibly be out of sync by the time
+ * the driver will use them. The synchronized view is currently
+ * guaranteed only in certain callbacks.
+ */
+ if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) {
+ sdata->vif.bss_conf.sync_tsf =
+ le64_to_cpu(mgmt->u.beacon.timestamp);
+ sdata->vif.bss_conf.sync_device_ts =
+ rx_status->device_timestamp;
+ if (elems.tim)
+ sdata->vif.bss_conf.sync_dtim_count =
+ elems.tim->dtim_count;
+ else
+ sdata->vif.bss_conf.sync_dtim_count = 0;
+ }
+
if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
return;
ifmgd->beacon_crc = ncrc;
@@ -3460,18 +3512,6 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
else
bss_conf->dtim_period = 1;
- if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) {
- sdata->vif.bss_conf.sync_tsf =
- le64_to_cpu(mgmt->u.beacon.timestamp);
- sdata->vif.bss_conf.sync_device_ts =
- rx_status->device_timestamp;
- if (elems.tim)
- sdata->vif.bss_conf.sync_dtim_count =
- elems.tim->dtim_count;
- else
- sdata->vif.bss_conf.sync_dtim_count = 0;
- }
-
changed |= BSS_CHANGED_BEACON_INFO;
ifmgd->have_beacon = true;
@@ -3502,8 +3542,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
WLAN_REASON_DEAUTH_LEAVING,
true, deauth_buf);
- cfg80211_tx_mlme_mgmt(sdata->dev, deauth_buf,
- sizeof(deauth_buf));
+ ieee80211_report_disconnect(sdata, deauth_buf,
+ sizeof(deauth_buf), true,
+ WLAN_REASON_DEAUTH_LEAVING);
return;
}
@@ -3621,8 +3662,8 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason,
tx, frame_buf);
- cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
- IEEE80211_DEAUTH_FRAME_LEN);
+ ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true,
+ reason);
}
static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
@@ -3816,12 +3857,18 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
ieee80211_destroy_auth_data(sdata, false);
} else if (ieee80211_probe_auth(sdata)) {
u8 bssid[ETH_ALEN];
+ struct ieee80211_event event = {
+ .type = MLME_EVENT,
+ .u.mlme.data = AUTH_EVENT,
+ .u.mlme.status = MLME_TIMEOUT,
+ };
memcpy(bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN);
ieee80211_destroy_auth_data(sdata, false);
cfg80211_auth_timeout(sdata->dev, bssid);
+ drv_event_callback(sdata->local, sdata, &event);
}
} else if (ifmgd->auth_data && ifmgd->auth_data->timeout_started)
run_again(sdata, ifmgd->auth_data->timeout);
@@ -3831,9 +3878,15 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
if ((ifmgd->assoc_data->need_beacon && !ifmgd->have_beacon) ||
ieee80211_do_assoc(sdata)) {
struct cfg80211_bss *bss = ifmgd->assoc_data->bss;
+ struct ieee80211_event event = {
+ .type = MLME_EVENT,
+ .u.mlme.data = ASSOC_EVENT,
+ .u.mlme.status = MLME_TIMEOUT,
+ };
ieee80211_destroy_assoc_data(sdata, false);
cfg80211_assoc_timeout(sdata->dev, bss);
+ drv_event_callback(sdata->local, sdata, &event);
}
} else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started)
run_again(sdata, ifmgd->assoc_data->timeout);
@@ -3905,12 +3958,8 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data)
{
struct ieee80211_sub_if_data *sdata =
(struct ieee80211_sub_if_data *) data;
- struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- if (local->quiescing)
- return;
-
if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
return;
@@ -3926,9 +3975,6 @@ static void ieee80211_sta_conn_mon_timer(unsigned long data)
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
- if (local->quiescing)
- return;
-
if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
return;
@@ -3991,6 +4037,34 @@ void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata)
IEEE80211_DEAUTH_FRAME_LEN);
}
+ /* This is a bit of a hack - we should find a better and more generic
+ * solution to this. Normally when suspending, cfg80211 will in fact
+ * deauthenticate. However, it doesn't (and cannot) stop an ongoing
+ * auth (not so important) or assoc (this is the problem) process.
+ *
+ * As a consequence, it can happen that we are in the process of both
+ * associating and suspending, and receive an association response
+ * after cfg80211 has checked if it needs to disconnect, but before
+ * we actually set the flag to drop incoming frames. This will then
+ * cause the workqueue flush to process the association response in
+ * the suspend, resulting in a successful association just before it
+ * tries to remove the interface from the driver, which now though
+ * has a channel context assigned ... this results in issues.
+ *
+ * To work around this (for now) simply deauth here again if we're
+ * now connected.
+ */
+ if (ifmgd->associated && !sdata->local->wowlan) {
+ u8 bssid[ETH_ALEN];
+ struct cfg80211_deauth_request req = {
+ .reason_code = WLAN_REASON_DEAUTH_LEAVING,
+ .bssid = bssid,
+ };
+
+ memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
+ ieee80211_mgd_deauth(sdata, &req);
+ }
+
sdata_unlock(sdata);
}
@@ -4379,6 +4453,10 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
} else
WARN_ON_ONCE(!ether_addr_equal(ifmgd->bssid, cbss->bssid));
+ /* Cancel scan to ensure that nothing interferes with connection */
+ if (local->scanning)
+ ieee80211_scan_cancel(local);
+
return 0;
}
@@ -4467,8 +4545,9 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
WLAN_REASON_UNSPECIFIED,
false, frame_buf);
- cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
- sizeof(frame_buf));
+ ieee80211_report_disconnect(sdata, frame_buf,
+ sizeof(frame_buf), true,
+ WLAN_REASON_UNSPECIFIED);
}
sdata_info(sdata, "authenticate with %pM\n", req->bss->bssid);
@@ -4488,7 +4567,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
return 0;
err_clear:
- memset(ifmgd->bssid, 0, ETH_ALEN);
+ eth_zero_addr(ifmgd->bssid);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
ifmgd->auth_data = NULL;
err_free:
@@ -4568,8 +4647,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
WLAN_REASON_UNSPECIFIED,
false, frame_buf);
- cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
- sizeof(frame_buf));
+ ieee80211_report_disconnect(sdata, frame_buf,
+ sizeof(frame_buf), true,
+ WLAN_REASON_UNSPECIFIED);
}
if (ifmgd->auth_data && !ifmgd->auth_data->done) {
@@ -4831,7 +4911,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
return 0;
err_clear:
- memset(ifmgd->bssid, 0, ETH_ALEN);
+ eth_zero_addr(ifmgd->bssid);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
ifmgd->assoc_data = NULL;
err_free:
@@ -4859,8 +4939,9 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
req->reason_code, tx,
frame_buf);
ieee80211_destroy_auth_data(sdata, false);
- cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
- IEEE80211_DEAUTH_FRAME_LEN);
+ ieee80211_report_disconnect(sdata, frame_buf,
+ sizeof(frame_buf), true,
+ req->reason_code);
return 0;
}
@@ -4874,8 +4955,9 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
req->reason_code, tx, frame_buf);
- cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
- IEEE80211_DEAUTH_FRAME_LEN);
+ ieee80211_report_disconnect(sdata, frame_buf,
+ sizeof(frame_buf), true,
+ req->reason_code);
return 0;
}
@@ -4907,8 +4989,8 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
req->reason_code, !req->local_state_change,
frame_buf);
- cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
- IEEE80211_DEAUTH_FRAME_LEN);
+ ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true,
+ req->reason_code);
return 0;
}
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index ca405b6b686d..ac6ad6238e3a 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -59,9 +59,26 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
cancel_work_sync(&local->dynamic_ps_enable_work);
del_timer_sync(&local->dynamic_ps_timer);
- local->wowlan = wowlan && local->open_count;
+ local->wowlan = wowlan;
if (local->wowlan) {
- int err = drv_suspend(local, wowlan);
+ int err;
+
+ /* Drivers don't expect to suspend while some operations like
+ * authenticating or associating are in progress. It doesn't
+ * make sense anyway to accept that, since the authentication
+ * or association would never finish since the driver can't do
+ * that on its own.
+ * Thus, clean up in-progress auth/assoc first.
+ */
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (!ieee80211_sdata_running(sdata))
+ continue;
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ continue;
+ ieee80211_mgd_quiesce(sdata);
+ }
+
+ err = drv_suspend(local, wowlan);
if (err < 0) {
local->quiescing = false;
local->wowlan = false;
@@ -80,6 +97,13 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
return err;
} else if (err > 0) {
WARN_ON(err != 1);
+ /* cfg80211 will call back into mac80211 to disconnect
+ * all interfaces, allow that to proceed properly
+ */
+ ieee80211_wake_queues_by_reason(hw,
+ IEEE80211_MAX_QUEUE_MAP,
+ IEEE80211_QUEUE_STOP_REASON_SUSPEND,
+ false);
return err;
} else {
goto suspend;
diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c
index ef6e8a6c4253..247552a7f6c2 100644
--- a/net/mac80211/rc80211_minstrel.c
+++ b/net/mac80211/rc80211_minstrel.c
@@ -69,14 +69,39 @@ rix_to_ndx(struct minstrel_sta_info *mi, int rix)
return i;
}
+/* return current EMWA throughput */
+int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_ewma)
+{
+ int usecs;
+
+ usecs = mr->perfect_tx_time;
+ if (!usecs)
+ usecs = 1000000;
+
+ /* reset thr. below 10% success */
+ if (mr->stats.prob_ewma < MINSTREL_FRAC(10, 100))
+ return 0;
+
+ if (prob_ewma > MINSTREL_FRAC(90, 100))
+ return MINSTREL_TRUNC(100000 * (MINSTREL_FRAC(90, 100) / usecs));
+ else
+ return MINSTREL_TRUNC(100000 * (prob_ewma / usecs));
+}
+
/* find & sort topmost throughput rates */
static inline void
minstrel_sort_best_tp_rates(struct minstrel_sta_info *mi, int i, u8 *tp_list)
{
int j = MAX_THR_RATES;
+ struct minstrel_rate_stats *tmp_mrs = &mi->r[j - 1].stats;
+ struct minstrel_rate_stats *cur_mrs = &mi->r[i].stats;
- while (j > 0 && mi->r[i].stats.cur_tp > mi->r[tp_list[j - 1]].stats.cur_tp)
+ while (j > 0 && (minstrel_get_tp_avg(&mi->r[i], cur_mrs->prob_ewma) >
+ minstrel_get_tp_avg(&mi->r[tp_list[j - 1]], tmp_mrs->prob_ewma))) {
j--;
+ tmp_mrs = &mi->r[tp_list[j - 1]].stats;
+ }
+
if (j < MAX_THR_RATES - 1)
memmove(&tp_list[j + 1], &tp_list[j], MAX_THR_RATES - (j + 1));
if (j < MAX_THR_RATES)
@@ -127,13 +152,47 @@ minstrel_update_rates(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
rate_control_set_rates(mp->hw, mi->sta, ratetbl);
}
+/*
+* Recalculate statistics and counters of a given rate
+*/
+void
+minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs)
+{
+ if (unlikely(mrs->attempts > 0)) {
+ mrs->sample_skipped = 0;
+ mrs->cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts);
+ if (unlikely(!mrs->att_hist)) {
+ mrs->prob_ewma = mrs->cur_prob;
+ } else {
+ /* update exponential weighted moving variance */
+ mrs->prob_ewmsd = minstrel_ewmsd(mrs->prob_ewmsd,
+ mrs->cur_prob,
+ mrs->prob_ewma,
+ EWMA_LEVEL);
+
+ /*update exponential weighted moving avarage */
+ mrs->prob_ewma = minstrel_ewma(mrs->prob_ewma,
+ mrs->cur_prob,
+ EWMA_LEVEL);
+ }
+ mrs->att_hist += mrs->attempts;
+ mrs->succ_hist += mrs->success;
+ } else {
+ mrs->sample_skipped++;
+ }
+
+ mrs->last_success = mrs->success;
+ mrs->last_attempts = mrs->attempts;
+ mrs->success = 0;
+ mrs->attempts = 0;
+}
+
static void
minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
{
u8 tmp_tp_rate[MAX_THR_RATES];
u8 tmp_prob_rate = 0;
- u32 usecs;
- int i;
+ int i, tmp_cur_tp, tmp_prob_tp;
for (i = 0; i < MAX_THR_RATES; i++)
tmp_tp_rate[i] = 0;
@@ -141,38 +200,15 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
for (i = 0; i < mi->n_rates; i++) {
struct minstrel_rate *mr = &mi->r[i];
struct minstrel_rate_stats *mrs = &mi->r[i].stats;
+ struct minstrel_rate_stats *tmp_mrs = &mi->r[tmp_prob_rate].stats;
- usecs = mr->perfect_tx_time;
- if (!usecs)
- usecs = 1000000;
-
- if (unlikely(mrs->attempts > 0)) {
- mrs->sample_skipped = 0;
- mrs->cur_prob = MINSTREL_FRAC(mrs->success,
- mrs->attempts);
- mrs->succ_hist += mrs->success;
- mrs->att_hist += mrs->attempts;
- mrs->probability = minstrel_ewma(mrs->probability,
- mrs->cur_prob,
- EWMA_LEVEL);
- } else
- mrs->sample_skipped++;
-
- mrs->last_success = mrs->success;
- mrs->last_attempts = mrs->attempts;
- mrs->success = 0;
- mrs->attempts = 0;
-
- /* Update throughput per rate, reset thr. below 10% success */
- if (mrs->probability < MINSTREL_FRAC(10, 100))
- mrs->cur_tp = 0;
- else
- mrs->cur_tp = mrs->probability * (1000000 / usecs);
+ /* Update statistics of success probability per rate */
+ minstrel_calc_rate_stats(mrs);
/* Sample less often below the 10% chance of success.
* Sample less often above the 95% chance of success. */
- if (mrs->probability > MINSTREL_FRAC(95, 100) ||
- mrs->probability < MINSTREL_FRAC(10, 100)) {
+ if (mrs->prob_ewma > MINSTREL_FRAC(95, 100) ||
+ mrs->prob_ewma < MINSTREL_FRAC(10, 100)) {
mr->adjusted_retry_count = mrs->retry_count >> 1;
if (mr->adjusted_retry_count > 2)
mr->adjusted_retry_count = 2;
@@ -192,11 +228,14 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
* choose the maximum throughput rate as max_prob_rate
* (2) if all success probabilities < 95%, the rate with
* highest success probability is chosen as max_prob_rate */
- if (mrs->probability >= MINSTREL_FRAC(95, 100)) {
- if (mrs->cur_tp >= mi->r[tmp_prob_rate].stats.cur_tp)
+ if (mrs->prob_ewma >= MINSTREL_FRAC(95, 100)) {
+ tmp_cur_tp = minstrel_get_tp_avg(mr, mrs->prob_ewma);
+ tmp_prob_tp = minstrel_get_tp_avg(&mi->r[tmp_prob_rate],
+ tmp_mrs->prob_ewma);
+ if (tmp_cur_tp >= tmp_prob_tp)
tmp_prob_rate = i;
} else {
- if (mrs->probability >= mi->r[tmp_prob_rate].stats.probability)
+ if (mrs->prob_ewma >= tmp_mrs->prob_ewma)
tmp_prob_rate = i;
}
}
@@ -215,7 +254,7 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
#endif
/* Reset update timer */
- mi->stats_update = jiffies;
+ mi->last_stats_update = jiffies;
minstrel_update_rates(mp, mi);
}
@@ -253,7 +292,7 @@ minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband,
if (mi->sample_deferred > 0)
mi->sample_deferred--;
- if (time_after(jiffies, mi->stats_update +
+ if (time_after(jiffies, mi->last_stats_update +
(mp->update_interval * HZ) / 1000))
minstrel_update_stats(mp, mi);
}
@@ -385,7 +424,7 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
* has a probability of >95%, we shouldn't be attempting
* to use it, as this only wastes precious airtime */
if (!mrr_capable &&
- (mi->r[ndx].stats.probability > MINSTREL_FRAC(95, 100)))
+ (mi->r[ndx].stats.prob_ewma > MINSTREL_FRAC(95, 100)))
return;
mi->prev_sample = true;
@@ -519,7 +558,7 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
}
mi->n_rates = n;
- mi->stats_update = jiffies;
+ mi->last_stats_update = jiffies;
init_sample_table(mi);
minstrel_update_rates(mp, mi);
@@ -553,7 +592,7 @@ minstrel_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp)
if (!mi->sample_table)
goto error1;
- mi->stats_update = jiffies;
+ mi->last_stats_update = jiffies;
return mi;
error1:
@@ -663,12 +702,18 @@ minstrel_free(void *priv)
static u32 minstrel_get_expected_throughput(void *priv_sta)
{
struct minstrel_sta_info *mi = priv_sta;
+ struct minstrel_rate_stats *tmp_mrs;
int idx = mi->max_tp_rate[0];
+ int tmp_cur_tp;
/* convert pkt per sec in kbps (1200 is the average pkt size used for
* computing cur_tp
*/
- return MINSTREL_TRUNC(mi->r[idx].stats.cur_tp) * 1200 * 8 / 1024;
+ tmp_mrs = &mi->r[idx].stats;
+ tmp_cur_tp = minstrel_get_tp_avg(&mi->r[idx], tmp_mrs->prob_ewma);
+ tmp_cur_tp = tmp_cur_tp * 1200 * 8 / 1024;
+
+ return tmp_cur_tp;
}
const struct rate_control_ops mac80211_minstrel = {
diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h
index 410efe620c57..c230bbe93262 100644
--- a/net/mac80211/rc80211_minstrel.h
+++ b/net/mac80211/rc80211_minstrel.h
@@ -13,7 +13,6 @@
#define EWMA_DIV 128
#define SAMPLE_COLUMNS 10 /* number of columns in sample table */
-
/* scaled fraction values */
#define MINSTREL_SCALE 16
#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div)
@@ -24,11 +23,34 @@
/*
* Perform EWMA (Exponentially Weighted Moving Average) calculation
- */
+ */
static inline int
minstrel_ewma(int old, int new, int weight)
{
- return (new * (EWMA_DIV - weight) + old * weight) / EWMA_DIV;
+ int diff, incr;
+
+ diff = new - old;
+ incr = (EWMA_DIV - weight) * diff / EWMA_DIV;
+
+ return old + incr;
+}
+
+/*
+ * Perform EWMSD (Exponentially Weighted Moving Standard Deviation) calculation
+ */
+static inline int
+minstrel_ewmsd(int old_ewmsd, int cur_prob, int prob_ewma, int weight)
+{
+ int diff, incr, tmp_var;
+
+ /* calculate exponential weighted moving variance */
+ diff = MINSTREL_TRUNC((cur_prob - prob_ewma) * 1000000);
+ incr = (EWMA_DIV - weight) * diff / EWMA_DIV;
+ tmp_var = old_ewmsd * old_ewmsd;
+ tmp_var = weight * (tmp_var + diff * incr / 1000000) / EWMA_DIV;
+
+ /* return standard deviation */
+ return (u16) int_sqrt(tmp_var);
}
struct minstrel_rate_stats {
@@ -39,11 +61,13 @@ struct minstrel_rate_stats {
/* total attempts/success counters */
u64 att_hist, succ_hist;
- /* current throughput */
- unsigned int cur_tp;
-
- /* packet delivery probabilities */
- unsigned int cur_prob, probability;
+ /* statistis of packet delivery probability
+ * cur_prob - current prob within last update intervall
+ * prob_ewma - exponential weighted moving average of prob
+ * prob_ewmsd - exp. weighted moving standard deviation of prob */
+ unsigned int cur_prob;
+ unsigned int prob_ewma;
+ u16 prob_ewmsd;
/* maximum retry counts */
u8 retry_count;
@@ -71,7 +95,7 @@ struct minstrel_rate {
struct minstrel_sta_info {
struct ieee80211_sta *sta;
- unsigned long stats_update;
+ unsigned long last_stats_update;
unsigned int sp_ack_dur;
unsigned int rate_avg;
@@ -95,6 +119,7 @@ struct minstrel_sta_info {
#ifdef CONFIG_MAC80211_DEBUGFS
struct dentry *dbg_stats;
+ struct dentry *dbg_stats_csv;
#endif
};
@@ -121,7 +146,6 @@ struct minstrel_priv {
u32 fixed_rate_idx;
struct dentry *dbg_fixed_rate;
#endif
-
};
struct minstrel_debugfs_info {
@@ -133,8 +157,13 @@ extern const struct rate_control_ops mac80211_minstrel;
void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir);
void minstrel_remove_sta_debugfs(void *priv, void *priv_sta);
+/* Recalculate success probabilities and counters for a given rate using EWMA */
+void minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs);
+int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_ewma);
+
/* debugfs */
int minstrel_stats_open(struct inode *inode, struct file *file);
+int minstrel_stats_csv_open(struct inode *inode, struct file *file);
ssize_t minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *ppos);
int minstrel_stats_release(struct inode *inode, struct file *file);
diff --git a/net/mac80211/rc80211_minstrel_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c
index 2acab1bcaa4b..1db5f7c3318a 100644
--- a/net/mac80211/rc80211_minstrel_debugfs.c
+++ b/net/mac80211/rc80211_minstrel_debugfs.c
@@ -54,12 +54,28 @@
#include <net/mac80211.h>
#include "rc80211_minstrel.h"
+ssize_t
+minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *ppos)
+{
+ struct minstrel_debugfs_info *ms;
+
+ ms = file->private_data;
+ return simple_read_from_buffer(buf, len, ppos, ms->buf, ms->len);
+}
+
+int
+minstrel_stats_release(struct inode *inode, struct file *file)
+{
+ kfree(file->private_data);
+ return 0;
+}
+
int
minstrel_stats_open(struct inode *inode, struct file *file)
{
struct minstrel_sta_info *mi = inode->i_private;
struct minstrel_debugfs_info *ms;
- unsigned int i, tp, prob, eprob;
+ unsigned int i, tp_max, tp_avg, prob, eprob;
char *p;
ms = kmalloc(2048, GFP_KERNEL);
@@ -68,8 +84,14 @@ minstrel_stats_open(struct inode *inode, struct file *file)
file->private_data = ms;
p = ms->buf;
- p += sprintf(p, "rate tpt eprob *prob"
- " *ok(*cum) ok( cum)\n");
+ p += sprintf(p, "\n");
+ p += sprintf(p, "best __________rate_________ ______"
+ "statistics______ ________last_______ "
+ "______sum-of________\n");
+ p += sprintf(p, "rate [name idx airtime max_tp] [ ø(tp) ø(prob) "
+ "sd(prob)] [prob.|retry|suc|att] "
+ "[#success | #attempts]\n");
+
for (i = 0; i < mi->n_rates; i++) {
struct minstrel_rate *mr = &mi->r[i];
struct minstrel_rate_stats *mrs = &mi->r[i].stats;
@@ -79,18 +101,26 @@ minstrel_stats_open(struct inode *inode, struct file *file)
*(p++) = (i == mi->max_tp_rate[2]) ? 'C' : ' ';
*(p++) = (i == mi->max_tp_rate[3]) ? 'D' : ' ';
*(p++) = (i == mi->max_prob_rate) ? 'P' : ' ';
- p += sprintf(p, "%3u%s", mr->bitrate / 2,
+
+ p += sprintf(p, " %3u%s ", mr->bitrate / 2,
(mr->bitrate & 1 ? ".5" : " "));
+ p += sprintf(p, "%3u ", i);
+ p += sprintf(p, "%6u ", mr->perfect_tx_time);
- tp = MINSTREL_TRUNC(mrs->cur_tp / 10);
+ tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100));
+ tp_avg = minstrel_get_tp_avg(mr, mrs->prob_ewma);
prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
- eprob = MINSTREL_TRUNC(mrs->probability * 1000);
+ eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
- p += sprintf(p, " %4u.%1u %3u.%1u %3u.%1u"
- " %4u(%4u) %9llu(%9llu)\n",
- tp / 10, tp % 10,
+ p += sprintf(p, "%4u.%1u %4u.%1u %3u.%1u %3u.%1u"
+ " %3u.%1u %3u %3u %-3u "
+ "%9llu %-9llu\n",
+ tp_max / 10, tp_max % 10,
+ tp_avg / 10, tp_avg % 10,
eprob / 10, eprob % 10,
+ mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10,
prob / 10, prob % 10,
+ mrs->retry_count,
mrs->last_success,
mrs->last_attempts,
(unsigned long long)mrs->succ_hist,
@@ -107,25 +137,75 @@ minstrel_stats_open(struct inode *inode, struct file *file)
return 0;
}
-ssize_t
-minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *ppos)
+static const struct file_operations minstrel_stat_fops = {
+ .owner = THIS_MODULE,
+ .open = minstrel_stats_open,
+ .read = minstrel_stats_read,
+ .release = minstrel_stats_release,
+ .llseek = default_llseek,
+};
+
+int
+minstrel_stats_csv_open(struct inode *inode, struct file *file)
{
+ struct minstrel_sta_info *mi = inode->i_private;
struct minstrel_debugfs_info *ms;
+ unsigned int i, tp_max, tp_avg, prob, eprob;
+ char *p;
- ms = file->private_data;
- return simple_read_from_buffer(buf, len, ppos, ms->buf, ms->len);
-}
+ ms = kmalloc(2048, GFP_KERNEL);
+ if (!ms)
+ return -ENOMEM;
+
+ file->private_data = ms;
+ p = ms->buf;
+
+ for (i = 0; i < mi->n_rates; i++) {
+ struct minstrel_rate *mr = &mi->r[i];
+ struct minstrel_rate_stats *mrs = &mi->r[i].stats;
+
+ p += sprintf(p, "%s" ,((i == mi->max_tp_rate[0]) ? "A" : ""));
+ p += sprintf(p, "%s" ,((i == mi->max_tp_rate[1]) ? "B" : ""));
+ p += sprintf(p, "%s" ,((i == mi->max_tp_rate[2]) ? "C" : ""));
+ p += sprintf(p, "%s" ,((i == mi->max_tp_rate[3]) ? "D" : ""));
+ p += sprintf(p, "%s" ,((i == mi->max_prob_rate) ? "P" : ""));
+
+ p += sprintf(p, ",%u%s", mr->bitrate / 2,
+ (mr->bitrate & 1 ? ".5," : ","));
+ p += sprintf(p, "%u,", i);
+ p += sprintf(p, "%u,",mr->perfect_tx_time);
+
+ tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100));
+ tp_avg = minstrel_get_tp_avg(mr, mrs->prob_ewma);
+ prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
+ eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
+
+ p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u.%u,%u,%u,%u,"
+ "%llu,%llu,%d,%d\n",
+ tp_max / 10, tp_max % 10,
+ tp_avg / 10, tp_avg % 10,
+ eprob / 10, eprob % 10,
+ mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10,
+ prob / 10, prob % 10,
+ mrs->retry_count,
+ mrs->last_success,
+ mrs->last_attempts,
+ (unsigned long long)mrs->succ_hist,
+ (unsigned long long)mrs->att_hist,
+ mi->total_packets - mi->sample_packets,
+ mi->sample_packets);
+
+ }
+ ms->len = p - ms->buf;
+
+ WARN_ON(ms->len + sizeof(*ms) > 2048);
-int
-minstrel_stats_release(struct inode *inode, struct file *file)
-{
- kfree(file->private_data);
return 0;
}
-static const struct file_operations minstrel_stat_fops = {
+static const struct file_operations minstrel_stat_csv_fops = {
.owner = THIS_MODULE,
- .open = minstrel_stats_open,
+ .open = minstrel_stats_csv_open,
.read = minstrel_stats_read,
.release = minstrel_stats_release,
.llseek = default_llseek,
@@ -138,6 +218,9 @@ minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir)
mi->dbg_stats = debugfs_create_file("rc_stats", S_IRUGO, dir, mi,
&minstrel_stat_fops);
+
+ mi->dbg_stats_csv = debugfs_create_file("rc_stats_csv", S_IRUGO, dir,
+ mi, &minstrel_stat_csv_fops);
}
void
@@ -146,4 +229,6 @@ minstrel_remove_sta_debugfs(void *priv, void *priv_sta)
struct minstrel_sta_info *mi = priv_sta;
debugfs_remove(mi->dbg_stats);
+
+ debugfs_remove(mi->dbg_stats_csv);
}
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index 80452cfd2dc5..7430a1df2ab1 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -17,10 +17,11 @@
#include "rc80211_minstrel.h"
#include "rc80211_minstrel_ht.h"
+#define AVG_AMPDU_SIZE 16
#define AVG_PKT_SIZE 1200
/* Number of bits for an average sized packet */
-#define MCS_NBITS (AVG_PKT_SIZE << 3)
+#define MCS_NBITS ((AVG_PKT_SIZE * AVG_AMPDU_SIZE) << 3)
/* Number of symbols for a packet with (bps) bits per symbol */
#define MCS_NSYMS(bps) DIV_ROUND_UP(MCS_NBITS, (bps))
@@ -33,7 +34,8 @@
)
/* Transmit duration for the raw data part of an average sized packet */
-#define MCS_DURATION(streams, sgi, bps) MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps)))
+#define MCS_DURATION(streams, sgi, bps) \
+ (MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps))) / AVG_AMPDU_SIZE)
#define BW_20 0
#define BW_40 1
@@ -311,67 +313,35 @@ minstrel_get_ratestats(struct minstrel_ht_sta *mi, int index)
return &mi->groups[index / MCS_GROUP_RATES].rates[index % MCS_GROUP_RATES];
}
-
/*
- * Recalculate success probabilities and counters for a rate using EWMA
+ * Return current throughput based on the average A-MPDU length, taking into
+ * account the expected number of retransmissions and their expected length
*/
-static void
-minstrel_calc_rate_ewma(struct minstrel_rate_stats *mr)
+int
+minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate,
+ int prob_ewma)
{
- if (unlikely(mr->attempts > 0)) {
- mr->sample_skipped = 0;
- mr->cur_prob = MINSTREL_FRAC(mr->success, mr->attempts);
- if (!mr->att_hist)
- mr->probability = mr->cur_prob;
- else
- mr->probability = minstrel_ewma(mr->probability,
- mr->cur_prob, EWMA_LEVEL);
- mr->att_hist += mr->attempts;
- mr->succ_hist += mr->success;
- } else {
- mr->sample_skipped++;
- }
- mr->last_success = mr->success;
- mr->last_attempts = mr->attempts;
- mr->success = 0;
- mr->attempts = 0;
-}
-
-/*
- * Calculate throughput based on the average A-MPDU length, taking into account
- * the expected number of retransmissions and their expected length
- */
-static void
-minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate)
-{
- struct minstrel_rate_stats *mr;
unsigned int nsecs = 0;
- unsigned int tp;
- unsigned int prob;
- mr = &mi->groups[group].rates[rate];
- prob = mr->probability;
-
- if (prob < MINSTREL_FRAC(1, 10)) {
- mr->cur_tp = 0;
- return;
- }
-
- /*
- * For the throughput calculation, limit the probability value to 90% to
- * account for collision related packet error rate fluctuation
- */
- if (prob > MINSTREL_FRAC(9, 10))
- prob = MINSTREL_FRAC(9, 10);
+ /* do not account throughput if sucess prob is below 10% */
+ if (prob_ewma < MINSTREL_FRAC(10, 100))
+ return 0;
if (group != MINSTREL_CCK_GROUP)
nsecs = 1000 * mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len);
nsecs += minstrel_mcs_groups[group].duration[rate];
- /* prob is scaled - see MINSTREL_FRAC above */
- tp = 1000000 * ((prob * 1000) / nsecs);
- mr->cur_tp = MINSTREL_TRUNC(tp);
+ /*
+ * For the throughput calculation, limit the probability value to 90% to
+ * account for collision related packet error rate fluctuation
+ * (prob is scaled - see MINSTREL_FRAC above)
+ */
+ if (prob_ewma > MINSTREL_FRAC(90, 100))
+ return MINSTREL_TRUNC(100000 * ((MINSTREL_FRAC(90, 100) * 1000)
+ / nsecs));
+ else
+ return MINSTREL_TRUNC(100000 * ((prob_ewma * 1000) / nsecs));
}
/*
@@ -385,22 +355,23 @@ static void
minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u16 index,
u16 *tp_list)
{
- int cur_group, cur_idx, cur_thr, cur_prob;
- int tmp_group, tmp_idx, tmp_thr, tmp_prob;
+ int cur_group, cur_idx, cur_tp_avg, cur_prob;
+ int tmp_group, tmp_idx, tmp_tp_avg, tmp_prob;
int j = MAX_THR_RATES;
cur_group = index / MCS_GROUP_RATES;
cur_idx = index % MCS_GROUP_RATES;
- cur_thr = mi->groups[cur_group].rates[cur_idx].cur_tp;
- cur_prob = mi->groups[cur_group].rates[cur_idx].probability;
+ cur_prob = mi->groups[cur_group].rates[cur_idx].prob_ewma;
+ cur_tp_avg = minstrel_ht_get_tp_avg(mi, cur_group, cur_idx, cur_prob);
do {
tmp_group = tp_list[j - 1] / MCS_GROUP_RATES;
tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES;
- tmp_thr = mi->groups[tmp_group].rates[tmp_idx].cur_tp;
- tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability;
- if (cur_thr < tmp_thr ||
- (cur_thr == tmp_thr && cur_prob <= tmp_prob))
+ tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma;
+ tmp_tp_avg = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx,
+ tmp_prob);
+ if (cur_tp_avg < tmp_tp_avg ||
+ (cur_tp_avg == tmp_tp_avg && cur_prob <= tmp_prob))
break;
j--;
} while (j > 0);
@@ -420,16 +391,21 @@ static void
minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index)
{
struct minstrel_mcs_group_data *mg;
- struct minstrel_rate_stats *mr;
- int tmp_group, tmp_idx, tmp_tp, tmp_prob, max_tp_group;
+ struct minstrel_rate_stats *mrs;
+ int tmp_group, tmp_idx, tmp_tp_avg, tmp_prob;
+ int max_tp_group, cur_tp_avg, cur_group, cur_idx;
+ int max_gpr_group, max_gpr_idx;
+ int max_gpr_tp_avg, max_gpr_prob;
+ cur_group = index / MCS_GROUP_RATES;
+ cur_idx = index % MCS_GROUP_RATES;
mg = &mi->groups[index / MCS_GROUP_RATES];
- mr = &mg->rates[index % MCS_GROUP_RATES];
+ mrs = &mg->rates[index % MCS_GROUP_RATES];
tmp_group = mi->max_prob_rate / MCS_GROUP_RATES;
tmp_idx = mi->max_prob_rate % MCS_GROUP_RATES;
- tmp_tp = mi->groups[tmp_group].rates[tmp_idx].cur_tp;
- tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability;
+ tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma;
+ tmp_tp_avg = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob);
/* if max_tp_rate[0] is from MCS_GROUP max_prob_rate get selected from
* MCS_GROUP as well as CCK_GROUP rates do not allow aggregation */
@@ -438,15 +414,24 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index)
(max_tp_group != MINSTREL_CCK_GROUP))
return;
- if (mr->probability > MINSTREL_FRAC(75, 100)) {
- if (mr->cur_tp > tmp_tp)
+ if (mrs->prob_ewma > MINSTREL_FRAC(75, 100)) {
+ cur_tp_avg = minstrel_ht_get_tp_avg(mi, cur_group, cur_idx,
+ mrs->prob_ewma);
+ if (cur_tp_avg > tmp_tp_avg)
mi->max_prob_rate = index;
- if (mr->cur_tp > mg->rates[mg->max_group_prob_rate].cur_tp)
+
+ max_gpr_group = mg->max_group_prob_rate / MCS_GROUP_RATES;
+ max_gpr_idx = mg->max_group_prob_rate % MCS_GROUP_RATES;
+ max_gpr_prob = mi->groups[max_gpr_group].rates[max_gpr_idx].prob_ewma;
+ max_gpr_tp_avg = minstrel_ht_get_tp_avg(mi, max_gpr_group,
+ max_gpr_idx,
+ max_gpr_prob);
+ if (cur_tp_avg > max_gpr_tp_avg)
mg->max_group_prob_rate = index;
} else {
- if (mr->probability > tmp_prob)
+ if (mrs->prob_ewma > tmp_prob)
mi->max_prob_rate = index;
- if (mr->probability > mg->rates[mg->max_group_prob_rate].probability)
+ if (mrs->prob_ewma > mg->rates[mg->max_group_prob_rate].prob_ewma)
mg->max_group_prob_rate = index;
}
}
@@ -463,16 +448,18 @@ minstrel_ht_assign_best_tp_rates(struct minstrel_ht_sta *mi,
u16 tmp_mcs_tp_rate[MAX_THR_RATES],
u16 tmp_cck_tp_rate[MAX_THR_RATES])
{
- unsigned int tmp_group, tmp_idx, tmp_cck_tp, tmp_mcs_tp;
+ unsigned int tmp_group, tmp_idx, tmp_cck_tp, tmp_mcs_tp, tmp_prob;
int i;
tmp_group = tmp_cck_tp_rate[0] / MCS_GROUP_RATES;
tmp_idx = tmp_cck_tp_rate[0] % MCS_GROUP_RATES;
- tmp_cck_tp = mi->groups[tmp_group].rates[tmp_idx].cur_tp;
+ tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma;
+ tmp_cck_tp = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob);
tmp_group = tmp_mcs_tp_rate[0] / MCS_GROUP_RATES;
tmp_idx = tmp_mcs_tp_rate[0] % MCS_GROUP_RATES;
- tmp_mcs_tp = mi->groups[tmp_group].rates[tmp_idx].cur_tp;
+ tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma;
+ tmp_mcs_tp = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob);
if (tmp_cck_tp > tmp_mcs_tp) {
for(i = 0; i < MAX_THR_RATES; i++) {
@@ -491,8 +478,7 @@ static inline void
minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi)
{
struct minstrel_mcs_group_data *mg;
- struct minstrel_rate_stats *mr;
- int tmp_max_streams, group;
+ int tmp_max_streams, group, tmp_idx, tmp_prob;
int tmp_tp = 0;
tmp_max_streams = minstrel_mcs_groups[mi->max_tp_rate[0] /
@@ -501,11 +487,16 @@ minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi)
mg = &mi->groups[group];
if (!mg->supported || group == MINSTREL_CCK_GROUP)
continue;
- mr = minstrel_get_ratestats(mi, mg->max_group_prob_rate);
- if (tmp_tp < mr->cur_tp &&
+
+ tmp_idx = mg->max_group_prob_rate % MCS_GROUP_RATES;
+ tmp_prob = mi->groups[group].rates[tmp_idx].prob_ewma;
+
+ if (tmp_tp < minstrel_ht_get_tp_avg(mi, group, tmp_idx, tmp_prob) &&
(minstrel_mcs_groups[group].streams < tmp_max_streams)) {
mi->max_prob_rate = mg->max_group_prob_rate;
- tmp_tp = mr->cur_tp;
+ tmp_tp = minstrel_ht_get_tp_avg(mi, group,
+ tmp_idx,
+ tmp_prob);
}
}
}
@@ -523,8 +514,8 @@ static void
minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
{
struct minstrel_mcs_group_data *mg;
- struct minstrel_rate_stats *mr;
- int group, i, j;
+ struct minstrel_rate_stats *mrs;
+ int group, i, j, cur_prob;
u16 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES];
u16 tmp_cck_tp_rate[MAX_THR_RATES], index;
@@ -563,12 +554,12 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
index = MCS_GROUP_RATES * group + i;
- mr = &mg->rates[i];
- mr->retry_updated = false;
- minstrel_calc_rate_ewma(mr);
- minstrel_ht_calc_tp(mi, group, i);
+ mrs = &mg->rates[i];
+ mrs->retry_updated = false;
+ minstrel_calc_rate_stats(mrs);
+ cur_prob = mrs->prob_ewma;
- if (!mr->cur_tp)
+ if (minstrel_ht_get_tp_avg(mi, group, i, cur_prob) == 0)
continue;
/* Find max throughput rate set */
@@ -612,7 +603,7 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
#endif
/* Reset update timer */
- mi->stats_update = jiffies;
+ mi->last_stats_update = jiffies;
}
static bool
@@ -635,7 +626,7 @@ minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct ieee80211_tx_rate *rat
}
static void
-minstrel_next_sample_idx(struct minstrel_ht_sta *mi)
+minstrel_set_next_sample_idx(struct minstrel_ht_sta *mi)
{
struct minstrel_mcs_group_data *mg;
@@ -776,7 +767,8 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
update = true;
}
- if (time_after(jiffies, mi->stats_update + (mp->update_interval / 2 * HZ) / 1000)) {
+ if (time_after(jiffies, mi->last_stats_update +
+ (mp->update_interval / 2 * HZ) / 1000)) {
update = true;
minstrel_ht_update_stats(mp, mi);
}
@@ -789,7 +781,7 @@ static void
minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
int index)
{
- struct minstrel_rate_stats *mr;
+ struct minstrel_rate_stats *mrs;
const struct mcs_group *group;
unsigned int tx_time, tx_time_rtscts, tx_time_data;
unsigned int cw = mp->cw_min;
@@ -798,16 +790,16 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
unsigned int ampdu_len = MINSTREL_TRUNC(mi->avg_ampdu_len);
unsigned int overhead = 0, overhead_rtscts = 0;
- mr = minstrel_get_ratestats(mi, index);
- if (mr->probability < MINSTREL_FRAC(1, 10)) {
- mr->retry_count = 1;
- mr->retry_count_rtscts = 1;
+ mrs = minstrel_get_ratestats(mi, index);
+ if (mrs->prob_ewma < MINSTREL_FRAC(1, 10)) {
+ mrs->retry_count = 1;
+ mrs->retry_count_rtscts = 1;
return;
}
- mr->retry_count = 2;
- mr->retry_count_rtscts = 2;
- mr->retry_updated = true;
+ mrs->retry_count = 2;
+ mrs->retry_count_rtscts = 2;
+ mrs->retry_updated = true;
group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];
tx_time_data = group->duration[index % MCS_GROUP_RATES] * ampdu_len / 1000;
@@ -838,9 +830,9 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
tx_time_rtscts += ctime + overhead_rtscts + tx_time_data;
if (tx_time_rtscts < mp->segment_size)
- mr->retry_count_rtscts++;
+ mrs->retry_count_rtscts++;
} while ((tx_time < mp->segment_size) &&
- (++mr->retry_count < mp->max_retry));
+ (++mrs->retry_count < mp->max_retry));
}
@@ -849,22 +841,22 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
struct ieee80211_sta_rates *ratetbl, int offset, int index)
{
const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];
- struct minstrel_rate_stats *mr;
+ struct minstrel_rate_stats *mrs;
u8 idx;
u16 flags = group->flags;
- mr = minstrel_get_ratestats(mi, index);
- if (!mr->retry_updated)
+ mrs = minstrel_get_ratestats(mi, index);
+ if (!mrs->retry_updated)
minstrel_calc_retransmit(mp, mi, index);
- if (mr->probability < MINSTREL_FRAC(20, 100) || !mr->retry_count) {
+ if (mrs->prob_ewma < MINSTREL_FRAC(20, 100) || !mrs->retry_count) {
ratetbl->rate[offset].count = 2;
ratetbl->rate[offset].count_rts = 2;
ratetbl->rate[offset].count_cts = 2;
} else {
- ratetbl->rate[offset].count = mr->retry_count;
- ratetbl->rate[offset].count_cts = mr->retry_count;
- ratetbl->rate[offset].count_rts = mr->retry_count_rtscts;
+ ratetbl->rate[offset].count = mrs->retry_count;
+ ratetbl->rate[offset].count_cts = mrs->retry_count;
+ ratetbl->rate[offset].count_rts = mrs->retry_count_rtscts;
}
if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP)
@@ -922,7 +914,7 @@ minstrel_get_duration(int index)
static int
minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
{
- struct minstrel_rate_stats *mr;
+ struct minstrel_rate_stats *mrs;
struct minstrel_mcs_group_data *mg;
unsigned int sample_dur, sample_group, cur_max_tp_streams;
int sample_idx = 0;
@@ -938,12 +930,12 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
sample_group = mi->sample_group;
mg = &mi->groups[sample_group];
sample_idx = sample_table[mg->column][mg->index];
- minstrel_next_sample_idx(mi);
+ minstrel_set_next_sample_idx(mi);
if (!(mg->supported & BIT(sample_idx)))
return -1;
- mr = &mg->rates[sample_idx];
+ mrs = &mg->rates[sample_idx];
sample_idx += sample_group * MCS_GROUP_RATES;
/*
@@ -960,7 +952,7 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
* Do not sample if the probability is already higher than 95%
* to avoid wasting airtime.
*/
- if (mr->probability > MINSTREL_FRAC(95, 100))
+ if (mrs->prob_ewma > MINSTREL_FRAC(95, 100))
return -1;
/*
@@ -975,7 +967,7 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
(cur_max_tp_streams - 1 <
minstrel_mcs_groups[sample_group].streams ||
sample_dur >= minstrel_get_duration(mi->max_prob_rate))) {
- if (mr->sample_skipped < 20)
+ if (mrs->sample_skipped < 20)
return -1;
if (mi->sample_slow++ > 2)
@@ -1129,7 +1121,7 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
memset(mi, 0, sizeof(*mi));
mi->sta = sta;
- mi->stats_update = jiffies;
+ mi->last_stats_update = jiffies;
ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1, 0);
mi->overhead = ieee80211_frame_duration(sband->band, 0, 60, 1, 1, 0);
@@ -1326,16 +1318,19 @@ static u32 minstrel_ht_get_expected_throughput(void *priv_sta)
{
struct minstrel_ht_sta_priv *msp = priv_sta;
struct minstrel_ht_sta *mi = &msp->ht;
- int i, j;
+ int i, j, prob, tp_avg;
if (!msp->is_ht)
return mac80211_minstrel.get_expected_throughput(priv_sta);
i = mi->max_tp_rate[0] / MCS_GROUP_RATES;
j = mi->max_tp_rate[0] % MCS_GROUP_RATES;
+ prob = mi->groups[i].rates[j].prob_ewma;
+
+ /* convert tp_avg from pkt per second in kbps */
+ tp_avg = minstrel_ht_get_tp_avg(mi, i, j, prob) * AVG_PKT_SIZE * 8 / 1024;
- /* convert cur_tp from pkt per second in kbps */
- return mi->groups[i].rates[j].cur_tp * AVG_PKT_SIZE * 8 / 1024;
+ return tp_avg;
}
static const struct rate_control_ops mac80211_minstrel_ht = {
diff --git a/net/mac80211/rc80211_minstrel_ht.h b/net/mac80211/rc80211_minstrel_ht.h
index f2217d6aa0c2..e8b52a94d24b 100644
--- a/net/mac80211/rc80211_minstrel_ht.h
+++ b/net/mac80211/rc80211_minstrel_ht.h
@@ -78,7 +78,7 @@ struct minstrel_ht_sta {
u16 max_prob_rate;
/* time of last status update */
- unsigned long stats_update;
+ unsigned long last_stats_update;
/* overhead time in usec for each frame */
unsigned int overhead;
@@ -112,6 +112,7 @@ struct minstrel_ht_sta_priv {
};
#ifdef CONFIG_MAC80211_DEBUGFS
struct dentry *dbg_stats;
+ struct dentry *dbg_stats_csv;
#endif
void *ratelist;
void *sample_table;
@@ -120,5 +121,7 @@ struct minstrel_ht_sta_priv {
void minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir);
void minstrel_ht_remove_sta_debugfs(void *priv, void *priv_sta);
+int minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate,
+ int prob_ewma);
#endif
diff --git a/net/mac80211/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c
index 20c676b8e5b6..6822ce0f95e5 100644
--- a/net/mac80211/rc80211_minstrel_ht_debugfs.c
+++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c
@@ -19,7 +19,7 @@ static char *
minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
{
const struct mcs_group *mg;
- unsigned int j, tp, prob, eprob;
+ unsigned int j, tp_max, tp_avg, prob, eprob, tx_time;
char htmode = '2';
char gimode = 'L';
u32 gflags;
@@ -38,19 +38,26 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
gimode = 'S';
for (j = 0; j < MCS_GROUP_RATES; j++) {
- struct minstrel_rate_stats *mr = &mi->groups[i].rates[j];
+ struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j];
static const int bitrates[4] = { 10, 20, 55, 110 };
int idx = i * MCS_GROUP_RATES + j;
if (!(mi->groups[i].supported & BIT(j)))
continue;
- if (gflags & IEEE80211_TX_RC_MCS)
- p += sprintf(p, " HT%c0/%cGI ", htmode, gimode);
- else if (gflags & IEEE80211_TX_RC_VHT_MCS)
- p += sprintf(p, "VHT%c0/%cGI ", htmode, gimode);
- else
- p += sprintf(p, " CCK/%cP ", j < 4 ? 'L' : 'S');
+ if (gflags & IEEE80211_TX_RC_MCS) {
+ p += sprintf(p, "HT%c0 ", htmode);
+ p += sprintf(p, "%cGI ", gimode);
+ p += sprintf(p, "%d ", mg->streams);
+ } else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
+ p += sprintf(p, "VHT%c0 ", htmode);
+ p += sprintf(p, "%cGI ", gimode);
+ p += sprintf(p, "%d ", mg->streams);
+ } else {
+ p += sprintf(p, "CCK ");
+ p += sprintf(p, "%cP ", j < 4 ? 'L' : 'S');
+ p += sprintf(p, "1 ");
+ }
*(p++) = (idx == mi->max_tp_rate[0]) ? 'A' : ' ';
*(p++) = (idx == mi->max_tp_rate[1]) ? 'B' : ' ';
@@ -59,29 +66,39 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
*(p++) = (idx == mi->max_prob_rate) ? 'P' : ' ';
if (gflags & IEEE80211_TX_RC_MCS) {
- p += sprintf(p, " MCS%-2u ", (mg->streams - 1) * 8 + j);
+ p += sprintf(p, " MCS%-2u", (mg->streams - 1) * 8 + j);
} else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
- p += sprintf(p, " MCS%-1u/%1u", j, mg->streams);
+ p += sprintf(p, " MCS%-1u/%1u", j, mg->streams);
} else {
int r = bitrates[j % 4];
- p += sprintf(p, " %2u.%1uM ", r / 10, r % 10);
+ p += sprintf(p, " %2u.%1uM", r / 10, r % 10);
}
- tp = mr->cur_tp / 10;
- prob = MINSTREL_TRUNC(mr->cur_prob * 1000);
- eprob = MINSTREL_TRUNC(mr->probability * 1000);
+ p += sprintf(p, " %3u ", idx);
- p += sprintf(p, " %4u.%1u %3u.%1u %3u.%1u "
- "%3u %4u(%4u) %9llu(%9llu)\n",
- tp / 10, tp % 10,
+ /* tx_time[rate(i)] in usec */
+ tx_time = DIV_ROUND_CLOSEST(mg->duration[j], 1000);
+ p += sprintf(p, "%6u ", tx_time);
+
+ tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100));
+ tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_ewma);
+ prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
+ eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
+
+ p += sprintf(p, "%4u.%1u %4u.%1u %3u.%1u %3u.%1u"
+ " %3u.%1u %3u %3u %-3u "
+ "%9llu %-9llu\n",
+ tp_max / 10, tp_max % 10,
+ tp_avg / 10, tp_avg % 10,
eprob / 10, eprob % 10,
+ mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10,
prob / 10, prob % 10,
- mr->retry_count,
- mr->last_success,
- mr->last_attempts,
- (unsigned long long)mr->succ_hist,
- (unsigned long long)mr->att_hist);
+ mrs->retry_count,
+ mrs->last_success,
+ mrs->last_attempts,
+ (unsigned long long)mrs->succ_hist,
+ (unsigned long long)mrs->att_hist);
}
return p;
@@ -94,8 +111,8 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file)
struct minstrel_ht_sta *mi = &msp->ht;
struct minstrel_debugfs_info *ms;
unsigned int i;
- char *p;
int ret;
+ char *p;
if (!msp->is_ht) {
inode->i_private = &msp->legacy;
@@ -110,8 +127,14 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file)
file->private_data = ms;
p = ms->buf;
- p += sprintf(p, " type rate tpt eprob *prob "
- "ret *ok(*cum) ok( cum)\n");
+
+ p += sprintf(p, "\n");
+ p += sprintf(p, " best ____________rate__________ "
+ "______statistics______ ________last_______ "
+ "______sum-of________\n");
+ p += sprintf(p, "mode guard # rate [name idx airtime max_tp] "
+ "[ ø(tp) ø(prob) sd(prob)] [prob.|retry|suc|att] [#success | "
+ "#attempts]\n");
p = minstrel_ht_stats_dump(mi, MINSTREL_CCK_GROUP, p);
for (i = 0; i < MINSTREL_CCK_GROUP; i++)
@@ -123,11 +146,10 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file)
"lookaround %d\n",
max(0, (int) mi->total_packets - (int) mi->sample_packets),
mi->sample_packets);
- p += sprintf(p, "Average A-MPDU length: %d.%d\n",
+ p += sprintf(p, "Average # of aggregated frames per A-MPDU: %d.%d\n",
MINSTREL_TRUNC(mi->avg_ampdu_len),
MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10);
ms->len = p - ms->buf;
-
WARN_ON(ms->len + sizeof(*ms) > 32768);
return nonseekable_open(inode, file);
@@ -141,6 +163,143 @@ static const struct file_operations minstrel_ht_stat_fops = {
.llseek = no_llseek,
};
+static char *
+minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p)
+{
+ const struct mcs_group *mg;
+ unsigned int j, tp_max, tp_avg, prob, eprob, tx_time;
+ char htmode = '2';
+ char gimode = 'L';
+ u32 gflags;
+
+ if (!mi->groups[i].supported)
+ return p;
+
+ mg = &minstrel_mcs_groups[i];
+ gflags = mg->flags;
+
+ if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+ htmode = '4';
+ else if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH)
+ htmode = '8';
+ if (gflags & IEEE80211_TX_RC_SHORT_GI)
+ gimode = 'S';
+
+ for (j = 0; j < MCS_GROUP_RATES; j++) {
+ struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j];
+ static const int bitrates[4] = { 10, 20, 55, 110 };
+ int idx = i * MCS_GROUP_RATES + j;
+
+ if (!(mi->groups[i].supported & BIT(j)))
+ continue;
+
+ if (gflags & IEEE80211_TX_RC_MCS) {
+ p += sprintf(p, "HT%c0,", htmode);
+ p += sprintf(p, "%cGI,", gimode);
+ p += sprintf(p, "%d,", mg->streams);
+ } else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
+ p += sprintf(p, "VHT%c0,", htmode);
+ p += sprintf(p, "%cGI,", gimode);
+ p += sprintf(p, "%d,", mg->streams);
+ } else {
+ p += sprintf(p, "CCK,");
+ p += sprintf(p, "%cP,", j < 4 ? 'L' : 'S');
+ p += sprintf(p, "1,");
+ }
+
+ p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[0]) ? "A" : ""));
+ p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[1]) ? "B" : ""));
+ p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[2]) ? "C" : ""));
+ p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[3]) ? "D" : ""));
+ p += sprintf(p, "%s" ,((idx == mi->max_prob_rate) ? "P" : ""));
+
+ if (gflags & IEEE80211_TX_RC_MCS) {
+ p += sprintf(p, ",MCS%-2u,", (mg->streams - 1) * 8 + j);
+ } else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
+ p += sprintf(p, ",MCS%-1u/%1u,", j, mg->streams);
+ } else {
+ int r = bitrates[j % 4];
+ p += sprintf(p, ",%2u.%1uM,", r / 10, r % 10);
+ }
+
+ p += sprintf(p, "%u,", idx);
+ tx_time = DIV_ROUND_CLOSEST(mg->duration[j], 1000);
+ p += sprintf(p, "%u,", tx_time);
+
+ tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100));
+ tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_ewma);
+ prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
+ eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
+
+ p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u.%u,%u,%u,"
+ "%u,%llu,%llu,",
+ tp_max / 10, tp_max % 10,
+ tp_avg / 10, tp_avg % 10,
+ eprob / 10, eprob % 10,
+ mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10,
+ prob / 10, prob % 10,
+ mrs->retry_count,
+ mrs->last_success,
+ mrs->last_attempts,
+ (unsigned long long)mrs->succ_hist,
+ (unsigned long long)mrs->att_hist);
+ p += sprintf(p, "%d,%d,%d.%d\n",
+ max(0, (int) mi->total_packets -
+ (int) mi->sample_packets),
+ mi->sample_packets,
+ MINSTREL_TRUNC(mi->avg_ampdu_len),
+ MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10);
+ }
+
+ return p;
+}
+
+static int
+minstrel_ht_stats_csv_open(struct inode *inode, struct file *file)
+{
+ struct minstrel_ht_sta_priv *msp = inode->i_private;
+ struct minstrel_ht_sta *mi = &msp->ht;
+ struct minstrel_debugfs_info *ms;
+ unsigned int i;
+ int ret;
+ char *p;
+
+ if (!msp->is_ht) {
+ inode->i_private = &msp->legacy;
+ ret = minstrel_stats_csv_open(inode, file);
+ inode->i_private = msp;
+ return ret;
+ }
+
+ ms = kmalloc(32768, GFP_KERNEL);
+
+ if (!ms)
+ return -ENOMEM;
+
+ file->private_data = ms;
+
+ p = ms->buf;
+
+ p = minstrel_ht_stats_csv_dump(mi, MINSTREL_CCK_GROUP, p);
+ for (i = 0; i < MINSTREL_CCK_GROUP; i++)
+ p = minstrel_ht_stats_csv_dump(mi, i, p);
+ for (i++; i < ARRAY_SIZE(mi->groups); i++)
+ p = minstrel_ht_stats_csv_dump(mi, i, p);
+
+ ms->len = p - ms->buf;
+ WARN_ON(ms->len + sizeof(*ms) > 32768);
+
+ return nonseekable_open(inode, file);
+}
+
+static const struct file_operations minstrel_ht_stat_csv_fops = {
+ .owner = THIS_MODULE,
+ .open = minstrel_ht_stats_csv_open,
+ .read = minstrel_stats_read,
+ .release = minstrel_stats_release,
+ .llseek = no_llseek,
+};
+
void
minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir)
{
@@ -148,6 +307,8 @@ minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir)
msp->dbg_stats = debugfs_create_file("rc_stats", S_IRUGO, dir, msp,
&minstrel_ht_stat_fops);
+ msp->dbg_stats_csv = debugfs_create_file("rc_stats_csv", S_IRUGO,
+ dir, msp, &minstrel_ht_stat_csv_fops);
}
void
@@ -156,4 +317,5 @@ minstrel_ht_remove_sta_debugfs(void *priv, void *priv_sta)
struct minstrel_ht_sta_priv *msp = priv_sta;
debugfs_remove(msp->dbg_stats);
+ debugfs_remove(msp->dbg_stats_csv);
}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 1eb730bf8752..260eed45b6d2 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1185,6 +1185,7 @@ static void sta_ps_start(struct sta_info *sta)
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
struct ps_data *ps;
+ int tid;
if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
@@ -1198,6 +1199,18 @@ static void sta_ps_start(struct sta_info *sta)
drv_sta_notify(local, sdata, STA_NOTIFY_SLEEP, &sta->sta);
ps_dbg(sdata, "STA %pM aid %d enters power save mode\n",
sta->sta.addr, sta->sta.aid);
+
+ if (!sta->sta.txq[0])
+ return;
+
+ for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) {
+ struct txq_info *txqi = to_txq_info(sta->sta.txq[tid]);
+
+ if (!skb_queue_len(&txqi->queue))
+ set_bit(tid, &sta->txq_buffered_tids);
+ else
+ clear_bit(tid, &sta->txq_buffered_tids);
+ }
}
static void sta_ps_end(struct sta_info *sta)
@@ -1913,8 +1926,7 @@ static int ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
/* Drop unencrypted frames if key is set. */
if (unlikely(!ieee80211_has_protected(fc) &&
!ieee80211_is_nullfunc(fc) &&
- ieee80211_is_data(fc) &&
- (rx->key || rx->sdata->drop_unencrypted)))
+ ieee80211_is_data(fc) && rx->key))
return -EACCES;
return 0;
@@ -2044,6 +2056,9 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
struct sta_info *dsta;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += rx->skb->len;
+
skb = rx->skb;
xmit_skb = NULL;
@@ -2174,8 +2189,6 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
dev_kfree_skb(rx->skb);
continue;
}
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += rx->skb->len;
ieee80211_deliver_skb(rx);
}
@@ -2401,9 +2414,6 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
rx->skb->dev = dev;
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += rx->skb->len;
-
if (local->ps_sdata && local->hw.conf.dynamic_ps_timeout > 0 &&
!is_multicast_ether_addr(
((struct ethhdr *)rx->skb->data)->h_dest) &&
@@ -3129,6 +3139,12 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,
goto rxh_next; \
} while (0);
+ /* Lock here to avoid hitting all of the data used in the RX
+ * path (e.g. key data, station data, ...) concurrently when
+ * a frame is released from the reorder buffer due to timeout
+ * from the timer, potentially concurrently with RX from the
+ * driver.
+ */
spin_lock_bh(&rx->local->rx_path_lock);
while ((skb = __skb_dequeue(frames))) {
@@ -3421,7 +3437,8 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
__le16 fc;
struct ieee80211_rx_data rx;
struct ieee80211_sub_if_data *prev;
- struct sta_info *sta, *tmp, *prev_sta;
+ struct sta_info *sta, *prev_sta;
+ struct rhash_head *tmp;
int err = 0;
fc = ((struct ieee80211_hdr *)skb->data)->frame_control;
@@ -3456,9 +3473,13 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
ieee80211_scan_rx(local, skb);
if (ieee80211_is_data(fc)) {
+ const struct bucket_table *tbl;
+
prev_sta = NULL;
- for_each_sta_info(local, hdr->addr2, sta, tmp) {
+ tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash);
+
+ for_each_sta_info(local, tbl, hdr->addr2, sta, tmp) {
if (!prev_sta) {
prev_sta = sta;
continue;
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 05f0d711b6d8..7bb6a9383f58 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -928,11 +928,12 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
const u8 *ssid, u8 ssid_len,
- struct ieee80211_channel *chan,
+ struct ieee80211_channel **channels,
+ unsigned int n_channels,
enum nl80211_bss_scan_width scan_width)
{
struct ieee80211_local *local = sdata->local;
- int ret = -EBUSY;
+ int ret = -EBUSY, i, n_ch = 0;
enum ieee80211_band band;
mutex_lock(&local->mtx);
@@ -942,9 +943,8 @@ int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
goto unlock;
/* fill internal scan request */
- if (!chan) {
- int i, max_n;
- int n_ch = 0;
+ if (!channels) {
+ int max_n;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
if (!local->hw.wiphy->bands[band])
@@ -969,12 +969,19 @@ int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
local->int_scan_req->n_channels = n_ch;
} else {
- if (WARN_ON_ONCE(chan->flags & (IEEE80211_CHAN_NO_IR |
- IEEE80211_CHAN_DISABLED)))
+ for (i = 0; i < n_channels; i++) {
+ if (channels[i]->flags & (IEEE80211_CHAN_NO_IR |
+ IEEE80211_CHAN_DISABLED))
+ continue;
+
+ local->int_scan_req->channels[n_ch] = channels[i];
+ n_ch++;
+ }
+
+ if (WARN_ON_ONCE(n_ch == 0))
goto unlock;
- local->int_scan_req->channels[0] = chan;
- local->int_scan_req->n_channels = 1;
+ local->int_scan_req->n_channels = n_ch;
}
local->int_scan_req->ssids = &local->scan_ssid;
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 00ca8dcc2bcf..12971b71d0fa 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -64,32 +64,20 @@
* freed before they are done using it.
*/
+static const struct rhashtable_params sta_rht_params = {
+ .nelem_hint = 3, /* start small */
+ .head_offset = offsetof(struct sta_info, hash_node),
+ .key_offset = offsetof(struct sta_info, sta.addr),
+ .key_len = ETH_ALEN,
+ .hashfn = sta_addr_hash,
+};
+
/* Caller must hold local->sta_mtx */
static int sta_info_hash_del(struct ieee80211_local *local,
struct sta_info *sta)
{
- struct sta_info *s;
-
- s = rcu_dereference_protected(local->sta_hash[STA_HASH(sta->sta.addr)],
- lockdep_is_held(&local->sta_mtx));
- if (!s)
- return -ENOENT;
- if (s == sta) {
- rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)],
- s->hnext);
- return 0;
- }
-
- while (rcu_access_pointer(s->hnext) &&
- rcu_access_pointer(s->hnext) != sta)
- s = rcu_dereference_protected(s->hnext,
- lockdep_is_held(&local->sta_mtx));
- if (rcu_access_pointer(s->hnext)) {
- rcu_assign_pointer(s->hnext, sta->hnext);
- return 0;
- }
-
- return -ENOENT;
+ return rhashtable_remove_fast(&local->sta_hash, &sta->hash_node,
+ sta_rht_params);
}
static void __cleanup_single_sta(struct sta_info *sta)
@@ -118,6 +106,16 @@ static void __cleanup_single_sta(struct sta_info *sta)
atomic_dec(&ps->num_sta_ps);
}
+ if (sta->sta.txq[0]) {
+ for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
+ struct txq_info *txqi = to_txq_info(sta->sta.txq[i]);
+ int n = skb_queue_len(&txqi->queue);
+
+ ieee80211_purge_tx_queue(&local->hw, &txqi->queue);
+ atomic_sub(n, &sdata->txqs_len[txqi->txq.ac]);
+ }
+ }
+
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]);
ieee80211_purge_tx_queue(&local->hw, &sta->ps_tx_buf[ac]);
@@ -159,18 +157,8 @@ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,
const u8 *addr)
{
struct ieee80211_local *local = sdata->local;
- struct sta_info *sta;
- sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)],
- lockdep_is_held(&local->sta_mtx));
- while (sta) {
- if (sta->sdata == sdata &&
- ether_addr_equal(sta->sta.addr, addr))
- break;
- sta = rcu_dereference_check(sta->hnext,
- lockdep_is_held(&local->sta_mtx));
- }
- return sta;
+ return rhashtable_lookup_fast(&local->sta_hash, addr, sta_rht_params);
}
/*
@@ -182,18 +170,24 @@ struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
+ struct rhash_head *tmp;
+ const struct bucket_table *tbl;
- sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)],
- lockdep_is_held(&local->sta_mtx));
- while (sta) {
- if ((sta->sdata == sdata ||
- (sta->sdata->bss && sta->sdata->bss == sdata->bss)) &&
- ether_addr_equal(sta->sta.addr, addr))
- break;
- sta = rcu_dereference_check(sta->hnext,
- lockdep_is_held(&local->sta_mtx));
+ rcu_read_lock();
+ tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash);
+
+ for_each_sta_info(local, tbl, addr, sta, tmp) {
+ if (sta->sdata == sdata ||
+ (sta->sdata->bss && sta->sdata->bss == sdata->bss)) {
+ rcu_read_unlock();
+ /* this is safe as the caller must already hold
+ * another rcu read section or the mutex
+ */
+ return sta;
+ }
}
- return sta;
+ rcu_read_unlock();
+ return NULL;
}
struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata,
@@ -229,19 +223,13 @@ struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata,
*/
void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
{
- int i;
-
if (sta->rate_ctrl)
rate_control_free_sta(sta);
- if (sta->tx_lat) {
- for (i = 0; i < IEEE80211_NUM_TIDS; i++)
- kfree(sta->tx_lat[i].bins);
- kfree(sta->tx_lat);
- }
-
sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr);
+ if (sta->sta.txq[0])
+ kfree(to_txq_info(sta->sta.txq[0]));
kfree(rcu_dereference_raw(sta->sta.rates));
kfree(sta);
}
@@ -250,9 +238,8 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
static void sta_info_hash_add(struct ieee80211_local *local,
struct sta_info *sta)
{
- lockdep_assert_held(&local->sta_mtx);
- sta->hnext = local->sta_hash[STA_HASH(sta->sta.addr)];
- rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta);
+ rhashtable_insert_fast(&local->sta_hash, &sta->hash_node,
+ sta_rht_params);
}
static void sta_deliver_ps_frames(struct work_struct *wk)
@@ -293,44 +280,15 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
const u8 *addr, gfp_t gfp)
{
struct ieee80211_local *local = sdata->local;
+ struct ieee80211_hw *hw = &local->hw;
struct sta_info *sta;
struct timespec uptime;
- struct ieee80211_tx_latency_bin_ranges *tx_latency;
int i;
- sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp);
+ sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp);
if (!sta)
return NULL;
- rcu_read_lock();
- tx_latency = rcu_dereference(local->tx_latency);
- /* init stations Tx latency statistics && TID bins */
- if (tx_latency) {
- sta->tx_lat = kzalloc(IEEE80211_NUM_TIDS *
- sizeof(struct ieee80211_tx_latency_stat),
- GFP_ATOMIC);
- if (!sta->tx_lat) {
- rcu_read_unlock();
- goto free;
- }
-
- if (tx_latency->n_ranges) {
- for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
- /* size of bins is size of the ranges +1 */
- sta->tx_lat[i].bin_count =
- tx_latency->n_ranges + 1;
- sta->tx_lat[i].bins =
- kcalloc(sta->tx_lat[i].bin_count,
- sizeof(u32), GFP_ATOMIC);
- if (!sta->tx_lat[i].bins) {
- rcu_read_unlock();
- goto free;
- }
- }
- }
- }
- rcu_read_unlock();
-
spin_lock_init(&sta->lock);
spin_lock_init(&sta->ps_lock);
INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames);
@@ -359,8 +317,24 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++)
ewma_init(&sta->chain_signal_avg[i], 1024, 8);
+ if (local->ops->wake_tx_queue) {
+ void *txq_data;
+ int size = sizeof(struct txq_info) +
+ ALIGN(hw->txq_data_size, sizeof(void *));
+
+ txq_data = kcalloc(ARRAY_SIZE(sta->sta.txq), size, gfp);
+ if (!txq_data)
+ goto free;
+
+ for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
+ struct txq_info *txq = txq_data + i * size;
+
+ ieee80211_init_tx_queue(sdata, sta, txq, i);
+ }
+ }
+
if (sta_prepare_rate_control(local, sta, gfp))
- goto free;
+ goto free_txq;
for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
/*
@@ -382,7 +356,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
if (sdata->vif.type == NL80211_IFTYPE_AP ||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
struct ieee80211_supported_band *sband =
- local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)];
+ hw->wiphy->bands[ieee80211_get_sdata_band(sdata)];
u8 smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >>
IEEE80211_HT_CAP_SM_PS_SHIFT;
/*
@@ -405,14 +379,13 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
}
sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
+
return sta;
+free_txq:
+ if (sta->sta.txq[0])
+ kfree(to_txq_info(sta->sta.txq[0]));
free:
- if (sta->tx_lat) {
- for (i = 0; i < IEEE80211_NUM_TIDS; i++)
- kfree(sta->tx_lat[i].bins);
- kfree(sta->tx_lat);
- }
kfree(sta);
return NULL;
}
@@ -684,6 +657,8 @@ static void __sta_info_recalc_tim(struct sta_info *sta, bool ignore_pending)
indicate_tim |=
sta->driver_buffered_tids & tids;
+ indicate_tim |=
+ sta->txq_buffered_tids & tids;
}
done:
@@ -992,19 +967,32 @@ static void sta_info_cleanup(unsigned long data)
round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL));
}
-void sta_info_init(struct ieee80211_local *local)
+u32 sta_addr_hash(const void *key, u32 length, u32 seed)
+{
+ return jhash(key, ETH_ALEN, seed);
+}
+
+int sta_info_init(struct ieee80211_local *local)
{
+ int err;
+
+ err = rhashtable_init(&local->sta_hash, &sta_rht_params);
+ if (err)
+ return err;
+
spin_lock_init(&local->tim_lock);
mutex_init(&local->sta_mtx);
INIT_LIST_HEAD(&local->sta_list);
setup_timer(&local->sta_cleanup, sta_info_cleanup,
(unsigned long)local);
+ return 0;
}
void sta_info_stop(struct ieee80211_local *local)
{
del_timer_sync(&local->sta_cleanup);
+ rhashtable_destroy(&local->sta_hash);
}
@@ -1068,16 +1056,21 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
}
struct ieee80211_sta *ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw,
- const u8 *addr,
- const u8 *localaddr)
+ const u8 *addr,
+ const u8 *localaddr)
{
- struct sta_info *sta, *nxt;
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct sta_info *sta;
+ struct rhash_head *tmp;
+ const struct bucket_table *tbl;
+
+ tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash);
/*
* Just return a random station if localaddr is NULL
* ... first in list.
*/
- for_each_sta_info(hw_to_local(hw), addr, sta, nxt) {
+ for_each_sta_info(local, tbl, addr, sta, tmp) {
if (localaddr &&
!ether_addr_equal(sta->sdata->vif.addr, localaddr))
continue;
@@ -1115,7 +1108,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
struct sk_buff_head pending;
- int filtered = 0, buffered = 0, ac;
+ int filtered = 0, buffered = 0, ac, i;
unsigned long flags;
struct ps_data *ps;
@@ -1134,10 +1127,22 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
BUILD_BUG_ON(BITS_TO_LONGS(IEEE80211_NUM_TIDS) > 1);
sta->driver_buffered_tids = 0;
+ sta->txq_buffered_tids = 0;
if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS))
drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta);
+ if (sta->sta.txq[0]) {
+ for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
+ struct txq_info *txqi = to_txq_info(sta->sta.txq[i]);
+
+ if (!skb_queue_len(&txqi->queue))
+ continue;
+
+ drv_wake_tx_queue(local, txqi);
+ }
+ }
+
skb_queue_head_init(&pending);
/* sync with ieee80211_tx_h_unicast_ps_buf */
@@ -1275,7 +1280,7 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
}
info->band = chanctx_conf->def.chan->band;
- ieee80211_xmit(sdata, skb);
+ ieee80211_xmit(sdata, sta, skb);
rcu_read_unlock();
}
@@ -1319,8 +1324,10 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
/* if we already have frames from software, then we can't also
* release from hardware queues
*/
- if (skb_queue_empty(&frames))
+ if (skb_queue_empty(&frames)) {
driver_release_tids |= sta->driver_buffered_tids & tids;
+ driver_release_tids |= sta->txq_buffered_tids & tids;
+ }
if (driver_release_tids) {
/* If the driver has data on more than one TID then
@@ -1491,6 +1498,9 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
sta_info_recalc_tim(sta);
} else {
+ unsigned long tids = sta->txq_buffered_tids & driver_release_tids;
+ int tid;
+
/*
* We need to release a frame that is buffered somewhere in the
* driver ... it'll have to handle that.
@@ -1510,8 +1520,22 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
* that the TID(s) became empty before returning here from the
* release function.
* Either way, however, when the driver tells us that the TID(s)
- * became empty we'll do the TIM recalculation.
+ * became empty or we find that a txq became empty, we'll do the
+ * TIM recalculation.
*/
+
+ if (!sta->sta.txq[0])
+ return;
+
+ for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) {
+ struct txq_info *txqi = to_txq_info(sta->sta.txq[tid]);
+
+ if (!(tids & BIT(tid)) || skb_queue_len(&txqi->queue))
+ continue;
+
+ sta_info_recalc_tim(sta);
+ break;
+ }
}
}
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index fb0fc1302a58..5c164fb3f6c5 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -16,6 +16,7 @@
#include <linux/workqueue.h>
#include <linux/average.h>
#include <linux/etherdevice.h>
+#include <linux/rhashtable.h>
#include "key.h"
/**
@@ -236,25 +237,6 @@ struct sta_ampdu_mlme {
u8 dialog_token_allocator;
};
-/*
- * struct ieee80211_tx_latency_stat - Tx latency statistics
- *
- * Measures TX latency and jitter for a station per TID.
- *
- * @max: worst case latency
- * @sum: sum of all latencies
- * @counter: amount of Tx frames sent from interface
- * @bins: each bin counts how many frames transmitted within a certain
- * latency range. when disabled it is NULL.
- * @bin_count: amount of bins.
- */
-struct ieee80211_tx_latency_stat {
- u32 max;
- u32 sum;
- u32 counter;
- u32 *bins;
- u32 bin_count;
-};
/* Value to indicate no TID reservation */
#define IEEE80211_TID_UNRESERVED 0xff
@@ -267,7 +249,7 @@ struct ieee80211_tx_latency_stat {
*
* @list: global linked list entry
* @free_list: list entry for keeping track of stations to free
- * @hnext: hash table linked list pointer
+ * @hash_node: hash node for rhashtable
* @local: pointer to the global information
* @sdata: virtual interface this station belongs to
* @ptk: peer keys negotiated with this station, if any
@@ -295,6 +277,7 @@ struct ieee80211_tx_latency_stat {
* entered power saving state, these are also delivered to
* the station when it leaves powersave or polls for frames
* @driver_buffered_tids: bitmap of TIDs the driver has data buffered on
+ * @txq_buffered_tids: bitmap of TIDs that mac80211 has txq data buffered on
* @rx_packets: Number of MSDUs received from this STA
* @rx_bytes: Number of bytes received from this STA
* @last_rx: time (in jiffies) when last frame was received from this STA
@@ -316,7 +299,6 @@ struct ieee80211_tx_latency_stat {
* @tid_seq: per-TID sequence numbers for sending to this STA
* @ampdu_mlme: A-MPDU state machine state
* @timer_to_tid: identity mapping to ID timers
- * @tx_lat: Tx latency statistics
* @llid: Local link ID
* @plid: Peer link ID
* @reason: Cancel reason on PLINK_HOLDING state
@@ -361,7 +343,7 @@ struct sta_info {
/* General information, mostly static */
struct list_head list, free_list;
struct rcu_head rcu_head;
- struct sta_info __rcu *hnext;
+ struct rhash_head hash_node;
struct ieee80211_local *local;
struct ieee80211_sub_if_data *sdata;
struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS];
@@ -390,6 +372,7 @@ struct sta_info {
struct sk_buff_head ps_tx_buf[IEEE80211_NUM_ACS];
struct sk_buff_head tx_filtered[IEEE80211_NUM_ACS];
unsigned long driver_buffered_tids;
+ unsigned long txq_buffered_tids;
/* Updated from RX path only, no locking requirements */
unsigned long rx_packets;
@@ -437,8 +420,6 @@ struct sta_info {
struct sta_ampdu_mlme ampdu_mlme;
u8 timer_to_tid[IEEE80211_NUM_TIDS];
- struct ieee80211_tx_latency_stat *tx_lat;
-
#ifdef CONFIG_MAC80211_MESH
/*
* Mesh peer link attributes
@@ -559,10 +540,6 @@ rcu_dereference_protected_tid_tx(struct sta_info *sta, int tid)
lockdep_is_held(&sta->ampdu_mlme.mtx));
}
-#define STA_HASH_SIZE 256
-#define STA_HASH(sta) (sta[5])
-
-
/* Maximum number of frames to buffer per power saving station per AC */
#define STA_MAX_TX_BUFFER 64
@@ -583,26 +560,15 @@ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata,
const u8 *addr);
-static inline
-void for_each_sta_info_type_check(struct ieee80211_local *local,
- const u8 *addr,
- struct sta_info *sta,
- struct sta_info *nxt)
-{
-}
+u32 sta_addr_hash(const void *key, u32 length, u32 seed);
+
+#define _sta_bucket_idx(_tbl, _a) \
+ rht_bucket_index(_tbl, sta_addr_hash(_a, ETH_ALEN, (_tbl)->hash_rnd))
-#define for_each_sta_info(local, _addr, _sta, nxt) \
- for ( /* initialise loop */ \
- _sta = rcu_dereference(local->sta_hash[STA_HASH(_addr)]),\
- nxt = _sta ? rcu_dereference(_sta->hnext) : NULL; \
- /* typecheck */ \
- for_each_sta_info_type_check(local, (_addr), _sta, nxt),\
- /* continue condition */ \
- _sta; \
- /* advance loop */ \
- _sta = nxt, \
- nxt = _sta ? rcu_dereference(_sta->hnext) : NULL \
- ) \
+#define for_each_sta_info(local, tbl, _addr, _sta, _tmp) \
+ rht_for_each_entry_rcu(_sta, _tmp, tbl, \
+ _sta_bucket_idx(tbl, _addr), \
+ hash_node) \
/* compare address and run code only if it matches */ \
if (ether_addr_equal(_sta->sta.addr, (_addr)))
@@ -639,7 +605,7 @@ int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata,
void sta_info_recalc_tim(struct sta_info *sta);
-void sta_info_init(struct ieee80211_local *local);
+int sta_info_init(struct ieee80211_local *local);
void sta_info_stop(struct ieee80211_local *local);
/**
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index e679b7c9b160..005fdbe39a8b 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -12,7 +12,6 @@
#include <linux/export.h>
#include <linux/etherdevice.h>
-#include <linux/time.h>
#include <net/mac80211.h>
#include <asm/unaligned.h>
#include "ieee80211_i.h"
@@ -515,73 +514,6 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
}
/*
- * Measure Tx frame completion and removal time for Tx latency statistics
- * calculation. A single Tx frame latency should be measured from when it
- * is entering the Kernel until we receive Tx complete confirmation indication
- * and remove the skb.
- */
-static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local,
- struct sk_buff *skb,
- struct sta_info *sta,
- struct ieee80211_hdr *hdr)
-{
- u32 msrmnt;
- u16 tid;
- u8 *qc;
- int i, bin_range_count;
- u32 *bin_ranges;
- __le16 fc;
- struct ieee80211_tx_latency_stat *tx_lat;
- struct ieee80211_tx_latency_bin_ranges *tx_latency;
- ktime_t skb_arv = skb->tstamp;
-
- tx_latency = rcu_dereference(local->tx_latency);
-
- /* assert Tx latency stats are enabled & frame arrived when enabled */
- if (!tx_latency || !ktime_to_ns(skb_arv))
- return;
-
- fc = hdr->frame_control;
-
- if (!ieee80211_is_data(fc)) /* make sure it is a data frame */
- return;
-
- /* get frame tid */
- if (ieee80211_is_data_qos(hdr->frame_control)) {
- qc = ieee80211_get_qos_ctl(hdr);
- tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
- } else {
- tid = 0;
- }
-
- tx_lat = &sta->tx_lat[tid];
-
- /* Calculate the latency */
- msrmnt = ktime_to_ms(ktime_sub(ktime_get(), skb_arv));
-
- if (tx_lat->max < msrmnt) /* update stats */
- tx_lat->max = msrmnt;
- tx_lat->counter++;
- tx_lat->sum += msrmnt;
-
- if (!tx_lat->bins) /* bins not activated */
- return;
-
- /* count how many Tx frames transmitted with the appropriate latency */
- bin_range_count = tx_latency->n_ranges;
- bin_ranges = tx_latency->ranges;
-
- for (i = 0; i < bin_range_count; i++) {
- if (msrmnt <= bin_ranges[i]) {
- tx_lat->bins[i]++;
- break;
- }
- }
- if (i == bin_range_count) /* msrmnt is bigger than the biggest range */
- tx_lat->bins[i]++;
-}
-
-/*
* Use a static threshold for now, best value to be determined
* by testing ...
* Should it depend on:
@@ -722,7 +654,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
struct ieee80211_supported_band *sband;
struct ieee80211_sub_if_data *sdata;
struct net_device *prev_dev = NULL;
- struct sta_info *sta, *tmp;
+ struct sta_info *sta;
+ struct rhash_head *tmp;
int retry_count;
int rates_idx;
bool send_to_cooked;
@@ -731,6 +664,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
int rtap_len;
int shift = 0;
int tid = IEEE80211_NUM_TIDS;
+ const struct bucket_table *tbl;
rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
@@ -739,7 +673,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
sband = local->hw.wiphy->bands[info->band];
fc = hdr->frame_control;
- for_each_sta_info(local, hdr->addr1, sta, tmp) {
+ tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash);
+
+ for_each_sta_info(local, tbl, hdr->addr1, sta, tmp) {
/* skip wrong virtual interface */
if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr))
continue;
@@ -853,12 +789,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
if (acked)
sta->last_ack_signal = info->status.ack_signal;
-
- /*
- * Measure frame removal for tx latency
- * statistics calculation
- */
- ieee80211_tx_latency_end_msrmnt(local, skb, sta, hdr);
}
rcu_read_unlock();
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index c9f9752217ac..fff0d864adfa 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -136,6 +136,24 @@ ieee80211_tdls_add_supp_channels(struct ieee80211_sub_if_data *sdata,
*pos = 2 * subband_cnt;
}
+static void ieee80211_tdls_add_oper_classes(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb)
+{
+ u8 *pos;
+ u8 op_class;
+
+ if (!ieee80211_chandef_to_operating_class(&sdata->vif.bss_conf.chandef,
+ &op_class))
+ return;
+
+ pos = skb_put(skb, 4);
+ *pos++ = WLAN_EID_SUPPORTED_REGULATORY_CLASSES;
+ *pos++ = 2; /* len */
+
+ *pos++ = op_class;
+ *pos++ = op_class; /* give current operating class as alternate too */
+}
+
static void ieee80211_tdls_add_bss_coex_ie(struct sk_buff *skb)
{
u8 *pos = (void *)skb_put(skb, 3);
@@ -193,6 +211,17 @@ static void ieee80211_tdls_add_link_ie(struct ieee80211_sub_if_data *sdata,
memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN);
}
+static void
+ieee80211_tdls_add_aid(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
+{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ u8 *pos = (void *)skb_put(skb, 4);
+
+ *pos++ = WLAN_EID_AID;
+ *pos++ = 2; /* len */
+ put_unaligned_le16(ifmgd->aid, pos);
+}
+
/* translate numbering in the WMM parameter IE to the mac80211 notation */
static enum ieee80211_ac_numbers ieee80211_ac_from_wmm(int ac)
{
@@ -271,21 +300,11 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
struct ieee80211_sta_ht_cap ht_cap;
+ struct ieee80211_sta_vht_cap vht_cap;
struct sta_info *sta = NULL;
size_t offset = 0, noffset;
u8 *pos;
- rcu_read_lock();
-
- /* we should have the peer STA if we're already responding */
- if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
- sta = sta_info_get(sdata, peer);
- if (WARN_ON_ONCE(!sta)) {
- rcu_read_unlock();
- return;
- }
- }
-
ieee80211_add_srates_ie(sdata, skb, false, band);
ieee80211_add_ext_srates_ie(sdata, skb, false, band);
ieee80211_tdls_add_supp_channels(sdata, skb);
@@ -338,6 +357,19 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
offset = noffset;
}
+ rcu_read_lock();
+
+ /* we should have the peer STA if we're already responding */
+ if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
+ sta = sta_info_get(sdata, peer);
+ if (WARN_ON_ONCE(!sta)) {
+ rcu_read_unlock();
+ return;
+ }
+ }
+
+ ieee80211_tdls_add_oper_classes(sdata, skb);
+
/*
* with TDLS we can switch channels, and HT-caps are not necessarily
* the same on all bands. The specification limits the setup to a
@@ -346,7 +378,9 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
sband = local->hw.wiphy->bands[band];
memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
- if (action_code == WLAN_TDLS_SETUP_REQUEST && ht_cap.ht_supported) {
+ if ((action_code == WLAN_TDLS_SETUP_REQUEST ||
+ action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) &&
+ ht_cap.ht_supported) {
ieee80211_apply_htcap_overrides(sdata, &ht_cap);
/* disable SMPS in TDLS initiator */
@@ -368,12 +402,63 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
}
- rcu_read_unlock();
-
if (ht_cap.ht_supported &&
(ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
ieee80211_tdls_add_bss_coex_ie(skb);
+ ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+
+ /* add any custom IEs that go before VHT capabilities */
+ if (extra_ies_len) {
+ static const u8 before_vht_cap[] = {
+ WLAN_EID_SUPP_RATES,
+ WLAN_EID_COUNTRY,
+ WLAN_EID_EXT_SUPP_RATES,
+ WLAN_EID_SUPPORTED_CHANNELS,
+ WLAN_EID_RSN,
+ WLAN_EID_EXT_CAPABILITY,
+ WLAN_EID_QOS_CAPA,
+ WLAN_EID_FAST_BSS_TRANSITION,
+ WLAN_EID_TIMEOUT_INTERVAL,
+ WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
+ WLAN_EID_MULTI_BAND,
+ };
+ noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
+ before_vht_cap,
+ ARRAY_SIZE(before_vht_cap),
+ offset);
+ pos = skb_put(skb, noffset - offset);
+ memcpy(pos, extra_ies + offset, noffset - offset);
+ offset = noffset;
+ }
+
+ /* build the VHT-cap similarly to the HT-cap */
+ memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap));
+ if ((action_code == WLAN_TDLS_SETUP_REQUEST ||
+ action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) &&
+ vht_cap.vht_supported) {
+ ieee80211_apply_vhtcap_overrides(sdata, &vht_cap);
+
+ /* the AID is present only when VHT is implemented */
+ if (action_code == WLAN_TDLS_SETUP_REQUEST)
+ ieee80211_tdls_add_aid(sdata, skb);
+
+ pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
+ ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap);
+ } else if (action_code == WLAN_TDLS_SETUP_RESPONSE &&
+ vht_cap.vht_supported && sta->sta.vht_cap.vht_supported) {
+ /* the peer caps are already intersected with our own */
+ memcpy(&vht_cap, &sta->sta.vht_cap, sizeof(vht_cap));
+
+ /* the AID is present only when VHT is implemented */
+ ieee80211_tdls_add_aid(sdata, skb);
+
+ pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
+ ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap);
+ }
+
+ rcu_read_unlock();
+
/* add any remaining IEs */
if (extra_ies_len) {
noffset = extra_ies_len;
@@ -381,7 +466,6 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
memcpy(pos, extra_ies + offset, noffset - offset);
}
- ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
}
static void
@@ -394,6 +478,7 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
size_t offset = 0, noffset;
struct sta_info *sta, *ap_sta;
+ enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
u8 *pos;
rcu_read_lock();
@@ -453,6 +538,21 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
}
}
+ ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+
+ /* only include VHT-operation if not on the 2.4GHz band */
+ if (band != IEEE80211_BAND_2GHZ && !ap_sta->sta.vht_cap.vht_supported &&
+ sta->sta.vht_cap.vht_supported) {
+ struct ieee80211_chanctx_conf *chanctx_conf =
+ rcu_dereference(sdata->vif.chanctx_conf);
+ if (!WARN_ON(!chanctx_conf)) {
+ pos = skb_put(skb, 2 +
+ sizeof(struct ieee80211_vht_operation));
+ ieee80211_ie_build_vht_oper(pos, &sta->sta.vht_cap,
+ &chanctx_conf->def);
+ }
+ }
+
rcu_read_unlock();
/* add any remaining IEs */
@@ -461,8 +561,6 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
pos = skb_put(skb, noffset - offset);
memcpy(pos, extra_ies + offset, noffset - offset);
}
-
- ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
}
static void
@@ -708,8 +806,12 @@ ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata,
26 + /* max(WMM-info, WMM-param) */
2 + max(sizeof(struct ieee80211_ht_cap),
sizeof(struct ieee80211_ht_operation)) +
+ 2 + max(sizeof(struct ieee80211_vht_cap),
+ sizeof(struct ieee80211_vht_operation)) +
50 + /* supported channels */
3 + /* 40/20 BSS coex */
+ 4 + /* AID */
+ 4 + /* oper classes */
extra_ies_len +
sizeof(struct ieee80211_tdls_lnkie));
if (!skb)
@@ -907,7 +1009,7 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer) &&
!ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) {
ret = -EBUSY;
- goto exit;
+ goto out_unlock;
}
/*
@@ -922,27 +1024,34 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
if (!sta_info_get(sdata, peer)) {
rcu_read_unlock();
ret = -ENOLINK;
- goto exit;
+ goto out_unlock;
}
rcu_read_unlock();
}
ieee80211_flush_queues(local, sdata, false);
+ memcpy(sdata->u.mgd.tdls_peer, peer, ETH_ALEN);
+ mutex_unlock(&local->mtx);
+ /* we cannot take the mutex while preparing the setup packet */
ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
dialog_token, status_code,
peer_capability, initiator,
extra_ies, extra_ies_len, 0,
NULL);
- if (ret < 0)
- goto exit;
+ if (ret < 0) {
+ mutex_lock(&local->mtx);
+ eth_zero_addr(sdata->u.mgd.tdls_peer);
+ mutex_unlock(&local->mtx);
+ return ret;
+ }
- memcpy(sdata->u.mgd.tdls_peer, peer, ETH_ALEN);
ieee80211_queue_delayed_work(&sdata->local->hw,
&sdata->u.mgd.tdls_peer_del_work,
TDLS_PEER_SETUP_TIMEOUT);
+ return 0;
-exit:
+out_unlock:
mutex_unlock(&local->mtx);
return ret;
}
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 755a5388dbca..4c2e7690226a 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -1256,28 +1256,28 @@ TRACE_EVENT(drv_set_rekey_data,
LOCAL_PR_ARG, VIF_PR_ARG)
);
-TRACE_EVENT(drv_rssi_callback,
+TRACE_EVENT(drv_event_callback,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
- enum ieee80211_rssi_event rssi_event),
+ const struct ieee80211_event *_event),
- TP_ARGS(local, sdata, rssi_event),
+ TP_ARGS(local, sdata, _event),
TP_STRUCT__entry(
LOCAL_ENTRY
VIF_ENTRY
- __field(u32, rssi_event)
+ __field(u32, type)
),
TP_fast_assign(
LOCAL_ASSIGN;
VIF_ASSIGN;
- __entry->rssi_event = rssi_event;
+ __entry->type = _event->type;
),
TP_printk(
- LOCAL_PR_FMT VIF_PR_FMT " rssi_event:%d",
- LOCAL_PR_ARG, VIF_PR_ARG, __entry->rssi_event
+ LOCAL_PR_FMT VIF_PR_FMT " event:%d",
+ LOCAL_PR_ARG, VIF_PR_ARG, __entry->type
)
);
@@ -2312,6 +2312,37 @@ TRACE_EVENT(drv_tdls_recv_channel_switch,
)
);
+TRACE_EVENT(drv_wake_tx_queue,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct txq_info *txq),
+
+ TP_ARGS(local, sdata, txq),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ STA_ENTRY
+ __field(u8, ac)
+ __field(u8, tid)
+ ),
+
+ TP_fast_assign(
+ struct ieee80211_sta *sta = txq->txq.sta;
+
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ STA_ASSIGN;
+ __entry->ac = txq->txq.ac;
+ __entry->tid = txq->txq.tid;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " ac:%d tid:%d",
+ LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->ac, __entry->tid
+ )
+);
+
#endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
#undef TRACE_INCLUDE_PATH
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 07bd8db00af8..667111ee6a20 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -20,7 +20,6 @@
#include <linux/bitmap.h>
#include <linux/rcupdate.h>
#include <linux/export.h>
-#include <linux/time.h>
#include <net/net_namespace.h>
#include <net/ieee80211_radiotap.h>
#include <net/cfg80211.h>
@@ -595,23 +594,8 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
else if (!is_multicast_ether_addr(hdr->addr1) &&
(key = rcu_dereference(tx->sdata->default_unicast_key)))
tx->key = key;
- else if (info->flags & IEEE80211_TX_CTL_INJECTED)
- tx->key = NULL;
- else if (!tx->sdata->drop_unencrypted)
- tx->key = NULL;
- else if (tx->skb->protocol == tx->sdata->control_port_protocol)
- tx->key = NULL;
- else if (ieee80211_is_robust_mgmt_frame(tx->skb) &&
- !(ieee80211_is_action(hdr->frame_control) &&
- tx->sta && test_sta_flag(tx->sta, WLAN_STA_MFP)))
- tx->key = NULL;
- else if (ieee80211_is_mgmt(hdr->frame_control) &&
- !ieee80211_is_robust_mgmt_frame(tx->skb))
+ else
tx->key = NULL;
- else {
- I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted);
- return TX_DROP;
- }
if (tx->key) {
bool skip_hw = false;
@@ -783,12 +767,22 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
return TX_CONTINUE;
}
+static __le16 ieee80211_tx_next_seq(struct sta_info *sta, int tid)
+{
+ u16 *seq = &sta->tid_seq[tid];
+ __le16 ret = cpu_to_le16(*seq);
+
+ /* Increase the sequence number. */
+ *seq = (*seq + 0x10) & IEEE80211_SCTL_SEQ;
+
+ return ret;
+}
+
static ieee80211_tx_result debug_noinline
ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
- u16 *seq;
u8 *qc;
int tid;
@@ -839,13 +833,10 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
qc = ieee80211_get_qos_ctl(hdr);
tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
- seq = &tx->sta->tid_seq[tid];
tx->sta->tx_msdu[tid]++;
- hdr->seq_ctrl = cpu_to_le16(*seq);
-
- /* Increase the sequence number. */
- *seq = (*seq + 0x10) & IEEE80211_SCTL_SEQ;
+ if (!tx->sta->sta.txq[0])
+ hdr->seq_ctrl = ieee80211_tx_next_seq(tx->sta, tid);
return TX_CONTINUE;
}
@@ -1086,7 +1077,7 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx,
* nothing -- this aggregation session is being started
* but that might still fail with the driver
*/
- } else {
+ } else if (!tx->sta->sta.txq[tid]) {
spin_lock(&tx->sta->lock);
/*
* Need to re-check now, because we may get here
@@ -1137,11 +1128,13 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx,
/*
* initialises @tx
+ * pass %NULL for the station if unknown, a valid pointer if known
+ * or an ERR_PTR() if the station is known not to exist
*/
static ieee80211_tx_result
ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
struct ieee80211_tx_data *tx,
- struct sk_buff *skb)
+ struct sta_info *sta, struct sk_buff *skb)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_hdr *hdr;
@@ -1164,17 +1157,22 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
hdr = (struct ieee80211_hdr *) skb->data;
- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
- tx->sta = rcu_dereference(sdata->u.vlan.sta);
- if (!tx->sta && sdata->dev->ieee80211_ptr->use_4addr)
- return TX_DROP;
- } else if (info->flags & (IEEE80211_TX_CTL_INJECTED |
- IEEE80211_TX_INTFL_NL80211_FRAME_TX) ||
- tx->sdata->control_port_protocol == tx->skb->protocol) {
- tx->sta = sta_info_get_bss(sdata, hdr->addr1);
+ if (likely(sta)) {
+ if (!IS_ERR(sta))
+ tx->sta = sta;
+ } else {
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+ tx->sta = rcu_dereference(sdata->u.vlan.sta);
+ if (!tx->sta && sdata->wdev.use_4addr)
+ return TX_DROP;
+ } else if (info->flags & (IEEE80211_TX_INTFL_NL80211_FRAME_TX |
+ IEEE80211_TX_CTL_INJECTED) ||
+ tx->sdata->control_port_protocol == tx->skb->protocol) {
+ tx->sta = sta_info_get_bss(sdata, hdr->addr1);
+ }
+ if (!tx->sta && !is_multicast_ether_addr(hdr->addr1))
+ tx->sta = sta_info_get(sdata, hdr->addr1);
}
- if (!tx->sta)
- tx->sta = sta_info_get(sdata, hdr->addr1);
if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) &&
!ieee80211_is_qos_nullfunc(hdr->frame_control) &&
@@ -1220,13 +1218,102 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
return TX_CONTINUE;
}
+static void ieee80211_drv_tx(struct ieee80211_local *local,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *pubsta,
+ struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_tx_control control = {
+ .sta = pubsta,
+ };
+ struct ieee80211_txq *txq = NULL;
+ struct txq_info *txqi;
+ u8 ac;
+
+ if (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE)
+ goto tx_normal;
+
+ if (!ieee80211_is_data(hdr->frame_control))
+ goto tx_normal;
+
+ if (pubsta) {
+ u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
+
+ txq = pubsta->txq[tid];
+ } else if (vif) {
+ txq = vif->txq;
+ }
+
+ if (!txq)
+ goto tx_normal;
+
+ ac = txq->ac;
+ txqi = to_txq_info(txq);
+ atomic_inc(&sdata->txqs_len[ac]);
+ if (atomic_read(&sdata->txqs_len[ac]) >= local->hw.txq_ac_max_pending)
+ netif_stop_subqueue(sdata->dev, ac);
+
+ skb_queue_tail(&txqi->queue, skb);
+ drv_wake_tx_queue(local, txqi);
+
+ return;
+
+tx_normal:
+ drv_tx(local, &control, skb);
+}
+
+struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->vif);
+ struct txq_info *txqi = container_of(txq, struct txq_info, txq);
+ struct ieee80211_hdr *hdr;
+ struct sk_buff *skb = NULL;
+ u8 ac = txq->ac;
+
+ spin_lock_bh(&txqi->queue.lock);
+
+ if (test_bit(IEEE80211_TXQ_STOP, &txqi->flags))
+ goto out;
+
+ skb = __skb_dequeue(&txqi->queue);
+ if (!skb)
+ goto out;
+
+ atomic_dec(&sdata->txqs_len[ac]);
+ if (__netif_subqueue_stopped(sdata->dev, ac))
+ ieee80211_propagate_queue_wake(local, sdata->vif.hw_queue[ac]);
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+ if (txq->sta && ieee80211_is_data_qos(hdr->frame_control)) {
+ struct sta_info *sta = container_of(txq->sta, struct sta_info,
+ sta);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ hdr->seq_ctrl = ieee80211_tx_next_seq(sta, txq->tid);
+ if (test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags))
+ info->flags |= IEEE80211_TX_CTL_AMPDU;
+ else
+ info->flags &= ~IEEE80211_TX_CTL_AMPDU;
+ }
+
+out:
+ spin_unlock_bh(&txqi->queue.lock);
+
+ return skb;
+}
+EXPORT_SYMBOL(ieee80211_tx_dequeue);
+
static bool ieee80211_tx_frags(struct ieee80211_local *local,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct sk_buff_head *skbs,
bool txpending)
{
- struct ieee80211_tx_control control;
struct sk_buff *skb, *tmp;
unsigned long flags;
@@ -1284,10 +1371,9 @@ static bool ieee80211_tx_frags(struct ieee80211_local *local,
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
info->control.vif = vif;
- control.sta = sta;
__skb_unlink(skb, skbs);
- drv_tx(local, &control, skb);
+ ieee80211_drv_tx(local, vif, sta, skb);
}
return true;
@@ -1422,8 +1508,9 @@ bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_tx_data tx;
+ struct sk_buff *skb2;
- if (ieee80211_tx_prepare(sdata, &tx, skb) == TX_DROP)
+ if (ieee80211_tx_prepare(sdata, &tx, NULL, skb) == TX_DROP)
return false;
info->band = band;
@@ -1440,6 +1527,14 @@ bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
*sta = NULL;
}
+ /* this function isn't suitable for fragmented data frames */
+ skb2 = __skb_dequeue(&tx.skbs);
+ if (WARN_ON(skb2 != skb || !skb_queue_empty(&tx.skbs))) {
+ ieee80211_free_txskb(hw, skb2);
+ ieee80211_purge_tx_queue(hw, &tx.skbs);
+ return false;
+ }
+
return true;
}
EXPORT_SYMBOL(ieee80211_tx_prepare_skb);
@@ -1448,7 +1543,8 @@ EXPORT_SYMBOL(ieee80211_tx_prepare_skb);
* Returns false if the frame couldn't be transmitted but was queued instead.
*/
static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, bool txpending)
+ struct sta_info *sta, struct sk_buff *skb,
+ bool txpending)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_data tx;
@@ -1464,7 +1560,7 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
/* initialises tx */
led_len = skb->len;
- res_prepare = ieee80211_tx_prepare(sdata, &tx, skb);
+ res_prepare = ieee80211_tx_prepare(sdata, &tx, sta, skb);
if (unlikely(res_prepare == TX_DROP)) {
ieee80211_free_txskb(&local->hw, skb);
@@ -1520,7 +1616,8 @@ static int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata,
return 0;
}
-void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
+void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta, struct sk_buff *skb)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -1555,7 +1652,7 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
}
ieee80211_set_qos_hdr(sdata, skb);
- ieee80211_tx(sdata, skb, false);
+ ieee80211_tx(sdata, sta, skb, false);
}
static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
@@ -1776,7 +1873,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
goto fail_rcu;
info->band = chandef->chan->band;
- ieee80211_xmit(sdata, skb);
+ ieee80211_xmit(sdata, NULL, skb);
rcu_read_unlock();
return NETDEV_TX_OK;
@@ -1788,21 +1885,89 @@ fail:
return NETDEV_TX_OK; /* meaning, we dealt with the skb */
}
-/*
- * Measure Tx frame arrival time for Tx latency statistics calculation
- * A single Tx frame latency should be measured from when it is entering the
- * Kernel until we receive Tx complete confirmation indication and the skb is
- * freed.
- */
-static void ieee80211_tx_latency_start_msrmnt(struct ieee80211_local *local,
- struct sk_buff *skb)
+static inline bool ieee80211_is_tdls_setup(struct sk_buff *skb)
{
- struct ieee80211_tx_latency_bin_ranges *tx_latency;
+ u16 ethertype = (skb->data[12] << 8) | skb->data[13];
- tx_latency = rcu_dereference(local->tx_latency);
- if (!tx_latency)
- return;
- skb->tstamp = ktime_get();
+ return ethertype == ETH_P_TDLS &&
+ skb->len > 14 &&
+ skb->data[14] == WLAN_TDLS_SNAP_RFTYPE;
+}
+
+static int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb,
+ struct sta_info **sta_out)
+{
+ struct sta_info *sta;
+
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_AP_VLAN:
+ sta = rcu_dereference(sdata->u.vlan.sta);
+ if (sta) {
+ *sta_out = sta;
+ return 0;
+ } else if (sdata->wdev.use_4addr) {
+ return -ENOLINK;
+ }
+ /* fall through */
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_OCB:
+ case NL80211_IFTYPE_ADHOC:
+ if (is_multicast_ether_addr(skb->data)) {
+ *sta_out = ERR_PTR(-ENOENT);
+ return 0;
+ }
+ sta = sta_info_get_bss(sdata, skb->data);
+ break;
+ case NL80211_IFTYPE_WDS:
+ sta = sta_info_get(sdata, sdata->u.wds.remote_addr);
+ break;
+#ifdef CONFIG_MAC80211_MESH
+ case NL80211_IFTYPE_MESH_POINT:
+ /* determined much later */
+ *sta_out = NULL;
+ return 0;
+#endif
+ case NL80211_IFTYPE_STATION:
+ if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) {
+ sta = sta_info_get(sdata, skb->data);
+ if (sta) {
+ bool tdls_peer, tdls_auth;
+
+ tdls_peer = test_sta_flag(sta,
+ WLAN_STA_TDLS_PEER);
+ tdls_auth = test_sta_flag(sta,
+ WLAN_STA_TDLS_PEER_AUTH);
+
+ if (tdls_peer && tdls_auth) {
+ *sta_out = sta;
+ return 0;
+ }
+
+ /*
+ * TDLS link during setup - throw out frames to
+ * peer. Allow TDLS-setup frames to unauthorized
+ * peers for the special case of a link teardown
+ * after a TDLS sta is removed due to being
+ * unreachable.
+ */
+ if (tdls_peer && !tdls_auth &&
+ !ieee80211_is_tdls_setup(skb))
+ return -EINVAL;
+ }
+
+ }
+
+ sta = sta_info_get(sdata, sdata->u.mgd.bssid);
+ if (!sta)
+ return -ENOLINK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *sta_out = sta ?: ERR_PTR(-ENOENT);
+ return 0;
}
/**
@@ -1824,7 +1989,8 @@ static void ieee80211_tx_latency_start_msrmnt(struct ieee80211_local *local,
* Returns: the (possibly reallocated) skb or an ERR_PTR() code
*/
static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, u32 info_flags)
+ struct sk_buff *skb, u32 info_flags,
+ struct sta_info *sta)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_info *info;
@@ -1837,9 +2003,8 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
const u8 *encaps_data;
int encaps_len, skip_header_bytes;
int nh_pos, h_pos;
- struct sta_info *sta = NULL;
- bool wme_sta = false, authorized = false, tdls_auth = false;
- bool tdls_peer = false, tdls_setup_frame = false;
+ bool wme_sta = false, authorized = false;
+ bool tdls_peer;
bool multicast;
u16 info_id = 0;
struct ieee80211_chanctx_conf *chanctx_conf;
@@ -1847,6 +2012,9 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
enum ieee80211_band band;
int ret;
+ if (IS_ERR(sta))
+ sta = NULL;
+
/* convert Ethernet header to proper 802.11 header (based on
* operation mode) */
ethertype = (skb->data[12] << 8) | skb->data[13];
@@ -1854,8 +2022,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP_VLAN:
- sta = rcu_dereference(sdata->u.vlan.sta);
- if (sta) {
+ if (sdata->wdev.use_4addr) {
fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
/* RA TA DA SA */
memcpy(hdr.addr1, sta->sta.addr, ETH_ALEN);
@@ -1874,7 +2041,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
goto free;
}
band = chanctx_conf->def.chan->band;
- if (sta)
+ if (sdata->wdev.use_4addr)
break;
/* fall through */
case NL80211_IFTYPE_AP:
@@ -1978,38 +2145,10 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
break;
#endif
case NL80211_IFTYPE_STATION:
- if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) {
- sta = sta_info_get(sdata, skb->data);
- if (sta) {
- authorized = test_sta_flag(sta,
- WLAN_STA_AUTHORIZED);
- wme_sta = sta->sta.wme;
- tdls_peer = test_sta_flag(sta,
- WLAN_STA_TDLS_PEER);
- tdls_auth = test_sta_flag(sta,
- WLAN_STA_TDLS_PEER_AUTH);
- }
-
- if (tdls_peer)
- tdls_setup_frame =
- ethertype == ETH_P_TDLS &&
- skb->len > 14 &&
- skb->data[14] == WLAN_TDLS_SNAP_RFTYPE;
- }
-
- /*
- * TDLS link during setup - throw out frames to peer. We allow
- * TDLS-setup frames to unauthorized peers for the special case
- * of a link teardown after a TDLS sta is removed due to being
- * unreachable.
- */
- if (tdls_peer && !tdls_auth && !tdls_setup_frame) {
- ret = -EINVAL;
- goto free;
- }
+ /* we already did checks when looking up the RA STA */
+ tdls_peer = test_sta_flag(sta, WLAN_STA_TDLS_PEER);
- /* send direct packets to authorized TDLS peers */
- if (tdls_peer && tdls_auth) {
+ if (tdls_peer) {
/* DA SA BSSID */
memcpy(hdr.addr1, skb->data, ETH_ALEN);
memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
@@ -2071,26 +2210,19 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
goto free;
}
- /*
- * There's no need to try to look up the destination
- * if it is a multicast address (which can only happen
- * in AP mode)
- */
multicast = is_multicast_ether_addr(hdr.addr1);
- if (!multicast) {
- sta = sta_info_get(sdata, hdr.addr1);
- if (sta) {
- authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
- wme_sta = sta->sta.wme;
- }
- }
- /* For mesh, the use of the QoS header is mandatory */
- if (ieee80211_vif_is_mesh(&sdata->vif))
+ /* sta is always NULL for mesh */
+ if (sta) {
+ authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
+ wme_sta = sta->sta.wme;
+ } else if (ieee80211_vif_is_mesh(&sdata->vif)) {
+ /* For mesh, the use of the QoS header is mandatory */
wme_sta = true;
+ }
- /* receiver and we are QoS enabled, use a QoS type frame */
- if (wme_sta && local->hw.queues >= IEEE80211_NUM_ACS) {
+ /* receiver does QoS (which also means we do) use it */
+ if (wme_sta) {
fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
hdrlen += 2;
}
@@ -2260,7 +2392,7 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
u32 info_flags)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_local *local = sdata->local;
+ struct sta_info *sta;
if (unlikely(skb->len < ETH_HLEN)) {
kfree_skb(skb);
@@ -2269,10 +2401,12 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
rcu_read_lock();
- /* Measure frame arrival for Tx latency statistics calculation */
- ieee80211_tx_latency_start_msrmnt(local, skb);
+ if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
+ kfree_skb(skb);
+ goto out;
+ }
- skb = ieee80211_build_hdr(sdata, skb, info_flags);
+ skb = ieee80211_build_hdr(sdata, skb, info_flags, sta);
if (IS_ERR(skb))
goto out;
@@ -2280,7 +2414,7 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
dev->stats.tx_bytes += skb->len;
dev->trans_start = jiffies;
- ieee80211_xmit(sdata, skb);
+ ieee80211_xmit(sdata, sta, skb);
out:
rcu_read_unlock();
}
@@ -2308,10 +2442,17 @@ ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata,
.local = sdata->local,
.sdata = sdata,
};
+ struct sta_info *sta;
rcu_read_lock();
- skb = ieee80211_build_hdr(sdata, skb, info_flags);
+ if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
+ kfree_skb(skb);
+ skb = ERR_PTR(-EINVAL);
+ goto out;
+ }
+
+ skb = ieee80211_build_hdr(sdata, skb, info_flags, sta);
if (IS_ERR(skb))
goto out;
@@ -2369,7 +2510,7 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,
return true;
}
info->band = chanctx_conf->def.chan->band;
- result = ieee80211_tx(sdata, skb, true);
+ result = ieee80211_tx(sdata, NULL, skb, true);
} else {
struct sk_buff_head skbs;
@@ -3107,7 +3248,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
if (sdata->vif.type == NL80211_IFTYPE_AP)
sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev);
- if (!ieee80211_tx_prepare(sdata, &tx, skb))
+ if (!ieee80211_tx_prepare(sdata, &tx, NULL, skb))
break;
dev_kfree_skb_any(skb);
}
@@ -3239,6 +3380,6 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
*/
local_bh_disable();
IEEE80211_SKB_CB(skb)->band = band;
- ieee80211_xmit(sdata, skb);
+ ieee80211_xmit(sdata, NULL, skb);
local_bh_enable();
}
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 747bdcf72e92..79412f16b61d 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -308,6 +308,11 @@ void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue)
for (ac = 0; ac < n_acs; ac++) {
int ac_queue = sdata->vif.hw_queue[ac];
+ if (local->ops->wake_tx_queue &&
+ (atomic_read(&sdata->txqs_len[ac]) >
+ local->hw.txq_ac_max_pending))
+ continue;
+
if (ac_queue == queue ||
(sdata->vif.cab_queue == queue &&
local->queue_stop_reasons[ac_queue] == 0 &&
@@ -625,13 +630,14 @@ void ieee80211_wake_vif_queues(struct ieee80211_local *local,
reason, true);
}
-static void __iterate_active_interfaces(struct ieee80211_local *local,
- u32 iter_flags,
- void (*iterator)(void *data, u8 *mac,
- struct ieee80211_vif *vif),
- void *data)
+static void __iterate_interfaces(struct ieee80211_local *local,
+ u32 iter_flags,
+ void (*iterator)(void *data, u8 *mac,
+ struct ieee80211_vif *vif),
+ void *data)
{
struct ieee80211_sub_if_data *sdata;
+ bool active_only = iter_flags & IEEE80211_IFACE_ITER_ACTIVE;
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
switch (sdata->vif.type) {
@@ -645,9 +651,9 @@ static void __iterate_active_interfaces(struct ieee80211_local *local,
break;
}
if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
- !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
+ active_only && !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
continue;
- if (ieee80211_sdata_running(sdata))
+ if (ieee80211_sdata_running(sdata) || !active_only)
iterator(data, sdata->vif.addr,
&sdata->vif);
}
@@ -656,12 +662,12 @@ static void __iterate_active_interfaces(struct ieee80211_local *local,
lockdep_is_held(&local->iflist_mtx) ||
lockdep_rtnl_is_held());
if (sdata &&
- (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
+ (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || !active_only ||
sdata->flags & IEEE80211_SDATA_IN_DRIVER))
iterator(data, sdata->vif.addr, &sdata->vif);
}
-void ieee80211_iterate_active_interfaces(
+void ieee80211_iterate_interfaces(
struct ieee80211_hw *hw, u32 iter_flags,
void (*iterator)(void *data, u8 *mac,
struct ieee80211_vif *vif),
@@ -670,10 +676,10 @@ void ieee80211_iterate_active_interfaces(
struct ieee80211_local *local = hw_to_local(hw);
mutex_lock(&local->iflist_mtx);
- __iterate_active_interfaces(local, iter_flags, iterator, data);
+ __iterate_interfaces(local, iter_flags, iterator, data);
mutex_unlock(&local->iflist_mtx);
}
-EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);
+EXPORT_SYMBOL_GPL(ieee80211_iterate_interfaces);
void ieee80211_iterate_active_interfaces_atomic(
struct ieee80211_hw *hw, u32 iter_flags,
@@ -684,7 +690,8 @@ void ieee80211_iterate_active_interfaces_atomic(
struct ieee80211_local *local = hw_to_local(hw);
rcu_read_lock();
- __iterate_active_interfaces(local, iter_flags, iterator, data);
+ __iterate_interfaces(local, iter_flags | IEEE80211_IFACE_ITER_ACTIVE,
+ iterator, data);
rcu_read_unlock();
}
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
@@ -699,7 +706,8 @@ void ieee80211_iterate_active_interfaces_rtnl(
ASSERT_RTNL();
- __iterate_active_interfaces(local, iter_flags, iterator, data);
+ __iterate_interfaces(local, iter_flags | IEEE80211_IFACE_ITER_ACTIVE,
+ iterator, data);
}
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl);
@@ -742,6 +750,18 @@ struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev)
}
EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif);
+struct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+ if (!ieee80211_sdata_running(sdata) ||
+ !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
+ return NULL;
+
+ return &sdata->wdev;
+}
+EXPORT_SYMBOL_GPL(ieee80211_vif_to_wdev);
+
/*
* Nothing should have been stuffed into the workqueue during
* the suspend->resume cycle. Since we can't check each caller
@@ -1811,8 +1831,25 @@ int ieee80211_reconfig(struct ieee80211_local *local)
list_for_each_entry(sdata, &local->interfaces, list) {
if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
sdata->vif.type != NL80211_IFTYPE_MONITOR &&
- ieee80211_sdata_running(sdata))
+ ieee80211_sdata_running(sdata)) {
res = drv_add_interface(local, sdata);
+ if (WARN_ON(res))
+ break;
+ }
+ }
+
+ /* If adding any of the interfaces failed above, roll back and
+ * report failure.
+ */
+ if (res) {
+ list_for_each_entry_continue_reverse(sdata, &local->interfaces,
+ list)
+ if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
+ sdata->vif.type != NL80211_IFTYPE_MONITOR &&
+ ieee80211_sdata_running(sdata))
+ drv_remove_interface(local, sdata);
+ ieee80211_handle_reconfig_failure(local);
+ return res;
}
/* add channel contexts */
@@ -2157,46 +2194,6 @@ void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata)
mutex_unlock(&local->chanctx_mtx);
}
-static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
-{
- int i;
-
- for (i = 0; i < n_ids; i++)
- if (ids[i] == id)
- return true;
- return false;
-}
-
-size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
- const u8 *ids, int n_ids,
- const u8 *after_ric, int n_after_ric,
- size_t offset)
-{
- size_t pos = offset;
-
- while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) {
- if (ies[pos] == WLAN_EID_RIC_DATA && n_after_ric) {
- pos += 2 + ies[pos + 1];
-
- while (pos < ielen &&
- !ieee80211_id_in_list(after_ric, n_after_ric,
- ies[pos]))
- pos += 2 + ies[pos + 1];
- } else {
- pos += 2 + ies[pos + 1];
- }
- }
-
- return pos;
-}
-
-size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
- const u8 *ids, int n_ids, size_t offset)
-{
- return ieee80211_ie_split_ric(ies, ielen, ids, n_ids, NULL, 0, offset);
-}
-EXPORT_SYMBOL(ieee80211_ie_split);
-
size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset)
{
size_t pos = offset;
@@ -2344,6 +2341,41 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
return pos + sizeof(struct ieee80211_ht_operation);
}
+u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
+ const struct cfg80211_chan_def *chandef)
+{
+ struct ieee80211_vht_operation *vht_oper;
+
+ *pos++ = WLAN_EID_VHT_OPERATION;
+ *pos++ = sizeof(struct ieee80211_vht_operation);
+ vht_oper = (struct ieee80211_vht_operation *)pos;
+ vht_oper->center_freq_seg1_idx = ieee80211_frequency_to_channel(
+ chandef->center_freq1);
+ if (chandef->center_freq2)
+ vht_oper->center_freq_seg2_idx =
+ ieee80211_frequency_to_channel(chandef->center_freq2);
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_160:
+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_160MHZ;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80P80MHZ;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
+ break;
+ default:
+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT;
+ break;
+ }
+
+ /* don't require special VHT peer rates */
+ vht_oper->basic_mcs_set = cpu_to_le16(0xffff);
+
+ return pos + sizeof(struct ieee80211_vht_operation);
+}
+
void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
const struct ieee80211_ht_operation *ht_oper,
struct cfg80211_chan_def *chandef)
@@ -2373,6 +2405,39 @@ void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
cfg80211_chandef_create(chandef, control_chan, channel_type);
}
+void ieee80211_vht_oper_to_chandef(struct ieee80211_channel *control_chan,
+ const struct ieee80211_vht_operation *oper,
+ struct cfg80211_chan_def *chandef)
+{
+ if (!oper)
+ return;
+
+ chandef->chan = control_chan;
+
+ switch (oper->chan_width) {
+ case IEEE80211_VHT_CHANWIDTH_USE_HT:
+ break;
+ case IEEE80211_VHT_CHANWIDTH_80MHZ:
+ chandef->width = NL80211_CHAN_WIDTH_80;
+ break;
+ case IEEE80211_VHT_CHANWIDTH_160MHZ:
+ chandef->width = NL80211_CHAN_WIDTH_160;
+ break;
+ case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
+ chandef->width = NL80211_CHAN_WIDTH_80P80;
+ break;
+ default:
+ break;
+ }
+
+ chandef->center_freq1 =
+ ieee80211_channel_to_frequency(oper->center_freq_seg1_idx,
+ control_chan->band);
+ chandef->center_freq2 =
+ ieee80211_channel_to_frequency(oper->center_freq_seg2_idx,
+ control_chan->band);
+}
+
int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
const struct ieee80211_supported_band *sband,
const u8 *srates, int srates_len, u32 *rates)
@@ -3252,3 +3317,20 @@ u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo)
return buf;
}
+
+void ieee80211_init_tx_queue(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta,
+ struct txq_info *txqi, int tid)
+{
+ skb_queue_head_init(&txqi->queue);
+ txqi->txq.vif = &sdata->vif;
+
+ if (sta) {
+ txqi->txq.sta = &sta->sta;
+ sta->sta.txq[tid] = &txqi->txq;
+ txqi->txq.ac = ieee802_1d_to_ac[tid & 7];
+ } else {
+ sdata->vif.txq = &txqi->txq;
+ txqi->txq.ac = IEEE80211_AC_BE;
+ }
+}
diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c
index 85f9596da07b..80694d55db74 100644
--- a/net/mac80211/vht.c
+++ b/net/mac80211/vht.c
@@ -129,10 +129,6 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
if (!vht_cap_ie || !sband->vht_cap.vht_supported)
return;
- /* don't support VHT for TDLS peers for now */
- if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
- return;
-
/*
* A VHT STA must support 40 MHz, but if we verify that here
* then we break a few things - some APs (e.g. Netgear R6300v2
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index 75de6fac40d1..9d63d93c836e 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -780,9 +780,8 @@ ieee80211_crypto_cs_encrypt(struct ieee80211_tx_data *tx,
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_key *key = tx->key;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- const struct ieee80211_cipher_scheme *cs = key->sta->cipher_scheme;
int hdrlen;
- u8 *pos;
+ u8 *pos, iv_len = key->conf.iv_len;
if (info->control.hw_key &&
!(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) {
@@ -790,14 +789,14 @@ ieee80211_crypto_cs_encrypt(struct ieee80211_tx_data *tx,
return TX_CONTINUE;
}
- if (unlikely(skb_headroom(skb) < cs->hdr_len &&
- pskb_expand_head(skb, cs->hdr_len, 0, GFP_ATOMIC)))
+ if (unlikely(skb_headroom(skb) < iv_len &&
+ pskb_expand_head(skb, iv_len, 0, GFP_ATOMIC)))
return TX_DROP;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
- pos = skb_push(skb, cs->hdr_len);
- memmove(pos, pos + cs->hdr_len, hdrlen);
+ pos = skb_push(skb, iv_len);
+ memmove(pos, pos + iv_len, hdrlen);
return TX_CONTINUE;
}
@@ -1217,7 +1216,7 @@ ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx)
if (!info->control.hw_key)
return TX_DROP;
- if (tx->key->sta->cipher_scheme) {
+ if (tx->key->flags & KEY_FLAG_CIPHER_SCHEME) {
res = ieee80211_crypto_cs_encrypt(tx, skb);
if (res != TX_CONTINUE)
return res;
OpenPOWER on IntegriCloud