]> git.ipfire.org Git - thirdparty/iproute2.git/commitdiff
tc/mqprio: add support for preemptible traffic classes
authorVladimir Oltean <vladimir.oltean@nxp.com>
Tue, 18 Apr 2023 11:39:52 +0000 (14:39 +0300)
committerDavid Ahern <dsahern@kernel.org>
Tue, 25 Apr 2023 01:42:33 +0000 (19:42 -0600)
Add support for the "fp" argument in tc-mqprio, which takes an array
of letters "E" (for express) or "P" (for preemptible), one per traffic
class, and transforms them into TCA_MQPRIO_TC_ENTRY_FP u32 attributes of
the TCA_MQPRIO_TC_ENTRY nest. We also dump these new netlink attributes
when they come from the kernel.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David Ahern <dsahern@kernel.org>
man/man8/tc-mqprio.8
tc/q_mqprio.c

index 3441cb68a27fc68c04f13c6b30501cfa504fa275..724ef906090c6c825074876de15661520c482fa4 100644 (file)
@@ -30,9 +30,11 @@ dcb|bw_rlimit ]
 .B min_rate
 min_rate1 min_rate2 ... ] [
 .B max_rate
-max_rate1 max_rate2 ...
-.B ]
-
+max_rate1 max_rate2 ... ]
+.ti +8
+[
+.B fp
+FP0 FP1 FP2 ... ]
 
 .SH DESCRIPTION
 The MQPRIO qdisc is a simple queuing discipline that allows mapping
@@ -162,6 +164,34 @@ the
 argument is set to
 .B 'bw_rlimit'.
 
+.TP
+fp
+Selects whether traffic classes are express (deliver packets via the eMAC) or
+preemptible (deliver packets via the pMAC), according to IEEE 802.1Q-2018
+clause 6.7.2 Frame preemption. Takes the form of an array (one element per
+traffic class) with values being
+.B 'E'
+(for express) or
+.B 'P'
+(for preemptible).
+
+Multiple priorities which map to the same traffic class, as well as multiple
+TXQs which map to the same traffic class, must have the same FP attributes.
+To interpret the FP as an attribute per priority, the
+.B 'map'
+argument can be used for translation. To interpret FP as an attribute per TXQ,
+the
+.B 'queues'
+argument can be used for translation.
+
+Traffic classes are express by default. The argument is supported only with
+.B 'hw'
+set to 1. Preemptible traffic classes are accepted only if the device has a MAC
+Merge layer configurable through
+.BR ethtool(8).
+
+.SH SEE ALSO
+.BR ethtool(8)
 
 .SH EXAMPLE
 
index 99c43491e0bea948c8007e1528d5d72417fb5386..7a4417f5363b968f381538781f90cab619f70b6b 100644 (file)
@@ -23,12 +23,29 @@ static void explain(void)
                "Usage: ... mqprio      [num_tc NUMBER] [map P0 P1 ...]\n"
                "                       [queues count1@offset1 count2@offset2 ...] "
                "[hw 1|0]\n"
+               "                       [fp FP0 FP1 FP2 ...]\n"
                "                       [mode dcb|channel]\n"
                "                       [shaper bw_rlimit SHAPER_PARAMS]\n"
                "Where: SHAPER_PARAMS := { min_rate MIN_RATE1 MIN_RATE2 ...|\n"
                "                         max_rate MAX_RATE1 MAX_RATE2 ... }\n");
 }
 
+static void add_tc_entries(struct nlmsghdr *n, __u32 fp[TC_QOPT_MAX_QUEUE],
+                          int num_fp_entries)
+{
+       struct rtattr *l;
+       __u32 tc;
+
+       for (tc = 0; tc < num_fp_entries; tc++) {
+               l = addattr_nest(n, 1024, TCA_MQPRIO_TC_ENTRY | NLA_F_NESTED);
+
+               addattr32(n, 1024, TCA_MQPRIO_TC_ENTRY_INDEX, tc);
+               addattr32(n, 1024, TCA_MQPRIO_TC_ENTRY_FP, fp[tc]);
+
+               addattr_nest_end(n, l);
+       }
+}
+
 static int mqprio_parse_opt(struct qdisc_util *qu, int argc,
                            char **argv, struct nlmsghdr *n, const char *dev)
 {
@@ -43,7 +60,10 @@ static int mqprio_parse_opt(struct qdisc_util *qu, int argc,
        __u64 min_rate64[TC_QOPT_MAX_QUEUE] = {0};
        __u64 max_rate64[TC_QOPT_MAX_QUEUE] = {0};
        __u16 shaper = TC_MQPRIO_SHAPER_DCB;
+       __u32 fp[TC_QOPT_MAX_QUEUE] = { };
        __u16 mode = TC_MQPRIO_MODE_DCB;
+       bool have_tc_entries = false;
+       int num_fp_entries = 0;
        int cnt_off_pairs = 0;
        struct rtattr *tail;
        __u32 flags = 0;
@@ -93,6 +113,21 @@ static int mqprio_parse_opt(struct qdisc_util *qu, int argc,
                                idx++;
                                cnt_off_pairs++;
                        }
+               } 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 {
+                                       PREV_ARG();
+                                       break;
+                               }
+                               num_fp_entries++;
+                               idx++;
+                       }
+                       have_tc_entries = true;
                } else if (strcmp(*argv, "hw") == 0) {
                        NEXT_ARG();
                        if (get_u8(&opt.hw, *argv, 10)) {
@@ -187,6 +222,9 @@ static int mqprio_parse_opt(struct qdisc_util *qu, int argc,
                addattr_l(n, 1024, TCA_MQPRIO_SHAPER,
                          &shaper, sizeof(shaper));
 
+       if (have_tc_entries)
+               add_tc_entries(n, fp, num_fp_entries);
+
        if (flags & TC_MQPRIO_F_MIN_RATE) {
                struct rtattr *start;
 
@@ -218,6 +256,64 @@ static int mqprio_parse_opt(struct qdisc_util *qu, int argc,
        return 0;
 }
 
+static void dump_tc_entry(struct rtattr *rta, __u32 fp[TC_QOPT_MAX_QUEUE],
+                         int *max_tc_fp)
+{
+       struct rtattr *tb[TCA_MQPRIO_TC_ENTRY_MAX + 1];
+       __u32 tc, val = 0;
+
+       parse_rtattr_nested(tb, TCA_MQPRIO_TC_ENTRY_MAX, rta);
+
+       if (!tb[TCA_MQPRIO_TC_ENTRY_INDEX]) {
+               fprintf(stderr, "Missing tc entry index\n");
+               return;
+       }
+
+       tc = rta_getattr_u32(tb[TCA_MQPRIO_TC_ENTRY_INDEX]);
+       /* Prevent array out of bounds access */
+       if (tc >= TC_QOPT_MAX_QUEUE) {
+               fprintf(stderr, "Unexpected tc entry index %d\n", tc);
+               return;
+       }
+
+       if (tb[TCA_MQPRIO_TC_ENTRY_FP]) {
+               val = rta_getattr_u32(tb[TCA_MQPRIO_TC_ENTRY_FP]);
+               fp[tc] = val;
+
+               if (*max_tc_fp < (int)tc)
+                       *max_tc_fp = tc;
+       }
+}
+
+static void dump_tc_entries(FILE *f, struct rtattr *opt, int len)
+{
+       __u32 fp[TC_QOPT_MAX_QUEUE] = {};
+       int max_tc_fp = -1;
+       struct rtattr *rta;
+       int tc;
+
+       for (rta = opt; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
+               if (rta->rta_type != (TCA_MQPRIO_TC_ENTRY | NLA_F_NESTED))
+                       continue;
+
+               dump_tc_entry(rta, fp, &max_tc_fp);
+       }
+
+       if (max_tc_fp >= 0) {
+               open_json_array(PRINT_ANY,
+                               is_json_context() ? "fp" : "\n             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 mqprio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
 {
        int i;
@@ -309,7 +405,10 @@ static int mqprio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
                                tc_print_rate(PRINT_ANY, NULL, "%s ", max_rate64[i]);
                        close_json_array(PRINT_ANY, "");
                }
+
+               dump_tc_entries(f, RTA_DATA(opt) + RTA_ALIGN(sizeof(*qopt)), len);
        }
+
        return 0;
 }