1
2
3
4
5
6
7
8
9
10
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <unistd.h>
15#include <fcntl.h>
16#include <sys/socket.h>
17#include <netinet/in.h>
18#include <arpa/inet.h>
19#include <string.h>
20
21#include "utils.h"
22#include "tc_util.h"
23
24static void explain(void)
25{
26 fprintf(stderr,
27 "Usage: ... mqprio [num_tc NUMBER] [map P0 P1 ...]\n"
28 " [queues count1@offset1 count2@offset2 ...] "
29 "[hw 1|0]\n"
30 " [mode dcb|channel]\n"
31 " [shaper bw_rlimit SHAPER_PARAMS]\n"
32 "Where: SHAPER_PARAMS := { min_rate MIN_RATE1 MIN_RATE2 ...|\n"
33 " max_rate MAX_RATE1 MAX_RATE2 ... }\n");
34}
35
36static int mqprio_parse_opt(struct qdisc_util *qu, int argc,
37 char **argv, struct nlmsghdr *n, const char *dev)
38{
39 int idx;
40 struct tc_mqprio_qopt opt = {
41 .num_tc = 8,
42 .prio_tc_map = { 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 1, 1, 3, 3, 3, 3 },
43 .hw = 1,
44 .count = { },
45 .offset = { },
46 };
47 __u64 min_rate64[TC_QOPT_MAX_QUEUE] = {0};
48 __u64 max_rate64[TC_QOPT_MAX_QUEUE] = {0};
49 __u16 shaper = TC_MQPRIO_SHAPER_DCB;
50 __u16 mode = TC_MQPRIO_MODE_DCB;
51 int cnt_off_pairs = 0;
52 struct rtattr *tail;
53 __u32 flags = 0;
54
55 while (argc > 0) {
56 idx = 0;
57 if (strcmp(*argv, "num_tc") == 0) {
58 NEXT_ARG();
59 if (get_u8(&opt.num_tc, *argv, 10)) {
60 fprintf(stderr, "Illegal \"num_tc\"\n");
61 return -1;
62 }
63 } else if (strcmp(*argv, "map") == 0) {
64 while (idx < TC_QOPT_MAX_QUEUE && NEXT_ARG_OK()) {
65 NEXT_ARG();
66 if (get_u8(&opt.prio_tc_map[idx], *argv, 10)) {
67 PREV_ARG();
68 break;
69 }
70 idx++;
71 }
72 for ( ; idx < TC_QOPT_MAX_QUEUE; idx++)
73 opt.prio_tc_map[idx] = 0;
74 } else if (strcmp(*argv, "queues") == 0) {
75 char *tmp, *tok;
76
77 while (idx < TC_QOPT_MAX_QUEUE && NEXT_ARG_OK()) {
78 NEXT_ARG();
79
80 tmp = strdup(*argv);
81 if (!tmp)
82 break;
83
84 tok = strtok(tmp, "@");
85 if (get_u16(&opt.count[idx], tok, 10)) {
86 free(tmp);
87 PREV_ARG();
88 break;
89 }
90 tok = strtok(NULL, "@");
91 if (get_u16(&opt.offset[idx], tok, 10)) {
92 free(tmp);
93 PREV_ARG();
94 break;
95 }
96 free(tmp);
97 idx++;
98 cnt_off_pairs++;
99 }
100 } else if (strcmp(*argv, "hw") == 0) {
101 NEXT_ARG();
102 if (get_u8(&opt.hw, *argv, 10)) {
103 fprintf(stderr, "Illegal \"hw\"\n");
104 return -1;
105 }
106 idx++;
107 } else if (opt.hw && strcmp(*argv, "mode") == 0) {
108 NEXT_ARG();
109 if (matches(*argv, "dcb") == 0) {
110 mode = TC_MQPRIO_MODE_DCB;
111 } else if (matches(*argv, "channel") == 0) {
112 mode = TC_MQPRIO_MODE_CHANNEL;
113 } else {
114 fprintf(stderr, "Illegal mode (%s)\n",
115 *argv);
116 return -1;
117 }
118 if (mode != TC_MQPRIO_MODE_DCB)
119 flags |= TC_MQPRIO_F_MODE;
120 idx++;
121 } else if (opt.hw && strcmp(*argv, "shaper") == 0) {
122 NEXT_ARG();
123 if (matches(*argv, "dcb") == 0) {
124 shaper = TC_MQPRIO_SHAPER_DCB;
125 } else if (matches(*argv, "bw_rlimit") == 0) {
126 shaper = TC_MQPRIO_SHAPER_BW_RATE;
127 if (!NEXT_ARG_OK()) {
128 fprintf(stderr, "Incomplete shaper arguments\n");
129 return -1;
130 }
131 } else {
132 fprintf(stderr, "Illegal shaper (%s)\n",
133 *argv);
134 return -1;
135 }
136 if (shaper != TC_MQPRIO_SHAPER_DCB)
137 flags |= TC_MQPRIO_F_SHAPER;
138 idx++;
139 } else if ((shaper == TC_MQPRIO_SHAPER_BW_RATE) &&
140 strcmp(*argv, "min_rate") == 0) {
141 while (idx < TC_QOPT_MAX_QUEUE && NEXT_ARG_OK()) {
142 NEXT_ARG();
143 if (get_rate64(&min_rate64[idx], *argv)) {
144 PREV_ARG();
145 break;
146 }
147 idx++;
148 }
149 if (idx < opt.num_tc && !NEXT_ARG_OK()) {
150 fprintf(stderr, "Incomplete arguments, min_rate values expected\n");
151 return -1;
152 }
153 flags |= TC_MQPRIO_F_MIN_RATE;
154 } else if ((shaper == TC_MQPRIO_SHAPER_BW_RATE) &&
155 strcmp(*argv, "max_rate") == 0) {
156 while (idx < TC_QOPT_MAX_QUEUE && NEXT_ARG_OK()) {
157 NEXT_ARG();
158 if (get_rate64(&max_rate64[idx], *argv)) {
159 PREV_ARG();
160 break;
161 }
162 idx++;
163 }
164 if (idx < opt.num_tc && !NEXT_ARG_OK()) {
165 fprintf(stderr, "Incomplete arguments, max_rate values expected\n");
166 return -1;
167 }
168 flags |= TC_MQPRIO_F_MAX_RATE;
169 } else if (strcmp(*argv, "help") == 0) {
170 explain();
171 return -1;
172 } else {
173 invarg("unknown argument", *argv);
174 }
175 argc--; argv++;
176 }
177
178 if (cnt_off_pairs > opt.num_tc) {
179 fprintf(stderr, "queues count/offset pair count %d can not be higher than given num_tc %d\n",
180 cnt_off_pairs, opt.num_tc);
181 return -1;
182 }
183
184 tail = NLMSG_TAIL(n);
185 addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt));
186
187 if (flags & TC_MQPRIO_F_MODE)
188 addattr_l(n, 1024, TCA_MQPRIO_MODE,
189 &mode, sizeof(mode));
190 if (flags & TC_MQPRIO_F_SHAPER)
191 addattr_l(n, 1024, TCA_MQPRIO_SHAPER,
192 &shaper, sizeof(shaper));
193
194 if (flags & TC_MQPRIO_F_MIN_RATE) {
195 struct rtattr *start;
196
197 start = addattr_nest(n, 1024,
198 TCA_MQPRIO_MIN_RATE64 | NLA_F_NESTED);
199
200 for (idx = 0; idx < TC_QOPT_MAX_QUEUE; idx++)
201 addattr_l(n, 1024, TCA_MQPRIO_MIN_RATE64,
202 &min_rate64[idx], sizeof(min_rate64[idx]));
203
204 addattr_nest_end(n, start);
205 }
206
207 if (flags & TC_MQPRIO_F_MAX_RATE) {
208 struct rtattr *start;
209
210 start = addattr_nest(n, 1024,
211 TCA_MQPRIO_MAX_RATE64 | NLA_F_NESTED);
212
213 for (idx = 0; idx < TC_QOPT_MAX_QUEUE; idx++)
214 addattr_l(n, 1024, TCA_MQPRIO_MAX_RATE64,
215 &max_rate64[idx], sizeof(max_rate64[idx]));
216
217 addattr_nest_end(n, start);
218 }
219
220 tail->rta_len = (void *)NLMSG_TAIL(n) - (void *)tail;
221
222 return 0;
223}
224
225static int mqprio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
226{
227 int i;
228 struct tc_mqprio_qopt *qopt;
229 __u64 min_rate64[TC_QOPT_MAX_QUEUE] = {0};
230 __u64 max_rate64[TC_QOPT_MAX_QUEUE] = {0};
231 int len;
232
233 SPRINT_BUF(b1);
234
235 if (opt == NULL)
236 return 0;
237
238 len = RTA_PAYLOAD(opt) - RTA_ALIGN(sizeof(*qopt));
239 if (len < 0) {
240 fprintf(stderr, "options size error\n");
241 return -1;
242 }
243
244 qopt = RTA_DATA(opt);
245
246 print_uint(PRINT_ANY, "tc", "tc %u ", qopt->num_tc);
247 open_json_array(PRINT_ANY, is_json_context() ? "map" : "map ");
248 for (i = 0; i <= TC_PRIO_MAX; i++)
249 print_uint(PRINT_ANY, NULL, "%u ", qopt->prio_tc_map[i]);
250 close_json_array(PRINT_ANY, "");
251 open_json_array(PRINT_ANY, is_json_context() ? "queues" : "\n queues:");
252 for (i = 0; i < qopt->num_tc; i++) {
253 open_json_array(PRINT_JSON, NULL);
254 print_uint(PRINT_ANY, NULL, "(%u:", qopt->offset[i]);
255 print_uint(PRINT_ANY, NULL, "%u) ", qopt->offset[i] + qopt->count[i] - 1);
256 close_json_array(PRINT_JSON, NULL);
257 }
258 close_json_array(PRINT_ANY, "");
259
260 if (len > 0) {
261 struct rtattr *tb[TCA_MQPRIO_MAX + 1];
262
263 parse_rtattr(tb, TCA_MQPRIO_MAX,
264 RTA_DATA(opt) + RTA_ALIGN(sizeof(*qopt)),
265 len);
266
267 if (tb[TCA_MQPRIO_MODE]) {
268 __u16 *mode = RTA_DATA(tb[TCA_MQPRIO_MODE]);
269
270 if (*mode == TC_MQPRIO_MODE_CHANNEL)
271 print_string(PRINT_ANY, "mode", "\n mode:%s", "channel");
272 } else {
273 print_string(PRINT_ANY, "mode", "\n mode:%s", "dcb");
274 }
275
276 if (tb[TCA_MQPRIO_SHAPER]) {
277 __u16 *shaper = RTA_DATA(tb[TCA_MQPRIO_SHAPER]);
278
279 if (*shaper == TC_MQPRIO_SHAPER_BW_RATE)
280 print_string(PRINT_ANY, "shaper", "\n shaper:%s", "bw_rlimit");
281 } else {
282 print_string(PRINT_ANY, "shaper", "\n shaper:%s", "dcb");
283 }
284
285 if (tb[TCA_MQPRIO_MIN_RATE64]) {
286 struct rtattr *r;
287 int rem = RTA_PAYLOAD(tb[TCA_MQPRIO_MIN_RATE64]);
288 __u64 *min = min_rate64;
289
290 for (r = RTA_DATA(tb[TCA_MQPRIO_MIN_RATE64]);
291 RTA_OK(r, rem); r = RTA_NEXT(r, rem)) {
292 if (r->rta_type != TCA_MQPRIO_MIN_RATE64)
293 return -1;
294 *(min++) = rta_getattr_u64(r);
295 }
296 open_json_array(PRINT_ANY, is_json_context() ? "min_rate" : " min_rate:");
297 for (i = 0; i < qopt->num_tc; i++)
298 print_string(PRINT_ANY, NULL, "%s ", sprint_rate(min_rate64[i], b1));
299 close_json_array(PRINT_ANY, "");
300 }
301
302 if (tb[TCA_MQPRIO_MAX_RATE64]) {
303 struct rtattr *r;
304 int rem = RTA_PAYLOAD(tb[TCA_MQPRIO_MAX_RATE64]);
305 __u64 *max = max_rate64;
306
307 for (r = RTA_DATA(tb[TCA_MQPRIO_MAX_RATE64]);
308 RTA_OK(r, rem); r = RTA_NEXT(r, rem)) {
309 if (r->rta_type != TCA_MQPRIO_MAX_RATE64)
310 return -1;
311 *(max++) = rta_getattr_u64(r);
312 }
313 open_json_array(PRINT_ANY, is_json_context() ? "max_rate" : " max_rate:");
314 for (i = 0; i < qopt->num_tc; i++)
315 print_string(PRINT_ANY, NULL, "%s ", sprint_rate(max_rate64[i], b1));
316 close_json_array(PRINT_ANY, "");
317 }
318 }
319 return 0;
320}
321
322struct qdisc_util mqprio_qdisc_util = {
323 .id = "mqprio",
324 .parse_qopt = mqprio_parse_opt,
325 .print_qopt = mqprio_print_opt,
326};
327