1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3 * Copyright (c) 2003-2011 Thomas Graf <[email protected]>
4 */
5
6 /**
7 * @ingroup qdisc
8 * @defgroup qdisc_prio (Fast) Prio
9 * @brief
10 *
11 * @par 1) Typical PRIO configuration
12 * @code
13 * // Specify the maximal number of bands to be used for this PRIO qdisc.
14 * rtnl_qdisc_prio_set_bands(qdisc, QDISC_PRIO_DEFAULT_BANDS);
15 *
16 * // Provide a map assigning each priority to a band number.
17 * uint8_t map[] = QDISC_PRIO_DEFAULT_PRIOMAP;
18 * rtnl_qdisc_prio_set_priomap(qdisc, map, sizeof(map));
19 * @endcode
20 * @{
21 */
22
23 #include "nl-default.h"
24
25 #include <netlink/netlink.h>
26 #include <netlink/utils.h>
27 #include <netlink/route/qdisc.h>
28 #include <netlink/route/qdisc/prio.h>
29
30 #include "tc-api.h"
31
32 /** @cond SKIP */
33 struct rtnl_prio {
34 uint32_t qp_bands;
35 uint8_t qp_priomap[TC_PRIO_MAX + 1];
36 uint32_t qp_mask;
37 };
38
39 #define SCH_PRIO_ATTR_BANDS 1
40 #define SCH_PRIO_ATTR_PRIOMAP 2
41 /** @endcond */
42
prio_msg_parser(struct rtnl_tc * tc,void * data)43 static int prio_msg_parser(struct rtnl_tc *tc, void *data)
44 {
45 struct rtnl_prio *prio = data;
46 struct tc_prio_qopt *opt;
47
48 if (tc->tc_opts->d_size < sizeof(*opt))
49 return -NLE_INVAL;
50
51 opt = (struct tc_prio_qopt *) tc->tc_opts->d_data;
52 prio->qp_bands = opt->bands;
53 memcpy(prio->qp_priomap, opt->priomap, sizeof(prio->qp_priomap));
54 prio->qp_mask = (SCH_PRIO_ATTR_BANDS | SCH_PRIO_ATTR_PRIOMAP);
55
56 return 0;
57 }
58
prio_dump_line(struct rtnl_tc * tc,void * data,struct nl_dump_params * p)59 static void prio_dump_line(struct rtnl_tc *tc, void *data,
60 struct nl_dump_params *p)
61 {
62 struct rtnl_prio *prio = data;
63
64 if (prio)
65 nl_dump(p, " bands %u", prio->qp_bands);
66 }
67
prio_dump_details(struct rtnl_tc * tc,void * data,struct nl_dump_params * p)68 static void prio_dump_details(struct rtnl_tc *tc, void *data,
69 struct nl_dump_params *p)
70 {
71 struct rtnl_prio *prio = data;
72 int i, hp;
73
74 if (!prio)
75 return;
76
77 nl_dump(p, "priomap [");
78
79 for (i = 0; i <= TC_PRIO_MAX; i++)
80 nl_dump(p, "%u%s", prio->qp_priomap[i],
81 i < TC_PRIO_MAX ? " " : "");
82
83 nl_dump(p, "]\n");
84 nl_new_line(p);
85
86 hp = (((TC_PRIO_MAX/2) + 1) & ~1);
87
88 for (i = 0; i < hp; i++) {
89 char a[32];
90 nl_dump(p, " %18s => %u",
91 rtnl_prio2str(i, a, sizeof(a)),
92 prio->qp_priomap[i]);
93 if (hp+i <= TC_PRIO_MAX) {
94 nl_dump(p, " %18s => %u",
95 rtnl_prio2str(hp+i, a, sizeof(a)),
96 prio->qp_priomap[hp+i]);
97 if (i < (hp - 1)) {
98 nl_dump(p, "\n");
99 nl_new_line(p);
100 }
101 }
102 }
103 }
104
prio_msg_fill(struct rtnl_tc * tc,void * data,struct nl_msg * msg)105 static int prio_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
106 {
107 struct rtnl_prio *prio = data;
108 struct tc_prio_qopt opts;
109
110 if (!prio || !(prio->qp_mask & SCH_PRIO_ATTR_PRIOMAP))
111 BUG();
112
113 opts.bands = prio->qp_bands;
114 memcpy(opts.priomap, prio->qp_priomap, sizeof(opts.priomap));
115
116 return nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD);
117 }
118
119 /**
120 * @name Attribute Modification
121 * @{
122 */
123
124 /**
125 * Set number of bands of PRIO qdisc.
126 * @arg qdisc PRIO qdisc to be modified.
127 * @arg bands New number of bands.
128 * @return 0 on success or a negative error code.
129 */
rtnl_qdisc_prio_set_bands(struct rtnl_qdisc * qdisc,int bands)130 void rtnl_qdisc_prio_set_bands(struct rtnl_qdisc *qdisc, int bands)
131 {
132 struct rtnl_prio *prio;
133
134 if (!(prio = rtnl_tc_data(TC_CAST(qdisc))))
135 BUG();
136
137 prio->qp_bands = bands;
138 prio->qp_mask |= SCH_PRIO_ATTR_BANDS;
139 }
140
141 /**
142 * Get number of bands of PRIO qdisc.
143 * @arg qdisc PRIO qdisc.
144 * @return Number of bands or a negative error code.
145 */
rtnl_qdisc_prio_get_bands(struct rtnl_qdisc * qdisc)146 int rtnl_qdisc_prio_get_bands(struct rtnl_qdisc *qdisc)
147 {
148 struct rtnl_prio *prio;
149
150 if (!(prio = rtnl_tc_data(TC_CAST(qdisc))))
151 BUG();
152
153 if (prio->qp_mask & SCH_PRIO_ATTR_BANDS)
154 return prio->qp_bands;
155 else
156 return -NLE_NOMEM;
157 }
158
159 /**
160 * Set priomap of the PRIO qdisc.
161 * @arg qdisc PRIO qdisc to be modified.
162 * @arg priomap New priority mapping.
163 * @arg len Length of priomap (# of elements).
164 * @return 0 on success or a negative error code.
165 */
rtnl_qdisc_prio_set_priomap(struct rtnl_qdisc * qdisc,uint8_t priomap[],int len)166 int rtnl_qdisc_prio_set_priomap(struct rtnl_qdisc *qdisc, uint8_t priomap[],
167 int len)
168 {
169 struct rtnl_prio *prio;
170 int i;
171
172 if (!(prio = rtnl_tc_data(TC_CAST(qdisc))))
173 BUG();
174
175 if (!(prio->qp_mask & SCH_PRIO_ATTR_BANDS))
176 return -NLE_MISSING_ATTR;
177
178 if ((len / sizeof(uint8_t)) > (TC_PRIO_MAX+1))
179 return -NLE_RANGE;
180
181 for (i = 0; i <= TC_PRIO_MAX; i++) {
182 if (priomap[i] > prio->qp_bands)
183 return -NLE_RANGE;
184 }
185
186 memcpy(prio->qp_priomap, priomap, len);
187 prio->qp_mask |= SCH_PRIO_ATTR_PRIOMAP;
188
189 return 0;
190 }
191
192 /**
193 * Get priomap of a PRIO qdisc.
194 * @arg qdisc PRIO qdisc.
195 * @return Priority mapping as array of size TC_PRIO_MAX+1
196 * or NULL if an error occured.
197 */
rtnl_qdisc_prio_get_priomap(struct rtnl_qdisc * qdisc)198 uint8_t *rtnl_qdisc_prio_get_priomap(struct rtnl_qdisc *qdisc)
199 {
200 struct rtnl_prio *prio;
201
202 if (!(prio = rtnl_tc_data(TC_CAST(qdisc))))
203 BUG();
204
205 if (prio->qp_mask & SCH_PRIO_ATTR_PRIOMAP)
206 return prio->qp_priomap;
207 else
208 return NULL;
209 }
210
211 /** @} */
212
213 /**
214 * @name Priority Band Translations
215 * @{
216 */
217
218 static const struct trans_tbl prios[] = {
219 __ADD(TC_PRIO_BESTEFFORT,besteffort),
220 __ADD(TC_PRIO_FILLER,filler),
221 __ADD(TC_PRIO_BULK,bulk),
222 __ADD(TC_PRIO_INTERACTIVE_BULK,interactive_bulk),
223 __ADD(TC_PRIO_INTERACTIVE,interactive),
224 __ADD(TC_PRIO_CONTROL,control),
225 };
226
227 /**
228 * Convert priority to character string.
229 * @arg prio Priority.
230 * @arg buf Destination buffer
231 * @arg size Size of destination buffer.
232 *
233 * Converts a priority to a character string and stores the result in
234 * the specified destination buffer.
235 *
236 * @return Name of priority as character string.
237 */
rtnl_prio2str(int prio,char * buf,size_t size)238 char * rtnl_prio2str(int prio, char *buf, size_t size)
239 {
240 return __type2str(prio, buf, size, prios, ARRAY_SIZE(prios));
241 }
242
243 /**
244 * Convert character string to priority.
245 * @arg name Name of priority.
246 *
247 * Converts the provided character string specifying a priority
248 * to the corresponding numeric value.
249 *
250 * @return Numeric priority or a negative value if no match was found.
251 */
rtnl_str2prio(const char * name)252 int rtnl_str2prio(const char *name)
253 {
254 return __str2type(name, prios, ARRAY_SIZE(prios));
255 }
256
257 /** @} */
258
259 static struct rtnl_tc_ops prio_ops = {
260 .to_kind = "prio",
261 .to_type = RTNL_TC_TYPE_QDISC,
262 .to_size = sizeof(struct rtnl_prio),
263 .to_msg_parser = prio_msg_parser,
264 .to_dump = {
265 [NL_DUMP_LINE] = prio_dump_line,
266 [NL_DUMP_DETAILS] = prio_dump_details,
267 },
268 .to_msg_fill = prio_msg_fill,
269 };
270
271 static struct rtnl_tc_ops pfifo_fast_ops = {
272 .to_kind = "pfifo_fast",
273 .to_type = RTNL_TC_TYPE_QDISC,
274 .to_size = sizeof(struct rtnl_prio),
275 .to_msg_parser = prio_msg_parser,
276 .to_dump = {
277 [NL_DUMP_LINE] = prio_dump_line,
278 [NL_DUMP_DETAILS] = prio_dump_details,
279 },
280 .to_msg_fill = prio_msg_fill,
281 };
282
prio_init(void)283 static void _nl_init prio_init(void)
284 {
285 rtnl_tc_register(&prio_ops);
286 rtnl_tc_register(&pfifo_fast_ops);
287 }
288
prio_exit(void)289 static void _nl_exit prio_exit(void)
290 {
291 rtnl_tc_unregister(&prio_ops);
292 rtnl_tc_unregister(&pfifo_fast_ops);
293 }
294
295 /** @} */
296