Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 907a4425 authored by David S. Miller's avatar David S. Miller
Browse files

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 338d182f 836af83b
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -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,
};

@@ -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 */
+111 −29
Original line number Diff line number Diff line
@@ -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;
@@ -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
@@ -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)
@@ -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,
static s64 tabledist(s64 mu, s64 sigma,
		     struct crndstate *state,
			 const struct disttable *dist)
{
	psched_tdiff_t x;
	s64 x;
	long t;
	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;
}

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) {
@@ -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)
@@ -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) {
@@ -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;
@@ -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;
			}

@@ -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);
@@ -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);
@@ -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;
@@ -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;
@@ -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) {
@@ -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);
@@ -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);
@@ -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,
@@ -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;
@@ -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;
}

@@ -1010,9 +1073,12 @@ 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;
@@ -1020,6 +1086,12 @@ static int netem_dump(struct Qdisc *sch, struct sk_buff *skb)
	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;
@@ -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: