1 /* $KAME: qop_jobs.c,v 1.2 2002/10/26 07:09:22 kjc Exp $ */
3 * Copyright (c) 2001-2002, by the Rector and Board of Visitors of
4 * the University of Virginia.
7 * Redistribution and use in source and binary forms,
8 * with or without modification, are permitted provided
9 * that the following conditions are met:
11 * Redistributions of source code must retain the above
12 * copyright notice, this list of conditions and the following
15 * Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following
17 * disclaimer in the documentation and/or other materials provided
18 * with the distribution.
20 * Neither the name of the University of Virginia nor the names
21 * of its contributors may be used to endorse or promote products
22 * derived from this software without specific prior written
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
26 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
27 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
28 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
29 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
30 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
31 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
34 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
36 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
37 * THE POSSIBILITY OF SUCH DAMAGE.
40 * Copyright (C) 1999-2000
41 * Sony Computer Science Laboratories, Inc. All rights reserved.
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
46 * 1. Redistributions of source code must retain the above copyright
47 * notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 * notice, this list of conditions and the following disclaimer in the
50 * documentation and/or other materials provided with the distribution.
52 * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55 * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65 * JoBS - altq prototype implementation
67 * Author: Nicolas Christin <nicolas@cs.virginia.edu>
69 * JoBS algorithms originally devised and proposed by
70 * Nicolas Christin and Jorg Liebeherr.
71 * Grateful Acknowledgments to Tarek Abdelzaher for his help and
72 * comments, and to Kenjiro Cho for some helpful advice.
73 * Contributed by the Multimedia Networks Group at the University
76 * http://qosbox.cs.virginia.edu
80 #include <sys/param.h>
81 #include <sys/socket.h>
82 #include <sys/sockio.h>
83 #include <sys/ioctl.h>
84 #include <sys/fcntl.h>
86 #include <netinet/in.h>
87 #include <arpa/inet.h>
99 #include <altq/altq.h>
100 #include <altq/altq_var.h>
101 #include <altq/altq_jobs.h>
102 #include "altq_qop.h"
103 #include "qop_jobs.h"
104 #define SCALE_LOSS 32
107 static int qop_jobs_enable_hook(struct ifinfo
*);
109 static int qop_jobs_delete_class_hook(struct classinfo
*);
111 static int jobs_attach(struct ifinfo
*);
112 static int jobs_detach(struct ifinfo
*);
113 static int jobs_clear(struct ifinfo
*);
114 static int jobs_enable(struct ifinfo
*);
115 static int jobs_disable(struct ifinfo
*);
116 static int jobs_add_class(struct classinfo
*);
117 static int jobs_modify_class(struct classinfo
*, void *);
118 static int jobs_delete_class(struct classinfo
*);
119 static int jobs_add_filter(struct fltrinfo
*);
120 static int jobs_delete_filter(struct fltrinfo
*);
122 #define JOBS_DEVICE "/dev/altq/jobs"
124 static int jobs_fd
= -1;
125 static int jobs_refcount
= 0;
127 static struct qdisc_ops jobs_qdisc
= {
145 #define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0)
148 jobs_interface_parser(const char *ifname
, int argc
, char **argv
)
150 u_int bandwidth
= 100000000; /* 100 Mbps */
152 int qlimit
= 200; /* 200 packets */
153 int separate
= 0; /* by default: shared buffer */
159 if (EQUAL(*argv
, "bandwidth")) {
162 bandwidth
= atobps(*argv
);
163 } else if (EQUAL(*argv
, "tbrsize")) {
166 tbrsize
= atobytes(*argv
);
167 } else if (EQUAL(*argv
, "qlimit")) {
170 qlimit
= (int)strtol(*argv
, NULL
, 0);
171 } else if (EQUAL(*argv
, "separate")) {
174 } else if (EQUAL(*argv
, "jobs")) {
177 LOG(LOG_ERR
, 0, "Unknown keyword '%s'", *argv
);
183 if (qcmd_tbr_register(ifname
, bandwidth
, tbrsize
) != 0)
186 if (qcmd_jobs_add_if(ifname
, bandwidth
, qlimit
, separate
) != 0)
192 jobs_class_parser(const char *ifname
, const char *class_name
,
193 const char *parent_name
, int argc
, char **argv
)
195 int64_t adc
, rdc
, alc
, rlc
, arc
;
200 /* disable everything by default */
208 if (EQUAL(*argv
, "priority")) {
211 if (strtol(*argv
, NULL
, 0) < 0)
214 pri
= strtol(*argv
, NULL
, 0);
216 } else if (EQUAL(*argv
, "adc")) {
219 if (strtol(*argv
, NULL
, 0) < 0)
222 adc
= strtol(*argv
, NULL
, 0);
224 } else if (EQUAL(*argv
, "rdc")) {
227 if (strtol(*argv
, NULL
, 0) < 0)
230 rdc
= strtol(*argv
, NULL
, 0);
232 } else if (EQUAL(*argv
, "alc")) {
235 if (strtol(*argv
, NULL
, 0) < 0)
238 alc
= (int64_t)(strtod(*argv
, NULL
)
239 * ((int64_t)1 << SCALE_LOSS
));
241 * alc is given in fraction of 1, convert it
242 * to a fraction of 2^(SCALE_LOSS)
245 } else if (EQUAL(*argv
, "rlc")) {
248 if (strtol(*argv
, NULL
, 0) < 0)
251 rlc
= strtol(*argv
, NULL
, 0);
253 } else if (EQUAL(*argv
, "arc")) {
256 if (EQUAL(*argv
,"-1"))
261 } else if (EQUAL(*argv
, "default")) {
262 flags
|= JOCF_DEFAULTCLASS
;
265 "Unknown keyword '%s' in %s, line %d",
266 *argv
, altqconfigfile
, line_no
);
272 error
= qcmd_jobs_add_class(ifname
, class_name
, pri
,
273 adc
, rdc
, alc
, rlc
, arc
, flags
);
276 LOG(LOG_ERR
, errno
, "jobs_class_parser: %s",
287 qcmd_jobs_add_if(const char *ifname
, u_int bandwidth
, int qlimit
, int separate
)
291 error
= qop_jobs_add_if(NULL
, ifname
, bandwidth
, qlimit
, separate
);
293 LOG(LOG_ERR
, errno
, "%s: can't add jobs on interface '%s'",
294 qoperror(error
), ifname
);
299 qcmd_jobs_add_class(const char *ifname
, const char *class_name
, int pri
,
300 int64_t adc
, int64_t rdc
, int64_t alc
, int64_t rlc
, int64_t arc
,
303 struct ifinfo
*ifinfo
;
305 char name_adc
[20],name_alc
[20],name_rdc
[20],name_rlc
[20],name_arc
[20];
307 if ((ifinfo
= ifname2ifinfo(ifname
)) == NULL
)
308 error
= QOPERR_BADIF
;
310 error
= qop_jobs_add_class(NULL
, class_name
, ifinfo
,
311 pri
, adc
, rdc
, alc
, rlc
, arc
, flags
);
314 "jobs: %s: can't add class '%s' on interface '%s'",
315 qoperror(error
), class_name
, ifname
);
318 sprintf(name_adc
,"%.3f ms",(double)adc
/1000.);
320 sprintf(name_adc
,"N/A");
322 sprintf(name_alc
,"%.2f%%",
323 (double)100*alc
/((u_int64_t
)1 << SCALE_LOSS
));
325 sprintf(name_alc
,"N/A");
327 sprintf(name_rdc
,"%d",(int)rdc
);
329 sprintf(name_rdc
,"N/A");
331 sprintf(name_rlc
,"%d",(int)rlc
);
333 sprintf(name_rlc
,"N/A");
335 sprintf(name_arc
,"%.2f Mbps",(double)arc
/1000000.);
337 sprintf(name_arc
,"N/A");
340 "added '%s' (pri=%d,adc=%s,rdc=%s,alc=%s,rlc=%s,arc=%s) on interface '%s'\n",
341 class_name
,pri
,name_adc
,name_rdc
,name_alc
,name_rlc
,name_arc
,ifname
);
347 qcmd_jobs_modify_class(const char *ifname
, const char *class_name
, int pri
,
348 int64_t adc
, int64_t rdc
, int64_t alc
, int64_t rlc
, int64_t arc
)
350 struct ifinfo
*ifinfo
;
351 struct classinfo
*clinfo
;
353 if ((ifinfo
= ifname2ifinfo(ifname
)) == NULL
)
354 return (QOPERR_BADIF
);
356 if ((clinfo
= clname2clinfo(ifinfo
, class_name
)) == NULL
)
357 return (QOPERR_BADCLASS
);
359 return qop_jobs_modify_class(clinfo
, pri
, adc
, rdc
, alc
, rlc
, arc
);
366 qop_jobs_add_if(struct ifinfo
**rp
, const char *ifname
,
367 u_int bandwidth
, int qlimit
, int separate
)
369 struct ifinfo
*ifinfo
= NULL
;
370 struct jobs_ifinfo
*jobs_ifinfo
;
373 if ((jobs_ifinfo
= calloc(1, sizeof(*jobs_ifinfo
))) == NULL
)
374 return (QOPERR_NOMEM
);
375 jobs_ifinfo
->qlimit
= qlimit
;
376 jobs_ifinfo
->separate
= separate
;
378 error
= qop_add_if(&ifinfo
, ifname
, bandwidth
,
379 &jobs_qdisc
, jobs_ifinfo
);
391 qop_jobs_add_class(struct classinfo
**rp
, const char *class_name
,
392 struct ifinfo
*ifinfo
, int pri
,
393 int64_t adc
, int64_t rdc
, int64_t alc
, int64_t rlc
, int64_t arc
,
396 struct classinfo
*clinfo
;
397 struct jobs_ifinfo
*jobs_ifinfo
;
398 struct jobs_classinfo
*jobs_clinfo
= NULL
;
401 jobs_ifinfo
= ifinfo
->private;
402 if ((flags
& JOCF_DEFAULTCLASS
) && jobs_ifinfo
->default_class
!= NULL
)
403 return (QOPERR_CLASS_INVAL
);
405 if ((jobs_clinfo
= calloc(1, sizeof(*jobs_clinfo
))) == NULL
) {
406 error
= QOPERR_NOMEM
;
410 jobs_clinfo
->pri
= pri
;
411 jobs_clinfo
->adc
= adc
;
412 jobs_clinfo
->rdc
= rdc
;
413 jobs_clinfo
->alc
= alc
;
414 jobs_clinfo
->rlc
= rlc
;
415 jobs_clinfo
->arc
= arc
;
416 jobs_clinfo
->flags
= flags
;
418 if ((error
= qop_add_class(&clinfo
, class_name
, ifinfo
, NULL
,
422 /* set delete hook */
423 clinfo
->delete_hook
= qop_jobs_delete_class_hook
;
425 if (flags
& JOCF_DEFAULTCLASS
)
426 jobs_ifinfo
->default_class
= clinfo
;
433 if (jobs_clinfo
!= NULL
) {
435 clinfo
->private = NULL
;
442 * this is called from qop_delete_class() before a class is destroyed
443 * for discipline specific cleanup.
446 qop_jobs_delete_class_hook(struct classinfo
*clinfo
)
448 /* in fact this function doesn't do anything
449 * i'm not sure how/when it's used, so I just
452 struct jobs_classinfo
*jobs_clinfo
;
454 jobs_clinfo
= clinfo
->private;
459 qop_jobs_modify_class(struct classinfo
*clinfo
, int pri
,
460 int64_t adc
, int64_t rdc
, int64_t alc
, int64_t rlc
, int64_t arc
)
462 struct jobs_classinfo
*jobs_clinfo
= clinfo
->private;
465 int64_t adc_old
, rdc_old
, alc_old
, rlc_old
, arc_old
;
467 pri_old
= jobs_clinfo
->pri
;
468 adc_old
= jobs_clinfo
->adc
;
469 rdc_old
= jobs_clinfo
->rdc
;
470 alc_old
= jobs_clinfo
->alc
;
471 rlc_old
= jobs_clinfo
->rlc
;
472 arc_old
= jobs_clinfo
->arc
;
474 jobs_clinfo
= clinfo
->private;
475 jobs_clinfo
->adc
= adc
;
476 jobs_clinfo
->rdc
= rdc
;
477 jobs_clinfo
->alc
= alc
;
478 jobs_clinfo
->rlc
= rlc
;
479 jobs_clinfo
->arc
= arc
;
480 jobs_clinfo
->pri
= pri
;
482 error
= qop_modify_class(clinfo
, NULL
);
486 /* modify failed!, restore the old service guarantees */
487 jobs_clinfo
->adc
= adc_old
;
488 jobs_clinfo
->rdc
= rdc_old
;
489 jobs_clinfo
->alc
= alc_old
;
490 jobs_clinfo
->rlc
= rlc_old
;
491 jobs_clinfo
->arc
= arc_old
;
492 jobs_clinfo
->pri
= pri_old
;
498 * sanity check at enabling jobs:
499 * there must one default class for an interface
502 qop_jobs_enable_hook(struct ifinfo
*ifinfo
)
504 struct jobs_ifinfo
*jobs_ifinfo
;
506 jobs_ifinfo
= ifinfo
->private;
507 if (jobs_ifinfo
->default_class
== NULL
) {
508 LOG(LOG_ERR
, 0, "jobs: no default class on interface %s!",
510 return (QOPERR_CLASS
);
517 * system call interfaces for qdisc_ops
520 jobs_attach(struct ifinfo
*ifinfo
)
522 struct jobs_attach attach
;
525 (jobs_fd
= open(JOBS_DEVICE
, O_RDWR
)) < 0 &&
526 (jobs_fd
= open_module(JOBS_DEVICE
, O_RDWR
)) < 0) {
527 LOG(LOG_ERR
, errno
, "JOBS open");
528 return (QOPERR_SYSCALL
);
532 memset(&attach
, 0, sizeof(attach
));
533 strncpy(attach
.iface
.jobs_ifname
, ifinfo
->ifname
, IFNAMSIZ
);
534 attach
.bandwidth
= ifinfo
->bandwidth
;
535 attach
.qlimit
= (u_int
)((struct jobs_ifinfo
*)ifinfo
->private)->qlimit
;
536 attach
.separate
= (u_int
)((struct jobs_ifinfo
*)ifinfo
->private)->separate
;
538 if (ioctl(jobs_fd
, JOBS_IF_ATTACH
, (struct jobs_attach
*) &attach
) < 0)
539 return (QOPERR_SYSCALL
);
543 "jobs attached to %s (b/w = %d bps, buff = %d pkts [%s])\n",
544 attach
.iface
.jobs_ifname
,
545 (int) attach
.bandwidth
, (int) attach
.qlimit
,
546 attach
.separate
?"separate buffers":"shared buffer");
552 jobs_detach(struct ifinfo
*ifinfo
)
554 struct jobs_interface iface
;
556 memset(&iface
, 0, sizeof(iface
));
557 strncpy(iface
.jobs_ifname
, ifinfo
->ifname
, IFNAMSIZ
);
559 if (ioctl(jobs_fd
, JOBS_IF_DETACH
, &iface
) < 0)
560 return (QOPERR_SYSCALL
);
562 if (--jobs_refcount
== 0) {
570 jobs_enable(struct ifinfo
*ifinfo
)
572 struct jobs_interface iface
;
574 memset(&iface
, 0, sizeof(iface
));
575 strncpy(iface
.jobs_ifname
, ifinfo
->ifname
, IFNAMSIZ
);
577 if (ioctl(jobs_fd
, JOBS_ENABLE
, &iface
) < 0)
578 return (QOPERR_SYSCALL
);
583 jobs_disable(struct ifinfo
*ifinfo
)
585 struct jobs_interface iface
;
587 memset(&iface
, 0, sizeof(iface
));
588 strncpy(iface
.jobs_ifname
, ifinfo
->ifname
, IFNAMSIZ
);
590 if (ioctl(jobs_fd
, JOBS_DISABLE
, &iface
) < 0)
591 return (QOPERR_SYSCALL
);
596 jobs_clear(struct ifinfo
*ifinfo
)
598 struct jobs_interface iface
;
600 memset(&iface
, 0, sizeof(iface
));
601 strncpy(iface
.jobs_ifname
, ifinfo
->ifname
, IFNAMSIZ
);
603 if (ioctl(jobs_fd
, JOBS_CLEAR
, &iface
) < 0)
604 return (QOPERR_SYSCALL
);
609 jobs_add_class(struct classinfo
*clinfo
)
611 struct jobs_add_class class_add
;
612 struct jobs_classinfo
*jobs_clinfo
;
613 struct jobs_ifinfo
*jobs_ifinfo
;
615 jobs_ifinfo
= clinfo
->ifinfo
->private;
616 jobs_clinfo
= clinfo
->private;
618 memset(&class_add
, 0, sizeof(class_add
));
619 strncpy(class_add
.iface
.jobs_ifname
, clinfo
->ifinfo
->ifname
, IFNAMSIZ
);
620 class_add
.pri
= jobs_clinfo
->pri
;
621 class_add
.cl_adc
= jobs_clinfo
->adc
;
622 class_add
.cl_rdc
= jobs_clinfo
->rdc
;
623 class_add
.cl_alc
= jobs_clinfo
->alc
;
624 class_add
.cl_rlc
= jobs_clinfo
->rlc
;
625 class_add
.cl_arc
= jobs_clinfo
->arc
;
626 class_add
.flags
= jobs_clinfo
->flags
;
627 if (ioctl(jobs_fd
, JOBS_ADD_CLASS
, &class_add
) < 0) {
628 clinfo
->handle
= JOBS_NULLCLASS_HANDLE
;
629 return (QOPERR_SYSCALL
);
631 clinfo
->handle
= class_add
.class_handle
;
636 jobs_modify_class(struct classinfo
*clinfo
, void *arg
)
638 struct jobs_modify_class class_mod
;
639 struct jobs_classinfo
*jobs_clinfo
;
641 jobs_clinfo
= clinfo
->private;
643 memset(&class_mod
, 0, sizeof(class_mod
));
644 strncpy(class_mod
.iface
.jobs_ifname
, clinfo
->ifinfo
->ifname
, IFNAMSIZ
);
645 class_mod
.class_handle
= clinfo
->handle
;
647 class_mod
.pri
= jobs_clinfo
->pri
;
648 class_mod
.cl_adc
= jobs_clinfo
->adc
;
649 class_mod
.cl_rdc
= jobs_clinfo
->rdc
;
650 class_mod
.cl_alc
= jobs_clinfo
->alc
;
651 class_mod
.cl_rlc
= jobs_clinfo
->rlc
;
652 class_mod
.cl_arc
= jobs_clinfo
->arc
;
653 class_mod
.flags
= jobs_clinfo
->flags
;
655 if (ioctl(jobs_fd
, JOBS_MOD_CLASS
, &class_mod
) < 0)
656 return (QOPERR_SYSCALL
);
661 jobs_delete_class(struct classinfo
*clinfo
)
663 struct jobs_delete_class class_delete
;
665 if (clinfo
->handle
== JOBS_NULLCLASS_HANDLE
)
668 memset(&class_delete
, 0, sizeof(class_delete
));
669 strncpy(class_delete
.iface
.jobs_ifname
, clinfo
->ifinfo
->ifname
,
671 class_delete
.class_handle
= clinfo
->handle
;
673 if (ioctl(jobs_fd
, JOBS_DEL_CLASS
, &class_delete
) < 0)
674 return (QOPERR_SYSCALL
);
679 jobs_add_filter(struct fltrinfo
*fltrinfo
)
681 struct jobs_add_filter fltr_add
;
683 memset(&fltr_add
, 0, sizeof(fltr_add
));
684 strncpy(fltr_add
.iface
.jobs_ifname
, fltrinfo
->clinfo
->ifinfo
->ifname
,
686 fltr_add
.class_handle
= fltrinfo
->clinfo
->handle
;
687 fltr_add
.filter
= fltrinfo
->fltr
;
689 if (ioctl(jobs_fd
, JOBS_ADD_FILTER
, &fltr_add
) < 0)
690 return (QOPERR_SYSCALL
);
691 fltrinfo
->handle
= fltr_add
.filter_handle
;
696 jobs_delete_filter(struct fltrinfo
*fltrinfo
)
698 struct jobs_delete_filter fltr_del
;
700 memset(&fltr_del
, 0, sizeof(fltr_del
));
701 strncpy(fltr_del
.iface
.jobs_ifname
, fltrinfo
->clinfo
->ifinfo
->ifname
,
703 fltr_del
.filter_handle
= fltrinfo
->handle
;
705 if (ioctl(jobs_fd
, JOBS_DEL_FILTER
, &fltr_del
) < 0)
706 return (QOPERR_SYSCALL
);