]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/contrib/altq/altq/altq_cbq.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / contrib / altq / altq / altq_cbq.c
1 /*      $FreeBSD$       */
2 /*      $KAME: altq_cbq.c,v 1.19 2003/09/17 14:23:25 kjc Exp $  */
3
4 /*
5  * Copyright (c) Sun Microsystems, Inc. 1993-1998 All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *      This product includes software developed by the SMCC Technology
21  *      Development Group at Sun Microsystems, Inc.
22  *
23  * 4. The name of the Sun Microsystems, Inc nor may not be used to endorse or
24  *      promote products derived from this software without specific prior
25  *      written permission.
26  *
27  * SUN MICROSYSTEMS DOES NOT CLAIM MERCHANTABILITY OF THIS SOFTWARE OR THE
28  * SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE.  The software is
29  * provided "as is" without express or implied warranty of any kind.
30  *
31  * These notices must be retained in any copies of any part of this software.
32  */
33
34 #if defined(__FreeBSD__) || defined(__NetBSD__)
35 #include "opt_altq.h"
36 #include "opt_inet.h"
37 #ifdef __FreeBSD__
38 #include "opt_inet6.h"
39 #endif
40 #endif /* __FreeBSD__ || __NetBSD__ */
41 #ifdef ALTQ_CBQ /* cbq is enabled by ALTQ_CBQ option in opt_altq.h */
42
43 #include <sys/param.h>
44 #include <sys/malloc.h>
45 #include <sys/mbuf.h>
46 #include <sys/socket.h>
47 #include <sys/systm.h>
48 #include <sys/proc.h>
49 #include <sys/errno.h>
50 #include <sys/time.h>
51 #ifdef ALTQ3_COMPAT
52 #include <sys/uio.h>
53 #include <sys/kernel.h>
54 #endif
55
56 #include <net/if.h>
57 #include <netinet/in.h>
58
59 #include <net/pfvar.h>
60 #include <altq/altq.h>
61 #include <altq/altq_cbq.h>
62 #ifdef ALTQ3_COMPAT
63 #include <altq/altq_conf.h>
64 #endif
65
66 #ifdef ALTQ3_COMPAT
67 /*
68  * Local Data structures.
69  */
70 static cbq_state_t *cbq_list = NULL;
71 #endif
72
73 /*
74  * Forward Declarations.
75  */
76 static int               cbq_class_destroy(cbq_state_t *, struct rm_class *);
77 static struct rm_class  *clh_to_clp(cbq_state_t *, u_int32_t);
78 static int               cbq_clear_interface(cbq_state_t *);
79 static int               cbq_request(struct ifaltq *, int, void *);
80 static int               cbq_enqueue(struct ifaltq *, struct mbuf *,
81                              struct altq_pktattr *);
82 static struct mbuf      *cbq_dequeue(struct ifaltq *, int);
83 static void              cbqrestart(struct ifaltq *);
84 static void              get_class_stats(class_stats_t *, struct rm_class *);
85 static void              cbq_purge(cbq_state_t *);
86 #ifdef ALTQ3_COMPAT
87 static int      cbq_add_class(struct cbq_add_class *);
88 static int      cbq_delete_class(struct cbq_delete_class *);
89 static int      cbq_modify_class(struct cbq_modify_class *);
90 static int      cbq_class_create(cbq_state_t *, struct cbq_add_class *,
91                                  struct rm_class *, struct rm_class *);
92 static int      cbq_clear_hierarchy(struct cbq_interface *);
93 static int      cbq_set_enable(struct cbq_interface *, int);
94 static int      cbq_ifattach(struct cbq_interface *);
95 static int      cbq_ifdetach(struct cbq_interface *);
96 static int      cbq_getstats(struct cbq_getstats *);
97
98 static int      cbq_add_filter(struct cbq_add_filter *);
99 static int      cbq_delete_filter(struct cbq_delete_filter *);
100 #endif /* ALTQ3_COMPAT */
101
102 /*
103  * int
104  * cbq_class_destroy(cbq_mod_state_t *, struct rm_class *) - This
105  *      function destroys a given traffic class.  Before destroying
106  *      the class, all traffic for that class is released.
107  */
108 static int
109 cbq_class_destroy(cbq_state_t *cbqp, struct rm_class *cl)
110 {
111         int     i;
112
113         /* delete the class */
114         rmc_delete_class(&cbqp->ifnp, cl);
115
116         /*
117          * free the class handle
118          */
119         for (i = 0; i < CBQ_MAX_CLASSES; i++)
120                 if (cbqp->cbq_class_tbl[i] == cl)
121                         cbqp->cbq_class_tbl[i] = NULL;
122
123         if (cl == cbqp->ifnp.root_)
124                 cbqp->ifnp.root_ = NULL;
125         if (cl == cbqp->ifnp.default_)
126                 cbqp->ifnp.default_ = NULL;
127 #ifdef ALTQ3_COMPAT
128         if (cl == cbqp->ifnp.ctl_)
129                 cbqp->ifnp.ctl_ = NULL;
130 #endif
131         return (0);
132 }
133
134 /* convert class handle to class pointer */
135 static struct rm_class *
136 clh_to_clp(cbq_state_t *cbqp, u_int32_t chandle)
137 {
138         int i;
139         struct rm_class *cl;
140
141         if (chandle == 0)
142                 return (NULL);
143         /*
144          * first, try optimistically the slot matching the lower bits of
145          * the handle.  if it fails, do the linear table search.
146          */
147         i = chandle % CBQ_MAX_CLASSES;
148         if ((cl = cbqp->cbq_class_tbl[i]) != NULL &&
149             cl->stats_.handle == chandle)
150                 return (cl);
151         for (i = 0; i < CBQ_MAX_CLASSES; i++)
152                 if ((cl = cbqp->cbq_class_tbl[i]) != NULL &&
153                     cl->stats_.handle == chandle)
154                         return (cl);
155         return (NULL);
156 }
157
158 static int
159 cbq_clear_interface(cbq_state_t *cbqp)
160 {
161         int              again, i;
162         struct rm_class *cl;
163
164 #ifdef ALTQ3_CLFIER_COMPAT
165         /* free the filters for this interface */
166         acc_discard_filters(&cbqp->cbq_classifier, NULL, 1);
167 #endif
168
169         /* clear out the classes now */
170         do {
171                 again = 0;
172                 for (i = 0; i < CBQ_MAX_CLASSES; i++) {
173                         if ((cl = cbqp->cbq_class_tbl[i]) != NULL) {
174                                 if (is_a_parent_class(cl))
175                                         again++;
176                                 else {
177                                         cbq_class_destroy(cbqp, cl);
178                                         cbqp->cbq_class_tbl[i] = NULL;
179                                         if (cl == cbqp->ifnp.root_)
180                                                 cbqp->ifnp.root_ = NULL;
181                                         if (cl == cbqp->ifnp.default_)
182                                                 cbqp->ifnp.default_ = NULL;
183 #ifdef ALTQ3_COMPAT
184                                         if (cl == cbqp->ifnp.ctl_)
185                                                 cbqp->ifnp.ctl_ = NULL;
186 #endif
187                                 }
188                         }
189                 }
190         } while (again);
191
192         return (0);
193 }
194
195 static int
196 cbq_request(struct ifaltq *ifq, int req, void *arg)
197 {
198         cbq_state_t     *cbqp = (cbq_state_t *)ifq->altq_disc;
199
200         IFQ_LOCK_ASSERT(ifq);
201
202         switch (req) {
203         case ALTRQ_PURGE:
204                 cbq_purge(cbqp);
205                 break;
206         }
207         return (0);
208 }
209
210 /* copy the stats info in rm_class to class_states_t */
211 static void
212 get_class_stats(class_stats_t *statsp, struct rm_class *cl)
213 {
214         statsp->xmit_cnt        = cl->stats_.xmit_cnt;
215         statsp->drop_cnt        = cl->stats_.drop_cnt;
216         statsp->over            = cl->stats_.over;
217         statsp->borrows         = cl->stats_.borrows;
218         statsp->overactions     = cl->stats_.overactions;
219         statsp->delays          = cl->stats_.delays;
220
221         statsp->depth           = cl->depth_;
222         statsp->priority        = cl->pri_;
223         statsp->maxidle         = cl->maxidle_;
224         statsp->minidle         = cl->minidle_;
225         statsp->offtime         = cl->offtime_;
226         statsp->qmax            = qlimit(cl->q_);
227         statsp->ns_per_byte     = cl->ns_per_byte_;
228         statsp->wrr_allot       = cl->w_allotment_;
229         statsp->qcnt            = qlen(cl->q_);
230         statsp->avgidle         = cl->avgidle_;
231
232         statsp->qtype           = qtype(cl->q_);
233 #ifdef ALTQ_RED
234         if (q_is_red(cl->q_))
235                 red_getstats(cl->red_, &statsp->red[0]);
236 #endif
237 #ifdef ALTQ_RIO
238         if (q_is_rio(cl->q_))
239                 rio_getstats((rio_t *)cl->red_, &statsp->red[0]);
240 #endif
241 }
242
243 int
244 cbq_pfattach(struct pf_altq *a)
245 {
246         struct ifnet    *ifp;
247         int              s, error;
248
249         if ((ifp = ifunit(a->ifname)) == NULL || a->altq_disc == NULL)
250                 return (EINVAL);
251 #ifdef __NetBSD__
252         s = splnet();
253 #else
254         s = splimp();
255 #endif
256         error = altq_attach(&ifp->if_snd, ALTQT_CBQ, a->altq_disc,
257             cbq_enqueue, cbq_dequeue, cbq_request, NULL, NULL);
258         splx(s);
259         return (error);
260 }
261
262 int
263 cbq_add_altq(struct pf_altq *a)
264 {
265         cbq_state_t     *cbqp;
266         struct ifnet    *ifp;
267
268         if ((ifp = ifunit(a->ifname)) == NULL)
269                 return (EINVAL);
270         if (!ALTQ_IS_READY(&ifp->if_snd))
271                 return (ENODEV);
272
273         /* allocate and initialize cbq_state_t */
274         cbqp = malloc(sizeof(cbq_state_t), M_DEVBUF, M_WAITOK);
275         if (cbqp == NULL)
276                 return (ENOMEM);
277         bzero(cbqp, sizeof(cbq_state_t));
278         CALLOUT_INIT(&cbqp->cbq_callout);
279         cbqp->cbq_qlen = 0;
280         cbqp->ifnp.ifq_ = &ifp->if_snd;     /* keep the ifq */
281
282         /* keep the state in pf_altq */
283         a->altq_disc = cbqp;
284
285         return (0);
286 }
287
288 int
289 cbq_remove_altq(struct pf_altq *a)
290 {
291         cbq_state_t     *cbqp;
292
293         if ((cbqp = a->altq_disc) == NULL)
294                 return (EINVAL);
295         a->altq_disc = NULL;
296
297         cbq_clear_interface(cbqp);
298
299         if (cbqp->ifnp.default_)
300                 cbq_class_destroy(cbqp, cbqp->ifnp.default_);
301         if (cbqp->ifnp.root_)
302                 cbq_class_destroy(cbqp, cbqp->ifnp.root_);
303
304         /* deallocate cbq_state_t */
305         free(cbqp, M_DEVBUF);
306
307         return (0);
308 }
309
310 int
311 cbq_add_queue(struct pf_altq *a)
312 {
313         struct rm_class *borrow, *parent;
314         cbq_state_t     *cbqp;
315         struct rm_class *cl;
316         struct cbq_opts *opts;
317         int             i;
318
319         if ((cbqp = a->altq_disc) == NULL)
320                 return (EINVAL);
321         if (a->qid == 0)
322                 return (EINVAL);
323
324         /*
325          * find a free slot in the class table.  if the slot matching
326          * the lower bits of qid is free, use this slot.  otherwise,
327          * use the first free slot.
328          */
329         i = a->qid % CBQ_MAX_CLASSES;
330         if (cbqp->cbq_class_tbl[i] != NULL) {
331                 for (i = 0; i < CBQ_MAX_CLASSES; i++)
332                         if (cbqp->cbq_class_tbl[i] == NULL)
333                                 break;
334                 if (i == CBQ_MAX_CLASSES)
335                         return (EINVAL);
336         }
337
338         opts = &a->pq_u.cbq_opts;
339         /* check parameters */
340         if (a->priority >= CBQ_MAXPRI)
341                 return (EINVAL);
342
343         /* Get pointers to parent and borrow classes.  */
344         parent = clh_to_clp(cbqp, a->parent_qid);
345         if (opts->flags & CBQCLF_BORROW)
346                 borrow = parent;
347         else
348                 borrow = NULL;
349
350         /*
351          * A class must borrow from it's parent or it can not
352          * borrow at all.  Hence, borrow can be null.
353          */
354         if (parent == NULL && (opts->flags & CBQCLF_ROOTCLASS) == 0) {
355                 printf("cbq_add_queue: no parent class!\n");
356                 return (EINVAL);
357         }
358
359         if ((borrow != parent)  && (borrow != NULL)) {
360                 printf("cbq_add_class: borrow class != parent\n");
361                 return (EINVAL);
362         }
363
364         /*
365          * check parameters
366          */
367         switch (opts->flags & CBQCLF_CLASSMASK) {
368         case CBQCLF_ROOTCLASS:
369                 if (parent != NULL)
370                         return (EINVAL);
371                 if (cbqp->ifnp.root_)
372                         return (EINVAL);
373                 break;
374         case CBQCLF_DEFCLASS:
375                 if (cbqp->ifnp.default_)
376                         return (EINVAL);
377                 break;
378         case 0:
379                 if (a->qid == 0)
380                         return (EINVAL);
381                 break;
382         default:
383                 /* more than two flags bits set */
384                 return (EINVAL);
385         }
386
387         /*
388          * create a class.  if this is a root class, initialize the
389          * interface.
390          */
391         if ((opts->flags & CBQCLF_CLASSMASK) == CBQCLF_ROOTCLASS) {
392                 rmc_init(cbqp->ifnp.ifq_, &cbqp->ifnp, opts->ns_per_byte,
393                     cbqrestart, a->qlimit, RM_MAXQUEUED,
394                     opts->maxidle, opts->minidle, opts->offtime,
395                     opts->flags);
396                 cl = cbqp->ifnp.root_;
397         } else {
398                 cl = rmc_newclass(a->priority,
399                                   &cbqp->ifnp, opts->ns_per_byte,
400                                   rmc_delay_action, a->qlimit, parent, borrow,
401                                   opts->maxidle, opts->minidle, opts->offtime,
402                                   opts->pktsize, opts->flags);
403         }
404         if (cl == NULL)
405                 return (ENOMEM);
406
407         /* return handle to user space. */
408         cl->stats_.handle = a->qid;
409         cl->stats_.depth = cl->depth_;
410
411         /* save the allocated class */
412         cbqp->cbq_class_tbl[i] = cl;
413
414         if ((opts->flags & CBQCLF_CLASSMASK) == CBQCLF_DEFCLASS)
415                 cbqp->ifnp.default_ = cl;
416
417         return (0);
418 }
419
420 int
421 cbq_remove_queue(struct pf_altq *a)
422 {
423         struct rm_class *cl;
424         cbq_state_t     *cbqp;
425         int             i;
426
427         if ((cbqp = a->altq_disc) == NULL)
428                 return (EINVAL);
429
430         if ((cl = clh_to_clp(cbqp, a->qid)) == NULL)
431                 return (EINVAL);
432
433         /* if we are a parent class, then return an error. */
434         if (is_a_parent_class(cl))
435                 return (EINVAL);
436
437         /* delete the class */
438         rmc_delete_class(&cbqp->ifnp, cl);
439
440         /*
441          * free the class handle
442          */
443         for (i = 0; i < CBQ_MAX_CLASSES; i++)
444                 if (cbqp->cbq_class_tbl[i] == cl) {
445                         cbqp->cbq_class_tbl[i] = NULL;
446                         if (cl == cbqp->ifnp.root_)
447                                 cbqp->ifnp.root_ = NULL;
448                         if (cl == cbqp->ifnp.default_)
449                                 cbqp->ifnp.default_ = NULL;
450                         break;
451                 }
452
453         return (0);
454 }
455
456 int
457 cbq_getqstats(struct pf_altq *a, void *ubuf, int *nbytes)
458 {
459         cbq_state_t     *cbqp;
460         struct rm_class *cl;
461         class_stats_t    stats;
462         int              error = 0;
463
464         if ((cbqp = altq_lookup(a->ifname, ALTQT_CBQ)) == NULL)
465                 return (EBADF);
466
467         if ((cl = clh_to_clp(cbqp, a->qid)) == NULL)
468                 return (EINVAL);
469
470         if (*nbytes < sizeof(stats))
471                 return (EINVAL);
472
473         get_class_stats(&stats, cl);
474
475         if ((error = copyout((caddr_t)&stats, ubuf, sizeof(stats))) != 0)
476                 return (error);
477         *nbytes = sizeof(stats);
478         return (0);
479 }
480
481 /*
482  * int
483  * cbq_enqueue(struct ifaltq *ifq, struct mbuf *m, struct altq_pktattr *pattr)
484  *              - Queue data packets.
485  *
486  *      cbq_enqueue is set to ifp->if_altqenqueue and called by an upper
487  *      layer (e.g. ether_output).  cbq_enqueue queues the given packet
488  *      to the cbq, then invokes the driver's start routine.
489  *
490  *      Assumptions:    called in splimp
491  *      Returns:        0 if the queueing is successful.
492  *                      ENOBUFS if a packet dropping occurred as a result of
493  *                      the queueing.
494  */
495
496 static int
497 cbq_enqueue(struct ifaltq *ifq, struct mbuf *m, struct altq_pktattr *pktattr)
498 {
499         cbq_state_t     *cbqp = (cbq_state_t *)ifq->altq_disc;
500         struct rm_class *cl;
501         struct pf_mtag  *t;
502         int              len;
503
504         IFQ_LOCK_ASSERT(ifq);
505
506         /* grab class set by classifier */
507         if ((m->m_flags & M_PKTHDR) == 0) {
508                 /* should not happen */
509                 printf("altq: packet for %s does not have pkthdr\n",
510                     ifq->altq_ifp->if_xname);
511                 m_freem(m);
512                 return (ENOBUFS);
513         }
514         cl = NULL;
515         if ((t = pf_find_mtag(m)) != NULL)
516                 cl = clh_to_clp(cbqp, t->qid);
517 #ifdef ALTQ3_COMPAT
518         else if ((ifq->altq_flags & ALTQF_CLASSIFY) && pktattr != NULL)
519                 cl = pktattr->pattr_class;
520 #endif
521         if (cl == NULL) {
522                 cl = cbqp->ifnp.default_;
523                 if (cl == NULL) {
524                         m_freem(m);
525                         return (ENOBUFS);
526                 }
527         }
528 #ifdef ALTQ3_COMPAT
529         if (pktattr != NULL)
530                 cl->pktattr_ = pktattr;  /* save proto hdr used by ECN */
531         else
532 #endif
533                 cl->pktattr_ = NULL;
534         len = m_pktlen(m);
535         if (rmc_queue_packet(cl, m) != 0) {
536                 /* drop occurred.  some mbuf was freed in rmc_queue_packet. */
537                 PKTCNTR_ADD(&cl->stats_.drop_cnt, len);
538                 return (ENOBUFS);
539         }
540
541         /* successfully queued. */
542         ++cbqp->cbq_qlen;
543         IFQ_INC_LEN(ifq);
544         return (0);
545 }
546
547 static struct mbuf *
548 cbq_dequeue(struct ifaltq *ifq, int op)
549 {
550         cbq_state_t     *cbqp = (cbq_state_t *)ifq->altq_disc;
551         struct mbuf     *m;
552
553         IFQ_LOCK_ASSERT(ifq);
554
555         m = rmc_dequeue_next(&cbqp->ifnp, op);
556
557         if (m && op == ALTDQ_REMOVE) {
558                 --cbqp->cbq_qlen;  /* decrement # of packets in cbq */
559                 IFQ_DEC_LEN(ifq);
560
561                 /* Update the class. */
562                 rmc_update_class_util(&cbqp->ifnp);
563         }
564         return (m);
565 }
566
567 /*
568  * void
569  * cbqrestart(queue_t *) - Restart sending of data.
570  * called from rmc_restart in splimp via timeout after waking up
571  * a suspended class.
572  *      Returns:        NONE
573  */
574
575 static void
576 cbqrestart(struct ifaltq *ifq)
577 {
578         cbq_state_t     *cbqp;
579         struct ifnet    *ifp;
580
581         IFQ_LOCK_ASSERT(ifq);
582
583         if (!ALTQ_IS_ENABLED(ifq))
584                 /* cbq must have been detached */
585                 return;
586
587         if ((cbqp = (cbq_state_t *)ifq->altq_disc) == NULL)
588                 /* should not happen */
589                 return;
590
591         ifp = ifq->altq_ifp;
592         if (ifp->if_start &&
593             cbqp->cbq_qlen > 0 && (ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) {
594                 IFQ_UNLOCK(ifq);
595                 (*ifp->if_start)(ifp);
596                 IFQ_LOCK(ifq);
597         }
598 }
599
600 static void cbq_purge(cbq_state_t *cbqp)
601 {
602         struct rm_class *cl;
603         int              i;
604
605         for (i = 0; i < CBQ_MAX_CLASSES; i++)
606                 if ((cl = cbqp->cbq_class_tbl[i]) != NULL)
607                         rmc_dropall(cl);
608         if (ALTQ_IS_ENABLED(cbqp->ifnp.ifq_))
609                 cbqp->ifnp.ifq_->ifq_len = 0;
610 }
611 #ifdef ALTQ3_COMPAT
612
613 static int
614 cbq_add_class(acp)
615         struct cbq_add_class *acp;
616 {
617         char            *ifacename;
618         struct rm_class *borrow, *parent;
619         cbq_state_t     *cbqp;
620
621         ifacename = acp->cbq_iface.cbq_ifacename;
622         if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
623                 return (EBADF);
624
625         /* check parameters */
626         if (acp->cbq_class.priority >= CBQ_MAXPRI ||
627             acp->cbq_class.maxq > CBQ_MAXQSIZE)
628                 return (EINVAL);
629
630         /* Get pointers to parent and borrow classes.  */
631         parent = clh_to_clp(cbqp, acp->cbq_class.parent_class_handle);
632         borrow = clh_to_clp(cbqp, acp->cbq_class.borrow_class_handle);
633
634         /*
635          * A class must borrow from it's parent or it can not
636          * borrow at all.  Hence, borrow can be null.
637          */
638         if (parent == NULL && (acp->cbq_class.flags & CBQCLF_ROOTCLASS) == 0) {
639                 printf("cbq_add_class: no parent class!\n");
640                 return (EINVAL);
641         }
642
643         if ((borrow != parent)  && (borrow != NULL)) {
644                 printf("cbq_add_class: borrow class != parent\n");
645                 return (EINVAL);
646         }
647
648         return cbq_class_create(cbqp, acp, parent, borrow);
649 }
650
651 static int
652 cbq_delete_class(dcp)
653         struct cbq_delete_class *dcp;
654 {
655         char            *ifacename;
656         struct rm_class *cl;
657         cbq_state_t     *cbqp;
658
659         ifacename = dcp->cbq_iface.cbq_ifacename;
660         if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
661                 return (EBADF);
662
663         if ((cl = clh_to_clp(cbqp, dcp->cbq_class_handle)) == NULL)
664                 return (EINVAL);
665
666         /* if we are a parent class, then return an error. */
667         if (is_a_parent_class(cl))
668                 return (EINVAL);
669
670         /* if a filter has a reference to this class delete the filter */
671         acc_discard_filters(&cbqp->cbq_classifier, cl, 0);
672
673         return cbq_class_destroy(cbqp, cl);
674 }
675
676 static int
677 cbq_modify_class(acp)
678         struct cbq_modify_class *acp;
679 {
680         char            *ifacename;
681         struct rm_class *cl;
682         cbq_state_t     *cbqp;
683
684         ifacename = acp->cbq_iface.cbq_ifacename;
685         if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
686                 return (EBADF);
687
688         /* Get pointer to this class */
689         if ((cl = clh_to_clp(cbqp, acp->cbq_class_handle)) == NULL)
690                 return (EINVAL);
691
692         if (rmc_modclass(cl, acp->cbq_class.nano_sec_per_byte,
693                          acp->cbq_class.maxq, acp->cbq_class.maxidle,
694                          acp->cbq_class.minidle, acp->cbq_class.offtime,
695                          acp->cbq_class.pktsize) < 0)
696                 return (EINVAL);
697         return (0);
698 }
699
700 /*
701  * struct rm_class *
702  * cbq_class_create(cbq_mod_state_t *cbqp, struct cbq_add_class *acp,
703  *              struct rm_class *parent, struct rm_class *borrow)
704  *
705  * This function create a new traffic class in the CBQ class hierarchy of
706  * given paramters.  The class that created is either the root, default,
707  * or a new dynamic class.  If CBQ is not initilaized, the the root class
708  * will be created.
709  */
710 static int
711 cbq_class_create(cbqp, acp, parent, borrow)
712         cbq_state_t *cbqp;
713         struct cbq_add_class *acp;
714         struct rm_class *parent, *borrow;
715 {
716         struct rm_class *cl;
717         cbq_class_spec_t *spec = &acp->cbq_class;
718         u_int32_t       chandle;
719         int             i;
720
721         /*
722          * allocate class handle
723          */
724         for (i = 1; i < CBQ_MAX_CLASSES; i++)
725                 if (cbqp->cbq_class_tbl[i] == NULL)
726                         break;
727         if (i == CBQ_MAX_CLASSES)
728                 return (EINVAL);
729         chandle = i;    /* use the slot number as class handle */
730
731         /*
732          * create a class.  if this is a root class, initialize the
733          * interface.
734          */
735         if ((spec->flags & CBQCLF_CLASSMASK) == CBQCLF_ROOTCLASS) {
736                 rmc_init(cbqp->ifnp.ifq_, &cbqp->ifnp, spec->nano_sec_per_byte,
737                          cbqrestart, spec->maxq, RM_MAXQUEUED,
738                          spec->maxidle, spec->minidle, spec->offtime,
739                          spec->flags);
740                 cl = cbqp->ifnp.root_;
741         } else {
742                 cl = rmc_newclass(spec->priority,
743                                   &cbqp->ifnp, spec->nano_sec_per_byte,
744                                   rmc_delay_action, spec->maxq, parent, borrow,
745                                   spec->maxidle, spec->minidle, spec->offtime,
746                                   spec->pktsize, spec->flags);
747         }
748         if (cl == NULL)
749                 return (ENOMEM);
750
751         /* return handle to user space. */
752         acp->cbq_class_handle = chandle;
753
754         cl->stats_.handle = chandle;
755         cl->stats_.depth = cl->depth_;
756
757         /* save the allocated class */
758         cbqp->cbq_class_tbl[i] = cl;
759
760         if ((spec->flags & CBQCLF_CLASSMASK) == CBQCLF_DEFCLASS)
761                 cbqp->ifnp.default_ = cl;
762         if ((spec->flags & CBQCLF_CLASSMASK) == CBQCLF_CTLCLASS)
763                 cbqp->ifnp.ctl_ = cl;
764
765         return (0);
766 }
767
768 static int
769 cbq_add_filter(afp)
770         struct cbq_add_filter *afp;
771 {
772         char            *ifacename;
773         cbq_state_t     *cbqp;
774         struct rm_class *cl;
775
776         ifacename = afp->cbq_iface.cbq_ifacename;
777         if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
778                 return (EBADF);
779
780         /* Get the pointer to class. */
781         if ((cl = clh_to_clp(cbqp, afp->cbq_class_handle)) == NULL)
782                 return (EINVAL);
783
784         return acc_add_filter(&cbqp->cbq_classifier, &afp->cbq_filter,
785                               cl, &afp->cbq_filter_handle);
786 }
787
788 static int
789 cbq_delete_filter(dfp)
790         struct cbq_delete_filter *dfp;
791 {
792         char            *ifacename;
793         cbq_state_t     *cbqp;
794
795         ifacename = dfp->cbq_iface.cbq_ifacename;
796         if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
797                 return (EBADF);
798
799         return acc_delete_filter(&cbqp->cbq_classifier,
800                                  dfp->cbq_filter_handle);
801 }
802
803 /*
804  * cbq_clear_hierarchy deletes all classes and their filters on the
805  * given interface.
806  */
807 static int
808 cbq_clear_hierarchy(ifacep)
809         struct cbq_interface *ifacep;
810 {
811         char            *ifacename;
812         cbq_state_t     *cbqp;
813
814         ifacename = ifacep->cbq_ifacename;
815         if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
816                 return (EBADF);
817
818         return cbq_clear_interface(cbqp);
819 }
820
821 /*
822  * static int
823  * cbq_set_enable(struct cbq_enable *ep) - this function processed the
824  *      ioctl request to enable class based queueing.  It searches the list
825  *      of interfaces for the specified interface and then enables CBQ on
826  *      that interface.
827  *
828  *      Returns:        0, for no error.
829  *                      EBADF, for specified inteface not found.
830  */
831
832 static int
833 cbq_set_enable(ep, enable)
834         struct cbq_interface *ep;
835         int enable;
836 {
837         int     error = 0;
838         cbq_state_t     *cbqp;
839         char    *ifacename;
840
841         ifacename = ep->cbq_ifacename;
842         if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
843                 return (EBADF);
844
845         switch (enable) {
846         case ENABLE:
847                 if (cbqp->ifnp.root_ == NULL || cbqp->ifnp.default_ == NULL ||
848                     cbqp->ifnp.ctl_ == NULL) {
849                         if (cbqp->ifnp.root_ == NULL)
850                                 printf("No Root Class for %s\n", ifacename);
851                         if (cbqp->ifnp.default_ == NULL)
852                                 printf("No Default Class for %s\n", ifacename);
853                         if (cbqp->ifnp.ctl_ == NULL)
854                                 printf("No Control Class for %s\n", ifacename);
855                         error = EINVAL;
856                 } else if ((error = altq_enable(cbqp->ifnp.ifq_)) == 0) {
857                         cbqp->cbq_qlen = 0;
858                 }
859                 break;
860
861         case DISABLE:
862                 error = altq_disable(cbqp->ifnp.ifq_);
863                 break;
864         }
865         return (error);
866 }
867
868 static int
869 cbq_getstats(gsp)
870         struct cbq_getstats *gsp;
871 {
872         char            *ifacename;
873         int             i, n, nclasses;
874         cbq_state_t     *cbqp;
875         struct rm_class *cl;
876         class_stats_t   stats, *usp;
877         int error = 0;
878
879         ifacename = gsp->iface.cbq_ifacename;
880         nclasses = gsp->nclasses;
881         usp = gsp->stats;
882
883         if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
884                 return (EBADF);
885         if (nclasses <= 0)
886                 return (EINVAL);
887
888         for (n = 0, i = 0; n < nclasses && i < CBQ_MAX_CLASSES; n++, i++) {
889                 while ((cl = cbqp->cbq_class_tbl[i]) == NULL)
890                         if (++i >= CBQ_MAX_CLASSES)
891                                 goto out;
892
893                 get_class_stats(&stats, cl);
894                 stats.handle = cl->stats_.handle;
895
896                 if ((error = copyout((caddr_t)&stats, (caddr_t)usp++,
897                     sizeof(stats))) != 0)
898                         return (error);
899         }
900
901  out:
902         gsp->nclasses = n;
903         return (error);
904 }
905
906 static int
907 cbq_ifattach(ifacep)
908         struct cbq_interface *ifacep;
909 {
910         int             error = 0;
911         char            *ifacename;
912         cbq_state_t     *new_cbqp;
913         struct ifnet    *ifp;
914
915         ifacename = ifacep->cbq_ifacename;
916         if ((ifp = ifunit(ifacename)) == NULL)
917                 return (ENXIO);
918         if (!ALTQ_IS_READY(&ifp->if_snd))
919                 return (ENXIO);
920
921         /* allocate and initialize cbq_state_t */
922         new_cbqp = malloc(sizeof(cbq_state_t), M_DEVBUF, M_WAITOK);
923         if (new_cbqp == NULL)
924                 return (ENOMEM);
925         bzero(new_cbqp, sizeof(cbq_state_t));
926         CALLOUT_INIT(&new_cbqp->cbq_callout);
927
928         new_cbqp->cbq_qlen = 0;
929         new_cbqp->ifnp.ifq_ = &ifp->if_snd;         /* keep the ifq */
930
931         /*
932          * set CBQ to this ifnet structure.
933          */
934         error = altq_attach(&ifp->if_snd, ALTQT_CBQ, new_cbqp,
935                             cbq_enqueue, cbq_dequeue, cbq_request,
936                             &new_cbqp->cbq_classifier, acc_classify);
937         if (error) {
938                 free(new_cbqp, M_DEVBUF);
939                 return (error);
940         }
941
942         /* prepend to the list of cbq_state_t's. */
943         new_cbqp->cbq_next = cbq_list;
944         cbq_list = new_cbqp;
945
946         return (0);
947 }
948
949 static int
950 cbq_ifdetach(ifacep)
951         struct cbq_interface *ifacep;
952 {
953         char            *ifacename;
954         cbq_state_t     *cbqp;
955
956         ifacename = ifacep->cbq_ifacename;
957         if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
958                 return (EBADF);
959
960         (void)cbq_set_enable(ifacep, DISABLE);
961
962         cbq_clear_interface(cbqp);
963
964         /* remove CBQ from the ifnet structure. */
965         (void)altq_detach(cbqp->ifnp.ifq_);
966
967         /* remove from the list of cbq_state_t's. */
968         if (cbq_list == cbqp)
969                 cbq_list = cbqp->cbq_next;
970         else {
971                 cbq_state_t *cp;
972
973                 for (cp = cbq_list; cp != NULL; cp = cp->cbq_next)
974                         if (cp->cbq_next == cbqp) {
975                                 cp->cbq_next = cbqp->cbq_next;
976                                 break;
977                         }
978                 ASSERT(cp != NULL);
979         }
980
981         /* deallocate cbq_state_t */
982         free(cbqp, M_DEVBUF);
983
984         return (0);
985 }
986
987 /*
988  * cbq device interface
989  */
990
991 altqdev_decl(cbq);
992
993 int
994 cbqopen(dev, flag, fmt, p)
995         dev_t dev;
996         int flag, fmt;
997 #if (__FreeBSD_version > 500000)
998         struct thread *p;
999 #else
1000         struct proc *p;
1001 #endif
1002 {
1003         return (0);
1004 }
1005
1006 int
1007 cbqclose(dev, flag, fmt, p)
1008         dev_t dev;
1009         int flag, fmt;
1010 #if (__FreeBSD_version > 500000)
1011         struct thread *p;
1012 #else
1013         struct proc *p;
1014 #endif
1015 {
1016         struct ifnet *ifp;
1017         struct cbq_interface iface;
1018         int err, error = 0;
1019
1020         while (cbq_list) {
1021                 ifp = cbq_list->ifnp.ifq_->altq_ifp;
1022                 sprintf(iface.cbq_ifacename, "%s", ifp->if_xname);
1023                 err = cbq_ifdetach(&iface);
1024                 if (err != 0 && error == 0)
1025                         error = err;
1026         }
1027
1028         return (error);
1029 }
1030
1031 int
1032 cbqioctl(dev, cmd, addr, flag, p)
1033         dev_t dev;
1034         ioctlcmd_t cmd;
1035         caddr_t addr;
1036         int flag;
1037 #if (__FreeBSD_version > 500000)
1038         struct thread *p;
1039 #else
1040         struct proc *p;
1041 #endif
1042 {
1043         int     error = 0;
1044
1045         /* check cmd for superuser only */
1046         switch (cmd) {
1047         case CBQ_GETSTATS:
1048                 /* currently only command that an ordinary user can call */
1049                 break;
1050         default:
1051 #if (__FreeBSD_version > 700000)
1052                 error = priv_check(p, PRIV_ALTQ_MANAGE);
1053 #elsif (__FreeBSD_version > 400000)
1054                 error = suser(p);
1055 #else
1056                 error = suser(p->p_ucred, &p->p_acflag);
1057 #endif
1058                 if (error)
1059                         return (error);
1060                 break;
1061         }
1062
1063         switch (cmd) {
1064
1065         case CBQ_ENABLE:
1066                 error = cbq_set_enable((struct cbq_interface *)addr, ENABLE);
1067                 break;
1068
1069         case CBQ_DISABLE:
1070                 error = cbq_set_enable((struct cbq_interface *)addr, DISABLE);
1071                 break;
1072
1073         case CBQ_ADD_FILTER:
1074                 error = cbq_add_filter((struct cbq_add_filter *)addr);
1075                 break;
1076
1077         case CBQ_DEL_FILTER:
1078                 error = cbq_delete_filter((struct cbq_delete_filter *)addr);
1079                 break;
1080
1081         case CBQ_ADD_CLASS:
1082                 error = cbq_add_class((struct cbq_add_class *)addr);
1083                 break;
1084
1085         case CBQ_DEL_CLASS:
1086                 error = cbq_delete_class((struct cbq_delete_class *)addr);
1087                 break;
1088
1089         case CBQ_MODIFY_CLASS:
1090                 error = cbq_modify_class((struct cbq_modify_class *)addr);
1091                 break;
1092
1093         case CBQ_CLEAR_HIERARCHY:
1094                 error = cbq_clear_hierarchy((struct cbq_interface *)addr);
1095                 break;
1096
1097         case CBQ_IF_ATTACH:
1098                 error = cbq_ifattach((struct cbq_interface *)addr);
1099                 break;
1100
1101         case CBQ_IF_DETACH:
1102                 error = cbq_ifdetach((struct cbq_interface *)addr);
1103                 break;
1104
1105         case CBQ_GETSTATS:
1106                 error = cbq_getstats((struct cbq_getstats *)addr);
1107                 break;
1108
1109         default:
1110                 error = EINVAL;
1111                 break;
1112         }
1113
1114         return error;
1115 }
1116
1117 #if 0
1118 /* for debug */
1119 static void cbq_class_dump(int);
1120
1121 static void cbq_class_dump(i)
1122         int i;
1123 {
1124         struct rm_class *cl;
1125         rm_class_stats_t *s;
1126         struct _class_queue_ *q;
1127
1128         if (cbq_list == NULL) {
1129                 printf("cbq_class_dump: no cbq_state found\n");
1130                 return;
1131         }
1132         cl = cbq_list->cbq_class_tbl[i];
1133
1134         printf("class %d cl=%p\n", i, cl);
1135         if (cl != NULL) {
1136                 s = &cl->stats_;
1137                 q = cl->q_;
1138
1139                 printf("pri=%d, depth=%d, maxrate=%d, allotment=%d\n",
1140                        cl->pri_, cl->depth_, cl->maxrate_, cl->allotment_);
1141                 printf("w_allotment=%d, bytes_alloc=%d, avgidle=%d, maxidle=%d\n",
1142                        cl->w_allotment_, cl->bytes_alloc_, cl->avgidle_,
1143                        cl->maxidle_);
1144                 printf("minidle=%d, offtime=%d, sleeping=%d, leaf=%d\n",
1145                        cl->minidle_, cl->offtime_, cl->sleeping_, cl->leaf_);
1146                 printf("handle=%d, depth=%d, packets=%d, bytes=%d\n",
1147                        s->handle, s->depth,
1148                        (int)s->xmit_cnt.packets, (int)s->xmit_cnt.bytes);
1149                 printf("over=%d\n, borrows=%d, drops=%d, overactions=%d, delays=%d\n",
1150                        s->over, s->borrows, (int)s->drop_cnt.packets,
1151                        s->overactions, s->delays);
1152                 printf("tail=%p, head=%p, qlen=%d, qlim=%d, qthresh=%d,qtype=%d\n",
1153                        q->tail_, q->head_, q->qlen_, q->qlim_,
1154                        q->qthresh_, q->qtype_);
1155         }
1156 }
1157 #endif /* 0 */
1158
1159 #ifdef KLD_MODULE
1160
1161 static struct altqsw cbq_sw =
1162         {"cbq", cbqopen, cbqclose, cbqioctl};
1163
1164 ALTQ_MODULE(altq_cbq, ALTQT_CBQ, &cbq_sw);
1165 MODULE_DEPEND(altq_cbq, altq_red, 1, 1, 1);
1166 MODULE_DEPEND(altq_cbq, altq_rio, 1, 1, 1);
1167
1168 #endif /* KLD_MODULE */
1169 #endif /* ALTQ3_COMPAT */
1170
1171 #endif /* ALTQ_CBQ */