aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
authorMatti Gottlieb <matti.gottlieb@intel.com>2013-11-18 12:06:45 -0500
committerJohannes Berg <johannes.berg@intel.com>2013-12-02 05:51:50 -0500
commitad38bfc916da6aee9160bfa5335aed8d6c190e39 (patch)
treebc00170187f5e2036a8c804f546c32d94bdee349 /net/mac80211
parent1d940aaab881b0ee62557ffbaad877ac5a1b51db (diff)
mac80211: Tx frame latency statistics
Measure TX latency and jitter statistics per station per TID. These Measurements are disabled by default and can be enabled via debugfs. Features included for each station's TID: 1. Keep count of the maximum and average latency of Tx frames. 2. Keep track of many frames arrived in a specific time range (need to enable through debugfs and configure the bins ranges) Signed-off-by: Matti Gottlieb <matti.gottlieb@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/debugfs.c168
-rw-r--r--net/mac80211/debugfs_sta.c134
-rw-r--r--net/mac80211/ieee80211_i.h24
-rw-r--r--net/mac80211/main.c2
-rw-r--r--net/mac80211/sta_info.c34
-rw-r--r--net/mac80211/sta_info.h22
-rw-r--r--net/mac80211/status.c78
-rw-r--r--net/mac80211/tx.c24
8 files changed, 486 insertions, 0 deletions
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 5c090e41d9bb..fa16e54980a1 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -17,6 +17,172 @@
17 17
18#define DEBUGFS_FORMAT_BUFFER_SIZE 100 18#define DEBUGFS_FORMAT_BUFFER_SIZE 100
19 19
20#define TX_LATENCY_BIN_DELIMTER_C ','
21#define TX_LATENCY_BIN_DELIMTER_S ","
22#define TX_LATENCY_BINS_DISABLED "enable(bins disabled)\n"
23#define TX_LATENCY_DISABLED "disable\n"
24
25
26/*
27 * Display if Tx latency statistics & bins are enabled/disabled
28 */
29static ssize_t sta_tx_latency_stat_read(struct file *file,
30 char __user *userbuf,
31 size_t count, loff_t *ppos)
32{
33 struct ieee80211_local *local = file->private_data;
34 struct ieee80211_tx_latency_bin_ranges *tx_latency;
35 char *buf;
36 int bufsz, i, ret;
37 int pos = 0;
38
39 rcu_read_lock();
40
41 tx_latency = rcu_dereference(local->tx_latency);
42
43 if (tx_latency && tx_latency->n_ranges) {
44 bufsz = tx_latency->n_ranges * 15;
45 buf = kzalloc(bufsz, GFP_ATOMIC);
46 if (!buf)
47 goto err;
48
49 for (i = 0; i < tx_latency->n_ranges; i++)
50 pos += scnprintf(buf + pos, bufsz - pos, "%d,",
51 tx_latency->ranges[i]);
52 pos += scnprintf(buf + pos, bufsz - pos, "\n");
53 } else if (tx_latency) {
54 bufsz = sizeof(TX_LATENCY_BINS_DISABLED) + 1;
55 buf = kzalloc(bufsz, GFP_ATOMIC);
56 if (!buf)
57 goto err;
58
59 pos += scnprintf(buf + pos, bufsz - pos, "%s\n",
60 TX_LATENCY_BINS_DISABLED);
61 } else {
62 bufsz = sizeof(TX_LATENCY_DISABLED) + 1;
63 buf = kzalloc(bufsz, GFP_ATOMIC);
64 if (!buf)
65 goto err;
66
67 pos += scnprintf(buf + pos, bufsz - pos, "%s\n",
68 TX_LATENCY_DISABLED);
69 }
70
71 rcu_read_unlock();
72
73 ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
74 kfree(buf);
75
76 return ret;
77err:
78 rcu_read_unlock();
79 return -ENOMEM;
80}
81
82/*
83 * Receive input from user regarding Tx latency statistics
84 * The input should indicate if Tx latency statistics and bins are
85 * enabled/disabled.
86 * If bins are enabled input should indicate the amount of different bins and
87 * their ranges. Each bin will count how many Tx frames transmitted within the
88 * appropriate latency.
89 * Legal input is:
90 * a) "enable(bins disabled)" - to enable only general statistics
91 * b) "a,b,c,d,...z" - to enable general statistics and bins, where all are
92 * numbers and a < b < c < d.. < z
93 * c) "disable" - disable all statistics
94 * NOTE: must configure Tx latency statistics bins before stations connected.
95 */
96
97static ssize_t sta_tx_latency_stat_write(struct file *file,
98 const char __user *userbuf,
99 size_t count, loff_t *ppos)
100{
101 struct ieee80211_local *local = file->private_data;
102 char buf[128] = {};
103 char *bins = buf;
104 char *token;
105 int buf_size, i, alloc_size;
106 int prev_bin = 0;
107 int n_ranges = 0;
108 int ret = count;
109 struct ieee80211_tx_latency_bin_ranges *tx_latency;
110
111 if (sizeof(buf) <= count)
112 return -EINVAL;
113 buf_size = count;
114 if (copy_from_user(buf, userbuf, buf_size))
115 return -EFAULT;
116
117 mutex_lock(&local->sta_mtx);
118
119 /* cannot change config once we have stations */
120 if (local->num_sta)
121 goto unlock;
122
123 tx_latency =
124 rcu_dereference_protected(local->tx_latency,
125 lockdep_is_held(&local->sta_mtx));
126
127 /* disable Tx statistics */
128 if (!strcmp(buf, TX_LATENCY_DISABLED)) {
129 if (!tx_latency)
130 goto unlock;
131 rcu_assign_pointer(local->tx_latency, NULL);
132 synchronize_rcu();
133 kfree(tx_latency);
134 goto unlock;
135 }
136
137 /* Tx latency already enabled */
138 if (tx_latency)
139 goto unlock;
140
141 if (strcmp(TX_LATENCY_BINS_DISABLED, buf)) {
142 /* check how many bins and between what ranges user requested */
143 token = buf;
144 while (*token != '\0') {
145 if (*token == TX_LATENCY_BIN_DELIMTER_C)
146 n_ranges++;
147 token++;
148 }
149 n_ranges++;
150 }
151
152 alloc_size = sizeof(struct ieee80211_tx_latency_bin_ranges) +
153 n_ranges * sizeof(u32);
154 tx_latency = kzalloc(alloc_size, GFP_ATOMIC);
155 if (!tx_latency) {
156 ret = -ENOMEM;
157 goto unlock;
158 }
159 tx_latency->n_ranges = n_ranges;
160 for (i = 0; i < n_ranges; i++) { /* setting bin ranges */
161 token = strsep(&bins, TX_LATENCY_BIN_DELIMTER_S);
162 sscanf(token, "%d", &tx_latency->ranges[i]);
163 /* bins values should be in ascending order */
164 if (prev_bin >= tx_latency->ranges[i]) {
165 ret = -EINVAL;
166 kfree(tx_latency);
167 goto unlock;
168 }
169 prev_bin = tx_latency->ranges[i];
170 }
171 rcu_assign_pointer(local->tx_latency, tx_latency);
172
173unlock:
174 mutex_unlock(&local->sta_mtx);
175
176 return ret;
177}
178
179static const struct file_operations stats_tx_latency_ops = {
180 .write = sta_tx_latency_stat_write,
181 .read = sta_tx_latency_stat_read,
182 .open = simple_open,
183 .llseek = generic_file_llseek,
184};
185
20int mac80211_format_buffer(char __user *userbuf, size_t count, 186int mac80211_format_buffer(char __user *userbuf, size_t count,
21 loff_t *ppos, char *fmt, ...) 187 loff_t *ppos, char *fmt, ...)
22{ 188{
@@ -315,4 +481,6 @@ void debugfs_hw_add(struct ieee80211_local *local)
315 DEBUGFS_DEVSTATS_ADD(dot11RTSFailureCount); 481 DEBUGFS_DEVSTATS_ADD(dot11RTSFailureCount);
316 DEBUGFS_DEVSTATS_ADD(dot11FCSErrorCount); 482 DEBUGFS_DEVSTATS_ADD(dot11FCSErrorCount);
317 DEBUGFS_DEVSTATS_ADD(dot11RTSSuccessCount); 483 DEBUGFS_DEVSTATS_ADD(dot11RTSSuccessCount);
484
485 DEBUGFS_DEVSTATS_ADD(tx_latency);
318} 486}
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 19c54a44ed47..80194b557a0c 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -38,6 +38,13 @@ static const struct file_operations sta_ ##name## _ops = { \
38 .llseek = generic_file_llseek, \ 38 .llseek = generic_file_llseek, \
39} 39}
40 40
41#define STA_OPS_W(name) \
42static const struct file_operations sta_ ##name## _ops = { \
43 .write = sta_##name##_write, \
44 .open = simple_open, \
45 .llseek = generic_file_llseek, \
46}
47
41#define STA_OPS_RW(name) \ 48#define STA_OPS_RW(name) \
42static const struct file_operations sta_ ##name## _ops = { \ 49static const struct file_operations sta_ ##name## _ops = { \
43 .read = sta_##name##_read, \ 50 .read = sta_##name##_read, \
@@ -388,6 +395,131 @@ static ssize_t sta_last_rx_rate_read(struct file *file, char __user *userbuf,
388} 395}
389STA_OPS(last_rx_rate); 396STA_OPS(last_rx_rate);
390 397
398static int
399sta_tx_latency_stat_header(struct ieee80211_tx_latency_bin_ranges *tx_latency,
400 char *buf, int pos, int bufsz)
401{
402 int i;
403 int range_count = tx_latency->n_ranges;
404 u32 *bin_ranges = tx_latency->ranges;
405
406 pos += scnprintf(buf + pos, bufsz - pos,
407 "Station\t\t\tTID\tMax\tAvg");
408 if (range_count) {
409 pos += scnprintf(buf + pos, bufsz - pos,
410 "\t<=%d", bin_ranges[0]);
411 for (i = 0; i < range_count - 1; i++)
412 pos += scnprintf(buf + pos, bufsz - pos, "\t%d-%d",
413 bin_ranges[i], bin_ranges[i+1]);
414 pos += scnprintf(buf + pos, bufsz - pos,
415 "\t%d<", bin_ranges[range_count - 1]);
416 }
417
418 pos += scnprintf(buf + pos, bufsz - pos, "\n");
419
420 return pos;
421}
422
423static int
424sta_tx_latency_stat_table(struct ieee80211_tx_latency_bin_ranges *tx_lat_range,
425 struct ieee80211_tx_latency_stat *tx_lat,
426 char *buf, int pos, int bufsz, int tid)
427{
428 u32 avg = 0;
429 int j;
430 int bin_count = tx_lat->bin_count;
431
432 pos += scnprintf(buf + pos, bufsz - pos, "\t\t\t%d", tid);
433 /* make sure you don't divide in 0 */
434 if (tx_lat->counter)
435 avg = tx_lat->sum / tx_lat->counter;
436
437 pos += scnprintf(buf + pos, bufsz - pos, "\t%d\t%d",
438 tx_lat->max, avg);
439
440 if (tx_lat_range->n_ranges && tx_lat->bins)
441 for (j = 0; j < bin_count; j++)
442 pos += scnprintf(buf + pos, bufsz - pos,
443 "\t%d", tx_lat->bins[j]);
444 pos += scnprintf(buf + pos, bufsz - pos, "\n");
445
446 return pos;
447}
448
449/*
450 * Output Tx latency statistics station && restart all statistics information
451 */
452static ssize_t sta_tx_latency_stat_read(struct file *file,
453 char __user *userbuf,
454 size_t count, loff_t *ppos)
455{
456 struct sta_info *sta = file->private_data;
457 struct ieee80211_local *local = sta->local;
458 struct ieee80211_tx_latency_bin_ranges *tx_latency;
459 char *buf;
460 int bufsz, ret, i;
461 int pos = 0;
462
463 bufsz = 20 * IEEE80211_NUM_TIDS *
464 sizeof(struct ieee80211_tx_latency_stat);
465 buf = kzalloc(bufsz, GFP_KERNEL);
466 if (!buf)
467 return -ENOMEM;
468
469 rcu_read_lock();
470
471 tx_latency = rcu_dereference(local->tx_latency);
472
473 if (!sta->tx_lat) {
474 pos += scnprintf(buf + pos, bufsz - pos,
475 "Tx latency statistics are not enabled\n");
476 goto unlock;
477 }
478
479 pos = sta_tx_latency_stat_header(tx_latency, buf, pos, bufsz);
480
481 pos += scnprintf(buf + pos, bufsz - pos, "%pM\n", sta->sta.addr);
482 for (i = 0; i < IEEE80211_NUM_TIDS; i++)
483 pos = sta_tx_latency_stat_table(tx_latency, &sta->tx_lat[i],
484 buf, pos, bufsz, i);
485unlock:
486 rcu_read_unlock();
487
488 ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
489 kfree(buf);
490
491 return ret;
492}
493STA_OPS(tx_latency_stat);
494
495static ssize_t sta_tx_latency_stat_reset_write(struct file *file,
496 const char __user *userbuf,
497 size_t count, loff_t *ppos)
498{
499 u32 *bins;
500 int bin_count;
501 struct sta_info *sta = file->private_data;
502 int i;
503
504 if (!sta->tx_lat)
505 return -EINVAL;
506
507 for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
508 bins = sta->tx_lat[i].bins;
509 bin_count = sta->tx_lat[i].bin_count;
510
511 sta->tx_lat[i].max = 0;
512 sta->tx_lat[i].sum = 0;
513 sta->tx_lat[i].counter = 0;
514
515 if (bin_count)
516 memset(bins, 0, bin_count * sizeof(u32));
517 }
518
519 return count;
520}
521STA_OPS_W(tx_latency_stat_reset);
522
391#define DEBUGFS_ADD(name) \ 523#define DEBUGFS_ADD(name) \
392 debugfs_create_file(#name, 0400, \ 524 debugfs_create_file(#name, 0400, \
393 sta->debugfs.dir, sta, &sta_ ##name## _ops); 525 sta->debugfs.dir, sta, &sta_ ##name## _ops);
@@ -441,6 +573,8 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
441 DEBUGFS_ADD(last_ack_signal); 573 DEBUGFS_ADD(last_ack_signal);
442 DEBUGFS_ADD(current_tx_rate); 574 DEBUGFS_ADD(current_tx_rate);
443 DEBUGFS_ADD(last_rx_rate); 575 DEBUGFS_ADD(last_rx_rate);
576 DEBUGFS_ADD(tx_latency_stat);
577 DEBUGFS_ADD(tx_latency_stat_reset);
444 578
445 DEBUGFS_ADD_COUNTER(rx_packets, rx_packets); 579 DEBUGFS_ADD_COUNTER(rx_packets, rx_packets);
446 DEBUGFS_ADD_COUNTER(tx_packets, tx_packets); 580 DEBUGFS_ADD_COUNTER(tx_packets, tx_packets);
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 4022ee1afb9f..834f0eb3e420 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -898,6 +898,24 @@ struct tpt_led_trigger {
898}; 898};
899#endif 899#endif
900 900
901/*
902 * struct ieee80211_tx_latency_bin_ranges - Tx latency statistics bins ranges
903 *
904 * Measuring Tx latency statistics. Counts how many Tx frames transmitted in a
905 * certain latency range (in Milliseconds). Each station that uses these
906 * ranges will have bins to count the amount of frames received in that range.
907 * The user can configure the ranges via debugfs.
908 * If ranges is NULL then Tx latency statistics bins are disabled for all
909 * stations.
910 *
911 * @n_ranges: number of ranges that are taken in account
912 * @ranges: the ranges that the user requested or NULL if disabled.
913 */
914struct ieee80211_tx_latency_bin_ranges {
915 int n_ranges;
916 u32 ranges[];
917};
918
901/** 919/**
902 * mac80211 scan flags - currently active scan mode 920 * mac80211 scan flags - currently active scan mode
903 * 921 *
@@ -1050,6 +1068,12 @@ struct ieee80211_local {
1050 struct timer_list sta_cleanup; 1068 struct timer_list sta_cleanup;
1051 int sta_generation; 1069 int sta_generation;
1052 1070
1071 /*
1072 * Tx latency statistics parameters for all stations.
1073 * Can enable via debugfs (NULL when disabled).
1074 */
1075 struct ieee80211_tx_latency_bin_ranges __rcu *tx_latency;
1076
1053 struct sk_buff_head pending[IEEE80211_MAX_QUEUES]; 1077 struct sk_buff_head pending[IEEE80211_MAX_QUEUES];
1054 struct tasklet_struct tx_pending_tasklet; 1078 struct tasklet_struct tx_pending_tasklet;
1055 1079
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index bdb0b6c104b5..8af75f0eed6d 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -1142,6 +1142,8 @@ void ieee80211_free_hw(struct ieee80211_hw *hw)
1142 ieee80211_free_ack_frame, NULL); 1142 ieee80211_free_ack_frame, NULL);
1143 idr_destroy(&local->ack_status_frames); 1143 idr_destroy(&local->ack_status_frames);
1144 1144
1145 kfree(rcu_access_pointer(local->tx_latency));
1146
1145 wiphy_free(local->hw.wiphy); 1147 wiphy_free(local->hw.wiphy);
1146} 1148}
1147EXPORT_SYMBOL(ieee80211_free_hw); 1149EXPORT_SYMBOL(ieee80211_free_hw);
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 7b69d4c3db55..8ed97f76c3cf 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -266,9 +266,17 @@ struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata,
266 */ 266 */
267void sta_info_free(struct ieee80211_local *local, struct sta_info *sta) 267void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
268{ 268{
269 int i;
270
269 if (sta->rate_ctrl) 271 if (sta->rate_ctrl)
270 rate_control_free_sta(sta); 272 rate_control_free_sta(sta);
271 273
274 if (sta->tx_lat) {
275 for (i = 0; i < IEEE80211_NUM_TIDS; i++)
276 kfree(sta->tx_lat[i].bins);
277 kfree(sta->tx_lat);
278 }
279
272 sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr); 280 sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr);
273 281
274 kfree(sta); 282 kfree(sta);
@@ -333,6 +341,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
333 struct ieee80211_local *local = sdata->local; 341 struct ieee80211_local *local = sdata->local;
334 struct sta_info *sta; 342 struct sta_info *sta;
335 struct timespec uptime; 343 struct timespec uptime;
344 struct ieee80211_tx_latency_bin_ranges *tx_latency;
336 int i; 345 int i;
337 346
338 sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp); 347 sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp);
@@ -410,6 +419,31 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
410 } 419 }
411 } 420 }
412 421
422 rcu_read_lock();
423
424 tx_latency = rcu_dereference(local->tx_latency);
425 /* init stations Tx latency statistics && TID bins */
426 if (tx_latency)
427 sta->tx_lat = kzalloc(IEEE80211_NUM_TIDS *
428 sizeof(struct ieee80211_tx_latency_stat),
429 GFP_ATOMIC);
430
431 /*
432 * if Tx latency and bins are enabled and the previous allocation
433 * succeeded
434 */
435 if (tx_latency && tx_latency->n_ranges && sta->tx_lat)
436 for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
437 /* size of bins is size of the ranges +1 */
438 sta->tx_lat[i].bin_count =
439 tx_latency->n_ranges + 1;
440 sta->tx_lat[i].bins = kcalloc(sta->tx_lat[i].bin_count,
441 sizeof(u32),
442 GFP_ATOMIC);
443 }
444
445 rcu_read_unlock();
446
413 sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr); 447 sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
414 448
415 return sta; 449 return sta;
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index a2c9a4c45aa3..0218caf5c14a 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -220,6 +220,25 @@ struct sta_ampdu_mlme {
220 u8 dialog_token_allocator; 220 u8 dialog_token_allocator;
221}; 221};
222 222
223/*
224 * struct ieee80211_tx_latency_stat - Tx latency statistics
225 *
226 * Measures TX latency and jitter for a station per TID.
227 *
228 * @max: worst case latency
229 * @sum: sum of all latencies
230 * @counter: amount of Tx frames sent from interface
231 * @bins: each bin counts how many frames transmitted within a certain
232 * latency range. when disabled it is NULL.
233 * @bin_count: amount of bins.
234 */
235struct ieee80211_tx_latency_stat {
236 u32 max;
237 u32 sum;
238 u32 counter;
239 u32 *bins;
240 u32 bin_count;
241};
223 242
224/** 243/**
225 * struct sta_info - STA information 244 * struct sta_info - STA information
@@ -276,6 +295,7 @@ struct sta_ampdu_mlme {
276 * @tid_seq: per-TID sequence numbers for sending to this STA 295 * @tid_seq: per-TID sequence numbers for sending to this STA
277 * @ampdu_mlme: A-MPDU state machine state 296 * @ampdu_mlme: A-MPDU state machine state
278 * @timer_to_tid: identity mapping to ID timers 297 * @timer_to_tid: identity mapping to ID timers
298 * @tx_lat: Tx latency statistics
279 * @llid: Local link ID 299 * @llid: Local link ID
280 * @plid: Peer link ID 300 * @plid: Peer link ID
281 * @reason: Cancel reason on PLINK_HOLDING state 301 * @reason: Cancel reason on PLINK_HOLDING state
@@ -385,6 +405,8 @@ struct sta_info {
385 struct sta_ampdu_mlme ampdu_mlme; 405 struct sta_ampdu_mlme ampdu_mlme;
386 u8 timer_to_tid[IEEE80211_NUM_TIDS]; 406 u8 timer_to_tid[IEEE80211_NUM_TIDS];
387 407
408 struct ieee80211_tx_latency_stat *tx_lat;
409
388#ifdef CONFIG_MAC80211_MESH 410#ifdef CONFIG_MAC80211_MESH
389 /* 411 /*
390 * Mesh peer link attributes 412 * Mesh peer link attributes
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 52a152b01b06..1ee85c402439 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -11,6 +11,7 @@
11 11
12#include <linux/export.h> 12#include <linux/export.h>
13#include <linux/etherdevice.h> 13#include <linux/etherdevice.h>
14#include <linux/time.h>
14#include <net/mac80211.h> 15#include <net/mac80211.h>
15#include <asm/unaligned.h> 16#include <asm/unaligned.h>
16#include "ieee80211_i.h" 17#include "ieee80211_i.h"
@@ -463,6 +464,77 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
463} 464}
464 465
465/* 466/*
467 * Measure Tx frame completion and removal time for Tx latency statistics
468 * calculation. A single Tx frame latency should be measured from when it
469 * is entering the Kernel until we receive Tx complete confirmation indication
470 * and remove the skb.
471 */
472static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local,
473 struct sk_buff *skb,
474 struct sta_info *sta,
475 struct ieee80211_hdr *hdr)
476{
477 ktime_t skb_dprt;
478 struct timespec dprt_time;
479 u32 msrmnt;
480 u16 tid;
481 u8 *qc;
482 int i, bin_range_count, bin_count;
483 u32 *bin_ranges;
484 __le16 fc;
485 struct ieee80211_tx_latency_stat *tx_lat;
486 struct ieee80211_tx_latency_bin_ranges *tx_latency;
487 ktime_t skb_arv = skb->tstamp;
488
489 tx_latency = rcu_dereference(local->tx_latency);
490
491 /* assert Tx latency stats are enabled & frame arrived when enabled */
492 if (!tx_latency || !ktime_to_ns(skb_arv))
493 return;
494
495 fc = hdr->frame_control;
496
497 if (!ieee80211_is_data(fc)) /* make sure it is a data frame */
498 return;
499
500 /* get frame tid */
501 if (ieee80211_is_data_qos(hdr->frame_control)) {
502 qc = ieee80211_get_qos_ctl(hdr);
503 tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
504 } else {
505 tid = 0;
506 }
507
508 tx_lat = &sta->tx_lat[tid];
509
510 ktime_get_ts(&dprt_time); /* time stamp completion time */
511 skb_dprt = ktime_set(dprt_time.tv_sec, dprt_time.tv_nsec);
512 msrmnt = ktime_to_ms(ktime_sub(skb_dprt, skb_arv));
513
514 if (tx_lat->max < msrmnt) /* update stats */
515 tx_lat->max = msrmnt;
516 tx_lat->counter++;
517 tx_lat->sum += msrmnt;
518
519 if (!tx_lat->bins) /* bins not activated */
520 return;
521
522 /* count how many Tx frames transmitted with the appropriate latency */
523 bin_range_count = tx_latency->n_ranges;
524 bin_ranges = tx_latency->ranges;
525 bin_count = tx_lat->bin_count;
526
527 for (i = 0; i < bin_range_count; i++) {
528 if (msrmnt <= bin_ranges[i]) {
529 tx_lat->bins[i]++;
530 break;
531 }
532 }
533 if (i == bin_range_count) /* msrmnt is bigger than the biggest range */
534 tx_lat->bins[i]++;
535}
536
537/*
466 * Use a static threshold for now, best value to be determined 538 * Use a static threshold for now, best value to be determined
467 * by testing ... 539 * by testing ...
468 * Should it depend on: 540 * Should it depend on:
@@ -620,6 +692,12 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
620 692
621 if (acked) 693 if (acked)
622 sta->last_ack_signal = info->status.ack_signal; 694 sta->last_ack_signal = info->status.ack_signal;
695
696 /*
697 * Measure frame removal for tx latency
698 * statistics calculation
699 */
700 ieee80211_tx_latency_end_msrmnt(local, skb, sta, hdr);
623 } 701 }
624 702
625 rcu_read_unlock(); 703 rcu_read_unlock();
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index e541856b4007..6d59e21cdb9f 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -19,6 +19,7 @@
19#include <linux/bitmap.h> 19#include <linux/bitmap.h>
20#include <linux/rcupdate.h> 20#include <linux/rcupdate.h>
21#include <linux/export.h> 21#include <linux/export.h>
22#include <linux/time.h>
22#include <net/net_namespace.h> 23#include <net/net_namespace.h>
23#include <net/ieee80211_radiotap.h> 24#include <net/ieee80211_radiotap.h>
24#include <net/cfg80211.h> 25#include <net/cfg80211.h>
@@ -1741,6 +1742,26 @@ fail:
1741 return NETDEV_TX_OK; /* meaning, we dealt with the skb */ 1742 return NETDEV_TX_OK; /* meaning, we dealt with the skb */
1742} 1743}
1743 1744
1745/*
1746 * Measure Tx frame arrival time for Tx latency statistics calculation
1747 * A single Tx frame latency should be measured from when it is entering the
1748 * Kernel until we receive Tx complete confirmation indication and the skb is
1749 * freed.
1750 */
1751static void ieee80211_tx_latency_start_msrmnt(struct ieee80211_local *local,
1752 struct sk_buff *skb)
1753{
1754 struct timespec skb_arv;
1755 struct ieee80211_tx_latency_bin_ranges *tx_latency;
1756
1757 tx_latency = rcu_dereference(local->tx_latency);
1758 if (!tx_latency)
1759 return;
1760
1761 ktime_get_ts(&skb_arv);
1762 skb->tstamp = ktime_set(skb_arv.tv_sec, skb_arv.tv_nsec);
1763}
1764
1744/** 1765/**
1745 * ieee80211_subif_start_xmit - netif start_xmit function for Ethernet-type 1766 * ieee80211_subif_start_xmit - netif start_xmit function for Ethernet-type
1746 * subinterfaces (wlan#, WDS, and VLAN interfaces) 1767 * subinterfaces (wlan#, WDS, and VLAN interfaces)
@@ -1791,6 +1812,9 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
1791 1812
1792 rcu_read_lock(); 1813 rcu_read_lock();
1793 1814
1815 /* Measure frame arrival for Tx latency statistics calculation */
1816 ieee80211_tx_latency_start_msrmnt(local, skb);
1817
1794 switch (sdata->vif.type) { 1818 switch (sdata->vif.type) {
1795 case NL80211_IFTYPE_AP_VLAN: 1819 case NL80211_IFTYPE_AP_VLAN:
1796 sta = rcu_dereference(sdata->u.vlan.sta); 1820 sta = rcu_dereference(sdata->u.vlan.sta);