diff options
-rw-r--r-- | net/mac80211/debugfs.c | 168 | ||||
-rw-r--r-- | net/mac80211/debugfs_sta.c | 134 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 24 | ||||
-rw-r--r-- | net/mac80211/main.c | 2 | ||||
-rw-r--r-- | net/mac80211/sta_info.c | 34 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 22 | ||||
-rw-r--r-- | net/mac80211/status.c | 78 | ||||
-rw-r--r-- | net/mac80211/tx.c | 24 |
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 | */ | ||
29 | static 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; | ||
77 | err: | ||
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 | |||
97 | static 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 | |||
173 | unlock: | ||
174 | mutex_unlock(&local->sta_mtx); | ||
175 | |||
176 | return ret; | ||
177 | } | ||
178 | |||
179 | static 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 | |||
20 | int mac80211_format_buffer(char __user *userbuf, size_t count, | 186 | int 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) \ | ||
42 | static 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) \ |
42 | static const struct file_operations sta_ ##name## _ops = { \ | 49 | static 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 | } |
389 | STA_OPS(last_rx_rate); | 396 | STA_OPS(last_rx_rate); |
390 | 397 | ||
398 | static int | ||
399 | sta_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 | |||
423 | static int | ||
424 | sta_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 | */ | ||
452 | static 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); | ||
485 | unlock: | ||
486 | rcu_read_unlock(); | ||
487 | |||
488 | ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); | ||
489 | kfree(buf); | ||
490 | |||
491 | return ret; | ||
492 | } | ||
493 | STA_OPS(tx_latency_stat); | ||
494 | |||
495 | static 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 | } | ||
521 | STA_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 | */ | ||
914 | struct 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 | } |
1147 | EXPORT_SYMBOL(ieee80211_free_hw); | 1149 | EXPORT_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 | */ |
267 | void sta_info_free(struct ieee80211_local *local, struct sta_info *sta) | 267 | void 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 | */ | ||
235 | struct 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 | */ | ||
472 | static 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 | */ | ||
1751 | static 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); |