diff mbox series

[iproute2-next,2/3] netem: use 64 bit value for latency and jitter

Message ID 20240308171656.9034-2-stephen@networkplumber.org (mailing list archive)
State Accepted
Delegated to: Stephen Hemminger
Headers show
Series [iproute2-next,1/3] README: add note about kernel version compatibility | expand

Checks

Context Check Description
netdev/tree_selection success Not a local patch

Commit Message

Stephen Hemminger March 8, 2024, 5:16 p.m. UTC
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>
---
 tc/q_netem.c | 83 +++++++++++++++++++++++++++++-----------------------
 1 file changed, 46 insertions(+), 37 deletions(-)
diff mbox series

Patch

diff --git a/tc/q_netem.c b/tc/q_netem.c
index 4ce9ab6e529b..86cabbfe7b3a 100644
--- a/tc/q_netem.c
+++ b/tc/q_netem.c
@@ -170,26 +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(struct qdisc_util *qu, int argc, char **argv,
 			   struct nlmsghdr *n, const char *dev)
 {
@@ -208,6 +188,8 @@  static int netem_parse_opt(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 +203,20 @@  static int netem_parse_opt(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 +540,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 +561,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 +570,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 +672,8 @@  static int netem_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
 	__u64 seed = 0;
 	int len;
 	__u64 rate64 = 0;
+	__u64 latency64 = 0;
+	__u64 jitter64 = 0;
 
 	SPRINT_BUF(b1);
 
@@ -734,6 +732,18 @@  static int netem_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
 				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 +759,23 @@  static int netem_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
 
 	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();
 	}