aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Hemminger <stephen@networkplumber.org>2024-03-13 09:54:44 -0700
committerStephen Hemminger <stephen@networkplumber.org>2024-03-13 09:54:44 -0700
commit9a6b231ea1b09e450688c5814a4c89a57cdbee77 (patch)
treeeb08529a63f2cbf373ee06f9beaa2f41e1734dbb
parent56511223ef26245b111e95baeeafd83d00ce7c2f (diff)
downloadiproute2-next-9a6b231ea1b09e450688c5814a4c89a57cdbee77.tar.gz
netem: use 64 bit value for latency and jitter
The current version of netem in iproute2 has a maximum of 4.3 seconds because of scaled 32 bit clock values. Some users would like to be able to use larger delays to emulate things like storage delays. Since kernel version 4.15, netem qdisc had netlink parameters to express wider range of delays in nanoseconds. But the iproute2 side was never updated to use them. This does break compatibility with older kernels (4.14 and earlier). With these out of support kernels, the latency/delay parameter will end up being ignored. Reported-by: Marc Blanchet <marc.blanchet@viagenie.ca> Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
-rw-r--r--tc/q_netem.c82
1 files changed, 46 insertions, 36 deletions
diff --git a/tc/q_netem.c b/tc/q_netem.c
index 3954d1f3e..90b26136e 100644
--- a/tc/q_netem.c
+++ b/tc/q_netem.c
@@ -170,25 +170,6 @@ static int get_distribution(const char *type, __s16 *data, int maxdata)
#define NEXT_IS_SIGNED_NUMBER() \
(NEXT_ARG_OK() && (isdigit(argv[1][0]) || argv[1][0] == '-'))
-/*
- * Adjust for the fact that psched_ticks aren't always usecs
- * (based on kernel PSCHED_CLOCK configuration
- */
-static int get_ticks(__u32 *ticks, const char *str)
-{
- unsigned int t;
-
- if (get_time(&t, str))
- return -1;
-
- if (tc_core_time2big(t)) {
- fprintf(stderr, "Illegal %u time (too large)\n", t);
- return -1;
- }
-
- *ticks = tc_core_time2tick(t);
- return 0;
-}
static int netem_parse_opt(const struct qdisc_util *qu, int argc, char **argv,
struct nlmsghdr *n, const char *dev)
@@ -208,6 +189,8 @@ static int netem_parse_opt(const struct qdisc_util *qu, int argc, char **argv,
__s16 *slot_dist_data = NULL;
__u16 loss_type = NETEM_LOSS_UNSPEC;
int present[__TCA_NETEM_MAX] = {};
+ __s64 latency64 = 0;
+ __s64 jitter64 = 0;
__u64 rate64 = 0;
__u64 seed = 0;
@@ -221,14 +204,20 @@ static int netem_parse_opt(const struct qdisc_util *qu, int argc, char **argv,
} else if (matches(*argv, "latency") == 0 ||
matches(*argv, "delay") == 0) {
NEXT_ARG();
- if (get_ticks(&opt.latency, *argv)) {
+
+ /* Old latency value in opt is no longer used. */
+ present[TCA_NETEM_LATENCY64] = 1;
+
+ if (get_time64(&latency64, *argv)) {
explain1("latency");
return -1;
}
if (NEXT_IS_NUMBER()) {
NEXT_ARG();
- if (get_ticks(&opt.jitter, *argv)) {
+
+ present[TCA_NETEM_JITTER64] = 1;
+ if (get_time64(&jitter64, *argv)) {
explain1("latency");
return -1;
}
@@ -552,7 +541,7 @@ random_loss_model:
tail = NLMSG_TAIL(n);
if (reorder.probability) {
- if (opt.latency == 0) {
+ if (latency64 == 0) {
fprintf(stderr, "reordering not possible without specifying some delay\n");
explain();
return -1;
@@ -573,7 +562,7 @@ random_loss_model:
}
}
- if (dist_data && (opt.latency == 0 || opt.jitter == 0)) {
+ if (dist_data && (latency64 == 0 || jitter64 == 0)) {
fprintf(stderr, "distribution specified but no latency and jitter values\n");
explain();
return -1;
@@ -582,6 +571,14 @@ random_loss_model:
if (addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt)) < 0)
return -1;
+ if (present[TCA_NETEM_LATENCY64] &&
+ addattr_l(n, 1024, TCA_NETEM_LATENCY64, &latency64, sizeof(latency64)) < 0)
+ return -1;
+
+ if (present[TCA_NETEM_JITTER64] &&
+ addattr_l(n, 1024, TCA_NETEM_JITTER64, &jitter64, sizeof(jitter64)) < 0)
+ return -1;
+
if (present[TCA_NETEM_CORR] &&
addattr_l(n, 1024, TCA_NETEM_CORR, &cor, sizeof(cor)) < 0)
return -1;
@@ -676,6 +673,8 @@ static int netem_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *
__u64 seed = 0;
int len;
__u64 rate64 = 0;
+ __u64 latency64 = 0;
+ __u64 jitter64 = 0;
SPRINT_BUF(b1);
@@ -734,6 +733,18 @@ static int netem_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *
return -1;
rate64 = rta_getattr_u64(tb[TCA_NETEM_RATE64]);
}
+ if (tb[TCA_NETEM_LATENCY64]) {
+ if (RTA_PAYLOAD(tb[TCA_NETEM_LATENCY64]) < sizeof(latency64))
+ return -1;
+ latency64 = rta_getattr_u64(tb[TCA_NETEM_LATENCY64]);
+
+ }
+ if (tb[TCA_NETEM_JITTER64]) {
+ if (RTA_PAYLOAD(tb[TCA_NETEM_JITTER64]) < sizeof(jitter64))
+ return -1;
+ jitter64 = rta_getattr_u64(tb[TCA_NETEM_JITTER64]);
+
+ }
if (tb[TCA_NETEM_SLOT]) {
if (RTA_PAYLOAD(tb[TCA_NETEM_SLOT]) < sizeof(*slot))
return -1;
@@ -749,24 +760,23 @@ static int netem_print_opt(const struct qdisc_util *qu, FILE *f, struct rtattr *
print_uint(PRINT_ANY, "limit", "limit %d", qopt.limit);
- if (qopt.latency) {
+
+ if (latency64 != 0) {
open_json_object("delay");
- if (!is_json_context()) {
- print_string(PRINT_FP, NULL, " delay %s",
- sprint_ticks(qopt.latency, b1));
- if (qopt.jitter)
- print_string(PRINT_FP, NULL, " %s",
- sprint_ticks(qopt.jitter, b1));
- } else {
+ if (is_json_context()) {
print_float(PRINT_JSON, "delay", NULL,
- tc_core_tick2time(qopt.latency) /
- 1000000.);
+ (double)latency64 / 1000000000.);
print_float(PRINT_JSON, "jitter", NULL,
- tc_core_tick2time(qopt.jitter) /
- 1000000.);
+ (double)jitter64 / 1000000000.);
+ } else {
+ print_string(PRINT_FP, NULL, " delay %s",
+ sprint_time64(latency64, b1));
+ if (jitter64 != 0)
+ print_string(PRINT_FP, NULL, " %s",
+ sprint_time64(jitter64, b1));
}
- print_corr(qopt.jitter && cor && cor->delay_corr,
+ print_corr(jitter64 && cor && cor->delay_corr,
cor ? cor->delay_corr : 0);
close_json_object();
}