Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ath9k: add dynamic ACK timeout estimation

Add dynamic ACK timeout estimation algorithm based on ACK frame RX timestamp,
TX frame timestamp and frame duration.

Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
Tested-by: Philippe Duchein <wireless-dev@duchein.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>

authored by

Lorenzo Bianconi and committed by
John W. Linville
c774d57f 36678b2b

+464
+2
drivers/net/wireless/ath/ath.h
··· 234 234 * AR9462. 235 235 * @ATH_DBG_DFS: radar datection 236 236 * @ATH_DBG_WOW: Wake on Wireless 237 + * @ATH_DBG_DYNACK: dynack handling 237 238 * @ATH_DBG_ANY: enable all debugging 238 239 * 239 240 * The debug level is used to control the amount and type of debugging output ··· 263 262 ATH_DBG_DFS = 0x00010000, 264 263 ATH_DBG_WOW = 0x00020000, 265 264 ATH_DBG_CHAN_CTX = 0x00040000, 265 + ATH_DBG_DYNACK = 0x00080000, 266 266 ATH_DBG_ANY = 0xffffffff 267 267 }; 268 268
+3
drivers/net/wireless/ath/ath9k/ath9k.h
··· 274 274 struct ath_rx_rate_stats rx_rate_stats; 275 275 #endif 276 276 u8 key_idx[4]; 277 + 278 + u32 ackto; 279 + struct list_head list; 277 280 }; 278 281 279 282 struct ath_tx_control {
+351
drivers/net/wireless/ath/ath9k/dynack.c
··· 1 + /* 2 + * Copyright (c) 2014, Lorenzo Bianconi <lorenzo.bianconi83@gmail.com> 3 + * 4 + * Permission to use, copy, modify, and/or distribute this software for any 5 + * purpose with or without fee is hereby granted, provided that the above 6 + * copyright notice and this permission notice appear in all copies. 7 + * 8 + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 + */ 16 + 17 + #include "ath9k.h" 18 + #include "hw.h" 19 + #include "dynack.h" 20 + 21 + #define COMPUTE_TO (5 * HZ) 22 + #define LATEACK_DELAY (10 * HZ) 23 + #define LATEACK_TO 256 24 + #define MAX_DELAY 300 25 + #define EWMA_LEVEL 96 26 + #define EWMA_DIV 128 27 + 28 + /** 29 + * ath_dynack_ewma - EWMA (Exponentially Weighted Moving Average) calculation 30 + * 31 + */ 32 + static inline u32 ath_dynack_ewma(u32 old, u32 new) 33 + { 34 + return (new * (EWMA_DIV - EWMA_LEVEL) + old * EWMA_LEVEL) / EWMA_DIV; 35 + } 36 + 37 + /** 38 + * ath_dynack_get_sifs - get sifs time based on phy used 39 + * @ah: ath hw 40 + * @phy: phy used 41 + * 42 + */ 43 + static inline u32 ath_dynack_get_sifs(struct ath_hw *ah, int phy) 44 + { 45 + u32 sifs = CCK_SIFS_TIME; 46 + 47 + if (phy == WLAN_RC_PHY_OFDM) { 48 + if (IS_CHAN_QUARTER_RATE(ah->curchan)) 49 + sifs = OFDM_SIFS_TIME_QUARTER; 50 + else if (IS_CHAN_HALF_RATE(ah->curchan)) 51 + sifs = OFDM_SIFS_TIME_HALF; 52 + else 53 + sifs = OFDM_SIFS_TIME; 54 + } 55 + return sifs; 56 + } 57 + 58 + /** 59 + * ath_dynack_bssidmask - filter out ACK frames based on BSSID mask 60 + * @ah: ath hw 61 + * @mac: receiver address 62 + */ 63 + static inline bool ath_dynack_bssidmask(struct ath_hw *ah, const u8 *mac) 64 + { 65 + int i; 66 + struct ath_common *common = ath9k_hw_common(ah); 67 + 68 + for (i = 0; i < ETH_ALEN; i++) { 69 + if ((common->macaddr[i] & common->bssidmask[i]) != 70 + (mac[i] & common->bssidmask[i])) 71 + return false; 72 + } 73 + 74 + return true; 75 + } 76 + 77 + /** 78 + * ath_dynack_compute_ackto - compute ACK timeout as the maximum STA timeout 79 + * @ah: ath hw 80 + * 81 + * should be called while holding qlock 82 + */ 83 + static void ath_dynack_compute_ackto(struct ath_hw *ah) 84 + { 85 + struct ath_node *an; 86 + u32 to = 0; 87 + struct ath_dynack *da = &ah->dynack; 88 + struct ath_common *common = ath9k_hw_common(ah); 89 + 90 + list_for_each_entry(an, &da->nodes, list) 91 + if (an->ackto > to) 92 + to = an->ackto; 93 + 94 + if (to && da->ackto != to) { 95 + u32 slottime; 96 + 97 + slottime = (to - 3) / 2; 98 + da->ackto = to; 99 + ath_dbg(common, DYNACK, "ACK timeout %u slottime %u\n", 100 + da->ackto, slottime); 101 + ath9k_hw_setslottime(ah, slottime); 102 + ath9k_hw_set_ack_timeout(ah, da->ackto); 103 + ath9k_hw_set_cts_timeout(ah, da->ackto); 104 + } 105 + } 106 + 107 + /** 108 + * ath_dynack_compute_to - compute STA ACK timeout 109 + * @ah: ath hw 110 + * 111 + * should be called while holding qlock 112 + */ 113 + static void ath_dynack_compute_to(struct ath_hw *ah) 114 + { 115 + u32 ackto, ack_ts; 116 + u8 *dst, *src; 117 + struct ieee80211_sta *sta; 118 + struct ath_node *an; 119 + struct ts_info *st_ts; 120 + struct ath_dynack *da = &ah->dynack; 121 + 122 + rcu_read_lock(); 123 + 124 + while (da->st_rbf.h_rb != da->st_rbf.t_rb && 125 + da->ack_rbf.h_rb != da->ack_rbf.t_rb) { 126 + ack_ts = da->ack_rbf.tstamp[da->ack_rbf.h_rb]; 127 + st_ts = &da->st_rbf.ts[da->st_rbf.h_rb]; 128 + dst = da->st_rbf.addr[da->st_rbf.h_rb].h_dest; 129 + src = da->st_rbf.addr[da->st_rbf.h_rb].h_src; 130 + 131 + ath_dbg(ath9k_hw_common(ah), DYNACK, 132 + "ack_ts %u st_ts %u st_dur %u [%u-%u]\n", 133 + ack_ts, st_ts->tstamp, st_ts->dur, 134 + da->ack_rbf.h_rb, da->st_rbf.h_rb); 135 + 136 + if (ack_ts > st_ts->tstamp + st_ts->dur) { 137 + ackto = ack_ts - st_ts->tstamp - st_ts->dur; 138 + 139 + if (ackto < MAX_DELAY) { 140 + sta = ieee80211_find_sta_by_ifaddr(ah->hw, dst, 141 + src); 142 + if (sta) { 143 + an = (struct ath_node *)sta->drv_priv; 144 + an->ackto = ath_dynack_ewma(an->ackto, 145 + ackto); 146 + ath_dbg(ath9k_hw_common(ah), DYNACK, 147 + "%pM to %u\n", dst, an->ackto); 148 + if (time_is_before_jiffies(da->lto)) { 149 + ath_dynack_compute_ackto(ah); 150 + da->lto = jiffies + COMPUTE_TO; 151 + } 152 + } 153 + INCR(da->ack_rbf.h_rb, ATH_DYN_BUF); 154 + } 155 + INCR(da->st_rbf.h_rb, ATH_DYN_BUF); 156 + } else { 157 + INCR(da->ack_rbf.h_rb, ATH_DYN_BUF); 158 + } 159 + } 160 + 161 + rcu_read_unlock(); 162 + } 163 + 164 + /** 165 + * ath_dynack_sample_tx_ts - status timestamp sampling method 166 + * @ah: ath hw 167 + * @skb: socket buffer 168 + * @ts: tx status info 169 + * 170 + */ 171 + void ath_dynack_sample_tx_ts(struct ath_hw *ah, struct sk_buff *skb, 172 + struct ath_tx_status *ts) 173 + { 174 + u8 ridx; 175 + struct ieee80211_hdr *hdr; 176 + struct ath_dynack *da = &ah->dynack; 177 + struct ath_common *common = ath9k_hw_common(ah); 178 + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 179 + 180 + if ((info->flags & IEEE80211_TX_CTL_NO_ACK) || !da->enabled) 181 + return; 182 + 183 + spin_lock_bh(&da->qlock); 184 + 185 + hdr = (struct ieee80211_hdr *)skb->data; 186 + 187 + /* late ACK */ 188 + if (ts->ts_status & ATH9K_TXERR_XRETRY) { 189 + if (ieee80211_is_assoc_req(hdr->frame_control) || 190 + ieee80211_is_assoc_resp(hdr->frame_control)) { 191 + ath_dbg(common, DYNACK, "late ack\n"); 192 + ath9k_hw_setslottime(ah, (LATEACK_TO - 3) / 2); 193 + ath9k_hw_set_ack_timeout(ah, LATEACK_TO); 194 + ath9k_hw_set_cts_timeout(ah, LATEACK_TO); 195 + da->lto = jiffies + LATEACK_DELAY; 196 + } 197 + 198 + spin_unlock_bh(&da->qlock); 199 + return; 200 + } 201 + 202 + ridx = ts->ts_rateindex; 203 + 204 + da->st_rbf.ts[da->st_rbf.t_rb].tstamp = ts->ts_tstamp; 205 + da->st_rbf.ts[da->st_rbf.t_rb].dur = ts->duration[ts->ts_rateindex]; 206 + ether_addr_copy(da->st_rbf.addr[da->st_rbf.t_rb].h_dest, hdr->addr1); 207 + ether_addr_copy(da->st_rbf.addr[da->st_rbf.t_rb].h_src, hdr->addr2); 208 + 209 + if (!(info->status.rates[ridx].flags & IEEE80211_TX_RC_MCS)) { 210 + u32 phy, sifs; 211 + const struct ieee80211_rate *rate; 212 + struct ieee80211_tx_rate *rates = info->status.rates; 213 + 214 + rate = &common->sbands[info->band].bitrates[rates[ridx].idx]; 215 + if (info->band == IEEE80211_BAND_2GHZ && 216 + !(rate->flags & IEEE80211_RATE_ERP_G)) 217 + phy = WLAN_RC_PHY_CCK; 218 + else 219 + phy = WLAN_RC_PHY_OFDM; 220 + 221 + sifs = ath_dynack_get_sifs(ah, phy); 222 + da->st_rbf.ts[da->st_rbf.t_rb].dur -= sifs; 223 + } 224 + 225 + ath_dbg(common, DYNACK, "{%pM} tx sample %u [dur %u][h %u-t %u]\n", 226 + hdr->addr1, da->st_rbf.ts[da->st_rbf.t_rb].tstamp, 227 + da->st_rbf.ts[da->st_rbf.t_rb].dur, da->st_rbf.h_rb, 228 + (da->st_rbf.t_rb + 1) % ATH_DYN_BUF); 229 + 230 + INCR(da->st_rbf.t_rb, ATH_DYN_BUF); 231 + if (da->st_rbf.t_rb == da->st_rbf.h_rb) 232 + INCR(da->st_rbf.h_rb, ATH_DYN_BUF); 233 + 234 + ath_dynack_compute_to(ah); 235 + 236 + spin_unlock_bh(&da->qlock); 237 + } 238 + EXPORT_SYMBOL(ath_dynack_sample_tx_ts); 239 + 240 + /** 241 + * ath_dynack_sample_ack_ts - ACK timestamp sampling method 242 + * @ah: ath hw 243 + * @skb: socket buffer 244 + * @ts: rx timestamp 245 + * 246 + */ 247 + void ath_dynack_sample_ack_ts(struct ath_hw *ah, struct sk_buff *skb, 248 + u32 ts) 249 + { 250 + struct ath_dynack *da = &ah->dynack; 251 + struct ath_common *common = ath9k_hw_common(ah); 252 + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; 253 + 254 + if (!ath_dynack_bssidmask(ah, hdr->addr1) || !da->enabled) 255 + return; 256 + 257 + spin_lock_bh(&da->qlock); 258 + da->ack_rbf.tstamp[da->ack_rbf.t_rb] = ts; 259 + 260 + ath_dbg(common, DYNACK, "rx sample %u [h %u-t %u]\n", 261 + da->ack_rbf.tstamp[da->ack_rbf.t_rb], 262 + da->ack_rbf.h_rb, (da->ack_rbf.t_rb + 1) % ATH_DYN_BUF); 263 + 264 + INCR(da->ack_rbf.t_rb, ATH_DYN_BUF); 265 + if (da->ack_rbf.t_rb == da->ack_rbf.h_rb) 266 + INCR(da->ack_rbf.h_rb, ATH_DYN_BUF); 267 + 268 + ath_dynack_compute_to(ah); 269 + 270 + spin_unlock_bh(&da->qlock); 271 + } 272 + EXPORT_SYMBOL(ath_dynack_sample_ack_ts); 273 + 274 + /** 275 + * ath_dynack_node_init - init ath_node related info 276 + * @ah: ath hw 277 + * @an: ath node 278 + * 279 + */ 280 + void ath_dynack_node_init(struct ath_hw *ah, struct ath_node *an) 281 + { 282 + /* ackto = slottime + sifs + air delay */ 283 + u32 ackto = ATH9K_SLOT_TIME_9 + 16 + 64; 284 + struct ath_dynack *da = &ah->dynack; 285 + 286 + an->ackto = ackto; 287 + 288 + spin_lock(&da->qlock); 289 + list_add_tail(&an->list, &da->nodes); 290 + spin_unlock(&da->qlock); 291 + } 292 + EXPORT_SYMBOL(ath_dynack_node_init); 293 + 294 + /** 295 + * ath_dynack_node_deinit - deinit ath_node related info 296 + * @ah: ath hw 297 + * @an: ath node 298 + * 299 + */ 300 + void ath_dynack_node_deinit(struct ath_hw *ah, struct ath_node *an) 301 + { 302 + struct ath_dynack *da = &ah->dynack; 303 + 304 + spin_lock(&da->qlock); 305 + list_del(&an->list); 306 + spin_unlock(&da->qlock); 307 + } 308 + EXPORT_SYMBOL(ath_dynack_node_deinit); 309 + 310 + /** 311 + * ath_dynack_reset - reset dynack processing 312 + * @ah: ath hw 313 + * 314 + */ 315 + void ath_dynack_reset(struct ath_hw *ah) 316 + { 317 + /* ackto = slottime + sifs + air delay */ 318 + u32 ackto = ATH9K_SLOT_TIME_9 + 16 + 64; 319 + struct ath_dynack *da = &ah->dynack; 320 + 321 + da->lto = jiffies; 322 + da->ackto = ackto; 323 + 324 + da->st_rbf.t_rb = 0; 325 + da->st_rbf.h_rb = 0; 326 + da->ack_rbf.t_rb = 0; 327 + da->ack_rbf.h_rb = 0; 328 + 329 + /* init acktimeout */ 330 + ath9k_hw_setslottime(ah, (ackto - 3) / 2); 331 + ath9k_hw_set_ack_timeout(ah, ackto); 332 + ath9k_hw_set_cts_timeout(ah, ackto); 333 + } 334 + EXPORT_SYMBOL(ath_dynack_reset); 335 + 336 + /** 337 + * ath_dynack_init - init dynack data structure 338 + * @ah: ath hw 339 + * 340 + */ 341 + void ath_dynack_init(struct ath_hw *ah) 342 + { 343 + struct ath_dynack *da = &ah->dynack; 344 + 345 + memset(da, 0, sizeof(struct ath_dynack)); 346 + 347 + spin_lock_init(&da->qlock); 348 + INIT_LIST_HEAD(&da->nodes); 349 + 350 + ah->hw->wiphy->features |= NL80211_FEATURE_ACKTO_ESTIMATION; 351 + }
+103
drivers/net/wireless/ath/ath9k/dynack.h
··· 1 + /* 2 + * Copyright (c) 2014, Lorenzo Bianconi <lorenzo.bianconi83@gmail.com> 3 + * 4 + * Permission to use, copy, modify, and/or distribute this software for any 5 + * purpose with or without fee is hereby granted, provided that the above 6 + * copyright notice and this permission notice appear in all copies. 7 + * 8 + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 + */ 16 + 17 + #ifndef DYNACK_H 18 + #define DYNACK_H 19 + 20 + #define ATH_DYN_BUF 64 21 + 22 + struct ath_hw; 23 + struct ath_node; 24 + 25 + /** 26 + * struct ath_dyn_rxbuf - ACK frame ring buffer 27 + * @h_rb: ring buffer head 28 + * @t_rb: ring buffer tail 29 + * @tstamp: ACK RX timestamp buffer 30 + */ 31 + struct ath_dyn_rxbuf { 32 + u16 h_rb, t_rb; 33 + u32 tstamp[ATH_DYN_BUF]; 34 + }; 35 + 36 + struct ts_info { 37 + u32 tstamp; 38 + u32 dur; 39 + }; 40 + 41 + struct haddr_pair { 42 + u8 h_dest[ETH_ALEN]; 43 + u8 h_src[ETH_ALEN]; 44 + }; 45 + 46 + /** 47 + * struct ath_dyn_txbuf - tx frame ring buffer 48 + * @h_rb: ring buffer head 49 + * @t_rb: ring buffer tail 50 + * @addr: dest/src address pair for a given TX frame 51 + * @ts: TX frame timestamp buffer 52 + */ 53 + struct ath_dyn_txbuf { 54 + u16 h_rb, t_rb; 55 + struct haddr_pair addr[ATH_DYN_BUF]; 56 + struct ts_info ts[ATH_DYN_BUF]; 57 + }; 58 + 59 + /** 60 + * struct ath_dynack - dynack processing info 61 + * @enabled: enable dyn ack processing 62 + * @ackto: current ACK timeout 63 + * @lto: last ACK timeout computation 64 + * @nodes: ath_node linked list 65 + * @qlock: ts queue spinlock 66 + * @ack_rbf: ACK ts ring buffer 67 + * @st_rbf: status ts ring buffer 68 + */ 69 + struct ath_dynack { 70 + bool enabled; 71 + int ackto; 72 + unsigned long lto; 73 + 74 + struct list_head nodes; 75 + 76 + /* protect timestamp queue access */ 77 + spinlock_t qlock; 78 + struct ath_dyn_rxbuf ack_rbf; 79 + struct ath_dyn_txbuf st_rbf; 80 + }; 81 + 82 + #if defined(CONFIG_ATH9K_DYNACK) 83 + void ath_dynack_reset(struct ath_hw *ah); 84 + void ath_dynack_node_init(struct ath_hw *ah, struct ath_node *an); 85 + void ath_dynack_node_deinit(struct ath_hw *ah, struct ath_node *an); 86 + void ath_dynack_init(struct ath_hw *ah); 87 + void ath_dynack_sample_ack_ts(struct ath_hw *ah, struct sk_buff *skb, u32 ts); 88 + void ath_dynack_sample_tx_ts(struct ath_hw *ah, struct sk_buff *skb, 89 + struct ath_tx_status *ts); 90 + #else 91 + static inline void ath_dynack_init(struct ath_hw *ah) {} 92 + static inline void ath_dynack_node_init(struct ath_hw *ah, 93 + struct ath_node *an) {} 94 + static inline void ath_dynack_node_deinit(struct ath_hw *ah, 95 + struct ath_node *an) {} 96 + static inline void ath_dynack_sample_ack_ts(struct ath_hw *ah, 97 + struct sk_buff *skb, u32 ts) {} 98 + static inline void ath_dynack_sample_tx_ts(struct ath_hw *ah, 99 + struct sk_buff *skb, 100 + struct ath_tx_status *ts) {} 101 + #endif 102 + 103 + #endif /* DYNACK_H */
+2
drivers/net/wireless/ath/ath9k/hw.c
··· 647 647 return ret; 648 648 } 649 649 650 + ath_dynack_init(ah); 651 + 650 652 return 0; 651 653 } 652 654 EXPORT_SYMBOL(ath9k_hw_init);
+3
drivers/net/wireless/ath/ath9k/hw.h
··· 29 29 #include "reg.h" 30 30 #include "phy.h" 31 31 #include "btcoex.h" 32 + #include "dynack.h" 32 33 33 34 #include "../regd.h" 34 35 ··· 925 924 int (*external_reset)(void); 926 925 927 926 const struct firmware *eeprom_blob; 927 + 928 + struct ath_dynack dynack; 928 929 }; 929 930 930 931 struct ath_bus_ops {