/* * q_mqprio.c MQ prio qdisc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Author: John Fastabend, */ #include #include #include #include #include #include #include #include #include "utils.h" #include "tc_util.h" static void explain(void) { fprintf(stderr, "Usage: ... mqprio [num_tc NUMBER] [map P0 P1 ...]\n" " [queues count1@offset1 count2@offset2 ...] " "[hw 1|0]\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 int mqprio_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { int idx; struct tc_mqprio_qopt opt = { .num_tc = 8, .prio_tc_map = { 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 1, 1, 3, 3, 3, 3 }, .hw = 1, .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; int cnt_off_pairs = 0; struct rtattr *tail; __u32 flags = 0; while (argc > 0) { idx = 0; if (strcmp(*argv, "num_tc") == 0) { NEXT_ARG(); if (get_u8(&opt.num_tc, *argv, 10)) { fprintf(stderr, "Illegal \"num_tc\"\n"); return -1; } } else if (strcmp(*argv, "map") == 0) { while (idx < TC_QOPT_MAX_QUEUE && NEXT_ARG_OK()) { NEXT_ARG(); if (get_u8(&opt.prio_tc_map[idx], *argv, 10)) { PREV_ARG(); break; } idx++; } for ( ; idx < TC_QOPT_MAX_QUEUE; idx++) opt.prio_tc_map[idx] = 0; } else if (strcmp(*argv, "queues") == 0) { char *tmp, *tok; while (idx < TC_QOPT_MAX_QUEUE && NEXT_ARG_OK()) { NEXT_ARG(); tmp = strdup(*argv); if (!tmp) break; tok = strtok(tmp, "@"); if (get_u16(&opt.count[idx], tok, 10)) { free(tmp); PREV_ARG(); break; } tok = strtok(NULL, "@"); if (get_u16(&opt.offset[idx], tok, 10)) { free(tmp); PREV_ARG(); break; } free(tmp); idx++; cnt_off_pairs++; } } else if (strcmp(*argv, "hw") == 0) { NEXT_ARG(); if (get_u8(&opt.hw, *argv, 10)) { fprintf(stderr, "Illegal \"hw\"\n"); 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; } else { invarg("unknown argument", *argv); } argc--; argv++; } if (cnt_off_pairs > opt.num_tc) { fprintf(stderr, "queues count/offset pair count %d can not be higher than given num_tc %d\n", cnt_off_pairs, opt.num_tc); return -1; } 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; } 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); for (i = 0; i <= TC_PRIO_MAX; i++) fprintf(f, "%u ", qopt->prio_tc_map[i]); fprintf(f, "\n queues:"); 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; } struct qdisc_util mqprio_qdisc_util = { .id = "mqprio", .parse_qopt = mqprio_parse_opt, .print_qopt = mqprio_print_opt, };