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