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