1 /* 2 * q_hfsc.c HFSC. 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 * 9 * Authors: Patrick McHardy, <kaber (at) trash.net> 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> 22 #include <math.h> 23 24 #include "utils.h" 25 #include "tc_util.h" 26 27 static int hfsc_get_sc(int *, char ***, struct tc_service_curve *); 28 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, 44 "Usage: ... hfsc [ [ rt SC ] [ ls SC ] | [ sc SC ] ] [ ul SC ]\n" 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) 104 { 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; 117 } 118 119 static int 120 hfsc_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats) 121 { 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) 132 fprintf(f, "work %llu bytes ", (unsigned long long) st->work); 133 if (st->rtwork != 0) 134 fprintf(f, "rtwork %llu bytes ", (unsigned long long) st->rtwork); 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; 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; 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 208 tail = NLMSG_TAIL(n); 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 218 tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; 219 return 0; 220 } 221 222 static void 223 hfsc_print_sc(FILE *f, char *name, struct tc_service_curve *sc) 224 { 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_time(tc_core_ktime2time(sc->d), b1)); 230 fprintf(f, "m2 %s ", sprint_rate(sc->m2, b1)); 231 } 232 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 242 parse_rtattr_nested(tb, TCA_HFSC_MAX, opt); 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 263 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 } 273 if (usc != NULL) 274 hfsc_print_sc(f, "ul", usc); 275 276 return 0; 277 } 278 279 struct qdisc_util hfsc_qdisc_util = { 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, 286 }; 287 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_time(&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 = tc_core_time2ktime(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_time(&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(1.0 * umax * TIME_UNITS_PER_SEC / dmax) > rate) { 371 /* 372 * concave curve, slope of first segment is umax/dmax, 373 * intersection is at dmax 374 */ 375 sc->m1 = ceil(1.0 * umax * TIME_UNITS_PER_SEC / dmax); /* in bps */ 376 sc->d = tc_core_time2ktime(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 = tc_core_time2ktime(ceil(dmax - umax * TIME_UNITS_PER_SEC / rate)); 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 } 407