diff options
-rw-r--r-- | net/mac80211/Makefile | 11 | ||||
-rw-r--r-- | net/mac80211/rc80211_pid.h | 242 | ||||
-rw-r--r-- | net/mac80211/rc80211_pid_algo.c (renamed from net/mac80211/rc80211_pid.c) | 149 | ||||
-rw-r--r-- | net/mac80211/rc80211_pid_debugfs.c | 223 |
4 files changed, 508 insertions, 117 deletions
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index a375f0477da0..06aea8009cd4 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile | |||
@@ -1,10 +1,17 @@ | |||
1 | obj-$(CONFIG_MAC80211) += mac80211.o | 1 | obj-$(CONFIG_MAC80211) += mac80211.o |
2 | 2 | ||
3 | mac80211-objs-$(CONFIG_MAC80211_LEDS) += ieee80211_led.o | 3 | mac80211-objs-$(CONFIG_MAC80211_LEDS) += ieee80211_led.o |
4 | mac80211-objs-$(CONFIG_MAC80211_DEBUGFS) += debugfs.o debugfs_sta.o debugfs_netdev.o debugfs_key.o | ||
5 | mac80211-objs-$(CONFIG_NET_SCHED) += wme.o | 4 | mac80211-objs-$(CONFIG_NET_SCHED) += wme.o |
6 | mac80211-objs-$(CONFIG_MAC80211_RC_SIMPLE) += rc80211_simple.o | 5 | mac80211-objs-$(CONFIG_MAC80211_RC_SIMPLE) += rc80211_simple.o |
7 | mac80211-objs-$(CONFIG_MAC80211_RC_PID) += rc80211_pid.o | 6 | mac80211-objs-$(CONFIG_MAC80211_RC_PID) += rc80211_pid_algo.o |
7 | |||
8 | mac80211-debugfs-objs-$(CONFIG_MAC80211_RC_PID) += rc80211_pid_debugfs.o | ||
9 | mac80211-objs-$(CONFIG_MAC80211_DEBUGFS) += \ | ||
10 | debugfs.o \ | ||
11 | debugfs_sta.o \ | ||
12 | debugfs_netdev.o \ | ||
13 | debugfs_key.o \ | ||
14 | $(mac80211-debugfs-objs-y) | ||
8 | 15 | ||
9 | mac80211-objs := \ | 16 | mac80211-objs := \ |
10 | ieee80211.o \ | 17 | ieee80211.o \ |
diff --git a/net/mac80211/rc80211_pid.h b/net/mac80211/rc80211_pid.h new file mode 100644 index 000000000000..5d0056c1513b --- /dev/null +++ b/net/mac80211/rc80211_pid.h | |||
@@ -0,0 +1,242 @@ | |||
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 | #ifndef RC80211_PID_H | ||
10 | #define RC80211_PID_H | ||
11 | |||
12 | /* Sampling period for measuring percentage of failed frames. */ | ||
13 | #define RC_PID_INTERVAL (HZ / 8) | ||
14 | |||
15 | /* Exponential averaging smoothness (used for I part of PID controller) */ | ||
16 | #define RC_PID_SMOOTHING_SHIFT 3 | ||
17 | #define RC_PID_SMOOTHING (1 << RC_PID_SMOOTHING_SHIFT) | ||
18 | |||
19 | /* Sharpening factor (used for D part of PID controller) */ | ||
20 | #define RC_PID_SHARPENING_FACTOR 0 | ||
21 | #define RC_PID_SHARPENING_DURATION 0 | ||
22 | |||
23 | /* Fixed point arithmetic shifting amount. */ | ||
24 | #define RC_PID_ARITH_SHIFT 8 | ||
25 | |||
26 | /* Fixed point arithmetic factor. */ | ||
27 | #define RC_PID_ARITH_FACTOR (1 << RC_PID_ARITH_SHIFT) | ||
28 | |||
29 | /* Proportional PID component coefficient. */ | ||
30 | #define RC_PID_COEFF_P 15 | ||
31 | /* Integral PID component coefficient. */ | ||
32 | #define RC_PID_COEFF_I 9 | ||
33 | /* Derivative PID component coefficient. */ | ||
34 | #define RC_PID_COEFF_D 15 | ||
35 | |||
36 | /* Target failed frames rate for the PID controller. NB: This effectively gives | ||
37 | * maximum failed frames percentage we're willing to accept. If the wireless | ||
38 | * link quality is good, the controller will fail to adjust failed frames | ||
39 | * percentage to the target. This is intentional. | ||
40 | */ | ||
41 | #define RC_PID_TARGET_PF (11 << RC_PID_ARITH_SHIFT) | ||
42 | |||
43 | /* Rate behaviour normalization quantity over time. */ | ||
44 | #define RC_PID_NORM_OFFSET 3 | ||
45 | |||
46 | /* Push high rates right after loading. */ | ||
47 | #define RC_PID_FAST_START 0 | ||
48 | |||
49 | /* Arithmetic right shift for positive and negative values for ISO C. */ | ||
50 | #define RC_PID_DO_ARITH_RIGHT_SHIFT(x, y) \ | ||
51 | (x) < 0 ? -((-(x)) >> (y)) : (x) >> (y) | ||
52 | |||
53 | enum rc_pid_event_type { | ||
54 | RC_PID_EVENT_TYPE_TX_STATUS, | ||
55 | RC_PID_EVENT_TYPE_RATE_CHANGE, | ||
56 | RC_PID_EVENT_TYPE_TX_RATE, | ||
57 | RC_PID_EVENT_TYPE_PF_SAMPLE, | ||
58 | }; | ||
59 | |||
60 | union rc_pid_event_data { | ||
61 | /* RC_PID_EVENT_TX_STATUS */ | ||
62 | struct { | ||
63 | struct ieee80211_tx_status tx_status; | ||
64 | }; | ||
65 | /* RC_PID_EVENT_TYPE_RATE_CHANGE */ | ||
66 | /* RC_PID_EVENT_TYPE_TX_RATE */ | ||
67 | struct { | ||
68 | int index; | ||
69 | int rate; | ||
70 | }; | ||
71 | /* RC_PID_EVENT_TYPE_PF_SAMPLE */ | ||
72 | struct { | ||
73 | s32 pf_sample; | ||
74 | s32 prop_err; | ||
75 | s32 int_err; | ||
76 | s32 der_err; | ||
77 | }; | ||
78 | }; | ||
79 | |||
80 | struct rc_pid_event { | ||
81 | /* The time when the event occured */ | ||
82 | unsigned long timestamp; | ||
83 | |||
84 | /* Event ID number */ | ||
85 | unsigned int id; | ||
86 | |||
87 | /* Type of event */ | ||
88 | enum rc_pid_event_type type; | ||
89 | |||
90 | /* type specific data */ | ||
91 | union rc_pid_event_data data; | ||
92 | }; | ||
93 | |||
94 | /* Size of the event ring buffer. */ | ||
95 | #define RC_PID_EVENT_RING_SIZE 32 | ||
96 | |||
97 | struct rc_pid_event_buffer { | ||
98 | /* Counter that generates event IDs */ | ||
99 | unsigned int ev_count; | ||
100 | |||
101 | /* Ring buffer of events */ | ||
102 | struct rc_pid_event ring[RC_PID_EVENT_RING_SIZE]; | ||
103 | |||
104 | /* Index to the entry in events_buf to be reused */ | ||
105 | unsigned int next_entry; | ||
106 | |||
107 | /* Lock that guards against concurrent access to this buffer struct */ | ||
108 | spinlock_t lock; | ||
109 | |||
110 | /* Wait queue for poll/select and blocking I/O */ | ||
111 | wait_queue_head_t waitqueue; | ||
112 | }; | ||
113 | |||
114 | struct rc_pid_events_file_info { | ||
115 | /* The event buffer we read */ | ||
116 | struct rc_pid_event_buffer *events; | ||
117 | |||
118 | /* The entry we have should read next */ | ||
119 | unsigned int next_entry; | ||
120 | }; | ||
121 | |||
122 | void rate_control_pid_event_tx_status(struct rc_pid_event_buffer *buf, | ||
123 | struct ieee80211_tx_status *stat); | ||
124 | |||
125 | void rate_control_pid_event_rate_change(struct rc_pid_event_buffer *buf, | ||
126 | int index, int rate); | ||
127 | |||
128 | void rate_control_pid_event_tx_rate(struct rc_pid_event_buffer *buf, | ||
129 | int index, int rate); | ||
130 | |||
131 | void rate_control_pid_event_pf_sample(struct rc_pid_event_buffer *buf, | ||
132 | s32 pf_sample, s32 prop_err, | ||
133 | s32 int_err, s32 der_err); | ||
134 | |||
135 | void rate_control_pid_add_sta_debugfs(void *priv, void *priv_sta, | ||
136 | struct dentry *dir); | ||
137 | |||
138 | void rate_control_pid_remove_sta_debugfs(void *priv, void *priv_sta); | ||
139 | |||
140 | struct rc_pid_sta_info { | ||
141 | unsigned long last_change; | ||
142 | unsigned long last_sample; | ||
143 | |||
144 | u32 tx_num_failed; | ||
145 | u32 tx_num_xmit; | ||
146 | |||
147 | /* Average failed frames percentage error (i.e. actual vs. target | ||
148 | * percentage), scaled by RC_PID_SMOOTHING. This value is computed | ||
149 | * using using an exponential weighted average technique: | ||
150 | * | ||
151 | * (RC_PID_SMOOTHING - 1) * err_avg_old + err | ||
152 | * err_avg = ------------------------------------------ | ||
153 | * RC_PID_SMOOTHING | ||
154 | * | ||
155 | * where err_avg is the new approximation, err_avg_old the previous one | ||
156 | * and err is the error w.r.t. to the current failed frames percentage | ||
157 | * sample. Note that the bigger RC_PID_SMOOTHING the more weight is | ||
158 | * given to the previous estimate, resulting in smoother behavior (i.e. | ||
159 | * corresponding to a longer integration window). | ||
160 | * | ||
161 | * For computation, we actually don't use the above formula, but this | ||
162 | * one: | ||
163 | * | ||
164 | * err_avg_scaled = err_avg_old_scaled - err_avg_old + err | ||
165 | * | ||
166 | * where: | ||
167 | * err_avg_scaled = err * RC_PID_SMOOTHING | ||
168 | * err_avg_old_scaled = err_avg_old * RC_PID_SMOOTHING | ||
169 | * | ||
170 | * This avoids floating point numbers and the per_failed_old value can | ||
171 | * easily be obtained by shifting per_failed_old_scaled right by | ||
172 | * RC_PID_SMOOTHING_SHIFT. | ||
173 | */ | ||
174 | s32 err_avg_sc; | ||
175 | |||
176 | /* Last framed failes percentage sample. */ | ||
177 | u32 last_pf; | ||
178 | |||
179 | /* Sharpening needed. */ | ||
180 | u8 sharp_cnt; | ||
181 | |||
182 | #ifdef CONFIG_MAC80211_DEBUGFS | ||
183 | /* Event buffer */ | ||
184 | struct rc_pid_event_buffer events; | ||
185 | |||
186 | /* Events debugfs file entry */ | ||
187 | struct dentry *events_entry; | ||
188 | #endif | ||
189 | }; | ||
190 | |||
191 | /* Algorithm parameters. We keep them on a per-algorithm approach, so they can | ||
192 | * be tuned individually for each interface. | ||
193 | */ | ||
194 | struct rc_pid_rateinfo { | ||
195 | |||
196 | /* Map sorted rates to rates in ieee80211_hw_mode. */ | ||
197 | int index; | ||
198 | |||
199 | /* Map rates in ieee80211_hw_mode to sorted rates. */ | ||
200 | int rev_index; | ||
201 | |||
202 | /* Did we do any measurement on this rate? */ | ||
203 | bool valid; | ||
204 | |||
205 | /* Comparison with the lowest rate. */ | ||
206 | int diff; | ||
207 | }; | ||
208 | |||
209 | struct rc_pid_info { | ||
210 | |||
211 | /* The failed frames percentage target. */ | ||
212 | unsigned int target; | ||
213 | |||
214 | /* Rate at which failed frames percentage is sampled in 0.001s. */ | ||
215 | unsigned int sampling_period; | ||
216 | |||
217 | /* P, I and D coefficients. */ | ||
218 | int coeff_p; | ||
219 | int coeff_i; | ||
220 | int coeff_d; | ||
221 | |||
222 | /* Exponential averaging shift. */ | ||
223 | unsigned int smoothing_shift; | ||
224 | |||
225 | /* Sharpening shift and duration. */ | ||
226 | unsigned int sharpen_shift; | ||
227 | unsigned int sharpen_duration; | ||
228 | |||
229 | /* Normalization offset. */ | ||
230 | unsigned int norm_offset; | ||
231 | |||
232 | /* Fast starst parameter. */ | ||
233 | unsigned int fast_start; | ||
234 | |||
235 | /* Rates information. */ | ||
236 | struct rc_pid_rateinfo *rinfo; | ||
237 | |||
238 | /* Index of the last used rate. */ | ||
239 | int oldrate; | ||
240 | }; | ||
241 | |||
242 | #endif /* RC80211_PID_H */ | ||
diff --git a/net/mac80211/rc80211_pid.c b/net/mac80211/rc80211_pid_algo.c index 7f8cf27ad2f9..3fac3a5d7e00 100644 --- a/net/mac80211/rc80211_pid.c +++ b/net/mac80211/rc80211_pid_algo.c | |||
@@ -16,6 +16,8 @@ | |||
16 | #include <net/mac80211.h> | 16 | #include <net/mac80211.h> |
17 | #include "ieee80211_rate.h" | 17 | #include "ieee80211_rate.h" |
18 | 18 | ||
19 | #include "rc80211_pid.h" | ||
20 | |||
19 | 21 | ||
20 | /* This is an implementation of a TX rate control algorithm that uses a PID | 22 | /* This is an implementation of a TX rate control algorithm that uses a PID |
21 | * controller. Given a target failed frames rate, the controller decides about | 23 | * controller. Given a target failed frames rate, the controller decides about |
@@ -61,121 +63,6 @@ | |||
61 | * RC_PID_ARITH_SHIFT. | 63 | * RC_PID_ARITH_SHIFT. |
62 | */ | 64 | */ |
63 | 65 | ||
64 | /* Sampling period for measuring percentage of failed frames. */ | ||
65 | #define RC_PID_INTERVAL (HZ / 8) | ||
66 | |||
67 | /* Exponential averaging smoothness (used for I part of PID controller) */ | ||
68 | #define RC_PID_SMOOTHING_SHIFT 3 | ||
69 | #define RC_PID_SMOOTHING (1 << RC_PID_SMOOTHING_SHIFT) | ||
70 | |||
71 | /* Sharpening factor (used for D part of PID controller) */ | ||
72 | #define RC_PID_SHARPENING_FACTOR 0 | ||
73 | #define RC_PID_SHARPENING_DURATION 0 | ||
74 | |||
75 | /* Fixed point arithmetic shifting amount. */ | ||
76 | #define RC_PID_ARITH_SHIFT 8 | ||
77 | |||
78 | /* Fixed point arithmetic factor. */ | ||
79 | #define RC_PID_ARITH_FACTOR (1 << RC_PID_ARITH_SHIFT) | ||
80 | |||
81 | /* Proportional PID component coefficient. */ | ||
82 | #define RC_PID_COEFF_P 15 | ||
83 | /* Integral PID component coefficient. */ | ||
84 | #define RC_PID_COEFF_I 9 | ||
85 | /* Derivative PID component coefficient. */ | ||
86 | #define RC_PID_COEFF_D 15 | ||
87 | |||
88 | /* Target failed frames rate for the PID controller. NB: This effectively gives | ||
89 | * maximum failed frames percentage we're willing to accept. If the wireless | ||
90 | * link quality is good, the controller will fail to adjust failed frames | ||
91 | * percentage to the target. This is intentional. | ||
92 | */ | ||
93 | #define RC_PID_TARGET_PF (11 << RC_PID_ARITH_SHIFT) | ||
94 | |||
95 | /* Rate behaviour normalization quantity over time. */ | ||
96 | #define RC_PID_NORM_OFFSET 3 | ||
97 | |||
98 | /* Push high rates right after loading. */ | ||
99 | #define RC_PID_FAST_START 0 | ||
100 | |||
101 | /* Arithmetic right shift for positive and negative values for ISO C. */ | ||
102 | #define RC_PID_DO_ARITH_RIGHT_SHIFT(x, y) \ | ||
103 | (x) < 0 ? -((-(x)) >> (y)) : (x) >> (y) | ||
104 | |||
105 | struct rc_pid_sta_info { | ||
106 | unsigned long last_change; | ||
107 | unsigned long last_sample; | ||
108 | |||
109 | u32 tx_num_failed; | ||
110 | u32 tx_num_xmit; | ||
111 | |||
112 | /* Average failed frames percentage error (i.e. actual vs. target | ||
113 | * percentage), scaled by RC_PID_SMOOTHING. This value is computed | ||
114 | * using using an exponential weighted average technique: | ||
115 | * | ||
116 | * (RC_PID_SMOOTHING - 1) * err_avg_old + err | ||
117 | * err_avg = ------------------------------------------ | ||
118 | * RC_PID_SMOOTHING | ||
119 | * | ||
120 | * where err_avg is the new approximation, err_avg_old the previous one | ||
121 | * and err is the error w.r.t. to the current failed frames percentage | ||
122 | * sample. Note that the bigger RC_PID_SMOOTHING the more weight is | ||
123 | * given to the previous estimate, resulting in smoother behavior (i.e. | ||
124 | * corresponding to a longer integration window). | ||
125 | * | ||
126 | * For computation, we actually don't use the above formula, but this | ||
127 | * one: | ||
128 | * | ||
129 | * err_avg_scaled = err_avg_old_scaled - err_avg_old + err | ||
130 | * | ||
131 | * where: | ||
132 | * err_avg_scaled = err * RC_PID_SMOOTHING | ||
133 | * err_avg_old_scaled = err_avg_old * RC_PID_SMOOTHING | ||
134 | * | ||
135 | * This avoids floating point numbers and the per_failed_old value can | ||
136 | * easily be obtained by shifting per_failed_old_scaled right by | ||
137 | * RC_PID_SMOOTHING_SHIFT. | ||
138 | */ | ||
139 | s32 err_avg_sc; | ||
140 | |||
141 | /* Last framed failes percentage sample. */ | ||
142 | u32 last_pf; | ||
143 | |||
144 | /* Sharpening needed. */ | ||
145 | u8 sharp_cnt; | ||
146 | }; | ||
147 | |||
148 | /* Algorithm parameters. We keep them on a per-algorithm approach, so they can | ||
149 | * be tuned individually for each interface. | ||
150 | */ | ||
151 | struct rc_pid_rateinfo { | ||
152 | |||
153 | /* Map sorted rates to rates in ieee80211_hw_mode. */ | ||
154 | int index; | ||
155 | |||
156 | /* Map rates in ieee80211_hw_mode to sorted rates. */ | ||
157 | int rev_index; | ||
158 | |||
159 | /* Comparison with the lowest rate. */ | ||
160 | int diff; | ||
161 | }; | ||
162 | |||
163 | struct rc_pid_info { | ||
164 | |||
165 | /* The failed frames percentage target. */ | ||
166 | u32 target; | ||
167 | |||
168 | /* P, I and D coefficients. */ | ||
169 | s32 coeff_p; | ||
170 | s32 coeff_i; | ||
171 | s32 coeff_d; | ||
172 | |||
173 | /* Rates information. */ | ||
174 | struct rc_pid_rateinfo *rinfo; | ||
175 | |||
176 | /* Index of the last used rate. */ | ||
177 | int oldrate; | ||
178 | }; | ||
179 | 66 | ||
180 | /* Shift the adjustment so that we won't switch to a lower rate if it exhibited | 67 | /* Shift the adjustment so that we won't switch to a lower rate if it exhibited |
181 | * a worse failed frames behaviour and we'll choose the highest rate whose | 68 | * a worse failed frames behaviour and we'll choose the highest rate whose |
@@ -243,6 +130,12 @@ static void rate_control_pid_adjust_rate(struct ieee80211_local *local, | |||
243 | 130 | ||
244 | newidx += back; | 131 | newidx += back; |
245 | } | 132 | } |
133 | |||
134 | #ifdef CONFIG_MAC80211_DEBUGFS | ||
135 | rate_control_pid_event_rate_change( | ||
136 | &((struct rc_pid_sta_info *)sta->rate_ctrl_priv)->events, | ||
137 | newidx, mode->rates[newidx].rate); | ||
138 | #endif | ||
246 | } | 139 | } |
247 | 140 | ||
248 | /* Normalize the failed frames per-rate differences. */ | 141 | /* Normalize the failed frames per-rate differences. */ |
@@ -324,6 +217,11 @@ static void rate_control_pid_sample(struct rc_pid_info *pinfo, | |||
324 | if (spinfo->sharp_cnt) | 217 | if (spinfo->sharp_cnt) |
325 | spinfo->sharp_cnt--; | 218 | spinfo->sharp_cnt--; |
326 | 219 | ||
220 | #ifdef CONFIG_MAC80211_DEBUGFS | ||
221 | rate_control_pid_event_pf_sample(&spinfo->events, pf, err_prop, err_int, | ||
222 | err_der); | ||
223 | #endif | ||
224 | |||
327 | /* Compute the controller output. */ | 225 | /* Compute the controller output. */ |
328 | adj = (err_prop * pinfo->coeff_p + err_int * pinfo->coeff_i | 226 | adj = (err_prop * pinfo->coeff_p + err_int * pinfo->coeff_i |
329 | + err_der * pinfo->coeff_d); | 227 | + err_der * pinfo->coeff_d); |
@@ -357,6 +255,10 @@ static void rate_control_pid_tx_status(void *priv, struct net_device *dev, | |||
357 | spinfo = sta->rate_ctrl_priv; | 255 | spinfo = sta->rate_ctrl_priv; |
358 | spinfo->tx_num_xmit++; | 256 | spinfo->tx_num_xmit++; |
359 | 257 | ||
258 | #ifdef CONFIG_MAC80211_DEBUGFS | ||
259 | rate_control_pid_event_tx_status(&spinfo->events, status); | ||
260 | #endif | ||
261 | |||
360 | /* We count frames that totally failed to be transmitted as two bad | 262 | /* We count frames that totally failed to be transmitted as two bad |
361 | * frames, those that made it out but had some retries as one good and | 263 | * frames, those that made it out but had some retries as one good and |
362 | * one bad frame. */ | 264 | * one bad frame. */ |
@@ -415,6 +317,12 @@ static void rate_control_pid_get_rate(void *priv, struct net_device *dev, | |||
415 | sta_info_put(sta); | 317 | sta_info_put(sta); |
416 | 318 | ||
417 | sel->rate = &mode->rates[rateidx]; | 319 | sel->rate = &mode->rates[rateidx]; |
320 | |||
321 | #ifdef CONFIG_MAC80211_DEBUGFS | ||
322 | rate_control_pid_event_tx_rate( | ||
323 | &((struct rc_pid_sta_info *) sta->rate_ctrl_priv)->events, | ||
324 | rateidx, mode->rates[rateidx].rate); | ||
325 | #endif | ||
418 | } | 326 | } |
419 | 327 | ||
420 | static void rate_control_pid_rate_init(void *priv, void *priv_sta, | 328 | static void rate_control_pid_rate_init(void *priv, void *priv_sta, |
@@ -502,6 +410,13 @@ static void *rate_control_pid_alloc_sta(void *priv, gfp_t gfp) | |||
502 | struct rc_pid_sta_info *spinfo; | 410 | struct rc_pid_sta_info *spinfo; |
503 | 411 | ||
504 | spinfo = kzalloc(sizeof(*spinfo), gfp); | 412 | spinfo = kzalloc(sizeof(*spinfo), gfp); |
413 | if (spinfo == NULL) | ||
414 | return NULL; | ||
415 | |||
416 | #ifdef CONFIG_MAC80211_DEBUGFS | ||
417 | spin_lock_init(&spinfo->events.lock); | ||
418 | init_waitqueue_head(&spinfo->events.waitqueue); | ||
419 | #endif | ||
505 | 420 | ||
506 | return spinfo; | 421 | return spinfo; |
507 | } | 422 | } |
@@ -522,4 +437,8 @@ struct rate_control_ops mac80211_rcpid = { | |||
522 | .free = rate_control_pid_free, | 437 | .free = rate_control_pid_free, |
523 | .alloc_sta = rate_control_pid_alloc_sta, | 438 | .alloc_sta = rate_control_pid_alloc_sta, |
524 | .free_sta = rate_control_pid_free_sta, | 439 | .free_sta = rate_control_pid_free_sta, |
440 | #ifdef CONFIG_MAC80211_DEBUGFS | ||
441 | .add_sta_debugfs = rate_control_pid_add_sta_debugfs, | ||
442 | .remove_sta_debugfs = rate_control_pid_remove_sta_debugfs, | ||
443 | #endif | ||
525 | }; | 444 | }; |
diff --git a/net/mac80211/rc80211_pid_debugfs.c b/net/mac80211/rc80211_pid_debugfs.c new file mode 100644 index 000000000000..91818e4ff002 --- /dev/null +++ b/net/mac80211/rc80211_pid_debugfs.c | |||
@@ -0,0 +1,223 @@ | |||
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/spinlock.h> | ||
10 | #include <linux/poll.h> | ||
11 | #include <linux/netdevice.h> | ||
12 | #include <linux/types.h> | ||
13 | #include <linux/skbuff.h> | ||
14 | |||
15 | #include <net/mac80211.h> | ||
16 | #include "ieee80211_rate.h" | ||
17 | |||
18 | #include "rc80211_pid.h" | ||
19 | |||
20 | static void rate_control_pid_event(struct rc_pid_event_buffer *buf, | ||
21 | enum rc_pid_event_type type, | ||
22 | union rc_pid_event_data *data) | ||
23 | { | ||
24 | struct rc_pid_event *ev; | ||
25 | unsigned long status; | ||
26 | |||
27 | spin_lock_irqsave(&buf->lock, status); | ||
28 | ev = &(buf->ring[buf->next_entry]); | ||
29 | buf->next_entry = (buf->next_entry + 1) % RC_PID_EVENT_RING_SIZE; | ||
30 | |||
31 | ev->timestamp = jiffies; | ||
32 | ev->id = buf->ev_count++; | ||
33 | ev->type = type; | ||
34 | ev->data = *data; | ||
35 | |||
36 | spin_unlock_irqrestore(&buf->lock, status); | ||
37 | |||
38 | wake_up_all(&buf->waitqueue); | ||
39 | } | ||
40 | |||
41 | void rate_control_pid_event_tx_status(struct rc_pid_event_buffer *buf, | ||
42 | struct ieee80211_tx_status *stat) | ||
43 | { | ||
44 | union rc_pid_event_data evd; | ||
45 | |||
46 | memcpy(&evd.tx_status, stat, sizeof(struct ieee80211_tx_status)); | ||
47 | rate_control_pid_event(buf, RC_PID_EVENT_TYPE_TX_STATUS, &evd); | ||
48 | } | ||
49 | |||
50 | void rate_control_pid_event_rate_change(struct rc_pid_event_buffer *buf, | ||
51 | int index, int rate) | ||
52 | { | ||
53 | union rc_pid_event_data evd; | ||
54 | |||
55 | evd.index = index; | ||
56 | evd.rate = rate; | ||
57 | rate_control_pid_event(buf, RC_PID_EVENT_TYPE_RATE_CHANGE, &evd); | ||
58 | } | ||
59 | |||
60 | void rate_control_pid_event_tx_rate(struct rc_pid_event_buffer *buf, | ||
61 | int index, int rate) | ||
62 | { | ||
63 | union rc_pid_event_data evd; | ||
64 | |||
65 | evd.index = index; | ||
66 | evd.rate = rate; | ||
67 | rate_control_pid_event(buf, RC_PID_EVENT_TYPE_TX_RATE, &evd); | ||
68 | } | ||
69 | |||
70 | void rate_control_pid_event_pf_sample(struct rc_pid_event_buffer *buf, | ||
71 | s32 pf_sample, s32 prop_err, | ||
72 | s32 int_err, s32 der_err) | ||
73 | { | ||
74 | union rc_pid_event_data evd; | ||
75 | |||
76 | evd.pf_sample = pf_sample; | ||
77 | evd.prop_err = prop_err; | ||
78 | evd.int_err = int_err; | ||
79 | evd.der_err = der_err; | ||
80 | rate_control_pid_event(buf, RC_PID_EVENT_TYPE_PF_SAMPLE, &evd); | ||
81 | } | ||
82 | |||
83 | static int rate_control_pid_events_open(struct inode *inode, struct file *file) | ||
84 | { | ||
85 | struct rc_pid_sta_info *sinfo = inode->i_private; | ||
86 | struct rc_pid_event_buffer *events = &sinfo->events; | ||
87 | struct rc_pid_events_file_info *file_info; | ||
88 | unsigned int status; | ||
89 | |||
90 | /* Allocate a state struct */ | ||
91 | file_info = kmalloc(sizeof(*file_info), GFP_KERNEL); | ||
92 | if (file_info == NULL) | ||
93 | return -ENOMEM; | ||
94 | |||
95 | spin_lock_irqsave(&events->lock, status); | ||
96 | |||
97 | file_info->next_entry = events->next_entry; | ||
98 | file_info->events = events; | ||
99 | |||
100 | spin_unlock_irqrestore(&events->lock, status); | ||
101 | |||
102 | file->private_data = file_info; | ||
103 | |||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | static int rate_control_pid_events_release(struct inode *inode, | ||
108 | struct file *file) | ||
109 | { | ||
110 | struct rc_pid_events_file_info *file_info = file->private_data; | ||
111 | |||
112 | kfree(file_info); | ||
113 | |||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | static unsigned int rate_control_pid_events_poll(struct file *file, | ||
118 | poll_table *wait) | ||
119 | { | ||
120 | struct rc_pid_events_file_info *file_info = file->private_data; | ||
121 | |||
122 | poll_wait(file, &file_info->events->waitqueue, wait); | ||
123 | |||
124 | return POLLIN | POLLRDNORM; | ||
125 | } | ||
126 | |||
127 | #define RC_PID_PRINT_BUF_SIZE 64 | ||
128 | |||
129 | static ssize_t rate_control_pid_events_read(struct file *file, char __user *buf, | ||
130 | size_t length, loff_t *offset) | ||
131 | { | ||
132 | struct rc_pid_events_file_info *file_info = file->private_data; | ||
133 | struct rc_pid_event_buffer *events = file_info->events; | ||
134 | struct rc_pid_event *ev; | ||
135 | char pb[RC_PID_PRINT_BUF_SIZE]; | ||
136 | int ret; | ||
137 | int p; | ||
138 | unsigned int status; | ||
139 | |||
140 | /* Check if there is something to read. */ | ||
141 | if (events->next_entry == file_info->next_entry) { | ||
142 | if (file->f_flags & O_NONBLOCK) | ||
143 | return -EAGAIN; | ||
144 | |||
145 | /* Wait */ | ||
146 | ret = wait_event_interruptible(events->waitqueue, | ||
147 | events->next_entry != file_info->next_entry); | ||
148 | |||
149 | if (ret) | ||
150 | return ret; | ||
151 | } | ||
152 | |||
153 | /* Write out one event per call. I don't care whether it's a little | ||
154 | * inefficient, this is debugging code anyway. */ | ||
155 | spin_lock_irqsave(&events->lock, status); | ||
156 | |||
157 | /* Get an event */ | ||
158 | ev = &(events->ring[file_info->next_entry]); | ||
159 | file_info->next_entry = (file_info->next_entry + 1) % | ||
160 | RC_PID_EVENT_RING_SIZE; | ||
161 | |||
162 | /* Print information about the event. Note that userpace needs to | ||
163 | * provide large enough buffers. */ | ||
164 | length = length < RC_PID_PRINT_BUF_SIZE ? | ||
165 | length : RC_PID_PRINT_BUF_SIZE; | ||
166 | p = snprintf(pb, length, "%u %lu ", ev->id, ev->timestamp); | ||
167 | switch (ev->type) { | ||
168 | case RC_PID_EVENT_TYPE_TX_STATUS: | ||
169 | p += snprintf(pb + p, length - p, "tx_status %u %u", | ||
170 | ev->data.tx_status.excessive_retries, | ||
171 | ev->data.tx_status.retry_count); | ||
172 | break; | ||
173 | case RC_PID_EVENT_TYPE_RATE_CHANGE: | ||
174 | p += snprintf(pb + p, length - p, "rate_change %d %d", | ||
175 | ev->data.index, ev->data.rate); | ||
176 | break; | ||
177 | case RC_PID_EVENT_TYPE_TX_RATE: | ||
178 | p += snprintf(pb + p, length - p, "tx_rate %d %d", | ||
179 | ev->data.index, ev->data.rate); | ||
180 | break; | ||
181 | case RC_PID_EVENT_TYPE_PF_SAMPLE: | ||
182 | p += snprintf(pb + p, length - p, | ||
183 | "pf_sample %d %d %d %d", | ||
184 | ev->data.pf_sample, ev->data.prop_err, | ||
185 | ev->data.int_err, ev->data.der_err); | ||
186 | break; | ||
187 | } | ||
188 | p += snprintf(pb + p, length - p, "\n"); | ||
189 | |||
190 | spin_unlock_irqrestore(&events->lock, status); | ||
191 | |||
192 | if (copy_to_user(buf, pb, p)) | ||
193 | return -EFAULT; | ||
194 | |||
195 | return p; | ||
196 | } | ||
197 | |||
198 | #undef RC_PID_PRINT_BUF_SIZE | ||
199 | |||
200 | struct file_operations rc_pid_fop_events = { | ||
201 | .owner = THIS_MODULE, | ||
202 | .read = rate_control_pid_events_read, | ||
203 | .poll = rate_control_pid_events_poll, | ||
204 | .open = rate_control_pid_events_open, | ||
205 | .release = rate_control_pid_events_release, | ||
206 | }; | ||
207 | |||
208 | void rate_control_pid_add_sta_debugfs(void *priv, void *priv_sta, | ||
209 | struct dentry *dir) | ||
210 | { | ||
211 | struct rc_pid_sta_info *spinfo = priv_sta; | ||
212 | |||
213 | spinfo->events_entry = debugfs_create_file("rc_pid_events", S_IRUGO, | ||
214 | dir, spinfo, | ||
215 | &rc_pid_fop_events); | ||
216 | } | ||
217 | |||
218 | void rate_control_pid_remove_sta_debugfs(void *priv, void *priv_sta) | ||
219 | { | ||
220 | struct rc_pid_sta_info *spinfo = priv_sta; | ||
221 | |||
222 | debugfs_remove(spinfo->events_entry); | ||
223 | } | ||