1 /* $NetBSD: qop_hfsc.c,v 1.7.12.1 2006/03/18 12:12:59 peter Exp $ */
2 /* $KAME: qop_hfsc.c,v 1.12 2005/01/05 04:53:47 itojun Exp $ */
4 * Copyright (C) 1999-2000
5 * Sony Computer Science Laboratories, Inc. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/param.h>
30 #include <sys/socket.h>
31 #include <sys/sockio.h>
32 #include <sys/ioctl.h>
33 #include <sys/fcntl.h>
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
49 #include <altq/altq.h>
50 #include <altq/altq_hfsc.h>
54 static int read_sc(int *, char ***, int *, u_int
*, u_int
*, u_int
*);
55 static int qop_hfsc_enable_hook(struct ifinfo
*);
56 static int qop_hfsc_delete_class_hook(struct classinfo
*);
57 static int validate_sc(struct service_curve
*);
59 static void gsc_add_sc(struct gen_sc
*, struct service_curve
*);
60 static void gsc_sub_sc(struct gen_sc
*, struct service_curve
*);
61 static int is_gsc_under_sc(struct gen_sc
*, struct service_curve
*);
62 static void gsc_destroy(struct gen_sc
*);
63 static struct segment
*gsc_getentry(struct gen_sc
*, double);
64 static int gsc_add_seg(struct gen_sc
*, double, double, double, double);
65 static int gsc_sub_seg(struct gen_sc
*, double, double, double, double);
66 static void gsc_compress(struct gen_sc
*);
67 static double sc_x2y(struct service_curve
*, double);
69 static int hfsc_attach(struct ifinfo
*);
70 static int hfsc_detach(struct ifinfo
*);
71 static int hfsc_clear(struct ifinfo
*);
72 static int hfsc_enable(struct ifinfo
*);
73 static int hfsc_disable(struct ifinfo
*);
74 static int hfsc_add_class(struct classinfo
*);
75 static int hfsc_modify_class(struct classinfo
*, void *);
76 static int hfsc_delete_class(struct classinfo
*);
77 static int hfsc_add_filter(struct fltrinfo
*);
78 static int hfsc_delete_filter(struct fltrinfo
*);
80 #define HFSC_DEVICE "/dev/altq/hfsc"
82 static int hfsc_fd
= -1;
83 static int hfsc_refcount
= 0;
85 static struct qdisc_ops hfsc_qdisc
= {
100 #define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0)
106 hfsc_interface_parser(const char *ifname
, int argc
, char **argv
)
108 u_int bandwidth
= 100000000; /* 100Mbps */
116 if (EQUAL(*argv
, "bandwidth")) {
119 bandwidth
= atobps(*argv
);
120 } else if (EQUAL(*argv
, "tbrsize")) {
123 tbrsize
= atobytes(*argv
);
124 } else if (EQUAL(*argv
, "hfsc")) {
127 LOG(LOG_ERR
, 0, "Unknown keyword '%s'", *argv
);
133 if (qcmd_tbr_register(ifname
, bandwidth
, tbrsize
) != 0)
136 if (qcmd_hfsc_add_if(ifname
, bandwidth
, flags
) != 0)
142 hfsc_class_parser(const char *ifname
, const char *class_name
,
143 const char *parent_name
, int argc
, char **argv
)
145 u_int m1
, d
, m2
, rm1
, rd
, rm2
, fm1
, fd
, fm2
, um1
, ud
, um2
;
147 int flags
= 0, admission
= 0;
150 rm1
= rd
= rm2
= fm1
= fd
= fm2
= um1
= ud
= um2
= 0;
152 if (*argv
[0] == '[') {
153 if (read_sc(&argc
, &argv
, &type
, &m1
, &d
, &m2
) != 0) {
155 "Bad service curve in %s, line %d",
156 altqconfigfile
, line_no
);
159 if (type
& HFSC_REALTIMESC
) {
160 rm1
= m1
; rd
= d
; rm2
= m2
;
162 if (type
& HFSC_LINKSHARINGSC
) {
163 fm1
= m1
; fd
= d
; fm2
= m2
;
165 if (type
& HFSC_UPPERLIMITSC
) {
166 um1
= m1
; ud
= d
; um2
= m2
;
168 } else if (EQUAL(*argv
, "ulimit")) {
172 type
|= HFSC_UPPERLIMITSC
;
174 } else if (EQUAL(*argv
, "pshare")) {
177 struct ifinfo
*ifinfo
;
180 pshare
= (u_int
)strtoul(*argv
, NULL
, 0);
181 if ((ifinfo
= ifname2ifinfo(ifname
)) != NULL
) {
182 fm2
= ifinfo
->bandwidth
/ 100 * pshare
;
183 type
|= HFSC_LINKSHARINGSC
;
186 } else if (EQUAL(*argv
, "grate")) {
190 type
|= HFSC_REALTIMESC
;
192 } else if (EQUAL(*argv
, "bandwidth")) {
195 rm2
= fm2
= atobps(*argv
);
196 type
|= (HFSC_REALTIMESC
| HFSC_LINKSHARINGSC
);
198 } else if (EQUAL(*argv
, "qlimit")) {
201 qlimit
= strtoul(*argv
, NULL
, 0);
202 } else if (EQUAL(*argv
, "default")) {
203 flags
|= HFCF_DEFAULTCLASS
;
204 } else if (EQUAL(*argv
, "admission")) {
207 if (EQUAL(*argv
, "guaranteed")
208 || EQUAL(*argv
, "cntlload"))
210 else if (EQUAL(*argv
, "none")) {
214 "unknown admission type - %s, line %d",
219 } else if (EQUAL(*argv
, "red")) {
221 } else if (EQUAL(*argv
, "ecn")) {
223 } else if (EQUAL(*argv
, "rio")) {
225 } else if (EQUAL(*argv
, "cleardscp")) {
226 flags
|= HFCF_CLEARDSCP
;
229 "Unknown keyword '%s' in %s, line %d",
230 *argv
, altqconfigfile
, line_no
);
239 "hfsc: service curve not specified in %s, line %d",
240 altqconfigfile
, line_no
);
244 if ((flags
& HFCF_ECN
) && (flags
& (HFCF_RED
|HFCF_RIO
)) == 0)
248 * if the link-sharing service curve is diffrent from
249 * the real-time service curve, we first create a class with the
250 * smaller service curve and then modify the other service curve.
253 m1
= rm1
; d
= rd
; m2
= rm2
;
255 m1
= fm1
; d
= fd
; m2
= fm2
;
257 error
= qcmd_hfsc_add_class(ifname
, class_name
, parent_name
,
258 m1
, d
, m2
, qlimit
, flags
);
260 if (error
== 0 && (rm1
!= fm1
|| rd
!= fd
|| rm2
!= fm2
)) {
262 m1
= fm1
; d
= fd
; m2
= fm2
; type
= HFSC_LINKSHARINGSC
;
264 m1
= rm1
; d
= rd
; m2
= rm2
; type
= HFSC_REALTIMESC
;
266 error
= qcmd_hfsc_modify_class(ifname
, class_name
,
270 if (error
== 0 && (um1
!= 0 || um2
!= 0)) {
271 error
= qcmd_hfsc_modify_class(ifname
, class_name
,
272 um1
, ud
, um2
, HFSC_UPPERLIMITSC
);
275 if (error
== 0 && admission
) {
276 /* this is a special class for rsvp */
277 struct ifinfo
*ifinfo
= ifname2ifinfo(ifname
);
278 struct classinfo
*clinfo
= clname2clinfo(ifinfo
, class_name
);
280 if (ifinfo
->resv_class
!= NULL
) {
282 "more than one admission class specified: %s",
286 ifinfo
->resv_class
= clinfo
;
290 LOG(LOG_ERR
, errno
, "hfsc_class_parser: %s",
298 * read service curve parameters
299 * '[' <type> <m1> <d> <m2> ']'
300 * type := "sc", "rt", "ls", or "ul"
303 read_sc(int *argcp
, char ***argvp
, int *type
, u_int
*m1
, u_int
*d
, u_int
*m2
)
306 char **argv
= *argvp
;
313 cp
= *++argv
; --argc
;
315 if (*cp
== 's' || *cp
== 'S')
316 *type
= HFSC_DEFAULTSC
;
317 else if (*cp
== 'r' || *cp
== 'R')
318 *type
= HFSC_REALTIMESC
;
319 else if (*cp
== 'l' || *cp
== 'L')
320 *type
= HFSC_LINKSHARINGSC
;
321 else if (*cp
== 'u' || *cp
== 'U')
322 *type
= HFSC_UPPERLIMITSC
;
325 cp
= *++argv
; --argc
;
327 cp
= *++argv
; --argc
;
328 *d
= (u_int
)strtoul(cp
, NULL
, 0);
329 cp
= *++argv
; --argc
;
331 if (strchr(cp
, ']') == NULL
) {
332 cp
= *++argv
; --argc
;
345 qcmd_hfsc_add_if(const char *ifname
, u_int bandwidth
, int flags
)
349 error
= qop_hfsc_add_if(NULL
, ifname
, bandwidth
, flags
);
351 LOG(LOG_ERR
, errno
, "%s: can't add hfsc on interface '%s'",
352 qoperror(error
), ifname
);
357 qcmd_hfsc_add_class(const char *ifname
, const char *class_name
,
358 const char *parent_name
, u_int m1
, u_int d
, u_int m2
,
359 int qlimit
, int flags
)
361 struct ifinfo
*ifinfo
;
362 struct classinfo
*parent
= NULL
;
363 struct service_curve sc
;
366 if ((ifinfo
= ifname2ifinfo(ifname
)) == NULL
)
367 error
= QOPERR_BADIF
;
370 (parent
= clname2clinfo(ifinfo
, parent_name
)) == NULL
)
371 error
= QOPERR_BADCLASS
;
378 error
= qop_hfsc_add_class(NULL
, class_name
, ifinfo
, parent
,
382 "hfsc: %s: can't add class '%s' on interface '%s'",
383 qoperror(error
), class_name
, ifname
);
388 qcmd_hfsc_modify_class(const char *ifname
, const char *class_name
,
389 u_int m1
, u_int d
, u_int m2
, int sctype
)
391 struct ifinfo
*ifinfo
;
392 struct classinfo
*clinfo
;
393 struct service_curve sc
;
395 if ((ifinfo
= ifname2ifinfo(ifname
)) == NULL
)
396 return (QOPERR_BADIF
);
398 if ((clinfo
= clname2clinfo(ifinfo
, class_name
)) == NULL
)
399 return (QOPERR_BADCLASS
);
405 return qop_hfsc_modify_class(clinfo
, &sc
, sctype
);
412 qop_hfsc_add_if(struct ifinfo
**rp
, const char *ifname
,
413 u_int bandwidth
, int flags
)
415 struct ifinfo
*ifinfo
= NULL
;
416 struct hfsc_ifinfo
*hfsc_ifinfo
= NULL
;
417 struct service_curve sc
;
420 if ((hfsc_ifinfo
= calloc(1, sizeof(*hfsc_ifinfo
))) == NULL
)
421 return (QOPERR_NOMEM
);
423 error
= qop_add_if(&ifinfo
, ifname
, bandwidth
,
424 &hfsc_qdisc
, hfsc_ifinfo
);
428 /* set enable hook */
429 ifinfo
->enable_hook
= qop_hfsc_enable_hook
;
431 /* create root class */
435 if ((error
= qop_hfsc_add_class(&hfsc_ifinfo
->root_class
, "root",
436 ifinfo
, NULL
, &sc
, 0, 0)) != 0) {
438 "hfsc: %s: can't create dummy root class on %s!",
439 qoperror(error
), ifname
);
440 (void)qop_delete_if(ifinfo
);
441 return (QOPERR_CLASS
);
449 if (hfsc_ifinfo
!= NULL
) {
452 ifinfo
->private = NULL
;
457 #define is_sc_null(sc) (((sc) == NULL) || ((sc)->m1 == 0 && (sc)->m2 == 0))
460 qop_hfsc_add_class(struct classinfo
**rp
, const char *class_name
,
461 struct ifinfo
*ifinfo
, struct classinfo
*parent
,
462 struct service_curve
*sc
, int qlimit
, int flags
)
464 struct classinfo
*clinfo
;
465 struct hfsc_ifinfo
*hfsc_ifinfo
;
466 struct hfsc_classinfo
*hfsc_clinfo
= NULL
, *parent_clinfo
= NULL
;
469 hfsc_ifinfo
= ifinfo
->private;
470 if ((flags
& HFCF_DEFAULTCLASS
) && hfsc_ifinfo
->default_class
!= NULL
)
471 return (QOPERR_CLASS_INVAL
);
473 if (validate_sc(sc
) != 0)
474 return (QOPERR_INVAL
);
476 /* admission control */
477 if (parent
!= NULL
&& !is_sc_null(sc
)) {
478 parent_clinfo
= parent
->private;
479 gsc_add_sc(&parent_clinfo
->gen_rsc
, sc
);
480 gsc_add_sc(&parent_clinfo
->gen_fsc
, sc
);
481 if (!is_gsc_under_sc(&parent_clinfo
->gen_rsc
,
482 &parent_clinfo
->rsc
) ||
483 !is_gsc_under_sc(&parent_clinfo
->gen_fsc
,
484 &parent_clinfo
->fsc
)) {
485 /* admission control failure */
486 error
= QOPERR_ADMISSION_NOBW
;
491 if ((hfsc_clinfo
= calloc(1, sizeof(*hfsc_clinfo
))) == NULL
) {
492 error
= QOPERR_NOMEM
;
496 hfsc_clinfo
->rsc
= *sc
;
497 hfsc_clinfo
->fsc
= *sc
;
498 LIST_INIT(&hfsc_clinfo
->gen_rsc
);
499 LIST_INIT(&hfsc_clinfo
->gen_fsc
);
500 hfsc_clinfo
->qlimit
= qlimit
;
501 hfsc_clinfo
->flags
= flags
;
503 if ((error
= qop_add_class(&clinfo
, class_name
, ifinfo
, parent
,
507 /* set delete hook */
508 clinfo
->delete_hook
= qop_hfsc_delete_class_hook
;
510 if (flags
& HFCF_DEFAULTCLASS
)
511 hfsc_ifinfo
->default_class
= clinfo
;
513 if (parent
== NULL
) {
515 * if this is a root class, reserve 20% of the real-time
516 * bandwidth for safety.
517 * many network cards are not able to saturate the wire,
518 * and if we allocate real-time traffic more than the
519 * maximum sending rate of the card, hfsc is no longer
520 * able to meet the delay bound requirements.
522 hfsc_clinfo
->rsc
.m1
= hfsc_clinfo
->rsc
.m1
/ 10 * 8;
523 hfsc_clinfo
->rsc
.m2
= hfsc_clinfo
->rsc
.m2
/ 10 * 8;
531 /* cancel admission control */
532 if (parent
!= NULL
&& !is_sc_null(sc
)) {
533 gsc_sub_sc(&parent_clinfo
->gen_rsc
, sc
);
534 gsc_sub_sc(&parent_clinfo
->gen_fsc
, sc
);
537 if (hfsc_clinfo
!= NULL
) {
539 clinfo
->private = NULL
;
546 * this is called from qop_delete_class() before a class is destroyed
547 * for discipline specific cleanup.
550 qop_hfsc_delete_class_hook(struct classinfo
*clinfo
)
552 struct hfsc_classinfo
*hfsc_clinfo
, *parent_clinfo
;
554 hfsc_clinfo
= clinfo
->private;
556 /* cancel admission control */
557 if (clinfo
->parent
!= NULL
) {
558 parent_clinfo
= clinfo
->parent
->private;
560 gsc_sub_sc(&parent_clinfo
->gen_rsc
, &hfsc_clinfo
->rsc
);
561 gsc_sub_sc(&parent_clinfo
->gen_fsc
, &hfsc_clinfo
->fsc
);
564 gsc_destroy(&hfsc_clinfo
->gen_rsc
);
565 gsc_destroy(&hfsc_clinfo
->gen_fsc
);
570 qop_hfsc_modify_class(struct classinfo
*clinfo
,
571 struct service_curve
*sc
, int sctype
)
573 struct hfsc_classinfo
*hfsc_clinfo
, *parent_clinfo
;
574 struct service_curve rsc
, fsc
, usc
;
577 if (validate_sc(sc
) != 0)
578 return (QOPERR_INVAL
);
580 hfsc_clinfo
= clinfo
->private;
581 if (clinfo
->parent
== NULL
)
582 return (QOPERR_CLASS_INVAL
);
583 parent_clinfo
= clinfo
->parent
->private;
585 /* save old service curves */
586 rsc
= hfsc_clinfo
->rsc
;
587 fsc
= hfsc_clinfo
->fsc
;
588 usc
= hfsc_clinfo
->usc
;
590 /* admission control */
591 if (sctype
& HFSC_REALTIMESC
) {
592 /* if the class has usc, rsc should be smaller than usc */
593 if (!is_sc_null(&hfsc_clinfo
->usc
)) {
594 gsc_head_t tmp_gen_rsc
=
595 LIST_HEAD_INITIALIZER(tmp_gen_rsc
);
597 gsc_add_sc(&tmp_gen_rsc
, sc
);
598 if (!is_gsc_under_sc(&tmp_gen_rsc
, &hfsc_clinfo
->usc
)) {
599 gsc_destroy(&tmp_gen_rsc
);
600 return (QOPERR_ADMISSION
);
602 gsc_destroy(&tmp_gen_rsc
);
605 if (!is_gsc_under_sc(&hfsc_clinfo
->gen_rsc
, sc
)) {
606 /* admission control failure */
607 return (QOPERR_ADMISSION
);
610 gsc_sub_sc(&parent_clinfo
->gen_rsc
, &hfsc_clinfo
->rsc
);
611 gsc_add_sc(&parent_clinfo
->gen_rsc
, sc
);
612 if (!is_gsc_under_sc(&parent_clinfo
->gen_rsc
,
613 &parent_clinfo
->rsc
)) {
614 /* admission control failure */
615 gsc_sub_sc(&parent_clinfo
->gen_rsc
, sc
);
616 gsc_add_sc(&parent_clinfo
->gen_rsc
, &hfsc_clinfo
->rsc
);
617 return (QOPERR_ADMISSION_NOBW
);
619 hfsc_clinfo
->rsc
= *sc
;
621 if (sctype
& HFSC_LINKSHARINGSC
) {
622 if (!is_gsc_under_sc(&hfsc_clinfo
->gen_fsc
, sc
)) {
623 /* admission control failure */
624 return (QOPERR_ADMISSION
);
627 gsc_sub_sc(&parent_clinfo
->gen_fsc
, &hfsc_clinfo
->fsc
);
628 gsc_add_sc(&parent_clinfo
->gen_fsc
, sc
);
629 if (!is_gsc_under_sc(&parent_clinfo
->gen_fsc
,
630 &parent_clinfo
->fsc
)) {
631 /* admission control failure */
632 gsc_sub_sc(&parent_clinfo
->gen_fsc
, sc
);
633 gsc_add_sc(&parent_clinfo
->gen_fsc
, &hfsc_clinfo
->fsc
);
634 return (QOPERR_ADMISSION_NOBW
);
636 hfsc_clinfo
->fsc
= *sc
;
638 if (sctype
& HFSC_UPPERLIMITSC
) {
639 if (!is_sc_null(sc
)) {
640 /* usc must be smaller than interface bandwidth */
641 struct classinfo
*root_clinfo
=
642 clname2clinfo(clinfo
->ifinfo
, "root");
643 if (root_clinfo
!= NULL
) {
644 struct hfsc_classinfo
*root_hfsc_clinfo
=
645 root_clinfo
->private;
646 if (!is_sc_null(&root_hfsc_clinfo
->rsc
)) {
647 gsc_head_t tmp_gen_usc
=
648 LIST_HEAD_INITIALIZER(tmp_gen_usc
);
649 gsc_add_sc(&tmp_gen_usc
, sc
);
650 if (!is_gsc_under_sc(&tmp_gen_usc
,
651 &root_hfsc_clinfo
->fsc
)) {
652 /* illegal attempt to set
653 upper limit curve to be
654 greater than the interface
656 gsc_destroy(&tmp_gen_usc
);
657 return (QOPERR_ADMISSION
);
659 gsc_destroy(&tmp_gen_usc
);
662 /* if this class has rsc, check that usc >= rsc */
663 if (!is_sc_null(&hfsc_clinfo
->rsc
)) {
664 gsc_head_t tmp_gen_rsc
=
665 LIST_HEAD_INITIALIZER(tmp_gen_rsc
);
666 gsc_add_sc(&tmp_gen_rsc
, &hfsc_clinfo
->rsc
);
667 if (!is_gsc_under_sc(&tmp_gen_rsc
, sc
)) {
668 /* illegal attempt to set upper limit
669 curve to be under the real-time
671 gsc_destroy(&tmp_gen_rsc
);
672 return (QOPERR_ADMISSION
);
674 gsc_destroy(&tmp_gen_rsc
);
677 hfsc_clinfo
->usc
= *sc
;
680 error
= qop_modify_class(clinfo
, (void *)((long)sctype
));
684 /* modify failed!, restore the old service curves */
685 if (sctype
& HFSC_REALTIMESC
) {
686 gsc_sub_sc(&parent_clinfo
->gen_rsc
, sc
);
687 gsc_add_sc(&parent_clinfo
->gen_rsc
, &rsc
);
688 hfsc_clinfo
->rsc
= rsc
;
690 if (sctype
& HFSC_LINKSHARINGSC
) {
691 gsc_sub_sc(&parent_clinfo
->gen_fsc
, sc
);
692 gsc_add_sc(&parent_clinfo
->gen_fsc
, &fsc
);
693 hfsc_clinfo
->fsc
= fsc
;
695 if (sctype
& HFSC_UPPERLIMITSC
) {
696 hfsc_clinfo
->usc
= usc
;
702 * sanity check at enabling hfsc:
703 * 1. there must one default class for an interface
704 * 2. the default class must be a leaf class
705 * 3. an internal class should not have filters
706 * (rule 2 and 3 are due to the fact that the hfsc link-sharing algorithm
707 * do not schedule internal classes.)
710 qop_hfsc_enable_hook(struct ifinfo
*ifinfo
)
712 struct hfsc_ifinfo
*hfsc_ifinfo
;
713 struct classinfo
*clinfo
;
715 hfsc_ifinfo
= ifinfo
->private;
716 if (hfsc_ifinfo
->default_class
== NULL
) {
717 LOG(LOG_ERR
, 0, "hfsc: no default class on interface %s!",
719 return (QOPERR_CLASS
);
720 } else if (hfsc_ifinfo
->default_class
->child
!= NULL
) {
721 LOG(LOG_ERR
, 0, "hfsc: default class on %s must be a leaf!",
723 return (QOPERR_CLASS
);
726 LIST_FOREACH(clinfo
, &ifinfo
->cllist
, next
) {
727 if (clinfo
->child
!= NULL
&& !LIST_EMPTY(&clinfo
->fltrlist
)) {
729 "hfsc: internal class \"%s\" should not have a filter!",
731 return (QOPERR_CLASS
);
739 validate_sc(struct service_curve
*sc
)
741 /* the 1st segment of a concave curve must be zero */
742 if (sc
->m1
< sc
->m2
&& sc
->m1
!= 0) {
743 LOG(LOG_ERR
, 0, "m1 must be 0 for convex!");
746 if (sc
->m1
> sc
->m2
&& sc
->m2
== 0) {
747 LOG(LOG_ERR
, 0, "m2 must be nonzero for concave!");
754 * admission control using generalized service curve
757 #define INFINITY HUGE_VAL /* positive infinity defined in <math.h> */
760 /* add a new service curve to a generilized service curve */
762 gsc_add_sc(struct gen_sc
*gsc
, struct service_curve
*sc
)
767 gsc_add_seg(gsc
, 0, 0, (double)sc
->d
, (double)sc
->m1
);
768 gsc_add_seg(gsc
, (double)sc
->d
, 0, INFINITY
, (double)sc
->m2
);
771 /* subtract a service curve from a generilized service curve */
773 gsc_sub_sc(struct gen_sc
*gsc
, struct service_curve
*sc
)
778 gsc_sub_seg(gsc
, 0, 0, (double)sc
->d
, (double)sc
->m1
);
779 gsc_sub_seg(gsc
, (double)sc
->d
, 0, INFINITY
, (double)sc
->m2
);
783 * check whether all points of a generalized service curve have
784 * their y-coordinates no larger than a given two-piece linear
788 is_gsc_under_sc(struct gen_sc
*gsc
, struct service_curve
*sc
)
790 struct segment
*s
, *last
, *end
;
793 if (is_sc_null(sc
)) {
796 LIST_FOREACH(s
, gsc
, _next
) {
803 * gsc has a dummy entry at the end with x = INFINITY.
804 * loop through up to this dummy entry.
806 end
= gsc_getentry(gsc
, INFINITY
);
810 for (s
= LIST_FIRST(gsc
); s
!= end
; s
= LIST_NEXT(s
, _next
)) {
811 if (s
->y
> sc_x2y(sc
, s
->x
))
815 /* last now holds the real last segment */
818 if (last
->m
> sc
->m2
)
820 if (last
->x
< sc
->d
&& last
->m
> sc
->m1
) {
821 y
= last
->y
+ (sc
->d
- last
->x
) * last
->m
;
822 if (y
> sc_x2y(sc
, sc
->d
))
829 gsc_destroy(struct gen_sc
*gsc
)
833 while ((s
= LIST_FIRST(gsc
)) != NULL
) {
834 LIST_REMOVE(s
, _next
);
840 * return a segment entry starting at x.
841 * if gsc has no entry starting at x, a new entry is created at x.
843 static struct segment
*
844 gsc_getentry(struct gen_sc
*gsc
, double x
)
846 struct segment
*new, *prev
, *s
;
849 LIST_FOREACH(s
, gsc
, _next
) {
851 return (s
); /* matching entry found */
858 /* we have to create a new entry */
859 if ((new = calloc(1, sizeof(struct segment
))) == NULL
)
863 if (x
== INFINITY
|| s
== NULL
)
865 else if (s
->x
== INFINITY
)
870 /* insert the new entry at the head of the list */
873 LIST_INSERT_HEAD(gsc
, new, _next
);
876 * the start point intersects with the segment pointed by
877 * prev. divide prev into 2 segments
886 prev
->d
= x
- prev
->x
;
887 new->y
= prev
->d
* prev
->m
+ prev
->y
;
890 LIST_INSERT_AFTER(prev
, new, _next
);
895 /* add a segment to a generalized service curve */
897 gsc_add_seg(struct gen_sc
*gsc
, double x
, double y
, double d
, double m
)
899 struct segment
*start
, *end
, *s
;
906 start
= gsc_getentry(gsc
, x
);
907 end
= gsc_getentry(gsc
, x2
);
908 if (start
== NULL
|| end
== NULL
)
911 for (s
= start
; s
!= end
; s
= LIST_NEXT(s
, _next
)) {
913 s
->y
+= y
+ (s
->x
- x
) * m
;
916 end
= gsc_getentry(gsc
, INFINITY
);
917 for (; s
!= end
; s
= LIST_NEXT(s
, _next
)) {
924 /* subtract a segment from a generalized service curve */
926 gsc_sub_seg(struct gen_sc
*gsc
, double x
, double y
, double d
, double m
)
928 if (gsc_add_seg(gsc
, x
, y
, d
, -m
) < 0)
935 * collapse adjacent segments with the same slope
938 gsc_compress(struct gen_sc
*gsc
)
940 struct segment
*s
, *next
;
943 LIST_FOREACH(s
, gsc
, _next
) {
945 if ((next
= LIST_NEXT(s
, _next
)) == NULL
) {
946 if (LIST_FIRST(gsc
) == s
&& s
->m
== 0) {
948 * if this is the only entry and its
949 * slope is 0, it's a remaining dummy
950 * entry. we can discard it.
952 LIST_REMOVE(s
, _next
);
958 if (s
->x
== next
->x
) {
959 /* discard this entry */
960 LIST_REMOVE(s
, _next
);
963 } else if (s
->m
== next
->m
) {
964 /* join the two entries */
965 if (s
->d
!= INFINITY
&& next
->d
!= INFINITY
)
967 LIST_REMOVE(next
, _next
);
974 /* get y-projection of a service curve */
976 sc_x2y(struct service_curve
*sc
, double x
)
980 if (x
<= (double)sc
->d
)
981 /* y belongs to the 1st segment */
982 y
= x
* (double)sc
->m1
;
984 /* y belongs to the 2nd segment */
985 y
= (double)sc
->d
* (double)sc
->m1
986 + (x
- (double)sc
->d
) * (double)sc
->m2
;
991 * system call interfaces for qdisc_ops
994 hfsc_attach(struct ifinfo
*ifinfo
)
996 struct hfsc_attach attach
;
999 (hfsc_fd
= open(HFSC_DEVICE
, O_RDWR
)) < 0 &&
1000 (hfsc_fd
= open_module(HFSC_DEVICE
, O_RDWR
)) < 0) {
1001 LOG(LOG_ERR
, errno
, "HFSC open");
1002 return (QOPERR_SYSCALL
);
1006 memset(&attach
, 0, sizeof(attach
));
1007 strncpy(attach
.iface
.hfsc_ifname
, ifinfo
->ifname
, IFNAMSIZ
);
1008 attach
.bandwidth
= ifinfo
->bandwidth
;
1010 if (ioctl(hfsc_fd
, HFSC_IF_ATTACH
, &attach
) < 0)
1011 return (QOPERR_SYSCALL
);
1016 hfsc_detach(struct ifinfo
*ifinfo
)
1018 struct hfsc_interface iface
;
1020 memset(&iface
, 0, sizeof(iface
));
1021 strncpy(iface
.hfsc_ifname
, ifinfo
->ifname
, IFNAMSIZ
);
1023 if (ioctl(hfsc_fd
, HFSC_IF_DETACH
, &iface
) < 0)
1024 return (QOPERR_SYSCALL
);
1026 if (--hfsc_refcount
== 0) {
1034 hfsc_clear(struct ifinfo
*ifinfo
)
1036 struct hfsc_interface iface
;
1038 memset(&iface
, 0, sizeof(iface
));
1039 strncpy(iface
.hfsc_ifname
, ifinfo
->ifname
, IFNAMSIZ
);
1041 if (ioctl(hfsc_fd
, HFSC_CLEAR_HIERARCHY
, &iface
) < 0)
1042 return (QOPERR_SYSCALL
);
1047 hfsc_enable(struct ifinfo
*ifinfo
)
1049 struct hfsc_interface iface
;
1051 memset(&iface
, 0, sizeof(iface
));
1052 strncpy(iface
.hfsc_ifname
, ifinfo
->ifname
, IFNAMSIZ
);
1054 if (ioctl(hfsc_fd
, HFSC_ENABLE
, &iface
) < 0)
1055 return (QOPERR_SYSCALL
);
1060 hfsc_disable(struct ifinfo
*ifinfo
)
1062 struct hfsc_interface iface
;
1064 memset(&iface
, 0, sizeof(iface
));
1065 strncpy(iface
.hfsc_ifname
, ifinfo
->ifname
, IFNAMSIZ
);
1067 if (ioctl(hfsc_fd
, HFSC_DISABLE
, &iface
) < 0)
1068 return (QOPERR_SYSCALL
);
1073 hfsc_add_class(struct classinfo
*clinfo
)
1075 struct hfsc_add_class class_add
;
1076 struct hfsc_classinfo
*hfsc_clinfo
;
1077 struct hfsc_ifinfo
*hfsc_ifinfo
;
1079 hfsc_ifinfo
= clinfo
->ifinfo
->private;
1080 hfsc_clinfo
= clinfo
->private;
1082 memset(&class_add
, 0, sizeof(class_add
));
1083 strncpy(class_add
.iface
.hfsc_ifname
, clinfo
->ifinfo
->ifname
, IFNAMSIZ
);
1085 if (clinfo
->parent
== NULL
)
1086 class_add
.parent_handle
= HFSC_NULLCLASS_HANDLE
;
1088 class_add
.parent_handle
= clinfo
->parent
->handle
;
1090 class_add
.service_curve
= hfsc_clinfo
->rsc
;
1091 class_add
.qlimit
= hfsc_clinfo
->qlimit
;
1092 class_add
.flags
= hfsc_clinfo
->flags
;
1094 if (ioctl(hfsc_fd
, HFSC_ADD_CLASS
, &class_add
) < 0) {
1095 clinfo
->handle
= HFSC_NULLCLASS_HANDLE
;
1096 return (QOPERR_SYSCALL
);
1098 clinfo
->handle
= class_add
.class_handle
;
1103 hfsc_modify_class(struct classinfo
*clinfo
, void *arg
)
1105 struct hfsc_modify_class class_mod
;
1106 struct hfsc_classinfo
*hfsc_clinfo
;
1110 hfsc_clinfo
= clinfo
->private;
1112 memset(&class_mod
, 0, sizeof(class_mod
));
1113 strncpy(class_mod
.iface
.hfsc_ifname
, clinfo
->ifinfo
->ifname
, IFNAMSIZ
);
1114 class_mod
.class_handle
= clinfo
->handle
;
1115 if (sctype
& HFSC_REALTIMESC
)
1116 class_mod
.service_curve
= hfsc_clinfo
->rsc
;
1117 else if (sctype
& HFSC_LINKSHARINGSC
)
1118 class_mod
.service_curve
= hfsc_clinfo
->fsc
;
1119 else if (sctype
& HFSC_UPPERLIMITSC
)
1120 class_mod
.service_curve
= hfsc_clinfo
->usc
;
1122 return (QOPERR_INVAL
);
1123 class_mod
.sctype
= sctype
;
1125 if (ioctl(hfsc_fd
, HFSC_MOD_CLASS
, &class_mod
) < 0)
1126 return (QOPERR_SYSCALL
);
1131 hfsc_delete_class(struct classinfo
*clinfo
)
1133 struct hfsc_delete_class class_delete
;
1135 if (clinfo
->handle
== HFSC_NULLCLASS_HANDLE
)
1138 memset(&class_delete
, 0, sizeof(class_delete
));
1139 strncpy(class_delete
.iface
.hfsc_ifname
, clinfo
->ifinfo
->ifname
,
1141 class_delete
.class_handle
= clinfo
->handle
;
1143 if (ioctl(hfsc_fd
, HFSC_DEL_CLASS
, &class_delete
) < 0)
1144 return (QOPERR_SYSCALL
);
1149 hfsc_add_filter(struct fltrinfo
*fltrinfo
)
1151 struct hfsc_add_filter fltr_add
;
1153 memset(&fltr_add
, 0, sizeof(fltr_add
));
1154 strncpy(fltr_add
.iface
.hfsc_ifname
, fltrinfo
->clinfo
->ifinfo
->ifname
,
1156 fltr_add
.class_handle
= fltrinfo
->clinfo
->handle
;
1157 fltr_add
.filter
= fltrinfo
->fltr
;
1159 if (ioctl(hfsc_fd
, HFSC_ADD_FILTER
, &fltr_add
) < 0)
1160 return (QOPERR_SYSCALL
);
1161 fltrinfo
->handle
= fltr_add
.filter_handle
;
1166 hfsc_delete_filter(struct fltrinfo
*fltrinfo
)
1168 struct hfsc_delete_filter fltr_del
;
1170 memset(&fltr_del
, 0, sizeof(fltr_del
));
1171 strncpy(fltr_del
.iface
.hfsc_ifname
, fltrinfo
->clinfo
->ifinfo
->ifname
,
1173 fltr_del
.filter_handle
= fltrinfo
->handle
;
1175 if (ioctl(hfsc_fd
, HFSC_DEL_FILTER
, &fltr_del
) < 0)
1176 return (QOPERR_SYSCALL
);