]>
Commit | Line | Data |
---|---|---|
aba5acdf | 1 | /* |
7518df00 | 2 | * q_hfsc.c HFSC. |
aba5acdf SH |
3 | * |
4 | * This program is free software; you can redistribute it and/or | |
5 | * modify it under the terms of the GNU General Public License | |
6 | * as published by the Free Software Foundation; either version | |
7 | * 2 of the License, or (at your option) any later version. | |
8 | * | |
7518df00 | 9 | * Authors: Patrick McHardy, <kaber@trash.net> |
aba5acdf SH |
10 | * |
11 | */ | |
12 | ||
13 | #include <stdio.h> | |
14 | #include <stdlib.h> | |
15 | #include <unistd.h> | |
16 | #include <syslog.h> | |
17 | #include <fcntl.h> | |
18 | #include <sys/socket.h> | |
19 | #include <netinet/in.h> | |
20 | #include <arpa/inet.h> | |
21 | #include <string.h> | |
7518df00 | 22 | #include <math.h> |
aba5acdf SH |
23 | |
24 | #include "utils.h" | |
25 | #include "tc_util.h" | |
26 | ||
7518df00 | 27 | static int hfsc_get_sc(int *, char ***, struct tc_service_curve *); |
aba5acdf | 28 | |
7518df00 | 29 | |
30 | static void | |
31 | explain_qdisc(void) | |
32 | { | |
33 | fprintf(stderr, | |
34 | "Usage: ... hfsc [ default CLASSID ]\n" | |
35 | "\n" | |
36 | " default: default class for unclassified packets\n" | |
37 | ); | |
38 | } | |
39 | ||
40 | static void | |
41 | explain_class(void) | |
42 | { | |
43 | fprintf(stderr, | |
b7de67da | 44 | "Usage: ... hfsc [ [ rt SC ] [ ls SC ] | [ sc SC ] ] [ ul SC ]\n" |
7518df00 | 45 | "\n" |
46 | "SC := [ [ m1 BPS ] [ d SEC ] m2 BPS\n" | |
47 | "\n" | |
48 | " m1 : slope of first segment\n" | |
49 | " d : x-coordinate of intersection\n" | |
50 | " m2 : slope of second segment\n" | |
51 | "\n" | |
52 | "Alternative format:\n" | |
53 | "\n" | |
54 | "SC := [ [ umax BYTE ] dmax SEC ] rate BPS\n" | |
55 | "\n" | |
56 | " umax : maximum unit of work\n" | |
57 | " dmax : maximum delay\n" | |
58 | " rate : rate\n" | |
59 | "\n" | |
60 | ); | |
61 | } | |
62 | ||
63 | static void | |
64 | explain1(char *arg) | |
65 | { | |
66 | fprintf(stderr, "HFSC: Illegal \"%s\"\n", arg); | |
67 | } | |
68 | ||
69 | static int | |
70 | hfsc_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) | |
71 | { | |
72 | struct tc_hfsc_qopt qopt; | |
73 | ||
74 | memset(&qopt, 0, sizeof(qopt)); | |
75 | ||
76 | while (argc > 0) { | |
77 | if (matches(*argv, "default") == 0) { | |
78 | NEXT_ARG(); | |
79 | if (qopt.defcls != 0) { | |
80 | fprintf(stderr, "HFSC: Double \"default\"\n"); | |
81 | return -1; | |
82 | } | |
83 | if (get_u16(&qopt.defcls, *argv, 16) < 0) { | |
84 | explain1("default"); | |
85 | return -1; | |
86 | } | |
87 | } else if (matches(*argv, "help") == 0) { | |
88 | explain_qdisc(); | |
89 | return -1; | |
90 | } else { | |
91 | fprintf(stderr, "HFSC: What is \"%s\" ?\n", *argv); | |
92 | explain_qdisc(); | |
93 | return -1; | |
94 | } | |
95 | argc--, argv++; | |
96 | } | |
97 | ||
98 | addattr_l(n, 1024, TCA_OPTIONS, &qopt, sizeof(qopt)); | |
99 | return 0; | |
100 | } | |
101 | ||
102 | static int | |
103 | hfsc_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) | |
aba5acdf | 104 | { |
7518df00 | 105 | struct tc_hfsc_qopt *qopt; |
106 | ||
107 | if (opt == NULL) | |
108 | return 0; | |
109 | if (RTA_PAYLOAD(opt) < sizeof(*qopt)) | |
110 | return -1; | |
111 | qopt = RTA_DATA(opt); | |
112 | ||
113 | if (qopt->defcls != 0) | |
114 | fprintf(f, "default %x ", qopt->defcls); | |
115 | ||
116 | return 0; | |
aba5acdf SH |
117 | } |
118 | ||
7518df00 | 119 | static int |
120 | hfsc_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats) | |
aba5acdf | 121 | { |
7518df00 | 122 | struct tc_hfsc_stats *st; |
123 | ||
124 | if (xstats == NULL) | |
125 | return 0; | |
126 | if (RTA_PAYLOAD(xstats) < sizeof(*st)) | |
127 | return -1; | |
128 | st = RTA_DATA(xstats); | |
129 | ||
130 | fprintf(f, " period %u ", st->period); | |
131 | if (st->work != 0) | |
b906243b | 132 | fprintf(f, "work %llu bytes ", (unsigned long long) st->work); |
7518df00 | 133 | if (st->rtwork != 0) |
b906243b | 134 | fprintf(f, "rtwork %llu bytes ", (unsigned long long) st->rtwork); |
7518df00 | 135 | fprintf(f, "level %u ", st->level); |
136 | fprintf(f, "\n"); | |
137 | ||
138 | return 0; | |
139 | } | |
140 | ||
141 | static int | |
142 | hfsc_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, | |
143 | struct nlmsghdr *n) | |
144 | { | |
145 | struct tc_service_curve rsc, fsc, usc; | |
146 | int rsc_ok, fsc_ok, usc_ok; | |
147 | struct rtattr *tail; | |
148 | ||
149 | memset(&rsc, 0, sizeof(rsc)); | |
150 | memset(&fsc, 0, sizeof(fsc)); | |
151 | memset(&usc, 0, sizeof(usc)); | |
152 | rsc_ok = fsc_ok = usc_ok = 0; | |
153 | ||
154 | while (argc > 0) { | |
155 | if (matches(*argv, "rt") == 0) { | |
156 | NEXT_ARG(); | |
157 | if (hfsc_get_sc(&argc, &argv, &rsc) < 0) { | |
158 | explain1("rt"); | |
159 | return -1; | |
160 | } | |
161 | rsc_ok = 1; | |
162 | } else if (matches(*argv, "ls") == 0) { | |
163 | NEXT_ARG(); | |
164 | if (hfsc_get_sc(&argc, &argv, &fsc) < 0) { | |
165 | explain1("ls"); | |
166 | return -1; | |
167 | } | |
168 | fsc_ok = 1; | |
b7de67da | 169 | } else if (matches(*argv, "sc") == 0) { |
170 | NEXT_ARG(); | |
171 | if (hfsc_get_sc(&argc, &argv, &rsc) < 0) { | |
172 | explain1("sc"); | |
173 | return -1; | |
174 | } | |
175 | memcpy(&fsc, &rsc, sizeof(fsc)); | |
176 | rsc_ok = 1; | |
177 | fsc_ok = 1; | |
7518df00 | 178 | } else if (matches(*argv, "ul") == 0) { |
179 | NEXT_ARG(); | |
180 | if (hfsc_get_sc(&argc, &argv, &usc) < 0) { | |
181 | explain1("ul"); | |
182 | return -1; | |
183 | } | |
184 | usc_ok = 1; | |
185 | } else if (matches(*argv, "help") == 0) { | |
186 | explain_class(); | |
187 | return -1; | |
188 | } else { | |
189 | fprintf(stderr, "HFSC: What is \"%s\" ?\n", *argv); | |
190 | explain_class(); | |
191 | return -1; | |
192 | } | |
193 | argc--, argv++; | |
194 | } | |
195 | ||
196 | if (!(rsc_ok || fsc_ok || usc_ok)) { | |
197 | fprintf(stderr, "HFSC: no parameters given\n"); | |
198 | explain_class(); | |
199 | return -1; | |
200 | } | |
201 | if (usc_ok && !fsc_ok) { | |
202 | fprintf(stderr, "HFSC: Upper-limit Service Curve without " | |
203 | "Link-Share Service Curve\n"); | |
204 | explain_class(); | |
205 | return -1; | |
206 | } | |
207 | ||
4a86fe19 | 208 | tail = NLMSG_TAIL(n); |
7518df00 | 209 | |
210 | addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); | |
211 | if (rsc_ok) | |
212 | addattr_l(n, 1024, TCA_HFSC_RSC, &rsc, sizeof(rsc)); | |
213 | if (fsc_ok) | |
214 | addattr_l(n, 1024, TCA_HFSC_FSC, &fsc, sizeof(fsc)); | |
215 | if (usc_ok) | |
216 | addattr_l(n, 1024, TCA_HFSC_USC, &usc, sizeof(usc)); | |
217 | ||
4a86fe19 | 218 | tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; |
7518df00 | 219 | return 0; |
aba5acdf SH |
220 | } |
221 | ||
7518df00 | 222 | static void |
223 | hfsc_print_sc(FILE *f, char *name, struct tc_service_curve *sc) | |
aba5acdf | 224 | { |
7518df00 | 225 | SPRINT_BUF(b1); |
226 | ||
227 | fprintf(f, "%s ", name); | |
228 | fprintf(f, "m1 %s ", sprint_rate(sc->m1, b1)); | |
229 | fprintf(f, "d %s ", sprint_usecs(sc->d, b1)); | |
230 | fprintf(f, "m2 %s ", sprint_rate(sc->m2, b1)); | |
aba5acdf SH |
231 | } |
232 | ||
7518df00 | 233 | static int |
234 | hfsc_print_class_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) | |
235 | { | |
236 | struct rtattr *tb[TCA_HFSC_MAX+1]; | |
237 | struct tc_service_curve *rsc = NULL, *fsc = NULL, *usc = NULL; | |
238 | ||
239 | if (opt == NULL) | |
240 | return 0; | |
241 | ||
ac2fc2df | 242 | parse_rtattr_nested(tb, TCA_HFSC_MAX, opt); |
7518df00 | 243 | |
244 | if (tb[TCA_HFSC_RSC]) { | |
245 | if (RTA_PAYLOAD(tb[TCA_HFSC_RSC]) < sizeof(*rsc)) | |
246 | fprintf(stderr, "HFSC: truncated realtime option\n"); | |
247 | else | |
248 | rsc = RTA_DATA(tb[TCA_HFSC_RSC]); | |
249 | } | |
250 | if (tb[TCA_HFSC_FSC]) { | |
251 | if (RTA_PAYLOAD(tb[TCA_HFSC_FSC]) < sizeof(*fsc)) | |
252 | fprintf(stderr, "HFSC: truncated linkshare option\n"); | |
253 | else | |
254 | fsc = RTA_DATA(tb[TCA_HFSC_FSC]); | |
255 | } | |
256 | if (tb[TCA_HFSC_USC]) { | |
257 | if (RTA_PAYLOAD(tb[TCA_HFSC_USC]) < sizeof(*usc)) | |
258 | fprintf(stderr, "HFSC: truncated upperlimit option\n"); | |
259 | else | |
260 | usc = RTA_DATA(tb[TCA_HFSC_USC]); | |
261 | } | |
262 | ||
ae665a52 | 263 | |
b7de67da | 264 | if (rsc != NULL && fsc != NULL && |
265 | memcmp(rsc, fsc, sizeof(*rsc)) == 0) | |
266 | hfsc_print_sc(f, "sc", rsc); | |
267 | else { | |
268 | if (rsc != NULL) | |
269 | hfsc_print_sc(f, "rt", rsc); | |
270 | if (fsc != NULL) | |
271 | hfsc_print_sc(f, "ls", fsc); | |
272 | } | |
7518df00 | 273 | if (usc != NULL) |
274 | hfsc_print_sc(f, "ul", usc); | |
275 | ||
276 | return 0; | |
277 | } | |
ae665a52 | 278 | |
95812b56 | 279 | struct qdisc_util hfsc_qdisc_util = { |
f2f99e2e SH |
280 | .id = "hfsc", |
281 | .parse_qopt = hfsc_parse_opt, | |
282 | .print_qopt = hfsc_print_opt, | |
283 | .print_xstats = hfsc_print_xstats, | |
284 | .parse_copt = hfsc_parse_class_opt, | |
285 | .print_copt = hfsc_print_class_opt, | |
aba5acdf SH |
286 | }; |
287 | ||
7518df00 | 288 | static int |
289 | hfsc_get_sc1(int *argcp, char ***argvp, struct tc_service_curve *sc) | |
290 | { | |
291 | char **argv = *argvp; | |
292 | int argc = *argcp; | |
293 | unsigned int m1 = 0, d = 0, m2 = 0; | |
294 | ||
295 | if (matches(*argv, "m1") == 0) { | |
296 | NEXT_ARG(); | |
297 | if (get_rate(&m1, *argv) < 0) { | |
298 | explain1("m1"); | |
299 | return -1; | |
300 | } | |
301 | NEXT_ARG(); | |
302 | } | |
303 | ||
304 | if (matches(*argv, "d") == 0) { | |
305 | NEXT_ARG(); | |
306 | if (get_usecs(&d, *argv) < 0) { | |
307 | explain1("d"); | |
308 | return -1; | |
309 | } | |
310 | NEXT_ARG(); | |
311 | } | |
312 | ||
313 | if (matches(*argv, "m2") == 0) { | |
314 | NEXT_ARG(); | |
315 | if (get_rate(&m2, *argv) < 0) { | |
316 | explain1("m2"); | |
317 | return -1; | |
318 | } | |
319 | } else | |
320 | return -1; | |
321 | ||
322 | sc->m1 = m1; | |
323 | sc->d = d; | |
324 | sc->m2 = m2; | |
325 | ||
326 | *argvp = argv; | |
327 | *argcp = argc; | |
328 | return 0; | |
329 | } | |
330 | ||
331 | static int | |
332 | hfsc_get_sc2(int *argcp, char ***argvp, struct tc_service_curve *sc) | |
333 | { | |
334 | char **argv = *argvp; | |
335 | int argc = *argcp; | |
336 | unsigned int umax = 0, dmax = 0, rate = 0; | |
337 | ||
338 | if (matches(*argv, "umax") == 0) { | |
339 | NEXT_ARG(); | |
340 | if (get_size(&umax, *argv) < 0) { | |
341 | explain1("umax"); | |
342 | return -1; | |
343 | } | |
344 | NEXT_ARG(); | |
345 | } | |
346 | ||
347 | if (matches(*argv, "dmax") == 0) { | |
348 | NEXT_ARG(); | |
349 | if (get_usecs(&dmax, *argv) < 0) { | |
350 | explain1("dmax"); | |
351 | return -1; | |
352 | } | |
353 | NEXT_ARG(); | |
354 | } | |
355 | ||
356 | if (matches(*argv, "rate") == 0) { | |
357 | NEXT_ARG(); | |
358 | if (get_rate(&rate, *argv) < 0) { | |
359 | explain1("rate"); | |
360 | return -1; | |
361 | } | |
362 | } else | |
363 | return -1; | |
364 | ||
365 | if (umax != 0 && dmax == 0) { | |
366 | fprintf(stderr, "HFSC: umax given but dmax is zero.\n"); | |
367 | return -1; | |
368 | } | |
369 | ||
370 | if (dmax != 0 && ceil(umax * 1000000.0 / dmax) > rate) { | |
371 | /* | |
372 | * concave curve, slope of first segment is umax/dmax, | |
373 | * intersection is at dmax | |
374 | */ | |
375 | sc->m1 = ceil(umax * 1000000.0 / dmax); /* in bps */ | |
376 | sc->d = dmax; | |
377 | sc->m2 = rate; | |
378 | } else { | |
379 | /* | |
380 | * convex curve, slope of first segment is 0, intersection | |
381 | * is at dmax - umax / rate | |
382 | */ | |
383 | sc->m1 = 0; | |
384 | sc->d = ceil(dmax - umax * 1000000.0 / rate); /* in usec */ | |
385 | sc->m2 = rate; | |
386 | } | |
387 | ||
388 | *argvp = argv; | |
389 | *argcp = argc; | |
390 | return 0; | |
391 | } | |
392 | ||
393 | static int | |
394 | hfsc_get_sc(int *argcp, char ***argvp, struct tc_service_curve *sc) | |
395 | { | |
396 | if (hfsc_get_sc1(argcp, argvp, sc) < 0 && | |
397 | hfsc_get_sc2(argcp, argvp, sc) < 0) | |
398 | return -1; | |
399 | ||
400 | if (sc->m1 == 0 && sc->m2 == 0) { | |
401 | fprintf(stderr, "HFSC: Service Curve has two zero slopes\n"); | |
402 | return -1; | |
403 | } | |
404 | ||
405 | return 0; | |
406 | } |