diff options
author | Eric Dumazet <edumazet@google.com> | 2012-05-11 23:32:13 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-05-12 15:50:49 -0400 |
commit | 536edd67109df5e0cdb2c4ee759e9bade7976367 (patch) | |
tree | b253ee5ce32fdc37346120c9ebbfd1f187ad6b95 | |
parent | 470f16c83ce5e481d50cb6da076e836b6219a57c (diff) |
codel: use Newton method instead of sqrt() and divides
As Van pointed out, interval/sqrt(count) can be implemented using
multiplies only.
http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Iterative_methods_for_reciprocal_square_roots
This patch implements the Newton method and reciprocal divide.
Total cost is 15 cycles instead of 120 on my Corei5 machine (64bit
kernel).
There is a small 'error' for count values < 5, but we don't really care.
I reuse a hole in struct codel_vars :
- pack the dropping boolean into one bit
- use 31bit to store the reciprocal value of sqrt(count).
Suggested-by: Van Jacobson <van@pollere.net>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Dave Taht <dave.taht@bufferbloat.net>
Cc: Kathleen Nichols <nichols@pollere.com>
Cc: Tom Herbert <therbert@google.com>
Cc: Matt Mathis <mattmathis@google.com>
Cc: Yuchung Cheng <ycheng@google.com>
Cc: Nandita Dukkipati <nanditad@google.com>
Cc: Stephen Hemminger <shemminger@vyatta.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/codel.h | 68 |
1 files changed, 37 insertions, 31 deletions
diff --git a/include/net/codel.h b/include/net/codel.h index bce2cefa8c94..bd8747c3ba69 100644 --- a/include/net/codel.h +++ b/include/net/codel.h | |||
@@ -46,6 +46,7 @@ | |||
46 | #include <linux/skbuff.h> | 46 | #include <linux/skbuff.h> |
47 | #include <net/pkt_sched.h> | 47 | #include <net/pkt_sched.h> |
48 | #include <net/inet_ecn.h> | 48 | #include <net/inet_ecn.h> |
49 | #include <linux/reciprocal_div.h> | ||
49 | 50 | ||
50 | /* Controlling Queue Delay (CoDel) algorithm | 51 | /* Controlling Queue Delay (CoDel) algorithm |
51 | * ========================================= | 52 | * ========================================= |
@@ -123,6 +124,7 @@ struct codel_params { | |||
123 | * entered dropping state | 124 | * entered dropping state |
124 | * @lastcount: count at entry to dropping state | 125 | * @lastcount: count at entry to dropping state |
125 | * @dropping: set to true if in dropping state | 126 | * @dropping: set to true if in dropping state |
127 | * @rec_inv_sqrt: reciprocal value of sqrt(count) >> 1 | ||
126 | * @first_above_time: when we went (or will go) continuously above target | 128 | * @first_above_time: when we went (or will go) continuously above target |
127 | * for interval | 129 | * for interval |
128 | * @drop_next: time to drop next packet, or when we dropped last | 130 | * @drop_next: time to drop next packet, or when we dropped last |
@@ -131,7 +133,8 @@ struct codel_params { | |||
131 | struct codel_vars { | 133 | struct codel_vars { |
132 | u32 count; | 134 | u32 count; |
133 | u32 lastcount; | 135 | u32 lastcount; |
134 | bool dropping; | 136 | bool dropping:1; |
137 | u32 rec_inv_sqrt:31; | ||
135 | codel_time_t first_above_time; | 138 | codel_time_t first_above_time; |
136 | codel_time_t drop_next; | 139 | codel_time_t drop_next; |
137 | codel_time_t ldelay; | 140 | codel_time_t ldelay; |
@@ -158,11 +161,7 @@ static void codel_params_init(struct codel_params *params) | |||
158 | 161 | ||
159 | static void codel_vars_init(struct codel_vars *vars) | 162 | static void codel_vars_init(struct codel_vars *vars) |
160 | { | 163 | { |
161 | vars->drop_next = 0; | 164 | memset(vars, 0, sizeof(*vars)); |
162 | vars->first_above_time = 0; | ||
163 | vars->dropping = false; /* exit dropping state */ | ||
164 | vars->count = 0; | ||
165 | vars->lastcount = 0; | ||
166 | } | 165 | } |
167 | 166 | ||
168 | static void codel_stats_init(struct codel_stats *stats) | 167 | static void codel_stats_init(struct codel_stats *stats) |
@@ -170,38 +169,37 @@ static void codel_stats_init(struct codel_stats *stats) | |||
170 | stats->maxpacket = 256; | 169 | stats->maxpacket = 256; |
171 | } | 170 | } |
172 | 171 | ||
173 | /* return interval/sqrt(x) with good precision | 172 | /* |
174 | * relies on int_sqrt(unsigned long x) kernel implementation | 173 | * http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Iterative_methods_for_reciprocal_square_roots |
174 | * new_invsqrt = (invsqrt / 2) * (3 - count * invsqrt^2) | ||
175 | * | ||
176 | * Here, invsqrt is a fixed point number (< 1.0), 31bit mantissa) | ||
175 | */ | 177 | */ |
176 | static u32 codel_inv_sqrt(u32 _interval, u32 _x) | 178 | static void codel_Newton_step(struct codel_vars *vars) |
177 | { | 179 | { |
178 | u64 interval = _interval; | 180 | u32 invsqrt = vars->rec_inv_sqrt; |
179 | unsigned long x = _x; | 181 | u32 invsqrt2 = ((u64)invsqrt * invsqrt) >> 31; |
182 | u64 val = (3LL << 31) - ((u64)vars->count * invsqrt2); | ||
180 | 183 | ||
181 | /* Scale operands for max precision */ | 184 | val = (val * invsqrt) >> 32; |
182 | |||
183 | #if BITS_PER_LONG == 64 | ||
184 | x <<= 32; /* On 64bit arches, we can prescale x by 32bits */ | ||
185 | interval <<= 16; | ||
186 | #endif | ||
187 | 185 | ||
188 | while (x < (1UL << (BITS_PER_LONG - 2))) { | 186 | vars->rec_inv_sqrt = val; |
189 | x <<= 2; | ||
190 | interval <<= 1; | ||
191 | } | ||
192 | do_div(interval, int_sqrt(x)); | ||
193 | return (u32)interval; | ||
194 | } | 187 | } |
195 | 188 | ||
189 | /* | ||
190 | * CoDel control_law is t + interval/sqrt(count) | ||
191 | * We maintain in rec_inv_sqrt the reciprocal value of sqrt(count) to avoid | ||
192 | * both sqrt() and divide operation. | ||
193 | */ | ||
196 | static codel_time_t codel_control_law(codel_time_t t, | 194 | static codel_time_t codel_control_law(codel_time_t t, |
197 | codel_time_t interval, | 195 | codel_time_t interval, |
198 | u32 count) | 196 | u32 rec_inv_sqrt) |
199 | { | 197 | { |
200 | return t + codel_inv_sqrt(interval, count); | 198 | return t + reciprocal_divide(interval, rec_inv_sqrt << 1); |
201 | } | 199 | } |
202 | 200 | ||
203 | 201 | ||
204 | static bool codel_should_drop(struct sk_buff *skb, | 202 | static bool codel_should_drop(const struct sk_buff *skb, |
205 | unsigned int *backlog, | 203 | unsigned int *backlog, |
206 | struct codel_vars *vars, | 204 | struct codel_vars *vars, |
207 | struct codel_params *params, | 205 | struct codel_params *params, |
@@ -274,14 +272,16 @@ static struct sk_buff *codel_dequeue(struct Qdisc *sch, | |||
274 | */ | 272 | */ |
275 | while (vars->dropping && | 273 | while (vars->dropping && |
276 | codel_time_after_eq(now, vars->drop_next)) { | 274 | codel_time_after_eq(now, vars->drop_next)) { |
277 | if (++vars->count == 0) /* avoid zero divides */ | 275 | vars->count++; /* dont care of possible wrap |
278 | vars->count = ~0U; | 276 | * since there is no more divide |
277 | */ | ||
278 | codel_Newton_step(vars); | ||
279 | if (params->ecn && INET_ECN_set_ce(skb)) { | 279 | if (params->ecn && INET_ECN_set_ce(skb)) { |
280 | stats->ecn_mark++; | 280 | stats->ecn_mark++; |
281 | vars->drop_next = | 281 | vars->drop_next = |
282 | codel_control_law(vars->drop_next, | 282 | codel_control_law(vars->drop_next, |
283 | params->interval, | 283 | params->interval, |
284 | vars->count); | 284 | vars->rec_inv_sqrt); |
285 | goto end; | 285 | goto end; |
286 | } | 286 | } |
287 | qdisc_drop(skb, sch); | 287 | qdisc_drop(skb, sch); |
@@ -296,7 +296,7 @@ static struct sk_buff *codel_dequeue(struct Qdisc *sch, | |||
296 | vars->drop_next = | 296 | vars->drop_next = |
297 | codel_control_law(vars->drop_next, | 297 | codel_control_law(vars->drop_next, |
298 | params->interval, | 298 | params->interval, |
299 | vars->count); | 299 | vars->rec_inv_sqrt); |
300 | } | 300 | } |
301 | } | 301 | } |
302 | } | 302 | } |
@@ -319,12 +319,18 @@ static struct sk_buff *codel_dequeue(struct Qdisc *sch, | |||
319 | if (codel_time_before(now - vars->drop_next, | 319 | if (codel_time_before(now - vars->drop_next, |
320 | 16 * params->interval)) { | 320 | 16 * params->interval)) { |
321 | vars->count = (vars->count - vars->lastcount) | 1; | 321 | vars->count = (vars->count - vars->lastcount) | 1; |
322 | /* we dont care if rec_inv_sqrt approximation | ||
323 | * is not very precise : | ||
324 | * Next Newton steps will correct it quadratically. | ||
325 | */ | ||
326 | codel_Newton_step(vars); | ||
322 | } else { | 327 | } else { |
323 | vars->count = 1; | 328 | vars->count = 1; |
329 | vars->rec_inv_sqrt = 0x7fffffff; | ||
324 | } | 330 | } |
325 | vars->lastcount = vars->count; | 331 | vars->lastcount = vars->count; |
326 | vars->drop_next = codel_control_law(now, params->interval, | 332 | vars->drop_next = codel_control_law(now, params->interval, |
327 | vars->count); | 333 | vars->rec_inv_sqrt); |
328 | } | 334 | } |
329 | end: | 335 | end: |
330 | return skb; | 336 | return skb; |