aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Dumazet <edumazet@google.com>2012-05-11 23:32:13 -0400
committerDavid S. Miller <davem@davemloft.net>2012-05-12 15:50:49 -0400
commit536edd67109df5e0cdb2c4ee759e9bade7976367 (patch)
treeb253ee5ce32fdc37346120c9ebbfd1f187ad6b95
parent470f16c83ce5e481d50cb6da076e836b6219a57c (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.h68
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 {
131struct codel_vars { 133struct 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
159static void codel_vars_init(struct codel_vars *vars) 162static 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
168static void codel_stats_init(struct codel_stats *stats) 167static 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 */
176static u32 codel_inv_sqrt(u32 _interval, u32 _x) 178static 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 */
196static codel_time_t codel_control_law(codel_time_t t, 194static 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
204static bool codel_should_drop(struct sk_buff *skb, 202static 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 }
329end: 335end:
330 return skb; 336 return skb;