diff options
author | David S. Miller <davem@davemloft.net> | 2015-04-12 20:43:46 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-04-12 20:43:46 -0400 |
commit | 4e78eb0dbf867ccf206706ff2af34084f71a99bf (patch) | |
tree | f3193699ad846ec45abcf5bc95105cb5a460de8f /net | |
parent | 716723c2d2f0d5af9911966fb3cd8ccd33480d63 (diff) | |
parent | 6d00ec0514bd909e89ede59501342732dbef49fd (diff) |
Merge tag 'mac80211-next-for-davem-2015-04-10' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
Johannes Berg says:
====================
There isn't much left, but we have
* new mac80211 internal software queue to allow drivers to have
shorter hardware queues and pull on-demand
* use rhashtable for mac80211 station table
* minstrel rate control debug improvements and some refactoring
* fix noisy message about TX power reduction
* fix continuous message printing and activity if CRDA doesn't respond
* fix VHT-related capabilities with "iw connect" or "iwconfig ..."
* fix Kconfig for cfg80211 wireless extensions compatibility
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/agg-tx.c | 44 | ||||
-rw-r--r-- | net/mac80211/driver-ops.h | 12 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 28 | ||||
-rw-r--r-- | net/mac80211/iface.c | 23 | ||||
-rw-r--r-- | net/mac80211/main.c | 12 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 14 | ||||
-rw-r--r-- | net/mac80211/rc80211_minstrel.c | 125 | ||||
-rw-r--r-- | net/mac80211/rc80211_minstrel.h | 49 | ||||
-rw-r--r-- | net/mac80211/rc80211_minstrel_debugfs.c | 125 | ||||
-rw-r--r-- | net/mac80211/rc80211_minstrel_ht.c | 217 | ||||
-rw-r--r-- | net/mac80211/rc80211_minstrel_ht.h | 5 | ||||
-rw-r--r-- | net/mac80211/rc80211_minstrel_ht_debugfs.c | 216 | ||||
-rw-r--r-- | net/mac80211/rx.c | 22 | ||||
-rw-r--r-- | net/mac80211/sta_info.c | 186 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 40 | ||||
-rw-r--r-- | net/mac80211/status.c | 8 | ||||
-rw-r--r-- | net/mac80211/trace.h | 31 | ||||
-rw-r--r-- | net/mac80211/tx.c | 115 | ||||
-rw-r--r-- | net/mac80211/util.c | 62 | ||||
-rw-r--r-- | net/wireless/Kconfig | 2 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 2 | ||||
-rw-r--r-- | net/wireless/reg.c | 34 | ||||
-rw-r--r-- | net/wireless/reg.h | 9 | ||||
-rw-r--r-- | net/wireless/sme.c | 74 | ||||
-rw-r--r-- | net/wireless/util.c | 41 |
25 files changed, 1113 insertions, 383 deletions
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 20522492d8cc..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) | |||
188 | __release(agg_queue); | 188 | __release(agg_queue); |
189 | } | 189 | } |
190 | 190 | ||
191 | static void | ||
192 | ieee80211_agg_stop_txq(struct sta_info *sta, int tid) | ||
193 | { | ||
194 | struct ieee80211_txq *txq = sta->sta.txq[tid]; | ||
195 | struct txq_info *txqi; | ||
196 | |||
197 | if (!txq) | ||
198 | return; | ||
199 | |||
200 | txqi = to_txq_info(txq); | ||
201 | |||
202 | /* Lock here to protect against further seqno updates on dequeue */ | ||
203 | spin_lock_bh(&txqi->queue.lock); | ||
204 | set_bit(IEEE80211_TXQ_STOP, &txqi->flags); | ||
205 | spin_unlock_bh(&txqi->queue.lock); | ||
206 | } | ||
207 | |||
208 | static void | ||
209 | ieee80211_agg_start_txq(struct sta_info *sta, int tid, bool enable) | ||
210 | { | ||
211 | struct ieee80211_txq *txq = sta->sta.txq[tid]; | ||
212 | struct txq_info *txqi; | ||
213 | |||
214 | if (!txq) | ||
215 | return; | ||
216 | |||
217 | txqi = to_txq_info(txq); | ||
218 | |||
219 | if (enable) | ||
220 | set_bit(IEEE80211_TXQ_AMPDU, &txqi->flags); | ||
221 | else | ||
222 | clear_bit(IEEE80211_TXQ_AMPDU, &txqi->flags); | ||
223 | |||
224 | clear_bit(IEEE80211_TXQ_STOP, &txqi->flags); | ||
225 | drv_wake_tx_queue(sta->sdata->local, txqi); | ||
226 | } | ||
227 | |||
191 | /* | 228 | /* |
192 | * splice packets from the STA's pending to the local pending, | 229 | * splice packets from the STA's pending to the local pending, |
193 | * requires a call to ieee80211_agg_splice_finish later | 230 | * 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) | |||
247 | ieee80211_assign_tid_tx(sta, tid, NULL); | 284 | ieee80211_assign_tid_tx(sta, tid, NULL); |
248 | 285 | ||
249 | ieee80211_agg_splice_finish(sta->sdata, tid); | 286 | ieee80211_agg_splice_finish(sta->sdata, tid); |
287 | ieee80211_agg_start_txq(sta, tid, false); | ||
250 | 288 | ||
251 | kfree_rcu(tid_tx, rcu_head); | 289 | kfree_rcu(tid_tx, rcu_head); |
252 | } | 290 | } |
@@ -418,6 +456,8 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) | |||
418 | */ | 456 | */ |
419 | clear_bit(HT_AGG_STATE_WANT_START, &tid_tx->state); | 457 | clear_bit(HT_AGG_STATE_WANT_START, &tid_tx->state); |
420 | 458 | ||
459 | ieee80211_agg_stop_txq(sta, tid); | ||
460 | |||
421 | /* | 461 | /* |
422 | * Make sure no packets are being processed. This ensures that | 462 | * Make sure no packets are being processed. This ensures that |
423 | * we have a valid starting sequence number and that in-flight | 463 | * 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) | |||
440 | ieee80211_agg_splice_finish(sdata, tid); | 480 | ieee80211_agg_splice_finish(sdata, tid); |
441 | spin_unlock_bh(&sta->lock); | 481 | spin_unlock_bh(&sta->lock); |
442 | 482 | ||
483 | ieee80211_agg_start_txq(sta, tid, false); | ||
484 | |||
443 | kfree_rcu(tid_tx, rcu_head); | 485 | kfree_rcu(tid_tx, rcu_head); |
444 | return; | 486 | return; |
445 | } | 487 | } |
@@ -669,6 +711,8 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local, | |||
669 | ieee80211_agg_splice_finish(sta->sdata, tid); | 711 | ieee80211_agg_splice_finish(sta->sdata, tid); |
670 | 712 | ||
671 | spin_unlock_bh(&sta->lock); | 713 | spin_unlock_bh(&sta->lock); |
714 | |||
715 | ieee80211_agg_start_txq(sta, tid, true); | ||
672 | } | 716 | } |
673 | 717 | ||
674 | void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid) | 718 | void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid) |
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 0a39d3db951a..26e1ca8a474a 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h | |||
@@ -1367,4 +1367,16 @@ drv_tdls_recv_channel_switch(struct ieee80211_local *local, | |||
1367 | trace_drv_return_void(local); | 1367 | trace_drv_return_void(local); |
1368 | } | 1368 | } |
1369 | 1369 | ||
1370 | static inline void drv_wake_tx_queue(struct ieee80211_local *local, | ||
1371 | struct txq_info *txq) | ||
1372 | { | ||
1373 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->txq.vif); | ||
1374 | |||
1375 | if (!check_sdata_in_driver(sdata)) | ||
1376 | return; | ||
1377 | |||
1378 | trace_drv_wake_tx_queue(local, sdata, txq); | ||
1379 | local->ops->wake_tx_queue(&local->hw, &txq->txq); | ||
1380 | } | ||
1381 | |||
1370 | #endif /* __MAC80211_DRIVER_OPS */ | 1382 | #endif /* __MAC80211_DRIVER_OPS */ |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 487f5e2a9283..ab46ab4a7249 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <linux/etherdevice.h> | 26 | #include <linux/etherdevice.h> |
27 | #include <linux/leds.h> | 27 | #include <linux/leds.h> |
28 | #include <linux/idr.h> | 28 | #include <linux/idr.h> |
29 | #include <linux/rhashtable.h> | ||
29 | #include <net/ieee80211_radiotap.h> | 30 | #include <net/ieee80211_radiotap.h> |
30 | #include <net/cfg80211.h> | 31 | #include <net/cfg80211.h> |
31 | #include <net/mac80211.h> | 32 | #include <net/mac80211.h> |
@@ -810,6 +811,19 @@ struct mac80211_qos_map { | |||
810 | struct rcu_head rcu_head; | 811 | struct rcu_head rcu_head; |
811 | }; | 812 | }; |
812 | 813 | ||
814 | enum txq_info_flags { | ||
815 | IEEE80211_TXQ_STOP, | ||
816 | IEEE80211_TXQ_AMPDU, | ||
817 | }; | ||
818 | |||
819 | struct txq_info { | ||
820 | struct sk_buff_head queue; | ||
821 | unsigned long flags; | ||
822 | |||
823 | /* keep last! */ | ||
824 | struct ieee80211_txq txq; | ||
825 | }; | ||
826 | |||
813 | struct ieee80211_sub_if_data { | 827 | struct ieee80211_sub_if_data { |
814 | struct list_head list; | 828 | struct list_head list; |
815 | 829 | ||
@@ -852,6 +866,7 @@ struct ieee80211_sub_if_data { | |||
852 | bool control_port_no_encrypt; | 866 | bool control_port_no_encrypt; |
853 | int encrypt_headroom; | 867 | int encrypt_headroom; |
854 | 868 | ||
869 | atomic_t txqs_len[IEEE80211_NUM_ACS]; | ||
855 | struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS]; | 870 | struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS]; |
856 | struct mac80211_qos_map __rcu *qos_map; | 871 | struct mac80211_qos_map __rcu *qos_map; |
857 | 872 | ||
@@ -1187,7 +1202,7 @@ struct ieee80211_local { | |||
1187 | spinlock_t tim_lock; | 1202 | spinlock_t tim_lock; |
1188 | unsigned long num_sta; | 1203 | unsigned long num_sta; |
1189 | struct list_head sta_list; | 1204 | struct list_head sta_list; |
1190 | struct sta_info __rcu *sta_hash[STA_HASH_SIZE]; | 1205 | struct rhashtable sta_hash; |
1191 | struct timer_list sta_cleanup; | 1206 | struct timer_list sta_cleanup; |
1192 | int sta_generation; | 1207 | int sta_generation; |
1193 | 1208 | ||
@@ -1449,6 +1464,10 @@ static inline struct ieee80211_local *hw_to_local( | |||
1449 | return container_of(hw, struct ieee80211_local, hw); | 1464 | return container_of(hw, struct ieee80211_local, hw); |
1450 | } | 1465 | } |
1451 | 1466 | ||
1467 | static inline struct txq_info *to_txq_info(struct ieee80211_txq *txq) | ||
1468 | { | ||
1469 | return container_of(txq, struct txq_info, txq); | ||
1470 | } | ||
1452 | 1471 | ||
1453 | static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr) | 1472 | static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr) |
1454 | { | 1473 | { |
@@ -1905,6 +1924,9 @@ static inline bool ieee80211_can_run_worker(struct ieee80211_local *local) | |||
1905 | return true; | 1924 | return true; |
1906 | } | 1925 | } |
1907 | 1926 | ||
1927 | void ieee80211_init_tx_queue(struct ieee80211_sub_if_data *sdata, | ||
1928 | struct sta_info *sta, | ||
1929 | struct txq_info *txq, int tid); | ||
1908 | void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, | 1930 | void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, |
1909 | u16 transaction, u16 auth_alg, u16 status, | 1931 | u16 transaction, u16 auth_alg, u16 status, |
1910 | const u8 *extra, size_t extra_len, const u8 *bssid, | 1932 | const u8 *extra, size_t extra_len, const u8 *bssid, |
@@ -1943,10 +1965,6 @@ int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata, | |||
1943 | void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata); | 1965 | void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata); |
1944 | void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata); | 1966 | void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata); |
1945 | 1967 | ||
1946 | size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen, | ||
1947 | const u8 *ids, int n_ids, | ||
1948 | const u8 *after_ric, int n_after_ric, | ||
1949 | size_t offset); | ||
1950 | size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset); | 1968 | size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset); |
1951 | u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, | 1969 | u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, |
1952 | u16 cap); | 1970 | u16 cap); |
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index a0cd97fd0c49..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, | |||
969 | } | 969 | } |
970 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); | 970 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); |
971 | 971 | ||
972 | if (sdata->vif.txq) { | ||
973 | struct txq_info *txqi = to_txq_info(sdata->vif.txq); | ||
974 | |||
975 | ieee80211_purge_tx_queue(&local->hw, &txqi->queue); | ||
976 | atomic_set(&sdata->txqs_len[txqi->txq.ac], 0); | ||
977 | } | ||
978 | |||
972 | if (local->open_count == 0) | 979 | if (local->open_count == 0) |
973 | ieee80211_clear_tx_pending(local); | 980 | ieee80211_clear_tx_pending(local); |
974 | 981 | ||
@@ -1654,6 +1661,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, | |||
1654 | { | 1661 | { |
1655 | struct net_device *ndev = NULL; | 1662 | struct net_device *ndev = NULL; |
1656 | struct ieee80211_sub_if_data *sdata = NULL; | 1663 | struct ieee80211_sub_if_data *sdata = NULL; |
1664 | struct txq_info *txqi; | ||
1657 | int ret, i; | 1665 | int ret, i; |
1658 | int txqs = 1; | 1666 | int txqs = 1; |
1659 | 1667 | ||
@@ -1673,10 +1681,18 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, | |||
1673 | ieee80211_assign_perm_addr(local, wdev->address, type); | 1681 | ieee80211_assign_perm_addr(local, wdev->address, type); |
1674 | memcpy(sdata->vif.addr, wdev->address, ETH_ALEN); | 1682 | memcpy(sdata->vif.addr, wdev->address, ETH_ALEN); |
1675 | } else { | 1683 | } else { |
1684 | int size = ALIGN(sizeof(*sdata) + local->hw.vif_data_size, | ||
1685 | sizeof(void *)); | ||
1686 | int txq_size = 0; | ||
1687 | |||
1688 | if (local->ops->wake_tx_queue) | ||
1689 | txq_size += sizeof(struct txq_info) + | ||
1690 | local->hw.txq_data_size; | ||
1691 | |||
1676 | if (local->hw.queues >= IEEE80211_NUM_ACS) | 1692 | if (local->hw.queues >= IEEE80211_NUM_ACS) |
1677 | txqs = IEEE80211_NUM_ACS; | 1693 | txqs = IEEE80211_NUM_ACS; |
1678 | 1694 | ||
1679 | ndev = alloc_netdev_mqs(sizeof(*sdata) + local->hw.vif_data_size, | 1695 | ndev = alloc_netdev_mqs(size + txq_size, |
1680 | name, name_assign_type, | 1696 | name, name_assign_type, |
1681 | ieee80211_if_setup, txqs, 1); | 1697 | ieee80211_if_setup, txqs, 1); |
1682 | if (!ndev) | 1698 | if (!ndev) |
@@ -1711,6 +1727,11 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, | |||
1711 | memcpy(sdata->vif.addr, ndev->dev_addr, ETH_ALEN); | 1727 | memcpy(sdata->vif.addr, ndev->dev_addr, ETH_ALEN); |
1712 | memcpy(sdata->name, ndev->name, IFNAMSIZ); | 1728 | memcpy(sdata->name, ndev->name, IFNAMSIZ); |
1713 | 1729 | ||
1730 | if (txq_size) { | ||
1731 | txqi = netdev_priv(ndev) + size; | ||
1732 | ieee80211_init_tx_queue(sdata, NULL, txqi, 0); | ||
1733 | } | ||
1734 | |||
1714 | sdata->dev = ndev; | 1735 | sdata->dev = ndev; |
1715 | } | 1736 | } |
1716 | 1737 | ||
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 4977967c8b00..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, | |||
557 | 557 | ||
558 | local = wiphy_priv(wiphy); | 558 | local = wiphy_priv(wiphy); |
559 | 559 | ||
560 | if (sta_info_init(local)) | ||
561 | goto err_free; | ||
562 | |||
560 | local->hw.wiphy = wiphy; | 563 | local->hw.wiphy = wiphy; |
561 | 564 | ||
562 | local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN); | 565 | 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, | |||
629 | spin_lock_init(&local->ack_status_lock); | 632 | spin_lock_init(&local->ack_status_lock); |
630 | idr_init(&local->ack_status_frames); | 633 | idr_init(&local->ack_status_frames); |
631 | 634 | ||
632 | sta_info_init(local); | ||
633 | |||
634 | for (i = 0; i < IEEE80211_MAX_QUEUES; i++) { | 635 | for (i = 0; i < IEEE80211_MAX_QUEUES; i++) { |
635 | skb_queue_head_init(&local->pending[i]); | 636 | skb_queue_head_init(&local->pending[i]); |
636 | atomic_set(&local->agg_queue_stop[i], 0); | 637 | atomic_set(&local->agg_queue_stop[i], 0); |
@@ -650,6 +651,9 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, | |||
650 | ieee80211_roc_setup(local); | 651 | ieee80211_roc_setup(local); |
651 | 652 | ||
652 | return &local->hw; | 653 | return &local->hw; |
654 | err_free: | ||
655 | wiphy_free(wiphy); | ||
656 | return NULL; | ||
653 | } | 657 | } |
654 | EXPORT_SYMBOL(ieee80211_alloc_hw_nm); | 658 | EXPORT_SYMBOL(ieee80211_alloc_hw_nm); |
655 | 659 | ||
@@ -1035,6 +1039,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) | |||
1035 | 1039 | ||
1036 | local->dynamic_ps_forced_timeout = -1; | 1040 | local->dynamic_ps_forced_timeout = -1; |
1037 | 1041 | ||
1042 | if (!local->hw.txq_ac_max_pending) | ||
1043 | local->hw.txq_ac_max_pending = 64; | ||
1044 | |||
1038 | result = ieee80211_wep_init(local); | 1045 | result = ieee80211_wep_init(local); |
1039 | if (result < 0) | 1046 | if (result < 0) |
1040 | wiphy_debug(local->hw.wiphy, "Failed to initialize wep: %d\n", | 1047 | wiphy_debug(local->hw.wiphy, "Failed to initialize wep: %d\n", |
@@ -1173,7 +1180,6 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) | |||
1173 | 1180 | ||
1174 | destroy_workqueue(local->workqueue); | 1181 | destroy_workqueue(local->workqueue); |
1175 | wiphy_unregister(local->hw.wiphy); | 1182 | wiphy_unregister(local->hw.wiphy); |
1176 | sta_info_stop(local); | ||
1177 | ieee80211_wep_free(local); | 1183 | ieee80211_wep_free(local); |
1178 | ieee80211_led_exit(local); | 1184 | ieee80211_led_exit(local); |
1179 | kfree(local->int_scan_req); | 1185 | kfree(local->int_scan_req); |
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 00103f36dcbf..26053bf2faa8 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -1348,15 +1348,15 @@ static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, | |||
1348 | */ | 1348 | */ |
1349 | if (has_80211h_pwr && | 1349 | if (has_80211h_pwr && |
1350 | (!has_cisco_pwr || pwr_level_80211h <= pwr_level_cisco)) { | 1350 | (!has_cisco_pwr || pwr_level_80211h <= pwr_level_cisco)) { |
1351 | sdata_info(sdata, | 1351 | sdata_dbg(sdata, |
1352 | "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n", | 1352 | "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n", |
1353 | pwr_level_80211h, chan_pwr, pwr_reduction_80211h, | 1353 | pwr_level_80211h, chan_pwr, pwr_reduction_80211h, |
1354 | sdata->u.mgd.bssid); | 1354 | sdata->u.mgd.bssid); |
1355 | new_ap_level = pwr_level_80211h; | 1355 | new_ap_level = pwr_level_80211h; |
1356 | } else { /* has_cisco_pwr is always true here. */ | 1356 | } else { /* has_cisco_pwr is always true here. */ |
1357 | sdata_info(sdata, | 1357 | sdata_dbg(sdata, |
1358 | "Limiting TX power to %d dBm as advertised by %pM\n", | 1358 | "Limiting TX power to %d dBm as advertised by %pM\n", |
1359 | pwr_level_cisco, sdata->u.mgd.bssid); | 1359 | pwr_level_cisco, sdata->u.mgd.bssid); |
1360 | new_ap_level = pwr_level_cisco; | 1360 | new_ap_level = pwr_level_cisco; |
1361 | } | 1361 | } |
1362 | 1362 | ||
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) | |||
69 | return i; | 69 | return i; |
70 | } | 70 | } |
71 | 71 | ||
72 | /* return current EMWA throughput */ | ||
73 | int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_ewma) | ||
74 | { | ||
75 | int usecs; | ||
76 | |||
77 | usecs = mr->perfect_tx_time; | ||
78 | if (!usecs) | ||
79 | usecs = 1000000; | ||
80 | |||
81 | /* reset thr. below 10% success */ | ||
82 | if (mr->stats.prob_ewma < MINSTREL_FRAC(10, 100)) | ||
83 | return 0; | ||
84 | |||
85 | if (prob_ewma > MINSTREL_FRAC(90, 100)) | ||
86 | return MINSTREL_TRUNC(100000 * (MINSTREL_FRAC(90, 100) / usecs)); | ||
87 | else | ||
88 | return MINSTREL_TRUNC(100000 * (prob_ewma / usecs)); | ||
89 | } | ||
90 | |||
72 | /* find & sort topmost throughput rates */ | 91 | /* find & sort topmost throughput rates */ |
73 | static inline void | 92 | static inline void |
74 | minstrel_sort_best_tp_rates(struct minstrel_sta_info *mi, int i, u8 *tp_list) | 93 | minstrel_sort_best_tp_rates(struct minstrel_sta_info *mi, int i, u8 *tp_list) |
75 | { | 94 | { |
76 | int j = MAX_THR_RATES; | 95 | int j = MAX_THR_RATES; |
96 | struct minstrel_rate_stats *tmp_mrs = &mi->r[j - 1].stats; | ||
97 | struct minstrel_rate_stats *cur_mrs = &mi->r[i].stats; | ||
77 | 98 | ||
78 | while (j > 0 && mi->r[i].stats.cur_tp > mi->r[tp_list[j - 1]].stats.cur_tp) | 99 | while (j > 0 && (minstrel_get_tp_avg(&mi->r[i], cur_mrs->prob_ewma) > |
100 | minstrel_get_tp_avg(&mi->r[tp_list[j - 1]], tmp_mrs->prob_ewma))) { | ||
79 | j--; | 101 | j--; |
102 | tmp_mrs = &mi->r[tp_list[j - 1]].stats; | ||
103 | } | ||
104 | |||
80 | if (j < MAX_THR_RATES - 1) | 105 | if (j < MAX_THR_RATES - 1) |
81 | memmove(&tp_list[j + 1], &tp_list[j], MAX_THR_RATES - (j + 1)); | 106 | memmove(&tp_list[j + 1], &tp_list[j], MAX_THR_RATES - (j + 1)); |
82 | if (j < MAX_THR_RATES) | 107 | if (j < MAX_THR_RATES) |
@@ -127,13 +152,47 @@ minstrel_update_rates(struct minstrel_priv *mp, struct minstrel_sta_info *mi) | |||
127 | rate_control_set_rates(mp->hw, mi->sta, ratetbl); | 152 | rate_control_set_rates(mp->hw, mi->sta, ratetbl); |
128 | } | 153 | } |
129 | 154 | ||
155 | /* | ||
156 | * Recalculate statistics and counters of a given rate | ||
157 | */ | ||
158 | void | ||
159 | minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs) | ||
160 | { | ||
161 | if (unlikely(mrs->attempts > 0)) { | ||
162 | mrs->sample_skipped = 0; | ||
163 | mrs->cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts); | ||
164 | if (unlikely(!mrs->att_hist)) { | ||
165 | mrs->prob_ewma = mrs->cur_prob; | ||
166 | } else { | ||
167 | /* update exponential weighted moving variance */ | ||
168 | mrs->prob_ewmsd = minstrel_ewmsd(mrs->prob_ewmsd, | ||
169 | mrs->cur_prob, | ||
170 | mrs->prob_ewma, | ||
171 | EWMA_LEVEL); | ||
172 | |||
173 | /*update exponential weighted moving avarage */ | ||
174 | mrs->prob_ewma = minstrel_ewma(mrs->prob_ewma, | ||
175 | mrs->cur_prob, | ||
176 | EWMA_LEVEL); | ||
177 | } | ||
178 | mrs->att_hist += mrs->attempts; | ||
179 | mrs->succ_hist += mrs->success; | ||
180 | } else { | ||
181 | mrs->sample_skipped++; | ||
182 | } | ||
183 | |||
184 | mrs->last_success = mrs->success; | ||
185 | mrs->last_attempts = mrs->attempts; | ||
186 | mrs->success = 0; | ||
187 | mrs->attempts = 0; | ||
188 | } | ||
189 | |||
130 | static void | 190 | static void |
131 | minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) | 191 | minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) |
132 | { | 192 | { |
133 | u8 tmp_tp_rate[MAX_THR_RATES]; | 193 | u8 tmp_tp_rate[MAX_THR_RATES]; |
134 | u8 tmp_prob_rate = 0; | 194 | u8 tmp_prob_rate = 0; |
135 | u32 usecs; | 195 | int i, tmp_cur_tp, tmp_prob_tp; |
136 | int i; | ||
137 | 196 | ||
138 | for (i = 0; i < MAX_THR_RATES; i++) | 197 | for (i = 0; i < MAX_THR_RATES; i++) |
139 | tmp_tp_rate[i] = 0; | 198 | tmp_tp_rate[i] = 0; |
@@ -141,38 +200,15 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) | |||
141 | for (i = 0; i < mi->n_rates; i++) { | 200 | for (i = 0; i < mi->n_rates; i++) { |
142 | struct minstrel_rate *mr = &mi->r[i]; | 201 | struct minstrel_rate *mr = &mi->r[i]; |
143 | struct minstrel_rate_stats *mrs = &mi->r[i].stats; | 202 | struct minstrel_rate_stats *mrs = &mi->r[i].stats; |
203 | struct minstrel_rate_stats *tmp_mrs = &mi->r[tmp_prob_rate].stats; | ||
144 | 204 | ||
145 | usecs = mr->perfect_tx_time; | 205 | /* Update statistics of success probability per rate */ |
146 | if (!usecs) | 206 | minstrel_calc_rate_stats(mrs); |
147 | usecs = 1000000; | ||
148 | |||
149 | if (unlikely(mrs->attempts > 0)) { | ||
150 | mrs->sample_skipped = 0; | ||
151 | mrs->cur_prob = MINSTREL_FRAC(mrs->success, | ||
152 | mrs->attempts); | ||
153 | mrs->succ_hist += mrs->success; | ||
154 | mrs->att_hist += mrs->attempts; | ||
155 | mrs->probability = minstrel_ewma(mrs->probability, | ||
156 | mrs->cur_prob, | ||
157 | EWMA_LEVEL); | ||
158 | } else | ||
159 | mrs->sample_skipped++; | ||
160 | |||
161 | mrs->last_success = mrs->success; | ||
162 | mrs->last_attempts = mrs->attempts; | ||
163 | mrs->success = 0; | ||
164 | mrs->attempts = 0; | ||
165 | |||
166 | /* Update throughput per rate, reset thr. below 10% success */ | ||
167 | if (mrs->probability < MINSTREL_FRAC(10, 100)) | ||
168 | mrs->cur_tp = 0; | ||
169 | else | ||
170 | mrs->cur_tp = mrs->probability * (1000000 / usecs); | ||
171 | 207 | ||
172 | /* Sample less often below the 10% chance of success. | 208 | /* Sample less often below the 10% chance of success. |
173 | * Sample less often above the 95% chance of success. */ | 209 | * Sample less often above the 95% chance of success. */ |
174 | if (mrs->probability > MINSTREL_FRAC(95, 100) || | 210 | if (mrs->prob_ewma > MINSTREL_FRAC(95, 100) || |
175 | mrs->probability < MINSTREL_FRAC(10, 100)) { | 211 | mrs->prob_ewma < MINSTREL_FRAC(10, 100)) { |
176 | mr->adjusted_retry_count = mrs->retry_count >> 1; | 212 | mr->adjusted_retry_count = mrs->retry_count >> 1; |
177 | if (mr->adjusted_retry_count > 2) | 213 | if (mr->adjusted_retry_count > 2) |
178 | mr->adjusted_retry_count = 2; | 214 | mr->adjusted_retry_count = 2; |
@@ -192,11 +228,14 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) | |||
192 | * choose the maximum throughput rate as max_prob_rate | 228 | * choose the maximum throughput rate as max_prob_rate |
193 | * (2) if all success probabilities < 95%, the rate with | 229 | * (2) if all success probabilities < 95%, the rate with |
194 | * highest success probability is chosen as max_prob_rate */ | 230 | * highest success probability is chosen as max_prob_rate */ |
195 | if (mrs->probability >= MINSTREL_FRAC(95, 100)) { | 231 | if (mrs->prob_ewma >= MINSTREL_FRAC(95, 100)) { |
196 | if (mrs->cur_tp >= mi->r[tmp_prob_rate].stats.cur_tp) | 232 | tmp_cur_tp = minstrel_get_tp_avg(mr, mrs->prob_ewma); |
233 | tmp_prob_tp = minstrel_get_tp_avg(&mi->r[tmp_prob_rate], | ||
234 | tmp_mrs->prob_ewma); | ||
235 | if (tmp_cur_tp >= tmp_prob_tp) | ||
197 | tmp_prob_rate = i; | 236 | tmp_prob_rate = i; |
198 | } else { | 237 | } else { |
199 | if (mrs->probability >= mi->r[tmp_prob_rate].stats.probability) | 238 | if (mrs->prob_ewma >= tmp_mrs->prob_ewma) |
200 | tmp_prob_rate = i; | 239 | tmp_prob_rate = i; |
201 | } | 240 | } |
202 | } | 241 | } |
@@ -215,7 +254,7 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) | |||
215 | #endif | 254 | #endif |
216 | 255 | ||
217 | /* Reset update timer */ | 256 | /* Reset update timer */ |
218 | mi->stats_update = jiffies; | 257 | mi->last_stats_update = jiffies; |
219 | 258 | ||
220 | minstrel_update_rates(mp, mi); | 259 | minstrel_update_rates(mp, mi); |
221 | } | 260 | } |
@@ -253,7 +292,7 @@ minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband, | |||
253 | if (mi->sample_deferred > 0) | 292 | if (mi->sample_deferred > 0) |
254 | mi->sample_deferred--; | 293 | mi->sample_deferred--; |
255 | 294 | ||
256 | if (time_after(jiffies, mi->stats_update + | 295 | if (time_after(jiffies, mi->last_stats_update + |
257 | (mp->update_interval * HZ) / 1000)) | 296 | (mp->update_interval * HZ) / 1000)) |
258 | minstrel_update_stats(mp, mi); | 297 | minstrel_update_stats(mp, mi); |
259 | } | 298 | } |
@@ -385,7 +424,7 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta, | |||
385 | * has a probability of >95%, we shouldn't be attempting | 424 | * has a probability of >95%, we shouldn't be attempting |
386 | * to use it, as this only wastes precious airtime */ | 425 | * to use it, as this only wastes precious airtime */ |
387 | if (!mrr_capable && | 426 | if (!mrr_capable && |
388 | (mi->r[ndx].stats.probability > MINSTREL_FRAC(95, 100))) | 427 | (mi->r[ndx].stats.prob_ewma > MINSTREL_FRAC(95, 100))) |
389 | return; | 428 | return; |
390 | 429 | ||
391 | mi->prev_sample = true; | 430 | mi->prev_sample = true; |
@@ -519,7 +558,7 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband, | |||
519 | } | 558 | } |
520 | 559 | ||
521 | mi->n_rates = n; | 560 | mi->n_rates = n; |
522 | mi->stats_update = jiffies; | 561 | mi->last_stats_update = jiffies; |
523 | 562 | ||
524 | init_sample_table(mi); | 563 | init_sample_table(mi); |
525 | minstrel_update_rates(mp, mi); | 564 | minstrel_update_rates(mp, mi); |
@@ -553,7 +592,7 @@ minstrel_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp) | |||
553 | if (!mi->sample_table) | 592 | if (!mi->sample_table) |
554 | goto error1; | 593 | goto error1; |
555 | 594 | ||
556 | mi->stats_update = jiffies; | 595 | mi->last_stats_update = jiffies; |
557 | return mi; | 596 | return mi; |
558 | 597 | ||
559 | error1: | 598 | error1: |
@@ -663,12 +702,18 @@ minstrel_free(void *priv) | |||
663 | static u32 minstrel_get_expected_throughput(void *priv_sta) | 702 | static u32 minstrel_get_expected_throughput(void *priv_sta) |
664 | { | 703 | { |
665 | struct minstrel_sta_info *mi = priv_sta; | 704 | struct minstrel_sta_info *mi = priv_sta; |
705 | struct minstrel_rate_stats *tmp_mrs; | ||
666 | int idx = mi->max_tp_rate[0]; | 706 | int idx = mi->max_tp_rate[0]; |
707 | int tmp_cur_tp; | ||
667 | 708 | ||
668 | /* convert pkt per sec in kbps (1200 is the average pkt size used for | 709 | /* convert pkt per sec in kbps (1200 is the average pkt size used for |
669 | * computing cur_tp | 710 | * computing cur_tp |
670 | */ | 711 | */ |
671 | return MINSTREL_TRUNC(mi->r[idx].stats.cur_tp) * 1200 * 8 / 1024; | 712 | tmp_mrs = &mi->r[idx].stats; |
713 | tmp_cur_tp = minstrel_get_tp_avg(&mi->r[idx], tmp_mrs->prob_ewma); | ||
714 | tmp_cur_tp = tmp_cur_tp * 1200 * 8 / 1024; | ||
715 | |||
716 | return tmp_cur_tp; | ||
672 | } | 717 | } |
673 | 718 | ||
674 | const struct rate_control_ops mac80211_minstrel = { | 719 | 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 @@ | |||
13 | #define EWMA_DIV 128 | 13 | #define EWMA_DIV 128 |
14 | #define SAMPLE_COLUMNS 10 /* number of columns in sample table */ | 14 | #define SAMPLE_COLUMNS 10 /* number of columns in sample table */ |
15 | 15 | ||
16 | |||
17 | /* scaled fraction values */ | 16 | /* scaled fraction values */ |
18 | #define MINSTREL_SCALE 16 | 17 | #define MINSTREL_SCALE 16 |
19 | #define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div) | 18 | #define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div) |
@@ -24,11 +23,34 @@ | |||
24 | 23 | ||
25 | /* | 24 | /* |
26 | * Perform EWMA (Exponentially Weighted Moving Average) calculation | 25 | * Perform EWMA (Exponentially Weighted Moving Average) calculation |
27 | */ | 26 | */ |
28 | static inline int | 27 | static inline int |
29 | minstrel_ewma(int old, int new, int weight) | 28 | minstrel_ewma(int old, int new, int weight) |
30 | { | 29 | { |
31 | return (new * (EWMA_DIV - weight) + old * weight) / EWMA_DIV; | 30 | int diff, incr; |
31 | |||
32 | diff = new - old; | ||
33 | incr = (EWMA_DIV - weight) * diff / EWMA_DIV; | ||
34 | |||
35 | return old + incr; | ||
36 | } | ||
37 | |||
38 | /* | ||
39 | * Perform EWMSD (Exponentially Weighted Moving Standard Deviation) calculation | ||
40 | */ | ||
41 | static inline int | ||
42 | minstrel_ewmsd(int old_ewmsd, int cur_prob, int prob_ewma, int weight) | ||
43 | { | ||
44 | int diff, incr, tmp_var; | ||
45 | |||
46 | /* calculate exponential weighted moving variance */ | ||
47 | diff = MINSTREL_TRUNC((cur_prob - prob_ewma) * 1000000); | ||
48 | incr = (EWMA_DIV - weight) * diff / EWMA_DIV; | ||
49 | tmp_var = old_ewmsd * old_ewmsd; | ||
50 | tmp_var = weight * (tmp_var + diff * incr / 1000000) / EWMA_DIV; | ||
51 | |||
52 | /* return standard deviation */ | ||
53 | return (u16) int_sqrt(tmp_var); | ||
32 | } | 54 | } |
33 | 55 | ||
34 | struct minstrel_rate_stats { | 56 | struct minstrel_rate_stats { |
@@ -39,11 +61,13 @@ struct minstrel_rate_stats { | |||
39 | /* total attempts/success counters */ | 61 | /* total attempts/success counters */ |
40 | u64 att_hist, succ_hist; | 62 | u64 att_hist, succ_hist; |
41 | 63 | ||
42 | /* current throughput */ | 64 | /* statistis of packet delivery probability |
43 | unsigned int cur_tp; | 65 | * cur_prob - current prob within last update intervall |
44 | 66 | * prob_ewma - exponential weighted moving average of prob | |
45 | /* packet delivery probabilities */ | 67 | * prob_ewmsd - exp. weighted moving standard deviation of prob */ |
46 | unsigned int cur_prob, probability; | 68 | unsigned int cur_prob; |
69 | unsigned int prob_ewma; | ||
70 | u16 prob_ewmsd; | ||
47 | 71 | ||
48 | /* maximum retry counts */ | 72 | /* maximum retry counts */ |
49 | u8 retry_count; | 73 | u8 retry_count; |
@@ -71,7 +95,7 @@ struct minstrel_rate { | |||
71 | struct minstrel_sta_info { | 95 | struct minstrel_sta_info { |
72 | struct ieee80211_sta *sta; | 96 | struct ieee80211_sta *sta; |
73 | 97 | ||
74 | unsigned long stats_update; | 98 | unsigned long last_stats_update; |
75 | unsigned int sp_ack_dur; | 99 | unsigned int sp_ack_dur; |
76 | unsigned int rate_avg; | 100 | unsigned int rate_avg; |
77 | 101 | ||
@@ -95,6 +119,7 @@ struct minstrel_sta_info { | |||
95 | 119 | ||
96 | #ifdef CONFIG_MAC80211_DEBUGFS | 120 | #ifdef CONFIG_MAC80211_DEBUGFS |
97 | struct dentry *dbg_stats; | 121 | struct dentry *dbg_stats; |
122 | struct dentry *dbg_stats_csv; | ||
98 | #endif | 123 | #endif |
99 | }; | 124 | }; |
100 | 125 | ||
@@ -121,7 +146,6 @@ struct minstrel_priv { | |||
121 | u32 fixed_rate_idx; | 146 | u32 fixed_rate_idx; |
122 | struct dentry *dbg_fixed_rate; | 147 | struct dentry *dbg_fixed_rate; |
123 | #endif | 148 | #endif |
124 | |||
125 | }; | 149 | }; |
126 | 150 | ||
127 | struct minstrel_debugfs_info { | 151 | struct minstrel_debugfs_info { |
@@ -133,8 +157,13 @@ extern const struct rate_control_ops mac80211_minstrel; | |||
133 | void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir); | 157 | void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir); |
134 | void minstrel_remove_sta_debugfs(void *priv, void *priv_sta); | 158 | void minstrel_remove_sta_debugfs(void *priv, void *priv_sta); |
135 | 159 | ||
160 | /* Recalculate success probabilities and counters for a given rate using EWMA */ | ||
161 | void minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs); | ||
162 | int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_ewma); | ||
163 | |||
136 | /* debugfs */ | 164 | /* debugfs */ |
137 | int minstrel_stats_open(struct inode *inode, struct file *file); | 165 | int minstrel_stats_open(struct inode *inode, struct file *file); |
166 | int minstrel_stats_csv_open(struct inode *inode, struct file *file); | ||
138 | ssize_t minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *ppos); | 167 | ssize_t minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *ppos); |
139 | int minstrel_stats_release(struct inode *inode, struct file *file); | 168 | int minstrel_stats_release(struct inode *inode, struct file *file); |
140 | 169 | ||
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 @@ | |||
54 | #include <net/mac80211.h> | 54 | #include <net/mac80211.h> |
55 | #include "rc80211_minstrel.h" | 55 | #include "rc80211_minstrel.h" |
56 | 56 | ||
57 | ssize_t | ||
58 | minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *ppos) | ||
59 | { | ||
60 | struct minstrel_debugfs_info *ms; | ||
61 | |||
62 | ms = file->private_data; | ||
63 | return simple_read_from_buffer(buf, len, ppos, ms->buf, ms->len); | ||
64 | } | ||
65 | |||
66 | int | ||
67 | minstrel_stats_release(struct inode *inode, struct file *file) | ||
68 | { | ||
69 | kfree(file->private_data); | ||
70 | return 0; | ||
71 | } | ||
72 | |||
57 | int | 73 | int |
58 | minstrel_stats_open(struct inode *inode, struct file *file) | 74 | minstrel_stats_open(struct inode *inode, struct file *file) |
59 | { | 75 | { |
60 | struct minstrel_sta_info *mi = inode->i_private; | 76 | struct minstrel_sta_info *mi = inode->i_private; |
61 | struct minstrel_debugfs_info *ms; | 77 | struct minstrel_debugfs_info *ms; |
62 | unsigned int i, tp, prob, eprob; | 78 | unsigned int i, tp_max, tp_avg, prob, eprob; |
63 | char *p; | 79 | char *p; |
64 | 80 | ||
65 | ms = kmalloc(2048, GFP_KERNEL); | 81 | ms = kmalloc(2048, GFP_KERNEL); |
@@ -68,8 +84,14 @@ minstrel_stats_open(struct inode *inode, struct file *file) | |||
68 | 84 | ||
69 | file->private_data = ms; | 85 | file->private_data = ms; |
70 | p = ms->buf; | 86 | p = ms->buf; |
71 | p += sprintf(p, "rate tpt eprob *prob" | 87 | p += sprintf(p, "\n"); |
72 | " *ok(*cum) ok( cum)\n"); | 88 | p += sprintf(p, "best __________rate_________ ______" |
89 | "statistics______ ________last_______ " | ||
90 | "______sum-of________\n"); | ||
91 | p += sprintf(p, "rate [name idx airtime max_tp] [ ø(tp) ø(prob) " | ||
92 | "sd(prob)] [prob.|retry|suc|att] " | ||
93 | "[#success | #attempts]\n"); | ||
94 | |||
73 | for (i = 0; i < mi->n_rates; i++) { | 95 | for (i = 0; i < mi->n_rates; i++) { |
74 | struct minstrel_rate *mr = &mi->r[i]; | 96 | struct minstrel_rate *mr = &mi->r[i]; |
75 | struct minstrel_rate_stats *mrs = &mi->r[i].stats; | 97 | struct minstrel_rate_stats *mrs = &mi->r[i].stats; |
@@ -79,18 +101,26 @@ minstrel_stats_open(struct inode *inode, struct file *file) | |||
79 | *(p++) = (i == mi->max_tp_rate[2]) ? 'C' : ' '; | 101 | *(p++) = (i == mi->max_tp_rate[2]) ? 'C' : ' '; |
80 | *(p++) = (i == mi->max_tp_rate[3]) ? 'D' : ' '; | 102 | *(p++) = (i == mi->max_tp_rate[3]) ? 'D' : ' '; |
81 | *(p++) = (i == mi->max_prob_rate) ? 'P' : ' '; | 103 | *(p++) = (i == mi->max_prob_rate) ? 'P' : ' '; |
82 | p += sprintf(p, "%3u%s", mr->bitrate / 2, | 104 | |
105 | p += sprintf(p, " %3u%s ", mr->bitrate / 2, | ||
83 | (mr->bitrate & 1 ? ".5" : " ")); | 106 | (mr->bitrate & 1 ? ".5" : " ")); |
107 | p += sprintf(p, "%3u ", i); | ||
108 | p += sprintf(p, "%6u ", mr->perfect_tx_time); | ||
84 | 109 | ||
85 | tp = MINSTREL_TRUNC(mrs->cur_tp / 10); | 110 | tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100)); |
111 | tp_avg = minstrel_get_tp_avg(mr, mrs->prob_ewma); | ||
86 | prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); | 112 | prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); |
87 | eprob = MINSTREL_TRUNC(mrs->probability * 1000); | 113 | eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); |
88 | 114 | ||
89 | p += sprintf(p, " %4u.%1u %3u.%1u %3u.%1u" | 115 | p += sprintf(p, "%4u.%1u %4u.%1u %3u.%1u %3u.%1u" |
90 | " %4u(%4u) %9llu(%9llu)\n", | 116 | " %3u.%1u %3u %3u %-3u " |
91 | tp / 10, tp % 10, | 117 | "%9llu %-9llu\n", |
118 | tp_max / 10, tp_max % 10, | ||
119 | tp_avg / 10, tp_avg % 10, | ||
92 | eprob / 10, eprob % 10, | 120 | eprob / 10, eprob % 10, |
121 | mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10, | ||
93 | prob / 10, prob % 10, | 122 | prob / 10, prob % 10, |
123 | mrs->retry_count, | ||
94 | mrs->last_success, | 124 | mrs->last_success, |
95 | mrs->last_attempts, | 125 | mrs->last_attempts, |
96 | (unsigned long long)mrs->succ_hist, | 126 | (unsigned long long)mrs->succ_hist, |
@@ -107,25 +137,75 @@ minstrel_stats_open(struct inode *inode, struct file *file) | |||
107 | return 0; | 137 | return 0; |
108 | } | 138 | } |
109 | 139 | ||
110 | ssize_t | 140 | static const struct file_operations minstrel_stat_fops = { |
111 | minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *ppos) | 141 | .owner = THIS_MODULE, |
142 | .open = minstrel_stats_open, | ||
143 | .read = minstrel_stats_read, | ||
144 | .release = minstrel_stats_release, | ||
145 | .llseek = default_llseek, | ||
146 | }; | ||
147 | |||
148 | int | ||
149 | minstrel_stats_csv_open(struct inode *inode, struct file *file) | ||
112 | { | 150 | { |
151 | struct minstrel_sta_info *mi = inode->i_private; | ||
113 | struct minstrel_debugfs_info *ms; | 152 | struct minstrel_debugfs_info *ms; |
153 | unsigned int i, tp_max, tp_avg, prob, eprob; | ||
154 | char *p; | ||
114 | 155 | ||
115 | ms = file->private_data; | 156 | ms = kmalloc(2048, GFP_KERNEL); |
116 | return simple_read_from_buffer(buf, len, ppos, ms->buf, ms->len); | 157 | if (!ms) |
117 | } | 158 | return -ENOMEM; |
159 | |||
160 | file->private_data = ms; | ||
161 | p = ms->buf; | ||
162 | |||
163 | for (i = 0; i < mi->n_rates; i++) { | ||
164 | struct minstrel_rate *mr = &mi->r[i]; | ||
165 | struct minstrel_rate_stats *mrs = &mi->r[i].stats; | ||
166 | |||
167 | p += sprintf(p, "%s" ,((i == mi->max_tp_rate[0]) ? "A" : "")); | ||
168 | p += sprintf(p, "%s" ,((i == mi->max_tp_rate[1]) ? "B" : "")); | ||
169 | p += sprintf(p, "%s" ,((i == mi->max_tp_rate[2]) ? "C" : "")); | ||
170 | p += sprintf(p, "%s" ,((i == mi->max_tp_rate[3]) ? "D" : "")); | ||
171 | p += sprintf(p, "%s" ,((i == mi->max_prob_rate) ? "P" : "")); | ||
172 | |||
173 | p += sprintf(p, ",%u%s", mr->bitrate / 2, | ||
174 | (mr->bitrate & 1 ? ".5," : ",")); | ||
175 | p += sprintf(p, "%u,", i); | ||
176 | p += sprintf(p, "%u,",mr->perfect_tx_time); | ||
177 | |||
178 | tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100)); | ||
179 | tp_avg = minstrel_get_tp_avg(mr, mrs->prob_ewma); | ||
180 | prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); | ||
181 | eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); | ||
182 | |||
183 | p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u.%u,%u,%u,%u," | ||
184 | "%llu,%llu,%d,%d\n", | ||
185 | tp_max / 10, tp_max % 10, | ||
186 | tp_avg / 10, tp_avg % 10, | ||
187 | eprob / 10, eprob % 10, | ||
188 | mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10, | ||
189 | prob / 10, prob % 10, | ||
190 | mrs->retry_count, | ||
191 | mrs->last_success, | ||
192 | mrs->last_attempts, | ||
193 | (unsigned long long)mrs->succ_hist, | ||
194 | (unsigned long long)mrs->att_hist, | ||
195 | mi->total_packets - mi->sample_packets, | ||
196 | mi->sample_packets); | ||
197 | |||
198 | } | ||
199 | ms->len = p - ms->buf; | ||
200 | |||
201 | WARN_ON(ms->len + sizeof(*ms) > 2048); | ||
118 | 202 | ||
119 | int | ||
120 | minstrel_stats_release(struct inode *inode, struct file *file) | ||
121 | { | ||
122 | kfree(file->private_data); | ||
123 | return 0; | 203 | return 0; |
124 | } | 204 | } |
125 | 205 | ||
126 | static const struct file_operations minstrel_stat_fops = { | 206 | static const struct file_operations minstrel_stat_csv_fops = { |
127 | .owner = THIS_MODULE, | 207 | .owner = THIS_MODULE, |
128 | .open = minstrel_stats_open, | 208 | .open = minstrel_stats_csv_open, |
129 | .read = minstrel_stats_read, | 209 | .read = minstrel_stats_read, |
130 | .release = minstrel_stats_release, | 210 | .release = minstrel_stats_release, |
131 | .llseek = default_llseek, | 211 | .llseek = default_llseek, |
@@ -138,6 +218,9 @@ minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir) | |||
138 | 218 | ||
139 | mi->dbg_stats = debugfs_create_file("rc_stats", S_IRUGO, dir, mi, | 219 | mi->dbg_stats = debugfs_create_file("rc_stats", S_IRUGO, dir, mi, |
140 | &minstrel_stat_fops); | 220 | &minstrel_stat_fops); |
221 | |||
222 | mi->dbg_stats_csv = debugfs_create_file("rc_stats_csv", S_IRUGO, dir, | ||
223 | mi, &minstrel_stat_csv_fops); | ||
141 | } | 224 | } |
142 | 225 | ||
143 | void | 226 | void |
@@ -146,4 +229,6 @@ minstrel_remove_sta_debugfs(void *priv, void *priv_sta) | |||
146 | struct minstrel_sta_info *mi = priv_sta; | 229 | struct minstrel_sta_info *mi = priv_sta; |
147 | 230 | ||
148 | debugfs_remove(mi->dbg_stats); | 231 | debugfs_remove(mi->dbg_stats); |
232 | |||
233 | debugfs_remove(mi->dbg_stats_csv); | ||
149 | } | 234 | } |
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 60698fc7042e..7430a1df2ab1 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c | |||
@@ -313,67 +313,35 @@ minstrel_get_ratestats(struct minstrel_ht_sta *mi, int index) | |||
313 | return &mi->groups[index / MCS_GROUP_RATES].rates[index % MCS_GROUP_RATES]; | 313 | return &mi->groups[index / MCS_GROUP_RATES].rates[index % MCS_GROUP_RATES]; |
314 | } | 314 | } |
315 | 315 | ||
316 | |||
317 | /* | 316 | /* |
318 | * Recalculate success probabilities and counters for a rate using EWMA | 317 | * Return current throughput based on the average A-MPDU length, taking into |
318 | * account the expected number of retransmissions and their expected length | ||
319 | */ | 319 | */ |
320 | static void | 320 | int |
321 | minstrel_calc_rate_ewma(struct minstrel_rate_stats *mr) | 321 | minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate, |
322 | int prob_ewma) | ||
322 | { | 323 | { |
323 | if (unlikely(mr->attempts > 0)) { | ||
324 | mr->sample_skipped = 0; | ||
325 | mr->cur_prob = MINSTREL_FRAC(mr->success, mr->attempts); | ||
326 | if (!mr->att_hist) | ||
327 | mr->probability = mr->cur_prob; | ||
328 | else | ||
329 | mr->probability = minstrel_ewma(mr->probability, | ||
330 | mr->cur_prob, EWMA_LEVEL); | ||
331 | mr->att_hist += mr->attempts; | ||
332 | mr->succ_hist += mr->success; | ||
333 | } else { | ||
334 | mr->sample_skipped++; | ||
335 | } | ||
336 | mr->last_success = mr->success; | ||
337 | mr->last_attempts = mr->attempts; | ||
338 | mr->success = 0; | ||
339 | mr->attempts = 0; | ||
340 | } | ||
341 | |||
342 | /* | ||
343 | * Calculate throughput based on the average A-MPDU length, taking into account | ||
344 | * the expected number of retransmissions and their expected length | ||
345 | */ | ||
346 | static void | ||
347 | minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate) | ||
348 | { | ||
349 | struct minstrel_rate_stats *mr; | ||
350 | unsigned int nsecs = 0; | 324 | unsigned int nsecs = 0; |
351 | unsigned int tp; | ||
352 | unsigned int prob; | ||
353 | 325 | ||
354 | mr = &mi->groups[group].rates[rate]; | 326 | /* do not account throughput if sucess prob is below 10% */ |
355 | prob = mr->probability; | 327 | if (prob_ewma < MINSTREL_FRAC(10, 100)) |
356 | 328 | return 0; | |
357 | if (prob < MINSTREL_FRAC(1, 10)) { | ||
358 | mr->cur_tp = 0; | ||
359 | return; | ||
360 | } | ||
361 | |||
362 | /* | ||
363 | * For the throughput calculation, limit the probability value to 90% to | ||
364 | * account for collision related packet error rate fluctuation | ||
365 | */ | ||
366 | if (prob > MINSTREL_FRAC(9, 10)) | ||
367 | prob = MINSTREL_FRAC(9, 10); | ||
368 | 329 | ||
369 | if (group != MINSTREL_CCK_GROUP) | 330 | if (group != MINSTREL_CCK_GROUP) |
370 | nsecs = 1000 * mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len); | 331 | nsecs = 1000 * mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len); |
371 | 332 | ||
372 | nsecs += minstrel_mcs_groups[group].duration[rate]; | 333 | nsecs += minstrel_mcs_groups[group].duration[rate]; |
373 | 334 | ||
374 | /* prob is scaled - see MINSTREL_FRAC above */ | 335 | /* |
375 | tp = 1000000 * ((prob * 1000) / nsecs); | 336 | * For the throughput calculation, limit the probability value to 90% to |
376 | mr->cur_tp = MINSTREL_TRUNC(tp); | 337 | * account for collision related packet error rate fluctuation |
338 | * (prob is scaled - see MINSTREL_FRAC above) | ||
339 | */ | ||
340 | if (prob_ewma > MINSTREL_FRAC(90, 100)) | ||
341 | return MINSTREL_TRUNC(100000 * ((MINSTREL_FRAC(90, 100) * 1000) | ||
342 | / nsecs)); | ||
343 | else | ||
344 | return MINSTREL_TRUNC(100000 * ((prob_ewma * 1000) / nsecs)); | ||
377 | } | 345 | } |
378 | 346 | ||
379 | /* | 347 | /* |
@@ -387,22 +355,23 @@ static void | |||
387 | minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u16 index, | 355 | minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u16 index, |
388 | u16 *tp_list) | 356 | u16 *tp_list) |
389 | { | 357 | { |
390 | int cur_group, cur_idx, cur_thr, cur_prob; | 358 | int cur_group, cur_idx, cur_tp_avg, cur_prob; |
391 | int tmp_group, tmp_idx, tmp_thr, tmp_prob; | 359 | int tmp_group, tmp_idx, tmp_tp_avg, tmp_prob; |
392 | int j = MAX_THR_RATES; | 360 | int j = MAX_THR_RATES; |
393 | 361 | ||
394 | cur_group = index / MCS_GROUP_RATES; | 362 | cur_group = index / MCS_GROUP_RATES; |
395 | cur_idx = index % MCS_GROUP_RATES; | 363 | cur_idx = index % MCS_GROUP_RATES; |
396 | cur_thr = mi->groups[cur_group].rates[cur_idx].cur_tp; | 364 | cur_prob = mi->groups[cur_group].rates[cur_idx].prob_ewma; |
397 | cur_prob = mi->groups[cur_group].rates[cur_idx].probability; | 365 | cur_tp_avg = minstrel_ht_get_tp_avg(mi, cur_group, cur_idx, cur_prob); |
398 | 366 | ||
399 | do { | 367 | do { |
400 | tmp_group = tp_list[j - 1] / MCS_GROUP_RATES; | 368 | tmp_group = tp_list[j - 1] / MCS_GROUP_RATES; |
401 | tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES; | 369 | tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES; |
402 | tmp_thr = mi->groups[tmp_group].rates[tmp_idx].cur_tp; | 370 | tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma; |
403 | tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability; | 371 | tmp_tp_avg = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, |
404 | if (cur_thr < tmp_thr || | 372 | tmp_prob); |
405 | (cur_thr == tmp_thr && cur_prob <= tmp_prob)) | 373 | if (cur_tp_avg < tmp_tp_avg || |
374 | (cur_tp_avg == tmp_tp_avg && cur_prob <= tmp_prob)) | ||
406 | break; | 375 | break; |
407 | j--; | 376 | j--; |
408 | } while (j > 0); | 377 | } while (j > 0); |
@@ -422,16 +391,21 @@ static void | |||
422 | minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index) | 391 | minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index) |
423 | { | 392 | { |
424 | struct minstrel_mcs_group_data *mg; | 393 | struct minstrel_mcs_group_data *mg; |
425 | struct minstrel_rate_stats *mr; | 394 | struct minstrel_rate_stats *mrs; |
426 | int tmp_group, tmp_idx, tmp_tp, tmp_prob, max_tp_group; | 395 | int tmp_group, tmp_idx, tmp_tp_avg, tmp_prob; |
396 | int max_tp_group, cur_tp_avg, cur_group, cur_idx; | ||
397 | int max_gpr_group, max_gpr_idx; | ||
398 | int max_gpr_tp_avg, max_gpr_prob; | ||
427 | 399 | ||
400 | cur_group = index / MCS_GROUP_RATES; | ||
401 | cur_idx = index % MCS_GROUP_RATES; | ||
428 | mg = &mi->groups[index / MCS_GROUP_RATES]; | 402 | mg = &mi->groups[index / MCS_GROUP_RATES]; |
429 | mr = &mg->rates[index % MCS_GROUP_RATES]; | 403 | mrs = &mg->rates[index % MCS_GROUP_RATES]; |
430 | 404 | ||
431 | tmp_group = mi->max_prob_rate / MCS_GROUP_RATES; | 405 | tmp_group = mi->max_prob_rate / MCS_GROUP_RATES; |
432 | tmp_idx = mi->max_prob_rate % MCS_GROUP_RATES; | 406 | tmp_idx = mi->max_prob_rate % MCS_GROUP_RATES; |
433 | tmp_tp = mi->groups[tmp_group].rates[tmp_idx].cur_tp; | 407 | tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma; |
434 | tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability; | 408 | tmp_tp_avg = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob); |
435 | 409 | ||
436 | /* if max_tp_rate[0] is from MCS_GROUP max_prob_rate get selected from | 410 | /* if max_tp_rate[0] is from MCS_GROUP max_prob_rate get selected from |
437 | * MCS_GROUP as well as CCK_GROUP rates do not allow aggregation */ | 411 | * MCS_GROUP as well as CCK_GROUP rates do not allow aggregation */ |
@@ -440,15 +414,24 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index) | |||
440 | (max_tp_group != MINSTREL_CCK_GROUP)) | 414 | (max_tp_group != MINSTREL_CCK_GROUP)) |
441 | return; | 415 | return; |
442 | 416 | ||
443 | if (mr->probability > MINSTREL_FRAC(75, 100)) { | 417 | if (mrs->prob_ewma > MINSTREL_FRAC(75, 100)) { |
444 | if (mr->cur_tp > tmp_tp) | 418 | cur_tp_avg = minstrel_ht_get_tp_avg(mi, cur_group, cur_idx, |
419 | mrs->prob_ewma); | ||
420 | if (cur_tp_avg > tmp_tp_avg) | ||
445 | mi->max_prob_rate = index; | 421 | mi->max_prob_rate = index; |
446 | if (mr->cur_tp > mg->rates[mg->max_group_prob_rate].cur_tp) | 422 | |
423 | max_gpr_group = mg->max_group_prob_rate / MCS_GROUP_RATES; | ||
424 | max_gpr_idx = mg->max_group_prob_rate % MCS_GROUP_RATES; | ||
425 | max_gpr_prob = mi->groups[max_gpr_group].rates[max_gpr_idx].prob_ewma; | ||
426 | max_gpr_tp_avg = minstrel_ht_get_tp_avg(mi, max_gpr_group, | ||
427 | max_gpr_idx, | ||
428 | max_gpr_prob); | ||
429 | if (cur_tp_avg > max_gpr_tp_avg) | ||
447 | mg->max_group_prob_rate = index; | 430 | mg->max_group_prob_rate = index; |
448 | } else { | 431 | } else { |
449 | if (mr->probability > tmp_prob) | 432 | if (mrs->prob_ewma > tmp_prob) |
450 | mi->max_prob_rate = index; | 433 | mi->max_prob_rate = index; |
451 | if (mr->probability > mg->rates[mg->max_group_prob_rate].probability) | 434 | if (mrs->prob_ewma > mg->rates[mg->max_group_prob_rate].prob_ewma) |
452 | mg->max_group_prob_rate = index; | 435 | mg->max_group_prob_rate = index; |
453 | } | 436 | } |
454 | } | 437 | } |
@@ -465,16 +448,18 @@ minstrel_ht_assign_best_tp_rates(struct minstrel_ht_sta *mi, | |||
465 | u16 tmp_mcs_tp_rate[MAX_THR_RATES], | 448 | u16 tmp_mcs_tp_rate[MAX_THR_RATES], |
466 | u16 tmp_cck_tp_rate[MAX_THR_RATES]) | 449 | u16 tmp_cck_tp_rate[MAX_THR_RATES]) |
467 | { | 450 | { |
468 | unsigned int tmp_group, tmp_idx, tmp_cck_tp, tmp_mcs_tp; | 451 | unsigned int tmp_group, tmp_idx, tmp_cck_tp, tmp_mcs_tp, tmp_prob; |
469 | int i; | 452 | int i; |
470 | 453 | ||
471 | tmp_group = tmp_cck_tp_rate[0] / MCS_GROUP_RATES; | 454 | tmp_group = tmp_cck_tp_rate[0] / MCS_GROUP_RATES; |
472 | tmp_idx = tmp_cck_tp_rate[0] % MCS_GROUP_RATES; | 455 | tmp_idx = tmp_cck_tp_rate[0] % MCS_GROUP_RATES; |
473 | tmp_cck_tp = mi->groups[tmp_group].rates[tmp_idx].cur_tp; | 456 | tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma; |
457 | tmp_cck_tp = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob); | ||
474 | 458 | ||
475 | tmp_group = tmp_mcs_tp_rate[0] / MCS_GROUP_RATES; | 459 | tmp_group = tmp_mcs_tp_rate[0] / MCS_GROUP_RATES; |
476 | tmp_idx = tmp_mcs_tp_rate[0] % MCS_GROUP_RATES; | 460 | tmp_idx = tmp_mcs_tp_rate[0] % MCS_GROUP_RATES; |
477 | tmp_mcs_tp = mi->groups[tmp_group].rates[tmp_idx].cur_tp; | 461 | tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma; |
462 | tmp_mcs_tp = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob); | ||
478 | 463 | ||
479 | if (tmp_cck_tp > tmp_mcs_tp) { | 464 | if (tmp_cck_tp > tmp_mcs_tp) { |
480 | for(i = 0; i < MAX_THR_RATES; i++) { | 465 | for(i = 0; i < MAX_THR_RATES; i++) { |
@@ -493,8 +478,7 @@ static inline void | |||
493 | minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi) | 478 | minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi) |
494 | { | 479 | { |
495 | struct minstrel_mcs_group_data *mg; | 480 | struct minstrel_mcs_group_data *mg; |
496 | struct minstrel_rate_stats *mr; | 481 | int tmp_max_streams, group, tmp_idx, tmp_prob; |
497 | int tmp_max_streams, group; | ||
498 | int tmp_tp = 0; | 482 | int tmp_tp = 0; |
499 | 483 | ||
500 | tmp_max_streams = minstrel_mcs_groups[mi->max_tp_rate[0] / | 484 | tmp_max_streams = minstrel_mcs_groups[mi->max_tp_rate[0] / |
@@ -503,11 +487,16 @@ minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi) | |||
503 | mg = &mi->groups[group]; | 487 | mg = &mi->groups[group]; |
504 | if (!mg->supported || group == MINSTREL_CCK_GROUP) | 488 | if (!mg->supported || group == MINSTREL_CCK_GROUP) |
505 | continue; | 489 | continue; |
506 | mr = minstrel_get_ratestats(mi, mg->max_group_prob_rate); | 490 | |
507 | if (tmp_tp < mr->cur_tp && | 491 | tmp_idx = mg->max_group_prob_rate % MCS_GROUP_RATES; |
492 | tmp_prob = mi->groups[group].rates[tmp_idx].prob_ewma; | ||
493 | |||
494 | if (tmp_tp < minstrel_ht_get_tp_avg(mi, group, tmp_idx, tmp_prob) && | ||
508 | (minstrel_mcs_groups[group].streams < tmp_max_streams)) { | 495 | (minstrel_mcs_groups[group].streams < tmp_max_streams)) { |
509 | mi->max_prob_rate = mg->max_group_prob_rate; | 496 | mi->max_prob_rate = mg->max_group_prob_rate; |
510 | tmp_tp = mr->cur_tp; | 497 | tmp_tp = minstrel_ht_get_tp_avg(mi, group, |
498 | tmp_idx, | ||
499 | tmp_prob); | ||
511 | } | 500 | } |
512 | } | 501 | } |
513 | } | 502 | } |
@@ -525,8 +514,8 @@ static void | |||
525 | minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) | 514 | minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) |
526 | { | 515 | { |
527 | struct minstrel_mcs_group_data *mg; | 516 | struct minstrel_mcs_group_data *mg; |
528 | struct minstrel_rate_stats *mr; | 517 | struct minstrel_rate_stats *mrs; |
529 | int group, i, j; | 518 | int group, i, j, cur_prob; |
530 | u16 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES]; | 519 | u16 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES]; |
531 | u16 tmp_cck_tp_rate[MAX_THR_RATES], index; | 520 | u16 tmp_cck_tp_rate[MAX_THR_RATES], index; |
532 | 521 | ||
@@ -565,12 +554,12 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) | |||
565 | 554 | ||
566 | index = MCS_GROUP_RATES * group + i; | 555 | index = MCS_GROUP_RATES * group + i; |
567 | 556 | ||
568 | mr = &mg->rates[i]; | 557 | mrs = &mg->rates[i]; |
569 | mr->retry_updated = false; | 558 | mrs->retry_updated = false; |
570 | minstrel_calc_rate_ewma(mr); | 559 | minstrel_calc_rate_stats(mrs); |
571 | minstrel_ht_calc_tp(mi, group, i); | 560 | cur_prob = mrs->prob_ewma; |
572 | 561 | ||
573 | if (!mr->cur_tp) | 562 | if (minstrel_ht_get_tp_avg(mi, group, i, cur_prob) == 0) |
574 | continue; | 563 | continue; |
575 | 564 | ||
576 | /* Find max throughput rate set */ | 565 | /* Find max throughput rate set */ |
@@ -614,7 +603,7 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) | |||
614 | #endif | 603 | #endif |
615 | 604 | ||
616 | /* Reset update timer */ | 605 | /* Reset update timer */ |
617 | mi->stats_update = jiffies; | 606 | mi->last_stats_update = jiffies; |
618 | } | 607 | } |
619 | 608 | ||
620 | static bool | 609 | static bool |
@@ -637,7 +626,7 @@ minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct ieee80211_tx_rate *rat | |||
637 | } | 626 | } |
638 | 627 | ||
639 | static void | 628 | static void |
640 | minstrel_next_sample_idx(struct minstrel_ht_sta *mi) | 629 | minstrel_set_next_sample_idx(struct minstrel_ht_sta *mi) |
641 | { | 630 | { |
642 | struct minstrel_mcs_group_data *mg; | 631 | struct minstrel_mcs_group_data *mg; |
643 | 632 | ||
@@ -778,7 +767,8 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband, | |||
778 | update = true; | 767 | update = true; |
779 | } | 768 | } |
780 | 769 | ||
781 | if (time_after(jiffies, mi->stats_update + (mp->update_interval / 2 * HZ) / 1000)) { | 770 | if (time_after(jiffies, mi->last_stats_update + |
771 | (mp->update_interval / 2 * HZ) / 1000)) { | ||
782 | update = true; | 772 | update = true; |
783 | minstrel_ht_update_stats(mp, mi); | 773 | minstrel_ht_update_stats(mp, mi); |
784 | } | 774 | } |
@@ -791,7 +781,7 @@ static void | |||
791 | minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, | 781 | minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, |
792 | int index) | 782 | int index) |
793 | { | 783 | { |
794 | struct minstrel_rate_stats *mr; | 784 | struct minstrel_rate_stats *mrs; |
795 | const struct mcs_group *group; | 785 | const struct mcs_group *group; |
796 | unsigned int tx_time, tx_time_rtscts, tx_time_data; | 786 | unsigned int tx_time, tx_time_rtscts, tx_time_data; |
797 | unsigned int cw = mp->cw_min; | 787 | unsigned int cw = mp->cw_min; |
@@ -800,16 +790,16 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, | |||
800 | unsigned int ampdu_len = MINSTREL_TRUNC(mi->avg_ampdu_len); | 790 | unsigned int ampdu_len = MINSTREL_TRUNC(mi->avg_ampdu_len); |
801 | unsigned int overhead = 0, overhead_rtscts = 0; | 791 | unsigned int overhead = 0, overhead_rtscts = 0; |
802 | 792 | ||
803 | mr = minstrel_get_ratestats(mi, index); | 793 | mrs = minstrel_get_ratestats(mi, index); |
804 | if (mr->probability < MINSTREL_FRAC(1, 10)) { | 794 | if (mrs->prob_ewma < MINSTREL_FRAC(1, 10)) { |
805 | mr->retry_count = 1; | 795 | mrs->retry_count = 1; |
806 | mr->retry_count_rtscts = 1; | 796 | mrs->retry_count_rtscts = 1; |
807 | return; | 797 | return; |
808 | } | 798 | } |
809 | 799 | ||
810 | mr->retry_count = 2; | 800 | mrs->retry_count = 2; |
811 | mr->retry_count_rtscts = 2; | 801 | mrs->retry_count_rtscts = 2; |
812 | mr->retry_updated = true; | 802 | mrs->retry_updated = true; |
813 | 803 | ||
814 | group = &minstrel_mcs_groups[index / MCS_GROUP_RATES]; | 804 | group = &minstrel_mcs_groups[index / MCS_GROUP_RATES]; |
815 | tx_time_data = group->duration[index % MCS_GROUP_RATES] * ampdu_len / 1000; | 805 | tx_time_data = group->duration[index % MCS_GROUP_RATES] * ampdu_len / 1000; |
@@ -840,9 +830,9 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, | |||
840 | tx_time_rtscts += ctime + overhead_rtscts + tx_time_data; | 830 | tx_time_rtscts += ctime + overhead_rtscts + tx_time_data; |
841 | 831 | ||
842 | if (tx_time_rtscts < mp->segment_size) | 832 | if (tx_time_rtscts < mp->segment_size) |
843 | mr->retry_count_rtscts++; | 833 | mrs->retry_count_rtscts++; |
844 | } while ((tx_time < mp->segment_size) && | 834 | } while ((tx_time < mp->segment_size) && |
845 | (++mr->retry_count < mp->max_retry)); | 835 | (++mrs->retry_count < mp->max_retry)); |
846 | } | 836 | } |
847 | 837 | ||
848 | 838 | ||
@@ -851,22 +841,22 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, | |||
851 | struct ieee80211_sta_rates *ratetbl, int offset, int index) | 841 | struct ieee80211_sta_rates *ratetbl, int offset, int index) |
852 | { | 842 | { |
853 | const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES]; | 843 | const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES]; |
854 | struct minstrel_rate_stats *mr; | 844 | struct minstrel_rate_stats *mrs; |
855 | u8 idx; | 845 | u8 idx; |
856 | u16 flags = group->flags; | 846 | u16 flags = group->flags; |
857 | 847 | ||
858 | mr = minstrel_get_ratestats(mi, index); | 848 | mrs = minstrel_get_ratestats(mi, index); |
859 | if (!mr->retry_updated) | 849 | if (!mrs->retry_updated) |
860 | minstrel_calc_retransmit(mp, mi, index); | 850 | minstrel_calc_retransmit(mp, mi, index); |
861 | 851 | ||
862 | if (mr->probability < MINSTREL_FRAC(20, 100) || !mr->retry_count) { | 852 | if (mrs->prob_ewma < MINSTREL_FRAC(20, 100) || !mrs->retry_count) { |
863 | ratetbl->rate[offset].count = 2; | 853 | ratetbl->rate[offset].count = 2; |
864 | ratetbl->rate[offset].count_rts = 2; | 854 | ratetbl->rate[offset].count_rts = 2; |
865 | ratetbl->rate[offset].count_cts = 2; | 855 | ratetbl->rate[offset].count_cts = 2; |
866 | } else { | 856 | } else { |
867 | ratetbl->rate[offset].count = mr->retry_count; | 857 | ratetbl->rate[offset].count = mrs->retry_count; |
868 | ratetbl->rate[offset].count_cts = mr->retry_count; | 858 | ratetbl->rate[offset].count_cts = mrs->retry_count; |
869 | ratetbl->rate[offset].count_rts = mr->retry_count_rtscts; | 859 | ratetbl->rate[offset].count_rts = mrs->retry_count_rtscts; |
870 | } | 860 | } |
871 | 861 | ||
872 | if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) | 862 | if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) |
@@ -924,7 +914,7 @@ minstrel_get_duration(int index) | |||
924 | static int | 914 | static int |
925 | minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) | 915 | minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) |
926 | { | 916 | { |
927 | struct minstrel_rate_stats *mr; | 917 | struct minstrel_rate_stats *mrs; |
928 | struct minstrel_mcs_group_data *mg; | 918 | struct minstrel_mcs_group_data *mg; |
929 | unsigned int sample_dur, sample_group, cur_max_tp_streams; | 919 | unsigned int sample_dur, sample_group, cur_max_tp_streams; |
930 | int sample_idx = 0; | 920 | int sample_idx = 0; |
@@ -940,12 +930,12 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) | |||
940 | sample_group = mi->sample_group; | 930 | sample_group = mi->sample_group; |
941 | mg = &mi->groups[sample_group]; | 931 | mg = &mi->groups[sample_group]; |
942 | sample_idx = sample_table[mg->column][mg->index]; | 932 | sample_idx = sample_table[mg->column][mg->index]; |
943 | minstrel_next_sample_idx(mi); | 933 | minstrel_set_next_sample_idx(mi); |
944 | 934 | ||
945 | if (!(mg->supported & BIT(sample_idx))) | 935 | if (!(mg->supported & BIT(sample_idx))) |
946 | return -1; | 936 | return -1; |
947 | 937 | ||
948 | mr = &mg->rates[sample_idx]; | 938 | mrs = &mg->rates[sample_idx]; |
949 | sample_idx += sample_group * MCS_GROUP_RATES; | 939 | sample_idx += sample_group * MCS_GROUP_RATES; |
950 | 940 | ||
951 | /* | 941 | /* |
@@ -962,7 +952,7 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) | |||
962 | * Do not sample if the probability is already higher than 95% | 952 | * Do not sample if the probability is already higher than 95% |
963 | * to avoid wasting airtime. | 953 | * to avoid wasting airtime. |
964 | */ | 954 | */ |
965 | if (mr->probability > MINSTREL_FRAC(95, 100)) | 955 | if (mrs->prob_ewma > MINSTREL_FRAC(95, 100)) |
966 | return -1; | 956 | return -1; |
967 | 957 | ||
968 | /* | 958 | /* |
@@ -977,7 +967,7 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) | |||
977 | (cur_max_tp_streams - 1 < | 967 | (cur_max_tp_streams - 1 < |
978 | minstrel_mcs_groups[sample_group].streams || | 968 | minstrel_mcs_groups[sample_group].streams || |
979 | sample_dur >= minstrel_get_duration(mi->max_prob_rate))) { | 969 | sample_dur >= minstrel_get_duration(mi->max_prob_rate))) { |
980 | if (mr->sample_skipped < 20) | 970 | if (mrs->sample_skipped < 20) |
981 | return -1; | 971 | return -1; |
982 | 972 | ||
983 | if (mi->sample_slow++ > 2) | 973 | if (mi->sample_slow++ > 2) |
@@ -1131,7 +1121,7 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, | |||
1131 | memset(mi, 0, sizeof(*mi)); | 1121 | memset(mi, 0, sizeof(*mi)); |
1132 | 1122 | ||
1133 | mi->sta = sta; | 1123 | mi->sta = sta; |
1134 | mi->stats_update = jiffies; | 1124 | mi->last_stats_update = jiffies; |
1135 | 1125 | ||
1136 | ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1, 0); | 1126 | ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1, 0); |
1137 | mi->overhead = ieee80211_frame_duration(sband->band, 0, 60, 1, 1, 0); | 1127 | mi->overhead = ieee80211_frame_duration(sband->band, 0, 60, 1, 1, 0); |
@@ -1328,16 +1318,19 @@ static u32 minstrel_ht_get_expected_throughput(void *priv_sta) | |||
1328 | { | 1318 | { |
1329 | struct minstrel_ht_sta_priv *msp = priv_sta; | 1319 | struct minstrel_ht_sta_priv *msp = priv_sta; |
1330 | struct minstrel_ht_sta *mi = &msp->ht; | 1320 | struct minstrel_ht_sta *mi = &msp->ht; |
1331 | int i, j; | 1321 | int i, j, prob, tp_avg; |
1332 | 1322 | ||
1333 | if (!msp->is_ht) | 1323 | if (!msp->is_ht) |
1334 | return mac80211_minstrel.get_expected_throughput(priv_sta); | 1324 | return mac80211_minstrel.get_expected_throughput(priv_sta); |
1335 | 1325 | ||
1336 | i = mi->max_tp_rate[0] / MCS_GROUP_RATES; | 1326 | i = mi->max_tp_rate[0] / MCS_GROUP_RATES; |
1337 | j = mi->max_tp_rate[0] % MCS_GROUP_RATES; | 1327 | j = mi->max_tp_rate[0] % MCS_GROUP_RATES; |
1328 | prob = mi->groups[i].rates[j].prob_ewma; | ||
1329 | |||
1330 | /* convert tp_avg from pkt per second in kbps */ | ||
1331 | tp_avg = minstrel_ht_get_tp_avg(mi, i, j, prob) * AVG_PKT_SIZE * 8 / 1024; | ||
1338 | 1332 | ||
1339 | /* convert cur_tp from pkt per second in kbps */ | 1333 | return tp_avg; |
1340 | return mi->groups[i].rates[j].cur_tp * AVG_PKT_SIZE * 8 / 1024; | ||
1341 | } | 1334 | } |
1342 | 1335 | ||
1343 | static const struct rate_control_ops mac80211_minstrel_ht = { | 1336 | 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 { | |||
78 | u16 max_prob_rate; | 78 | u16 max_prob_rate; |
79 | 79 | ||
80 | /* time of last status update */ | 80 | /* time of last status update */ |
81 | unsigned long stats_update; | 81 | unsigned long last_stats_update; |
82 | 82 | ||
83 | /* overhead time in usec for each frame */ | 83 | /* overhead time in usec for each frame */ |
84 | unsigned int overhead; | 84 | unsigned int overhead; |
@@ -112,6 +112,7 @@ struct minstrel_ht_sta_priv { | |||
112 | }; | 112 | }; |
113 | #ifdef CONFIG_MAC80211_DEBUGFS | 113 | #ifdef CONFIG_MAC80211_DEBUGFS |
114 | struct dentry *dbg_stats; | 114 | struct dentry *dbg_stats; |
115 | struct dentry *dbg_stats_csv; | ||
115 | #endif | 116 | #endif |
116 | void *ratelist; | 117 | void *ratelist; |
117 | void *sample_table; | 118 | void *sample_table; |
@@ -120,5 +121,7 @@ struct minstrel_ht_sta_priv { | |||
120 | 121 | ||
121 | void minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir); | 122 | void minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir); |
122 | void minstrel_ht_remove_sta_debugfs(void *priv, void *priv_sta); | 123 | void minstrel_ht_remove_sta_debugfs(void *priv, void *priv_sta); |
124 | int minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate, | ||
125 | int prob_ewma); | ||
123 | 126 | ||
124 | #endif | 127 | #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 * | |||
19 | minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) | 19 | minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) |
20 | { | 20 | { |
21 | const struct mcs_group *mg; | 21 | const struct mcs_group *mg; |
22 | unsigned int j, tp, prob, eprob; | 22 | unsigned int j, tp_max, tp_avg, prob, eprob, tx_time; |
23 | char htmode = '2'; | 23 | char htmode = '2'; |
24 | char gimode = 'L'; | 24 | char gimode = 'L'; |
25 | u32 gflags; | 25 | u32 gflags; |
@@ -38,19 +38,26 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) | |||
38 | gimode = 'S'; | 38 | gimode = 'S'; |
39 | 39 | ||
40 | for (j = 0; j < MCS_GROUP_RATES; j++) { | 40 | for (j = 0; j < MCS_GROUP_RATES; j++) { |
41 | struct minstrel_rate_stats *mr = &mi->groups[i].rates[j]; | 41 | struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j]; |
42 | static const int bitrates[4] = { 10, 20, 55, 110 }; | 42 | static const int bitrates[4] = { 10, 20, 55, 110 }; |
43 | int idx = i * MCS_GROUP_RATES + j; | 43 | int idx = i * MCS_GROUP_RATES + j; |
44 | 44 | ||
45 | if (!(mi->groups[i].supported & BIT(j))) | 45 | if (!(mi->groups[i].supported & BIT(j))) |
46 | continue; | 46 | continue; |
47 | 47 | ||
48 | if (gflags & IEEE80211_TX_RC_MCS) | 48 | if (gflags & IEEE80211_TX_RC_MCS) { |
49 | p += sprintf(p, " HT%c0/%cGI ", htmode, gimode); | 49 | p += sprintf(p, "HT%c0 ", htmode); |
50 | else if (gflags & IEEE80211_TX_RC_VHT_MCS) | 50 | p += sprintf(p, "%cGI ", gimode); |
51 | p += sprintf(p, "VHT%c0/%cGI ", htmode, gimode); | 51 | p += sprintf(p, "%d ", mg->streams); |
52 | else | 52 | } else if (gflags & IEEE80211_TX_RC_VHT_MCS) { |
53 | p += sprintf(p, " CCK/%cP ", j < 4 ? 'L' : 'S'); | 53 | p += sprintf(p, "VHT%c0 ", htmode); |
54 | p += sprintf(p, "%cGI ", gimode); | ||
55 | p += sprintf(p, "%d ", mg->streams); | ||
56 | } else { | ||
57 | p += sprintf(p, "CCK "); | ||
58 | p += sprintf(p, "%cP ", j < 4 ? 'L' : 'S'); | ||
59 | p += sprintf(p, "1 "); | ||
60 | } | ||
54 | 61 | ||
55 | *(p++) = (idx == mi->max_tp_rate[0]) ? 'A' : ' '; | 62 | *(p++) = (idx == mi->max_tp_rate[0]) ? 'A' : ' '; |
56 | *(p++) = (idx == mi->max_tp_rate[1]) ? 'B' : ' '; | 63 | *(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) | |||
59 | *(p++) = (idx == mi->max_prob_rate) ? 'P' : ' '; | 66 | *(p++) = (idx == mi->max_prob_rate) ? 'P' : ' '; |
60 | 67 | ||
61 | if (gflags & IEEE80211_TX_RC_MCS) { | 68 | if (gflags & IEEE80211_TX_RC_MCS) { |
62 | p += sprintf(p, " MCS%-2u ", (mg->streams - 1) * 8 + j); | 69 | p += sprintf(p, " MCS%-2u", (mg->streams - 1) * 8 + j); |
63 | } else if (gflags & IEEE80211_TX_RC_VHT_MCS) { | 70 | } else if (gflags & IEEE80211_TX_RC_VHT_MCS) { |
64 | p += sprintf(p, " MCS%-1u/%1u", j, mg->streams); | 71 | p += sprintf(p, " MCS%-1u/%1u", j, mg->streams); |
65 | } else { | 72 | } else { |
66 | int r = bitrates[j % 4]; | 73 | int r = bitrates[j % 4]; |
67 | 74 | ||
68 | p += sprintf(p, " %2u.%1uM ", r / 10, r % 10); | 75 | p += sprintf(p, " %2u.%1uM", r / 10, r % 10); |
69 | } | 76 | } |
70 | 77 | ||
71 | tp = mr->cur_tp / 10; | 78 | p += sprintf(p, " %3u ", idx); |
72 | prob = MINSTREL_TRUNC(mr->cur_prob * 1000); | ||
73 | eprob = MINSTREL_TRUNC(mr->probability * 1000); | ||
74 | 79 | ||
75 | p += sprintf(p, " %4u.%1u %3u.%1u %3u.%1u " | 80 | /* tx_time[rate(i)] in usec */ |
76 | "%3u %4u(%4u) %9llu(%9llu)\n", | 81 | tx_time = DIV_ROUND_CLOSEST(mg->duration[j], 1000); |
77 | tp / 10, tp % 10, | 82 | p += sprintf(p, "%6u ", tx_time); |
83 | |||
84 | tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100)); | ||
85 | tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_ewma); | ||
86 | prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); | ||
87 | eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); | ||
88 | |||
89 | p += sprintf(p, "%4u.%1u %4u.%1u %3u.%1u %3u.%1u" | ||
90 | " %3u.%1u %3u %3u %-3u " | ||
91 | "%9llu %-9llu\n", | ||
92 | tp_max / 10, tp_max % 10, | ||
93 | tp_avg / 10, tp_avg % 10, | ||
78 | eprob / 10, eprob % 10, | 94 | eprob / 10, eprob % 10, |
95 | mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10, | ||
79 | prob / 10, prob % 10, | 96 | prob / 10, prob % 10, |
80 | mr->retry_count, | 97 | mrs->retry_count, |
81 | mr->last_success, | 98 | mrs->last_success, |
82 | mr->last_attempts, | 99 | mrs->last_attempts, |
83 | (unsigned long long)mr->succ_hist, | 100 | (unsigned long long)mrs->succ_hist, |
84 | (unsigned long long)mr->att_hist); | 101 | (unsigned long long)mrs->att_hist); |
85 | } | 102 | } |
86 | 103 | ||
87 | return p; | 104 | return p; |
@@ -94,8 +111,8 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file) | |||
94 | struct minstrel_ht_sta *mi = &msp->ht; | 111 | struct minstrel_ht_sta *mi = &msp->ht; |
95 | struct minstrel_debugfs_info *ms; | 112 | struct minstrel_debugfs_info *ms; |
96 | unsigned int i; | 113 | unsigned int i; |
97 | char *p; | ||
98 | int ret; | 114 | int ret; |
115 | char *p; | ||
99 | 116 | ||
100 | if (!msp->is_ht) { | 117 | if (!msp->is_ht) { |
101 | inode->i_private = &msp->legacy; | 118 | inode->i_private = &msp->legacy; |
@@ -110,8 +127,14 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file) | |||
110 | 127 | ||
111 | file->private_data = ms; | 128 | file->private_data = ms; |
112 | p = ms->buf; | 129 | p = ms->buf; |
113 | p += sprintf(p, " type rate tpt eprob *prob " | 130 | |
114 | "ret *ok(*cum) ok( cum)\n"); | 131 | p += sprintf(p, "\n"); |
132 | p += sprintf(p, " best ____________rate__________ " | ||
133 | "______statistics______ ________last_______ " | ||
134 | "______sum-of________\n"); | ||
135 | p += sprintf(p, "mode guard # rate [name idx airtime max_tp] " | ||
136 | "[ ø(tp) ø(prob) sd(prob)] [prob.|retry|suc|att] [#success | " | ||
137 | "#attempts]\n"); | ||
115 | 138 | ||
116 | p = minstrel_ht_stats_dump(mi, MINSTREL_CCK_GROUP, p); | 139 | p = minstrel_ht_stats_dump(mi, MINSTREL_CCK_GROUP, p); |
117 | for (i = 0; i < MINSTREL_CCK_GROUP; i++) | 140 | for (i = 0; i < MINSTREL_CCK_GROUP; i++) |
@@ -123,11 +146,10 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file) | |||
123 | "lookaround %d\n", | 146 | "lookaround %d\n", |
124 | max(0, (int) mi->total_packets - (int) mi->sample_packets), | 147 | max(0, (int) mi->total_packets - (int) mi->sample_packets), |
125 | mi->sample_packets); | 148 | mi->sample_packets); |
126 | p += sprintf(p, "Average A-MPDU length: %d.%d\n", | 149 | p += sprintf(p, "Average # of aggregated frames per A-MPDU: %d.%d\n", |
127 | MINSTREL_TRUNC(mi->avg_ampdu_len), | 150 | MINSTREL_TRUNC(mi->avg_ampdu_len), |
128 | MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10); | 151 | MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10); |
129 | ms->len = p - ms->buf; | 152 | ms->len = p - ms->buf; |
130 | |||
131 | WARN_ON(ms->len + sizeof(*ms) > 32768); | 153 | WARN_ON(ms->len + sizeof(*ms) > 32768); |
132 | 154 | ||
133 | return nonseekable_open(inode, file); | 155 | return nonseekable_open(inode, file); |
@@ -141,6 +163,143 @@ static const struct file_operations minstrel_ht_stat_fops = { | |||
141 | .llseek = no_llseek, | 163 | .llseek = no_llseek, |
142 | }; | 164 | }; |
143 | 165 | ||
166 | static char * | ||
167 | minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p) | ||
168 | { | ||
169 | const struct mcs_group *mg; | ||
170 | unsigned int j, tp_max, tp_avg, prob, eprob, tx_time; | ||
171 | char htmode = '2'; | ||
172 | char gimode = 'L'; | ||
173 | u32 gflags; | ||
174 | |||
175 | if (!mi->groups[i].supported) | ||
176 | return p; | ||
177 | |||
178 | mg = &minstrel_mcs_groups[i]; | ||
179 | gflags = mg->flags; | ||
180 | |||
181 | if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH) | ||
182 | htmode = '4'; | ||
183 | else if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH) | ||
184 | htmode = '8'; | ||
185 | if (gflags & IEEE80211_TX_RC_SHORT_GI) | ||
186 | gimode = 'S'; | ||
187 | |||
188 | for (j = 0; j < MCS_GROUP_RATES; j++) { | ||
189 | struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j]; | ||
190 | static const int bitrates[4] = { 10, 20, 55, 110 }; | ||
191 | int idx = i * MCS_GROUP_RATES + j; | ||
192 | |||
193 | if (!(mi->groups[i].supported & BIT(j))) | ||
194 | continue; | ||
195 | |||
196 | if (gflags & IEEE80211_TX_RC_MCS) { | ||
197 | p += sprintf(p, "HT%c0,", htmode); | ||
198 | p += sprintf(p, "%cGI,", gimode); | ||
199 | p += sprintf(p, "%d,", mg->streams); | ||
200 | } else if (gflags & IEEE80211_TX_RC_VHT_MCS) { | ||
201 | p += sprintf(p, "VHT%c0,", htmode); | ||
202 | p += sprintf(p, "%cGI,", gimode); | ||
203 | p += sprintf(p, "%d,", mg->streams); | ||
204 | } else { | ||
205 | p += sprintf(p, "CCK,"); | ||
206 | p += sprintf(p, "%cP,", j < 4 ? 'L' : 'S'); | ||
207 | p += sprintf(p, "1,"); | ||
208 | } | ||
209 | |||
210 | p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[0]) ? "A" : "")); | ||
211 | p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[1]) ? "B" : "")); | ||
212 | p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[2]) ? "C" : "")); | ||
213 | p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[3]) ? "D" : "")); | ||
214 | p += sprintf(p, "%s" ,((idx == mi->max_prob_rate) ? "P" : "")); | ||
215 | |||
216 | if (gflags & IEEE80211_TX_RC_MCS) { | ||
217 | p += sprintf(p, ",MCS%-2u,", (mg->streams - 1) * 8 + j); | ||
218 | } else if (gflags & IEEE80211_TX_RC_VHT_MCS) { | ||
219 | p += sprintf(p, ",MCS%-1u/%1u,", j, mg->streams); | ||
220 | } else { | ||
221 | int r = bitrates[j % 4]; | ||
222 | p += sprintf(p, ",%2u.%1uM,", r / 10, r % 10); | ||
223 | } | ||
224 | |||
225 | p += sprintf(p, "%u,", idx); | ||
226 | tx_time = DIV_ROUND_CLOSEST(mg->duration[j], 1000); | ||
227 | p += sprintf(p, "%u,", tx_time); | ||
228 | |||
229 | tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100)); | ||
230 | tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_ewma); | ||
231 | prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); | ||
232 | eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); | ||
233 | |||
234 | p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u.%u,%u,%u," | ||
235 | "%u,%llu,%llu,", | ||
236 | tp_max / 10, tp_max % 10, | ||
237 | tp_avg / 10, tp_avg % 10, | ||
238 | eprob / 10, eprob % 10, | ||
239 | mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10, | ||
240 | prob / 10, prob % 10, | ||
241 | mrs->retry_count, | ||
242 | mrs->last_success, | ||
243 | mrs->last_attempts, | ||
244 | (unsigned long long)mrs->succ_hist, | ||
245 | (unsigned long long)mrs->att_hist); | ||
246 | p += sprintf(p, "%d,%d,%d.%d\n", | ||
247 | max(0, (int) mi->total_packets - | ||
248 | (int) mi->sample_packets), | ||
249 | mi->sample_packets, | ||
250 | MINSTREL_TRUNC(mi->avg_ampdu_len), | ||
251 | MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10); | ||
252 | } | ||
253 | |||
254 | return p; | ||
255 | } | ||
256 | |||
257 | static int | ||
258 | minstrel_ht_stats_csv_open(struct inode *inode, struct file *file) | ||
259 | { | ||
260 | struct minstrel_ht_sta_priv *msp = inode->i_private; | ||
261 | struct minstrel_ht_sta *mi = &msp->ht; | ||
262 | struct minstrel_debugfs_info *ms; | ||
263 | unsigned int i; | ||
264 | int ret; | ||
265 | char *p; | ||
266 | |||
267 | if (!msp->is_ht) { | ||
268 | inode->i_private = &msp->legacy; | ||
269 | ret = minstrel_stats_csv_open(inode, file); | ||
270 | inode->i_private = msp; | ||
271 | return ret; | ||
272 | } | ||
273 | |||
274 | ms = kmalloc(32768, GFP_KERNEL); | ||
275 | |||
276 | if (!ms) | ||
277 | return -ENOMEM; | ||
278 | |||
279 | file->private_data = ms; | ||
280 | |||
281 | p = ms->buf; | ||
282 | |||
283 | p = minstrel_ht_stats_csv_dump(mi, MINSTREL_CCK_GROUP, p); | ||
284 | for (i = 0; i < MINSTREL_CCK_GROUP; i++) | ||
285 | p = minstrel_ht_stats_csv_dump(mi, i, p); | ||
286 | for (i++; i < ARRAY_SIZE(mi->groups); i++) | ||
287 | p = minstrel_ht_stats_csv_dump(mi, i, p); | ||
288 | |||
289 | ms->len = p - ms->buf; | ||
290 | WARN_ON(ms->len + sizeof(*ms) > 32768); | ||
291 | |||
292 | return nonseekable_open(inode, file); | ||
293 | } | ||
294 | |||
295 | static const struct file_operations minstrel_ht_stat_csv_fops = { | ||
296 | .owner = THIS_MODULE, | ||
297 | .open = minstrel_ht_stats_csv_open, | ||
298 | .read = minstrel_stats_read, | ||
299 | .release = minstrel_stats_release, | ||
300 | .llseek = no_llseek, | ||
301 | }; | ||
302 | |||
144 | void | 303 | void |
145 | minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir) | 304 | minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir) |
146 | { | 305 | { |
@@ -148,6 +307,8 @@ minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir) | |||
148 | 307 | ||
149 | msp->dbg_stats = debugfs_create_file("rc_stats", S_IRUGO, dir, msp, | 308 | msp->dbg_stats = debugfs_create_file("rc_stats", S_IRUGO, dir, msp, |
150 | &minstrel_ht_stat_fops); | 309 | &minstrel_ht_stat_fops); |
310 | msp->dbg_stats_csv = debugfs_create_file("rc_stats_csv", S_IRUGO, | ||
311 | dir, msp, &minstrel_ht_stat_csv_fops); | ||
151 | } | 312 | } |
152 | 313 | ||
153 | void | 314 | void |
@@ -156,4 +317,5 @@ minstrel_ht_remove_sta_debugfs(void *priv, void *priv_sta) | |||
156 | struct minstrel_ht_sta_priv *msp = priv_sta; | 317 | struct minstrel_ht_sta_priv *msp = priv_sta; |
157 | 318 | ||
158 | debugfs_remove(msp->dbg_stats); | 319 | debugfs_remove(msp->dbg_stats); |
320 | debugfs_remove(msp->dbg_stats_csv); | ||
159 | } | 321 | } |
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 2cd02278d4d4..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) | |||
1185 | struct ieee80211_sub_if_data *sdata = sta->sdata; | 1185 | struct ieee80211_sub_if_data *sdata = sta->sdata; |
1186 | struct ieee80211_local *local = sdata->local; | 1186 | struct ieee80211_local *local = sdata->local; |
1187 | struct ps_data *ps; | 1187 | struct ps_data *ps; |
1188 | int tid; | ||
1188 | 1189 | ||
1189 | if (sta->sdata->vif.type == NL80211_IFTYPE_AP || | 1190 | if (sta->sdata->vif.type == NL80211_IFTYPE_AP || |
1190 | sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) | 1191 | sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) |
@@ -1198,6 +1199,18 @@ static void sta_ps_start(struct sta_info *sta) | |||
1198 | drv_sta_notify(local, sdata, STA_NOTIFY_SLEEP, &sta->sta); | 1199 | drv_sta_notify(local, sdata, STA_NOTIFY_SLEEP, &sta->sta); |
1199 | ps_dbg(sdata, "STA %pM aid %d enters power save mode\n", | 1200 | ps_dbg(sdata, "STA %pM aid %d enters power save mode\n", |
1200 | sta->sta.addr, sta->sta.aid); | 1201 | sta->sta.addr, sta->sta.aid); |
1202 | |||
1203 | if (!sta->sta.txq[0]) | ||
1204 | return; | ||
1205 | |||
1206 | for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) { | ||
1207 | struct txq_info *txqi = to_txq_info(sta->sta.txq[tid]); | ||
1208 | |||
1209 | if (!skb_queue_len(&txqi->queue)) | ||
1210 | set_bit(tid, &sta->txq_buffered_tids); | ||
1211 | else | ||
1212 | clear_bit(tid, &sta->txq_buffered_tids); | ||
1213 | } | ||
1201 | } | 1214 | } |
1202 | 1215 | ||
1203 | static void sta_ps_end(struct sta_info *sta) | 1216 | static void sta_ps_end(struct sta_info *sta) |
@@ -3424,7 +3437,8 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, | |||
3424 | __le16 fc; | 3437 | __le16 fc; |
3425 | struct ieee80211_rx_data rx; | 3438 | struct ieee80211_rx_data rx; |
3426 | struct ieee80211_sub_if_data *prev; | 3439 | struct ieee80211_sub_if_data *prev; |
3427 | struct sta_info *sta, *tmp, *prev_sta; | 3440 | struct sta_info *sta, *prev_sta; |
3441 | struct rhash_head *tmp; | ||
3428 | int err = 0; | 3442 | int err = 0; |
3429 | 3443 | ||
3430 | fc = ((struct ieee80211_hdr *)skb->data)->frame_control; | 3444 | fc = ((struct ieee80211_hdr *)skb->data)->frame_control; |
@@ -3459,9 +3473,13 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, | |||
3459 | ieee80211_scan_rx(local, skb); | 3473 | ieee80211_scan_rx(local, skb); |
3460 | 3474 | ||
3461 | if (ieee80211_is_data(fc)) { | 3475 | if (ieee80211_is_data(fc)) { |
3476 | const struct bucket_table *tbl; | ||
3477 | |||
3462 | prev_sta = NULL; | 3478 | prev_sta = NULL; |
3463 | 3479 | ||
3464 | for_each_sta_info(local, hdr->addr2, sta, tmp) { | 3480 | tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash); |
3481 | |||
3482 | for_each_sta_info(local, tbl, hdr->addr2, sta, tmp) { | ||
3465 | if (!prev_sta) { | 3483 | if (!prev_sta) { |
3466 | prev_sta = sta; | 3484 | prev_sta = sta; |
3467 | continue; | 3485 | continue; |
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index aacaa1a85e63..12971b71d0fa 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c | |||
@@ -64,32 +64,20 @@ | |||
64 | * freed before they are done using it. | 64 | * freed before they are done using it. |
65 | */ | 65 | */ |
66 | 66 | ||
67 | static const struct rhashtable_params sta_rht_params = { | ||
68 | .nelem_hint = 3, /* start small */ | ||
69 | .head_offset = offsetof(struct sta_info, hash_node), | ||
70 | .key_offset = offsetof(struct sta_info, sta.addr), | ||
71 | .key_len = ETH_ALEN, | ||
72 | .hashfn = sta_addr_hash, | ||
73 | }; | ||
74 | |||
67 | /* Caller must hold local->sta_mtx */ | 75 | /* Caller must hold local->sta_mtx */ |
68 | static int sta_info_hash_del(struct ieee80211_local *local, | 76 | static int sta_info_hash_del(struct ieee80211_local *local, |
69 | struct sta_info *sta) | 77 | struct sta_info *sta) |
70 | { | 78 | { |
71 | struct sta_info *s; | 79 | return rhashtable_remove_fast(&local->sta_hash, &sta->hash_node, |
72 | 80 | sta_rht_params); | |
73 | s = rcu_dereference_protected(local->sta_hash[STA_HASH(sta->sta.addr)], | ||
74 | lockdep_is_held(&local->sta_mtx)); | ||
75 | if (!s) | ||
76 | return -ENOENT; | ||
77 | if (s == sta) { | ||
78 | rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], | ||
79 | s->hnext); | ||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | while (rcu_access_pointer(s->hnext) && | ||
84 | rcu_access_pointer(s->hnext) != sta) | ||
85 | s = rcu_dereference_protected(s->hnext, | ||
86 | lockdep_is_held(&local->sta_mtx)); | ||
87 | if (rcu_access_pointer(s->hnext)) { | ||
88 | rcu_assign_pointer(s->hnext, sta->hnext); | ||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | return -ENOENT; | ||
93 | } | 81 | } |
94 | 82 | ||
95 | static void __cleanup_single_sta(struct sta_info *sta) | 83 | static void __cleanup_single_sta(struct sta_info *sta) |
@@ -118,6 +106,16 @@ static void __cleanup_single_sta(struct sta_info *sta) | |||
118 | atomic_dec(&ps->num_sta_ps); | 106 | atomic_dec(&ps->num_sta_ps); |
119 | } | 107 | } |
120 | 108 | ||
109 | if (sta->sta.txq[0]) { | ||
110 | for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { | ||
111 | struct txq_info *txqi = to_txq_info(sta->sta.txq[i]); | ||
112 | int n = skb_queue_len(&txqi->queue); | ||
113 | |||
114 | ieee80211_purge_tx_queue(&local->hw, &txqi->queue); | ||
115 | atomic_sub(n, &sdata->txqs_len[txqi->txq.ac]); | ||
116 | } | ||
117 | } | ||
118 | |||
121 | for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { | 119 | for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { |
122 | local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]); | 120 | local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]); |
123 | ieee80211_purge_tx_queue(&local->hw, &sta->ps_tx_buf[ac]); | 121 | 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, | |||
159 | const u8 *addr) | 157 | const u8 *addr) |
160 | { | 158 | { |
161 | struct ieee80211_local *local = sdata->local; | 159 | struct ieee80211_local *local = sdata->local; |
162 | struct sta_info *sta; | ||
163 | 160 | ||
164 | sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)], | 161 | return rhashtable_lookup_fast(&local->sta_hash, addr, sta_rht_params); |
165 | lockdep_is_held(&local->sta_mtx)); | ||
166 | while (sta) { | ||
167 | if (sta->sdata == sdata && | ||
168 | ether_addr_equal(sta->sta.addr, addr)) | ||
169 | break; | ||
170 | sta = rcu_dereference_check(sta->hnext, | ||
171 | lockdep_is_held(&local->sta_mtx)); | ||
172 | } | ||
173 | return sta; | ||
174 | } | 162 | } |
175 | 163 | ||
176 | /* | 164 | /* |
@@ -182,18 +170,24 @@ struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata, | |||
182 | { | 170 | { |
183 | struct ieee80211_local *local = sdata->local; | 171 | struct ieee80211_local *local = sdata->local; |
184 | struct sta_info *sta; | 172 | struct sta_info *sta; |
173 | struct rhash_head *tmp; | ||
174 | const struct bucket_table *tbl; | ||
185 | 175 | ||
186 | sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)], | 176 | rcu_read_lock(); |
187 | lockdep_is_held(&local->sta_mtx)); | 177 | tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash); |
188 | while (sta) { | 178 | |
189 | if ((sta->sdata == sdata || | 179 | for_each_sta_info(local, tbl, addr, sta, tmp) { |
190 | (sta->sdata->bss && sta->sdata->bss == sdata->bss)) && | 180 | if (sta->sdata == sdata || |
191 | ether_addr_equal(sta->sta.addr, addr)) | 181 | (sta->sdata->bss && sta->sdata->bss == sdata->bss)) { |
192 | break; | 182 | rcu_read_unlock(); |
193 | sta = rcu_dereference_check(sta->hnext, | 183 | /* this is safe as the caller must already hold |
194 | lockdep_is_held(&local->sta_mtx)); | 184 | * another rcu read section or the mutex |
185 | */ | ||
186 | return sta; | ||
187 | } | ||
195 | } | 188 | } |
196 | return sta; | 189 | rcu_read_unlock(); |
190 | return NULL; | ||
197 | } | 191 | } |
198 | 192 | ||
199 | struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata, | 193 | struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata, |
@@ -234,6 +228,8 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta) | |||
234 | 228 | ||
235 | sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr); | 229 | sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr); |
236 | 230 | ||
231 | if (sta->sta.txq[0]) | ||
232 | kfree(to_txq_info(sta->sta.txq[0])); | ||
237 | kfree(rcu_dereference_raw(sta->sta.rates)); | 233 | kfree(rcu_dereference_raw(sta->sta.rates)); |
238 | kfree(sta); | 234 | kfree(sta); |
239 | } | 235 | } |
@@ -242,9 +238,8 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta) | |||
242 | static void sta_info_hash_add(struct ieee80211_local *local, | 238 | static void sta_info_hash_add(struct ieee80211_local *local, |
243 | struct sta_info *sta) | 239 | struct sta_info *sta) |
244 | { | 240 | { |
245 | lockdep_assert_held(&local->sta_mtx); | 241 | rhashtable_insert_fast(&local->sta_hash, &sta->hash_node, |
246 | sta->hnext = local->sta_hash[STA_HASH(sta->sta.addr)]; | 242 | sta_rht_params); |
247 | rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta); | ||
248 | } | 243 | } |
249 | 244 | ||
250 | static void sta_deliver_ps_frames(struct work_struct *wk) | 245 | static void sta_deliver_ps_frames(struct work_struct *wk) |
@@ -285,11 +280,12 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, | |||
285 | const u8 *addr, gfp_t gfp) | 280 | const u8 *addr, gfp_t gfp) |
286 | { | 281 | { |
287 | struct ieee80211_local *local = sdata->local; | 282 | struct ieee80211_local *local = sdata->local; |
283 | struct ieee80211_hw *hw = &local->hw; | ||
288 | struct sta_info *sta; | 284 | struct sta_info *sta; |
289 | struct timespec uptime; | 285 | struct timespec uptime; |
290 | int i; | 286 | int i; |
291 | 287 | ||
292 | sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp); | 288 | sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp); |
293 | if (!sta) | 289 | if (!sta) |
294 | return NULL; | 290 | return NULL; |
295 | 291 | ||
@@ -321,11 +317,25 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, | |||
321 | for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++) | 317 | for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++) |
322 | ewma_init(&sta->chain_signal_avg[i], 1024, 8); | 318 | ewma_init(&sta->chain_signal_avg[i], 1024, 8); |
323 | 319 | ||
324 | if (sta_prepare_rate_control(local, sta, gfp)) { | 320 | if (local->ops->wake_tx_queue) { |
325 | kfree(sta); | 321 | void *txq_data; |
326 | return NULL; | 322 | int size = sizeof(struct txq_info) + |
323 | ALIGN(hw->txq_data_size, sizeof(void *)); | ||
324 | |||
325 | txq_data = kcalloc(ARRAY_SIZE(sta->sta.txq), size, gfp); | ||
326 | if (!txq_data) | ||
327 | goto free; | ||
328 | |||
329 | for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { | ||
330 | struct txq_info *txq = txq_data + i * size; | ||
331 | |||
332 | ieee80211_init_tx_queue(sdata, sta, txq, i); | ||
333 | } | ||
327 | } | 334 | } |
328 | 335 | ||
336 | if (sta_prepare_rate_control(local, sta, gfp)) | ||
337 | goto free_txq; | ||
338 | |||
329 | for (i = 0; i < IEEE80211_NUM_TIDS; i++) { | 339 | for (i = 0; i < IEEE80211_NUM_TIDS; i++) { |
330 | /* | 340 | /* |
331 | * timer_to_tid must be initialized with identity mapping | 341 | * timer_to_tid must be initialized with identity mapping |
@@ -346,7 +356,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, | |||
346 | if (sdata->vif.type == NL80211_IFTYPE_AP || | 356 | if (sdata->vif.type == NL80211_IFTYPE_AP || |
347 | sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { | 357 | sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { |
348 | struct ieee80211_supported_band *sband = | 358 | struct ieee80211_supported_band *sband = |
349 | local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)]; | 359 | hw->wiphy->bands[ieee80211_get_sdata_band(sdata)]; |
350 | u8 smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> | 360 | u8 smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> |
351 | IEEE80211_HT_CAP_SM_PS_SHIFT; | 361 | IEEE80211_HT_CAP_SM_PS_SHIFT; |
352 | /* | 362 | /* |
@@ -371,6 +381,13 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, | |||
371 | sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr); | 381 | sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr); |
372 | 382 | ||
373 | return sta; | 383 | return sta; |
384 | |||
385 | free_txq: | ||
386 | if (sta->sta.txq[0]) | ||
387 | kfree(to_txq_info(sta->sta.txq[0])); | ||
388 | free: | ||
389 | kfree(sta); | ||
390 | return NULL; | ||
374 | } | 391 | } |
375 | 392 | ||
376 | static int sta_info_insert_check(struct sta_info *sta) | 393 | static int sta_info_insert_check(struct sta_info *sta) |
@@ -640,6 +657,8 @@ static void __sta_info_recalc_tim(struct sta_info *sta, bool ignore_pending) | |||
640 | 657 | ||
641 | indicate_tim |= | 658 | indicate_tim |= |
642 | sta->driver_buffered_tids & tids; | 659 | sta->driver_buffered_tids & tids; |
660 | indicate_tim |= | ||
661 | sta->txq_buffered_tids & tids; | ||
643 | } | 662 | } |
644 | 663 | ||
645 | done: | 664 | done: |
@@ -948,19 +967,32 @@ static void sta_info_cleanup(unsigned long data) | |||
948 | round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL)); | 967 | round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL)); |
949 | } | 968 | } |
950 | 969 | ||
951 | void sta_info_init(struct ieee80211_local *local) | 970 | u32 sta_addr_hash(const void *key, u32 length, u32 seed) |
971 | { | ||
972 | return jhash(key, ETH_ALEN, seed); | ||
973 | } | ||
974 | |||
975 | int sta_info_init(struct ieee80211_local *local) | ||
952 | { | 976 | { |
977 | int err; | ||
978 | |||
979 | err = rhashtable_init(&local->sta_hash, &sta_rht_params); | ||
980 | if (err) | ||
981 | return err; | ||
982 | |||
953 | spin_lock_init(&local->tim_lock); | 983 | spin_lock_init(&local->tim_lock); |
954 | mutex_init(&local->sta_mtx); | 984 | mutex_init(&local->sta_mtx); |
955 | INIT_LIST_HEAD(&local->sta_list); | 985 | INIT_LIST_HEAD(&local->sta_list); |
956 | 986 | ||
957 | setup_timer(&local->sta_cleanup, sta_info_cleanup, | 987 | setup_timer(&local->sta_cleanup, sta_info_cleanup, |
958 | (unsigned long)local); | 988 | (unsigned long)local); |
989 | return 0; | ||
959 | } | 990 | } |
960 | 991 | ||
961 | void sta_info_stop(struct ieee80211_local *local) | 992 | void sta_info_stop(struct ieee80211_local *local) |
962 | { | 993 | { |
963 | del_timer_sync(&local->sta_cleanup); | 994 | del_timer_sync(&local->sta_cleanup); |
995 | rhashtable_destroy(&local->sta_hash); | ||
964 | } | 996 | } |
965 | 997 | ||
966 | 998 | ||
@@ -1024,16 +1056,21 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, | |||
1024 | } | 1056 | } |
1025 | 1057 | ||
1026 | struct ieee80211_sta *ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw, | 1058 | struct ieee80211_sta *ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw, |
1027 | const u8 *addr, | 1059 | const u8 *addr, |
1028 | const u8 *localaddr) | 1060 | const u8 *localaddr) |
1029 | { | 1061 | { |
1030 | struct sta_info *sta, *nxt; | 1062 | struct ieee80211_local *local = hw_to_local(hw); |
1063 | struct sta_info *sta; | ||
1064 | struct rhash_head *tmp; | ||
1065 | const struct bucket_table *tbl; | ||
1066 | |||
1067 | tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash); | ||
1031 | 1068 | ||
1032 | /* | 1069 | /* |
1033 | * Just return a random station if localaddr is NULL | 1070 | * Just return a random station if localaddr is NULL |
1034 | * ... first in list. | 1071 | * ... first in list. |
1035 | */ | 1072 | */ |
1036 | for_each_sta_info(hw_to_local(hw), addr, sta, nxt) { | 1073 | for_each_sta_info(local, tbl, addr, sta, tmp) { |
1037 | if (localaddr && | 1074 | if (localaddr && |
1038 | !ether_addr_equal(sta->sdata->vif.addr, localaddr)) | 1075 | !ether_addr_equal(sta->sdata->vif.addr, localaddr)) |
1039 | continue; | 1076 | continue; |
@@ -1071,7 +1108,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) | |||
1071 | struct ieee80211_sub_if_data *sdata = sta->sdata; | 1108 | struct ieee80211_sub_if_data *sdata = sta->sdata; |
1072 | struct ieee80211_local *local = sdata->local; | 1109 | struct ieee80211_local *local = sdata->local; |
1073 | struct sk_buff_head pending; | 1110 | struct sk_buff_head pending; |
1074 | int filtered = 0, buffered = 0, ac; | 1111 | int filtered = 0, buffered = 0, ac, i; |
1075 | unsigned long flags; | 1112 | unsigned long flags; |
1076 | struct ps_data *ps; | 1113 | struct ps_data *ps; |
1077 | 1114 | ||
@@ -1090,10 +1127,22 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) | |||
1090 | 1127 | ||
1091 | BUILD_BUG_ON(BITS_TO_LONGS(IEEE80211_NUM_TIDS) > 1); | 1128 | BUILD_BUG_ON(BITS_TO_LONGS(IEEE80211_NUM_TIDS) > 1); |
1092 | sta->driver_buffered_tids = 0; | 1129 | sta->driver_buffered_tids = 0; |
1130 | sta->txq_buffered_tids = 0; | ||
1093 | 1131 | ||
1094 | if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS)) | 1132 | if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS)) |
1095 | drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta); | 1133 | drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta); |
1096 | 1134 | ||
1135 | if (sta->sta.txq[0]) { | ||
1136 | for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { | ||
1137 | struct txq_info *txqi = to_txq_info(sta->sta.txq[i]); | ||
1138 | |||
1139 | if (!skb_queue_len(&txqi->queue)) | ||
1140 | continue; | ||
1141 | |||
1142 | drv_wake_tx_queue(local, txqi); | ||
1143 | } | ||
1144 | } | ||
1145 | |||
1097 | skb_queue_head_init(&pending); | 1146 | skb_queue_head_init(&pending); |
1098 | 1147 | ||
1099 | /* sync with ieee80211_tx_h_unicast_ps_buf */ | 1148 | /* sync with ieee80211_tx_h_unicast_ps_buf */ |
@@ -1275,8 +1324,10 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, | |||
1275 | /* if we already have frames from software, then we can't also | 1324 | /* if we already have frames from software, then we can't also |
1276 | * release from hardware queues | 1325 | * release from hardware queues |
1277 | */ | 1326 | */ |
1278 | if (skb_queue_empty(&frames)) | 1327 | if (skb_queue_empty(&frames)) { |
1279 | driver_release_tids |= sta->driver_buffered_tids & tids; | 1328 | driver_release_tids |= sta->driver_buffered_tids & tids; |
1329 | driver_release_tids |= sta->txq_buffered_tids & tids; | ||
1330 | } | ||
1280 | 1331 | ||
1281 | if (driver_release_tids) { | 1332 | if (driver_release_tids) { |
1282 | /* If the driver has data on more than one TID then | 1333 | /* If the driver has data on more than one TID then |
@@ -1447,6 +1498,9 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, | |||
1447 | 1498 | ||
1448 | sta_info_recalc_tim(sta); | 1499 | sta_info_recalc_tim(sta); |
1449 | } else { | 1500 | } else { |
1501 | unsigned long tids = sta->txq_buffered_tids & driver_release_tids; | ||
1502 | int tid; | ||
1503 | |||
1450 | /* | 1504 | /* |
1451 | * We need to release a frame that is buffered somewhere in the | 1505 | * We need to release a frame that is buffered somewhere in the |
1452 | * driver ... it'll have to handle that. | 1506 | * driver ... it'll have to handle that. |
@@ -1466,8 +1520,22 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, | |||
1466 | * that the TID(s) became empty before returning here from the | 1520 | * that the TID(s) became empty before returning here from the |
1467 | * release function. | 1521 | * release function. |
1468 | * Either way, however, when the driver tells us that the TID(s) | 1522 | * Either way, however, when the driver tells us that the TID(s) |
1469 | * became empty we'll do the TIM recalculation. | 1523 | * became empty or we find that a txq became empty, we'll do the |
1524 | * TIM recalculation. | ||
1470 | */ | 1525 | */ |
1526 | |||
1527 | if (!sta->sta.txq[0]) | ||
1528 | return; | ||
1529 | |||
1530 | for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) { | ||
1531 | struct txq_info *txqi = to_txq_info(sta->sta.txq[tid]); | ||
1532 | |||
1533 | if (!(tids & BIT(tid)) || skb_queue_len(&txqi->queue)) | ||
1534 | continue; | ||
1535 | |||
1536 | sta_info_recalc_tim(sta); | ||
1537 | break; | ||
1538 | } | ||
1471 | } | 1539 | } |
1472 | } | 1540 | } |
1473 | 1541 | ||
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 7e2fa4018d41..5c164fb3f6c5 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/workqueue.h> | 16 | #include <linux/workqueue.h> |
17 | #include <linux/average.h> | 17 | #include <linux/average.h> |
18 | #include <linux/etherdevice.h> | 18 | #include <linux/etherdevice.h> |
19 | #include <linux/rhashtable.h> | ||
19 | #include "key.h" | 20 | #include "key.h" |
20 | 21 | ||
21 | /** | 22 | /** |
@@ -248,7 +249,7 @@ struct sta_ampdu_mlme { | |||
248 | * | 249 | * |
249 | * @list: global linked list entry | 250 | * @list: global linked list entry |
250 | * @free_list: list entry for keeping track of stations to free | 251 | * @free_list: list entry for keeping track of stations to free |
251 | * @hnext: hash table linked list pointer | 252 | * @hash_node: hash node for rhashtable |
252 | * @local: pointer to the global information | 253 | * @local: pointer to the global information |
253 | * @sdata: virtual interface this station belongs to | 254 | * @sdata: virtual interface this station belongs to |
254 | * @ptk: peer keys negotiated with this station, if any | 255 | * @ptk: peer keys negotiated with this station, if any |
@@ -276,6 +277,7 @@ struct sta_ampdu_mlme { | |||
276 | * entered power saving state, these are also delivered to | 277 | * entered power saving state, these are also delivered to |
277 | * the station when it leaves powersave or polls for frames | 278 | * the station when it leaves powersave or polls for frames |
278 | * @driver_buffered_tids: bitmap of TIDs the driver has data buffered on | 279 | * @driver_buffered_tids: bitmap of TIDs the driver has data buffered on |
280 | * @txq_buffered_tids: bitmap of TIDs that mac80211 has txq data buffered on | ||
279 | * @rx_packets: Number of MSDUs received from this STA | 281 | * @rx_packets: Number of MSDUs received from this STA |
280 | * @rx_bytes: Number of bytes received from this STA | 282 | * @rx_bytes: Number of bytes received from this STA |
281 | * @last_rx: time (in jiffies) when last frame was received from this STA | 283 | * @last_rx: time (in jiffies) when last frame was received from this STA |
@@ -341,7 +343,7 @@ struct sta_info { | |||
341 | /* General information, mostly static */ | 343 | /* General information, mostly static */ |
342 | struct list_head list, free_list; | 344 | struct list_head list, free_list; |
343 | struct rcu_head rcu_head; | 345 | struct rcu_head rcu_head; |
344 | struct sta_info __rcu *hnext; | 346 | struct rhash_head hash_node; |
345 | struct ieee80211_local *local; | 347 | struct ieee80211_local *local; |
346 | struct ieee80211_sub_if_data *sdata; | 348 | struct ieee80211_sub_if_data *sdata; |
347 | struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS]; | 349 | struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS]; |
@@ -370,6 +372,7 @@ struct sta_info { | |||
370 | struct sk_buff_head ps_tx_buf[IEEE80211_NUM_ACS]; | 372 | struct sk_buff_head ps_tx_buf[IEEE80211_NUM_ACS]; |
371 | struct sk_buff_head tx_filtered[IEEE80211_NUM_ACS]; | 373 | struct sk_buff_head tx_filtered[IEEE80211_NUM_ACS]; |
372 | unsigned long driver_buffered_tids; | 374 | unsigned long driver_buffered_tids; |
375 | unsigned long txq_buffered_tids; | ||
373 | 376 | ||
374 | /* Updated from RX path only, no locking requirements */ | 377 | /* Updated from RX path only, no locking requirements */ |
375 | unsigned long rx_packets; | 378 | unsigned long rx_packets; |
@@ -537,10 +540,6 @@ rcu_dereference_protected_tid_tx(struct sta_info *sta, int tid) | |||
537 | lockdep_is_held(&sta->ampdu_mlme.mtx)); | 540 | lockdep_is_held(&sta->ampdu_mlme.mtx)); |
538 | } | 541 | } |
539 | 542 | ||
540 | #define STA_HASH_SIZE 256 | ||
541 | #define STA_HASH(sta) (sta[5]) | ||
542 | |||
543 | |||
544 | /* Maximum number of frames to buffer per power saving station per AC */ | 543 | /* Maximum number of frames to buffer per power saving station per AC */ |
545 | #define STA_MAX_TX_BUFFER 64 | 544 | #define STA_MAX_TX_BUFFER 64 |
546 | 545 | ||
@@ -561,26 +560,15 @@ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, | |||
561 | struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata, | 560 | struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata, |
562 | const u8 *addr); | 561 | const u8 *addr); |
563 | 562 | ||
564 | static inline | 563 | u32 sta_addr_hash(const void *key, u32 length, u32 seed); |
565 | void for_each_sta_info_type_check(struct ieee80211_local *local, | 564 | |
566 | const u8 *addr, | 565 | #define _sta_bucket_idx(_tbl, _a) \ |
567 | struct sta_info *sta, | 566 | rht_bucket_index(_tbl, sta_addr_hash(_a, ETH_ALEN, (_tbl)->hash_rnd)) |
568 | struct sta_info *nxt) | ||
569 | { | ||
570 | } | ||
571 | 567 | ||
572 | #define for_each_sta_info(local, _addr, _sta, nxt) \ | 568 | #define for_each_sta_info(local, tbl, _addr, _sta, _tmp) \ |
573 | for ( /* initialise loop */ \ | 569 | rht_for_each_entry_rcu(_sta, _tmp, tbl, \ |
574 | _sta = rcu_dereference(local->sta_hash[STA_HASH(_addr)]),\ | 570 | _sta_bucket_idx(tbl, _addr), \ |
575 | nxt = _sta ? rcu_dereference(_sta->hnext) : NULL; \ | 571 | hash_node) \ |
576 | /* typecheck */ \ | ||
577 | for_each_sta_info_type_check(local, (_addr), _sta, nxt),\ | ||
578 | /* continue condition */ \ | ||
579 | _sta; \ | ||
580 | /* advance loop */ \ | ||
581 | _sta = nxt, \ | ||
582 | nxt = _sta ? rcu_dereference(_sta->hnext) : NULL \ | ||
583 | ) \ | ||
584 | /* compare address and run code only if it matches */ \ | 572 | /* compare address and run code only if it matches */ \ |
585 | if (ether_addr_equal(_sta->sta.addr, (_addr))) | 573 | if (ether_addr_equal(_sta->sta.addr, (_addr))) |
586 | 574 | ||
@@ -617,7 +605,7 @@ int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata, | |||
617 | 605 | ||
618 | void sta_info_recalc_tim(struct sta_info *sta); | 606 | void sta_info_recalc_tim(struct sta_info *sta); |
619 | 607 | ||
620 | void sta_info_init(struct ieee80211_local *local); | 608 | int sta_info_init(struct ieee80211_local *local); |
621 | void sta_info_stop(struct ieee80211_local *local); | 609 | void sta_info_stop(struct ieee80211_local *local); |
622 | 610 | ||
623 | /** | 611 | /** |
diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 2c51742428d5..005fdbe39a8b 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c | |||
@@ -654,7 +654,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
654 | struct ieee80211_supported_band *sband; | 654 | struct ieee80211_supported_band *sband; |
655 | struct ieee80211_sub_if_data *sdata; | 655 | struct ieee80211_sub_if_data *sdata; |
656 | struct net_device *prev_dev = NULL; | 656 | struct net_device *prev_dev = NULL; |
657 | struct sta_info *sta, *tmp; | 657 | struct sta_info *sta; |
658 | struct rhash_head *tmp; | ||
658 | int retry_count; | 659 | int retry_count; |
659 | int rates_idx; | 660 | int rates_idx; |
660 | bool send_to_cooked; | 661 | bool send_to_cooked; |
@@ -663,6 +664,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
663 | int rtap_len; | 664 | int rtap_len; |
664 | int shift = 0; | 665 | int shift = 0; |
665 | int tid = IEEE80211_NUM_TIDS; | 666 | int tid = IEEE80211_NUM_TIDS; |
667 | const struct bucket_table *tbl; | ||
666 | 668 | ||
667 | rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count); | 669 | rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count); |
668 | 670 | ||
@@ -671,7 +673,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
671 | sband = local->hw.wiphy->bands[info->band]; | 673 | sband = local->hw.wiphy->bands[info->band]; |
672 | fc = hdr->frame_control; | 674 | fc = hdr->frame_control; |
673 | 675 | ||
674 | for_each_sta_info(local, hdr->addr1, sta, tmp) { | 676 | tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash); |
677 | |||
678 | for_each_sta_info(local, tbl, hdr->addr1, sta, tmp) { | ||
675 | /* skip wrong virtual interface */ | 679 | /* skip wrong virtual interface */ |
676 | if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr)) | 680 | if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr)) |
677 | continue; | 681 | continue; |
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index e9e462b349e5..790bd45081c4 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h | |||
@@ -2312,6 +2312,37 @@ TRACE_EVENT(drv_tdls_recv_channel_switch, | |||
2312 | ) | 2312 | ) |
2313 | ); | 2313 | ); |
2314 | 2314 | ||
2315 | TRACE_EVENT(drv_wake_tx_queue, | ||
2316 | TP_PROTO(struct ieee80211_local *local, | ||
2317 | struct ieee80211_sub_if_data *sdata, | ||
2318 | struct txq_info *txq), | ||
2319 | |||
2320 | TP_ARGS(local, sdata, txq), | ||
2321 | |||
2322 | TP_STRUCT__entry( | ||
2323 | LOCAL_ENTRY | ||
2324 | VIF_ENTRY | ||
2325 | STA_ENTRY | ||
2326 | __field(u8, ac) | ||
2327 | __field(u8, tid) | ||
2328 | ), | ||
2329 | |||
2330 | TP_fast_assign( | ||
2331 | struct ieee80211_sta *sta = txq->txq.sta; | ||
2332 | |||
2333 | LOCAL_ASSIGN; | ||
2334 | VIF_ASSIGN; | ||
2335 | STA_ASSIGN; | ||
2336 | __entry->ac = txq->txq.ac; | ||
2337 | __entry->tid = txq->txq.tid; | ||
2338 | ), | ||
2339 | |||
2340 | TP_printk( | ||
2341 | LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " ac:%d tid:%d", | ||
2342 | LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->ac, __entry->tid | ||
2343 | ) | ||
2344 | ); | ||
2345 | |||
2315 | #ifdef CONFIG_MAC80211_MESSAGE_TRACING | 2346 | #ifdef CONFIG_MAC80211_MESSAGE_TRACING |
2316 | #undef TRACE_SYSTEM | 2347 | #undef TRACE_SYSTEM |
2317 | #define TRACE_SYSTEM mac80211_msg | 2348 | #define TRACE_SYSTEM mac80211_msg |
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 9f7fb4eec37b..667111ee6a20 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c | |||
@@ -767,12 +767,22 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) | |||
767 | return TX_CONTINUE; | 767 | return TX_CONTINUE; |
768 | } | 768 | } |
769 | 769 | ||
770 | static __le16 ieee80211_tx_next_seq(struct sta_info *sta, int tid) | ||
771 | { | ||
772 | u16 *seq = &sta->tid_seq[tid]; | ||
773 | __le16 ret = cpu_to_le16(*seq); | ||
774 | |||
775 | /* Increase the sequence number. */ | ||
776 | *seq = (*seq + 0x10) & IEEE80211_SCTL_SEQ; | ||
777 | |||
778 | return ret; | ||
779 | } | ||
780 | |||
770 | static ieee80211_tx_result debug_noinline | 781 | static ieee80211_tx_result debug_noinline |
771 | ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx) | 782 | ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx) |
772 | { | 783 | { |
773 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); | 784 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); |
774 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; | 785 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; |
775 | u16 *seq; | ||
776 | u8 *qc; | 786 | u8 *qc; |
777 | int tid; | 787 | int tid; |
778 | 788 | ||
@@ -823,13 +833,10 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx) | |||
823 | 833 | ||
824 | qc = ieee80211_get_qos_ctl(hdr); | 834 | qc = ieee80211_get_qos_ctl(hdr); |
825 | tid = *qc & IEEE80211_QOS_CTL_TID_MASK; | 835 | tid = *qc & IEEE80211_QOS_CTL_TID_MASK; |
826 | seq = &tx->sta->tid_seq[tid]; | ||
827 | tx->sta->tx_msdu[tid]++; | 836 | tx->sta->tx_msdu[tid]++; |
828 | 837 | ||
829 | hdr->seq_ctrl = cpu_to_le16(*seq); | 838 | if (!tx->sta->sta.txq[0]) |
830 | 839 | hdr->seq_ctrl = ieee80211_tx_next_seq(tx->sta, tid); | |
831 | /* Increase the sequence number. */ | ||
832 | *seq = (*seq + 0x10) & IEEE80211_SCTL_SEQ; | ||
833 | 840 | ||
834 | return TX_CONTINUE; | 841 | return TX_CONTINUE; |
835 | } | 842 | } |
@@ -1070,7 +1077,7 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx, | |||
1070 | * nothing -- this aggregation session is being started | 1077 | * nothing -- this aggregation session is being started |
1071 | * but that might still fail with the driver | 1078 | * but that might still fail with the driver |
1072 | */ | 1079 | */ |
1073 | } else { | 1080 | } else if (!tx->sta->sta.txq[tid]) { |
1074 | spin_lock(&tx->sta->lock); | 1081 | spin_lock(&tx->sta->lock); |
1075 | /* | 1082 | /* |
1076 | * Need to re-check now, because we may get here | 1083 | * Need to re-check now, because we may get here |
@@ -1211,13 +1218,102 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, | |||
1211 | return TX_CONTINUE; | 1218 | return TX_CONTINUE; |
1212 | } | 1219 | } |
1213 | 1220 | ||
1221 | static void ieee80211_drv_tx(struct ieee80211_local *local, | ||
1222 | struct ieee80211_vif *vif, | ||
1223 | struct ieee80211_sta *pubsta, | ||
1224 | struct sk_buff *skb) | ||
1225 | { | ||
1226 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | ||
1227 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); | ||
1228 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||
1229 | struct ieee80211_tx_control control = { | ||
1230 | .sta = pubsta, | ||
1231 | }; | ||
1232 | struct ieee80211_txq *txq = NULL; | ||
1233 | struct txq_info *txqi; | ||
1234 | u8 ac; | ||
1235 | |||
1236 | if (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE) | ||
1237 | goto tx_normal; | ||
1238 | |||
1239 | if (!ieee80211_is_data(hdr->frame_control)) | ||
1240 | goto tx_normal; | ||
1241 | |||
1242 | if (pubsta) { | ||
1243 | u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; | ||
1244 | |||
1245 | txq = pubsta->txq[tid]; | ||
1246 | } else if (vif) { | ||
1247 | txq = vif->txq; | ||
1248 | } | ||
1249 | |||
1250 | if (!txq) | ||
1251 | goto tx_normal; | ||
1252 | |||
1253 | ac = txq->ac; | ||
1254 | txqi = to_txq_info(txq); | ||
1255 | atomic_inc(&sdata->txqs_len[ac]); | ||
1256 | if (atomic_read(&sdata->txqs_len[ac]) >= local->hw.txq_ac_max_pending) | ||
1257 | netif_stop_subqueue(sdata->dev, ac); | ||
1258 | |||
1259 | skb_queue_tail(&txqi->queue, skb); | ||
1260 | drv_wake_tx_queue(local, txqi); | ||
1261 | |||
1262 | return; | ||
1263 | |||
1264 | tx_normal: | ||
1265 | drv_tx(local, &control, skb); | ||
1266 | } | ||
1267 | |||
1268 | struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw, | ||
1269 | struct ieee80211_txq *txq) | ||
1270 | { | ||
1271 | struct ieee80211_local *local = hw_to_local(hw); | ||
1272 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->vif); | ||
1273 | struct txq_info *txqi = container_of(txq, struct txq_info, txq); | ||
1274 | struct ieee80211_hdr *hdr; | ||
1275 | struct sk_buff *skb = NULL; | ||
1276 | u8 ac = txq->ac; | ||
1277 | |||
1278 | spin_lock_bh(&txqi->queue.lock); | ||
1279 | |||
1280 | if (test_bit(IEEE80211_TXQ_STOP, &txqi->flags)) | ||
1281 | goto out; | ||
1282 | |||
1283 | skb = __skb_dequeue(&txqi->queue); | ||
1284 | if (!skb) | ||
1285 | goto out; | ||
1286 | |||
1287 | atomic_dec(&sdata->txqs_len[ac]); | ||
1288 | if (__netif_subqueue_stopped(sdata->dev, ac)) | ||
1289 | ieee80211_propagate_queue_wake(local, sdata->vif.hw_queue[ac]); | ||
1290 | |||
1291 | hdr = (struct ieee80211_hdr *)skb->data; | ||
1292 | if (txq->sta && ieee80211_is_data_qos(hdr->frame_control)) { | ||
1293 | struct sta_info *sta = container_of(txq->sta, struct sta_info, | ||
1294 | sta); | ||
1295 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||
1296 | |||
1297 | hdr->seq_ctrl = ieee80211_tx_next_seq(sta, txq->tid); | ||
1298 | if (test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags)) | ||
1299 | info->flags |= IEEE80211_TX_CTL_AMPDU; | ||
1300 | else | ||
1301 | info->flags &= ~IEEE80211_TX_CTL_AMPDU; | ||
1302 | } | ||
1303 | |||
1304 | out: | ||
1305 | spin_unlock_bh(&txqi->queue.lock); | ||
1306 | |||
1307 | return skb; | ||
1308 | } | ||
1309 | EXPORT_SYMBOL(ieee80211_tx_dequeue); | ||
1310 | |||
1214 | static bool ieee80211_tx_frags(struct ieee80211_local *local, | 1311 | static bool ieee80211_tx_frags(struct ieee80211_local *local, |
1215 | struct ieee80211_vif *vif, | 1312 | struct ieee80211_vif *vif, |
1216 | struct ieee80211_sta *sta, | 1313 | struct ieee80211_sta *sta, |
1217 | struct sk_buff_head *skbs, | 1314 | struct sk_buff_head *skbs, |
1218 | bool txpending) | 1315 | bool txpending) |
1219 | { | 1316 | { |
1220 | struct ieee80211_tx_control control; | ||
1221 | struct sk_buff *skb, *tmp; | 1317 | struct sk_buff *skb, *tmp; |
1222 | unsigned long flags; | 1318 | unsigned long flags; |
1223 | 1319 | ||
@@ -1275,10 +1371,9 @@ static bool ieee80211_tx_frags(struct ieee80211_local *local, | |||
1275 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); | 1371 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); |
1276 | 1372 | ||
1277 | info->control.vif = vif; | 1373 | info->control.vif = vif; |
1278 | control.sta = sta; | ||
1279 | 1374 | ||
1280 | __skb_unlink(skb, skbs); | 1375 | __skb_unlink(skb, skbs); |
1281 | drv_tx(local, &control, skb); | 1376 | ieee80211_drv_tx(local, vif, sta, skb); |
1282 | } | 1377 | } |
1283 | 1378 | ||
1284 | return true; | 1379 | return true; |
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index d1742a7d9ea4..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) | |||
308 | for (ac = 0; ac < n_acs; ac++) { | 308 | for (ac = 0; ac < n_acs; ac++) { |
309 | int ac_queue = sdata->vif.hw_queue[ac]; | 309 | int ac_queue = sdata->vif.hw_queue[ac]; |
310 | 310 | ||
311 | if (local->ops->wake_tx_queue && | ||
312 | (atomic_read(&sdata->txqs_len[ac]) > | ||
313 | local->hw.txq_ac_max_pending)) | ||
314 | continue; | ||
315 | |||
311 | if (ac_queue == queue || | 316 | if (ac_queue == queue || |
312 | (sdata->vif.cab_queue == queue && | 317 | (sdata->vif.cab_queue == queue && |
313 | local->queue_stop_reasons[ac_queue] == 0 && | 318 | local->queue_stop_reasons[ac_queue] == 0 && |
@@ -2189,46 +2194,6 @@ void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata) | |||
2189 | mutex_unlock(&local->chanctx_mtx); | 2194 | mutex_unlock(&local->chanctx_mtx); |
2190 | } | 2195 | } |
2191 | 2196 | ||
2192 | static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) | ||
2193 | { | ||
2194 | int i; | ||
2195 | |||
2196 | for (i = 0; i < n_ids; i++) | ||
2197 | if (ids[i] == id) | ||
2198 | return true; | ||
2199 | return false; | ||
2200 | } | ||
2201 | |||
2202 | size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen, | ||
2203 | const u8 *ids, int n_ids, | ||
2204 | const u8 *after_ric, int n_after_ric, | ||
2205 | size_t offset) | ||
2206 | { | ||
2207 | size_t pos = offset; | ||
2208 | |||
2209 | while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) { | ||
2210 | if (ies[pos] == WLAN_EID_RIC_DATA && n_after_ric) { | ||
2211 | pos += 2 + ies[pos + 1]; | ||
2212 | |||
2213 | while (pos < ielen && | ||
2214 | !ieee80211_id_in_list(after_ric, n_after_ric, | ||
2215 | ies[pos])) | ||
2216 | pos += 2 + ies[pos + 1]; | ||
2217 | } else { | ||
2218 | pos += 2 + ies[pos + 1]; | ||
2219 | } | ||
2220 | } | ||
2221 | |||
2222 | return pos; | ||
2223 | } | ||
2224 | |||
2225 | size_t ieee80211_ie_split(const u8 *ies, size_t ielen, | ||
2226 | const u8 *ids, int n_ids, size_t offset) | ||
2227 | { | ||
2228 | return ieee80211_ie_split_ric(ies, ielen, ids, n_ids, NULL, 0, offset); | ||
2229 | } | ||
2230 | EXPORT_SYMBOL(ieee80211_ie_split); | ||
2231 | |||
2232 | size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset) | 2197 | size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset) |
2233 | { | 2198 | { |
2234 | size_t pos = offset; | 2199 | size_t pos = offset; |
@@ -3352,3 +3317,20 @@ u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo) | |||
3352 | 3317 | ||
3353 | return buf; | 3318 | return buf; |
3354 | } | 3319 | } |
3320 | |||
3321 | void ieee80211_init_tx_queue(struct ieee80211_sub_if_data *sdata, | ||
3322 | struct sta_info *sta, | ||
3323 | struct txq_info *txqi, int tid) | ||
3324 | { | ||
3325 | skb_queue_head_init(&txqi->queue); | ||
3326 | txqi->txq.vif = &sdata->vif; | ||
3327 | |||
3328 | if (sta) { | ||
3329 | txqi->txq.sta = &sta->sta; | ||
3330 | sta->sta.txq[tid] = &txqi->txq; | ||
3331 | txqi->txq.ac = ieee802_1d_to_ac[tid & 7]; | ||
3332 | } else { | ||
3333 | sdata->vif.txq = &txqi->txq; | ||
3334 | txqi->txq.ac = IEEE80211_AC_BE; | ||
3335 | } | ||
3336 | } | ||
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index b13dfb4ff001..4f5543dd2524 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig | |||
@@ -175,7 +175,7 @@ config CFG80211_INTERNAL_REGDB | |||
175 | Most distributions have a CRDA package. So if unsure, say N. | 175 | Most distributions have a CRDA package. So if unsure, say N. |
176 | 176 | ||
177 | config CFG80211_WEXT | 177 | config CFG80211_WEXT |
178 | bool "cfg80211 wireless extensions compatibility" | 178 | bool "cfg80211 wireless extensions compatibility" if !CFG80211_WEXT_EXPORT |
179 | depends on CFG80211 | 179 | depends on CFG80211 |
180 | select WEXT_CORE | 180 | select WEXT_CORE |
181 | default y if CFG80211_WEXT_EXPORT | 181 | default y if CFG80211_WEXT_EXPORT |
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 6dd1ab3b10ea..dd78445c7d50 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -5664,7 +5664,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
5664 | } | 5664 | } |
5665 | } | 5665 | } |
5666 | 5666 | ||
5667 | r = set_regdom(rd); | 5667 | r = set_regdom(rd, REGD_SOURCE_CRDA); |
5668 | /* set_regdom took ownership */ | 5668 | /* set_regdom took ownership */ |
5669 | rd = NULL; | 5669 | rd = NULL; |
5670 | 5670 | ||
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index be5f81caa488..0e347f888fe9 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -135,6 +135,11 @@ static spinlock_t reg_indoor_lock; | |||
135 | /* Used to track the userspace process controlling the indoor setting */ | 135 | /* Used to track the userspace process controlling the indoor setting */ |
136 | static u32 reg_is_indoor_portid; | 136 | static u32 reg_is_indoor_portid; |
137 | 137 | ||
138 | /* Max number of consecutive attempts to communicate with CRDA */ | ||
139 | #define REG_MAX_CRDA_TIMEOUTS 10 | ||
140 | |||
141 | static u32 reg_crda_timeouts; | ||
142 | |||
138 | static const struct ieee80211_regdomain *get_cfg80211_regdom(void) | 143 | static const struct ieee80211_regdomain *get_cfg80211_regdom(void) |
139 | { | 144 | { |
140 | return rtnl_dereference(cfg80211_regdomain); | 145 | return rtnl_dereference(cfg80211_regdomain); |
@@ -485,7 +490,7 @@ static void reg_regdb_search(struct work_struct *work) | |||
485 | mutex_unlock(®_regdb_search_mutex); | 490 | mutex_unlock(®_regdb_search_mutex); |
486 | 491 | ||
487 | if (!IS_ERR_OR_NULL(regdom)) | 492 | if (!IS_ERR_OR_NULL(regdom)) |
488 | set_regdom(regdom); | 493 | set_regdom(regdom, REGD_SOURCE_INTERNAL_DB); |
489 | 494 | ||
490 | rtnl_unlock(); | 495 | rtnl_unlock(); |
491 | } | 496 | } |
@@ -535,15 +540,20 @@ static int call_crda(const char *alpha2) | |||
535 | snprintf(country, sizeof(country), "COUNTRY=%c%c", | 540 | snprintf(country, sizeof(country), "COUNTRY=%c%c", |
536 | alpha2[0], alpha2[1]); | 541 | alpha2[0], alpha2[1]); |
537 | 542 | ||
543 | /* query internal regulatory database (if it exists) */ | ||
544 | reg_regdb_query(alpha2); | ||
545 | |||
546 | if (reg_crda_timeouts > REG_MAX_CRDA_TIMEOUTS) { | ||
547 | pr_info("Exceeded CRDA call max attempts. Not calling CRDA\n"); | ||
548 | return -EINVAL; | ||
549 | } | ||
550 | |||
538 | if (!is_world_regdom((char *) alpha2)) | 551 | if (!is_world_regdom((char *) alpha2)) |
539 | pr_info("Calling CRDA for country: %c%c\n", | 552 | pr_info("Calling CRDA for country: %c%c\n", |
540 | alpha2[0], alpha2[1]); | 553 | alpha2[0], alpha2[1]); |
541 | else | 554 | else |
542 | pr_info("Calling CRDA to update world regulatory domain\n"); | 555 | pr_info("Calling CRDA to update world regulatory domain\n"); |
543 | 556 | ||
544 | /* query internal regulatory database (if it exists) */ | ||
545 | reg_regdb_query(alpha2); | ||
546 | |||
547 | return kobject_uevent_env(®_pdev->dev.kobj, KOBJ_CHANGE, env); | 557 | return kobject_uevent_env(®_pdev->dev.kobj, KOBJ_CHANGE, env); |
548 | } | 558 | } |
549 | 559 | ||
@@ -2293,6 +2303,9 @@ int regulatory_hint_user(const char *alpha2, | |||
2293 | request->initiator = NL80211_REGDOM_SET_BY_USER; | 2303 | request->initiator = NL80211_REGDOM_SET_BY_USER; |
2294 | request->user_reg_hint_type = user_reg_hint_type; | 2304 | request->user_reg_hint_type = user_reg_hint_type; |
2295 | 2305 | ||
2306 | /* Allow calling CRDA again */ | ||
2307 | reg_crda_timeouts = 0; | ||
2308 | |||
2296 | queue_regulatory_request(request); | 2309 | queue_regulatory_request(request); |
2297 | 2310 | ||
2298 | return 0; | 2311 | return 0; |
@@ -2362,6 +2375,9 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2) | |||
2362 | request->alpha2[1] = alpha2[1]; | 2375 | request->alpha2[1] = alpha2[1]; |
2363 | request->initiator = NL80211_REGDOM_SET_BY_DRIVER; | 2376 | request->initiator = NL80211_REGDOM_SET_BY_DRIVER; |
2364 | 2377 | ||
2378 | /* Allow calling CRDA again */ | ||
2379 | reg_crda_timeouts = 0; | ||
2380 | |||
2365 | queue_regulatory_request(request); | 2381 | queue_regulatory_request(request); |
2366 | 2382 | ||
2367 | return 0; | 2383 | return 0; |
@@ -2415,6 +2431,9 @@ void regulatory_hint_country_ie(struct wiphy *wiphy, enum ieee80211_band band, | |||
2415 | request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE; | 2431 | request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE; |
2416 | request->country_ie_env = env; | 2432 | request->country_ie_env = env; |
2417 | 2433 | ||
2434 | /* Allow calling CRDA again */ | ||
2435 | reg_crda_timeouts = 0; | ||
2436 | |||
2418 | queue_regulatory_request(request); | 2437 | queue_regulatory_request(request); |
2419 | request = NULL; | 2438 | request = NULL; |
2420 | out: | 2439 | out: |
@@ -2893,7 +2912,8 @@ static int reg_set_rd_country_ie(const struct ieee80211_regdomain *rd, | |||
2893 | * multiple drivers can be ironed out later. Caller must've already | 2912 | * multiple drivers can be ironed out later. Caller must've already |
2894 | * kmalloc'd the rd structure. | 2913 | * kmalloc'd the rd structure. |
2895 | */ | 2914 | */ |
2896 | int set_regdom(const struct ieee80211_regdomain *rd) | 2915 | int set_regdom(const struct ieee80211_regdomain *rd, |
2916 | enum ieee80211_regd_source regd_src) | ||
2897 | { | 2917 | { |
2898 | struct regulatory_request *lr; | 2918 | struct regulatory_request *lr; |
2899 | bool user_reset = false; | 2919 | bool user_reset = false; |
@@ -2904,6 +2924,9 @@ int set_regdom(const struct ieee80211_regdomain *rd) | |||
2904 | return -EINVAL; | 2924 | return -EINVAL; |
2905 | } | 2925 | } |
2906 | 2926 | ||
2927 | if (regd_src == REGD_SOURCE_CRDA) | ||
2928 | reg_crda_timeouts = 0; | ||
2929 | |||
2907 | lr = get_last_request(); | 2930 | lr = get_last_request(); |
2908 | 2931 | ||
2909 | /* Note that this doesn't update the wiphys, this is done below */ | 2932 | /* Note that this doesn't update the wiphys, this is done below */ |
@@ -3063,6 +3086,7 @@ static void reg_timeout_work(struct work_struct *work) | |||
3063 | { | 3086 | { |
3064 | REG_DBG_PRINT("Timeout while waiting for CRDA to reply, restoring regulatory settings\n"); | 3087 | REG_DBG_PRINT("Timeout while waiting for CRDA to reply, restoring regulatory settings\n"); |
3065 | rtnl_lock(); | 3088 | rtnl_lock(); |
3089 | reg_crda_timeouts++; | ||
3066 | restore_regulatory_settings(true); | 3090 | restore_regulatory_settings(true); |
3067 | rtnl_unlock(); | 3091 | rtnl_unlock(); |
3068 | } | 3092 | } |
diff --git a/net/wireless/reg.h b/net/wireless/reg.h index a2c4e16459da..9f495d76eca0 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h | |||
@@ -16,6 +16,11 @@ | |||
16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 | */ | 17 | */ |
18 | 18 | ||
19 | enum ieee80211_regd_source { | ||
20 | REGD_SOURCE_INTERNAL_DB, | ||
21 | REGD_SOURCE_CRDA, | ||
22 | }; | ||
23 | |||
19 | extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain; | 24 | extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain; |
20 | 25 | ||
21 | bool reg_is_valid_request(const char *alpha2); | 26 | bool reg_is_valid_request(const char *alpha2); |
@@ -46,7 +51,9 @@ void wiphy_regulatory_deregister(struct wiphy *wiphy); | |||
46 | int __init regulatory_init(void); | 51 | int __init regulatory_init(void); |
47 | void regulatory_exit(void); | 52 | void regulatory_exit(void); |
48 | 53 | ||
49 | int set_regdom(const struct ieee80211_regdomain *rd); | 54 | int set_regdom(const struct ieee80211_regdomain *rd, |
55 | enum ieee80211_regd_source regd_src); | ||
56 | |||
50 | unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd, | 57 | unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd, |
51 | const struct ieee80211_reg_rule *rule); | 58 | const struct ieee80211_reg_rule *rule); |
52 | 59 | ||
diff --git a/net/wireless/sme.c b/net/wireless/sme.c index ea1da6621ff0..d11454f87bac 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c | |||
@@ -42,7 +42,7 @@ struct cfg80211_conn { | |||
42 | CFG80211_CONN_CONNECTED, | 42 | CFG80211_CONN_CONNECTED, |
43 | } state; | 43 | } state; |
44 | u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; | 44 | u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; |
45 | u8 *ie; | 45 | const u8 *ie; |
46 | size_t ie_len; | 46 | size_t ie_len; |
47 | bool auto_auth, prev_bssid_valid; | 47 | bool auto_auth, prev_bssid_valid; |
48 | }; | 48 | }; |
@@ -423,6 +423,62 @@ void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev) | |||
423 | schedule_work(&rdev->conn_work); | 423 | schedule_work(&rdev->conn_work); |
424 | } | 424 | } |
425 | 425 | ||
426 | static int cfg80211_sme_get_conn_ies(struct wireless_dev *wdev, | ||
427 | const u8 *ies, size_t ies_len, | ||
428 | const u8 **out_ies, size_t *out_ies_len) | ||
429 | { | ||
430 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | ||
431 | u8 *buf; | ||
432 | size_t offs; | ||
433 | |||
434 | if (!rdev->wiphy.extended_capabilities_len || | ||
435 | (ies && cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY, ies, ies_len))) { | ||
436 | *out_ies = kmemdup(ies, ies_len, GFP_KERNEL); | ||
437 | if (!*out_ies) | ||
438 | return -ENOMEM; | ||
439 | *out_ies_len = ies_len; | ||
440 | return 0; | ||
441 | } | ||
442 | |||
443 | buf = kmalloc(ies_len + rdev->wiphy.extended_capabilities_len + 2, | ||
444 | GFP_KERNEL); | ||
445 | if (!buf) | ||
446 | return -ENOMEM; | ||
447 | |||
448 | if (ies_len) { | ||
449 | static const u8 before_extcapa[] = { | ||
450 | /* not listing IEs expected to be created by driver */ | ||
451 | WLAN_EID_RSN, | ||
452 | WLAN_EID_QOS_CAPA, | ||
453 | WLAN_EID_RRM_ENABLED_CAPABILITIES, | ||
454 | WLAN_EID_MOBILITY_DOMAIN, | ||
455 | WLAN_EID_SUPPORTED_REGULATORY_CLASSES, | ||
456 | WLAN_EID_BSS_COEX_2040, | ||
457 | }; | ||
458 | |||
459 | offs = ieee80211_ie_split(ies, ies_len, before_extcapa, | ||
460 | ARRAY_SIZE(before_extcapa), 0); | ||
461 | memcpy(buf, ies, offs); | ||
462 | /* leave a whole for extended capabilities IE */ | ||
463 | memcpy(buf + offs + rdev->wiphy.extended_capabilities_len + 2, | ||
464 | ies + offs, ies_len - offs); | ||
465 | } else { | ||
466 | offs = 0; | ||
467 | } | ||
468 | |||
469 | /* place extended capabilities IE (with only driver capabilities) */ | ||
470 | buf[offs] = WLAN_EID_EXT_CAPABILITY; | ||
471 | buf[offs + 1] = rdev->wiphy.extended_capabilities_len; | ||
472 | memcpy(buf + offs + 2, | ||
473 | rdev->wiphy.extended_capabilities, | ||
474 | rdev->wiphy.extended_capabilities_len); | ||
475 | |||
476 | *out_ies = buf; | ||
477 | *out_ies_len = ies_len + rdev->wiphy.extended_capabilities_len + 2; | ||
478 | |||
479 | return 0; | ||
480 | } | ||
481 | |||
426 | static int cfg80211_sme_connect(struct wireless_dev *wdev, | 482 | static int cfg80211_sme_connect(struct wireless_dev *wdev, |
427 | struct cfg80211_connect_params *connect, | 483 | struct cfg80211_connect_params *connect, |
428 | const u8 *prev_bssid) | 484 | const u8 *prev_bssid) |
@@ -453,16 +509,14 @@ static int cfg80211_sme_connect(struct wireless_dev *wdev, | |||
453 | memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN); | 509 | memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN); |
454 | } | 510 | } |
455 | 511 | ||
456 | if (connect->ie) { | 512 | if (cfg80211_sme_get_conn_ies(wdev, connect->ie, connect->ie_len, |
457 | wdev->conn->ie = kmemdup(connect->ie, connect->ie_len, | 513 | &wdev->conn->ie, |
458 | GFP_KERNEL); | 514 | &wdev->conn->params.ie_len)) { |
459 | wdev->conn->params.ie = wdev->conn->ie; | 515 | kfree(wdev->conn); |
460 | if (!wdev->conn->ie) { | 516 | wdev->conn = NULL; |
461 | kfree(wdev->conn); | 517 | return -ENOMEM; |
462 | wdev->conn = NULL; | ||
463 | return -ENOMEM; | ||
464 | } | ||
465 | } | 518 | } |
519 | wdev->conn->params.ie = wdev->conn->ie; | ||
466 | 520 | ||
467 | if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { | 521 | if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { |
468 | wdev->conn->auto_auth = true; | 522 | wdev->conn->auto_auth = true; |
diff --git a/net/wireless/util.c b/net/wireless/util.c index f218b151530a..70051ab52f4f 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c | |||
@@ -1290,6 +1290,47 @@ int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len, | |||
1290 | } | 1290 | } |
1291 | EXPORT_SYMBOL(cfg80211_get_p2p_attr); | 1291 | EXPORT_SYMBOL(cfg80211_get_p2p_attr); |
1292 | 1292 | ||
1293 | static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) | ||
1294 | { | ||
1295 | int i; | ||
1296 | |||
1297 | for (i = 0; i < n_ids; i++) | ||
1298 | if (ids[i] == id) | ||
1299 | return true; | ||
1300 | return false; | ||
1301 | } | ||
1302 | |||
1303 | size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen, | ||
1304 | const u8 *ids, int n_ids, | ||
1305 | const u8 *after_ric, int n_after_ric, | ||
1306 | size_t offset) | ||
1307 | { | ||
1308 | size_t pos = offset; | ||
1309 | |||
1310 | while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) { | ||
1311 | if (ies[pos] == WLAN_EID_RIC_DATA && n_after_ric) { | ||
1312 | pos += 2 + ies[pos + 1]; | ||
1313 | |||
1314 | while (pos < ielen && | ||
1315 | !ieee80211_id_in_list(after_ric, n_after_ric, | ||
1316 | ies[pos])) | ||
1317 | pos += 2 + ies[pos + 1]; | ||
1318 | } else { | ||
1319 | pos += 2 + ies[pos + 1]; | ||
1320 | } | ||
1321 | } | ||
1322 | |||
1323 | return pos; | ||
1324 | } | ||
1325 | EXPORT_SYMBOL(ieee80211_ie_split_ric); | ||
1326 | |||
1327 | size_t ieee80211_ie_split(const u8 *ies, size_t ielen, | ||
1328 | const u8 *ids, int n_ids, size_t offset) | ||
1329 | { | ||
1330 | return ieee80211_ie_split_ric(ies, ielen, ids, n_ids, NULL, 0, offset); | ||
1331 | } | ||
1332 | EXPORT_SYMBOL(ieee80211_ie_split); | ||
1333 | |||
1293 | bool ieee80211_operating_class_to_band(u8 operating_class, | 1334 | bool ieee80211_operating_class_to_band(u8 operating_class, |
1294 | enum ieee80211_band *band) | 1335 | enum ieee80211_band *band) |
1295 | { | 1336 | { |