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