aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/generic/patches-3.3/041-codel-use-Newton-method-instead-of-sqrt-and-divides.patch
diff options
context:
space:
mode:
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.patch183
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 0000000000..97401bb12b
--- /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;