diff --git a/tc/q_mqprio.c b/tc/q_mqprio.c index d6718fb1..b57351c2 100644 --- a/tc/q_mqprio.c +++ b/tc/q_mqprio.c @@ -27,6 +27,10 @@ static void explain(void) fprintf(stderr, "Usage: ... mqprio [num_tc NUMBER] [map P0 P1 ...]\n"); fprintf(stderr, " [queues count1@offset1 count2@offset2 ...] "); fprintf(stderr, "[hw 1|0]\n"); + fprintf(stderr, " [mode dcb|channel]\n"); + fprintf(stderr, " [shaper bw_rlimit SHAPER_PARAMS]\n" + "Where: SHAPER_PARAMS := { min_rate MIN_RATE1 MIN_RATE2 ...|\n" + " max_rate MAX_RATE1 MAX_RATE2 ... }\n"); } static int mqprio_parse_opt(struct qdisc_util *qu, int argc, @@ -40,6 +44,12 @@ static int mqprio_parse_opt(struct qdisc_util *qu, int argc, .count = { }, .offset = { }, }; + __u64 min_rate64[TC_QOPT_MAX_QUEUE] = {0}; + __u64 max_rate64[TC_QOPT_MAX_QUEUE] = {0}; + __u16 shaper = TC_MQPRIO_SHAPER_DCB; + __u16 mode = TC_MQPRIO_MODE_DCB; + struct rtattr *tail; + __u32 flags = 0; while (argc > 0) { idx = 0; @@ -92,6 +102,68 @@ static int mqprio_parse_opt(struct qdisc_util *qu, int argc, return -1; } idx++; + } else if (opt.hw && strcmp(*argv, "mode") == 0) { + NEXT_ARG(); + if (matches(*argv, "dcb") == 0) { + mode = TC_MQPRIO_MODE_DCB; + } else if (matches(*argv, "channel") == 0) { + mode = TC_MQPRIO_MODE_CHANNEL; + } else { + fprintf(stderr, "Illegal mode (%s)\n", + *argv); + return -1; + } + if (mode != TC_MQPRIO_MODE_DCB) + flags |= TC_MQPRIO_F_MODE; + idx++; + } else if (opt.hw && strcmp(*argv, "shaper") == 0) { + NEXT_ARG(); + if (matches(*argv, "dcb") == 0) { + shaper = TC_MQPRIO_SHAPER_DCB; + } else if (matches(*argv, "bw_rlimit") == 0) { + shaper = TC_MQPRIO_SHAPER_BW_RATE; + if (!NEXT_ARG_OK()) { + fprintf(stderr, "Incomplete shaper arguments\n"); + return -1; + } + } else { + fprintf(stderr, "Illegal shaper (%s)\n", + *argv); + return -1; + } + if (shaper != TC_MQPRIO_SHAPER_DCB) + flags |= TC_MQPRIO_F_SHAPER; + idx++; + } else if ((shaper == TC_MQPRIO_SHAPER_BW_RATE) && + strcmp(*argv, "min_rate") == 0) { + while (idx < TC_QOPT_MAX_QUEUE && NEXT_ARG_OK()) { + NEXT_ARG(); + if (get_rate64(&min_rate64[idx], *argv)) { + PREV_ARG(); + break; + } + idx++; + } + if (idx < opt.num_tc && !NEXT_ARG_OK()) { + fprintf(stderr, "Incomplete arguments, min_rate values expected\n"); + return -1; + } + flags |= TC_MQPRIO_F_MIN_RATE; + } else if ((shaper == TC_MQPRIO_SHAPER_BW_RATE) && + strcmp(*argv, "max_rate") == 0) { + while (idx < TC_QOPT_MAX_QUEUE && NEXT_ARG_OK()) { + NEXT_ARG(); + if (get_rate64(&max_rate64[idx], *argv)) { + PREV_ARG(); + break; + } + idx++; + } + if (idx < opt.num_tc && !NEXT_ARG_OK()) { + fprintf(stderr, "Incomplete arguments, max_rate values expected\n"); + return -1; + } + flags |= TC_MQPRIO_F_MAX_RATE; } else if (strcmp(*argv, "help") == 0) { explain(); return -1; @@ -102,7 +174,44 @@ static int mqprio_parse_opt(struct qdisc_util *qu, int argc, argc--; argv++; } + tail = NLMSG_TAIL(n); addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt)); + + if (flags & TC_MQPRIO_F_MODE) + addattr_l(n, 1024, TCA_MQPRIO_MODE, + &mode, sizeof(mode)); + if (flags & TC_MQPRIO_F_SHAPER) + addattr_l(n, 1024, TCA_MQPRIO_SHAPER, + &shaper, sizeof(shaper)); + + if (flags & TC_MQPRIO_F_MIN_RATE) { + struct rtattr *start; + + start = addattr_nest(n, 1024, + TCA_MQPRIO_MIN_RATE64 | NLA_F_NESTED); + + for (idx = 0; idx < TC_QOPT_MAX_QUEUE; idx++) + addattr_l(n, 1024, TCA_MQPRIO_MIN_RATE64, + &min_rate64[idx], sizeof(min_rate64[idx])); + + addattr_nest_end(n, start); + } + + if (flags & TC_MQPRIO_F_MAX_RATE) { + struct rtattr *start; + + start = addattr_nest(n, 1024, + TCA_MQPRIO_MAX_RATE64 | NLA_F_NESTED); + + for (idx = 0; idx < TC_QOPT_MAX_QUEUE; idx++) + addattr_l(n, 1024, TCA_MQPRIO_MAX_RATE64, + &max_rate64[idx], sizeof(max_rate64[idx])); + + addattr_nest_end(n, start); + } + + tail->rta_len = (void *)NLMSG_TAIL(n) - (void *)tail; + return 0; } @@ -110,10 +219,21 @@ static int mqprio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) { int i; struct tc_mqprio_qopt *qopt; + __u64 min_rate64[TC_QOPT_MAX_QUEUE] = {0}; + __u64 max_rate64[TC_QOPT_MAX_QUEUE] = {0}; + int len; + + SPRINT_BUF(b1); if (opt == NULL) return 0; + len = RTA_PAYLOAD(opt) - RTA_ALIGN(sizeof(*qopt)); + if (len < 0) { + fprintf(stderr, "options size error\n"); + return -1; + } + qopt = RTA_DATA(opt); fprintf(f, " tc %u map ", qopt->num_tc); @@ -123,6 +243,64 @@ static int mqprio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) for (i = 0; i < qopt->num_tc; i++) fprintf(f, "(%u:%u) ", qopt->offset[i], qopt->offset[i] + qopt->count[i] - 1); + + if (len > 0) { + struct rtattr *tb[TCA_MQPRIO_MAX + 1]; + + parse_rtattr(tb, TCA_MQPRIO_MAX, + RTA_DATA(opt) + RTA_ALIGN(sizeof(*qopt)), + len); + + if (tb[TCA_MQPRIO_MODE]) { + __u16 *mode = RTA_DATA(tb[TCA_MQPRIO_MODE]); + + if (*mode == TC_MQPRIO_MODE_CHANNEL) + fprintf(f, "\n mode:channel"); + } else { + fprintf(f, "\n mode:dcb"); + } + + if (tb[TCA_MQPRIO_SHAPER]) { + __u16 *shaper = RTA_DATA(tb[TCA_MQPRIO_SHAPER]); + + if (*shaper == TC_MQPRIO_SHAPER_BW_RATE) + fprintf(f, "\n shaper:bw_rlimit"); + } else { + fprintf(f, "\n shaper:dcb"); + } + + if (tb[TCA_MQPRIO_MIN_RATE64]) { + struct rtattr *r; + int rem = RTA_PAYLOAD(tb[TCA_MQPRIO_MIN_RATE64]); + __u64 *min = min_rate64; + + for (r = RTA_DATA(tb[TCA_MQPRIO_MIN_RATE64]); + RTA_OK(r, rem); r = RTA_NEXT(r, rem)) { + if (r->rta_type != TCA_MQPRIO_MIN_RATE64) + return -1; + *(min++) = rta_getattr_u64(r); + } + fprintf(f, " min_rate:"); + for (i = 0; i < qopt->num_tc; i++) + fprintf(f, "%s ", sprint_rate(min_rate64[i], b1)); + } + + if (tb[TCA_MQPRIO_MAX_RATE64]) { + struct rtattr *r; + int rem = RTA_PAYLOAD(tb[TCA_MQPRIO_MAX_RATE64]); + __u64 *max = max_rate64; + + for (r = RTA_DATA(tb[TCA_MQPRIO_MAX_RATE64]); + RTA_OK(r, rem); r = RTA_NEXT(r, rem)) { + if (r->rta_type != TCA_MQPRIO_MAX_RATE64) + return -1; + *(max++) = rta_getattr_u64(r); + } + fprintf(f, " max_rate:"); + for (i = 0; i < qopt->num_tc; i++) + fprintf(f, "%s ", sprint_rate(max_rate64[i], b1)); + } + } return 0; }