Merge branch 'netem-add-nsec-scheduling-and-slot-feature'

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 <davem@davemloft.net>
This commit is contained in:
David S. Miller 2017-11-13 10:15:47 +09:00
commit 907a4425f7
2 changed files with 121 additions and 29 deletions

View File

@ -537,6 +537,9 @@ enum {
TCA_NETEM_ECN, TCA_NETEM_ECN,
TCA_NETEM_RATE64, TCA_NETEM_RATE64,
TCA_NETEM_PAD, TCA_NETEM_PAD,
TCA_NETEM_LATENCY64,
TCA_NETEM_JITTER64,
TCA_NETEM_SLOT,
__TCA_NETEM_MAX, __TCA_NETEM_MAX,
}; };
@ -574,6 +577,13 @@ struct tc_netem_rate {
__s32 cell_overhead; __s32 cell_overhead;
}; };
struct tc_netem_slot {
__s64 min_delay; /* nsec */
__s64 max_delay;
__s32 max_packets;
__s32 max_bytes;
};
enum { enum {
NETEM_LOSS_UNSPEC, NETEM_LOSS_UNSPEC,
NETEM_LOSS_GI, /* General Intuitive - 4 state model */ NETEM_LOSS_GI, /* General Intuitive - 4 state model */

View File

@ -77,8 +77,8 @@ struct netem_sched_data {
struct qdisc_watchdog watchdog; struct qdisc_watchdog watchdog;
psched_tdiff_t latency; s64 latency;
psched_tdiff_t jitter; s64 jitter;
u32 loss; u32 loss;
u32 ecn; u32 ecn;
@ -135,6 +135,13 @@ struct netem_sched_data {
u32 a5; /* p23 used only in 4-states */ u32 a5; /* p23 used only in 4-states */
} clg; } 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 /* Time stamp put into socket buffer control block
@ -145,7 +152,7 @@ struct netem_sched_data {
* we save skb->tstamp value in skb->cb[] before destroying it. * we save skb->tstamp value in skb->cb[] before destroying it.
*/ */
struct netem_skb_cb { 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) static inline struct netem_skb_cb *netem_skb_cb(struct sk_buff *skb)
@ -305,11 +312,11 @@ static bool loss_event(struct netem_sched_data *q)
* std deviation sigma. Uses table lookup to approximate the desired * std deviation sigma. Uses table lookup to approximate the desired
* distribution, and a uniformly-distributed pseudo-random source. * distribution, and a uniformly-distributed pseudo-random source.
*/ */
static psched_tdiff_t tabledist(psched_tdiff_t mu, psched_tdiff_t sigma, static s64 tabledist(s64 mu, s64 sigma,
struct crndstate *state, struct crndstate *state,
const struct disttable *dist) const struct disttable *dist)
{ {
psched_tdiff_t x; s64 x;
long t; long t;
u32 rnd; u32 rnd;
@ -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; 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; len += q->packet_overhead;
if (q->cell_size) { if (q->cell_size) {
@ -345,11 +352,9 @@ static psched_time_t packet_len_2_sched_time(unsigned int len, struct netem_sche
cells++; cells++;
len = cells * (q->cell_size + q->cell_overhead); len = cells * (q->cell_size + q->cell_overhead);
} }
offset = (u64)len * NSEC_PER_SEC;
ticks = (u64)len * NSEC_PER_SEC; do_div(offset, q->rate);
return offset;
do_div(ticks, q->rate);
return PSCHED_NS2TICKS(ticks);
} }
static void tfifo_reset(struct Qdisc *sch) static void tfifo_reset(struct Qdisc *sch)
@ -369,7 +374,7 @@ static void tfifo_reset(struct Qdisc *sch)
static void tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch) static void tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
{ {
struct netem_sched_data *q = qdisc_priv(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; struct rb_node **p = &q->t_root.rb_node, *parent = NULL;
while (*p) { while (*p) {
@ -515,13 +520,13 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
if (q->gap == 0 || /* not doing reordering */ if (q->gap == 0 || /* not doing reordering */
q->counter < q->gap - 1 || /* inside last reordering gap */ q->counter < q->gap - 1 || /* inside last reordering gap */
q->reorder < get_crandom(&q->reorder_cor)) { q->reorder < get_crandom(&q->reorder_cor)) {
psched_time_t now; u64 now;
psched_tdiff_t delay; s64 delay;
delay = tabledist(q->latency, q->jitter, delay = tabledist(q->latency, q->jitter,
&q->delay_cor, q->delay_dist); &q->delay_cor, q->delay_dist);
now = psched_get_time(); now = ktime_get_ns();
if (q->rate) { if (q->rate) {
struct netem_skb_cb *last = NULL; struct netem_skb_cb *last = NULL;
@ -547,7 +552,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
* from delay. * from delay.
*/ */
delay -= last->time_to_send - now; 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; now = last->time_to_send;
} }
@ -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 * Do re-ordering by putting one out of N packets at the front
* of the queue. * of the queue.
*/ */
cb->time_to_send = psched_get_time(); cb->time_to_send = ktime_get_ns();
q->counter = 0; q->counter = 0;
netem_enqueue_skb_head(&sch->q, skb); netem_enqueue_skb_head(&sch->q, skb);
@ -593,6 +598,20 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
return NET_XMIT_SUCCESS; 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) static struct sk_buff *netem_dequeue(struct Qdisc *sch)
{ {
struct netem_sched_data *q = qdisc_priv(sch); struct netem_sched_data *q = qdisc_priv(sch);
@ -609,15 +628,18 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch)
} }
p = rb_first(&q->t_root); p = rb_first(&q->t_root);
if (p) { if (p) {
psched_time_t time_to_send; u64 time_to_send;
u64 now = ktime_get_ns();
skb = rb_to_skb(p); skb = rb_to_skb(p);
/* if more time remaining? */ /* if more time remaining? */
time_to_send = netem_skb_cb(skb)->time_to_send; time_to_send = netem_skb_cb(skb)->time_to_send;
if (time_to_send <= psched_get_time()) { if (q->slot.slot_next && q->slot.slot_next < time_to_send)
rb_erase(p, &q->t_root); get_slot_next(q, now);
if (time_to_send <= now && q->slot.slot_next <= now) {
rb_erase(p, &q->t_root);
sch->q.qlen--; sch->q.qlen--;
qdisc_qstats_backlog_dec(sch, skb); qdisc_qstats_backlog_dec(sch, skb);
skb->next = NULL; skb->next = NULL;
@ -636,6 +658,14 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch)
skb->tstamp = 0; skb->tstamp = 0;
#endif #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) { if (q->qdisc) {
unsigned int pkt_len = qdisc_pkt_len(skb); unsigned int pkt_len = qdisc_pkt_len(skb);
struct sk_buff *to_free = NULL; struct sk_buff *to_free = NULL;
@ -659,7 +689,10 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch)
if (skb) if (skb)
goto deliver; 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) { if (q->qdisc) {
@ -690,6 +723,7 @@ static void dist_free(struct disttable *d)
* Distribution data is a variable size payload containing * Distribution data is a variable size payload containing
* 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, const struct nlattr *attr)
{ {
struct netem_sched_data *q = qdisc_priv(sch); struct netem_sched_data *q = qdisc_priv(sch);
@ -720,6 +754,23 @@ static int get_dist_table(struct Qdisc *sch, const struct nlattr *attr)
return 0; 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) static void get_correlation(struct netem_sched_data *q, const struct nlattr *attr)
{ {
const struct tc_netem_corr *c = nla_data(attr); const struct tc_netem_corr *c = nla_data(attr);
@ -821,6 +872,9 @@ static const struct nla_policy netem_policy[TCA_NETEM_MAX + 1] = {
[TCA_NETEM_LOSS] = { .type = NLA_NESTED }, [TCA_NETEM_LOSS] = { .type = NLA_NESTED },
[TCA_NETEM_ECN] = { .type = NLA_U32 }, [TCA_NETEM_ECN] = { .type = NLA_U32 },
[TCA_NETEM_RATE64] = { .type = NLA_U64 }, [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, static int parse_attr(struct nlattr *tb[], int maxtype, struct nlattr *nla,
@ -888,8 +942,8 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt)
sch->limit = qopt->limit; sch->limit = qopt->limit;
q->latency = qopt->latency; q->latency = PSCHED_TICKS2NS(qopt->latency);
q->jitter = qopt->jitter; q->jitter = PSCHED_TICKS2NS(qopt->jitter);
q->limit = qopt->limit; q->limit = qopt->limit;
q->gap = qopt->gap; q->gap = qopt->gap;
q->counter = 0; q->counter = 0;
@ -918,9 +972,18 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt)
q->rate = max_t(u64, q->rate, q->rate = max_t(u64, q->rate,
nla_get_u64(tb[TCA_NETEM_RATE64])); 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]) if (tb[TCA_NETEM_ECN])
q->ecn = nla_get_u32(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; return ret;
} }
@ -1010,9 +1073,12 @@ static int netem_dump(struct Qdisc *sch, struct sk_buff *skb)
struct tc_netem_reorder reorder; struct tc_netem_reorder reorder;
struct tc_netem_corrupt corrupt; struct tc_netem_corrupt corrupt;
struct tc_netem_rate rate; struct tc_netem_rate rate;
struct tc_netem_slot slot;
qopt.latency = q->latency; qopt.latency = min_t(psched_tdiff_t, PSCHED_NS2TICKS(q->latency),
qopt.jitter = q->jitter; UINT_MAX);
qopt.jitter = min_t(psched_tdiff_t, PSCHED_NS2TICKS(q->jitter),
UINT_MAX);
qopt.limit = q->limit; qopt.limit = q->limit;
qopt.loss = q->loss; qopt.loss = q->loss;
qopt.gap = q->gap; qopt.gap = q->gap;
@ -1020,6 +1086,12 @@ static int netem_dump(struct Qdisc *sch, struct sk_buff *skb)
if (nla_put(skb, TCA_OPTIONS, sizeof(qopt), &qopt)) if (nla_put(skb, TCA_OPTIONS, sizeof(qopt), &qopt))
goto nla_put_failure; 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.delay_corr = q->delay_cor.rho;
cor.loss_corr = q->loss_cor.rho; cor.loss_corr = q->loss_cor.rho;
cor.dup_corr = q->dup_cor.rho; cor.dup_corr = q->dup_cor.rho;
@ -1056,6 +1128,16 @@ 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) {
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); return nla_nest_end(skb, nla);
nla_put_failure: nla_put_failure: