mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-30 10:39:04 +00:00
54edbfabac
Codel is a new AQM algorithm and RED replacement designed by Kathie Nichols and Van Jacobson, and published in ACM queue: http://queue.acm.org/detail.cfm?id=2209336 Codel stands for "Controlled Delay", and needs no knobs in the general case, twiddled, for optimum results. It aims for 5ms of delay, at most, when in use. Additionally, fq_codel (by eric dumazet) builds on codel to provide fair queuing superior to what could be had with SFQ, and drop behavior saner than RED, BLUE, or choke. These patches are backported from net-next and are known to work on Linux 3.3.4 and later. Includes updates to codel for better portability and speed SVN-Revision: 31756
184 lines
5.8 KiB
Diff
184 lines
5.8 KiB
Diff
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;
|