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