]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/contrib/altq/altq/altq_priq.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / contrib / altq / altq / altq_priq.c
1 /*      $FreeBSD$       */
2 /*      $KAME: altq_priq.c,v 1.11 2003/09/17 14:23:25 kjc Exp $ */
3 /*
4  * Copyright (C) 2000-2003
5  *      Sony Computer Science Laboratories Inc.  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  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 /*
29  * priority queue
30  */
31
32 #if defined(__FreeBSD__) || defined(__NetBSD__)
33 #include "opt_altq.h"
34 #if (__FreeBSD__ != 2)
35 #include "opt_inet.h"
36 #ifdef __FreeBSD__
37 #include "opt_inet6.h"
38 #endif
39 #endif
40 #endif /* __FreeBSD__ || __NetBSD__ */
41
42 #ifdef ALTQ_PRIQ  /* priq is enabled by ALTQ_PRIQ option in opt_altq.h */
43
44 #include <sys/param.h>
45 #include <sys/malloc.h>
46 #include <sys/mbuf.h>
47 #include <sys/socket.h>
48 #include <sys/sockio.h>
49 #include <sys/systm.h>
50 #include <sys/proc.h>
51 #include <sys/errno.h>
52 #include <sys/kernel.h>
53 #include <sys/queue.h>
54
55 #include <net/if.h>
56 #include <netinet/in.h>
57
58 #include <net/pfvar.h>
59 #include <altq/altq.h>
60 #ifdef ALTQ3_COMPAT
61 #include <altq/altq_conf.h>
62 #endif
63 #include <altq/altq_priq.h>
64
65 /*
66  * function prototypes
67  */
68 #ifdef ALTQ3_COMPAT
69 static struct priq_if *priq_attach(struct ifaltq *, u_int);
70 static int priq_detach(struct priq_if *);
71 #endif
72 static int priq_clear_interface(struct priq_if *);
73 static int priq_request(struct ifaltq *, int, void *);
74 static void priq_purge(struct priq_if *);
75 static struct priq_class *priq_class_create(struct priq_if *, int, int, int,
76     int);
77 static int priq_class_destroy(struct priq_class *);
78 static int priq_enqueue(struct ifaltq *, struct mbuf *, struct altq_pktattr *);
79 static struct mbuf *priq_dequeue(struct ifaltq *, int);
80
81 static int priq_addq(struct priq_class *, struct mbuf *);
82 static struct mbuf *priq_getq(struct priq_class *);
83 static struct mbuf *priq_pollq(struct priq_class *);
84 static void priq_purgeq(struct priq_class *);
85
86 #ifdef ALTQ3_COMPAT
87 static int priqcmd_if_attach(struct priq_interface *);
88 static int priqcmd_if_detach(struct priq_interface *);
89 static int priqcmd_add_class(struct priq_add_class *);
90 static int priqcmd_delete_class(struct priq_delete_class *);
91 static int priqcmd_modify_class(struct priq_modify_class *);
92 static int priqcmd_add_filter(struct priq_add_filter *);
93 static int priqcmd_delete_filter(struct priq_delete_filter *);
94 static int priqcmd_class_stats(struct priq_class_stats *);
95 #endif /* ALTQ3_COMPAT */
96
97 static void get_class_stats(struct priq_classstats *, struct priq_class *);
98 static struct priq_class *clh_to_clp(struct priq_if *, u_int32_t);
99
100 #ifdef ALTQ3_COMPAT
101 altqdev_decl(priq);
102
103 /* pif_list keeps all priq_if's allocated. */
104 static struct priq_if *pif_list = NULL;
105 #endif /* ALTQ3_COMPAT */
106
107 int
108 priq_pfattach(struct pf_altq *a)
109 {
110         struct ifnet *ifp;
111         int s, error;
112
113         if ((ifp = ifunit(a->ifname)) == NULL || a->altq_disc == NULL)
114                 return (EINVAL);
115 #ifdef __NetBSD__
116         s = splnet();
117 #else
118         s = splimp();
119 #endif
120         error = altq_attach(&ifp->if_snd, ALTQT_PRIQ, a->altq_disc,
121             priq_enqueue, priq_dequeue, priq_request, NULL, NULL);
122         splx(s);
123         return (error);
124 }
125
126 int
127 priq_add_altq(struct pf_altq *a)
128 {
129         struct priq_if  *pif;
130         struct ifnet    *ifp;
131
132         if ((ifp = ifunit(a->ifname)) == NULL)
133                 return (EINVAL);
134         if (!ALTQ_IS_READY(&ifp->if_snd))
135                 return (ENODEV);
136
137         pif = malloc(sizeof(struct priq_if),
138             M_DEVBUF, M_WAITOK);
139         if (pif == NULL)
140                 return (ENOMEM);
141         bzero(pif, sizeof(struct priq_if));
142         pif->pif_bandwidth = a->ifbandwidth;
143         pif->pif_maxpri = -1;
144         pif->pif_ifq = &ifp->if_snd;
145
146         /* keep the state in pf_altq */
147         a->altq_disc = pif;
148
149         return (0);
150 }
151
152 int
153 priq_remove_altq(struct pf_altq *a)
154 {
155         struct priq_if *pif;
156
157         if ((pif = a->altq_disc) == NULL)
158                 return (EINVAL);
159         a->altq_disc = NULL;
160
161         (void)priq_clear_interface(pif);
162
163         free(pif, M_DEVBUF);
164         return (0);
165 }
166
167 int
168 priq_add_queue(struct pf_altq *a)
169 {
170         struct priq_if *pif;
171         struct priq_class *cl;
172
173         if ((pif = a->altq_disc) == NULL)
174                 return (EINVAL);
175
176         /* check parameters */
177         if (a->priority >= PRIQ_MAXPRI)
178                 return (EINVAL);
179         if (a->qid == 0)
180                 return (EINVAL);
181         if (pif->pif_classes[a->priority] != NULL)
182                 return (EBUSY);
183         if (clh_to_clp(pif, a->qid) != NULL)
184                 return (EBUSY);
185
186         cl = priq_class_create(pif, a->priority, a->qlimit,
187             a->pq_u.priq_opts.flags, a->qid);
188         if (cl == NULL)
189                 return (ENOMEM);
190
191         return (0);
192 }
193
194 int
195 priq_remove_queue(struct pf_altq *a)
196 {
197         struct priq_if *pif;
198         struct priq_class *cl;
199
200         if ((pif = a->altq_disc) == NULL)
201                 return (EINVAL);
202
203         if ((cl = clh_to_clp(pif, a->qid)) == NULL)
204                 return (EINVAL);
205
206         return (priq_class_destroy(cl));
207 }
208
209 int
210 priq_getqstats(struct pf_altq *a, void *ubuf, int *nbytes)
211 {
212         struct priq_if *pif;
213         struct priq_class *cl;
214         struct priq_classstats stats;
215         int error = 0;
216
217         if ((pif = altq_lookup(a->ifname, ALTQT_PRIQ)) == NULL)
218                 return (EBADF);
219
220         if ((cl = clh_to_clp(pif, a->qid)) == NULL)
221                 return (EINVAL);
222
223         if (*nbytes < sizeof(stats))
224                 return (EINVAL);
225
226         get_class_stats(&stats, cl);
227
228         if ((error = copyout((caddr_t)&stats, ubuf, sizeof(stats))) != 0)
229                 return (error);
230         *nbytes = sizeof(stats);
231         return (0);
232 }
233
234 /*
235  * bring the interface back to the initial state by discarding
236  * all the filters and classes.
237  */
238 static int
239 priq_clear_interface(struct priq_if *pif)
240 {
241         struct priq_class       *cl;
242         int pri;
243
244 #ifdef ALTQ3_CLFIER_COMPAT
245         /* free the filters for this interface */
246         acc_discard_filters(&pif->pif_classifier, NULL, 1);
247 #endif
248
249         /* clear out the classes */
250         for (pri = 0; pri <= pif->pif_maxpri; pri++)
251                 if ((cl = pif->pif_classes[pri]) != NULL)
252                         priq_class_destroy(cl);
253
254         return (0);
255 }
256
257 static int
258 priq_request(struct ifaltq *ifq, int req, void *arg)
259 {
260         struct priq_if  *pif = (struct priq_if *)ifq->altq_disc;
261
262         IFQ_LOCK_ASSERT(ifq);
263
264         switch (req) {
265         case ALTRQ_PURGE:
266                 priq_purge(pif);
267                 break;
268         }
269         return (0);
270 }
271
272 /* discard all the queued packets on the interface */
273 static void
274 priq_purge(struct priq_if *pif)
275 {
276         struct priq_class *cl;
277         int pri;
278
279         for (pri = 0; pri <= pif->pif_maxpri; pri++) {
280                 if ((cl = pif->pif_classes[pri]) != NULL && !qempty(cl->cl_q))
281                         priq_purgeq(cl);
282         }
283         if (ALTQ_IS_ENABLED(pif->pif_ifq))
284                 pif->pif_ifq->ifq_len = 0;
285 }
286
287 static struct priq_class *
288 priq_class_create(struct priq_if *pif, int pri, int qlimit, int flags, int qid)
289 {
290         struct priq_class *cl;
291         int s;
292
293 #ifndef ALTQ_RED
294         if (flags & PRCF_RED) {
295 #ifdef ALTQ_DEBUG
296                 printf("priq_class_create: RED not configured for PRIQ!\n");
297 #endif
298                 return (NULL);
299         }
300 #endif
301
302         if ((cl = pif->pif_classes[pri]) != NULL) {
303                 /* modify the class instead of creating a new one */
304 #ifdef __NetBSD__
305                 s = splnet();
306 #else
307                 s = splimp();
308 #endif
309                 IFQ_LOCK(cl->cl_pif->pif_ifq);
310                 if (!qempty(cl->cl_q))
311                         priq_purgeq(cl);
312                 IFQ_UNLOCK(cl->cl_pif->pif_ifq);
313                 splx(s);
314 #ifdef ALTQ_RIO
315                 if (q_is_rio(cl->cl_q))
316                         rio_destroy((rio_t *)cl->cl_red);
317 #endif
318 #ifdef ALTQ_RED
319                 if (q_is_red(cl->cl_q))
320                         red_destroy(cl->cl_red);
321 #endif
322         } else {
323                 cl = malloc(sizeof(struct priq_class),
324                        M_DEVBUF, M_WAITOK);
325                 if (cl == NULL)
326                         return (NULL);
327                 bzero(cl, sizeof(struct priq_class));
328
329                 cl->cl_q = malloc(sizeof(class_queue_t),
330                        M_DEVBUF, M_WAITOK);
331                 if (cl->cl_q == NULL)
332                         goto err_ret;
333                 bzero(cl->cl_q, sizeof(class_queue_t));
334         }
335
336         pif->pif_classes[pri] = cl;
337         if (flags & PRCF_DEFAULTCLASS)
338                 pif->pif_default = cl;
339         if (qlimit == 0)
340                 qlimit = 50;  /* use default */
341         qlimit(cl->cl_q) = qlimit;
342         qtype(cl->cl_q) = Q_DROPTAIL;
343         qlen(cl->cl_q) = 0;
344         cl->cl_flags = flags;
345         cl->cl_pri = pri;
346         if (pri > pif->pif_maxpri)
347                 pif->pif_maxpri = pri;
348         cl->cl_pif = pif;
349         cl->cl_handle = qid;
350
351 #ifdef ALTQ_RED
352         if (flags & (PRCF_RED|PRCF_RIO)) {
353                 int red_flags, red_pkttime;
354
355                 red_flags = 0;
356                 if (flags & PRCF_ECN)
357                         red_flags |= REDF_ECN;
358 #ifdef ALTQ_RIO
359                 if (flags & PRCF_CLEARDSCP)
360                         red_flags |= RIOF_CLEARDSCP;
361 #endif
362                 if (pif->pif_bandwidth < 8)
363                         red_pkttime = 1000 * 1000 * 1000; /* 1 sec */
364                 else
365                         red_pkttime = (int64_t)pif->pif_ifq->altq_ifp->if_mtu
366                           * 1000 * 1000 * 1000 / (pif->pif_bandwidth / 8);
367 #ifdef ALTQ_RIO
368                 if (flags & PRCF_RIO) {
369                         cl->cl_red = (red_t *)rio_alloc(0, NULL,
370                                                 red_flags, red_pkttime);
371                         if (cl->cl_red != NULL)
372                                 qtype(cl->cl_q) = Q_RIO;
373                 } else
374 #endif
375                 if (flags & PRCF_RED) {
376                         cl->cl_red = red_alloc(0, 0,
377                             qlimit(cl->cl_q) * 10/100,
378                             qlimit(cl->cl_q) * 30/100,
379                             red_flags, red_pkttime);
380                         if (cl->cl_red != NULL)
381                                 qtype(cl->cl_q) = Q_RED;
382                 }
383         }
384 #endif /* ALTQ_RED */
385
386         return (cl);
387
388  err_ret:
389         if (cl->cl_red != NULL) {
390 #ifdef ALTQ_RIO
391                 if (q_is_rio(cl->cl_q))
392                         rio_destroy((rio_t *)cl->cl_red);
393 #endif
394 #ifdef ALTQ_RED
395                 if (q_is_red(cl->cl_q))
396                         red_destroy(cl->cl_red);
397 #endif
398         }
399         if (cl->cl_q != NULL)
400                 free(cl->cl_q, M_DEVBUF);
401         free(cl, M_DEVBUF);
402         return (NULL);
403 }
404
405 static int
406 priq_class_destroy(struct priq_class *cl)
407 {
408         struct priq_if *pif;
409         int s, pri;
410
411 #ifdef __NetBSD__
412         s = splnet();
413 #else
414         s = splimp();
415 #endif
416         IFQ_LOCK(cl->cl_pif->pif_ifq);
417
418 #ifdef ALTQ3_CLFIER_COMPAT
419         /* delete filters referencing to this class */
420         acc_discard_filters(&cl->cl_pif->pif_classifier, cl, 0);
421 #endif
422
423         if (!qempty(cl->cl_q))
424                 priq_purgeq(cl);
425
426         pif = cl->cl_pif;
427         pif->pif_classes[cl->cl_pri] = NULL;
428         if (pif->pif_maxpri == cl->cl_pri) {
429                 for (pri = cl->cl_pri; pri >= 0; pri--)
430                         if (pif->pif_classes[pri] != NULL) {
431                                 pif->pif_maxpri = pri;
432                                 break;
433                         }
434                 if (pri < 0)
435                         pif->pif_maxpri = -1;
436         }
437         IFQ_UNLOCK(cl->cl_pif->pif_ifq);
438         splx(s);
439
440         if (cl->cl_red != NULL) {
441 #ifdef ALTQ_RIO
442                 if (q_is_rio(cl->cl_q))
443                         rio_destroy((rio_t *)cl->cl_red);
444 #endif
445 #ifdef ALTQ_RED
446                 if (q_is_red(cl->cl_q))
447                         red_destroy(cl->cl_red);
448 #endif
449         }
450         free(cl->cl_q, M_DEVBUF);
451         free(cl, M_DEVBUF);
452         return (0);
453 }
454
455 /*
456  * priq_enqueue is an enqueue function to be registered to
457  * (*altq_enqueue) in struct ifaltq.
458  */
459 static int
460 priq_enqueue(struct ifaltq *ifq, struct mbuf *m, struct altq_pktattr *pktattr)
461 {
462         struct priq_if  *pif = (struct priq_if *)ifq->altq_disc;
463         struct priq_class *cl;
464         struct pf_mtag *t;
465         int len;
466
467         IFQ_LOCK_ASSERT(ifq);
468
469         /* grab class set by classifier */
470         if ((m->m_flags & M_PKTHDR) == 0) {
471                 /* should not happen */
472 #if defined(__NetBSD__) || defined(__OpenBSD__)\
473     || (defined(__FreeBSD__) && __FreeBSD_version >= 501113)
474                 printf("altq: packet for %s does not have pkthdr\n",
475                     ifq->altq_ifp->if_xname);
476 #else
477                 printf("altq: packet for %s%d does not have pkthdr\n",
478                     ifq->altq_ifp->if_name, ifq->altq_ifp->if_unit);
479 #endif
480                 m_freem(m);
481                 return (ENOBUFS);
482         }
483         cl = NULL;
484         if ((t = pf_find_mtag(m)) != NULL)
485                 cl = clh_to_clp(pif, t->qid);
486 #ifdef ALTQ3_COMPAT
487         else if ((ifq->altq_flags & ALTQF_CLASSIFY) && pktattr != NULL)
488                 cl = pktattr->pattr_class;
489 #endif
490         if (cl == NULL) {
491                 cl = pif->pif_default;
492                 if (cl == NULL) {
493                         m_freem(m);
494                         return (ENOBUFS);
495                 }
496         }
497 #ifdef ALTQ3_COMPAT
498         if (pktattr != NULL)
499                 cl->cl_pktattr = pktattr;  /* save proto hdr used by ECN */
500         else
501 #endif
502                 cl->cl_pktattr = NULL;
503         len = m_pktlen(m);
504         if (priq_addq(cl, m) != 0) {
505                 /* drop occurred.  mbuf was freed in priq_addq. */
506                 PKTCNTR_ADD(&cl->cl_dropcnt, len);
507                 return (ENOBUFS);
508         }
509         IFQ_INC_LEN(ifq);
510
511         /* successfully queued. */
512         return (0);
513 }
514
515 /*
516  * priq_dequeue is a dequeue function to be registered to
517  * (*altq_dequeue) in struct ifaltq.
518  *
519  * note: ALTDQ_POLL returns the next packet without removing the packet
520  *      from the queue.  ALTDQ_REMOVE is a normal dequeue operation.
521  *      ALTDQ_REMOVE must return the same packet if called immediately
522  *      after ALTDQ_POLL.
523  */
524 static struct mbuf *
525 priq_dequeue(struct ifaltq *ifq, int op)
526 {
527         struct priq_if  *pif = (struct priq_if *)ifq->altq_disc;
528         struct priq_class *cl;
529         struct mbuf *m;
530         int pri;
531
532         IFQ_LOCK_ASSERT(ifq);
533
534         if (IFQ_IS_EMPTY(ifq))
535                 /* no packet in the queue */
536                 return (NULL);
537
538         for (pri = pif->pif_maxpri;  pri >= 0; pri--) {
539                 if ((cl = pif->pif_classes[pri]) != NULL &&
540                     !qempty(cl->cl_q)) {
541                         if (op == ALTDQ_POLL)
542                                 return (priq_pollq(cl));
543
544                         m = priq_getq(cl);
545                         if (m != NULL) {
546                                 IFQ_DEC_LEN(ifq);
547                                 if (qempty(cl->cl_q))
548                                         cl->cl_period++;
549                                 PKTCNTR_ADD(&cl->cl_xmitcnt, m_pktlen(m));
550                         }
551                         return (m);
552                 }
553         }
554         return (NULL);
555 }
556
557 static int
558 priq_addq(struct priq_class *cl, struct mbuf *m)
559 {
560
561 #ifdef ALTQ_RIO
562         if (q_is_rio(cl->cl_q))
563                 return rio_addq((rio_t *)cl->cl_red, cl->cl_q, m,
564                                 cl->cl_pktattr);
565 #endif
566 #ifdef ALTQ_RED
567         if (q_is_red(cl->cl_q))
568                 return red_addq(cl->cl_red, cl->cl_q, m, cl->cl_pktattr);
569 #endif
570         if (qlen(cl->cl_q) >= qlimit(cl->cl_q)) {
571                 m_freem(m);
572                 return (-1);
573         }
574
575         if (cl->cl_flags & PRCF_CLEARDSCP)
576                 write_dsfield(m, cl->cl_pktattr, 0);
577
578         _addq(cl->cl_q, m);
579
580         return (0);
581 }
582
583 static struct mbuf *
584 priq_getq(struct priq_class *cl)
585 {
586 #ifdef ALTQ_RIO
587         if (q_is_rio(cl->cl_q))
588                 return rio_getq((rio_t *)cl->cl_red, cl->cl_q);
589 #endif
590 #ifdef ALTQ_RED
591         if (q_is_red(cl->cl_q))
592                 return red_getq(cl->cl_red, cl->cl_q);
593 #endif
594         return _getq(cl->cl_q);
595 }
596
597 static struct mbuf *
598 priq_pollq(cl)
599         struct priq_class *cl;
600 {
601         return qhead(cl->cl_q);
602 }
603
604 static void
605 priq_purgeq(struct priq_class *cl)
606 {
607         struct mbuf *m;
608
609         if (qempty(cl->cl_q))
610                 return;
611
612         while ((m = _getq(cl->cl_q)) != NULL) {
613                 PKTCNTR_ADD(&cl->cl_dropcnt, m_pktlen(m));
614                 m_freem(m);
615         }
616         ASSERT(qlen(cl->cl_q) == 0);
617 }
618
619 static void
620 get_class_stats(struct priq_classstats *sp, struct priq_class *cl)
621 {
622         sp->class_handle = cl->cl_handle;
623         sp->qlength = qlen(cl->cl_q);
624         sp->qlimit = qlimit(cl->cl_q);
625         sp->period = cl->cl_period;
626         sp->xmitcnt = cl->cl_xmitcnt;
627         sp->dropcnt = cl->cl_dropcnt;
628
629         sp->qtype = qtype(cl->cl_q);
630 #ifdef ALTQ_RED
631         if (q_is_red(cl->cl_q))
632                 red_getstats(cl->cl_red, &sp->red[0]);
633 #endif
634 #ifdef ALTQ_RIO
635         if (q_is_rio(cl->cl_q))
636                 rio_getstats((rio_t *)cl->cl_red, &sp->red[0]);
637 #endif
638
639 }
640
641 /* convert a class handle to the corresponding class pointer */
642 static struct priq_class *
643 clh_to_clp(struct priq_if *pif, u_int32_t chandle)
644 {
645         struct priq_class *cl;
646         int idx;
647
648         if (chandle == 0)
649                 return (NULL);
650
651         for (idx = pif->pif_maxpri; idx >= 0; idx--)
652                 if ((cl = pif->pif_classes[idx]) != NULL &&
653                     cl->cl_handle == chandle)
654                         return (cl);
655
656         return (NULL);
657 }
658
659
660 #ifdef ALTQ3_COMPAT
661
662 static struct priq_if *
663 priq_attach(ifq, bandwidth)
664         struct ifaltq *ifq;
665         u_int bandwidth;
666 {
667         struct priq_if *pif;
668
669         pif = malloc(sizeof(struct priq_if),
670                M_DEVBUF, M_WAITOK);
671         if (pif == NULL)
672                 return (NULL);
673         bzero(pif, sizeof(struct priq_if));
674         pif->pif_bandwidth = bandwidth;
675         pif->pif_maxpri = -1;
676         pif->pif_ifq = ifq;
677
678         /* add this state to the priq list */
679         pif->pif_next = pif_list;
680         pif_list = pif;
681
682         return (pif);
683 }
684
685 static int
686 priq_detach(pif)
687         struct priq_if *pif;
688 {
689         (void)priq_clear_interface(pif);
690
691         /* remove this interface from the pif list */
692         if (pif_list == pif)
693                 pif_list = pif->pif_next;
694         else {
695                 struct priq_if *p;
696
697                 for (p = pif_list; p != NULL; p = p->pif_next)
698                         if (p->pif_next == pif) {
699                                 p->pif_next = pif->pif_next;
700                                 break;
701                         }
702                 ASSERT(p != NULL);
703         }
704
705         free(pif, M_DEVBUF);
706         return (0);
707 }
708
709 /*
710  * priq device interface
711  */
712 int
713 priqopen(dev, flag, fmt, p)
714         dev_t dev;
715         int flag, fmt;
716 #if (__FreeBSD_version > 500000)
717         struct thread *p;
718 #else
719         struct proc *p;
720 #endif
721 {
722         /* everything will be done when the queueing scheme is attached. */
723         return 0;
724 }
725
726 int
727 priqclose(dev, flag, fmt, p)
728         dev_t dev;
729         int flag, fmt;
730 #if (__FreeBSD_version > 500000)
731         struct thread *p;
732 #else
733         struct proc *p;
734 #endif
735 {
736         struct priq_if *pif;
737         int err, error = 0;
738
739         while ((pif = pif_list) != NULL) {
740                 /* destroy all */
741                 if (ALTQ_IS_ENABLED(pif->pif_ifq))
742                         altq_disable(pif->pif_ifq);
743
744                 err = altq_detach(pif->pif_ifq);
745                 if (err == 0)
746                         err = priq_detach(pif);
747                 if (err != 0 && error == 0)
748                         error = err;
749         }
750
751         return error;
752 }
753
754 int
755 priqioctl(dev, cmd, addr, flag, p)
756         dev_t dev;
757         ioctlcmd_t cmd;
758         caddr_t addr;
759         int flag;
760 #if (__FreeBSD_version > 500000)
761         struct thread *p;
762 #else
763         struct proc *p;
764 #endif
765 {
766         struct priq_if *pif;
767         struct priq_interface *ifacep;
768         int     error = 0;
769
770         /* check super-user privilege */
771         switch (cmd) {
772         case PRIQ_GETSTATS:
773                 break;
774         default:
775 #if (__FreeBSD_version > 700000)
776                 if ((error = priv_check(p, PRIV_ALTQ_MANAGE)) != 0)
777                         return (error);
778 #elsif (__FreeBSD_version > 400000)
779                 if ((error = suser(p)) != 0)
780                         return (error);
781 #else
782                 if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
783                         return (error);
784 #endif
785                 break;
786         }
787
788         switch (cmd) {
789
790         case PRIQ_IF_ATTACH:
791                 error = priqcmd_if_attach((struct priq_interface *)addr);
792                 break;
793
794         case PRIQ_IF_DETACH:
795                 error = priqcmd_if_detach((struct priq_interface *)addr);
796                 break;
797
798         case PRIQ_ENABLE:
799         case PRIQ_DISABLE:
800         case PRIQ_CLEAR:
801                 ifacep = (struct priq_interface *)addr;
802                 if ((pif = altq_lookup(ifacep->ifname,
803                                        ALTQT_PRIQ)) == NULL) {
804                         error = EBADF;
805                         break;
806                 }
807
808                 switch (cmd) {
809                 case PRIQ_ENABLE:
810                         if (pif->pif_default == NULL) {
811 #ifdef ALTQ_DEBUG
812                                 printf("priq: no default class\n");
813 #endif
814                                 error = EINVAL;
815                                 break;
816                         }
817                         error = altq_enable(pif->pif_ifq);
818                         break;
819
820                 case PRIQ_DISABLE:
821                         error = altq_disable(pif->pif_ifq);
822                         break;
823
824                 case PRIQ_CLEAR:
825                         priq_clear_interface(pif);
826                         break;
827                 }
828                 break;
829
830         case PRIQ_ADD_CLASS:
831                 error = priqcmd_add_class((struct priq_add_class *)addr);
832                 break;
833
834         case PRIQ_DEL_CLASS:
835                 error = priqcmd_delete_class((struct priq_delete_class *)addr);
836                 break;
837
838         case PRIQ_MOD_CLASS:
839                 error = priqcmd_modify_class((struct priq_modify_class *)addr);
840                 break;
841
842         case PRIQ_ADD_FILTER:
843                 error = priqcmd_add_filter((struct priq_add_filter *)addr);
844                 break;
845
846         case PRIQ_DEL_FILTER:
847                 error = priqcmd_delete_filter((struct priq_delete_filter *)addr);
848                 break;
849
850         case PRIQ_GETSTATS:
851                 error = priqcmd_class_stats((struct priq_class_stats *)addr);
852                 break;
853
854         default:
855                 error = EINVAL;
856                 break;
857         }
858         return error;
859 }
860
861 static int
862 priqcmd_if_attach(ap)
863         struct priq_interface *ap;
864 {
865         struct priq_if *pif;
866         struct ifnet *ifp;
867         int error;
868
869         if ((ifp = ifunit(ap->ifname)) == NULL)
870                 return (ENXIO);
871
872         if ((pif = priq_attach(&ifp->if_snd, ap->arg)) == NULL)
873                 return (ENOMEM);
874
875         /*
876          * set PRIQ to this ifnet structure.
877          */
878         if ((error = altq_attach(&ifp->if_snd, ALTQT_PRIQ, pif,
879                                  priq_enqueue, priq_dequeue, priq_request,
880                                  &pif->pif_classifier, acc_classify)) != 0)
881                 (void)priq_detach(pif);
882
883         return (error);
884 }
885
886 static int
887 priqcmd_if_detach(ap)
888         struct priq_interface *ap;
889 {
890         struct priq_if *pif;
891         int error;
892
893         if ((pif = altq_lookup(ap->ifname, ALTQT_PRIQ)) == NULL)
894                 return (EBADF);
895
896         if (ALTQ_IS_ENABLED(pif->pif_ifq))
897                 altq_disable(pif->pif_ifq);
898
899         if ((error = altq_detach(pif->pif_ifq)))
900                 return (error);
901
902         return priq_detach(pif);
903 }
904
905 static int
906 priqcmd_add_class(ap)
907         struct priq_add_class *ap;
908 {
909         struct priq_if *pif;
910         struct priq_class *cl;
911         int qid;
912
913         if ((pif = altq_lookup(ap->iface.ifname, ALTQT_PRIQ)) == NULL)
914                 return (EBADF);
915
916         if (ap->pri < 0 || ap->pri >= PRIQ_MAXPRI)
917                 return (EINVAL);
918         if (pif->pif_classes[ap->pri] != NULL)
919                 return (EBUSY);
920
921         qid = ap->pri + 1;
922         if ((cl = priq_class_create(pif, ap->pri,
923             ap->qlimit, ap->flags, qid)) == NULL)
924                 return (ENOMEM);
925
926         /* return a class handle to the user */
927         ap->class_handle = cl->cl_handle;
928
929         return (0);
930 }
931
932 static int
933 priqcmd_delete_class(ap)
934         struct priq_delete_class *ap;
935 {
936         struct priq_if *pif;
937         struct priq_class *cl;
938
939         if ((pif = altq_lookup(ap->iface.ifname, ALTQT_PRIQ)) == NULL)
940                 return (EBADF);
941
942         if ((cl = clh_to_clp(pif, ap->class_handle)) == NULL)
943                 return (EINVAL);
944
945         return priq_class_destroy(cl);
946 }
947
948 static int
949 priqcmd_modify_class(ap)
950         struct priq_modify_class *ap;
951 {
952         struct priq_if *pif;
953         struct priq_class *cl;
954
955         if ((pif = altq_lookup(ap->iface.ifname, ALTQT_PRIQ)) == NULL)
956                 return (EBADF);
957
958         if (ap->pri < 0 || ap->pri >= PRIQ_MAXPRI)
959                 return (EINVAL);
960
961         if ((cl = clh_to_clp(pif, ap->class_handle)) == NULL)
962                 return (EINVAL);
963
964         /*
965          * if priority is changed, move the class to the new priority
966          */
967         if (pif->pif_classes[ap->pri] != cl) {
968                 if (pif->pif_classes[ap->pri] != NULL)
969                         return (EEXIST);
970                 pif->pif_classes[cl->cl_pri] = NULL;
971                 pif->pif_classes[ap->pri] = cl;
972                 cl->cl_pri = ap->pri;
973         }
974
975         /* call priq_class_create to change class parameters */
976         if ((cl = priq_class_create(pif, ap->pri,
977             ap->qlimit, ap->flags, ap->class_handle)) == NULL)
978                 return (ENOMEM);
979         return 0;
980 }
981
982 static int
983 priqcmd_add_filter(ap)
984         struct priq_add_filter *ap;
985 {
986         struct priq_if *pif;
987         struct priq_class *cl;
988
989         if ((pif = altq_lookup(ap->iface.ifname, ALTQT_PRIQ)) == NULL)
990                 return (EBADF);
991
992         if ((cl = clh_to_clp(pif, ap->class_handle)) == NULL)
993                 return (EINVAL);
994
995         return acc_add_filter(&pif->pif_classifier, &ap->filter,
996                               cl, &ap->filter_handle);
997 }
998
999 static int
1000 priqcmd_delete_filter(ap)
1001         struct priq_delete_filter *ap;
1002 {
1003         struct priq_if *pif;
1004
1005         if ((pif = altq_lookup(ap->iface.ifname, ALTQT_PRIQ)) == NULL)
1006                 return (EBADF);
1007
1008         return acc_delete_filter(&pif->pif_classifier,
1009                                  ap->filter_handle);
1010 }
1011
1012 static int
1013 priqcmd_class_stats(ap)
1014         struct priq_class_stats *ap;
1015 {
1016         struct priq_if *pif;
1017         struct priq_class *cl;
1018         struct priq_classstats stats, *usp;
1019         int     pri, error;
1020
1021         if ((pif = altq_lookup(ap->iface.ifname, ALTQT_PRIQ)) == NULL)
1022                 return (EBADF);
1023
1024         ap->maxpri = pif->pif_maxpri;
1025
1026         /* then, read the next N classes in the tree */
1027         usp = ap->stats;
1028         for (pri = 0; pri <= pif->pif_maxpri; pri++) {
1029                 cl = pif->pif_classes[pri];
1030                 if (cl != NULL)
1031                         get_class_stats(&stats, cl);
1032                 else
1033                         bzero(&stats, sizeof(stats));
1034                 if ((error = copyout((caddr_t)&stats, (caddr_t)usp++,
1035                                      sizeof(stats))) != 0)
1036                         return (error);
1037         }
1038         return (0);
1039 }
1040
1041 #ifdef KLD_MODULE
1042
1043 static struct altqsw priq_sw =
1044         {"priq", priqopen, priqclose, priqioctl};
1045
1046 ALTQ_MODULE(altq_priq, ALTQT_PRIQ, &priq_sw);
1047 MODULE_DEPEND(altq_priq, altq_red, 1, 1, 1);
1048 MODULE_DEPEND(altq_priq, altq_rio, 1, 1, 1);
1049
1050 #endif /* KLD_MODULE */
1051
1052 #endif /* ALTQ3_COMPAT */
1053 #endif /* ALTQ_PRIQ */