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