diff options
author | Ron Rindjunsky <ron.rindjunsky@intel.com> | 2008-01-28 07:07:26 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-02-29 15:19:19 -0500 |
commit | 0c11b4de5d81771ba0fdc8a5d13d59ed01d41252 (patch) | |
tree | 26bcbf8828bb0351a2939c7b07d113fb80ef3012 /drivers/net/wireless/iwlwifi/iwl-4965-rs.c | |
parent | 995564382f9d177214b6ec64db6b9109d4cd41dd (diff) |
iwlwifi: A-MPDU Tx activation by load measures
This patch gives a heuristic for activation of the A-MPDU Tx.
As the rate scaling is rate aware, it now also measures estimated load, and
sends A-MPDU activation after a threshold has been met.
Signed-off-by: Ron Rindjunsky <ron.rindjunsky@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-4965-rs.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-4965-rs.c | 204 |
1 files changed, 186 insertions, 18 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965-rs.c b/drivers/net/wireless/iwlwifi/iwl-4965-rs.c index f4f2497646bd..660671f17a3b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965-rs.c | |||
@@ -83,7 +83,7 @@ struct iwl4965_rate_scale_data { | |||
83 | /** | 83 | /** |
84 | * struct iwl4965_scale_tbl_info -- tx params and success history for all rates | 84 | * struct iwl4965_scale_tbl_info -- tx params and success history for all rates |
85 | * | 85 | * |
86 | * There are two of these in struct iwl_rate_scale_priv, | 86 | * There are two of these in struct iwl4965_lq_sta, |
87 | * one for "active", and one for "search". | 87 | * one for "active", and one for "search". |
88 | */ | 88 | */ |
89 | struct iwl4965_scale_tbl_info { | 89 | struct iwl4965_scale_tbl_info { |
@@ -98,8 +98,23 @@ struct iwl4965_scale_tbl_info { | |||
98 | struct iwl4965_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */ | 98 | struct iwl4965_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */ |
99 | }; | 99 | }; |
100 | 100 | ||
101 | #ifdef CONFIG_IWL4965_HT | ||
102 | |||
103 | struct iwl4965_traffic_load { | ||
104 | unsigned long time_stamp; /* age of the oldest statistics */ | ||
105 | u32 packet_count[TID_QUEUE_MAX_SIZE]; /* packet count in this time | ||
106 | * slice */ | ||
107 | u32 total; /* total num of packets during the | ||
108 | * last TID_MAX_TIME_DIFF */ | ||
109 | u8 queue_count; /* number of queues that has | ||
110 | * been used since the last cleanup */ | ||
111 | u8 head; /* start of the circular buffer */ | ||
112 | }; | ||
113 | |||
114 | #endif /* CONFIG_IWL4965_HT */ | ||
115 | |||
101 | /** | 116 | /** |
102 | * struct iwl_rate_scale_priv -- driver's rate scaling private structure | 117 | * struct iwl4965_lq_sta -- driver's rate scaling private structure |
103 | * | 118 | * |
104 | * Pointer to this gets passed back and forth between driver and mac80211. | 119 | * Pointer to this gets passed back and forth between driver and mac80211. |
105 | */ | 120 | */ |
@@ -136,9 +151,16 @@ struct iwl4965_lq_sta { | |||
136 | 151 | ||
137 | struct iwl4965_link_quality_cmd lq; | 152 | struct iwl4965_link_quality_cmd lq; |
138 | struct iwl4965_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */ | 153 | struct iwl4965_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */ |
154 | #ifdef CONFIG_IWL4965_HT | ||
155 | struct iwl4965_traffic_load load[TID_MAX_LOAD_COUNT]; | ||
156 | u8 tx_agg_tid_en; | ||
157 | #endif | ||
139 | #ifdef CONFIG_MAC80211_DEBUGFS | 158 | #ifdef CONFIG_MAC80211_DEBUGFS |
140 | struct dentry *rs_sta_dbgfs_scale_table_file; | 159 | struct dentry *rs_sta_dbgfs_scale_table_file; |
141 | struct dentry *rs_sta_dbgfs_stats_table_file; | 160 | struct dentry *rs_sta_dbgfs_stats_table_file; |
161 | #ifdef CONFIG_IWL4965_HT | ||
162 | struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file; | ||
163 | #endif | ||
142 | struct iwl4965_rate dbg_fixed; | 164 | struct iwl4965_rate dbg_fixed; |
143 | struct iwl4965_priv *drv; | 165 | struct iwl4965_priv *drv; |
144 | #endif | 166 | #endif |
@@ -269,6 +291,135 @@ static void rs_rate_scale_clear_window(struct iwl4965_rate_scale_data *window) | |||
269 | window->stamp = 0; | 291 | window->stamp = 0; |
270 | } | 292 | } |
271 | 293 | ||
294 | #ifdef CONFIG_IWL4965_HT | ||
295 | /* | ||
296 | * removes the old data from the statistics. All data that is older than | ||
297 | * TID_MAX_TIME_DIFF, will be deleted. | ||
298 | */ | ||
299 | static void rs_tl_rm_old_stats(struct iwl4965_traffic_load *tl, u32 curr_time) | ||
300 | { | ||
301 | /* The oldest age we want to keep */ | ||
302 | u32 oldest_time = curr_time - TID_MAX_TIME_DIFF; | ||
303 | |||
304 | while (tl->queue_count && | ||
305 | (tl->time_stamp < oldest_time)) { | ||
306 | tl->total -= tl->packet_count[tl->head]; | ||
307 | tl->packet_count[tl->head] = 0; | ||
308 | tl->time_stamp += TID_QUEUE_CELL_SPACING; | ||
309 | tl->queue_count--; | ||
310 | tl->head++; | ||
311 | if (tl->head >= TID_QUEUE_MAX_SIZE) | ||
312 | tl->head = 0; | ||
313 | } | ||
314 | } | ||
315 | |||
316 | /* | ||
317 | * increment traffic load value for tid and also remove | ||
318 | * any old values if passed the certain time period | ||
319 | */ | ||
320 | static void rs_tl_add_packet(struct iwl4965_lq_sta *lq_data, u8 tid) | ||
321 | { | ||
322 | u32 curr_time = jiffies_to_msecs(jiffies); | ||
323 | u32 time_diff; | ||
324 | s32 index; | ||
325 | struct iwl4965_traffic_load *tl = NULL; | ||
326 | |||
327 | if (tid >= TID_MAX_LOAD_COUNT) | ||
328 | return; | ||
329 | |||
330 | tl = &lq_data->load[tid]; | ||
331 | |||
332 | curr_time -= curr_time % TID_ROUND_VALUE; | ||
333 | |||
334 | /* Happens only for the first packet. Initialize the data */ | ||
335 | if (!(tl->queue_count)) { | ||
336 | tl->total = 1; | ||
337 | tl->time_stamp = curr_time; | ||
338 | tl->queue_count = 1; | ||
339 | tl->head = 0; | ||
340 | tl->packet_count[0] = 1; | ||
341 | return; | ||
342 | } | ||
343 | |||
344 | time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); | ||
345 | index = time_diff / TID_QUEUE_CELL_SPACING; | ||
346 | |||
347 | /* The history is too long: remove data that is older than */ | ||
348 | /* TID_MAX_TIME_DIFF */ | ||
349 | if (index >= TID_QUEUE_MAX_SIZE) | ||
350 | rs_tl_rm_old_stats(tl, curr_time); | ||
351 | |||
352 | index = (tl->head + index) % TID_QUEUE_MAX_SIZE; | ||
353 | tl->packet_count[index] = tl->packet_count[index] + 1; | ||
354 | tl->total = tl->total + 1; | ||
355 | |||
356 | if ((index + 1) > tl->queue_count) | ||
357 | tl->queue_count = index + 1; | ||
358 | } | ||
359 | |||
360 | /* | ||
361 | get the traffic load value for tid | ||
362 | */ | ||
363 | static u32 rs_tl_get_load(struct iwl4965_lq_sta *lq_data, u8 tid) | ||
364 | { | ||
365 | u32 curr_time = jiffies_to_msecs(jiffies); | ||
366 | u32 time_diff; | ||
367 | s32 index; | ||
368 | struct iwl4965_traffic_load *tl = NULL; | ||
369 | |||
370 | if (tid >= TID_MAX_LOAD_COUNT) | ||
371 | return 0; | ||
372 | |||
373 | tl = &(lq_data->load[tid]); | ||
374 | |||
375 | curr_time -= curr_time % TID_ROUND_VALUE; | ||
376 | |||
377 | if (!(tl->queue_count)) | ||
378 | return 0; | ||
379 | |||
380 | time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); | ||
381 | index = time_diff / TID_QUEUE_CELL_SPACING; | ||
382 | |||
383 | /* The history is too long: remove data that is older than */ | ||
384 | /* TID_MAX_TIME_DIFF */ | ||
385 | if (index >= TID_QUEUE_MAX_SIZE) | ||
386 | rs_tl_rm_old_stats(tl, curr_time); | ||
387 | |||
388 | return tl->total; | ||
389 | } | ||
390 | |||
391 | static void rs_tl_turn_on_agg_for_tid(struct iwl4965_priv *priv, | ||
392 | struct iwl4965_lq_sta *lq_data, u8 tid, | ||
393 | struct sta_info *sta) | ||
394 | { | ||
395 | unsigned long state; | ||
396 | DECLARE_MAC_BUF(mac); | ||
397 | |||
398 | spin_lock_bh(&sta->ampdu_mlme.ampdu_tx); | ||
399 | state = sta->ampdu_mlme.tid_tx[tid].state; | ||
400 | spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx); | ||
401 | |||
402 | if (state == HT_AGG_STATE_IDLE && | ||
403 | rs_tl_get_load(lq_data, tid) > IWL_AGG_LOAD_THRESHOLD) { | ||
404 | IWL_DEBUG_HT("Starting Tx agg: STA: %s tid: %d\n", | ||
405 | print_mac(mac, sta->addr), tid); | ||
406 | ieee80211_start_tx_ba_session(priv->hw, sta->addr, tid); | ||
407 | } | ||
408 | } | ||
409 | |||
410 | static void rs_tl_turn_on_agg(struct iwl4965_priv *priv, u8 tid, | ||
411 | struct iwl4965_lq_sta *lq_data, | ||
412 | struct sta_info *sta) | ||
413 | { | ||
414 | if ((tid < TID_MAX_LOAD_COUNT)) | ||
415 | rs_tl_turn_on_agg_for_tid(priv, lq_data, tid, sta); | ||
416 | else if (tid == IWL_AGG_ALL_TID) | ||
417 | for (tid = 0; tid < TID_MAX_LOAD_COUNT; tid++) | ||
418 | rs_tl_turn_on_agg_for_tid(priv, lq_data, tid, sta); | ||
419 | } | ||
420 | |||
421 | #endif /* CONFIG_IWLWIFI_HT */ | ||
422 | |||
272 | /** | 423 | /** |
273 | * rs_collect_tx_data - Update the success/failure sliding window | 424 | * rs_collect_tx_data - Update the success/failure sliding window |
274 | * | 425 | * |
@@ -1134,7 +1285,7 @@ static int rs_switch_to_mimo(struct iwl4965_priv *priv, | |||
1134 | return 0; | 1285 | return 0; |
1135 | #else | 1286 | #else |
1136 | return -1; | 1287 | return -1; |
1137 | #endif /*CONFIG_IWL4965_HT */ | 1288 | #endif /*CONFIG_IWL4965_HT */ |
1138 | } | 1289 | } |
1139 | 1290 | ||
1140 | /* | 1291 | /* |
@@ -1197,7 +1348,7 @@ static int rs_switch_to_siso(struct iwl4965_priv *priv, | |||
1197 | #else | 1348 | #else |
1198 | return -1; | 1349 | return -1; |
1199 | 1350 | ||
1200 | #endif /*CONFIG_IWL4965_HT */ | 1351 | #endif /*CONFIG_IWL4965_HT */ |
1201 | } | 1352 | } |
1202 | 1353 | ||
1203 | /* | 1354 | /* |
@@ -1354,6 +1505,7 @@ static int rs_move_siso_to_other(struct iwl4965_priv *priv, | |||
1354 | break; | 1505 | break; |
1355 | case IWL_SISO_SWITCH_GI: | 1506 | case IWL_SISO_SWITCH_GI: |
1356 | IWL_DEBUG_HT("LQ: SISO SWITCH TO GI\n"); | 1507 | IWL_DEBUG_HT("LQ: SISO SWITCH TO GI\n"); |
1508 | |||
1357 | memcpy(search_tbl, tbl, sz); | 1509 | memcpy(search_tbl, tbl, sz); |
1358 | search_tbl->action = 0; | 1510 | search_tbl->action = 0; |
1359 | if (search_tbl->is_SGI) | 1511 | if (search_tbl->is_SGI) |
@@ -1419,6 +1571,7 @@ static int rs_move_mimo_to_other(struct iwl4965_priv *priv, | |||
1419 | case IWL_MIMO_SWITCH_ANTENNA_B: | 1571 | case IWL_MIMO_SWITCH_ANTENNA_B: |
1420 | IWL_DEBUG_HT("LQ: MIMO SWITCH TO SISO\n"); | 1572 | IWL_DEBUG_HT("LQ: MIMO SWITCH TO SISO\n"); |
1421 | 1573 | ||
1574 | |||
1422 | /* Set up new search table for SISO */ | 1575 | /* Set up new search table for SISO */ |
1423 | memcpy(search_tbl, tbl, sz); | 1576 | memcpy(search_tbl, tbl, sz); |
1424 | search_tbl->lq_type = LQ_SISO; | 1577 | search_tbl->lq_type = LQ_SISO; |
@@ -1603,6 +1756,10 @@ static void rs_rate_scale_perform(struct iwl4965_priv *priv, | |||
1603 | u8 active_tbl = 0; | 1756 | u8 active_tbl = 0; |
1604 | u8 done_search = 0; | 1757 | u8 done_search = 0; |
1605 | u16 high_low; | 1758 | u16 high_low; |
1759 | #ifdef CONFIG_IWL4965_HT | ||
1760 | u8 tid = MAX_TID_COUNT; | ||
1761 | __le16 *qc; | ||
1762 | #endif | ||
1606 | 1763 | ||
1607 | IWL_DEBUG_RATE("rate scale calculate new rate for skb\n"); | 1764 | IWL_DEBUG_RATE("rate scale calculate new rate for skb\n"); |
1608 | 1765 | ||
@@ -1623,6 +1780,13 @@ static void rs_rate_scale_perform(struct iwl4965_priv *priv, | |||
1623 | } | 1780 | } |
1624 | lq_sta = (struct iwl4965_lq_sta *)sta->rate_ctrl_priv; | 1781 | lq_sta = (struct iwl4965_lq_sta *)sta->rate_ctrl_priv; |
1625 | 1782 | ||
1783 | #ifdef CONFIG_IWL4965_HT | ||
1784 | qc = ieee80211_get_qos_ctrl(hdr); | ||
1785 | if (qc) { | ||
1786 | tid = (u8)(le16_to_cpu(*qc) & 0xf); | ||
1787 | rs_tl_add_packet(lq_sta, tid); | ||
1788 | } | ||
1789 | #endif | ||
1626 | /* | 1790 | /* |
1627 | * Select rate-scale / modulation-mode table to work with in | 1791 | * Select rate-scale / modulation-mode table to work with in |
1628 | * the rest of this function: "search" if searching for better | 1792 | * the rest of this function: "search" if searching for better |
@@ -1943,15 +2107,14 @@ static void rs_rate_scale_perform(struct iwl4965_priv *priv, | |||
1943 | * mode for a while before next round of mode comparisons. */ | 2107 | * mode for a while before next round of mode comparisons. */ |
1944 | if (lq_sta->enable_counter && | 2108 | if (lq_sta->enable_counter && |
1945 | (lq_sta->action_counter >= IWL_ACTION_LIMIT)) { | 2109 | (lq_sta->action_counter >= IWL_ACTION_LIMIT)) { |
1946 | #ifdef CONFIG_IWL4965_HT_AGG | 2110 | #ifdef CONFIG_IWL4965_HT |
1947 | /* If appropriate, set up aggregation! */ | 2111 | if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) && |
1948 | if ((lq_sta->last_tpt > TID_AGG_TPT_THREHOLD) && | 2112 | (lq_sta->tx_agg_tid_en & (1 << tid)) && |
1949 | (priv->lq_mngr.agg_ctrl.auto_agg)) { | 2113 | (tid != MAX_TID_COUNT)) { |
1950 | priv->lq_mngr.agg_ctrl.tid_retry = | 2114 | IWL_DEBUG_HT("try to aggregate tid %d\n", tid); |
1951 | TID_ALL_SPECIFIED; | 2115 | rs_tl_turn_on_agg(priv, tid, lq_sta, sta); |
1952 | schedule_work(&priv->agg_work); | ||
1953 | } | 2116 | } |
1954 | #endif /*CONFIG_IWL4965_HT_AGG */ | 2117 | #endif /*CONFIG_IWL4965_HT */ |
1955 | lq_sta->action_counter = 0; | 2118 | lq_sta->action_counter = 0; |
1956 | rs_set_stay_in_table(0, lq_sta); | 2119 | rs_set_stay_in_table(0, lq_sta); |
1957 | } | 2120 | } |
@@ -2209,6 +2372,8 @@ static void rs_rate_init(void *priv_rate, void *priv_sta, | |||
2209 | IWL_DEBUG_HT("SISO RATE 0x%X MIMO RATE 0x%X\n", | 2372 | IWL_DEBUG_HT("SISO RATE 0x%X MIMO RATE 0x%X\n", |
2210 | lq_sta->active_siso_rate, | 2373 | lq_sta->active_siso_rate, |
2211 | lq_sta->active_mimo_rate); | 2374 | lq_sta->active_mimo_rate); |
2375 | /* as default allow aggregation for all tids */ | ||
2376 | lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID; | ||
2212 | #endif /*CONFIG_IWL4965_HT*/ | 2377 | #endif /*CONFIG_IWL4965_HT*/ |
2213 | #ifdef CONFIG_MAC80211_DEBUGFS | 2378 | #ifdef CONFIG_MAC80211_DEBUGFS |
2214 | lq_sta->drv = priv; | 2379 | lq_sta->drv = priv; |
@@ -2352,12 +2517,6 @@ static void rs_clear(void *priv_rate) | |||
2352 | IWL_DEBUG_RATE("enter\n"); | 2517 | IWL_DEBUG_RATE("enter\n"); |
2353 | 2518 | ||
2354 | priv->lq_mngr.lq_ready = 0; | 2519 | priv->lq_mngr.lq_ready = 0; |
2355 | #ifdef CONFIG_IWL4965_HT | ||
2356 | #ifdef CONFIG_IWL4965_HT_AGG | ||
2357 | if (priv->lq_mngr.agg_ctrl.granted_ba) | ||
2358 | iwl4965_turn_off_agg(priv, TID_ALL_SPECIFIED); | ||
2359 | #endif /*CONFIG_IWL4965_HT_AGG */ | ||
2360 | #endif /* CONFIG_IWL4965_HT */ | ||
2361 | 2520 | ||
2362 | IWL_DEBUG_RATE("leave\n"); | 2521 | IWL_DEBUG_RATE("leave\n"); |
2363 | } | 2522 | } |
@@ -2524,6 +2683,12 @@ static void rs_add_debugfs(void *priv, void *priv_sta, | |||
2524 | lq_sta->rs_sta_dbgfs_stats_table_file = | 2683 | lq_sta->rs_sta_dbgfs_stats_table_file = |
2525 | debugfs_create_file("rate_stats_table", 0600, dir, | 2684 | debugfs_create_file("rate_stats_table", 0600, dir, |
2526 | lq_sta, &rs_sta_dbgfs_stats_table_ops); | 2685 | lq_sta, &rs_sta_dbgfs_stats_table_ops); |
2686 | #ifdef CONFIG_IWL4965_HT | ||
2687 | lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file = | ||
2688 | debugfs_create_u8("tx_agg_tid_enable", 0600, dir, | ||
2689 | &lq_sta->tx_agg_tid_en); | ||
2690 | #endif | ||
2691 | |||
2527 | } | 2692 | } |
2528 | 2693 | ||
2529 | static void rs_remove_debugfs(void *priv, void *priv_sta) | 2694 | static void rs_remove_debugfs(void *priv, void *priv_sta) |
@@ -2531,6 +2696,9 @@ static void rs_remove_debugfs(void *priv, void *priv_sta) | |||
2531 | struct iwl4965_lq_sta *lq_sta = priv_sta; | 2696 | struct iwl4965_lq_sta *lq_sta = priv_sta; |
2532 | debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file); | 2697 | debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file); |
2533 | debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file); | 2698 | debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file); |
2699 | #ifdef CONFIG_IWL4965_HT | ||
2700 | debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file); | ||
2701 | #endif | ||
2534 | } | 2702 | } |
2535 | #endif | 2703 | #endif |
2536 | 2704 | ||