diff mbox series

[v2,iproute2-next,10/10] tc/taprio: add support for preemptible traffic classes

Message ID 20230418113953.818831-11-vladimir.oltean@nxp.com (mailing list archive)
State Accepted
Delegated to: David Ahern
Headers show
Series Add tc-mqprio and tc-taprio support for preemptible traffic classes | expand

Checks

Context Check Description
netdev/tree_selection success Not a local patch, async

Commit Message

Vladimir Oltean April 18, 2023, 11:39 a.m. UTC
Add support for the same kind of "fp" array argument as in mqprio,
except here we already have some handling for per-tc entries (max-sdu).
We just need to expand that logic such that we also add (and parse) the
FP adminStatus property of each traffic class.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
v1->v2: amended help text so that user space (kselftests) could detect
        the presence of the new feature

 man/man8/tc-taprio.8 |  11 +++++
 tc/q_taprio.c        | 100 ++++++++++++++++++++++++++++++++-----------
 2 files changed, 87 insertions(+), 24 deletions(-)
diff mbox series

Patch

diff --git a/man/man8/tc-taprio.8 b/man/man8/tc-taprio.8
index c3ccefea9c8a..bf489b032a7e 100644
--- a/man/man8/tc-taprio.8
+++ b/man/man8/tc-taprio.8
@@ -36,6 +36,10 @@  clockid
 [
 .B max-sdu
 <queueMaxSDU[TC 0]> <queueMaxSDU[TC 1]> <queueMaxSDU[TC N]> ]
+.ti +8
+[
+.B fp
+<adminStatus[TC 0]> <adminStatus[TC 1]> <adminStatus[TC N]> ]
 
 .SH DESCRIPTION
 The TAPRIO qdisc implements a simplified version of the scheduling
@@ -163,6 +167,13 @@  represents the maximum L2 payload size that can egress that traffic class.
 Elements that are not filled in default to 0. The value 0 means that the
 traffic class can send packets up to the port's maximum MTU in size.
 
+.TP
+fp
+.br
+Selects whether traffic classes are express or preemptible. See
+.BR tc-mqprio(8)
+for details.
+
 .SH EXAMPLES
 
 The following example shows how an traffic schedule with three traffic
diff --git a/tc/q_taprio.c b/tc/q_taprio.c
index c0da65fe3744..bc29710c4686 100644
--- a/tc/q_taprio.c
+++ b/tc/q_taprio.c
@@ -49,6 +49,7 @@  static void explain(void)
 		"		[queues COUNT@OFFSET COUNT@OFFSET COUNT@OFFSET ...]\n"
 		"		[ [sched-entry index cmd gate-mask interval] ... ]\n"
 		"		[base-time time] [txtime-delay delay]\n"
+		"		[fp FP0 FP1 FP2 ...]\n"
 		"\n"
 		"CLOCKID must be a valid SYS-V id (i.e. CLOCK_TAI)\n");
 }
@@ -148,17 +149,29 @@  static struct sched_entry *create_entry(uint32_t gatemask, uint32_t interval, ui
 }
 
 static void add_tc_entries(struct nlmsghdr *n, __u32 max_sdu[TC_QOPT_MAX_QUEUE],
-			   int num_max_sdu_entries)
+			   int num_max_sdu_entries, __u32 fp[TC_QOPT_MAX_QUEUE],
+			   int num_fp_entries)
 {
 	struct rtattr *l;
+	int num_tc;
 	__u32 tc;
 
-	for (tc = 0; tc < num_max_sdu_entries; tc++) {
+	num_tc = max(num_max_sdu_entries, num_fp_entries);
+
+	for (tc = 0; tc < num_tc; tc++) {
 		l = addattr_nest(n, 1024, TCA_TAPRIO_ATTR_TC_ENTRY | NLA_F_NESTED);
 
 		addattr_l(n, 1024, TCA_TAPRIO_TC_ENTRY_INDEX, &tc, sizeof(tc));
-		addattr_l(n, 1024, TCA_TAPRIO_TC_ENTRY_MAX_SDU,
-			  &max_sdu[tc], sizeof(max_sdu[tc]));
+
+		if (tc < num_max_sdu_entries) {
+			addattr_l(n, 1024, TCA_TAPRIO_TC_ENTRY_MAX_SDU,
+				  &max_sdu[tc], sizeof(max_sdu[tc]));
+		}
+
+		if (tc < num_fp_entries) {
+			addattr_l(n, 1024, TCA_TAPRIO_TC_ENTRY_FP, &fp[tc],
+				  sizeof(fp[tc]));
+		}
 
 		addattr_nest_end(n, l);
 	}
@@ -168,6 +181,7 @@  static int taprio_parse_opt(struct qdisc_util *qu, int argc,
 			    char **argv, struct nlmsghdr *n, const char *dev)
 {
 	__u32 max_sdu[TC_QOPT_MAX_QUEUE] = { };
+	__u32 fp[TC_QOPT_MAX_QUEUE] = { };
 	__s32 clockid = CLOCKID_INVALID;
 	struct tc_mqprio_qopt opt = { };
 	__s64 cycle_time_extension = 0;
@@ -175,6 +189,7 @@  static int taprio_parse_opt(struct qdisc_util *qu, int argc,
 	bool have_tc_entries = false;
 	int num_max_sdu_entries = 0;
 	struct rtattr *tail, *l;
+	int num_fp_entries = 0;
 	__u32 taprio_flags = 0;
 	__u32 txtime_delay = 0;
 	__s64 cycle_time = 0;
@@ -227,6 +242,23 @@  static int taprio_parse_opt(struct qdisc_util *qu, int argc,
 				free(tmp);
 				idx++;
 			}
+		} else if (strcmp(*argv, "fp") == 0) {
+			while (idx < TC_QOPT_MAX_QUEUE && NEXT_ARG_OK()) {
+				NEXT_ARG();
+				if (strcmp(*argv, "E") == 0) {
+					fp[idx] = TC_FP_EXPRESS;
+				} else if (strcmp(*argv, "P") == 0) {
+					fp[idx] = TC_FP_PREEMPTIBLE;
+				} else {
+					fprintf(stderr,
+						"Illegal \"fp\" value \"%s\", expected \"E\" or \"P\"\n",
+						*argv);
+					return -1;
+				}
+				num_fp_entries++;
+				idx++;
+			}
+			have_tc_entries = true;
 		} else if (strcmp(*argv, "max-sdu") == 0) {
 			while (idx < TC_QOPT_MAX_QUEUE && NEXT_ARG_OK()) {
 				NEXT_ARG();
@@ -369,7 +401,7 @@  static int taprio_parse_opt(struct qdisc_util *qu, int argc,
 			  &cycle_time_extension, sizeof(cycle_time_extension));
 
 	if (have_tc_entries)
-		add_tc_entries(n, max_sdu, num_max_sdu_entries);
+		add_tc_entries(n, max_sdu, num_max_sdu_entries, fp, num_fp_entries);
 
 	l = addattr_nest(n, 1024, TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST | NLA_F_NESTED);
 
@@ -460,9 +492,10 @@  static int print_schedule(FILE *f, struct rtattr **tb)
 	return 0;
 }
 
-static void dump_tc_entry(__u32 max_sdu[TC_QOPT_MAX_QUEUE],
-			  struct rtattr *item, bool *have_tc_entries,
-			  int *max_tc_index)
+static void dump_tc_entry(struct rtattr *item,
+			  __u32 max_sdu[TC_QOPT_MAX_QUEUE],
+			  __u32 fp[TC_QOPT_MAX_QUEUE],
+			  int *max_tc_max_sdu, int *max_tc_fp)
 {
 	struct rtattr *tb[TCA_TAPRIO_TC_ENTRY_MAX + 1];
 	__u32 tc, val = 0;
@@ -481,23 +514,30 @@  static void dump_tc_entry(__u32 max_sdu[TC_QOPT_MAX_QUEUE],
 		return;
 	}
 
-	if (*max_tc_index < tc)
-		*max_tc_index = tc;
-
-	if (tb[TCA_TAPRIO_TC_ENTRY_MAX_SDU])
+	if (tb[TCA_TAPRIO_TC_ENTRY_MAX_SDU]) {
 		val = rta_getattr_u32(tb[TCA_TAPRIO_TC_ENTRY_MAX_SDU]);
+		max_sdu[tc] = val;
+		if (*max_tc_max_sdu < (int)tc)
+			*max_tc_max_sdu = tc;
+	}
 
-	max_sdu[tc] = val;
+	if (tb[TCA_TAPRIO_TC_ENTRY_FP]) {
+		val = rta_getattr_u32(tb[TCA_TAPRIO_TC_ENTRY_FP]);
+		fp[tc] = val;
 
-	*have_tc_entries = true;
+		if (*max_tc_fp < (int)tc)
+			*max_tc_fp = tc;
+	}
 }
 
 static void dump_tc_entries(FILE *f, struct rtattr *opt)
 {
 	__u32 max_sdu[TC_QOPT_MAX_QUEUE] = {};
-	int tc, rem, max_tc_index = 0;
-	bool have_tc_entries = false;
+	__u32 fp[TC_QOPT_MAX_QUEUE] = {};
+	int max_tc_max_sdu = -1;
+	int max_tc_fp = -1;
 	struct rtattr *i;
+	int tc, rem;
 
 	rem = RTA_PAYLOAD(opt);
 
@@ -505,18 +545,30 @@  static void dump_tc_entries(FILE *f, struct rtattr *opt)
 		if (i->rta_type != (TCA_TAPRIO_ATTR_TC_ENTRY | NLA_F_NESTED))
 			continue;
 
-		dump_tc_entry(max_sdu, i, &have_tc_entries, &max_tc_index);
+		dump_tc_entry(i, max_sdu, fp, &max_tc_max_sdu, &max_tc_fp);
 	}
 
-	if (!have_tc_entries)
-		return;
+	if (max_tc_max_sdu >= 0) {
+		open_json_array(PRINT_ANY, "max-sdu");
+		for (tc = 0; tc <= max_tc_max_sdu; tc++)
+			print_uint(PRINT_ANY, NULL, " %u", max_sdu[tc]);
+		close_json_array(PRINT_ANY, "");
 
-	open_json_array(PRINT_ANY, "max-sdu");
-	for (tc = 0; tc <= max_tc_index; tc++)
-		print_uint(PRINT_ANY, NULL, " %u", max_sdu[tc]);
-	close_json_array(PRINT_ANY, "");
+		print_nl();
+	}
 
-	print_nl();
+	if (max_tc_fp >= 0) {
+		open_json_array(PRINT_ANY, "fp");
+		for (tc = 0; tc <= max_tc_fp; tc++) {
+			print_string(PRINT_ANY, NULL, " %s",
+				     fp[tc] == TC_FP_PREEMPTIBLE ? "P" :
+				     fp[tc] == TC_FP_EXPRESS ? "E" :
+				     "?");
+		}
+		close_json_array(PRINT_ANY, "");
+
+		print_nl();
+	}
 }
 
 static int taprio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)