2011-04-12 23:57:27 +08:00
|
|
|
/*
|
|
|
|
* 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, <john.r.fastabend@intel.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "utils.h"
|
|
|
|
#include "tc_util.h"
|
|
|
|
|
|
|
|
static void explain(void)
|
|
|
|
{
|
treewide: refactor help messages
Every tool in the iproute2 package have one or more function to show
an help message to the user. Some of these functions print the help
line by line with a series of printf call, e.g. ip/xfrm_state.c does
60 fprintf calls.
If we group all the calls to a single one and just concatenate strings,
we save a lot of libc calls and thus object size. The size difference
of the compiled binaries calculated with bloat-o-meter is:
ip/ip:
add/remove: 0/0 grow/shrink: 5/15 up/down: 103/-4796 (-4693)
Total: Before=672591, After=667898, chg -0.70%
ip/rtmon:
add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-54 (-54)
Total: Before=48879, After=48825, chg -0.11%
tc/tc:
add/remove: 0/2 grow/shrink: 31/10 up/down: 882/-6133 (-5251)
Total: Before=351912, After=346661, chg -1.49%
bridge/bridge:
add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-459 (-459)
Total: Before=70502, After=70043, chg -0.65%
misc/lnstat:
add/remove: 0/1 grow/shrink: 1/0 up/down: 48/-486 (-438)
Total: Before=9960, After=9522, chg -4.40%
tipc/tipc:
add/remove: 0/0 grow/shrink: 1/1 up/down: 18/-62 (-44)
Total: Before=79182, After=79138, chg -0.06%
While at it, indent some strings which were starting at column 0,
and use tabs where possible, to have a consistent style across helps.
Signed-off-by: Matteo Croce <mcroce@redhat.com>
Signed-off-by: David Ahern <dsahern@gmail.com>
2019-05-17 21:38:28 +08:00
|
|
|
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"
|
2017-11-01 15:45:42 +08:00
|
|
|
"Where: SHAPER_PARAMS := { min_rate MIN_RATE1 MIN_RATE2 ...|\n"
|
treewide: refactor help messages
Every tool in the iproute2 package have one or more function to show
an help message to the user. Some of these functions print the help
line by line with a series of printf call, e.g. ip/xfrm_state.c does
60 fprintf calls.
If we group all the calls to a single one and just concatenate strings,
we save a lot of libc calls and thus object size. The size difference
of the compiled binaries calculated with bloat-o-meter is:
ip/ip:
add/remove: 0/0 grow/shrink: 5/15 up/down: 103/-4796 (-4693)
Total: Before=672591, After=667898, chg -0.70%
ip/rtmon:
add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-54 (-54)
Total: Before=48879, After=48825, chg -0.11%
tc/tc:
add/remove: 0/2 grow/shrink: 31/10 up/down: 882/-6133 (-5251)
Total: Before=351912, After=346661, chg -1.49%
bridge/bridge:
add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-459 (-459)
Total: Before=70502, After=70043, chg -0.65%
misc/lnstat:
add/remove: 0/1 grow/shrink: 1/0 up/down: 48/-486 (-438)
Total: Before=9960, After=9522, chg -4.40%
tipc/tipc:
add/remove: 0/0 grow/shrink: 1/1 up/down: 18/-62 (-44)
Total: Before=79182, After=79138, chg -0.06%
While at it, indent some strings which were starting at column 0,
and use tabs where possible, to have a consistent style across helps.
Signed-off-by: Matteo Croce <mcroce@redhat.com>
Signed-off-by: David Ahern <dsahern@gmail.com>
2019-05-17 21:38:28 +08:00
|
|
|
" max_rate MAX_RATE1 MAX_RATE2 ... }\n");
|
2011-04-12 23:57:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mqprio_parse_opt(struct qdisc_util *qu, int argc,
|
2017-11-21 10:20:47 +08:00
|
|
|
char **argv, struct nlmsghdr *n, const char *dev)
|
2011-04-12 23:57:27 +08:00
|
|
|
{
|
|
|
|
int idx;
|
|
|
|
struct tc_mqprio_qopt opt = {
|
2017-08-25 06:27:35 +08:00
|
|
|
.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 = { },
|
|
|
|
};
|
2017-11-01 15:45:42 +08:00
|
|
|
__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;
|
2011-04-12 23:57:27 +08:00
|
|
|
|
|
|
|
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;
|
2011-04-27 03:44:42 +08:00
|
|
|
} else if (strcmp(*argv, "queues") == 0) {
|
|
|
|
char *tmp, *tok;
|
|
|
|
|
2011-04-12 23:57:27 +08:00
|
|
|
while (idx < TC_QOPT_MAX_QUEUE && NEXT_ARG_OK()) {
|
|
|
|
NEXT_ARG();
|
2011-04-27 03:44:42 +08:00
|
|
|
|
|
|
|
tmp = strdup(*argv);
|
|
|
|
if (!tmp)
|
|
|
|
break;
|
|
|
|
|
|
|
|
tok = strtok(tmp, "@");
|
|
|
|
if (get_u16(&opt.count[idx], tok, 10)) {
|
|
|
|
free(tmp);
|
2011-04-12 23:57:27 +08:00
|
|
|
PREV_ARG();
|
|
|
|
break;
|
|
|
|
}
|
2011-04-27 03:44:42 +08:00
|
|
|
tok = strtok(NULL, "@");
|
|
|
|
if (get_u16(&opt.offset[idx], tok, 10)) {
|
|
|
|
free(tmp);
|
2011-04-12 23:57:27 +08:00
|
|
|
PREV_ARG();
|
|
|
|
break;
|
|
|
|
}
|
2011-04-27 03:44:42 +08:00
|
|
|
free(tmp);
|
2011-04-12 23:57:27 +08:00
|
|
|
idx++;
|
|
|
|
}
|
|
|
|
} else if (strcmp(*argv, "hw") == 0) {
|
|
|
|
NEXT_ARG();
|
|
|
|
if (get_u8(&opt.hw, *argv, 10)) {
|
|
|
|
fprintf(stderr, "Illegal \"hw\"\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
idx++;
|
2017-11-01 15:45:42 +08:00
|
|
|
} 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;
|
2011-04-12 23:57:27 +08:00
|
|
|
} else if (strcmp(*argv, "help") == 0) {
|
|
|
|
explain();
|
|
|
|
return -1;
|
|
|
|
} else {
|
2018-09-07 05:01:17 +08:00
|
|
|
invarg("unknown argument", *argv);
|
2011-04-12 23:57:27 +08:00
|
|
|
}
|
|
|
|
argc--; argv++;
|
|
|
|
}
|
|
|
|
|
2018-07-17 01:52:18 +08:00
|
|
|
tail = NLMSG_TAIL(n);
|
|
|
|
addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt));
|
2017-11-01 15:45:42 +08:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-07-17 01:52:18 +08:00
|
|
|
tail->rta_len = (void *)NLMSG_TAIL(n) - (void *)tail;
|
2017-11-01 15:45:42 +08:00
|
|
|
|
2011-04-12 23:57:27 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-02-13 03:09:03 +08:00
|
|
|
static int mqprio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
|
2011-04-12 23:57:27 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct tc_mqprio_qopt *qopt;
|
2017-11-01 15:45:42 +08:00
|
|
|
__u64 min_rate64[TC_QOPT_MAX_QUEUE] = {0};
|
|
|
|
__u64 max_rate64[TC_QOPT_MAX_QUEUE] = {0};
|
|
|
|
int len;
|
|
|
|
|
|
|
|
SPRINT_BUF(b1);
|
2011-04-12 23:57:27 +08:00
|
|
|
|
|
|
|
if (opt == NULL)
|
|
|
|
return 0;
|
|
|
|
|
2017-11-01 15:45:42 +08:00
|
|
|
len = RTA_PAYLOAD(opt) - RTA_ALIGN(sizeof(*qopt));
|
|
|
|
if (len < 0) {
|
|
|
|
fprintf(stderr, "options size error\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-04-12 23:57:27 +08:00
|
|
|
qopt = RTA_DATA(opt);
|
|
|
|
|
|
|
|
fprintf(f, " tc %u map ", qopt->num_tc);
|
|
|
|
for (i = 0; i <= TC_PRIO_MAX; i++)
|
2012-02-15 14:02:33 +08:00
|
|
|
fprintf(f, "%u ", qopt->prio_tc_map[i]);
|
2011-04-12 23:57:27 +08:00
|
|
|
fprintf(f, "\n queues:");
|
|
|
|
for (i = 0; i < qopt->num_tc; i++)
|
2012-02-15 14:02:33 +08:00
|
|
|
fprintf(f, "(%u:%u) ", qopt->offset[i],
|
2011-04-12 23:57:27 +08:00
|
|
|
qopt->offset[i] + qopt->count[i] - 1);
|
2017-11-01 15:45:42 +08:00
|
|
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
2011-04-12 23:57:27 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct qdisc_util mqprio_qdisc_util = {
|
|
|
|
.id = "mqprio",
|
|
|
|
.parse_qopt = mqprio_parse_opt,
|
|
|
|
.print_qopt = mqprio_print_opt,
|
|
|
|
};
|