xref: /aosp_15_r20/external/libnl/lib/route/qdisc/hfsc.c (revision 4dc78e53d49367fa8e61b07018507c90983a077d)
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