Skip to content

Commit

Permalink
Retire usage of cobalt.c and merge cobalt functionality elsewhere
Browse files Browse the repository at this point in the history
Not acceptible to mainline to inc the .c file
  • Loading branch information
dtaht committed Nov 21, 2017
1 parent bf68b6e commit 3fb9914
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 14 deletions.
2 changes: 1 addition & 1 deletion Makefile
Expand Up @@ -13,4 +13,4 @@ install:
[ "$(KERNEL_VERSION)" != `uname -r` ] || modprobe sch_cake

clean:
rm -rf Module.markers modules.order Module.symvers sch_cake.ko sch_cake.mod.c sch_cake.mod.o sch_cake.o cobalt.o cobalt.mod.o cobalt.ko
rm -rf Module.markers modules.order Module.symvers sch_cake.ko sch_cake.mod.c sch_cake.mod.o sch_cake.o
42 changes: 32 additions & 10 deletions cobalt.h
Expand Up @@ -6,8 +6,8 @@
* Copyright (C) 2011-2012 Kathleen Nichols <nichols@pollere.com>
* Copyright (C) 2011-2012 Van Jacobson <van@pollere.net>
* Copyright (C) 2012 Eric Dumazet <edumazet@google.com>
* Copyright (C) 2016 Michael D. Täht <dave.taht@gmail.com>
* Copyright (c) 2015-2016 Jonathan Morton <chromatix99@gmail.com>
* Copyright (C) 2016-2017 Michael D. Täht <dave.taht@gmail.com>
* Copyright (c) 2015-2017 Jonathan Morton <chromatix99@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
Expand Down Expand Up @@ -61,6 +61,16 @@ typedef s64 cobalt_tdiff_t;
#include "codel5_compat.h"
#endif

/* COBALT operates the Codel and BLUE algorithms in parallel, in order to
* obtain the best features of each. Codel is excellent on flows which
* respond to congestion signals in a TCP-like way. BLUE is more effective on
* unresponsive flows.
*/

struct cobalt_skb_cb {
cobalt_time_t enqueue_time;
};

static inline cobalt_time_t cobalt_get_time(void)
{
return ktime_get_ns();
Expand All @@ -72,9 +82,22 @@ static inline u32 cobalt_time_to_us(cobalt_time_t val)
return (u32)val;
}

struct cobalt_skb_cb {
cobalt_time_t enqueue_time;
};
static inline struct cobalt_skb_cb *get_cobalt_cb(const struct sk_buff *skb)
{
qdisc_cb_private_validate(skb, sizeof(struct cobalt_skb_cb));
return (struct cobalt_skb_cb *)qdisc_skb_cb(skb)->data;
}

static inline cobalt_time_t cobalt_get_enqueue_time(const struct sk_buff *skb)
{
return get_cobalt_cb(skb)->enqueue_time;
}

static inline void cobalt_set_enqueue_time(struct sk_buff *skb,
cobalt_time_t now)
{
get_cobalt_cb(skb)->enqueue_time = now;
}

/**
* struct cobalt_params - contains codel and blue parameters
Expand All @@ -90,15 +113,14 @@ struct cobalt_params {
u32 p_dec;
};

/**
* struct cobalt_vars - contains codel and blue variables
* @count: dropping frequency
/* struct cobalt_vars - contains codel and blue variables
* @count: codel dropping frequency
* @rec_inv_sqrt: reciprocal value of sqrt(count) >> 1
* @drop_next: time to drop next packet, or when we dropped last
* @drop_count: temp count of dropped packets in dequeue()
* @ecn_mark: number of packets we ECN marked instead of dropping
* @blue_timer: Blue time to next drop
* @p_drop: BLUE drop probability (0.32 fxp)
* @dropping: set if in dropping state
* @ecn_marked: set if marked
*/
struct cobalt_vars {
u32 count;
Expand Down
193 changes: 190 additions & 3 deletions sch_cake.c
Expand Up @@ -62,7 +62,7 @@
#else
#include <net/flow_dissector.h>
#endif
#include "cobalt.c"
#include "cobalt.h"

#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
#include <net/netfilter/nf_conntrack_core.h>
Expand Down Expand Up @@ -351,11 +351,198 @@ static const u8 besteffort[] = {0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
};

/* tin priority order for stats dumping only, for backwards */
/* compatibility with tc */
/* tin priority order for stats dumping */

static const u8 normal_order[] = {0, 1, 2, 3, 4, 5, 6, 7};
static const u8 bulk_order[] = {1, 0, 2, 3};

#define REC_INV_SQRT_CACHE (16)
static u32 cobalt_rec_inv_sqrt_cache[REC_INV_SQRT_CACHE] = {0};

/* http://en.wikipedia.org/wiki/Methods_of_computing_square_roots
* new_invsqrt = (invsqrt / 2) * (3 - count * invsqrt^2)
*
* Here, invsqrt is a fixed point number (< 1.0), 32bit mantissa, aka Q0.32
*/
static void cobalt_newton_step(struct cobalt_vars *vars)
{
u32 invsqrt = vars->rec_inv_sqrt;
u32 invsqrt2 = ((u64)invsqrt * invsqrt) >> 32;
u64 val = (3LL << 32) - ((u64)vars->count * invsqrt2);

val >>= 2; /* avoid overflow in following multiply */
val = (val * invsqrt) >> (32 - 2 + 1);

vars->rec_inv_sqrt = val;
}

static void cobalt_invsqrt(struct cobalt_vars *vars)
{
if (vars->count < REC_INV_SQRT_CACHE)
vars->rec_inv_sqrt = cobalt_rec_inv_sqrt_cache[vars->count];
else
cobalt_newton_step(vars);
}

static void cobalt_cache_init(void)
{
struct cobalt_vars v;

memset(&v, 0, sizeof(v));
v.rec_inv_sqrt = ~0U;
cobalt_rec_inv_sqrt_cache[0] = v.rec_inv_sqrt;

for (v.count = 1; v.count < REC_INV_SQRT_CACHE; v.count++) {
cobalt_newton_step(&v);
cobalt_newton_step(&v);
cobalt_newton_step(&v);
cobalt_newton_step(&v);

cobalt_rec_inv_sqrt_cache[v.count] = v.rec_inv_sqrt;
}
}

void cobalt_vars_init(struct cobalt_vars *vars)
{
memset(vars, 0, sizeof(*vars));

if (!cobalt_rec_inv_sqrt_cache[0]) {
cobalt_cache_init();
cobalt_rec_inv_sqrt_cache[0] = ~0;
}
}

/* 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 cobalt_time_t cobalt_control_law(cobalt_time_t t,
cobalt_time_t interval,
u32 rec_inv_sqrt)
{
return t + reciprocal_scale(interval, rec_inv_sqrt);
}

/* Call this when a packet had to be dropped due to queue overflow. Returns
* true if the BLUE state was quiescent before but active after this call.
*/
bool cobalt_queue_full(struct cobalt_vars *vars, struct cobalt_params *p,
cobalt_time_t now)
{
bool up = false;

if ((now - vars->blue_timer) > p->target) {
up = !vars->p_drop;
vars->p_drop += p->p_inc;
if (vars->p_drop < p->p_inc)
vars->p_drop = ~0;
vars->blue_timer = now;
}
vars->dropping = true;
vars->drop_next = now;
if (!vars->count)
vars->count = 1;

return up;
}

/* Call this when the queue was serviced but turned out to be empty. Returns
* true if the BLUE state was active before but quiescent after this call.
*/
bool cobalt_queue_empty(struct cobalt_vars *vars, struct cobalt_params *p,
cobalt_time_t now)
{
bool down = false;

if (vars->p_drop && (now - vars->blue_timer) > p->target) {
if (vars->p_drop < p->p_dec)
vars->p_drop = 0;
else
vars->p_drop -= p->p_dec;
vars->blue_timer = now;
down = !vars->p_drop;
}
vars->dropping = false;

if (vars->count && (now - vars->drop_next) >= 0) {
vars->count--;
cobalt_invsqrt(vars);
vars->drop_next = cobalt_control_law(vars->drop_next,
p->interval,
vars->rec_inv_sqrt);
}

return down;
}

/* Call this with a freshly dequeued packet for possible congestion marking.
* Returns true as an instruction to drop the packet, false for delivery.
*/
bool cobalt_should_drop(struct cobalt_vars *vars,
struct cobalt_params *p,
cobalt_time_t now,
struct sk_buff *skb)
{
bool drop = false;

/* Simplified Codel implementation */
cobalt_tdiff_t sojourn = now - cobalt_get_enqueue_time(skb);
cobalt_tdiff_t schedule = now - vars->drop_next;
bool over_target = sojourn > p->target;
bool next_due = vars->count && schedule >= 0;

vars->ecn_marked = false;

if (over_target) {
if (!vars->dropping) {
vars->dropping = true;
vars->drop_next = cobalt_control_law(now,
p->interval,
vars->rec_inv_sqrt);
}
if (!vars->count)
vars->count = 1;
} else if (vars->dropping) {
vars->dropping = false;
}

if (next_due && vars->dropping) {
/* Use ECN mark if possible, otherwise drop */
drop = !(vars->ecn_marked = INET_ECN_set_ce(skb));

vars->count++;
if (!vars->count)
vars->count--;
cobalt_invsqrt(vars);
vars->drop_next = cobalt_control_law(vars->drop_next,
p->interval,
vars->rec_inv_sqrt);
schedule = now - vars->drop_next;
} else {
while (next_due) {
vars->count--;
cobalt_invsqrt(vars);
vars->drop_next = cobalt_control_law(vars->drop_next,
p->interval,
vars->rec_inv_sqrt);
schedule = now - vars->drop_next;
next_due = vars->count && schedule >= 0;
}
}

/* Simple BLUE implementation. Lack of ECN is deliberate. */
if (vars->p_drop)
drop |= (prandom_u32() < vars->p_drop);

/* Overload the drop_next field as an activity timeout */
if (!vars->count)
vars->drop_next = now + p->interval;
else if (schedule > 0 && !drop)
vars->drop_next = now;

return drop;
}

#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)

#if KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE
Expand Down

0 comments on commit 3fb9914

Please sign in to comment.