Skip to content

Commit

Permalink
Merge branch 'netem-add-nsec-scheduling-and-slot-feature'
Browse files Browse the repository at this point in the history
Dave Taht says:

====================
netem: add nsec scheduling and slot feature

This patch series converts netem away from the old "ticks" interface and
userspace API, and adds support for a new "slot" feature intended to
emulate bursty macs such as WiFi and LTE better.

Changes since v2:
Use u64 for packet_len_sched_time()
Use simpler max(time_to_send,q->slot.slot_next)

Changes since v1:
Always pass new nanosecond APIs to userspace
====================

Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
davem330 committed Nov 13, 2017
2 parents 338d182 + 836af83 commit 907a442
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 29 deletions.
10 changes: 10 additions & 0 deletions include/uapi/linux/pkt_sched.h
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,9 @@ enum {
TCA_NETEM_ECN,
TCA_NETEM_RATE64,
TCA_NETEM_PAD,
TCA_NETEM_LATENCY64,
TCA_NETEM_JITTER64,
TCA_NETEM_SLOT,
__TCA_NETEM_MAX,
};

Expand Down Expand Up @@ -574,6 +577,13 @@ struct tc_netem_rate {
__s32 cell_overhead;
};

struct tc_netem_slot {
__s64 min_delay; /* nsec */
__s64 max_delay;
__s32 max_packets;
__s32 max_bytes;
};

enum {
NETEM_LOSS_UNSPEC,
NETEM_LOSS_GI, /* General Intuitive - 4 state model */
Expand Down
140 changes: 111 additions & 29 deletions net/sched/sch_netem.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ struct netem_sched_data {

struct qdisc_watchdog watchdog;

psched_tdiff_t latency;
psched_tdiff_t jitter;
s64 latency;
s64 jitter;

u32 loss;
u32 ecn;
Expand Down Expand Up @@ -135,6 +135,13 @@ struct netem_sched_data {
u32 a5; /* p23 used only in 4-states */
} clg;

struct tc_netem_slot slot_config;
struct slotstate {
u64 slot_next;
s32 packets_left;
s32 bytes_left;
} slot;

};

/* Time stamp put into socket buffer control block
Expand All @@ -145,7 +152,7 @@ struct netem_sched_data {
* we save skb->tstamp value in skb->cb[] before destroying it.
*/
struct netem_skb_cb {
psched_time_t time_to_send;
u64 time_to_send;
};

static inline struct netem_skb_cb *netem_skb_cb(struct sk_buff *skb)
Expand Down Expand Up @@ -305,11 +312,11 @@ static bool loss_event(struct netem_sched_data *q)
* std deviation sigma. Uses table lookup to approximate the desired
* distribution, and a uniformly-distributed pseudo-random source.
*/
static psched_tdiff_t tabledist(psched_tdiff_t mu, psched_tdiff_t sigma,
struct crndstate *state,
const struct disttable *dist)
static s64 tabledist(s64 mu, s64 sigma,
struct crndstate *state,
const struct disttable *dist)
{
psched_tdiff_t x;
s64 x;
long t;
u32 rnd;

Expand All @@ -332,10 +339,10 @@ static psched_tdiff_t tabledist(psched_tdiff_t mu, psched_tdiff_t sigma,
return x / NETEM_DIST_SCALE + (sigma / NETEM_DIST_SCALE) * t + mu;
}

static psched_time_t packet_len_2_sched_time(unsigned int len, struct netem_sched_data *q)
static u64 packet_len_2_sched_time(unsigned int len,
struct netem_sched_data *q)
{
u64 ticks;

u64 offset;
len += q->packet_overhead;

if (q->cell_size) {
Expand All @@ -345,11 +352,9 @@ static psched_time_t packet_len_2_sched_time(unsigned int len, struct netem_sche
cells++;
len = cells * (q->cell_size + q->cell_overhead);
}

ticks = (u64)len * NSEC_PER_SEC;

do_div(ticks, q->rate);
return PSCHED_NS2TICKS(ticks);
offset = (u64)len * NSEC_PER_SEC;
do_div(offset, q->rate);
return offset;
}

static void tfifo_reset(struct Qdisc *sch)
Expand All @@ -369,7 +374,7 @@ static void tfifo_reset(struct Qdisc *sch)
static void tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
{
struct netem_sched_data *q = qdisc_priv(sch);
psched_time_t tnext = netem_skb_cb(nskb)->time_to_send;
u64 tnext = netem_skb_cb(nskb)->time_to_send;
struct rb_node **p = &q->t_root.rb_node, *parent = NULL;

while (*p) {
Expand Down Expand Up @@ -515,13 +520,13 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
if (q->gap == 0 || /* not doing reordering */
q->counter < q->gap - 1 || /* inside last reordering gap */
q->reorder < get_crandom(&q->reorder_cor)) {
psched_time_t now;
psched_tdiff_t delay;
u64 now;
s64 delay;

delay = tabledist(q->latency, q->jitter,
&q->delay_cor, q->delay_dist);

now = psched_get_time();
now = ktime_get_ns();

if (q->rate) {
struct netem_skb_cb *last = NULL;
Expand All @@ -547,7 +552,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
* from delay.
*/
delay -= last->time_to_send - now;
delay = max_t(psched_tdiff_t, 0, delay);
delay = max_t(s64, 0, delay);
now = last->time_to_send;
}

Expand All @@ -562,7 +567,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
* Do re-ordering by putting one out of N packets at the front
* of the queue.
*/
cb->time_to_send = psched_get_time();
cb->time_to_send = ktime_get_ns();
q->counter = 0;

netem_enqueue_skb_head(&sch->q, skb);
Expand Down Expand Up @@ -593,6 +598,20 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
return NET_XMIT_SUCCESS;
}

/* Delay the next round with a new future slot with a
* correct number of bytes and packets.
*/

static void get_slot_next(struct netem_sched_data *q, u64 now)
{
q->slot.slot_next = now + q->slot_config.min_delay +
(prandom_u32() *
(q->slot_config.max_delay -
q->slot_config.min_delay) >> 32);
q->slot.packets_left = q->slot_config.max_packets;
q->slot.bytes_left = q->slot_config.max_bytes;
}

static struct sk_buff *netem_dequeue(struct Qdisc *sch)
{
struct netem_sched_data *q = qdisc_priv(sch);
Expand All @@ -609,15 +628,18 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch)
}
p = rb_first(&q->t_root);
if (p) {
psched_time_t time_to_send;
u64 time_to_send;
u64 now = ktime_get_ns();

skb = rb_to_skb(p);

/* if more time remaining? */
time_to_send = netem_skb_cb(skb)->time_to_send;
if (time_to_send <= psched_get_time()) {
rb_erase(p, &q->t_root);
if (q->slot.slot_next && q->slot.slot_next < time_to_send)
get_slot_next(q, now);

if (time_to_send <= now && q->slot.slot_next <= now) {
rb_erase(p, &q->t_root);
sch->q.qlen--;
qdisc_qstats_backlog_dec(sch, skb);
skb->next = NULL;
Expand All @@ -636,6 +658,14 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch)
skb->tstamp = 0;
#endif

if (q->slot.slot_next) {
q->slot.packets_left--;
q->slot.bytes_left -= qdisc_pkt_len(skb);
if (q->slot.packets_left <= 0 ||
q->slot.bytes_left <= 0)
get_slot_next(q, now);
}

if (q->qdisc) {
unsigned int pkt_len = qdisc_pkt_len(skb);
struct sk_buff *to_free = NULL;
Expand All @@ -659,7 +689,10 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch)
if (skb)
goto deliver;
}
qdisc_watchdog_schedule(&q->watchdog, time_to_send);

qdisc_watchdog_schedule_ns(&q->watchdog,
max(time_to_send,
q->slot.slot_next));
}

if (q->qdisc) {
Expand Down Expand Up @@ -690,6 +723,7 @@ static void dist_free(struct disttable *d)
* Distribution data is a variable size payload containing
* signed 16 bit values.
*/

static int get_dist_table(struct Qdisc *sch, const struct nlattr *attr)
{
struct netem_sched_data *q = qdisc_priv(sch);
Expand Down Expand Up @@ -720,6 +754,23 @@ static int get_dist_table(struct Qdisc *sch, const struct nlattr *attr)
return 0;
}

static void get_slot(struct netem_sched_data *q, const struct nlattr *attr)
{
const struct tc_netem_slot *c = nla_data(attr);

q->slot_config = *c;
if (q->slot_config.max_packets == 0)
q->slot_config.max_packets = INT_MAX;
if (q->slot_config.max_bytes == 0)
q->slot_config.max_bytes = INT_MAX;
q->slot.packets_left = q->slot_config.max_packets;
q->slot.bytes_left = q->slot_config.max_bytes;
if (q->slot_config.min_delay | q->slot_config.max_delay)
q->slot.slot_next = ktime_get_ns();
else
q->slot.slot_next = 0;
}

static void get_correlation(struct netem_sched_data *q, const struct nlattr *attr)
{
const struct tc_netem_corr *c = nla_data(attr);
Expand Down Expand Up @@ -821,6 +872,9 @@ static const struct nla_policy netem_policy[TCA_NETEM_MAX + 1] = {
[TCA_NETEM_LOSS] = { .type = NLA_NESTED },
[TCA_NETEM_ECN] = { .type = NLA_U32 },
[TCA_NETEM_RATE64] = { .type = NLA_U64 },
[TCA_NETEM_LATENCY64] = { .type = NLA_S64 },
[TCA_NETEM_JITTER64] = { .type = NLA_S64 },
[TCA_NETEM_SLOT] = { .len = sizeof(struct tc_netem_slot) },
};

static int parse_attr(struct nlattr *tb[], int maxtype, struct nlattr *nla,
Expand Down Expand Up @@ -888,8 +942,8 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt)

sch->limit = qopt->limit;

q->latency = qopt->latency;
q->jitter = qopt->jitter;
q->latency = PSCHED_TICKS2NS(qopt->latency);
q->jitter = PSCHED_TICKS2NS(qopt->jitter);
q->limit = qopt->limit;
q->gap = qopt->gap;
q->counter = 0;
Expand Down Expand Up @@ -918,9 +972,18 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt)
q->rate = max_t(u64, q->rate,
nla_get_u64(tb[TCA_NETEM_RATE64]));

if (tb[TCA_NETEM_LATENCY64])
q->latency = nla_get_s64(tb[TCA_NETEM_LATENCY64]);

if (tb[TCA_NETEM_JITTER64])
q->jitter = nla_get_s64(tb[TCA_NETEM_JITTER64]);

if (tb[TCA_NETEM_ECN])
q->ecn = nla_get_u32(tb[TCA_NETEM_ECN]);

if (tb[TCA_NETEM_SLOT])
get_slot(q, tb[TCA_NETEM_SLOT]);

return ret;
}

Expand Down Expand Up @@ -1010,16 +1073,25 @@ static int netem_dump(struct Qdisc *sch, struct sk_buff *skb)
struct tc_netem_reorder reorder;
struct tc_netem_corrupt corrupt;
struct tc_netem_rate rate;
struct tc_netem_slot slot;

qopt.latency = q->latency;
qopt.jitter = q->jitter;
qopt.latency = min_t(psched_tdiff_t, PSCHED_NS2TICKS(q->latency),
UINT_MAX);
qopt.jitter = min_t(psched_tdiff_t, PSCHED_NS2TICKS(q->jitter),
UINT_MAX);
qopt.limit = q->limit;
qopt.loss = q->loss;
qopt.gap = q->gap;
qopt.duplicate = q->duplicate;
if (nla_put(skb, TCA_OPTIONS, sizeof(qopt), &qopt))
goto nla_put_failure;

if (nla_put(skb, TCA_NETEM_LATENCY64, sizeof(q->latency), &q->latency))
goto nla_put_failure;

if (nla_put(skb, TCA_NETEM_JITTER64, sizeof(q->jitter), &q->jitter))
goto nla_put_failure;

cor.delay_corr = q->delay_cor.rho;
cor.loss_corr = q->loss_cor.rho;
cor.dup_corr = q->dup_cor.rho;
Expand Down Expand Up @@ -1056,6 +1128,16 @@ static int netem_dump(struct Qdisc *sch, struct sk_buff *skb)
if (dump_loss_model(q, skb) != 0)
goto nla_put_failure;

if (q->slot_config.min_delay | q->slot_config.max_delay) {
slot = q->slot_config;
if (slot.max_packets == INT_MAX)
slot.max_packets = 0;
if (slot.max_bytes == INT_MAX)
slot.max_bytes = 0;
if (nla_put(skb, TCA_NETEM_SLOT, sizeof(slot), &slot))
goto nla_put_failure;
}

return nla_nest_end(skb, nla);

nla_put_failure:
Expand Down

0 comments on commit 907a442

Please sign in to comment.