diff options
| -rw-r--r-- | include/net/cfg80211.h | 58 | ||||
| -rw-r--r-- | include/net/mac80211.h | 101 | ||||
| -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 |
27 files changed, 1249 insertions, 406 deletions
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 441306436569..f8d6813cd5b2 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h | |||
| @@ -5001,6 +5001,64 @@ int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len, | |||
| 5001 | u8 *buf, unsigned int bufsize); | 5001 | u8 *buf, unsigned int bufsize); |
| 5002 | 5002 | ||
| 5003 | /** | 5003 | /** |
| 5004 | * ieee80211_ie_split_ric - split an IE buffer according to ordering (with RIC) | ||
| 5005 | * @ies: the IE buffer | ||
| 5006 | * @ielen: the length of the IE buffer | ||
| 5007 | * @ids: an array with element IDs that are allowed before | ||
| 5008 | * the split | ||
| 5009 | * @n_ids: the size of the element ID array | ||
| 5010 | * @after_ric: array IE types that come after the RIC element | ||
| 5011 | * @n_after_ric: size of the @after_ric array | ||
| 5012 | * @offset: offset where to start splitting in the buffer | ||
| 5013 | * | ||
| 5014 | * This function splits an IE buffer by updating the @offset | ||
| 5015 | * variable to point to the location where the buffer should be | ||
| 5016 | * split. | ||
| 5017 | * | ||
| 5018 | * It assumes that the given IE buffer is well-formed, this | ||
| 5019 | * has to be guaranteed by the caller! | ||
| 5020 | * | ||
| 5021 | * It also assumes that the IEs in the buffer are ordered | ||
| 5022 | * correctly, if not the result of using this function will not | ||
| 5023 | * be ordered correctly either, i.e. it does no reordering. | ||
| 5024 | * | ||
| 5025 | * The function returns the offset where the next part of the | ||
| 5026 | * buffer starts, which may be @ielen if the entire (remainder) | ||
| 5027 | * of the buffer should be used. | ||
| 5028 | */ | ||
| 5029 | size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen, | ||
| 5030 | const u8 *ids, int n_ids, | ||
| 5031 | const u8 *after_ric, int n_after_ric, | ||
| 5032 | size_t offset); | ||
| 5033 | |||
| 5034 | /** | ||
| 5035 | * ieee80211_ie_split - split an IE buffer according to ordering | ||
| 5036 | * @ies: the IE buffer | ||
| 5037 | * @ielen: the length of the IE buffer | ||
| 5038 | * @ids: an array with element IDs that are allowed before | ||
| 5039 | * the split | ||
| 5040 | * @n_ids: the size of the element ID array | ||
| 5041 | * @offset: offset where to start splitting in the buffer | ||
| 5042 | * | ||
| 5043 | * This function splits an IE buffer by updating the @offset | ||
| 5044 | * variable to point to the location where the buffer should be | ||
| 5045 | * split. | ||
| 5046 | * | ||
| 5047 | * It assumes that the given IE buffer is well-formed, this | ||
| 5048 | * has to be guaranteed by the caller! | ||
| 5049 | * | ||
| 5050 | * It also assumes that the IEs in the buffer are ordered | ||
| 5051 | * correctly, if not the result of using this function will not | ||
| 5052 | * be ordered correctly either, i.e. it does no reordering. | ||
| 5053 | * | ||
| 5054 | * The function returns the offset where the next part of the | ||
| 5055 | * buffer starts, which may be @ielen if the entire (remainder) | ||
| 5056 | * of the buffer should be used. | ||
| 5057 | */ | ||
| 5058 | size_t ieee80211_ie_split(const u8 *ies, size_t ielen, | ||
| 5059 | const u8 *ids, int n_ids, size_t offset); | ||
| 5060 | |||
| 5061 | /** | ||
| 5004 | * cfg80211_report_wowlan_wakeup - report wakeup from WoWLAN | 5062 | * cfg80211_report_wowlan_wakeup - report wakeup from WoWLAN |
| 5005 | * @wdev: the wireless device reporting the wakeup | 5063 | * @wdev: the wireless device reporting the wakeup |
| 5006 | * @wakeup: the wakeup report | 5064 | * @wakeup: the wakeup report |
diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 201bc68e0cff..b4bef1152c05 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h | |||
| @@ -84,6 +84,39 @@ | |||
| 84 | * | 84 | * |
| 85 | */ | 85 | */ |
| 86 | 86 | ||
| 87 | /** | ||
| 88 | * DOC: mac80211 software tx queueing | ||
| 89 | * | ||
| 90 | * mac80211 provides an optional intermediate queueing implementation designed | ||
| 91 | * to allow the driver to keep hardware queues short and provide some fairness | ||
| 92 | * between different stations/interfaces. | ||
| 93 | * In this model, the driver pulls data frames from the mac80211 queue instead | ||
| 94 | * of letting mac80211 push them via drv_tx(). | ||
| 95 | * Other frames (e.g. control or management) are still pushed using drv_tx(). | ||
| 96 | * | ||
| 97 | * Drivers indicate that they use this model by implementing the .wake_tx_queue | ||
| 98 | * driver operation. | ||
| 99 | * | ||
| 100 | * Intermediate queues (struct ieee80211_txq) are kept per-sta per-tid, with a | ||
| 101 | * single per-vif queue for multicast data frames. | ||
| 102 | * | ||
| 103 | * The driver is expected to initialize its private per-queue data for stations | ||
| 104 | * and interfaces in the .add_interface and .sta_add ops. | ||
| 105 | * | ||
| 106 | * The driver can't access the queue directly. To dequeue a frame, it calls | ||
| 107 | * ieee80211_tx_dequeue(). Whenever mac80211 adds a new frame to a queue, it | ||
| 108 | * calls the .wake_tx_queue driver op. | ||
| 109 | * | ||
| 110 | * For AP powersave TIM handling, the driver only needs to indicate if it has | ||
| 111 | * buffered packets in the driver specific data structures by calling | ||
| 112 | * ieee80211_sta_set_buffered(). For frames buffered in the ieee80211_txq | ||
| 113 | * struct, mac80211 sets the appropriate TIM PVB bits and calls | ||
| 114 | * .release_buffered_frames(). | ||
| 115 | * In that callback the driver is therefore expected to release its own | ||
| 116 | * buffered frames and afterwards also frames from the ieee80211_txq (obtained | ||
| 117 | * via the usual ieee80211_tx_dequeue). | ||
| 118 | */ | ||
| 119 | |||
| 87 | struct device; | 120 | struct device; |
| 88 | 121 | ||
| 89 | /** | 122 | /** |
| @@ -1306,6 +1339,7 @@ enum ieee80211_vif_flags { | |||
| 1306 | * monitor interface (if that is requested.) | 1339 | * monitor interface (if that is requested.) |
| 1307 | * @drv_priv: data area for driver use, will always be aligned to | 1340 | * @drv_priv: data area for driver use, will always be aligned to |
| 1308 | * sizeof(void *). | 1341 | * sizeof(void *). |
| 1342 | * @txq: the multicast data TX queue (if driver uses the TXQ abstraction) | ||
| 1309 | */ | 1343 | */ |
| 1310 | struct ieee80211_vif { | 1344 | struct ieee80211_vif { |
| 1311 | enum nl80211_iftype type; | 1345 | enum nl80211_iftype type; |
| @@ -1317,6 +1351,8 @@ struct ieee80211_vif { | |||
| 1317 | u8 cab_queue; | 1351 | u8 cab_queue; |
| 1318 | u8 hw_queue[IEEE80211_NUM_ACS]; | 1352 | u8 hw_queue[IEEE80211_NUM_ACS]; |
| 1319 | 1353 | ||
| 1354 | struct ieee80211_txq *txq; | ||
| 1355 | |||
| 1320 | struct ieee80211_chanctx_conf __rcu *chanctx_conf; | 1356 | struct ieee80211_chanctx_conf __rcu *chanctx_conf; |
| 1321 | 1357 | ||
| 1322 | u32 driver_flags; | 1358 | u32 driver_flags; |
| @@ -1575,6 +1611,7 @@ struct ieee80211_sta_rates { | |||
| 1575 | * @tdls_initiator: indicates the STA is an initiator of the TDLS link. Only | 1611 | * @tdls_initiator: indicates the STA is an initiator of the TDLS link. Only |
| 1576 | * valid if the STA is a TDLS peer in the first place. | 1612 | * valid if the STA is a TDLS peer in the first place. |
| 1577 | * @mfp: indicates whether the STA uses management frame protection or not. | 1613 | * @mfp: indicates whether the STA uses management frame protection or not. |
| 1614 | * @txq: per-TID data TX queues (if driver uses the TXQ abstraction) | ||
| 1578 | */ | 1615 | */ |
| 1579 | struct ieee80211_sta { | 1616 | struct ieee80211_sta { |
| 1580 | u32 supp_rates[IEEE80211_NUM_BANDS]; | 1617 | u32 supp_rates[IEEE80211_NUM_BANDS]; |
| @@ -1593,6 +1630,8 @@ struct ieee80211_sta { | |||
| 1593 | bool tdls_initiator; | 1630 | bool tdls_initiator; |
| 1594 | bool mfp; | 1631 | bool mfp; |
| 1595 | 1632 | ||
| 1633 | struct ieee80211_txq *txq[IEEE80211_NUM_TIDS]; | ||
| 1634 | |||
| 1596 | /* must be last */ | 1635 | /* must be last */ |
| 1597 | u8 drv_priv[0] __aligned(sizeof(void *)); | 1636 | u8 drv_priv[0] __aligned(sizeof(void *)); |
| 1598 | }; | 1637 | }; |
| @@ -1621,6 +1660,27 @@ struct ieee80211_tx_control { | |||
| 1621 | }; | 1660 | }; |
| 1622 | 1661 | ||
| 1623 | /** | 1662 | /** |
| 1663 | * struct ieee80211_txq - Software intermediate tx queue | ||
| 1664 | * | ||
| 1665 | * @vif: &struct ieee80211_vif pointer from the add_interface callback. | ||
| 1666 | * @sta: station table entry, %NULL for per-vif queue | ||
| 1667 | * @tid: the TID for this queue (unused for per-vif queue) | ||
| 1668 | * @ac: the AC for this queue | ||
| 1669 | * | ||
| 1670 | * The driver can obtain packets from this queue by calling | ||
| 1671 | * ieee80211_tx_dequeue(). | ||
| 1672 | */ | ||
| 1673 | struct ieee80211_txq { | ||
| 1674 | struct ieee80211_vif *vif; | ||
| 1675 | struct ieee80211_sta *sta; | ||
| 1676 | u8 tid; | ||
| 1677 | u8 ac; | ||
| 1678 | |||
| 1679 | /* must be last */ | ||
| 1680 | u8 drv_priv[0] __aligned(sizeof(void *)); | ||
| 1681 | }; | ||
| 1682 | |||
| 1683 | /** | ||
| 1624 | * enum ieee80211_hw_flags - hardware flags | 1684 | * enum ieee80211_hw_flags - hardware flags |
| 1625 | * | 1685 | * |
| 1626 | * These flags are used to indicate hardware capabilities to | 1686 | * These flags are used to indicate hardware capabilities to |
| @@ -1844,6 +1904,8 @@ enum ieee80211_hw_flags { | |||
| 1844 | * within &struct ieee80211_sta. | 1904 | * within &struct ieee80211_sta. |
| 1845 | * @chanctx_data_size: size (in bytes) of the drv_priv data area | 1905 | * @chanctx_data_size: size (in bytes) of the drv_priv data area |
| 1846 | * within &struct ieee80211_chanctx_conf. | 1906 | * within &struct ieee80211_chanctx_conf. |
| 1907 | * @txq_data_size: size (in bytes) of the drv_priv data area | ||
| 1908 | * within @struct ieee80211_txq. | ||
| 1847 | * | 1909 | * |
| 1848 | * @max_rates: maximum number of alternate rate retry stages the hw | 1910 | * @max_rates: maximum number of alternate rate retry stages the hw |
| 1849 | * can handle. | 1911 | * can handle. |
| @@ -1892,6 +1954,9 @@ enum ieee80211_hw_flags { | |||
| 1892 | * @n_cipher_schemes: a size of an array of cipher schemes definitions. | 1954 | * @n_cipher_schemes: a size of an array of cipher schemes definitions. |
| 1893 | * @cipher_schemes: a pointer to an array of cipher scheme definitions | 1955 | * @cipher_schemes: a pointer to an array of cipher scheme definitions |
| 1894 | * supported by HW. | 1956 | * supported by HW. |
| 1957 | * | ||
| 1958 | * @txq_ac_max_pending: maximum number of frames per AC pending in all txq | ||
| 1959 | * entries for a vif. | ||
| 1895 | */ | 1960 | */ |
| 1896 | struct ieee80211_hw { | 1961 | struct ieee80211_hw { |
| 1897 | struct ieee80211_conf conf; | 1962 | struct ieee80211_conf conf; |
| @@ -1904,6 +1969,7 @@ struct ieee80211_hw { | |||
| 1904 | int vif_data_size; | 1969 | int vif_data_size; |
| 1905 | int sta_data_size; | 1970 | int sta_data_size; |
| 1906 | int chanctx_data_size; | 1971 | int chanctx_data_size; |
| 1972 | int txq_data_size; | ||
| 1907 | u16 queues; | 1973 | u16 queues; |
| 1908 | u16 max_listen_interval; | 1974 | u16 max_listen_interval; |
| 1909 | s8 max_signal; | 1975 | s8 max_signal; |
| @@ -1920,6 +1986,7 @@ struct ieee80211_hw { | |||
| 1920 | u8 uapsd_max_sp_len; | 1986 | u8 uapsd_max_sp_len; |
| 1921 | u8 n_cipher_schemes; | 1987 | u8 n_cipher_schemes; |
| 1922 | const struct ieee80211_cipher_scheme *cipher_schemes; | 1988 | const struct ieee80211_cipher_scheme *cipher_schemes; |
| 1989 | int txq_ac_max_pending; | ||
| 1923 | }; | 1990 | }; |
| 1924 | 1991 | ||
| 1925 | /** | 1992 | /** |
| @@ -3082,6 +3149,8 @@ enum ieee80211_reconfig_type { | |||
| 3082 | * response template is provided, together with the location of the | 3149 | * response template is provided, together with the location of the |
| 3083 | * switch-timing IE within the template. The skb can only be used within | 3150 | * switch-timing IE within the template. The skb can only be used within |
| 3084 | * the function call. | 3151 | * the function call. |
| 3152 | * | ||
| 3153 | * @wake_tx_queue: Called when new packets have been added to the queue. | ||
| 3085 | */ | 3154 | */ |
| 3086 | struct ieee80211_ops { | 3155 | struct ieee80211_ops { |
| 3087 | void (*tx)(struct ieee80211_hw *hw, | 3156 | void (*tx)(struct ieee80211_hw *hw, |
| @@ -3313,6 +3382,9 @@ struct ieee80211_ops { | |||
| 3313 | void (*tdls_recv_channel_switch)(struct ieee80211_hw *hw, | 3382 | void (*tdls_recv_channel_switch)(struct ieee80211_hw *hw, |
| 3314 | struct ieee80211_vif *vif, | 3383 | struct ieee80211_vif *vif, |
| 3315 | struct ieee80211_tdls_ch_sw_params *params); | 3384 | struct ieee80211_tdls_ch_sw_params *params); |
| 3385 | |||
| 3386 | void (*wake_tx_queue)(struct ieee80211_hw *hw, | ||
| 3387 | struct ieee80211_txq *txq); | ||
| 3316 | }; | 3388 | }; |
| 3317 | 3389 | ||
| 3318 | /** | 3390 | /** |
| @@ -5308,30 +5380,13 @@ int ieee80211_reserve_tid(struct ieee80211_sta *sta, u8 tid); | |||
| 5308 | void ieee80211_unreserve_tid(struct ieee80211_sta *sta, u8 tid); | 5380 | void ieee80211_unreserve_tid(struct ieee80211_sta *sta, u8 tid); |
| 5309 | 5381 | ||
| 5310 | /** | 5382 | /** |
| 5311 | * ieee80211_ie_split - split an IE buffer according to ordering | 5383 | * ieee80211_tx_dequeue - dequeue a packet from a software tx queue |
| 5312 | * | ||
| 5313 | * @ies: the IE buffer | ||
| 5314 | * @ielen: the length of the IE buffer | ||
| 5315 | * @ids: an array with element IDs that are allowed before | ||
| 5316 | * the split | ||
| 5317 | * @n_ids: the size of the element ID array | ||
| 5318 | * @offset: offset where to start splitting in the buffer | ||
| 5319 | * | 5384 | * |
| 5320 | * This function splits an IE buffer by updating the @offset | 5385 | * @hw: pointer as obtained from ieee80211_alloc_hw() |
| 5321 | * variable to point to the location where the buffer should be | 5386 | * @txq: pointer obtained from station or virtual interface |
| 5322 | * split. | ||
| 5323 | * | ||
| 5324 | * It assumes that the given IE buffer is well-formed, this | ||
| 5325 | * has to be guaranteed by the caller! | ||
| 5326 | * | ||
| 5327 | * It also assumes that the IEs in the buffer are ordered | ||
| 5328 | * correctly, if not the result of using this function will not | ||
| 5329 | * be ordered correctly either, i.e. it does no reordering. | ||
| 5330 | * | 5387 | * |
| 5331 | * The function returns the offset where the next part of the | 5388 | * Returns the skb if successful, %NULL if no frame was available. |
| 5332 | * buffer starts, which may be @ielen if the entire (remainder) | ||
| 5333 | * of the buffer should be used. | ||
| 5334 | */ | 5389 | */ |
| 5335 | size_t ieee80211_ie_split(const u8 *ies, size_t ielen, | 5390 | struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw, |
| 5336 | const u8 *ids, int n_ids, size_t offset); | 5391 | struct ieee80211_txq *txq); |
| 5337 | #endif /* MAC80211_H */ | 5392 | #endif /* MAC80211_H */ |
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 | { |
