q_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.

Syntax:
   slot distribution 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 distribution normal \
    800us 100us

  Optionally set the max slot size in bytes and/or packets.
  > tc qdisc add dev eth0 root netem slot distribution normal \
    800us 100us bytes 64k packets 42

Signed-off-by: Yousuk Seung <ysseung@google.com>
Signed-off-by: Neal Cardwell <ncardwell@google.com>
Signed-off-by: Dave Taht <dave.taht@gmail.com>
Signed-off-by: David Ahern <dsahern@gmail.com>
This commit is contained in:
Yousuk Seung 2018-08-26 19:42:30 -07:00 committed by David Ahern
parent b6268fbd58
commit 588dd51e2c
2 changed files with 78 additions and 19 deletions

View File

@ -53,9 +53,13 @@ NetEm \- Network Emulator
.IR RATE " [ " PACKETOVERHEAD " [ " CELLSIZE " [ " CELLOVERHEAD " ]]]]"
.IR SLOT " := "
.BR slot
.IR MIN_DELAY " [ " MAX_DELAY " ] ["
.BR packets
.BR slot " { "
.IR MIN_DELAY " [ " MAX_DELAY " ] |"
.br
.RB " " distribution " { "uniform " | " normal " | " pareto " | " paretonormal " | "
.IR FILE " } " DELAY " " JITTER " } "
.br
.RB " [ " packets
.IR PACKETS " ] [ "
.BR bytes
.IR BYTES " ]"
@ -172,9 +176,13 @@ an artificial packet compression (bursts). Another influence factor are network
adapter buffers which can also add artificial delay.
.SS slot
defer delivering accumulated packets to within a slot, with each available slot
configured with a minimum delay to acquire, and an optional maximum delay. Slot
delays can be specified in nanoseconds, microseconds, milliseconds or seconds
defer delivering accumulated packets to within a slot. Each available slot can be
configured with a minimum delay to acquire, and an optional maximum delay.
Alternatively it can be configured with the distribution similar to
.BR distribution
for
.BR delay
option. Slot delays can be specified in nanoseconds, microseconds, milliseconds or seconds
(e.g. 800us). Values for the optional parameters
.I BYTES
will limit the number of bytes delivered per slot, and/or

View File

@ -43,7 +43,9 @@ static void explain(void)
" [ rate RATE [PACKETOVERHEAD] [CELLSIZE] [CELLOVERHEAD]]\n" \
" [ slot MIN_DELAY [MAX_DELAY] [packets MAX_PACKETS]" \
" [bytes MAX_BYTES]]\n" \
);
" [ slot distribution" \
" {uniform|normal|pareto|paretonormal|custom} DELAY JITTER" \
" [packets MAX_PACKETS] [bytes MAX_BYTES]]\n");
}
static void explain1(const char *arg)
@ -159,6 +161,7 @@ static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv,
struct nlmsghdr *n, const char *dev)
{
int dist_size = 0;
int slot_dist_size = 0;
struct rtattr *tail;
struct tc_netem_qopt opt = { .limit = 1000 };
struct tc_netem_corr cor = {};
@ -169,6 +172,7 @@ static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv,
struct tc_netem_rate rate = {};
struct tc_netem_slot slot = {};
__s16 *dist_data = NULL;
__s16 *slot_dist_data = NULL;
__u16 loss_type = NETEM_LOSS_UNSPEC;
int present[__TCA_NETEM_MAX] = {};
__u64 rate64 = 0;
@ -417,21 +421,55 @@ static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv,
}
}
} else if (matches(*argv, "slot") == 0) {
NEXT_ARG();
present[TCA_NETEM_SLOT] = 1;
if (get_time64(&slot.min_delay, *argv)) {
explain1("slot min_delay");
return -1;
}
if (NEXT_IS_NUMBER()) {
NEXT_ARG();
if (get_time64(&slot.max_delay, *argv) ||
slot.max_delay < slot.min_delay) {
explain1("slot max_delay");
present[TCA_NETEM_SLOT] = 1;
if (get_time64(&slot.min_delay, *argv)) {
explain1("slot min_delay");
return -1;
}
if (NEXT_IS_NUMBER()) {
NEXT_ARG();
if (get_time64(&slot.max_delay, *argv) ||
slot.max_delay < slot.min_delay) {
explain1("slot max_delay");
return -1;
}
} else {
slot.max_delay = slot.min_delay;
}
} else {
slot.max_delay = slot.min_delay;
NEXT_ARG();
if (strcmp(*argv, "distribution") == 0) {
present[TCA_NETEM_SLOT] = 1;
NEXT_ARG();
slot_dist_data = calloc(sizeof(slot_dist_data[0]), MAX_DIST);
if (!slot_dist_data)
return -1;
slot_dist_size = get_distribution(*argv, slot_dist_data, MAX_DIST);
if (slot_dist_size <= 0) {
free(slot_dist_data);
return -1;
}
NEXT_ARG();
if (get_time64(&slot.dist_delay, *argv)) {
explain1("slot delay");
return -1;
}
NEXT_ARG();
if (get_time64(&slot.dist_jitter, *argv)) {
explain1("slot jitter");
return -1;
}
if (slot.dist_jitter <= 0) {
fprintf(stderr, "Non-positive jitter\n");
return -1;
}
} else {
fprintf(stderr, "Unknown slot parameter: %s\n",
*argv);
return -1;
}
}
if (NEXT_ARG_OK() &&
matches(*(argv+1), "packets") == 0) {
@ -559,6 +597,14 @@ static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv,
return -1;
free(dist_data);
}
if (slot_dist_data) {
if (addattr_l(n, MAX_DIST * sizeof(slot_dist_data[0]),
TCA_NETEM_SLOT_DIST,
slot_dist_data, slot_dist_size * sizeof(slot_dist_data[0])) < 0)
return -1;
free(slot_dist_data);
}
tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
return 0;
}
@ -713,8 +759,13 @@ static int netem_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
}
if (slot) {
fprintf(f, " slot %s", sprint_time64(slot->min_delay, b1));
fprintf(f, " %s", sprint_time64(slot->max_delay, b1));
if (slot->dist_jitter > 0) {
fprintf(f, " slot distribution %s", sprint_time64(slot->dist_delay, b1));
fprintf(f, " %s", sprint_time64(slot->dist_jitter, b1));
} else {
fprintf(f, " slot %s", sprint_time64(slot->min_delay, b1));
fprintf(f, " %s", sprint_time64(slot->max_delay, b1));
}
if (slot->max_packets)
fprintf(f, " packets %d", slot->max_packets);
if (slot->max_bytes)