Commit 0a9fe5c3 authored by Yousuk Seung's avatar Yousuk Seung Committed by David S. Miller

netem: slotting with non-uniform distribution

Extend slotting with support for non-uniform distributions. This is
similar to netem's non-uniform distribution delay feature.

Commit f043efeae2f1 ("netem: support delivering packets in delayed
time slots") added the slotting feature to approximate the behaviors
of media with packet aggregation but only supported a uniform
distribution for delays between transmission attempts. Tests with TCP
BBR with emulated wifi links with non-uniform distributions produced
more useful results.

Syntax:
   slot dist DISTRIBUTION DELAY JITTER [packets MAX_PACKETS] \
      [bytes MAX_BYTES]

The syntax and use of the distribution table is the same as in the
non-uniform distribution delay feature. A file DISTRIBUTION must be
present in TC_LIB_DIR (e.g. /usr/lib/tc) containing numbers scaled by
NETEM_DIST_SCALE. A random value x is selected from the table and it
takes DELAY + ( x * JITTER ) as delay. Correlation between values is not
supported.

Examples:
  Normal distribution delay with mean = 800us and stdev = 100us.
  > tc qdisc add dev eth0 root netem slot dist normal 800us 100us

  Optionally set the max slot size in bytes and/or packets.
  > tc qdisc add dev eth0 root netem slot dist normal 800us 100us \
    bytes 64k packets 42
Signed-off-by: default avatarYousuk Seung <ysseung@google.com>
Acked-by: default avatarEric Dumazet <edumazet@google.com>
Acked-by: default avatarNeal Cardwell <ncardwell@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7861552c
...@@ -539,6 +539,7 @@ enum { ...@@ -539,6 +539,7 @@ enum {
TCA_NETEM_LATENCY64, TCA_NETEM_LATENCY64,
TCA_NETEM_JITTER64, TCA_NETEM_JITTER64,
TCA_NETEM_SLOT, TCA_NETEM_SLOT,
TCA_NETEM_SLOT_DIST,
__TCA_NETEM_MAX, __TCA_NETEM_MAX,
}; };
...@@ -581,6 +582,8 @@ struct tc_netem_slot { ...@@ -581,6 +582,8 @@ struct tc_netem_slot {
__s64 max_delay; __s64 max_delay;
__s32 max_packets; __s32 max_packets;
__s32 max_bytes; __s32 max_bytes;
__s64 dist_delay; /* nsec */
__s64 dist_jitter; /* nsec */
}; };
enum { enum {
......
...@@ -68,6 +68,11 @@ ...@@ -68,6 +68,11 @@
Fabio Ludovici <fabio.ludovici at yahoo.it> Fabio Ludovici <fabio.ludovici at yahoo.it>
*/ */
struct disttable {
u32 size;
s16 table[0];
};
struct netem_sched_data { struct netem_sched_data {
/* internal t(ime)fifo qdisc uses t_root and sch->limit */ /* internal t(ime)fifo qdisc uses t_root and sch->limit */
struct rb_root t_root; struct rb_root t_root;
...@@ -99,10 +104,7 @@ struct netem_sched_data { ...@@ -99,10 +104,7 @@ struct netem_sched_data {
u32 rho; u32 rho;
} delay_cor, loss_cor, dup_cor, reorder_cor, corrupt_cor; } delay_cor, loss_cor, dup_cor, reorder_cor, corrupt_cor;
struct disttable { struct disttable *delay_dist;
u32 size;
s16 table[0];
} *delay_dist;
enum { enum {
CLG_RANDOM, CLG_RANDOM,
...@@ -142,6 +144,7 @@ struct netem_sched_data { ...@@ -142,6 +144,7 @@ struct netem_sched_data {
s32 bytes_left; s32 bytes_left;
} slot; } slot;
struct disttable *slot_dist;
}; };
/* Time stamp put into socket buffer control block /* Time stamp put into socket buffer control block
...@@ -180,7 +183,7 @@ static u32 get_crandom(struct crndstate *state) ...@@ -180,7 +183,7 @@ static u32 get_crandom(struct crndstate *state)
u64 value, rho; u64 value, rho;
unsigned long answer; unsigned long answer;
if (state->rho == 0) /* no correlation */ if (!state || state->rho == 0) /* no correlation */
return prandom_u32(); return prandom_u32();
value = prandom_u32(); value = prandom_u32();
...@@ -601,10 +604,19 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, ...@@ -601,10 +604,19 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
static void get_slot_next(struct netem_sched_data *q, u64 now) static void get_slot_next(struct netem_sched_data *q, u64 now)
{ {
q->slot.slot_next = now + q->slot_config.min_delay + s64 next_delay;
if (!q->slot_dist)
next_delay = q->slot_config.min_delay +
(prandom_u32() * (prandom_u32() *
(q->slot_config.max_delay - (q->slot_config.max_delay -
q->slot_config.min_delay) >> 32); q->slot_config.min_delay) >> 32);
else
next_delay = tabledist(q->slot_config.dist_delay,
(s32)(q->slot_config.dist_jitter),
NULL, q->slot_dist);
q->slot.slot_next = now + next_delay;
q->slot.packets_left = q->slot_config.max_packets; q->slot.packets_left = q->slot_config.max_packets;
q->slot.bytes_left = q->slot_config.max_bytes; q->slot.bytes_left = q->slot_config.max_bytes;
} }
...@@ -721,9 +733,9 @@ static void dist_free(struct disttable *d) ...@@ -721,9 +733,9 @@ static void dist_free(struct disttable *d)
* signed 16 bit values. * signed 16 bit values.
*/ */
static int get_dist_table(struct Qdisc *sch, const struct nlattr *attr) static int get_dist_table(struct Qdisc *sch, struct disttable **tbl,
const struct nlattr *attr)
{ {
struct netem_sched_data *q = qdisc_priv(sch);
size_t n = nla_len(attr)/sizeof(__s16); size_t n = nla_len(attr)/sizeof(__s16);
const __s16 *data = nla_data(attr); const __s16 *data = nla_data(attr);
spinlock_t *root_lock; spinlock_t *root_lock;
...@@ -744,7 +756,7 @@ static int get_dist_table(struct Qdisc *sch, const struct nlattr *attr) ...@@ -744,7 +756,7 @@ static int get_dist_table(struct Qdisc *sch, const struct nlattr *attr)
root_lock = qdisc_root_sleeping_lock(sch); root_lock = qdisc_root_sleeping_lock(sch);
spin_lock_bh(root_lock); spin_lock_bh(root_lock);
swap(q->delay_dist, d); swap(*tbl, d);
spin_unlock_bh(root_lock); spin_unlock_bh(root_lock);
dist_free(d); dist_free(d);
...@@ -762,7 +774,8 @@ static void get_slot(struct netem_sched_data *q, const struct nlattr *attr) ...@@ -762,7 +774,8 @@ static void get_slot(struct netem_sched_data *q, const struct nlattr *attr)
q->slot_config.max_bytes = INT_MAX; q->slot_config.max_bytes = INT_MAX;
q->slot.packets_left = q->slot_config.max_packets; q->slot.packets_left = q->slot_config.max_packets;
q->slot.bytes_left = q->slot_config.max_bytes; q->slot.bytes_left = q->slot_config.max_bytes;
if (q->slot_config.min_delay | q->slot_config.max_delay) if (q->slot_config.min_delay | q->slot_config.max_delay |
q->slot_config.dist_jitter)
q->slot.slot_next = ktime_get_ns(); q->slot.slot_next = ktime_get_ns();
else else
q->slot.slot_next = 0; q->slot.slot_next = 0;
...@@ -926,16 +939,17 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt, ...@@ -926,16 +939,17 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt,
} }
if (tb[TCA_NETEM_DELAY_DIST]) { if (tb[TCA_NETEM_DELAY_DIST]) {
ret = get_dist_table(sch, tb[TCA_NETEM_DELAY_DIST]); ret = get_dist_table(sch, &q->delay_dist,
if (ret) { tb[TCA_NETEM_DELAY_DIST]);
/* recover clg and loss_model, in case of if (ret)
* q->clg and q->loss_model were modified goto get_table_failure;
* in get_loss_clg()
*/
q->clg = old_clg;
q->loss_model = old_loss_model;
return ret;
} }
if (tb[TCA_NETEM_SLOT_DIST]) {
ret = get_dist_table(sch, &q->slot_dist,
tb[TCA_NETEM_SLOT_DIST]);
if (ret)
goto get_table_failure;
} }
sch->limit = qopt->limit; sch->limit = qopt->limit;
...@@ -983,6 +997,15 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt, ...@@ -983,6 +997,15 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt,
get_slot(q, tb[TCA_NETEM_SLOT]); get_slot(q, tb[TCA_NETEM_SLOT]);
return ret; return ret;
get_table_failure:
/* recover clg and loss_model, in case of
* q->clg and q->loss_model were modified
* in get_loss_clg()
*/
q->clg = old_clg;
q->loss_model = old_loss_model;
return ret;
} }
static int netem_init(struct Qdisc *sch, struct nlattr *opt, static int netem_init(struct Qdisc *sch, struct nlattr *opt,
...@@ -1011,6 +1034,7 @@ static void netem_destroy(struct Qdisc *sch) ...@@ -1011,6 +1034,7 @@ static void netem_destroy(struct Qdisc *sch)
if (q->qdisc) if (q->qdisc)
qdisc_destroy(q->qdisc); qdisc_destroy(q->qdisc);
dist_free(q->delay_dist); dist_free(q->delay_dist);
dist_free(q->slot_dist);
} }
static int dump_loss_model(const struct netem_sched_data *q, static int dump_loss_model(const struct netem_sched_data *q,
...@@ -1127,7 +1151,8 @@ static int netem_dump(struct Qdisc *sch, struct sk_buff *skb) ...@@ -1127,7 +1151,8 @@ static int netem_dump(struct Qdisc *sch, struct sk_buff *skb)
if (dump_loss_model(q, skb) != 0) if (dump_loss_model(q, skb) != 0)
goto nla_put_failure; goto nla_put_failure;
if (q->slot_config.min_delay | q->slot_config.max_delay) { if (q->slot_config.min_delay | q->slot_config.max_delay |
q->slot_config.dist_jitter) {
slot = q->slot_config; slot = q->slot_config;
if (slot.max_packets == INT_MAX) if (slot.max_packets == INT_MAX)
slot.max_packets = 0; slot.max_packets = 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment