]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/contrib/altq/altq/altq_cbq.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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_NOWAIT | M_ZERO);
275         if (cbqp == NULL)
276                 return (ENOMEM);
277         CALLOUT_INIT(&cbqp->cbq_callout);
278         cbqp->cbq_qlen = 0;
279         cbqp->ifnp.ifq_ = &ifp->if_snd;     /* keep the ifq */
280
281         /* keep the state in pf_altq */
282         a->altq_disc = cbqp;
283
284         return (0);
285 }
286
287 int
288 cbq_remove_altq(struct pf_altq *a)
289 {
290         cbq_state_t     *cbqp;
291
292         if ((cbqp = a->altq_disc) == NULL)
293                 return (EINVAL);
294         a->altq_disc = NULL;
295
296         cbq_clear_interface(cbqp);
297
298         if (cbqp->ifnp.default_)
299                 cbq_class_destroy(cbqp, cbqp->ifnp.default_);
300         if (cbqp->ifnp.root_)
301                 cbq_class_destroy(cbqp, cbqp->ifnp.root_);
302
303         /* deallocate cbq_state_t */
304         free(cbqp, M_DEVBUF);
305
306         return (0);
307 }
308
309 int
310 cbq_add_queue(struct pf_altq *a)
311 {
312         struct rm_class *borrow, *parent;
313         cbq_state_t     *cbqp;
314         struct rm_class *cl;
315         struct cbq_opts *opts;
316         int             i;
317
318         if ((cbqp = a->altq_disc) == NULL)
319                 return (EINVAL);
320         if (a->qid == 0)
321                 return (EINVAL);
322
323         /*
324          * find a free slot in the class table.  if the slot matching
325          * the lower bits of qid is free, use this slot.  otherwise,
326          * use the first free slot.
327          */
328         i = a->qid % CBQ_MAX_CLASSES;
329         if (cbqp->cbq_class_tbl[i] != NULL) {
330                 for (i = 0; i < CBQ_MAX_CLASSES; i++)
331                         if (cbqp->cbq_class_tbl[i] == NULL)
332                                 break;
333                 if (i == CBQ_MAX_CLASSES)
334                         return (EINVAL);
335         }
336
337         opts = &a->pq_u.cbq_opts;
338         /* check parameters */
339         if (a->priority >= CBQ_MAXPRI)
340                 return (EINVAL);
341
342         /* Get pointers to parent and borrow classes.  */
343         parent = clh_to_clp(cbqp, a->parent_qid);
344         if (opts->flags & CBQCLF_BORROW)
345                 borrow = parent;
346         else
347                 borrow = NULL;
348
349         /*
350          * A class must borrow from it's parent or it can not
351          * borrow at all.  Hence, borrow can be null.
352          */
353         if (parent == NULL && (opts->flags & CBQCLF_ROOTCLASS) == 0) {
354                 printf("cbq_add_queue: no parent class!\n");
355                 return (EINVAL);
356         }
357
358         if ((borrow != parent)  && (borrow != NULL)) {
359                 printf("cbq_add_class: borrow class != parent\n");
360                 return (EINVAL);
361         }
362
363         /*
364          * check parameters
365          */
366         switch (opts->flags & CBQCLF_CLASSMASK) {
367         case CBQCLF_ROOTCLASS:
368                 if (parent != NULL)
369                         return (EINVAL);
370                 if (cbqp->ifnp.root_)
371                         return (EINVAL);
372                 break;
373         case CBQCLF_DEFCLASS:
374                 if (cbqp->ifnp.default_)
375                         return (EINVAL);
376                 break;
377         case 0:
378                 if (a->qid == 0)
379                         return (EINVAL);
380                 break;
381         default:
382                 /* more than two flags bits set */
383                 return (EINVAL);
384         }
385
386         /*
387          * create a class.  if this is a root class, initialize the
388          * interface.
389          */
390         if ((opts->flags & CBQCLF_CLASSMASK) == CBQCLF_ROOTCLASS) {
391                 rmc_init(cbqp->ifnp.ifq_, &cbqp->ifnp, opts->ns_per_byte,
392                     cbqrestart, a->qlimit, RM_MAXQUEUED,
393                     opts->maxidle, opts->minidle, opts->offtime,
394                     opts->flags);
395                 cl = cbqp->ifnp.root_;
396         } else {
397                 cl = rmc_newclass(a->priority,
398                                   &cbqp->ifnp, opts->ns_per_byte,
399                                   rmc_delay_action, a->qlimit, parent, borrow,
400                                   opts->maxidle, opts->minidle, opts->offtime,
401                                   opts->pktsize, opts->flags);
402         }
403         if (cl == NULL)
404                 return (ENOMEM);
405
406         /* return handle to user space. */
407         cl->stats_.handle = a->qid;
408         cl->stats_.depth = cl->depth_;
409
410         /* save the allocated class */
411         cbqp->cbq_class_tbl[i] = cl;
412
413         if ((opts->flags & CBQCLF_CLASSMASK) == CBQCLF_DEFCLASS)
414                 cbqp->ifnp.default_ = cl;
415
416         return (0);
417 }
418
419 int
420 cbq_remove_queue(struct pf_altq *a)
421 {
422         struct rm_class *cl;
423         cbq_state_t     *cbqp;
424         int             i;
425
426         if ((cbqp = a->altq_disc) == NULL)
427                 return (EINVAL);
428
429         if ((cl = clh_to_clp(cbqp, a->qid)) == NULL)
430                 return (EINVAL);
431
432         /* if we are a parent class, then return an error. */
433         if (is_a_parent_class(cl))
434                 return (EINVAL);
435
436         /* delete the class */
437         rmc_delete_class(&cbqp->ifnp, cl);
438
439         /*
440          * free the class handle
441          */
442         for (i = 0; i < CBQ_MAX_CLASSES; i++)
443                 if (cbqp->cbq_class_tbl[i] == cl) {
444                         cbqp->cbq_class_tbl[i] = NULL;
445                         if (cl == cbqp->ifnp.root_)
446                                 cbqp->ifnp.root_ = NULL;
447                         if (cl == cbqp->ifnp.default_)
448                                 cbqp->ifnp.default_ = NULL;
449                         break;
450                 }
451
452         return (0);
453 }
454
455 int
456 cbq_getqstats(struct pf_altq *a, void *ubuf, int *nbytes)
457 {
458         cbq_state_t     *cbqp;
459         struct rm_class *cl;
460         class_stats_t    stats;
461         int              error = 0;
462
463         if ((cbqp = altq_lookup(a->ifname, ALTQT_CBQ)) == NULL)
464                 return (EBADF);
465
466         if ((cl = clh_to_clp(cbqp, a->qid)) == NULL)
467                 return (EINVAL);
468
469         if (*nbytes < sizeof(stats))
470                 return (EINVAL);
471
472         get_class_stats(&stats, cl);
473
474         if ((error = copyout((caddr_t)&stats, ubuf, sizeof(stats))) != 0)
475                 return (error);
476         *nbytes = sizeof(stats);
477         return (0);
478 }
479
480 /*
481  * int
482  * cbq_enqueue(struct ifaltq *ifq, struct mbuf *m, struct altq_pktattr *pattr)
483  *              - Queue data packets.
484  *
485  *      cbq_enqueue is set to ifp->if_altqenqueue and called by an upper
486  *      layer (e.g. ether_output).  cbq_enqueue queues the given packet
487  *      to the cbq, then invokes the driver's start routine.
488  *
489  *      Assumptions:    called in splimp
490  *      Returns:        0 if the queueing is successful.
491  *                      ENOBUFS if a packet dropping occurred as a result of
492  *                      the queueing.
493  */
494
495 static int
496 cbq_enqueue(struct ifaltq *ifq, struct mbuf *m, struct altq_pktattr *pktattr)
497 {
498         cbq_state_t     *cbqp = (cbq_state_t *)ifq->altq_disc;
499         struct rm_class *cl;
500         struct pf_mtag  *t;
501         int              len;
502
503         IFQ_LOCK_ASSERT(ifq);
504
505         /* grab class set by classifier */
506         if ((m->m_flags & M_PKTHDR) == 0) {
507                 /* should not happen */
508                 printf("altq: packet for %s does not have pkthdr\n",
509                     ifq->altq_ifp->if_xname);
510                 m_freem(m);
511                 return (ENOBUFS);
512         }
513         cl = NULL;
514         if ((t = pf_find_mtag(m)) != NULL)
515                 cl = clh_to_clp(cbqp, t->qid);
516 #ifdef ALTQ3_COMPAT
517         else if ((ifq->altq_flags & ALTQF_CLASSIFY) && pktattr != NULL)
518                 cl = pktattr->pattr_class;
519 #endif
520         if (cl == NULL) {
521                 cl = cbqp->ifnp.default_;
522                 if (cl == NULL) {
523                         m_freem(m);
524                         return (ENOBUFS);
525                 }
526         }
527 #ifdef ALTQ3_COMPAT
528         if (pktattr != NULL)
529                 cl->pktattr_ = pktattr;  /* save proto hdr used by ECN */
530         else
531 #endif
532                 cl->pktattr_ = NULL;
533         len = m_pktlen(m);
534         if (rmc_queue_packet(cl, m) != 0) {
535                 /* drop occurred.  some mbuf was freed in rmc_queue_packet. */
536                 PKTCNTR_ADD(&cl->stats_.drop_cnt, len);
537                 return (ENOBUFS);
538         }
539
540         /* successfully queued. */
541         ++cbqp->cbq_qlen;
542         IFQ_INC_LEN(ifq);
543         return (0);
544 }
545
546 static struct mbuf *
547 cbq_dequeue(struct ifaltq *ifq, int op)
548 {
549         cbq_state_t     *cbqp = (cbq_state_t *)ifq->altq_disc;
550         struct mbuf     *m;
551
552         IFQ_LOCK_ASSERT(ifq);
553
554         m = rmc_dequeue_next(&cbqp->ifnp, op);
555
556         if (m && op == ALTDQ_REMOVE) {
557                 --cbqp->cbq_qlen;  /* decrement # of packets in cbq */
558                 IFQ_DEC_LEN(ifq);
559
560                 /* Update the class. */
561                 rmc_update_class_util(&cbqp->ifnp);
562         }
563         return (m);
564 }
565
566 /*
567  * void
568  * cbqrestart(queue_t *) - Restart sending of data.
569  * called from rmc_restart in splimp via timeout after waking up
570  * a suspended class.
571  *      Returns:        NONE
572  */
573
574 static void
575 cbqrestart(struct ifaltq *ifq)
576 {
577         cbq_state_t     *cbqp;
578         struct ifnet    *ifp;
579
580         IFQ_LOCK_ASSERT(ifq);
581
582         if (!ALTQ_IS_ENABLED(ifq))
583                 /* cbq must have been detached */
584                 return;
585
586         if ((cbqp = (cbq_state_t *)ifq->altq_disc) == NULL)
587                 /* should not happen */
588                 return;
589
590         ifp = ifq->altq_ifp;
591         if (ifp->if_start &&
592             cbqp->cbq_qlen > 0 && (ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) {
593                 IFQ_UNLOCK(ifq);
594                 (*ifp->if_start)(ifp);
595                 IFQ_LOCK(ifq);
596         }
597 }
598
599 static void cbq_purge(cbq_state_t *cbqp)
600 {
601         struct rm_class *cl;
602         int              i;
603
604         for (i = 0; i < CBQ_MAX_CLASSES; i++)
605                 if ((cl = cbqp->cbq_class_tbl[i]) != NULL)
606                         rmc_dropall(cl);
607         if (ALTQ_IS_ENABLED(cbqp->ifnp.ifq_))
608                 cbqp->ifnp.ifq_->ifq_len = 0;
609 }
610 #ifdef ALTQ3_COMPAT
611
612 static int
613 cbq_add_class(acp)
614         struct cbq_add_class *acp;
615 {
616         char            *ifacename;
617         struct rm_class *borrow, *parent;
618         cbq_state_t     *cbqp;
619
620         ifacename = acp->cbq_iface.cbq_ifacename;
621         if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
622                 return (EBADF);
623
624         /* check parameters */
625         if (acp->cbq_class.priority >= CBQ_MAXPRI ||
626             acp->cbq_class.maxq > CBQ_MAXQSIZE)
627                 return (EINVAL);
628
629         /* Get pointers to parent and borrow classes.  */
630         parent = clh_to_clp(cbqp, acp->cbq_class.parent_class_handle);
631         borrow = clh_to_clp(cbqp, acp->cbq_class.borrow_class_handle);
632
633         /*
634          * A class must borrow from it's parent or it can not
635          * borrow at all.  Hence, borrow can be null.
636          */
637         if (parent == NULL && (acp->cbq_class.flags & CBQCLF_ROOTCLASS) == 0) {
638                 printf("cbq_add_class: no parent class!\n");
639                 return (EINVAL);
640         }
641
642         if ((borrow != parent)  && (borrow != NULL)) {
643                 printf("cbq_add_class: borrow class != parent\n");
644                 return (EINVAL);
645         }
646
647         return cbq_class_create(cbqp, acp, parent, borrow);
648 }
649
650 static int
651 cbq_delete_class(dcp)
652         struct cbq_delete_class *dcp;
653 {
654         char            *ifacename;
655         struct rm_class *cl;
656         cbq_state_t     *cbqp;
657
658         ifacename = dcp->cbq_iface.cbq_ifacename;
659         if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
660                 return (EBADF);
661
662         if ((cl = clh_to_clp(cbqp, dcp->cbq_class_handle)) == NULL)
663                 return (EINVAL);
664
665         /* if we are a parent class, then return an error. */
666         if (is_a_parent_class(cl))
667                 return (EINVAL);
668
669         /* if a filter has a reference to this class delete the filter */
670         acc_discard_filters(&cbqp->cbq_classifier, cl, 0);
671
672         return cbq_class_destroy(cbqp, cl);
673 }
674
675 static int
676 cbq_modify_class(acp)
677         struct cbq_modify_class *acp;
678 {
679         char            *ifacename;
680         struct rm_class *cl;
681         cbq_state_t     *cbqp;
682
683         ifacename = acp->cbq_iface.cbq_ifacename;
684         if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
685                 return (EBADF);
686
687         /* Get pointer to this class */
688         if ((cl = clh_to_clp(cbqp, acp->cbq_class_handle)) == NULL)
689                 return (EINVAL);
690
691         if (rmc_modclass(cl, acp->cbq_class.nano_sec_per_byte,
692                          acp->cbq_class.maxq, acp->cbq_class.maxidle,
693                          acp->cbq_class.minidle, acp->cbq_class.offtime,
694                          acp->cbq_class.pktsize) < 0)
695                 return (EINVAL);
696         return (0);
697 }
698
699 /*
700  * struct rm_class *
701  * cbq_class_create(cbq_mod_state_t *cbqp, struct cbq_add_class *acp,
702  *              struct rm_class *parent, struct rm_class *borrow)
703  *
704  * This function create a new traffic class in the CBQ class hierarchy of
705  * given paramters.  The class that created is either the root, default,
706  * or a new dynamic class.  If CBQ is not initilaized, the the root class
707  * will be created.
708  */
709 static int
710 cbq_class_create(cbqp, acp, parent, borrow)
711         cbq_state_t *cbqp;
712         struct cbq_add_class *acp;
713         struct rm_class *parent, *borrow;
714 {
715         struct rm_class *cl;
716         cbq_class_spec_t *spec = &acp->cbq_class;
717         u_int32_t       chandle;
718         int             i;
719
720         /*
721          * allocate class handle
722          */
723         for (i = 1; i < CBQ_MAX_CLASSES; i++)
724                 if (cbqp->cbq_class_tbl[i] == NULL)
725                         break;
726         if (i == CBQ_MAX_CLASSES)
727                 return (EINVAL);
728         chandle = i;    /* use the slot number as class handle */
729
730         /*
731          * create a class.  if this is a root class, initialize the
732          * interface.
733          */
734         if ((spec->flags & CBQCLF_CLASSMASK) == CBQCLF_ROOTCLASS) {
735                 rmc_init(cbqp->ifnp.ifq_, &cbqp->ifnp, spec->nano_sec_per_byte,
736                          cbqrestart, spec->maxq, RM_MAXQUEUED,
737                          spec->maxidle, spec->minidle, spec->offtime,
738                          spec->flags);
739                 cl = cbqp->ifnp.root_;
740         } else {
741                 cl = rmc_newclass(spec->priority,
742                                   &cbqp->ifnp, spec->nano_sec_per_byte,
743                                   rmc_delay_action, spec->maxq, parent, borrow,
744                                   spec->maxidle, spec->minidle, spec->offtime,
745                                   spec->pktsize, spec->flags);
746         }
747         if (cl == NULL)
748                 return (ENOMEM);
749
750         /* return handle to user space. */
751         acp->cbq_class_handle = chandle;
752
753         cl->stats_.handle = chandle;
754         cl->stats_.depth = cl->depth_;
755
756         /* save the allocated class */
757         cbqp->cbq_class_tbl[i] = cl;
758
759         if ((spec->flags & CBQCLF_CLASSMASK) == CBQCLF_DEFCLASS)
760                 cbqp->ifnp.default_ = cl;
761         if ((spec->flags & CBQCLF_CLASSMASK) == CBQCLF_CTLCLASS)
762                 cbqp->ifnp.ctl_ = cl;
763
764         return (0);
765 }
766
767 static int
768 cbq_add_filter(afp)
769         struct cbq_add_filter *afp;
770 {
771         char            *ifacename;
772         cbq_state_t     *cbqp;
773         struct rm_class *cl;
774
775         ifacename = afp->cbq_iface.cbq_ifacename;
776         if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
777                 return (EBADF);
778
779         /* Get the pointer to class. */
780         if ((cl = clh_to_clp(cbqp, afp->cbq_class_handle)) == NULL)
781                 return (EINVAL);
782
783         return acc_add_filter(&cbqp->cbq_classifier, &afp->cbq_filter,
784                               cl, &afp->cbq_filter_handle);
785 }
786
787 static int
788 cbq_delete_filter(dfp)
789         struct cbq_delete_filter *dfp;
790 {
791         char            *ifacename;
792         cbq_state_t     *cbqp;
793
794         ifacename = dfp->cbq_iface.cbq_ifacename;
795         if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
796                 return (EBADF);
797
798         return acc_delete_filter(&cbqp->cbq_classifier,
799                                  dfp->cbq_filter_handle);
800 }
801
802 /*
803  * cbq_clear_hierarchy deletes all classes and their filters on the
804  * given interface.
805  */
806 static int
807 cbq_clear_hierarchy(ifacep)
808         struct cbq_interface *ifacep;
809 {
810         char            *ifacename;
811         cbq_state_t     *cbqp;
812
813         ifacename = ifacep->cbq_ifacename;
814         if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
815                 return (EBADF);
816
817         return cbq_clear_interface(cbqp);
818 }
819
820 /*
821  * static int
822  * cbq_set_enable(struct cbq_enable *ep) - this function processed the
823  *      ioctl request to enable class based queueing.  It searches the list
824  *      of interfaces for the specified interface and then enables CBQ on
825  *      that interface.
826  *
827  *      Returns:        0, for no error.
828  *                      EBADF, for specified inteface not found.
829  */
830
831 static int
832 cbq_set_enable(ep, enable)
833         struct cbq_interface *ep;
834         int enable;
835 {
836         int     error = 0;
837         cbq_state_t     *cbqp;
838         char    *ifacename;
839
840         ifacename = ep->cbq_ifacename;
841         if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
842                 return (EBADF);
843
844         switch (enable) {
845         case ENABLE:
846                 if (cbqp->ifnp.root_ == NULL || cbqp->ifnp.default_ == NULL ||
847                     cbqp->ifnp.ctl_ == NULL) {
848                         if (cbqp->ifnp.root_ == NULL)
849                                 printf("No Root Class for %s\n", ifacename);
850                         if (cbqp->ifnp.default_ == NULL)
851                                 printf("No Default Class for %s\n", ifacename);
852                         if (cbqp->ifnp.ctl_ == NULL)
853                                 printf("No Control Class for %s\n", ifacename);
854                         error = EINVAL;
855                 } else if ((error = altq_enable(cbqp->ifnp.ifq_)) == 0) {
856                         cbqp->cbq_qlen = 0;
857                 }
858                 break;
859
860         case DISABLE:
861                 error = altq_disable(cbqp->ifnp.ifq_);
862                 break;
863         }
864         return (error);
865 }
866
867 static int
868 cbq_getstats(gsp)
869         struct cbq_getstats *gsp;
870 {
871         char            *ifacename;
872         int             i, n, nclasses;
873         cbq_state_t     *cbqp;
874         struct rm_class *cl;
875         class_stats_t   stats, *usp;
876         int error = 0;
877
878         ifacename = gsp->iface.cbq_ifacename;
879         nclasses = gsp->nclasses;
880         usp = gsp->stats;
881
882         if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
883                 return (EBADF);
884         if (nclasses <= 0)
885                 return (EINVAL);
886
887         for (n = 0, i = 0; n < nclasses && i < CBQ_MAX_CLASSES; n++, i++) {
888                 while ((cl = cbqp->cbq_class_tbl[i]) == NULL)
889                         if (++i >= CBQ_MAX_CLASSES)
890                                 goto out;
891
892                 get_class_stats(&stats, cl);
893                 stats.handle = cl->stats_.handle;
894
895                 if ((error = copyout((caddr_t)&stats, (caddr_t)usp++,
896                     sizeof(stats))) != 0)
897                         return (error);
898         }
899
900  out:
901         gsp->nclasses = n;
902         return (error);
903 }
904
905 static int
906 cbq_ifattach(ifacep)
907         struct cbq_interface *ifacep;
908 {
909         int             error = 0;
910         char            *ifacename;
911         cbq_state_t     *new_cbqp;
912         struct ifnet    *ifp;
913
914         ifacename = ifacep->cbq_ifacename;
915         if ((ifp = ifunit(ifacename)) == NULL)
916                 return (ENXIO);
917         if (!ALTQ_IS_READY(&ifp->if_snd))
918                 return (ENXIO);
919
920         /* allocate and initialize cbq_state_t */
921         new_cbqp = malloc(sizeof(cbq_state_t), M_DEVBUF, M_WAITOK);
922         if (new_cbqp == NULL)
923                 return (ENOMEM);
924         bzero(new_cbqp, sizeof(cbq_state_t));
925         CALLOUT_INIT(&new_cbqp->cbq_callout);
926
927         new_cbqp->cbq_qlen = 0;
928         new_cbqp->ifnp.ifq_ = &ifp->if_snd;         /* keep the ifq */
929
930         /*
931          * set CBQ to this ifnet structure.
932          */
933         error = altq_attach(&ifp->if_snd, ALTQT_CBQ, new_cbqp,
934                             cbq_enqueue, cbq_dequeue, cbq_request,
935                             &new_cbqp->cbq_classifier, acc_classify);
936         if (error) {
937                 free(new_cbqp, M_DEVBUF);
938                 return (error);
939         }
940
941         /* prepend to the list of cbq_state_t's. */
942         new_cbqp->cbq_next = cbq_list;
943         cbq_list = new_cbqp;
944
945         return (0);
946 }
947
948 static int
949 cbq_ifdetach(ifacep)
950         struct cbq_interface *ifacep;
951 {
952         char            *ifacename;
953         cbq_state_t     *cbqp;
954
955         ifacename = ifacep->cbq_ifacename;
956         if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
957                 return (EBADF);
958
959         (void)cbq_set_enable(ifacep, DISABLE);
960
961         cbq_clear_interface(cbqp);
962
963         /* remove CBQ from the ifnet structure. */
964         (void)altq_detach(cbqp->ifnp.ifq_);
965
966         /* remove from the list of cbq_state_t's. */
967         if (cbq_list == cbqp)
968                 cbq_list = cbqp->cbq_next;
969         else {
970                 cbq_state_t *cp;
971
972                 for (cp = cbq_list; cp != NULL; cp = cp->cbq_next)
973                         if (cp->cbq_next == cbqp) {
974                                 cp->cbq_next = cbqp->cbq_next;
975                                 break;
976                         }
977                 ASSERT(cp != NULL);
978         }
979
980         /* deallocate cbq_state_t */
981         free(cbqp, M_DEVBUF);
982
983         return (0);
984 }
985
986 /*
987  * cbq device interface
988  */
989
990 altqdev_decl(cbq);
991
992 int
993 cbqopen(dev, flag, fmt, p)
994         dev_t dev;
995         int flag, fmt;
996 #if (__FreeBSD_version > 500000)
997         struct thread *p;
998 #else
999         struct proc *p;
1000 #endif
1001 {
1002         return (0);
1003 }
1004
1005 int
1006 cbqclose(dev, flag, fmt, p)
1007         dev_t dev;
1008         int flag, fmt;
1009 #if (__FreeBSD_version > 500000)
1010         struct thread *p;
1011 #else
1012         struct proc *p;
1013 #endif
1014 {
1015         struct ifnet *ifp;
1016         struct cbq_interface iface;
1017         int err, error = 0;
1018
1019         while (cbq_list) {
1020                 ifp = cbq_list->ifnp.ifq_->altq_ifp;
1021                 sprintf(iface.cbq_ifacename, "%s", ifp->if_xname);
1022                 err = cbq_ifdetach(&iface);
1023                 if (err != 0 && error == 0)
1024                         error = err;
1025         }
1026
1027         return (error);
1028 }
1029
1030 int
1031 cbqioctl(dev, cmd, addr, flag, p)
1032         dev_t dev;
1033         ioctlcmd_t cmd;
1034         caddr_t addr;
1035         int flag;
1036 #if (__FreeBSD_version > 500000)
1037         struct thread *p;
1038 #else
1039         struct proc *p;
1040 #endif
1041 {
1042         int     error = 0;
1043
1044         /* check cmd for superuser only */
1045         switch (cmd) {
1046         case CBQ_GETSTATS:
1047                 /* currently only command that an ordinary user can call */
1048                 break;
1049         default:
1050 #if (__FreeBSD_version > 700000)
1051                 error = priv_check(p, PRIV_ALTQ_MANAGE);
1052 #elsif (__FreeBSD_version > 400000)
1053                 error = suser(p);
1054 #else
1055                 error = suser(p->p_ucred, &p->p_acflag);
1056 #endif
1057                 if (error)
1058                         return (error);
1059                 break;
1060         }
1061
1062         switch (cmd) {
1063
1064         case CBQ_ENABLE:
1065                 error = cbq_set_enable((struct cbq_interface *)addr, ENABLE);
1066                 break;
1067
1068         case CBQ_DISABLE:
1069                 error = cbq_set_enable((struct cbq_interface *)addr, DISABLE);
1070                 break;
1071
1072         case CBQ_ADD_FILTER:
1073                 error = cbq_add_filter((struct cbq_add_filter *)addr);
1074                 break;
1075
1076         case CBQ_DEL_FILTER:
1077                 error = cbq_delete_filter((struct cbq_delete_filter *)addr);
1078                 break;
1079
1080         case CBQ_ADD_CLASS:
1081                 error = cbq_add_class((struct cbq_add_class *)addr);
1082                 break;
1083
1084         case CBQ_DEL_CLASS:
1085                 error = cbq_delete_class((struct cbq_delete_class *)addr);
1086                 break;
1087
1088         case CBQ_MODIFY_CLASS:
1089                 error = cbq_modify_class((struct cbq_modify_class *)addr);
1090                 break;
1091
1092         case CBQ_CLEAR_HIERARCHY:
1093                 error = cbq_clear_hierarchy((struct cbq_interface *)addr);
1094                 break;
1095
1096         case CBQ_IF_ATTACH:
1097                 error = cbq_ifattach((struct cbq_interface *)addr);
1098                 break;
1099
1100         case CBQ_IF_DETACH:
1101                 error = cbq_ifdetach((struct cbq_interface *)addr);
1102                 break;
1103
1104         case CBQ_GETSTATS:
1105                 error = cbq_getstats((struct cbq_getstats *)addr);
1106                 break;
1107
1108         default:
1109                 error = EINVAL;
1110                 break;
1111         }
1112
1113         return error;
1114 }
1115
1116 #if 0
1117 /* for debug */
1118 static void cbq_class_dump(int);
1119
1120 static void cbq_class_dump(i)
1121         int i;
1122 {
1123         struct rm_class *cl;
1124         rm_class_stats_t *s;
1125         struct _class_queue_ *q;
1126
1127         if (cbq_list == NULL) {
1128                 printf("cbq_class_dump: no cbq_state found\n");
1129                 return;
1130         }
1131         cl = cbq_list->cbq_class_tbl[i];
1132
1133         printf("class %d cl=%p\n", i, cl);
1134         if (cl != NULL) {
1135                 s = &cl->stats_;
1136                 q = cl->q_;
1137
1138                 printf("pri=%d, depth=%d, maxrate=%d, allotment=%d\n",
1139                        cl->pri_, cl->depth_, cl->maxrate_, cl->allotment_);
1140                 printf("w_allotment=%d, bytes_alloc=%d, avgidle=%d, maxidle=%d\n",
1141                        cl->w_allotment_, cl->bytes_alloc_, cl->avgidle_,
1142                        cl->maxidle_);
1143                 printf("minidle=%d, offtime=%d, sleeping=%d, leaf=%d\n",
1144                        cl->minidle_, cl->offtime_, cl->sleeping_, cl->leaf_);
1145                 printf("handle=%d, depth=%d, packets=%d, bytes=%d\n",
1146                        s->handle, s->depth,
1147                        (int)s->xmit_cnt.packets, (int)s->xmit_cnt.bytes);
1148                 printf("over=%d\n, borrows=%d, drops=%d, overactions=%d, delays=%d\n",
1149                        s->over, s->borrows, (int)s->drop_cnt.packets,
1150                        s->overactions, s->delays);
1151                 printf("tail=%p, head=%p, qlen=%d, qlim=%d, qthresh=%d,qtype=%d\n",
1152                        q->tail_, q->head_, q->qlen_, q->qlim_,
1153                        q->qthresh_, q->qtype_);
1154         }
1155 }
1156 #endif /* 0 */
1157
1158 #ifdef KLD_MODULE
1159
1160 static struct altqsw cbq_sw =
1161         {"cbq", cbqopen, cbqclose, cbqioctl};
1162
1163 ALTQ_MODULE(altq_cbq, ALTQT_CBQ, &cbq_sw);
1164 MODULE_DEPEND(altq_cbq, altq_red, 1, 1, 1);
1165 MODULE_DEPEND(altq_cbq, altq_rio, 1, 1, 1);
1166
1167 #endif /* KLD_MODULE */
1168 #endif /* ALTQ3_COMPAT */
1169
1170 #endif /* ALTQ_CBQ */