• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * lib/route/qdisc/mqprio.c             MQPRIO Qdisc/Class
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation version 2.1
7  * of the License.
8  *
9  * Copyright (c) 2018 Volodymyr Bendiuga <volodymyr.bendiuga@westermo.se>
10  */
11 
12 #include <netlink-private/netlink.h>
13 #include <netlink-private/tc.h>
14 #include <netlink/netlink.h>
15 #include <netlink/utils.h>
16 #include <netlink-private/route/tc-api.h>
17 #include <netlink/route/qdisc.h>
18 #include <netlink/route/qdisc/mqprio.h>
19 
20 /** @cond SKIP */
21 #define SCH_MQPRIO_ATTR_NUMTC           (1 << 0)
22 #define SCH_MQPRIO_ATTR_PRIOMAP         (1 << 1)
23 #define SCH_MQPRIO_ATTR_HW              (1 << 2)
24 #define SCH_MQPRIO_ATTR_QUEUE           (1 << 3)
25 #define SCH_MQPRIO_ATTR_MODE            (1 << 4)
26 #define SCH_MQPRIO_ATTR_SHAPER          (1 << 5)
27 #define SCH_MQPRIO_ATTR_MIN_RATE        (1 << 6)
28 #define SCH_MQPRIO_ATTR_MAX_RATE        (1 << 7)
29 /** @endcond */
30 
31 static struct nla_policy mqprio_policy[TCA_MQPRIO_MAX + 1] = {
32 	[TCA_MQPRIO_MODE]       = { .minlen = sizeof(uint16_t) },
33 	[TCA_MQPRIO_SHAPER]     = { .minlen = sizeof(uint16_t) },
34 	[TCA_MQPRIO_MIN_RATE64] = { .type = NLA_NESTED },
35 	[TCA_MQPRIO_MAX_RATE64] = { .type = NLA_NESTED },
36 };
37 
mqprio_msg_parser(struct rtnl_tc * tc,void * data)38 static int mqprio_msg_parser(struct rtnl_tc *tc, void *data)
39 {
40 	struct rtnl_mqprio *mqprio = data;
41 	struct tc_mqprio_qopt *qopt;
42 	struct nlattr *attr;
43 	int len, rem, i, err;
44 
45 	if (tc->tc_opts->d_size < sizeof(*qopt))
46 		return -NLE_INVAL;
47 
48 	qopt = (struct tc_mqprio_qopt *) tc->tc_opts->d_data;
49 	mqprio->qm_num_tc = qopt->num_tc;
50 	mqprio->qm_hw = qopt->hw;
51 	memcpy(mqprio->qm_prio_map, qopt->prio_tc_map,
52 	       TC_QOPT_MAX_QUEUE * sizeof(uint8_t));
53 	memcpy(mqprio->qm_count, qopt->count,
54 	       TC_QOPT_MAX_QUEUE * sizeof(uint16_t));
55 	memcpy(mqprio->qm_offset, qopt->offset,
56 	       TC_QOPT_MAX_QUEUE * sizeof(uint16_t));
57 	mqprio->qm_mask = (SCH_MQPRIO_ATTR_NUMTC | SCH_MQPRIO_ATTR_PRIOMAP |
58 	                   SCH_MQPRIO_ATTR_QUEUE | SCH_MQPRIO_ATTR_HW);
59 
60 	len = tc->tc_opts->d_size - NLA_ALIGN(sizeof(*qopt));
61 
62 	if (len > 0) {
63 		struct nlattr *tb[TCA_MQPRIO_MAX + 1];
64 
65 		err = nla_parse(tb, TCA_MQPRIO_MAX, (struct nlattr *)
66 		                ((char *) tc->tc_opts->d_data + NLA_ALIGN(sizeof(*qopt))),
67 		                len, mqprio_policy);
68 		if (err < 0)
69 			return err;
70 
71 		if (tb[TCA_MQPRIO_MODE]) {
72 			mqprio->qm_mode = nla_get_u16(tb[TCA_MQPRIO_MODE]);
73 			mqprio->qm_mask |= SCH_MQPRIO_ATTR_MODE;
74 		}
75 
76 		if (tb[TCA_MQPRIO_SHAPER]) {
77 			mqprio->qm_shaper = nla_get_u16(tb[TCA_MQPRIO_SHAPER]);
78 			mqprio->qm_mask |= SCH_MQPRIO_ATTR_SHAPER;
79 		}
80 
81 		if (tb[TCA_MQPRIO_MIN_RATE64]) {
82 			i = 0;
83 			nla_for_each_nested(attr, tb[TCA_MQPRIO_MIN_RATE64], rem) {
84 				if (nla_type(attr) != TCA_MQPRIO_MIN_RATE64)
85 					return -EINVAL;
86 
87 				if (i >= mqprio->qm_num_tc)
88 					break;
89 
90 				mqprio->qm_min_rate[i] = nla_get_u64(attr);
91 			}
92 
93 			mqprio->qm_mask |= SCH_MQPRIO_ATTR_MIN_RATE;
94 		}
95 
96 		if (tb[TCA_MQPRIO_MAX_RATE64]) {
97 			i = 0;
98 			nla_for_each_nested(attr, tb[TCA_MQPRIO_MAX_RATE64], rem) {
99 				if (nla_type(attr) != TCA_MQPRIO_MAX_RATE64)
100 					return -EINVAL;
101 
102 				if (i >= mqprio->qm_num_tc)
103 					break;
104 
105 				mqprio->qm_max_rate[i] = nla_get_u64(attr);
106 			}
107 
108 			mqprio->qm_mask |= SCH_MQPRIO_ATTR_MAX_RATE;
109 		}
110 	}
111 
112 	return 0;
113 }
114 
mqprio_msg_fill(struct rtnl_tc * tc,void * data,struct nl_msg * msg)115 static int mqprio_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
116 {
117 	struct rtnl_mqprio *mqprio = data;
118 	struct tc_mqprio_qopt qopt = { 0 };
119 	struct nlattr *nest = NULL;
120 	int i;
121 
122 	if (!mqprio ||
123 	    !(mqprio->qm_mask & SCH_MQPRIO_ATTR_NUMTC) ||
124 	    !(mqprio->qm_mask & SCH_MQPRIO_ATTR_PRIOMAP) ||
125 	    !(mqprio->qm_mask & SCH_MQPRIO_ATTR_QUEUE))
126 		return -NLE_INVAL;
127 
128 	if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_HW))
129 		qopt.hw = 0;
130 	else
131 		qopt.hw = mqprio->qm_hw;
132 
133 	qopt.num_tc = mqprio->qm_num_tc;
134 	memcpy(qopt.count, mqprio->qm_count, TC_QOPT_MAX_QUEUE * sizeof(uint16_t));
135 	memcpy(qopt.offset, mqprio->qm_offset, TC_QOPT_MAX_QUEUE * sizeof(uint16_t));
136 	memcpy(qopt.prio_tc_map, mqprio->qm_prio_map, TC_QOPT_MAX_QUEUE * sizeof(uint8_t));
137 
138 	nlmsg_append(msg, &qopt, sizeof(qopt), NL_DONTPAD);
139 
140 	if (mqprio->qm_hw) {
141 		if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MODE)
142 			NLA_PUT_U16(msg, TCA_MQPRIO_MODE, mqprio->qm_mode);
143 
144 		if (mqprio->qm_mask & SCH_MQPRIO_ATTR_SHAPER)
145 			NLA_PUT_U16(msg, TCA_MQPRIO_SHAPER, mqprio->qm_shaper);
146 
147 		if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MIN_RATE) {
148 			nest = nla_nest_start(msg, TCA_MQPRIO_MIN_RATE64);
149 			if (!nest)
150 				goto nla_put_failure;
151 
152 			for (i = 0; i < mqprio->qm_num_tc; i++) {
153 				if (nla_put(msg, TCA_MQPRIO_MIN_RATE64,
154 				            sizeof(mqprio->qm_min_rate[i]),
155 				            &mqprio->qm_min_rate[i]) < 0)
156 					goto nla_nest_cancel;
157 			}
158 			nla_nest_end(msg, nest);
159 		}
160 
161 		if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MAX_RATE) {
162 			nest = nla_nest_start(msg, TCA_MQPRIO_MAX_RATE64);
163 			if (!nest)
164 				goto nla_put_failure;
165 
166 			for (i = 0; i < mqprio->qm_num_tc; i++) {
167 				if (nla_put(msg, TCA_MQPRIO_MAX_RATE64,
168 				            sizeof(mqprio->qm_max_rate[i]),
169 				            &mqprio->qm_max_rate[i]) < 0)
170 					goto nla_nest_cancel;
171 			}
172 			nla_nest_end(msg, nest);
173 		}
174 	}
175 
176 	return 0;
177 
178 nla_nest_cancel:
179 	nla_nest_cancel(msg, nest);
180 	return -NLE_MSGSIZE;
181 
182 nla_put_failure:
183 	return -NLE_MSGSIZE;
184 }
185 
mqprio_dump_line(struct rtnl_tc * tc,void * data,struct nl_dump_params * p)186 static void mqprio_dump_line(struct rtnl_tc *tc, void *data,
187                              struct nl_dump_params *p)
188 {
189 	struct rtnl_mqprio *mqprio = data;
190 
191 	if (mqprio)
192 		nl_dump(p, " num_tc %u", mqprio->qm_num_tc);
193 }
194 
mqprio_dump_details(struct rtnl_tc * tc,void * data,struct nl_dump_params * p)195 static void mqprio_dump_details(struct rtnl_tc *tc, void *data,
196                                 struct nl_dump_params *p)
197 {
198 	struct rtnl_mqprio *mqprio = data;
199 	int i;
200 
201 	if (!mqprio)
202 		return;
203 
204 	nl_dump(p, "map [");
205 
206 	for (i = 0; i <= TC_QOPT_BITMASK; i++)
207 		nl_dump(p, "%u%s", mqprio->qm_prio_map[i],
208 			i < TC_QOPT_BITMASK ? " " : "");
209 
210 	nl_dump(p, "]\n");
211 	nl_new_line(p);
212 }
213 
214 /**
215  * @name Attribute Modification
216  * @{
217  */
218 
219 /**
220  * Set number of traffic classes.
221  * @arg qdisc           MQPRIO qdisc to be modified.
222  * @arg num_tc          Number of traffic classes to create.
223  * @return 0 on success or a negative error code.
224  */
rtnl_qdisc_mqprio_set_num_tc(struct rtnl_qdisc * qdisc,int num_tc)225 int rtnl_qdisc_mqprio_set_num_tc(struct rtnl_qdisc *qdisc, int num_tc)
226 {
227 	struct rtnl_mqprio *mqprio;
228 
229 	if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc))))
230 		return -NLE_NOMEM;
231 
232 	mqprio->qm_num_tc = num_tc;
233 	mqprio->qm_mask |= SCH_MQPRIO_ATTR_NUMTC;
234 	return 0;
235 }
236 
237 /**
238  * Get number of traffic classes of MQPRIO qdisc.
239  * @arg qdisc           MQPRIO qdisc.
240  * @return Number of traffic classes or a negative error code.
241  */
rtnl_qdisc_mqprio_get_num_tc(struct rtnl_qdisc * qdisc)242 int rtnl_qdisc_mqprio_get_num_tc(struct rtnl_qdisc *qdisc)
243 {
244 	struct rtnl_mqprio *mqprio;
245 
246 	if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc))))
247 		return -NLE_INVAL;
248 
249 	if (mqprio->qm_mask & SCH_MQPRIO_ATTR_NUMTC)
250 		return mqprio->qm_num_tc;
251 	else
252 		return -NLE_MISSING_ATTR;
253 }
254 
255 /**
256  * Set priomap of the MQPRIO qdisc.
257  * @arg qdisc           MQPRIO qdisc to be modified.
258  * @arg priomap         New priority mapping.
259  * @arg len             Length of priomap (# of elements).
260  * @return 0 on success or a negative error code.
261  */
rtnl_qdisc_mqprio_set_priomap(struct rtnl_qdisc * qdisc,uint8_t priomap[],int len)262 int rtnl_qdisc_mqprio_set_priomap(struct rtnl_qdisc *qdisc, uint8_t priomap[],
263                                 int len)
264 {
265 	struct rtnl_mqprio *mqprio;
266 	int i;
267 
268 	if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc))))
269 		return -NLE_NOMEM;
270 
271 	if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_NUMTC))
272 		return -NLE_MISSING_ATTR;
273 
274 	if ((len / sizeof(uint8_t)) > (TC_QOPT_BITMASK+1))
275 		return -NLE_RANGE;
276 
277 	for (i = 0; i <= TC_QOPT_BITMASK; i++) {
278 		if (priomap[i] > mqprio->qm_num_tc)
279 			return -NLE_RANGE;
280 	}
281 
282 	memcpy(mqprio->qm_prio_map, priomap, len * sizeof(uint8_t));
283 	mqprio->qm_mask |= SCH_MQPRIO_ATTR_PRIOMAP;
284 
285 	return 0;
286 }
287 
288 /**
289  * Get priomap of MQPRIO qdisc.
290  * @arg qdisc           MQPRIO qdisc.
291  * @return Priority mapping as array of size TC_QOPT_BANDS+1
292  *         or NULL if an error occured.
293  */
rtnl_qdisc_mqprio_get_priomap(struct rtnl_qdisc * qdisc)294 uint8_t *rtnl_qdisc_mqprio_get_priomap(struct rtnl_qdisc *qdisc)
295 {
296 	struct rtnl_mqprio *mqprio;
297 
298 	if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc))))
299 		return NULL;
300 
301 	if (mqprio->qm_mask & SCH_MQPRIO_ATTR_PRIOMAP)
302 		return mqprio->qm_prio_map;
303 	else
304 		return NULL;
305 }
306 
307 /**
308  * Offload to HW or run in SW (default).
309  * @arg qdisc           MQPRIO qdisc to be modified.
310  * @arg offload         1 - offload to HW, 0 - run in SW only (default).
311  * @return 0 on success or a negative error code.
312  */
rtnl_qdisc_mqprio_hw_offload(struct rtnl_qdisc * qdisc,int offload)313 int rtnl_qdisc_mqprio_hw_offload(struct rtnl_qdisc *qdisc, int offload)
314 {
315 	struct rtnl_mqprio *mqprio;
316 
317 	if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc))))
318 		return -NLE_NOMEM;
319 
320 	switch (offload) {
321 	case 0:
322 	case 1:
323 		mqprio->qm_hw = offload;
324 		break;
325 	default:
326 		return -NLE_INVAL;
327 	}
328 
329 	mqprio->qm_mask |= SCH_MQPRIO_ATTR_HW;
330 	return 0;
331 }
332 
333 /**
334  * Check whether running in HW or SW.
335  * @arg qdisc           MQPRIO qdisc to be modified.
336  * @return 0 if running in SW, otherwise 1 (HW)
337  */
rtnl_qdisc_mqprio_get_hw_offload(struct rtnl_qdisc * qdisc)338 int rtnl_qdisc_mqprio_get_hw_offload(struct rtnl_qdisc *qdisc)
339 {
340 	struct rtnl_mqprio *mqprio;
341 
342 	if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc))))
343 		return -NLE_INVAL;
344 
345 	if (mqprio->qm_mask & SCH_MQPRIO_ATTR_HW)
346 		return mqprio->qm_hw;
347 
348 	return 0;
349 }
350 
351 /**
352  * Set tc queue of the MQPRIO qdisc.
353  * @arg qdisc           MQPRIO qdisc to be modified.
354  * @arg count           count of queue range for each traffic class
355  * @arg offset          offset of queue range for each traffic class
356  * @return 0 on success or a negative error code.
357  */
rtnl_qdisc_mqprio_set_queue(struct rtnl_qdisc * qdisc,uint16_t count[],uint16_t offset[],int len)358 int rtnl_qdisc_mqprio_set_queue(struct rtnl_qdisc *qdisc, uint16_t count[],
359                                 uint16_t offset[], int len)
360 {
361 	struct rtnl_mqprio *mqprio;
362 
363 	if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc))))
364 		return -NLE_NOMEM;
365 
366 	if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_NUMTC))
367 		return -NLE_MISSING_ATTR;
368 
369 	if ((len / sizeof(uint16_t)) > TC_QOPT_MAX_QUEUE)
370 		return -NLE_RANGE;
371 
372 	memcpy(mqprio->qm_count, count, len * sizeof(uint16_t));
373 	memcpy(mqprio->qm_offset, offset, len * sizeof(uint16_t));
374 	mqprio->qm_mask |= SCH_MQPRIO_ATTR_QUEUE;
375 
376 	return 0;
377 }
378 
379 /**
380  * Get tc queue of the MQPRIO qdisc.
381  * @arg qdisc           MQPRIO qdisc to be modified.
382  * @arg count           count of queue range for each traffic class
383  * @arg offset          offset of queue range for each traffic class
384  * @return 0 on success or a negative error code.
385  */
rtnl_qdisc_mqprio_get_queue(struct rtnl_qdisc * qdisc,uint16_t * count,uint16_t * offset)386 int rtnl_qdisc_mqprio_get_queue(struct rtnl_qdisc *qdisc, uint16_t *count,
387                                 uint16_t *offset)
388 {
389 	struct rtnl_mqprio *mqprio;
390 
391 	if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc))))
392 		return -NLE_INVAL;
393 
394 	if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_QUEUE))
395 		return -NLE_MISSING_ATTR;
396 
397 	memcpy(count, mqprio->qm_count, TC_QOPT_MAX_QUEUE * sizeof(uint16_t));
398 	memcpy(offset, mqprio->qm_offset, TC_QOPT_MAX_QUEUE * sizeof(uint16_t));
399 
400 	return 0;
401 }
402 
403 /**
404  * Set mode of mqprio Qdisc
405  * @arg qdisc           MQPRIO qdisc to be modified.
406  * @arg mode            one of: TC_MQPRIO_MODE_DCB, TC_MQPRIO_MODE_CHANNEL
407  * @return 0 on success or a negative error code.
408  */
rtnl_qdisc_mqprio_set_mode(struct rtnl_qdisc * qdisc,uint16_t mode)409 int rtnl_qdisc_mqprio_set_mode(struct rtnl_qdisc *qdisc, uint16_t mode)
410 {
411 	struct rtnl_mqprio *mqprio;
412 
413 	if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc))))
414 		return -NLE_NOMEM;
415 
416 	if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_HW))
417 		return -NLE_MISSING_ATTR;
418 
419 	mqprio->qm_mode = mode;
420 	mqprio->qm_mask |= SCH_MQPRIO_ATTR_MODE;
421 
422 	return 0;
423 }
424 
425 /**
426  * Get mode of mqprio Qdisc
427  * @arg qdisc           MQPRIO qdisc.
428  * @return mode on success or negative error code.
429  */
rtnl_qdisc_mqprio_get_mode(struct rtnl_qdisc * qdisc)430 int rtnl_qdisc_mqprio_get_mode(struct rtnl_qdisc *qdisc)
431 {
432 	struct rtnl_mqprio *mqprio;
433 
434 	if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc))))
435 		return -NLE_INVAL;
436 
437 	if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MODE)
438 		return mqprio->qm_mode;
439 	else
440 		return -NLE_MISSING_ATTR;
441 }
442 
443 /**
444  * Set shaper of mqprio Qdisc
445  * @arg qdisc           MQPRIO qdisc to be modified.
446  * @arg shaper          one of: TC_MQPRIO_SHAPER_DCB, TC_MQPRIO_SHAPER_BW_RATE
447  * @return 0 on success or a negative error code.
448  */
rtnl_qdisc_mqprio_set_shaper(struct rtnl_qdisc * qdisc,uint16_t shaper)449 int rtnl_qdisc_mqprio_set_shaper(struct rtnl_qdisc *qdisc, uint16_t shaper)
450 {
451 	struct rtnl_mqprio *mqprio;
452 
453 	if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc))))
454 		return -NLE_NOMEM;
455 
456 	if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_HW))
457 		return -NLE_MISSING_ATTR;
458 
459 	mqprio->qm_shaper = shaper;
460 	mqprio->qm_mask |= SCH_MQPRIO_ATTR_SHAPER;
461 
462 	return 0;
463 }
464 
465 /**
466  * Get shaper of mqprio Qdisc
467  * @arg qdisc           MQPRIO qdisc.
468  * @return shaper on success or negative error code.
469  */
rtnl_qdisc_mqprio_get_shaper(struct rtnl_qdisc * qdisc)470 int rtnl_qdisc_mqprio_get_shaper(struct rtnl_qdisc *qdisc)
471 {
472 	struct rtnl_mqprio *mqprio;
473 
474 	if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc))))
475 		return -NLE_INVAL;
476 
477 	if (mqprio->qm_mask & SCH_MQPRIO_ATTR_SHAPER)
478 		return mqprio->qm_shaper;
479 	else
480 		return -NLE_MISSING_ATTR;
481 }
482 
483 /**
484  * Set minimum value of bandwidth rate limit for each traffic class
485  * @arg qdisc           MQPRIO qdisc.
486  * @arg min             minimum rate for each traffic class
487  * @return 0 on success or a negative error code.
488  */
rtnl_qdisc_mqprio_set_min_rate(struct rtnl_qdisc * qdisc,uint64_t min[],int len)489 int rtnl_qdisc_mqprio_set_min_rate(struct rtnl_qdisc *qdisc, uint64_t min[], int len)
490 {
491 	struct rtnl_mqprio *mqprio;
492 
493 	if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc))))
494 		return -NLE_NOMEM;
495 
496 	if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_SHAPER))
497 		return -NLE_MISSING_ATTR;
498 
499 	if (mqprio->qm_shaper != TC_MQPRIO_SHAPER_BW_RATE)
500 		return -NLE_INVAL;
501 
502 	if ((len / sizeof(uint64_t)) > TC_QOPT_MAX_QUEUE)
503 		return -NLE_RANGE;
504 
505 	memcpy(mqprio->qm_min_rate, min, len * sizeof(uint64_t));
506 	mqprio->qm_mask |= SCH_MQPRIO_ATTR_MIN_RATE;
507 
508 	return 0;
509 }
510 
511 /**
512  * Get minimum value of bandwidth rate limit for each traffic class
513  * @arg qdisc           MQPRIO qdisc.
514  * @arg min             minimum rate for each traffic class
515  * @return 0 on success or a negative error code.
516  */
rtnl_qdisc_mqprio_get_min_rate(struct rtnl_qdisc * qdisc,uint64_t * min)517 int rtnl_qdisc_mqprio_get_min_rate(struct rtnl_qdisc *qdisc, uint64_t *min)
518 {
519 	struct rtnl_mqprio *mqprio;
520 
521 	if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc))))
522 		return -NLE_INVAL;
523 
524 	if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MIN_RATE) {
525 		memcpy(min, mqprio->qm_min_rate, TC_QOPT_MAX_QUEUE * sizeof(uint64_t));
526 		return 0;
527 	}
528 
529 	return -NLE_MISSING_ATTR;
530 }
531 
532 /**
533  * Set maximum value of bandwidth rate limit for each traffic class
534  * @arg qdisc           MQPRIO qdisc.
535  * @arg max             maximum rate for each traffic class
536  * @return 0 on success or a negative error code.
537  */
rtnl_qdisc_mqprio_set_max_rate(struct rtnl_qdisc * qdisc,uint64_t max[],int len)538 int rtnl_qdisc_mqprio_set_max_rate(struct rtnl_qdisc *qdisc, uint64_t max[], int len)
539 {
540 	struct rtnl_mqprio *mqprio;
541 
542 	if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc))))
543 		return -NLE_NOMEM;
544 
545 	if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_SHAPER))
546 		return -NLE_MISSING_ATTR;
547 
548 	if (mqprio->qm_shaper != TC_MQPRIO_SHAPER_BW_RATE)
549 		return -NLE_INVAL;
550 
551 	if ((len / sizeof(uint64_t)) > TC_QOPT_MAX_QUEUE)
552 		return -NLE_RANGE;
553 
554 	memcpy(mqprio->qm_max_rate, max, len * sizeof(uint64_t));
555 	mqprio->qm_mask |= SCH_MQPRIO_ATTR_MAX_RATE;
556 
557 	return 0;
558 }
559 
560 /**
561  * Get maximum value of bandwidth rate limit for each traffic class
562  * @arg qdisc           MQPRIO qdisc.
563  * @arg min             maximum rate for each traffic class
564  * @return 0 on success or a negative error code.
565  */
rtnl_qdisc_mqprio_get_max_rate(struct rtnl_qdisc * qdisc,uint64_t * max)566 int rtnl_qdisc_mqprio_get_max_rate(struct rtnl_qdisc *qdisc, uint64_t *max)
567 {
568 	struct rtnl_mqprio *mqprio;
569 
570 	if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc))))
571 		return -NLE_INVAL;
572 
573 	if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MAX_RATE) {
574 		memcpy(max, mqprio->qm_max_rate, TC_QOPT_MAX_QUEUE * sizeof(uint64_t));
575 		return 0;
576 	}
577 
578 	return -NLE_MISSING_ATTR;
579 }
580 
581 /** @} */
582 
583 static struct rtnl_tc_ops mqprio_ops = {
584 	.to_kind                = "mqprio",
585 	.to_type                = RTNL_TC_TYPE_QDISC,
586 	.to_size                = sizeof(struct rtnl_mqprio),
587 	.to_msg_parser          = mqprio_msg_parser,
588 	.to_dump = {
589 	    [NL_DUMP_LINE]      = mqprio_dump_line,
590 	    [NL_DUMP_DETAILS]   = mqprio_dump_details,
591 	},
592 	.to_msg_fill            = mqprio_msg_fill,
593 };
594 
mqprio_init(void)595 static void __init mqprio_init(void)
596 {
597 	rtnl_tc_register(&mqprio_ops);
598 }
599 
mqprio_exit(void)600 static void __exit mqprio_exit(void)
601 {
602 	rtnl_tc_unregister(&mqprio_ops);
603 }
604 
605 /** @} */
606