aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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);