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