mirror of https://gitee.com/openkylin/linux.git
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:
commit
907a4425f7
|
@ -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 */
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in New Issue