162306a36Sopenharmony_ci#ifndef __NET_SCHED_CODEL_IMPL_H 262306a36Sopenharmony_ci#define __NET_SCHED_CODEL_IMPL_H 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci/* 562306a36Sopenharmony_ci * Codel - The Controlled-Delay Active Queue Management algorithm 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2011-2012 Kathleen Nichols <nichols@pollere.com> 862306a36Sopenharmony_ci * Copyright (C) 2011-2012 Van Jacobson <van@pollere.net> 962306a36Sopenharmony_ci * Copyright (C) 2012 Michael D. Taht <dave.taht@bufferbloat.net> 1062306a36Sopenharmony_ci * Copyright (C) 2012,2015 Eric Dumazet <edumazet@google.com> 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 1362306a36Sopenharmony_ci * modification, are permitted provided that the following conditions 1462306a36Sopenharmony_ci * are met: 1562306a36Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 1662306a36Sopenharmony_ci * notice, this list of conditions, and the following disclaimer, 1762306a36Sopenharmony_ci * without modification. 1862306a36Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright 1962306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer in the 2062306a36Sopenharmony_ci * documentation and/or other materials provided with the distribution. 2162306a36Sopenharmony_ci * 3. The names of the authors may not be used to endorse or promote products 2262306a36Sopenharmony_ci * derived from this software without specific prior written permission. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * Alternatively, provided that this notice is retained in full, this 2562306a36Sopenharmony_ci * software may be distributed under the terms of the GNU General 2662306a36Sopenharmony_ci * Public License ("GPL") version 2, in which case the provisions of the 2762306a36Sopenharmony_ci * GPL apply INSTEAD OF those given above. 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 3062306a36Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 3162306a36Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 3262306a36Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 3362306a36Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 3462306a36Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 3562306a36Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3662306a36Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3762306a36Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3862306a36Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 3962306a36Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 4062306a36Sopenharmony_ci * DAMAGE. 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* Controlling Queue Delay (CoDel) algorithm 4562306a36Sopenharmony_ci * ========================================= 4662306a36Sopenharmony_ci * Source : Kathleen Nichols and Van Jacobson 4762306a36Sopenharmony_ci * http://queue.acm.org/detail.cfm?id=2209336 4862306a36Sopenharmony_ci * 4962306a36Sopenharmony_ci * Implemented on linux by Dave Taht and Eric Dumazet 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#include <net/inet_ecn.h> 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic void codel_params_init(struct codel_params *params) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci params->interval = MS2TIME(100); 5762306a36Sopenharmony_ci params->target = MS2TIME(5); 5862306a36Sopenharmony_ci params->ce_threshold = CODEL_DISABLED_THRESHOLD; 5962306a36Sopenharmony_ci params->ce_threshold_mask = 0; 6062306a36Sopenharmony_ci params->ce_threshold_selector = 0; 6162306a36Sopenharmony_ci params->ecn = false; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic void codel_vars_init(struct codel_vars *vars) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci memset(vars, 0, sizeof(*vars)); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic void codel_stats_init(struct codel_stats *stats) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci stats->maxpacket = 0; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* 7562306a36Sopenharmony_ci * http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Iterative_methods_for_reciprocal_square_roots 7662306a36Sopenharmony_ci * new_invsqrt = (invsqrt / 2) * (3 - count * invsqrt^2) 7762306a36Sopenharmony_ci * 7862306a36Sopenharmony_ci * Here, invsqrt is a fixed point number (< 1.0), 32bit mantissa, aka Q0.32 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_cistatic void codel_Newton_step(struct codel_vars *vars) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci u32 invsqrt = ((u32)vars->rec_inv_sqrt) << REC_INV_SQRT_SHIFT; 8362306a36Sopenharmony_ci u32 invsqrt2 = ((u64)invsqrt * invsqrt) >> 32; 8462306a36Sopenharmony_ci u64 val = (3LL << 32) - ((u64)vars->count * invsqrt2); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci val >>= 2; /* avoid overflow in following multiply */ 8762306a36Sopenharmony_ci val = (val * invsqrt) >> (32 - 2 + 1); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci vars->rec_inv_sqrt = val >> REC_INV_SQRT_SHIFT; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* 9362306a36Sopenharmony_ci * CoDel control_law is t + interval/sqrt(count) 9462306a36Sopenharmony_ci * We maintain in rec_inv_sqrt the reciprocal value of sqrt(count) to avoid 9562306a36Sopenharmony_ci * both sqrt() and divide operation. 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_cistatic codel_time_t codel_control_law(codel_time_t t, 9862306a36Sopenharmony_ci codel_time_t interval, 9962306a36Sopenharmony_ci u32 rec_inv_sqrt) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci return t + reciprocal_scale(interval, rec_inv_sqrt << REC_INV_SQRT_SHIFT); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic bool codel_should_drop(const struct sk_buff *skb, 10562306a36Sopenharmony_ci void *ctx, 10662306a36Sopenharmony_ci struct codel_vars *vars, 10762306a36Sopenharmony_ci struct codel_params *params, 10862306a36Sopenharmony_ci struct codel_stats *stats, 10962306a36Sopenharmony_ci codel_skb_len_t skb_len_func, 11062306a36Sopenharmony_ci codel_skb_time_t skb_time_func, 11162306a36Sopenharmony_ci u32 *backlog, 11262306a36Sopenharmony_ci codel_time_t now) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci bool ok_to_drop; 11562306a36Sopenharmony_ci u32 skb_len; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (!skb) { 11862306a36Sopenharmony_ci vars->first_above_time = 0; 11962306a36Sopenharmony_ci return false; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci skb_len = skb_len_func(skb); 12362306a36Sopenharmony_ci vars->ldelay = now - skb_time_func(skb); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (unlikely(skb_len > stats->maxpacket)) 12662306a36Sopenharmony_ci stats->maxpacket = skb_len; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (codel_time_before(vars->ldelay, params->target) || 12962306a36Sopenharmony_ci *backlog <= params->mtu) { 13062306a36Sopenharmony_ci /* went below - stay below for at least interval */ 13162306a36Sopenharmony_ci vars->first_above_time = 0; 13262306a36Sopenharmony_ci return false; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci ok_to_drop = false; 13562306a36Sopenharmony_ci if (vars->first_above_time == 0) { 13662306a36Sopenharmony_ci /* just went above from below. If we stay above 13762306a36Sopenharmony_ci * for at least interval we'll say it's ok to drop 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_ci vars->first_above_time = now + params->interval; 14062306a36Sopenharmony_ci } else if (codel_time_after(now, vars->first_above_time)) { 14162306a36Sopenharmony_ci ok_to_drop = true; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci return ok_to_drop; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic struct sk_buff *codel_dequeue(void *ctx, 14762306a36Sopenharmony_ci u32 *backlog, 14862306a36Sopenharmony_ci struct codel_params *params, 14962306a36Sopenharmony_ci struct codel_vars *vars, 15062306a36Sopenharmony_ci struct codel_stats *stats, 15162306a36Sopenharmony_ci codel_skb_len_t skb_len_func, 15262306a36Sopenharmony_ci codel_skb_time_t skb_time_func, 15362306a36Sopenharmony_ci codel_skb_drop_t drop_func, 15462306a36Sopenharmony_ci codel_skb_dequeue_t dequeue_func) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct sk_buff *skb = dequeue_func(vars, ctx); 15762306a36Sopenharmony_ci codel_time_t now; 15862306a36Sopenharmony_ci bool drop; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (!skb) { 16162306a36Sopenharmony_ci vars->dropping = false; 16262306a36Sopenharmony_ci return skb; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci now = codel_get_time(); 16562306a36Sopenharmony_ci drop = codel_should_drop(skb, ctx, vars, params, stats, 16662306a36Sopenharmony_ci skb_len_func, skb_time_func, backlog, now); 16762306a36Sopenharmony_ci if (vars->dropping) { 16862306a36Sopenharmony_ci if (!drop) { 16962306a36Sopenharmony_ci /* sojourn time below target - leave dropping state */ 17062306a36Sopenharmony_ci vars->dropping = false; 17162306a36Sopenharmony_ci } else if (codel_time_after_eq(now, vars->drop_next)) { 17262306a36Sopenharmony_ci /* It's time for the next drop. Drop the current 17362306a36Sopenharmony_ci * packet and dequeue the next. The dequeue might 17462306a36Sopenharmony_ci * take us out of dropping state. 17562306a36Sopenharmony_ci * If not, schedule the next drop. 17662306a36Sopenharmony_ci * A large backlog might result in drop rates so high 17762306a36Sopenharmony_ci * that the next drop should happen now, 17862306a36Sopenharmony_ci * hence the while loop. 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_ci while (vars->dropping && 18162306a36Sopenharmony_ci codel_time_after_eq(now, vars->drop_next)) { 18262306a36Sopenharmony_ci vars->count++; /* dont care of possible wrap 18362306a36Sopenharmony_ci * since there is no more divide 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_ci codel_Newton_step(vars); 18662306a36Sopenharmony_ci if (params->ecn && INET_ECN_set_ce(skb)) { 18762306a36Sopenharmony_ci stats->ecn_mark++; 18862306a36Sopenharmony_ci vars->drop_next = 18962306a36Sopenharmony_ci codel_control_law(vars->drop_next, 19062306a36Sopenharmony_ci params->interval, 19162306a36Sopenharmony_ci vars->rec_inv_sqrt); 19262306a36Sopenharmony_ci goto end; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci stats->drop_len += skb_len_func(skb); 19562306a36Sopenharmony_ci drop_func(skb, ctx); 19662306a36Sopenharmony_ci stats->drop_count++; 19762306a36Sopenharmony_ci skb = dequeue_func(vars, ctx); 19862306a36Sopenharmony_ci if (!codel_should_drop(skb, ctx, 19962306a36Sopenharmony_ci vars, params, stats, 20062306a36Sopenharmony_ci skb_len_func, 20162306a36Sopenharmony_ci skb_time_func, 20262306a36Sopenharmony_ci backlog, now)) { 20362306a36Sopenharmony_ci /* leave dropping state */ 20462306a36Sopenharmony_ci vars->dropping = false; 20562306a36Sopenharmony_ci } else { 20662306a36Sopenharmony_ci /* and schedule the next drop */ 20762306a36Sopenharmony_ci vars->drop_next = 20862306a36Sopenharmony_ci codel_control_law(vars->drop_next, 20962306a36Sopenharmony_ci params->interval, 21062306a36Sopenharmony_ci vars->rec_inv_sqrt); 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci } else if (drop) { 21562306a36Sopenharmony_ci u32 delta; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (params->ecn && INET_ECN_set_ce(skb)) { 21862306a36Sopenharmony_ci stats->ecn_mark++; 21962306a36Sopenharmony_ci } else { 22062306a36Sopenharmony_ci stats->drop_len += skb_len_func(skb); 22162306a36Sopenharmony_ci drop_func(skb, ctx); 22262306a36Sopenharmony_ci stats->drop_count++; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci skb = dequeue_func(vars, ctx); 22562306a36Sopenharmony_ci drop = codel_should_drop(skb, ctx, vars, params, 22662306a36Sopenharmony_ci stats, skb_len_func, 22762306a36Sopenharmony_ci skb_time_func, backlog, now); 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci vars->dropping = true; 23062306a36Sopenharmony_ci /* if min went above target close to when we last went below it 23162306a36Sopenharmony_ci * assume that the drop rate that controlled the queue on the 23262306a36Sopenharmony_ci * last cycle is a good starting point to control it now. 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_ci delta = vars->count - vars->lastcount; 23562306a36Sopenharmony_ci if (delta > 1 && 23662306a36Sopenharmony_ci codel_time_before(now - vars->drop_next, 23762306a36Sopenharmony_ci 16 * params->interval)) { 23862306a36Sopenharmony_ci vars->count = delta; 23962306a36Sopenharmony_ci /* we dont care if rec_inv_sqrt approximation 24062306a36Sopenharmony_ci * is not very precise : 24162306a36Sopenharmony_ci * Next Newton steps will correct it quadratically. 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_ci codel_Newton_step(vars); 24462306a36Sopenharmony_ci } else { 24562306a36Sopenharmony_ci vars->count = 1; 24662306a36Sopenharmony_ci vars->rec_inv_sqrt = ~0U >> REC_INV_SQRT_SHIFT; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci vars->lastcount = vars->count; 24962306a36Sopenharmony_ci vars->drop_next = codel_control_law(now, params->interval, 25062306a36Sopenharmony_ci vars->rec_inv_sqrt); 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ciend: 25362306a36Sopenharmony_ci if (skb && codel_time_after(vars->ldelay, params->ce_threshold)) { 25462306a36Sopenharmony_ci bool set_ce = true; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (params->ce_threshold_mask) { 25762306a36Sopenharmony_ci int dsfield = skb_get_dsfield(skb); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci set_ce = (dsfield >= 0 && 26062306a36Sopenharmony_ci (((u8)dsfield & params->ce_threshold_mask) == 26162306a36Sopenharmony_ci params->ce_threshold_selector)); 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci if (set_ce && INET_ECN_set_ce(skb)) 26462306a36Sopenharmony_ci stats->ce_mark++; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci return skb; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci#endif 270