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