1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3 * Copyright (c) 2014 Cong Wang <[email protected]>
4 */
5
6 /**
7 * @ingroup qdisc
8 * @ingroup class
9 * @defgroup qdisc_hfsc Hierarchical Fair Service Curve (HFSC)
10 * @{
11 */
12
13 #include "nl-default.h"
14
15 #include <netlink/netlink.h>
16 #include <netlink/cache.h>
17 #include <netlink/utils.h>
18 #include <netlink/route/qdisc.h>
19 #include <netlink/route/class.h>
20 #include <netlink/route/link.h>
21 #include <netlink/route/qdisc/hfsc.h>
22
23 #include "tc-api.h"
24
25 /** @cond SKIP */
26 struct rtnl_hfsc_qdisc {
27 uint32_t qh_defcls;
28 uint32_t qh_mask;
29 };
30
31 struct rtnl_hfsc_class {
32 struct tc_service_curve ch_rsc;
33 struct tc_service_curve ch_fsc;
34 struct tc_service_curve ch_usc;
35 uint32_t ch_mask;
36 };
37
38 #define SCH_HFSC_CLS_HAS_RSC 0x001
39 #define SCH_HFSC_CLS_HAS_FSC 0x002
40 #define SCH_HFSC_CLS_HAS_USC 0x004
41
42 #define SCH_HFSC_QD_HAS_DEFCLS 0x01
43 /** @endcond */
44
45 static struct nla_policy hfsc_policy[TCA_HFSC_MAX + 1] = {
46 [TCA_HFSC_RSC] = { .minlen = sizeof(struct tc_service_curve) },
47 [TCA_HFSC_FSC] = { .minlen = sizeof(struct tc_service_curve) },
48 [TCA_HFSC_USC] = { .minlen = sizeof(struct tc_service_curve) },
49 };
50
hfsc_qdisc_msg_parser(struct rtnl_tc * tc,void * data)51 static int hfsc_qdisc_msg_parser(struct rtnl_tc *tc, void *data)
52 {
53 struct rtnl_hfsc_qdisc *hfsc = data;
54 struct tc_hfsc_qopt *opts;
55
56 opts = (struct tc_hfsc_qopt *) tc->tc_opts->d_data;
57 hfsc->qh_defcls = opts->defcls;
58 hfsc->qh_mask |= SCH_HFSC_QD_HAS_DEFCLS;
59 return 0;
60 }
61
hfsc_class_msg_parser(struct rtnl_tc * tc,void * data)62 static int hfsc_class_msg_parser(struct rtnl_tc *tc, void *data)
63 {
64 struct nlattr *tb[TCA_HFSC_MAX + 1];
65 struct rtnl_hfsc_class *hfsc = data;
66 int err;
67
68 if ((err = tca_parse(tb, TCA_HFSC_MAX, tc, hfsc_policy)) < 0)
69 return err;
70
71 if (tb[TCA_HFSC_RSC]) {
72 struct tc_service_curve tsc;
73
74 nla_memcpy(&tsc, tb[TCA_HFSC_RSC], sizeof(tsc));
75 hfsc->ch_rsc = tsc;
76 hfsc->ch_mask |= SCH_HFSC_CLS_HAS_RSC;
77 }
78
79 if (tb[TCA_HFSC_FSC]) {
80 struct tc_service_curve tsc;
81
82 nla_memcpy(&tsc, tb[TCA_HFSC_FSC], sizeof(tsc));
83 hfsc->ch_fsc = tsc;
84 hfsc->ch_mask |= SCH_HFSC_CLS_HAS_FSC;
85 }
86
87 if (tb[TCA_HFSC_USC]) {
88 struct tc_service_curve tsc;
89
90 nla_memcpy(&tsc, tb[TCA_HFSC_USC], sizeof(tsc));
91 hfsc->ch_usc = tsc;
92 hfsc->ch_mask |= SCH_HFSC_CLS_HAS_USC;
93 }
94
95 return 0;
96 }
97
hfsc_qdisc_dump_line(struct rtnl_tc * tc,void * data,struct nl_dump_params * p)98 static void hfsc_qdisc_dump_line(struct rtnl_tc *tc, void *data,
99 struct nl_dump_params *p)
100 {
101 struct rtnl_hfsc_qdisc *hfsc = data;
102
103 if (!hfsc)
104 return;
105
106 if (hfsc->qh_mask & SCH_HFSC_QD_HAS_DEFCLS) {
107 char buf[64];
108 nl_dump(p, " default-class %s",
109 rtnl_tc_handle2str(hfsc->qh_defcls, buf, sizeof(buf)));
110 }
111 }
112
hfsc_dump_tsc(struct nl_dump_params * p,struct tc_service_curve * tsc)113 static void hfsc_dump_tsc(struct nl_dump_params *p, struct tc_service_curve *tsc)
114 {
115 nl_dump(p, " m1 %u d %u m2 %u\n", tsc->m1, tsc->d, tsc->m2);
116 }
117
hfsc_class_dump_line(struct rtnl_tc * tc,void * data,struct nl_dump_params * p)118 static void hfsc_class_dump_line(struct rtnl_tc *tc, void *data,
119 struct nl_dump_params *p)
120 {
121 struct rtnl_hfsc_class *hfsc = data;
122
123 if (!hfsc)
124 return;
125 if (hfsc->ch_mask & SCH_HFSC_CLS_HAS_RSC)
126 hfsc_dump_tsc(p, &hfsc->ch_rsc);
127 if (hfsc->ch_mask & SCH_HFSC_CLS_HAS_FSC)
128 hfsc_dump_tsc(p, &hfsc->ch_fsc);
129 if (hfsc->ch_mask & SCH_HFSC_CLS_HAS_USC)
130 hfsc_dump_tsc(p, &hfsc->ch_usc);
131 }
132
hfsc_class_dump_details(struct rtnl_tc * tc,void * data,struct nl_dump_params * p)133 static void hfsc_class_dump_details(struct rtnl_tc *tc, void *data,
134 struct nl_dump_params *p)
135 {
136 return;
137 }
138
hfsc_qdisc_msg_fill(struct rtnl_tc * tc,void * data,struct nl_msg * msg)139 static int hfsc_qdisc_msg_fill(struct rtnl_tc *tc, void *data,
140 struct nl_msg *msg)
141 {
142 struct rtnl_hfsc_qdisc *hfsc = data;
143 struct tc_hfsc_qopt opts = {0};
144
145 if (!hfsc)
146 BUG();
147
148 opts.defcls = hfsc->qh_defcls;
149 return nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD);
150 }
151
hfsc_class_msg_fill(struct rtnl_tc * tc,void * data,struct nl_msg * msg)152 static int hfsc_class_msg_fill(struct rtnl_tc *tc, void *data,
153 struct nl_msg *msg)
154 {
155 struct rtnl_hfsc_class *hfsc = data;
156 struct tc_service_curve tsc;
157
158 if (!hfsc)
159 BUG();
160
161 if (hfsc->ch_mask & SCH_HFSC_CLS_HAS_RSC) {
162 tsc = hfsc->ch_rsc;
163 NLA_PUT(msg, TCA_HFSC_RSC, sizeof(tsc), &tsc);
164 }
165
166 if (hfsc->ch_mask & SCH_HFSC_CLS_HAS_FSC) {
167 tsc = hfsc->ch_fsc;
168 NLA_PUT(msg, TCA_HFSC_FSC, sizeof(tsc), &tsc);
169 }
170
171 if (hfsc->ch_mask & SCH_HFSC_CLS_HAS_USC) {
172 tsc = hfsc->ch_usc;
173 NLA_PUT(msg, TCA_HFSC_USC, sizeof(tsc), &tsc);
174 }
175
176 return 0;
177
178 nla_put_failure:
179 return -NLE_MSGSIZE;
180 }
181
182 static struct rtnl_tc_ops hfsc_qdisc_ops;
183 static struct rtnl_tc_ops hfsc_class_ops;
184
hfsc_qdisc_data(const struct rtnl_qdisc * qdisc,int * err)185 static struct rtnl_hfsc_qdisc *hfsc_qdisc_data(const struct rtnl_qdisc *qdisc, int *err)
186 {
187 return rtnl_tc_data_check(TC_CAST(qdisc), &hfsc_qdisc_ops, err);
188 }
189
hfsc_class_data(const struct rtnl_class * class,int * err)190 static struct rtnl_hfsc_class *hfsc_class_data(const struct rtnl_class *class, int *err)
191 {
192 return rtnl_tc_data_check(TC_CAST(class), &hfsc_class_ops, err);
193 }
194
195 /**
196 * @name Attribute Modifications
197 * @{
198 */
199
200 /**
201 * Return default class of HFSC qdisc
202 * @arg qdisc hfsc qdisc object
203 *
204 * Returns the classid of the class where all unclassified traffic
205 * goes to.
206 *
207 * @return classid or TC_H_UNSPEC if unspecified.
208 */
rtnl_qdisc_hfsc_get_defcls(const struct rtnl_qdisc * qdisc)209 uint32_t rtnl_qdisc_hfsc_get_defcls(const struct rtnl_qdisc *qdisc)
210 {
211 struct rtnl_hfsc_qdisc *hfsc;
212
213 if ((hfsc = hfsc_qdisc_data(qdisc, NULL)) &&
214 (hfsc->qh_mask & SCH_HFSC_QD_HAS_DEFCLS))
215 return hfsc->qh_defcls;
216
217 return TC_H_UNSPEC;
218 }
219
220 /**
221 * Set default class of the hfsc qdisc to the specified value
222 * @arg qdisc qdisc to change
223 * @arg defcls new default class
224 */
rtnl_qdisc_hfsc_set_defcls(struct rtnl_qdisc * qdisc,uint32_t defcls)225 int rtnl_qdisc_hfsc_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls)
226 {
227 struct rtnl_hfsc_qdisc *hfsc;
228 int err;
229
230 if (!(hfsc = hfsc_qdisc_data(qdisc, &err)))
231 return err;
232
233 hfsc->qh_defcls = defcls;
234 hfsc->qh_mask |= SCH_HFSC_QD_HAS_DEFCLS;
235
236 return 0;
237 }
238
rtnl_class_hfsc_get_rsc(const struct rtnl_class * class,struct tc_service_curve * tsc)239 int rtnl_class_hfsc_get_rsc(const struct rtnl_class *class, struct tc_service_curve *tsc)
240 {
241 struct rtnl_hfsc_class *hfsc;
242 int err = -NLE_OPNOTSUPP;
243
244 if ((hfsc = hfsc_class_data(class, &err)) &&
245 (hfsc->ch_mask & SCH_HFSC_CLS_HAS_RSC)) {
246 *tsc = hfsc->ch_rsc;
247 return 0;
248 }
249
250 return err;
251 }
252
rtnl_class_hfsc_set_rsc(struct rtnl_class * class,const struct tc_service_curve * tsc)253 int rtnl_class_hfsc_set_rsc(struct rtnl_class *class, const struct tc_service_curve *tsc)
254 {
255 struct rtnl_hfsc_class *hfsc;
256 int err;
257
258 if (!(hfsc = hfsc_class_data(class, &err)))
259 return err;
260
261 hfsc->ch_rsc = *tsc;
262 hfsc->ch_mask |= SCH_HFSC_CLS_HAS_RSC;
263
264 return 0;
265 }
266
rtnl_class_hfsc_get_fsc(const struct rtnl_class * class,struct tc_service_curve * tsc)267 int rtnl_class_hfsc_get_fsc(const struct rtnl_class *class, struct tc_service_curve *tsc)
268 {
269 struct rtnl_hfsc_class *hfsc;
270 int err = -NLE_OPNOTSUPP;
271
272 if ((hfsc = hfsc_class_data(class, &err)) &&
273 (hfsc->ch_mask & SCH_HFSC_CLS_HAS_FSC)) {
274 *tsc = hfsc->ch_fsc;
275 return 0;
276 }
277
278 return err;
279 }
280
rtnl_class_hfsc_set_fsc(struct rtnl_class * class,const struct tc_service_curve * tsc)281 int rtnl_class_hfsc_set_fsc(struct rtnl_class *class, const struct tc_service_curve *tsc)
282 {
283 struct rtnl_hfsc_class *hfsc;
284 int err;
285
286 if (!(hfsc = hfsc_class_data(class, &err)))
287 return err;
288
289 hfsc->ch_fsc = *tsc;
290 hfsc->ch_mask |= SCH_HFSC_CLS_HAS_FSC;
291
292 return 0;
293 }
294
rtnl_class_hfsc_get_usc(const struct rtnl_class * class,struct tc_service_curve * tsc)295 int rtnl_class_hfsc_get_usc(const struct rtnl_class *class, struct tc_service_curve *tsc)
296 {
297 struct rtnl_hfsc_class *hfsc;
298 int err = -NLE_OPNOTSUPP;
299
300 if ((hfsc = hfsc_class_data(class, &err)) &&
301 (hfsc->ch_mask & SCH_HFSC_CLS_HAS_USC)) {
302 *tsc = hfsc->ch_usc;
303 return 0;
304 }
305
306 return err;
307 }
308
rtnl_class_hfsc_set_usc(struct rtnl_class * class,const struct tc_service_curve * tsc)309 int rtnl_class_hfsc_set_usc(struct rtnl_class *class, const struct tc_service_curve *tsc)
310 {
311 struct rtnl_hfsc_class *hfsc;
312 int err;
313
314 if (!(hfsc = hfsc_class_data(class, &err)))
315 return err;
316
317 hfsc->ch_usc = *tsc;
318 hfsc->ch_mask |= SCH_HFSC_CLS_HAS_USC;
319
320 return 0;
321 }
322
323 /** @} */
324
325 static struct rtnl_tc_ops hfsc_qdisc_ops = {
326 .to_kind = "hfsc",
327 .to_type = RTNL_TC_TYPE_QDISC,
328 .to_size = sizeof(struct rtnl_hfsc_qdisc),
329 .to_msg_parser = hfsc_qdisc_msg_parser,
330 .to_dump[NL_DUMP_LINE] = hfsc_qdisc_dump_line,
331 .to_msg_fill = hfsc_qdisc_msg_fill,
332 };
333
334 static struct rtnl_tc_ops hfsc_class_ops = {
335 .to_kind = "hfsc",
336 .to_type = RTNL_TC_TYPE_CLASS,
337 .to_size = sizeof(struct rtnl_hfsc_class),
338 .to_msg_parser = hfsc_class_msg_parser,
339 .to_dump = {
340 [NL_DUMP_LINE] = hfsc_class_dump_line,
341 [NL_DUMP_DETAILS] = hfsc_class_dump_details,
342 },
343 .to_msg_fill = hfsc_class_msg_fill,
344 };
345
hfsc_init(void)346 static void _nl_init hfsc_init(void)
347 {
348 rtnl_tc_register(&hfsc_qdisc_ops);
349 rtnl_tc_register(&hfsc_class_ops);
350 }
351
hfsc_exit(void)352 static void _nl_exit hfsc_exit(void)
353 {
354 rtnl_tc_unregister(&hfsc_qdisc_ops);
355 rtnl_tc_unregister(&hfsc_class_ops);
356 }
357
358 /** @} */
359