diff options
author | John W. Linville <linville@tuxdriver.com> | 2014-05-30 10:47:28 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2014-06-23 05:05:23 -0400 |
commit | 20edb50e593dca7522b4f3a95b801dbf99f24c52 (patch) | |
tree | 71637451c7436f89f4b9e15db94a6c425bf9a406 /net/mac80211 | |
parent | 7171511eaec5bf23fb06078f59784a3a0626b38f (diff) |
mac80211: remove PID rate control
Minstrel has long since proven its worth.
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/Kconfig | 17 | ||||
-rw-r--r-- | net/mac80211/Makefile | 5 | ||||
-rw-r--r-- | net/mac80211/main.c | 7 | ||||
-rw-r--r-- | net/mac80211/rate.h | 13 | ||||
-rw-r--r-- | net/mac80211/rc80211_pid.h | 278 | ||||
-rw-r--r-- | net/mac80211/rc80211_pid_algo.c | 478 | ||||
-rw-r--r-- | net/mac80211/rc80211_pid_debugfs.c | 228 |
7 files changed, 0 insertions, 1026 deletions
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index 97b5dcad5025..aeb6a483b3bc 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig | |||
@@ -19,14 +19,6 @@ if MAC80211 != n | |||
19 | config MAC80211_HAS_RC | 19 | config MAC80211_HAS_RC |
20 | bool | 20 | bool |
21 | 21 | ||
22 | config MAC80211_RC_PID | ||
23 | bool "PID controller based rate control algorithm" if EXPERT | ||
24 | select MAC80211_HAS_RC | ||
25 | ---help--- | ||
26 | This option enables a TX rate control algorithm for | ||
27 | mac80211 that uses a PID controller to select the TX | ||
28 | rate. | ||
29 | |||
30 | config MAC80211_RC_MINSTREL | 22 | config MAC80211_RC_MINSTREL |
31 | bool "Minstrel" if EXPERT | 23 | bool "Minstrel" if EXPERT |
32 | select MAC80211_HAS_RC | 24 | select MAC80211_HAS_RC |
@@ -51,14 +43,6 @@ choice | |||
51 | overridden through the ieee80211_default_rc_algo module | 43 | overridden through the ieee80211_default_rc_algo module |
52 | parameter if different algorithms are available. | 44 | parameter if different algorithms are available. |
53 | 45 | ||
54 | config MAC80211_RC_DEFAULT_PID | ||
55 | bool "PID controller based rate control algorithm" | ||
56 | depends on MAC80211_RC_PID | ||
57 | ---help--- | ||
58 | Select the PID controller based rate control as the | ||
59 | default rate control algorithm. You should choose | ||
60 | this unless you know what you are doing. | ||
61 | |||
62 | config MAC80211_RC_DEFAULT_MINSTREL | 46 | config MAC80211_RC_DEFAULT_MINSTREL |
63 | bool "Minstrel" | 47 | bool "Minstrel" |
64 | depends on MAC80211_RC_MINSTREL | 48 | depends on MAC80211_RC_MINSTREL |
@@ -72,7 +56,6 @@ config MAC80211_RC_DEFAULT | |||
72 | string | 56 | string |
73 | default "minstrel_ht" if MAC80211_RC_DEFAULT_MINSTREL && MAC80211_RC_MINSTREL_HT | 57 | default "minstrel_ht" if MAC80211_RC_DEFAULT_MINSTREL && MAC80211_RC_MINSTREL_HT |
74 | default "minstrel" if MAC80211_RC_DEFAULT_MINSTREL | 58 | default "minstrel" if MAC80211_RC_DEFAULT_MINSTREL |
75 | default "pid" if MAC80211_RC_DEFAULT_PID | ||
76 | default "" | 59 | default "" |
77 | 60 | ||
78 | endif | 61 | endif |
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 1e46ffa69167..4409bf506594 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile | |||
@@ -47,17 +47,12 @@ mac80211-$(CONFIG_PM) += pm.o | |||
47 | 47 | ||
48 | CFLAGS_trace.o := -I$(src) | 48 | CFLAGS_trace.o := -I$(src) |
49 | 49 | ||
50 | # objects for PID algorithm | ||
51 | rc80211_pid-y := rc80211_pid_algo.o | ||
52 | rc80211_pid-$(CONFIG_MAC80211_DEBUGFS) += rc80211_pid_debugfs.o | ||
53 | |||
54 | rc80211_minstrel-y := rc80211_minstrel.o | 50 | rc80211_minstrel-y := rc80211_minstrel.o |
55 | rc80211_minstrel-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_debugfs.o | 51 | rc80211_minstrel-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_debugfs.o |
56 | 52 | ||
57 | rc80211_minstrel_ht-y := rc80211_minstrel_ht.o | 53 | rc80211_minstrel_ht-y := rc80211_minstrel_ht.o |
58 | rc80211_minstrel_ht-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_ht_debugfs.o | 54 | rc80211_minstrel_ht-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_ht_debugfs.o |
59 | 55 | ||
60 | mac80211-$(CONFIG_MAC80211_RC_PID) += $(rc80211_pid-y) | ||
61 | mac80211-$(CONFIG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y) | 56 | mac80211-$(CONFIG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y) |
62 | mac80211-$(CONFIG_MAC80211_RC_MINSTREL_HT) += $(rc80211_minstrel_ht-y) | 57 | mac80211-$(CONFIG_MAC80211_RC_MINSTREL_HT) += $(rc80211_minstrel_ht-y) |
63 | 58 | ||
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index d17c26d6e369..0512a5096f0f 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c | |||
@@ -1187,18 +1187,12 @@ static int __init ieee80211_init(void) | |||
1187 | if (ret) | 1187 | if (ret) |
1188 | goto err_minstrel; | 1188 | goto err_minstrel; |
1189 | 1189 | ||
1190 | ret = rc80211_pid_init(); | ||
1191 | if (ret) | ||
1192 | goto err_pid; | ||
1193 | |||
1194 | ret = ieee80211_iface_init(); | 1190 | ret = ieee80211_iface_init(); |
1195 | if (ret) | 1191 | if (ret) |
1196 | goto err_netdev; | 1192 | goto err_netdev; |
1197 | 1193 | ||
1198 | return 0; | 1194 | return 0; |
1199 | err_netdev: | 1195 | err_netdev: |
1200 | rc80211_pid_exit(); | ||
1201 | err_pid: | ||
1202 | rc80211_minstrel_ht_exit(); | 1196 | rc80211_minstrel_ht_exit(); |
1203 | err_minstrel: | 1197 | err_minstrel: |
1204 | rc80211_minstrel_exit(); | 1198 | rc80211_minstrel_exit(); |
@@ -1208,7 +1202,6 @@ static int __init ieee80211_init(void) | |||
1208 | 1202 | ||
1209 | static void __exit ieee80211_exit(void) | 1203 | static void __exit ieee80211_exit(void) |
1210 | { | 1204 | { |
1211 | rc80211_pid_exit(); | ||
1212 | rc80211_minstrel_ht_exit(); | 1205 | rc80211_minstrel_ht_exit(); |
1213 | rc80211_minstrel_exit(); | 1206 | rc80211_minstrel_exit(); |
1214 | 1207 | ||
diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h index 9aa2a1190a86..18babe302832 100644 --- a/net/mac80211/rate.h +++ b/net/mac80211/rate.h | |||
@@ -143,19 +143,6 @@ void rate_control_deinitialize(struct ieee80211_local *local); | |||
143 | 143 | ||
144 | 144 | ||
145 | /* Rate control algorithms */ | 145 | /* Rate control algorithms */ |
146 | #ifdef CONFIG_MAC80211_RC_PID | ||
147 | int rc80211_pid_init(void); | ||
148 | void rc80211_pid_exit(void); | ||
149 | #else | ||
150 | static inline int rc80211_pid_init(void) | ||
151 | { | ||
152 | return 0; | ||
153 | } | ||
154 | static inline void rc80211_pid_exit(void) | ||
155 | { | ||
156 | } | ||
157 | #endif | ||
158 | |||
159 | #ifdef CONFIG_MAC80211_RC_MINSTREL | 146 | #ifdef CONFIG_MAC80211_RC_MINSTREL |
160 | int rc80211_minstrel_init(void); | 147 | int rc80211_minstrel_init(void); |
161 | void rc80211_minstrel_exit(void); | 148 | void rc80211_minstrel_exit(void); |
diff --git a/net/mac80211/rc80211_pid.h b/net/mac80211/rc80211_pid.h deleted file mode 100644 index 19111c7bf454..000000000000 --- a/net/mac80211/rc80211_pid.h +++ /dev/null | |||
@@ -1,278 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de> | ||
3 | * Copyright 2007, Stefano Brivio <stefano.brivio@polimi.it> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | */ | ||
9 | |||
10 | #ifndef RC80211_PID_H | ||
11 | #define RC80211_PID_H | ||
12 | |||
13 | /* Sampling period for measuring percentage of failed frames in ms. */ | ||
14 | #define RC_PID_INTERVAL 125 | ||
15 | |||
16 | /* Exponential averaging smoothness (used for I part of PID controller) */ | ||
17 | #define RC_PID_SMOOTHING_SHIFT 3 | ||
18 | #define RC_PID_SMOOTHING (1 << RC_PID_SMOOTHING_SHIFT) | ||
19 | |||
20 | /* Sharpening factor (used for D part of PID controller) */ | ||
21 | #define RC_PID_SHARPENING_FACTOR 0 | ||
22 | #define RC_PID_SHARPENING_DURATION 0 | ||
23 | |||
24 | /* Fixed point arithmetic shifting amount. */ | ||
25 | #define RC_PID_ARITH_SHIFT 8 | ||
26 | |||
27 | /* Proportional PID component coefficient. */ | ||
28 | #define RC_PID_COEFF_P 15 | ||
29 | /* Integral PID component coefficient. */ | ||
30 | #define RC_PID_COEFF_I 9 | ||
31 | /* Derivative PID component coefficient. */ | ||
32 | #define RC_PID_COEFF_D 15 | ||
33 | |||
34 | /* Target failed frames rate for the PID controller. NB: This effectively gives | ||
35 | * maximum failed frames percentage we're willing to accept. If the wireless | ||
36 | * link quality is good, the controller will fail to adjust failed frames | ||
37 | * percentage to the target. This is intentional. | ||
38 | */ | ||
39 | #define RC_PID_TARGET_PF 14 | ||
40 | |||
41 | /* Rate behaviour normalization quantity over time. */ | ||
42 | #define RC_PID_NORM_OFFSET 3 | ||
43 | |||
44 | /* Push high rates right after loading. */ | ||
45 | #define RC_PID_FAST_START 0 | ||
46 | |||
47 | /* Arithmetic right shift for positive and negative values for ISO C. */ | ||
48 | #define RC_PID_DO_ARITH_RIGHT_SHIFT(x, y) \ | ||
49 | ((x) < 0 ? -((-(x)) >> (y)) : (x) >> (y)) | ||
50 | |||
51 | enum rc_pid_event_type { | ||
52 | RC_PID_EVENT_TYPE_TX_STATUS, | ||
53 | RC_PID_EVENT_TYPE_RATE_CHANGE, | ||
54 | RC_PID_EVENT_TYPE_TX_RATE, | ||
55 | RC_PID_EVENT_TYPE_PF_SAMPLE, | ||
56 | }; | ||
57 | |||
58 | union rc_pid_event_data { | ||
59 | /* RC_PID_EVENT_TX_STATUS */ | ||
60 | struct { | ||
61 | u32 flags; | ||
62 | struct ieee80211_tx_info tx_status; | ||
63 | }; | ||
64 | /* RC_PID_EVENT_TYPE_RATE_CHANGE */ | ||
65 | /* RC_PID_EVENT_TYPE_TX_RATE */ | ||
66 | struct { | ||
67 | int index; | ||
68 | int rate; | ||
69 | }; | ||
70 | /* RC_PID_EVENT_TYPE_PF_SAMPLE */ | ||
71 | struct { | ||
72 | s32 pf_sample; | ||
73 | s32 prop_err; | ||
74 | s32 int_err; | ||
75 | s32 der_err; | ||
76 | }; | ||
77 | }; | ||
78 | |||
79 | struct rc_pid_event { | ||
80 | /* The time when the event occurred */ | ||
81 | unsigned long timestamp; | ||
82 | |||
83 | /* Event ID number */ | ||
84 | unsigned int id; | ||
85 | |||
86 | /* Type of event */ | ||
87 | enum rc_pid_event_type type; | ||
88 | |||
89 | /* type specific data */ | ||
90 | union rc_pid_event_data data; | ||
91 | }; | ||
92 | |||
93 | /* Size of the event ring buffer. */ | ||
94 | #define RC_PID_EVENT_RING_SIZE 32 | ||
95 | |||
96 | struct rc_pid_event_buffer { | ||
97 | /* Counter that generates event IDs */ | ||
98 | unsigned int ev_count; | ||
99 | |||
100 | /* Ring buffer of events */ | ||
101 | struct rc_pid_event ring[RC_PID_EVENT_RING_SIZE]; | ||
102 | |||
103 | /* Index to the entry in events_buf to be reused */ | ||
104 | unsigned int next_entry; | ||
105 | |||
106 | /* Lock that guards against concurrent access to this buffer struct */ | ||
107 | spinlock_t lock; | ||
108 | |||
109 | /* Wait queue for poll/select and blocking I/O */ | ||
110 | wait_queue_head_t waitqueue; | ||
111 | }; | ||
112 | |||
113 | struct rc_pid_events_file_info { | ||
114 | /* The event buffer we read */ | ||
115 | struct rc_pid_event_buffer *events; | ||
116 | |||
117 | /* The entry we have should read next */ | ||
118 | unsigned int next_entry; | ||
119 | }; | ||
120 | |||
121 | /** | ||
122 | * struct rc_pid_debugfs_entries - tunable parameters | ||
123 | * | ||
124 | * Algorithm parameters, tunable via debugfs. | ||
125 | * @target: target percentage for failed frames | ||
126 | * @sampling_period: error sampling interval in milliseconds | ||
127 | * @coeff_p: absolute value of the proportional coefficient | ||
128 | * @coeff_i: absolute value of the integral coefficient | ||
129 | * @coeff_d: absolute value of the derivative coefficient | ||
130 | * @smoothing_shift: absolute value of the integral smoothing factor (i.e. | ||
131 | * amount of smoothing introduced by the exponential moving average) | ||
132 | * @sharpen_factor: absolute value of the derivative sharpening factor (i.e. | ||
133 | * amount of emphasis given to the derivative term after low activity | ||
134 | * events) | ||
135 | * @sharpen_duration: duration of the sharpening effect after the detected low | ||
136 | * activity event, relative to sampling_period | ||
137 | * @norm_offset: amount of normalization periodically performed on the learnt | ||
138 | * rate behaviour values (lower means we should trust more what we learnt | ||
139 | * about behaviour of rates, higher means we should trust more the natural | ||
140 | * ordering of rates) | ||
141 | */ | ||
142 | struct rc_pid_debugfs_entries { | ||
143 | struct dentry *target; | ||
144 | struct dentry *sampling_period; | ||
145 | struct dentry *coeff_p; | ||
146 | struct dentry *coeff_i; | ||
147 | struct dentry *coeff_d; | ||
148 | struct dentry *smoothing_shift; | ||
149 | struct dentry *sharpen_factor; | ||
150 | struct dentry *sharpen_duration; | ||
151 | struct dentry *norm_offset; | ||
152 | }; | ||
153 | |||
154 | void rate_control_pid_event_tx_status(struct rc_pid_event_buffer *buf, | ||
155 | struct ieee80211_tx_info *stat); | ||
156 | |||
157 | void rate_control_pid_event_rate_change(struct rc_pid_event_buffer *buf, | ||
158 | int index, int rate); | ||
159 | |||
160 | void rate_control_pid_event_tx_rate(struct rc_pid_event_buffer *buf, | ||
161 | int index, int rate); | ||
162 | |||
163 | void rate_control_pid_event_pf_sample(struct rc_pid_event_buffer *buf, | ||
164 | s32 pf_sample, s32 prop_err, | ||
165 | s32 int_err, s32 der_err); | ||
166 | |||
167 | void rate_control_pid_add_sta_debugfs(void *priv, void *priv_sta, | ||
168 | struct dentry *dir); | ||
169 | |||
170 | void rate_control_pid_remove_sta_debugfs(void *priv, void *priv_sta); | ||
171 | |||
172 | struct rc_pid_sta_info { | ||
173 | unsigned long last_change; | ||
174 | unsigned long last_sample; | ||
175 | |||
176 | u32 tx_num_failed; | ||
177 | u32 tx_num_xmit; | ||
178 | |||
179 | int txrate_idx; | ||
180 | |||
181 | /* Average failed frames percentage error (i.e. actual vs. target | ||
182 | * percentage), scaled by RC_PID_SMOOTHING. This value is computed | ||
183 | * using using an exponential weighted average technique: | ||
184 | * | ||
185 | * (RC_PID_SMOOTHING - 1) * err_avg_old + err | ||
186 | * err_avg = ------------------------------------------ | ||
187 | * RC_PID_SMOOTHING | ||
188 | * | ||
189 | * where err_avg is the new approximation, err_avg_old the previous one | ||
190 | * and err is the error w.r.t. to the current failed frames percentage | ||
191 | * sample. Note that the bigger RC_PID_SMOOTHING the more weight is | ||
192 | * given to the previous estimate, resulting in smoother behavior (i.e. | ||
193 | * corresponding to a longer integration window). | ||
194 | * | ||
195 | * For computation, we actually don't use the above formula, but this | ||
196 | * one: | ||
197 | * | ||
198 | * err_avg_scaled = err_avg_old_scaled - err_avg_old + err | ||
199 | * | ||
200 | * where: | ||
201 | * err_avg_scaled = err * RC_PID_SMOOTHING | ||
202 | * err_avg_old_scaled = err_avg_old * RC_PID_SMOOTHING | ||
203 | * | ||
204 | * This avoids floating point numbers and the per_failed_old value can | ||
205 | * easily be obtained by shifting per_failed_old_scaled right by | ||
206 | * RC_PID_SMOOTHING_SHIFT. | ||
207 | */ | ||
208 | s32 err_avg_sc; | ||
209 | |||
210 | /* Last framed failes percentage sample. */ | ||
211 | u32 last_pf; | ||
212 | |||
213 | /* Sharpening needed. */ | ||
214 | u8 sharp_cnt; | ||
215 | |||
216 | #ifdef CONFIG_MAC80211_DEBUGFS | ||
217 | /* Event buffer */ | ||
218 | struct rc_pid_event_buffer events; | ||
219 | |||
220 | /* Events debugfs file entry */ | ||
221 | struct dentry *events_entry; | ||
222 | #endif | ||
223 | }; | ||
224 | |||
225 | /* Algorithm parameters. We keep them on a per-algorithm approach, so they can | ||
226 | * be tuned individually for each interface. | ||
227 | */ | ||
228 | struct rc_pid_rateinfo { | ||
229 | |||
230 | /* Map sorted rates to rates in ieee80211_hw_mode. */ | ||
231 | int index; | ||
232 | |||
233 | /* Map rates in ieee80211_hw_mode to sorted rates. */ | ||
234 | int rev_index; | ||
235 | |||
236 | /* Did we do any measurement on this rate? */ | ||
237 | bool valid; | ||
238 | |||
239 | /* Comparison with the lowest rate. */ | ||
240 | int diff; | ||
241 | }; | ||
242 | |||
243 | struct rc_pid_info { | ||
244 | |||
245 | /* The failed frames percentage target. */ | ||
246 | unsigned int target; | ||
247 | |||
248 | /* Rate at which failed frames percentage is sampled in 0.001s. */ | ||
249 | unsigned int sampling_period; | ||
250 | |||
251 | /* P, I and D coefficients. */ | ||
252 | int coeff_p; | ||
253 | int coeff_i; | ||
254 | int coeff_d; | ||
255 | |||
256 | /* Exponential averaging shift. */ | ||
257 | unsigned int smoothing_shift; | ||
258 | |||
259 | /* Sharpening factor and duration. */ | ||
260 | unsigned int sharpen_factor; | ||
261 | unsigned int sharpen_duration; | ||
262 | |||
263 | /* Normalization offset. */ | ||
264 | unsigned int norm_offset; | ||
265 | |||
266 | /* Rates information. */ | ||
267 | struct rc_pid_rateinfo *rinfo; | ||
268 | |||
269 | /* Index of the last used rate. */ | ||
270 | int oldrate; | ||
271 | |||
272 | #ifdef CONFIG_MAC80211_DEBUGFS | ||
273 | /* Debugfs entries created for the parameters above. */ | ||
274 | struct rc_pid_debugfs_entries dentries; | ||
275 | #endif | ||
276 | }; | ||
277 | |||
278 | #endif /* RC80211_PID_H */ | ||
diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c deleted file mode 100644 index d0da2a70fe68..000000000000 --- a/net/mac80211/rc80211_pid_algo.c +++ /dev/null | |||
@@ -1,478 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright 2002-2005, Instant802 Networks, Inc. | ||
3 | * Copyright 2005, Devicescape Software, Inc. | ||
4 | * Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de> | ||
5 | * Copyright 2007-2008, Stefano Brivio <stefano.brivio@polimi.it> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/netdevice.h> | ||
13 | #include <linux/types.h> | ||
14 | #include <linux/skbuff.h> | ||
15 | #include <linux/debugfs.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <net/mac80211.h> | ||
18 | #include "rate.h" | ||
19 | #include "mesh.h" | ||
20 | #include "rc80211_pid.h" | ||
21 | |||
22 | |||
23 | /* This is an implementation of a TX rate control algorithm that uses a PID | ||
24 | * controller. Given a target failed frames rate, the controller decides about | ||
25 | * TX rate changes to meet the target failed frames rate. | ||
26 | * | ||
27 | * The controller basically computes the following: | ||
28 | * | ||
29 | * adj = CP * err + CI * err_avg + CD * (err - last_err) * (1 + sharpening) | ||
30 | * | ||
31 | * where | ||
32 | * adj adjustment value that is used to switch TX rate (see below) | ||
33 | * err current error: target vs. current failed frames percentage | ||
34 | * last_err last error | ||
35 | * err_avg average (i.e. poor man's integral) of recent errors | ||
36 | * sharpening non-zero when fast response is needed (i.e. right after | ||
37 | * association or no frames sent for a long time), heading | ||
38 | * to zero over time | ||
39 | * CP Proportional coefficient | ||
40 | * CI Integral coefficient | ||
41 | * CD Derivative coefficient | ||
42 | * | ||
43 | * CP, CI, CD are subject to careful tuning. | ||
44 | * | ||
45 | * The integral component uses a exponential moving average approach instead of | ||
46 | * an actual sliding window. The advantage is that we don't need to keep an | ||
47 | * array of the last N error values and computation is easier. | ||
48 | * | ||
49 | * Once we have the adj value, we map it to a rate by means of a learning | ||
50 | * algorithm. This algorithm keeps the state of the percentual failed frames | ||
51 | * difference between rates. The behaviour of the lowest available rate is kept | ||
52 | * as a reference value, and every time we switch between two rates, we compute | ||
53 | * the difference between the failed frames each rate exhibited. By doing so, | ||
54 | * we compare behaviours which different rates exhibited in adjacent timeslices, | ||
55 | * thus the comparison is minimally affected by external conditions. This | ||
56 | * difference gets propagated to the whole set of measurements, so that the | ||
57 | * reference is always the same. Periodically, we normalize this set so that | ||
58 | * recent events weigh the most. By comparing the adj value with this set, we | ||
59 | * avoid pejorative switches to lower rates and allow for switches to higher | ||
60 | * rates if they behaved well. | ||
61 | * | ||
62 | * Note that for the computations we use a fixed-point representation to avoid | ||
63 | * floating point arithmetic. Hence, all values are shifted left by | ||
64 | * RC_PID_ARITH_SHIFT. | ||
65 | */ | ||
66 | |||
67 | |||
68 | /* Adjust the rate while ensuring that we won't switch to a lower rate if it | ||
69 | * exhibited a worse failed frames behaviour and we'll choose the highest rate | ||
70 | * whose failed frames behaviour is not worse than the one of the original rate | ||
71 | * target. While at it, check that the new rate is valid. */ | ||
72 | static void rate_control_pid_adjust_rate(struct ieee80211_supported_band *sband, | ||
73 | struct ieee80211_sta *sta, | ||
74 | struct rc_pid_sta_info *spinfo, int adj, | ||
75 | struct rc_pid_rateinfo *rinfo) | ||
76 | { | ||
77 | int cur_sorted, new_sorted, probe, tmp, n_bitrates, band; | ||
78 | int cur = spinfo->txrate_idx; | ||
79 | |||
80 | band = sband->band; | ||
81 | n_bitrates = sband->n_bitrates; | ||
82 | |||
83 | /* Map passed arguments to sorted values. */ | ||
84 | cur_sorted = rinfo[cur].rev_index; | ||
85 | new_sorted = cur_sorted + adj; | ||
86 | |||
87 | /* Check limits. */ | ||
88 | if (new_sorted < 0) | ||
89 | new_sorted = rinfo[0].rev_index; | ||
90 | else if (new_sorted >= n_bitrates) | ||
91 | new_sorted = rinfo[n_bitrates - 1].rev_index; | ||
92 | |||
93 | tmp = new_sorted; | ||
94 | |||
95 | if (adj < 0) { | ||
96 | /* Ensure that the rate decrease isn't disadvantageous. */ | ||
97 | for (probe = cur_sorted; probe >= new_sorted; probe--) | ||
98 | if (rinfo[probe].diff <= rinfo[cur_sorted].diff && | ||
99 | rate_supported(sta, band, rinfo[probe].index)) | ||
100 | tmp = probe; | ||
101 | } else { | ||
102 | /* Look for rate increase with zero (or below) cost. */ | ||
103 | for (probe = new_sorted + 1; probe < n_bitrates; probe++) | ||
104 | if (rinfo[probe].diff <= rinfo[new_sorted].diff && | ||
105 | rate_supported(sta, band, rinfo[probe].index)) | ||
106 | tmp = probe; | ||
107 | } | ||
108 | |||
109 | /* Fit the rate found to the nearest supported rate. */ | ||
110 | do { | ||
111 | if (rate_supported(sta, band, rinfo[tmp].index)) { | ||
112 | spinfo->txrate_idx = rinfo[tmp].index; | ||
113 | break; | ||
114 | } | ||
115 | if (adj < 0) | ||
116 | tmp--; | ||
117 | else | ||
118 | tmp++; | ||
119 | } while (tmp < n_bitrates && tmp >= 0); | ||
120 | |||
121 | #ifdef CONFIG_MAC80211_DEBUGFS | ||
122 | rate_control_pid_event_rate_change(&spinfo->events, | ||
123 | spinfo->txrate_idx, | ||
124 | sband->bitrates[spinfo->txrate_idx].bitrate); | ||
125 | #endif | ||
126 | } | ||
127 | |||
128 | /* Normalize the failed frames per-rate differences. */ | ||
129 | static void rate_control_pid_normalize(struct rc_pid_info *pinfo, int l) | ||
130 | { | ||
131 | int i, norm_offset = pinfo->norm_offset; | ||
132 | struct rc_pid_rateinfo *r = pinfo->rinfo; | ||
133 | |||
134 | if (r[0].diff > norm_offset) | ||
135 | r[0].diff -= norm_offset; | ||
136 | else if (r[0].diff < -norm_offset) | ||
137 | r[0].diff += norm_offset; | ||
138 | for (i = 0; i < l - 1; i++) | ||
139 | if (r[i + 1].diff > r[i].diff + norm_offset) | ||
140 | r[i + 1].diff -= norm_offset; | ||
141 | else if (r[i + 1].diff <= r[i].diff) | ||
142 | r[i + 1].diff += norm_offset; | ||
143 | } | ||
144 | |||
145 | static void rate_control_pid_sample(struct rc_pid_info *pinfo, | ||
146 | struct ieee80211_supported_band *sband, | ||
147 | struct ieee80211_sta *sta, | ||
148 | struct rc_pid_sta_info *spinfo) | ||
149 | { | ||
150 | struct rc_pid_rateinfo *rinfo = pinfo->rinfo; | ||
151 | u32 pf; | ||
152 | s32 err_avg; | ||
153 | u32 err_prop; | ||
154 | u32 err_int; | ||
155 | u32 err_der; | ||
156 | int adj, i, j, tmp; | ||
157 | unsigned long period; | ||
158 | |||
159 | /* In case nothing happened during the previous control interval, turn | ||
160 | * the sharpening factor on. */ | ||
161 | period = msecs_to_jiffies(pinfo->sampling_period); | ||
162 | if (jiffies - spinfo->last_sample > 2 * period) | ||
163 | spinfo->sharp_cnt = pinfo->sharpen_duration; | ||
164 | |||
165 | spinfo->last_sample = jiffies; | ||
166 | |||
167 | /* This should never happen, but in case, we assume the old sample is | ||
168 | * still a good measurement and copy it. */ | ||
169 | if (unlikely(spinfo->tx_num_xmit == 0)) | ||
170 | pf = spinfo->last_pf; | ||
171 | else | ||
172 | pf = spinfo->tx_num_failed * 100 / spinfo->tx_num_xmit; | ||
173 | |||
174 | spinfo->tx_num_xmit = 0; | ||
175 | spinfo->tx_num_failed = 0; | ||
176 | |||
177 | /* If we just switched rate, update the rate behaviour info. */ | ||
178 | if (pinfo->oldrate != spinfo->txrate_idx) { | ||
179 | |||
180 | i = rinfo[pinfo->oldrate].rev_index; | ||
181 | j = rinfo[spinfo->txrate_idx].rev_index; | ||
182 | |||
183 | tmp = (pf - spinfo->last_pf); | ||
184 | tmp = RC_PID_DO_ARITH_RIGHT_SHIFT(tmp, RC_PID_ARITH_SHIFT); | ||
185 | |||
186 | rinfo[j].diff = rinfo[i].diff + tmp; | ||
187 | pinfo->oldrate = spinfo->txrate_idx; | ||
188 | } | ||
189 | rate_control_pid_normalize(pinfo, sband->n_bitrates); | ||
190 | |||
191 | /* Compute the proportional, integral and derivative errors. */ | ||
192 | err_prop = (pinfo->target - pf) << RC_PID_ARITH_SHIFT; | ||
193 | |||
194 | err_avg = spinfo->err_avg_sc >> pinfo->smoothing_shift; | ||
195 | spinfo->err_avg_sc = spinfo->err_avg_sc - err_avg + err_prop; | ||
196 | err_int = spinfo->err_avg_sc >> pinfo->smoothing_shift; | ||
197 | |||
198 | err_der = (pf - spinfo->last_pf) * | ||
199 | (1 + pinfo->sharpen_factor * spinfo->sharp_cnt); | ||
200 | spinfo->last_pf = pf; | ||
201 | if (spinfo->sharp_cnt) | ||
202 | spinfo->sharp_cnt--; | ||
203 | |||
204 | #ifdef CONFIG_MAC80211_DEBUGFS | ||
205 | rate_control_pid_event_pf_sample(&spinfo->events, pf, err_prop, err_int, | ||
206 | err_der); | ||
207 | #endif | ||
208 | |||
209 | /* Compute the controller output. */ | ||
210 | adj = (err_prop * pinfo->coeff_p + err_int * pinfo->coeff_i | ||
211 | + err_der * pinfo->coeff_d); | ||
212 | adj = RC_PID_DO_ARITH_RIGHT_SHIFT(adj, 2 * RC_PID_ARITH_SHIFT); | ||
213 | |||
214 | /* Change rate. */ | ||
215 | if (adj) | ||
216 | rate_control_pid_adjust_rate(sband, sta, spinfo, adj, rinfo); | ||
217 | } | ||
218 | |||
219 | static void rate_control_pid_tx_status(void *priv, struct ieee80211_supported_band *sband, | ||
220 | struct ieee80211_sta *sta, void *priv_sta, | ||
221 | struct sk_buff *skb) | ||
222 | { | ||
223 | struct rc_pid_info *pinfo = priv; | ||
224 | struct rc_pid_sta_info *spinfo = priv_sta; | ||
225 | unsigned long period; | ||
226 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||
227 | |||
228 | if (!spinfo) | ||
229 | return; | ||
230 | |||
231 | /* Ignore all frames that were sent with a different rate than the rate | ||
232 | * we currently advise mac80211 to use. */ | ||
233 | if (info->status.rates[0].idx != spinfo->txrate_idx) | ||
234 | return; | ||
235 | |||
236 | spinfo->tx_num_xmit++; | ||
237 | |||
238 | #ifdef CONFIG_MAC80211_DEBUGFS | ||
239 | rate_control_pid_event_tx_status(&spinfo->events, info); | ||
240 | #endif | ||
241 | |||
242 | /* We count frames that totally failed to be transmitted as two bad | ||
243 | * frames, those that made it out but had some retries as one good and | ||
244 | * one bad frame. */ | ||
245 | if (!(info->flags & IEEE80211_TX_STAT_ACK)) { | ||
246 | spinfo->tx_num_failed += 2; | ||
247 | spinfo->tx_num_xmit++; | ||
248 | } else if (info->status.rates[0].count > 1) { | ||
249 | spinfo->tx_num_failed++; | ||
250 | spinfo->tx_num_xmit++; | ||
251 | } | ||
252 | |||
253 | /* Update PID controller state. */ | ||
254 | period = msecs_to_jiffies(pinfo->sampling_period); | ||
255 | if (time_after(jiffies, spinfo->last_sample + period)) | ||
256 | rate_control_pid_sample(pinfo, sband, sta, spinfo); | ||
257 | } | ||
258 | |||
259 | static void | ||
260 | rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta, | ||
261 | void *priv_sta, | ||
262 | struct ieee80211_tx_rate_control *txrc) | ||
263 | { | ||
264 | struct sk_buff *skb = txrc->skb; | ||
265 | struct ieee80211_supported_band *sband = txrc->sband; | ||
266 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||
267 | struct rc_pid_sta_info *spinfo = priv_sta; | ||
268 | int rateidx; | ||
269 | |||
270 | if (txrc->rts) | ||
271 | info->control.rates[0].count = | ||
272 | txrc->hw->conf.long_frame_max_tx_count; | ||
273 | else | ||
274 | info->control.rates[0].count = | ||
275 | txrc->hw->conf.short_frame_max_tx_count; | ||
276 | |||
277 | /* Send management frames and NO_ACK data using lowest rate. */ | ||
278 | if (rate_control_send_low(sta, priv_sta, txrc)) | ||
279 | return; | ||
280 | |||
281 | rateidx = spinfo->txrate_idx; | ||
282 | |||
283 | if (rateidx >= sband->n_bitrates) | ||
284 | rateidx = sband->n_bitrates - 1; | ||
285 | |||
286 | info->control.rates[0].idx = rateidx; | ||
287 | |||
288 | #ifdef CONFIG_MAC80211_DEBUGFS | ||
289 | rate_control_pid_event_tx_rate(&spinfo->events, | ||
290 | rateidx, sband->bitrates[rateidx].bitrate); | ||
291 | #endif | ||
292 | } | ||
293 | |||
294 | static void | ||
295 | rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband, | ||
296 | struct cfg80211_chan_def *chandef, | ||
297 | struct ieee80211_sta *sta, void *priv_sta) | ||
298 | { | ||
299 | struct rc_pid_sta_info *spinfo = priv_sta; | ||
300 | struct rc_pid_info *pinfo = priv; | ||
301 | struct rc_pid_rateinfo *rinfo = pinfo->rinfo; | ||
302 | int i, j, tmp; | ||
303 | bool s; | ||
304 | |||
305 | /* TODO: This routine should consider using RSSI from previous packets | ||
306 | * as we need to have IEEE 802.1X auth succeed immediately after assoc.. | ||
307 | * Until that method is implemented, we will use the lowest supported | ||
308 | * rate as a workaround. */ | ||
309 | |||
310 | /* Sort the rates. This is optimized for the most common case (i.e. | ||
311 | * almost-sorted CCK+OFDM rates). Kind of bubble-sort with reversed | ||
312 | * mapping too. */ | ||
313 | for (i = 0; i < sband->n_bitrates; i++) { | ||
314 | rinfo[i].index = i; | ||
315 | rinfo[i].rev_index = i; | ||
316 | if (RC_PID_FAST_START) | ||
317 | rinfo[i].diff = 0; | ||
318 | else | ||
319 | rinfo[i].diff = i * pinfo->norm_offset; | ||
320 | } | ||
321 | for (i = 1; i < sband->n_bitrates; i++) { | ||
322 | s = false; | ||
323 | for (j = 0; j < sband->n_bitrates - i; j++) | ||
324 | if (unlikely(sband->bitrates[rinfo[j].index].bitrate > | ||
325 | sband->bitrates[rinfo[j + 1].index].bitrate)) { | ||
326 | tmp = rinfo[j].index; | ||
327 | rinfo[j].index = rinfo[j + 1].index; | ||
328 | rinfo[j + 1].index = tmp; | ||
329 | rinfo[rinfo[j].index].rev_index = j; | ||
330 | rinfo[rinfo[j + 1].index].rev_index = j + 1; | ||
331 | s = true; | ||
332 | } | ||
333 | if (!s) | ||
334 | break; | ||
335 | } | ||
336 | |||
337 | spinfo->txrate_idx = rate_lowest_index(sband, sta); | ||
338 | } | ||
339 | |||
340 | static void *rate_control_pid_alloc(struct ieee80211_hw *hw, | ||
341 | struct dentry *debugfsdir) | ||
342 | { | ||
343 | struct rc_pid_info *pinfo; | ||
344 | struct rc_pid_rateinfo *rinfo; | ||
345 | struct ieee80211_supported_band *sband; | ||
346 | int i, max_rates = 0; | ||
347 | #ifdef CONFIG_MAC80211_DEBUGFS | ||
348 | struct rc_pid_debugfs_entries *de; | ||
349 | #endif | ||
350 | |||
351 | pinfo = kmalloc(sizeof(*pinfo), GFP_ATOMIC); | ||
352 | if (!pinfo) | ||
353 | return NULL; | ||
354 | |||
355 | for (i = 0; i < IEEE80211_NUM_BANDS; i++) { | ||
356 | sband = hw->wiphy->bands[i]; | ||
357 | if (sband && sband->n_bitrates > max_rates) | ||
358 | max_rates = sband->n_bitrates; | ||
359 | } | ||
360 | |||
361 | rinfo = kmalloc(sizeof(*rinfo) * max_rates, GFP_ATOMIC); | ||
362 | if (!rinfo) { | ||
363 | kfree(pinfo); | ||
364 | return NULL; | ||
365 | } | ||
366 | |||
367 | pinfo->target = RC_PID_TARGET_PF; | ||
368 | pinfo->sampling_period = RC_PID_INTERVAL; | ||
369 | pinfo->coeff_p = RC_PID_COEFF_P; | ||
370 | pinfo->coeff_i = RC_PID_COEFF_I; | ||
371 | pinfo->coeff_d = RC_PID_COEFF_D; | ||
372 | pinfo->smoothing_shift = RC_PID_SMOOTHING_SHIFT; | ||
373 | pinfo->sharpen_factor = RC_PID_SHARPENING_FACTOR; | ||
374 | pinfo->sharpen_duration = RC_PID_SHARPENING_DURATION; | ||
375 | pinfo->norm_offset = RC_PID_NORM_OFFSET; | ||
376 | pinfo->rinfo = rinfo; | ||
377 | pinfo->oldrate = 0; | ||
378 | |||
379 | #ifdef CONFIG_MAC80211_DEBUGFS | ||
380 | de = &pinfo->dentries; | ||
381 | de->target = debugfs_create_u32("target_pf", S_IRUSR | S_IWUSR, | ||
382 | debugfsdir, &pinfo->target); | ||
383 | de->sampling_period = debugfs_create_u32("sampling_period", | ||
384 | S_IRUSR | S_IWUSR, debugfsdir, | ||
385 | &pinfo->sampling_period); | ||
386 | de->coeff_p = debugfs_create_u32("coeff_p", S_IRUSR | S_IWUSR, | ||
387 | debugfsdir, (u32 *)&pinfo->coeff_p); | ||
388 | de->coeff_i = debugfs_create_u32("coeff_i", S_IRUSR | S_IWUSR, | ||
389 | debugfsdir, (u32 *)&pinfo->coeff_i); | ||
390 | de->coeff_d = debugfs_create_u32("coeff_d", S_IRUSR | S_IWUSR, | ||
391 | debugfsdir, (u32 *)&pinfo->coeff_d); | ||
392 | de->smoothing_shift = debugfs_create_u32("smoothing_shift", | ||
393 | S_IRUSR | S_IWUSR, debugfsdir, | ||
394 | &pinfo->smoothing_shift); | ||
395 | de->sharpen_factor = debugfs_create_u32("sharpen_factor", | ||
396 | S_IRUSR | S_IWUSR, debugfsdir, | ||
397 | &pinfo->sharpen_factor); | ||
398 | de->sharpen_duration = debugfs_create_u32("sharpen_duration", | ||
399 | S_IRUSR | S_IWUSR, debugfsdir, | ||
400 | &pinfo->sharpen_duration); | ||
401 | de->norm_offset = debugfs_create_u32("norm_offset", | ||
402 | S_IRUSR | S_IWUSR, debugfsdir, | ||
403 | &pinfo->norm_offset); | ||
404 | #endif | ||
405 | |||
406 | return pinfo; | ||
407 | } | ||
408 | |||
409 | static void rate_control_pid_free(void *priv) | ||
410 | { | ||
411 | struct rc_pid_info *pinfo = priv; | ||
412 | #ifdef CONFIG_MAC80211_DEBUGFS | ||
413 | struct rc_pid_debugfs_entries *de = &pinfo->dentries; | ||
414 | |||
415 | debugfs_remove(de->norm_offset); | ||
416 | debugfs_remove(de->sharpen_duration); | ||
417 | debugfs_remove(de->sharpen_factor); | ||
418 | debugfs_remove(de->smoothing_shift); | ||
419 | debugfs_remove(de->coeff_d); | ||
420 | debugfs_remove(de->coeff_i); | ||
421 | debugfs_remove(de->coeff_p); | ||
422 | debugfs_remove(de->sampling_period); | ||
423 | debugfs_remove(de->target); | ||
424 | #endif | ||
425 | |||
426 | kfree(pinfo->rinfo); | ||
427 | kfree(pinfo); | ||
428 | } | ||
429 | |||
430 | static void *rate_control_pid_alloc_sta(void *priv, struct ieee80211_sta *sta, | ||
431 | gfp_t gfp) | ||
432 | { | ||
433 | struct rc_pid_sta_info *spinfo; | ||
434 | |||
435 | spinfo = kzalloc(sizeof(*spinfo), gfp); | ||
436 | if (spinfo == NULL) | ||
437 | return NULL; | ||
438 | |||
439 | spinfo->last_sample = jiffies; | ||
440 | |||
441 | #ifdef CONFIG_MAC80211_DEBUGFS | ||
442 | spin_lock_init(&spinfo->events.lock); | ||
443 | init_waitqueue_head(&spinfo->events.waitqueue); | ||
444 | #endif | ||
445 | |||
446 | return spinfo; | ||
447 | } | ||
448 | |||
449 | static void rate_control_pid_free_sta(void *priv, struct ieee80211_sta *sta, | ||
450 | void *priv_sta) | ||
451 | { | ||
452 | kfree(priv_sta); | ||
453 | } | ||
454 | |||
455 | static const struct rate_control_ops mac80211_rcpid = { | ||
456 | .name = "pid", | ||
457 | .tx_status = rate_control_pid_tx_status, | ||
458 | .get_rate = rate_control_pid_get_rate, | ||
459 | .rate_init = rate_control_pid_rate_init, | ||
460 | .alloc = rate_control_pid_alloc, | ||
461 | .free = rate_control_pid_free, | ||
462 | .alloc_sta = rate_control_pid_alloc_sta, | ||
463 | .free_sta = rate_control_pid_free_sta, | ||
464 | #ifdef CONFIG_MAC80211_DEBUGFS | ||
465 | .add_sta_debugfs = rate_control_pid_add_sta_debugfs, | ||
466 | .remove_sta_debugfs = rate_control_pid_remove_sta_debugfs, | ||
467 | #endif | ||
468 | }; | ||
469 | |||
470 | int __init rc80211_pid_init(void) | ||
471 | { | ||
472 | return ieee80211_rate_control_register(&mac80211_rcpid); | ||
473 | } | ||
474 | |||
475 | void rc80211_pid_exit(void) | ||
476 | { | ||
477 | ieee80211_rate_control_unregister(&mac80211_rcpid); | ||
478 | } | ||
diff --git a/net/mac80211/rc80211_pid_debugfs.c b/net/mac80211/rc80211_pid_debugfs.c deleted file mode 100644 index 6ff134650a84..000000000000 --- a/net/mac80211/rc80211_pid_debugfs.c +++ /dev/null | |||
@@ -1,228 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #include <linux/sched.h> | ||
10 | #include <linux/spinlock.h> | ||
11 | #include <linux/poll.h> | ||
12 | #include <linux/netdevice.h> | ||
13 | #include <linux/types.h> | ||
14 | #include <linux/skbuff.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/export.h> | ||
17 | |||
18 | #include <net/mac80211.h> | ||
19 | #include "rate.h" | ||
20 | |||
21 | #include "rc80211_pid.h" | ||
22 | |||
23 | static void rate_control_pid_event(struct rc_pid_event_buffer *buf, | ||
24 | enum rc_pid_event_type type, | ||
25 | union rc_pid_event_data *data) | ||
26 | { | ||
27 | struct rc_pid_event *ev; | ||
28 | unsigned long status; | ||
29 | |||
30 | spin_lock_irqsave(&buf->lock, status); | ||
31 | ev = &(buf->ring[buf->next_entry]); | ||
32 | buf->next_entry = (buf->next_entry + 1) % RC_PID_EVENT_RING_SIZE; | ||
33 | |||
34 | ev->timestamp = jiffies; | ||
35 | ev->id = buf->ev_count++; | ||
36 | ev->type = type; | ||
37 | ev->data = *data; | ||
38 | |||
39 | spin_unlock_irqrestore(&buf->lock, status); | ||
40 | |||
41 | wake_up_all(&buf->waitqueue); | ||
42 | } | ||
43 | |||
44 | void rate_control_pid_event_tx_status(struct rc_pid_event_buffer *buf, | ||
45 | struct ieee80211_tx_info *stat) | ||
46 | { | ||
47 | union rc_pid_event_data evd; | ||
48 | |||
49 | evd.flags = stat->flags; | ||
50 | memcpy(&evd.tx_status, stat, sizeof(struct ieee80211_tx_info)); | ||
51 | rate_control_pid_event(buf, RC_PID_EVENT_TYPE_TX_STATUS, &evd); | ||
52 | } | ||
53 | |||
54 | void rate_control_pid_event_rate_change(struct rc_pid_event_buffer *buf, | ||
55 | int index, int rate) | ||
56 | { | ||
57 | union rc_pid_event_data evd; | ||
58 | |||
59 | evd.index = index; | ||
60 | evd.rate = rate; | ||
61 | rate_control_pid_event(buf, RC_PID_EVENT_TYPE_RATE_CHANGE, &evd); | ||
62 | } | ||
63 | |||
64 | void rate_control_pid_event_tx_rate(struct rc_pid_event_buffer *buf, | ||
65 | int index, int rate) | ||
66 | { | ||
67 | union rc_pid_event_data evd; | ||
68 | |||
69 | evd.index = index; | ||
70 | evd.rate = rate; | ||
71 | rate_control_pid_event(buf, RC_PID_EVENT_TYPE_TX_RATE, &evd); | ||
72 | } | ||
73 | |||
74 | void rate_control_pid_event_pf_sample(struct rc_pid_event_buffer *buf, | ||
75 | s32 pf_sample, s32 prop_err, | ||
76 | s32 int_err, s32 der_err) | ||
77 | { | ||
78 | union rc_pid_event_data evd; | ||
79 | |||
80 | evd.pf_sample = pf_sample; | ||
81 | evd.prop_err = prop_err; | ||
82 | evd.int_err = int_err; | ||
83 | evd.der_err = der_err; | ||
84 | rate_control_pid_event(buf, RC_PID_EVENT_TYPE_PF_SAMPLE, &evd); | ||
85 | } | ||
86 | |||
87 | static int rate_control_pid_events_open(struct inode *inode, struct file *file) | ||
88 | { | ||
89 | struct rc_pid_sta_info *sinfo = inode->i_private; | ||
90 | struct rc_pid_event_buffer *events = &sinfo->events; | ||
91 | struct rc_pid_events_file_info *file_info; | ||
92 | unsigned long status; | ||
93 | |||
94 | /* Allocate a state struct */ | ||
95 | file_info = kmalloc(sizeof(*file_info), GFP_KERNEL); | ||
96 | if (file_info == NULL) | ||
97 | return -ENOMEM; | ||
98 | |||
99 | spin_lock_irqsave(&events->lock, status); | ||
100 | |||
101 | file_info->next_entry = events->next_entry; | ||
102 | file_info->events = events; | ||
103 | |||
104 | spin_unlock_irqrestore(&events->lock, status); | ||
105 | |||
106 | file->private_data = file_info; | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | static int rate_control_pid_events_release(struct inode *inode, | ||
112 | struct file *file) | ||
113 | { | ||
114 | struct rc_pid_events_file_info *file_info = file->private_data; | ||
115 | |||
116 | kfree(file_info); | ||
117 | |||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | static unsigned int rate_control_pid_events_poll(struct file *file, | ||
122 | poll_table *wait) | ||
123 | { | ||
124 | struct rc_pid_events_file_info *file_info = file->private_data; | ||
125 | |||
126 | poll_wait(file, &file_info->events->waitqueue, wait); | ||
127 | |||
128 | return POLLIN | POLLRDNORM; | ||
129 | } | ||
130 | |||
131 | #define RC_PID_PRINT_BUF_SIZE 64 | ||
132 | |||
133 | static ssize_t rate_control_pid_events_read(struct file *file, char __user *buf, | ||
134 | size_t length, loff_t *offset) | ||
135 | { | ||
136 | struct rc_pid_events_file_info *file_info = file->private_data; | ||
137 | struct rc_pid_event_buffer *events = file_info->events; | ||
138 | struct rc_pid_event *ev; | ||
139 | char pb[RC_PID_PRINT_BUF_SIZE]; | ||
140 | int ret; | ||
141 | int p; | ||
142 | unsigned long status; | ||
143 | |||
144 | /* Check if there is something to read. */ | ||
145 | if (events->next_entry == file_info->next_entry) { | ||
146 | if (file->f_flags & O_NONBLOCK) | ||
147 | return -EAGAIN; | ||
148 | |||
149 | /* Wait */ | ||
150 | ret = wait_event_interruptible(events->waitqueue, | ||
151 | events->next_entry != file_info->next_entry); | ||
152 | |||
153 | if (ret) | ||
154 | return ret; | ||
155 | } | ||
156 | |||
157 | /* Write out one event per call. I don't care whether it's a little | ||
158 | * inefficient, this is debugging code anyway. */ | ||
159 | spin_lock_irqsave(&events->lock, status); | ||
160 | |||
161 | /* Get an event */ | ||
162 | ev = &(events->ring[file_info->next_entry]); | ||
163 | file_info->next_entry = (file_info->next_entry + 1) % | ||
164 | RC_PID_EVENT_RING_SIZE; | ||
165 | |||
166 | /* Print information about the event. Note that userspace needs to | ||
167 | * provide large enough buffers. */ | ||
168 | length = length < RC_PID_PRINT_BUF_SIZE ? | ||
169 | length : RC_PID_PRINT_BUF_SIZE; | ||
170 | p = scnprintf(pb, length, "%u %lu ", ev->id, ev->timestamp); | ||
171 | switch (ev->type) { | ||
172 | case RC_PID_EVENT_TYPE_TX_STATUS: | ||
173 | p += scnprintf(pb + p, length - p, "tx_status %u %u", | ||
174 | !(ev->data.flags & IEEE80211_TX_STAT_ACK), | ||
175 | ev->data.tx_status.status.rates[0].idx); | ||
176 | break; | ||
177 | case RC_PID_EVENT_TYPE_RATE_CHANGE: | ||
178 | p += scnprintf(pb + p, length - p, "rate_change %d %d", | ||
179 | ev->data.index, ev->data.rate); | ||
180 | break; | ||
181 | case RC_PID_EVENT_TYPE_TX_RATE: | ||
182 | p += scnprintf(pb + p, length - p, "tx_rate %d %d", | ||
183 | ev->data.index, ev->data.rate); | ||
184 | break; | ||
185 | case RC_PID_EVENT_TYPE_PF_SAMPLE: | ||
186 | p += scnprintf(pb + p, length - p, | ||
187 | "pf_sample %d %d %d %d", | ||
188 | ev->data.pf_sample, ev->data.prop_err, | ||
189 | ev->data.int_err, ev->data.der_err); | ||
190 | break; | ||
191 | } | ||
192 | p += scnprintf(pb + p, length - p, "\n"); | ||
193 | |||
194 | spin_unlock_irqrestore(&events->lock, status); | ||
195 | |||
196 | if (copy_to_user(buf, pb, p)) | ||
197 | return -EFAULT; | ||
198 | |||
199 | return p; | ||
200 | } | ||
201 | |||
202 | #undef RC_PID_PRINT_BUF_SIZE | ||
203 | |||
204 | static const struct file_operations rc_pid_fop_events = { | ||
205 | .owner = THIS_MODULE, | ||
206 | .read = rate_control_pid_events_read, | ||
207 | .poll = rate_control_pid_events_poll, | ||
208 | .open = rate_control_pid_events_open, | ||
209 | .release = rate_control_pid_events_release, | ||
210 | .llseek = noop_llseek, | ||
211 | }; | ||
212 | |||
213 | void rate_control_pid_add_sta_debugfs(void *priv, void *priv_sta, | ||
214 | struct dentry *dir) | ||
215 | { | ||
216 | struct rc_pid_sta_info *spinfo = priv_sta; | ||
217 | |||
218 | spinfo->events_entry = debugfs_create_file("rc_pid_events", S_IRUGO, | ||
219 | dir, spinfo, | ||
220 | &rc_pid_fop_events); | ||
221 | } | ||
222 | |||
223 | void rate_control_pid_remove_sta_debugfs(void *priv, void *priv_sta) | ||
224 | { | ||
225 | struct rc_pid_sta_info *spinfo = priv_sta; | ||
226 | |||
227 | debugfs_remove(spinfo->events_entry); | ||
228 | } | ||