diff options
Diffstat (limited to 'target/linux/generic/patches-3.3/041-codel-use-Newton-method-instead-of-sqrt-and-divides.patch')
-rw-r--r-- | target/linux/generic/patches-3.3/041-codel-use-Newton-method-instead-of-sqrt-and-divides.patch | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/target/linux/generic/patches-3.3/041-codel-use-Newton-method-instead-of-sqrt-and-divides.patch b/target/linux/generic/patches-3.3/041-codel-use-Newton-method-instead-of-sqrt-and-divides.patch new file mode 100644 index 0000000..97401bb --- /dev/null +++ b/target/linux/generic/patches-3.3/041-codel-use-Newton-method-instead-of-sqrt-and-divides.patch @@ -0,0 +1,183 @@ +From 536edd67109df5e0cdb2c4ee759e9bade7976367 Mon Sep 17 00:00:00 2001 +From: Eric Dumazet <edumazet@google.com> +Date: Sat, 12 May 2012 03:32:13 +0000 +Subject: [PATCH 37/41] 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> +--- + include/net/codel.h | 68 ++++++++++++++++++++++++++++----------------------- + 1 file changed, 37 insertions(+), 31 deletions(-) + +--- a/include/net/codel.h ++++ b/include/net/codel.h +@@ -46,6 +46,7 @@ + #include <linux/skbuff.h> + #include <net/pkt_sched.h> + #include <net/inet_ecn.h> ++#include <linux/reciprocal_div.h> + + /* Controlling Queue Delay (CoDel) algorithm + * ========================================= +@@ -123,6 +124,7 @@ struct codel_params { + * entered dropping state + * @lastcount: count at entry to dropping state + * @dropping: set to true if in dropping state ++ * @rec_inv_sqrt: reciprocal value of sqrt(count) >> 1 + * @first_above_time: when we went (or will go) continuously above target + * for interval + * @drop_next: time to drop next packet, or when we dropped last +@@ -131,7 +133,8 @@ struct codel_params { + struct codel_vars { + u32 count; + u32 lastcount; +- bool dropping; ++ bool dropping:1; ++ u32 rec_inv_sqrt:31; + codel_time_t first_above_time; + codel_time_t drop_next; + codel_time_t ldelay; +@@ -158,11 +161,7 @@ static void codel_params_init(struct cod + + static void codel_vars_init(struct codel_vars *vars) + { +- vars->drop_next = 0; +- vars->first_above_time = 0; +- vars->dropping = false; /* exit dropping state */ +- vars->count = 0; +- vars->lastcount = 0; ++ memset(vars, 0, sizeof(*vars)); + } + + static void codel_stats_init(struct codel_stats *stats) +@@ -170,38 +169,37 @@ static void codel_stats_init(struct code + stats->maxpacket = 256; + } + +-/* return interval/sqrt(x) with good precision +- * relies on int_sqrt(unsigned long x) kernel implementation ++/* ++ * http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Iterative_methods_for_reciprocal_square_roots ++ * new_invsqrt = (invsqrt / 2) * (3 - count * invsqrt^2) ++ * ++ * Here, invsqrt is a fixed point number (< 1.0), 31bit mantissa) + */ +-static u32 codel_inv_sqrt(u32 _interval, u32 _x) ++static void codel_Newton_step(struct codel_vars *vars) + { +- u64 interval = _interval; +- unsigned long x = _x; +- +- /* Scale operands for max precision */ ++ u32 invsqrt = vars->rec_inv_sqrt; ++ u32 invsqrt2 = ((u64)invsqrt * invsqrt) >> 31; ++ u64 val = (3LL << 31) - ((u64)vars->count * invsqrt2); + +-#if BITS_PER_LONG == 64 +- x <<= 32; /* On 64bit arches, we can prescale x by 32bits */ +- interval <<= 16; +-#endif ++ val = (val * invsqrt) >> 32; + +- while (x < (1UL << (BITS_PER_LONG - 2))) { +- x <<= 2; +- interval <<= 1; +- } +- do_div(interval, int_sqrt(x)); +- return (u32)interval; ++ vars->rec_inv_sqrt = val; + } + ++/* ++ * CoDel control_law is t + interval/sqrt(count) ++ * We maintain in rec_inv_sqrt the reciprocal value of sqrt(count) to avoid ++ * both sqrt() and divide operation. ++ */ + static codel_time_t codel_control_law(codel_time_t t, + codel_time_t interval, +- u32 count) ++ u32 rec_inv_sqrt) + { +- return t + codel_inv_sqrt(interval, count); ++ return t + reciprocal_divide(interval, rec_inv_sqrt << 1); + } + + +-static bool codel_should_drop(struct sk_buff *skb, ++static bool codel_should_drop(const struct sk_buff *skb, + unsigned int *backlog, + struct codel_vars *vars, + struct codel_params *params, +@@ -274,14 +272,16 @@ static struct sk_buff *codel_dequeue(str + */ + while (vars->dropping && + codel_time_after_eq(now, vars->drop_next)) { +- if (++vars->count == 0) /* avoid zero divides */ +- vars->count = ~0U; ++ vars->count++; /* dont care of possible wrap ++ * since there is no more divide ++ */ ++ codel_Newton_step(vars); + if (params->ecn && INET_ECN_set_ce(skb)) { + stats->ecn_mark++; + vars->drop_next = + codel_control_law(vars->drop_next, + params->interval, +- vars->count); ++ vars->rec_inv_sqrt); + goto end; + } + qdisc_drop(skb, sch); +@@ -296,7 +296,7 @@ static struct sk_buff *codel_dequeue(str + vars->drop_next = + codel_control_law(vars->drop_next, + params->interval, +- vars->count); ++ vars->rec_inv_sqrt); + } + } + } +@@ -319,12 +319,18 @@ static struct sk_buff *codel_dequeue(str + if (codel_time_before(now - vars->drop_next, + 16 * params->interval)) { + vars->count = (vars->count - vars->lastcount) | 1; ++ /* we dont care if rec_inv_sqrt approximation ++ * is not very precise : ++ * Next Newton steps will correct it quadratically. ++ */ ++ codel_Newton_step(vars); + } else { + vars->count = 1; ++ vars->rec_inv_sqrt = 0x7fffffff; + } + vars->lastcount = vars->count; + vars->drop_next = codel_control_law(now, params->interval, +- vars->count); ++ vars->rec_inv_sqrt); + } + end: + return skb; |