altq_hfsc.c revision 1.5
1/*	$NetBSD: altq_hfsc.c,v 1.5 2001/11/12 23:14:21 lukem Exp $	*/
2/*	$KAME: altq_hfsc.c,v 1.9 2001/10/26 04:56:11 kjc Exp $	*/
3
4/*
5 * Copyright (c) 1997-1999 Carnegie Mellon University. All Rights Reserved.
6 *
7 * Permission to use, copy, modify, and distribute this software and
8 * its documentation is hereby granted (including for commercial or
9 * for-profit use), provided that both the copyright notice and this
10 * permission notice appear in all copies of the software, derivative
11 * works, or modified versions, and any portions thereof, and that
12 * both notices appear in supporting documentation, and that credit
13 * is given to Carnegie Mellon University in all publications reporting
14 * on direct or indirect use of this code or its derivatives.
15 *
16 * THIS SOFTWARE IS EXPERIMENTAL AND IS KNOWN TO HAVE BUGS, SOME OF
17 * WHICH MAY HAVE SERIOUS CONSEQUENCES.  CARNEGIE MELLON PROVIDES THIS
18 * SOFTWARE IN ITS ``AS IS'' CONDITION, AND ANY EXPRESS OR IMPLIED
19 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED.  IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
24 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
28 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
29 * DAMAGE.
30 *
31 * Carnegie Mellon encourages (but does not require) users of this
32 * software to return any improvements or extensions that they make,
33 * and to grant Carnegie Mellon the rights to redistribute these
34 * changes without encumbrance.
35 */
36/*
37 * H-FSC is described in Proceedings of SIGCOMM'97,
38 * "A Hierarchical Fair Service Curve Algorithm for Link-Sharing,
39 * Real-Time and Priority Service"
40 * by Ion Stoica, Hui Zhang, and T. S. Eugene Ng.
41 */
42
43#include <sys/cdefs.h>
44__KERNEL_RCSID(0, "$NetBSD: altq_hfsc.c,v 1.5 2001/11/12 23:14:21 lukem Exp $");
45
46#if defined(__FreeBSD__) || defined(__NetBSD__)
47#include "opt_altq.h"
48#if (__FreeBSD__ != 2)
49#include "opt_inet.h"
50#ifdef __FreeBSD__
51#include "opt_inet6.h"
52#endif
53#endif
54#endif /* __FreeBSD__ || __NetBSD__ */
55
56#ifdef ALTQ_HFSC  /* hfsc is enabled by ALTQ_HFSC option in opt_altq.h */
57
58#include <sys/param.h>
59#include <sys/malloc.h>
60#include <sys/mbuf.h>
61#include <sys/socket.h>
62#include <sys/sockio.h>
63#include <sys/systm.h>
64#include <sys/proc.h>
65#include <sys/errno.h>
66#include <sys/kernel.h>
67#include <sys/queue.h>
68
69#include <net/if.h>
70#include <net/if_types.h>
71
72#include <altq/altq.h>
73#include <altq/altq_conf.h>
74#include <altq/altq_hfsc.h>
75
76/*
77 * function prototypes
78 */
79static struct hfsc_if *hfsc_attach __P((struct ifaltq *, u_int));
80static int hfsc_detach __P((struct hfsc_if *));
81static int hfsc_clear_interface __P((struct hfsc_if *));
82static int hfsc_request __P((struct ifaltq *, int, void *));
83static void hfsc_purge __P((struct hfsc_if *));
84static struct hfsc_class *hfsc_class_create __P((struct hfsc_if *,
85		 struct service_curve *, struct hfsc_class *, int, int));
86static int hfsc_class_destroy __P((struct hfsc_class *));
87static int hfsc_class_modify __P((struct hfsc_class *,
88			  struct service_curve *, struct service_curve *));
89static struct hfsc_class *hfsc_nextclass __P((struct hfsc_class *));
90
91static int hfsc_enqueue __P((struct ifaltq *, struct mbuf *,
92			     struct altq_pktattr *));
93static struct mbuf *hfsc_dequeue __P((struct ifaltq *, int));
94
95static int hfsc_addq __P((struct hfsc_class *, struct mbuf *));
96static struct mbuf *hfsc_getq __P((struct hfsc_class *));
97static struct mbuf *hfsc_pollq __P((struct hfsc_class *));
98static void hfsc_purgeq __P((struct hfsc_class *));
99
100static void set_active __P((struct hfsc_class *, int));
101static void set_passive __P((struct hfsc_class *));
102
103static void init_ed __P((struct hfsc_class *, int));
104static void update_ed __P((struct hfsc_class *, int));
105static void update_d __P((struct hfsc_class *, int));
106static void init_v __P((struct hfsc_class *, int));
107static void update_v __P((struct hfsc_class *, int));
108static ellist_t *ellist_alloc __P((void));
109static void ellist_destroy __P((ellist_t *));
110static void ellist_insert __P((struct hfsc_class *));
111static void ellist_remove __P((struct hfsc_class *));
112static void ellist_update __P((struct hfsc_class *));
113struct hfsc_class *ellist_get_mindl __P((ellist_t *));
114static actlist_t *actlist_alloc __P((void));
115static void actlist_destroy __P((actlist_t *));
116static void actlist_insert __P((struct hfsc_class *));
117static void actlist_remove __P((struct hfsc_class *));
118static void actlist_update __P((struct hfsc_class *));
119
120static __inline u_int64_t seg_x2y __P((u_int64_t, u_int64_t));
121static __inline u_int64_t seg_y2x __P((u_int64_t, u_int64_t));
122static __inline u_int64_t m2sm __P((u_int));
123static __inline u_int64_t m2ism __P((u_int));
124static __inline u_int64_t d2dx __P((u_int));
125static u_int sm2m __P((u_int64_t));
126static u_int dx2d __P((u_int64_t));
127
128static void sc2isc __P((struct service_curve *, struct internal_sc *));
129static void rtsc_init __P((struct runtime_sc *, struct internal_sc *,
130			   u_int64_t, u_int64_t));
131static u_int64_t rtsc_y2x __P((struct runtime_sc *, u_int64_t));
132static u_int64_t rtsc_x2y __P((struct runtime_sc *, u_int64_t));
133static void rtsc_min __P((struct runtime_sc *, struct internal_sc *,
134			  u_int64_t, u_int64_t));
135
136int hfscopen __P((dev_t, int, int, struct proc *));
137int hfscclose __P((dev_t, int, int, struct proc *));
138int hfscioctl __P((dev_t, ioctlcmd_t, caddr_t, int, struct proc *));
139static int hfsccmd_if_attach __P((struct hfsc_attach *));
140static int hfsccmd_if_detach __P((struct hfsc_interface *));
141static int hfsccmd_add_class __P((struct hfsc_add_class *));
142static int hfsccmd_delete_class __P((struct hfsc_delete_class *));
143static int hfsccmd_modify_class __P((struct hfsc_modify_class *));
144static int hfsccmd_add_filter __P((struct hfsc_add_filter *));
145static int hfsccmd_delete_filter __P((struct hfsc_delete_filter *));
146static int hfsccmd_class_stats __P((struct hfsc_class_stats *));
147static void get_class_stats __P((struct class_stats *, struct hfsc_class *));
148static struct hfsc_class *clh_to_clp __P((struct hfsc_if *, u_long));
149static u_long clp_to_clh __P((struct hfsc_class *));
150
151/*
152 * macros
153 */
154#define	is_a_parent_class(cl)	((cl)->cl_children != NULL)
155
156/* hif_list keeps all hfsc_if's allocated. */
157static struct hfsc_if *hif_list = NULL;
158
159static struct hfsc_if *
160hfsc_attach(ifq, bandwidth)
161	struct ifaltq *ifq;
162	u_int bandwidth;
163{
164	struct hfsc_if *hif;
165	struct service_curve root_sc;
166
167	MALLOC(hif, struct hfsc_if *, sizeof(struct hfsc_if),
168	       M_DEVBUF, M_WAITOK);
169	if (hif == NULL)
170		return (NULL);
171	bzero(hif, sizeof(struct hfsc_if));
172
173	hif->hif_eligible = ellist_alloc();
174	if (hif->hif_eligible == NULL) {
175		FREE(hif, M_DEVBUF);
176		return NULL;
177	}
178
179	hif->hif_ifq = ifq;
180
181	/*
182	 * create root class
183	 */
184	root_sc.m1 = bandwidth;
185	root_sc.d = 0;
186	root_sc.m2 = bandwidth;
187	if ((hif->hif_rootclass =
188	     hfsc_class_create(hif, &root_sc, NULL, 0, 0)) == NULL) {
189		FREE(hif, M_DEVBUF);
190		return (NULL);
191	}
192
193	/* add this state to the hfsc list */
194	hif->hif_next = hif_list;
195	hif_list = hif;
196
197	return (hif);
198}
199
200static int
201hfsc_detach(hif)
202	struct hfsc_if *hif;
203{
204	(void)hfsc_clear_interface(hif);
205	(void)hfsc_class_destroy(hif->hif_rootclass);
206
207	/* remove this interface from the hif list */
208	if (hif_list == hif)
209		hif_list = hif->hif_next;
210	else {
211		struct hfsc_if *h;
212
213		for (h = hif_list; h != NULL; h = h->hif_next)
214			if (h->hif_next == hif) {
215				h->hif_next = hif->hif_next;
216				break;
217			}
218		ASSERT(h != NULL);
219	}
220
221	ellist_destroy(hif->hif_eligible);
222
223	FREE(hif, M_DEVBUF);
224
225	return (0);
226}
227
228/*
229 * bring the interface back to the initial state by discarding
230 * all the filters and classes except the root class.
231 */
232static int
233hfsc_clear_interface(hif)
234	struct hfsc_if *hif;
235{
236	struct hfsc_class	*cl;
237
238	/* free the filters for this interface */
239	acc_discard_filters(&hif->hif_classifier, NULL, 1);
240
241	/* clear out the classes */
242	while ((cl = hif->hif_rootclass->cl_children) != NULL) {
243		/*
244		 * remove the first leaf class found in the hierarchy
245		 * then start over
246		 */
247		for (; cl != NULL; cl = hfsc_nextclass(cl)) {
248			if (!is_a_parent_class(cl)) {
249				(void)hfsc_class_destroy(cl);
250				break;
251			}
252		}
253	}
254
255	return (0);
256}
257
258static int
259hfsc_request(ifq, req, arg)
260	struct ifaltq *ifq;
261	int req;
262	void *arg;
263{
264	struct hfsc_if	*hif = (struct hfsc_if *)ifq->altq_disc;
265
266	switch (req) {
267	case ALTRQ_PURGE:
268		hfsc_purge(hif);
269		break;
270	}
271	return (0);
272}
273
274/* discard all the queued packets on the interface */
275static void
276hfsc_purge(hif)
277	struct hfsc_if *hif;
278{
279	struct hfsc_class *cl;
280
281	for (cl = hif->hif_rootclass; cl != NULL; cl = hfsc_nextclass(cl))
282		if (!qempty(cl->cl_q))
283			hfsc_purgeq(cl);
284	if (ALTQ_IS_ENABLED(hif->hif_ifq))
285		hif->hif_ifq->ifq_len = 0;
286}
287
288struct hfsc_class *
289hfsc_class_create(hif, sc, parent, qlimit, flags)
290	struct hfsc_if *hif;
291	struct service_curve *sc;
292	struct hfsc_class *parent;
293	int qlimit, flags;
294{
295	struct hfsc_class *cl, *p;
296	int s;
297
298#ifndef ALTQ_RED
299	if (flags & HFCF_RED) {
300		printf("hfsc_class_create: RED not configured for HFSC!\n");
301		return (NULL);
302	}
303#endif
304
305	MALLOC(cl, struct hfsc_class *, sizeof(struct hfsc_class),
306	       M_DEVBUF, M_WAITOK);
307	if (cl == NULL)
308		return (NULL);
309	bzero(cl, sizeof(struct hfsc_class));
310
311	MALLOC(cl->cl_q, class_queue_t *, sizeof(class_queue_t),
312	       M_DEVBUF, M_WAITOK);
313	if (cl->cl_q == NULL)
314		goto err_ret;
315	bzero(cl->cl_q, sizeof(class_queue_t));
316
317	cl->cl_actc = actlist_alloc();
318	if (cl->cl_actc == NULL)
319		goto err_ret;
320
321	if (qlimit == 0)
322		qlimit = 50;  /* use default */
323	qlimit(cl->cl_q) = qlimit;
324	qtype(cl->cl_q) = Q_DROPTAIL;
325	qlen(cl->cl_q) = 0;
326	cl->cl_flags = flags;
327#ifdef ALTQ_RED
328	if (flags & (HFCF_RED|HFCF_RIO)) {
329		int red_flags, red_pkttime;
330
331		red_flags = 0;
332		if (flags & HFCF_ECN)
333			red_flags |= REDF_ECN;
334#ifdef ALTQ_RIO
335		if (flags & HFCF_CLEARDSCP)
336			red_flags |= RIOF_CLEARDSCP;
337#endif
338		if (sc->m2 < 8)
339			red_pkttime = 1000 * 1000 * 1000; /* 1 sec */
340		else
341			red_pkttime = (int64_t)hif->hif_ifq->altq_ifp->if_mtu
342				* 1000 * 1000 * 1000 / (sc->m2 / 8);
343		if (flags & HFCF_RED) {
344			cl->cl_red = red_alloc(0, 0, 0, 0,
345					       red_flags, red_pkttime);
346			if (cl->cl_red != NULL)
347				qtype(cl->cl_q) = Q_RED;
348		}
349#ifdef ALTQ_RIO
350		else {
351			cl->cl_red = (red_t *)rio_alloc(0, NULL,
352						      red_flags, red_pkttime);
353			if (cl->cl_red != NULL)
354				qtype(cl->cl_q) = Q_RIO;
355		}
356#endif
357	}
358#endif /* ALTQ_RED */
359
360	if (sc != NULL && (sc->m1 != 0 || sc->m2 != 0)) {
361		MALLOC(cl->cl_rsc, struct internal_sc *,
362		       sizeof(struct internal_sc), M_DEVBUF, M_WAITOK);
363		if (cl->cl_rsc == NULL)
364			goto err_ret;
365		bzero(cl->cl_rsc, sizeof(struct internal_sc));
366		sc2isc(sc, cl->cl_rsc);
367		rtsc_init(&cl->cl_deadline, cl->cl_rsc, 0, 0);
368		rtsc_init(&cl->cl_eligible, cl->cl_rsc, 0, 0);
369
370		MALLOC(cl->cl_fsc, struct internal_sc *,
371		       sizeof(struct internal_sc), M_DEVBUF, M_WAITOK);
372		if (cl->cl_fsc == NULL)
373			goto err_ret;
374		bzero(cl->cl_fsc, sizeof(struct internal_sc));
375		sc2isc(sc, cl->cl_fsc);
376		rtsc_init(&cl->cl_virtual, cl->cl_fsc, 0, 0);
377	}
378
379	cl->cl_id = hif->hif_classid++;
380	cl->cl_handle = (u_long)cl;  /* XXX: just a pointer to this class */
381	cl->cl_hif = hif;
382	cl->cl_parent = parent;
383
384	s = splnet();
385	hif->hif_classes++;
386	if (flags & HFCF_DEFAULTCLASS)
387		hif->hif_defaultclass = cl;
388
389	/* add this class to the children list of the parent */
390	if (parent == NULL) {
391		/* this is root class */
392	}
393	else if ((p = parent->cl_children) == NULL)
394		parent->cl_children = cl;
395	else {
396		while (p->cl_siblings != NULL)
397			p = p->cl_siblings;
398		p->cl_siblings = cl;
399	}
400	splx(s);
401
402	return (cl);
403
404 err_ret:
405	if (cl->cl_actc != NULL)
406		actlist_destroy(cl->cl_actc);
407	if (cl->cl_red != NULL) {
408#ifdef ALTQ_RIO
409		if (q_is_rio(cl->cl_q))
410			rio_destroy((rio_t *)cl->cl_red);
411#endif
412#ifdef ALTQ_RED
413		if (q_is_red(cl->cl_q))
414			red_destroy(cl->cl_red);
415#endif
416	}
417	if (cl->cl_fsc != NULL)
418		FREE(cl->cl_fsc, M_DEVBUF);
419	if (cl->cl_rsc != NULL)
420		FREE(cl->cl_rsc, M_DEVBUF);
421	if (cl->cl_q != NULL)
422		FREE(cl->cl_q, M_DEVBUF);
423	FREE(cl, M_DEVBUF);
424	return (NULL);
425}
426
427static int
428hfsc_class_destroy(cl)
429	struct hfsc_class *cl;
430{
431	int s;
432
433	if (is_a_parent_class(cl))
434		return (EBUSY);
435
436	s = splnet();
437
438	/* delete filters referencing to this class */
439	acc_discard_filters(&cl->cl_hif->hif_classifier, cl, 0);
440
441	if (!qempty(cl->cl_q))
442		hfsc_purgeq(cl);
443
444	if (cl->cl_parent == NULL) {
445		/* this is root class */
446	} else {
447		struct hfsc_class *p = cl->cl_parent->cl_children;
448
449		if (p == cl)
450			cl->cl_parent->cl_children = cl->cl_siblings;
451		else do {
452			if (p->cl_siblings == cl) {
453				p->cl_siblings = cl->cl_siblings;
454				break;
455			}
456		} while ((p = p->cl_siblings) != NULL);
457		ASSERT(p != NULL);
458	}
459	cl->cl_hif->hif_classes--;
460	splx(s);
461
462	actlist_destroy(cl->cl_actc);
463
464	if (cl->cl_red != NULL) {
465#ifdef ALTQ_RIO
466		if (q_is_rio(cl->cl_q))
467			rio_destroy((rio_t *)cl->cl_red);
468#endif
469#ifdef ALTQ_RED
470		if (q_is_red(cl->cl_q))
471			red_destroy(cl->cl_red);
472#endif
473	}
474	if (cl->cl_fsc != NULL)
475		FREE(cl->cl_fsc, M_DEVBUF);
476	if (cl->cl_rsc != NULL)
477		FREE(cl->cl_rsc, M_DEVBUF);
478	FREE(cl->cl_q, M_DEVBUF);
479	FREE(cl, M_DEVBUF);
480
481	return (0);
482}
483
484static int
485hfsc_class_modify(cl, rsc, fsc)
486	struct hfsc_class *cl;
487	struct service_curve *rsc, *fsc;
488{
489	struct internal_sc *tmp;
490	int s;
491
492	s = splnet();
493	if (!qempty(cl->cl_q))
494		hfsc_purgeq(cl);
495
496	if (rsc != NULL) {
497		if (rsc->m1 == 0 && rsc->m2 == 0) {
498			if (cl->cl_rsc != NULL) {
499				FREE(cl->cl_rsc, M_DEVBUF);
500				cl->cl_rsc = NULL;
501			}
502		} else {
503			if (cl->cl_rsc == NULL) {
504				MALLOC(tmp, struct internal_sc *,
505				       sizeof(struct internal_sc),
506				       M_DEVBUF, M_WAITOK);
507				if (tmp == NULL) {
508					splx(s);
509					return (ENOMEM);
510				}
511				cl->cl_rsc = tmp;
512			}
513			bzero(cl->cl_rsc, sizeof(struct internal_sc));
514			sc2isc(rsc, cl->cl_rsc);
515			rtsc_init(&cl->cl_deadline, cl->cl_rsc, 0, 0);
516			rtsc_init(&cl->cl_eligible, cl->cl_rsc, 0, 0);
517		}
518	}
519
520	if (fsc != NULL) {
521		if (fsc->m1 == 0 && fsc->m2 == 0) {
522			if (cl->cl_fsc != NULL) {
523				FREE(cl->cl_fsc, M_DEVBUF);
524				cl->cl_fsc = NULL;
525			}
526		} else {
527			if (cl->cl_fsc == NULL) {
528				MALLOC(tmp, struct internal_sc *,
529				       sizeof(struct internal_sc),
530				       M_DEVBUF, M_WAITOK);
531				if (tmp == NULL) {
532					splx(s);
533					return (ENOMEM);
534				}
535				cl->cl_fsc = tmp;
536			}
537			bzero(cl->cl_fsc, sizeof(struct internal_sc));
538			sc2isc(fsc, cl->cl_fsc);
539			rtsc_init(&cl->cl_virtual, cl->cl_fsc, 0, 0);
540		}
541	}
542	splx(s);
543
544	return (0);
545}
546
547/*
548 * hfsc_nextclass returns the next class in the tree.
549 *   usage:
550 * 	for (cl = hif->hif_rootclass; cl != NULL; cl = hfsc_nextclass(cl))
551 *		do_something;
552 */
553static struct hfsc_class *
554hfsc_nextclass(cl)
555	struct hfsc_class *cl;
556{
557	if (cl->cl_children != NULL)
558		cl = cl->cl_children;
559	else if (cl->cl_siblings != NULL)
560		cl = cl->cl_siblings;
561	else {
562		while ((cl = cl->cl_parent) != NULL)
563			if (cl->cl_siblings) {
564				cl = cl->cl_siblings;
565				break;
566			}
567	}
568
569	return (cl);
570}
571
572/*
573 * hfsc_enqueue is an enqueue function to be registered to
574 * (*altq_enqueue) in struct ifaltq.
575 */
576static int
577hfsc_enqueue(ifq, m, pktattr)
578	struct ifaltq *ifq;
579	struct mbuf *m;
580	struct altq_pktattr *pktattr;
581{
582	struct hfsc_if	*hif = (struct hfsc_if *)ifq->altq_disc;
583	struct hfsc_class *cl;
584	int len;
585
586	/* grab class set by classifier */
587	if (pktattr == NULL || (cl = pktattr->pattr_class) == NULL)
588		cl = hif->hif_defaultclass;
589	cl->cl_pktattr = pktattr;  /* save proto hdr used by ECN */
590
591	len = m_pktlen(m);
592	if (hfsc_addq(cl, m) != 0) {
593		/* drop occurred.  mbuf was freed in hfsc_addq. */
594		PKTCNTR_ADD(&cl->cl_stats.drop_cnt, len);
595		return (ENOBUFS);
596	}
597	IFQ_INC_LEN(ifq);
598	cl->cl_hif->hif_packets++;
599
600	/* successfully queued. */
601	if (qlen(cl->cl_q) == 1)
602		set_active(cl, m_pktlen(m));
603
604#ifdef HFSC_PKTLOG
605	/* put the logging_hook here */
606#endif
607	return (0);
608}
609
610/*
611 * hfsc_dequeue is a dequeue function to be registered to
612 * (*altq_dequeue) in struct ifaltq.
613 *
614 * note: ALTDQ_POLL returns the next packet without removing the packet
615 *	from the queue.  ALTDQ_REMOVE is a normal dequeue operation.
616 *	ALTDQ_REMOVE must return the same packet if called immediately
617 *	after ALTDQ_POLL.
618 */
619static struct mbuf *
620hfsc_dequeue(ifq, op)
621	struct ifaltq	*ifq;
622	int		op;
623{
624	struct hfsc_if	*hif = (struct hfsc_if *)ifq->altq_disc;
625	struct hfsc_class *cl;
626	struct mbuf *m;
627	int len, next_len;
628	int realtime = 0;
629
630	if (hif->hif_packets == 0)
631		/* no packet in the tree */
632		return (NULL);
633
634	if (op == ALTDQ_REMOVE && hif->hif_pollcache != NULL) {
635		u_int64_t cur_time;
636
637		cl = hif->hif_pollcache;
638		hif->hif_pollcache = NULL;
639		/* check if the class was scheduled by real-time criteria */
640		if (cl->cl_rsc != NULL) {
641			cur_time = read_machclk();
642			realtime = (cl->cl_e <= cur_time);
643		}
644	} else {
645		/*
646		 * if there are eligible classes, use real-time criteria.
647		 * find the class with the minimum deadline among
648		 * the eligible classes.
649		 */
650		if ((cl = ellist_get_mindl(hif->hif_eligible)) != NULL) {
651			realtime = 1;
652		} else {
653			/*
654			 * use link-sharing criteria
655			 * get the class with the minimum vt in the hierarchy
656			 */
657			cl = hif->hif_rootclass;
658			while (is_a_parent_class(cl)) {
659				cl = actlist_first(cl->cl_actc);
660				if (cl == NULL)
661					return (NULL);
662			}
663		}
664
665		if (op == ALTDQ_POLL) {
666			hif->hif_pollcache = cl;
667			m = hfsc_pollq(cl);
668			return (m);
669		}
670	}
671
672	m = hfsc_getq(cl);
673	len = m_pktlen(m);
674	cl->cl_hif->hif_packets--;
675	IFQ_DEC_LEN(ifq);
676	PKTCNTR_ADD(&cl->cl_stats.xmit_cnt, len);
677
678	update_v(cl, len);
679	if (realtime)
680		cl->cl_cumul += len;
681
682	if (!qempty(cl->cl_q)) {
683		if (cl->cl_rsc != NULL) {
684			/* update ed */
685			next_len = m_pktlen(qhead(cl->cl_q));
686
687			if (realtime)
688				update_ed(cl, next_len);
689			else
690				update_d(cl, next_len);
691		}
692	} else {
693		/* the class becomes passive */
694		set_passive(cl);
695	}
696
697#ifdef HFSC_PKTLOG
698	/* put the logging_hook here */
699#endif
700
701	return (m);
702}
703
704static int
705hfsc_addq(cl, m)
706	struct hfsc_class *cl;
707	struct mbuf *m;
708{
709
710#ifdef ALTQ_RIO
711	if (q_is_rio(cl->cl_q))
712		return rio_addq((rio_t *)cl->cl_red, cl->cl_q,
713				m, cl->cl_pktattr);
714#endif
715#ifdef ALTQ_RED
716	if (q_is_red(cl->cl_q))
717		return red_addq(cl->cl_red, cl->cl_q, m, cl->cl_pktattr);
718#endif
719	if (qlen(cl->cl_q) >= qlimit(cl->cl_q)) {
720		m_freem(m);
721		return (-1);
722	}
723
724	if (cl->cl_flags & HFCF_CLEARDSCP)
725		write_dsfield(m, cl->cl_pktattr, 0);
726
727	_addq(cl->cl_q, m);
728
729	return (0);
730}
731
732static struct mbuf *
733hfsc_getq(cl)
734	struct hfsc_class *cl;
735{
736#ifdef ALTQ_RIO
737	if (q_is_rio(cl->cl_q))
738		return rio_getq((rio_t *)cl->cl_red, cl->cl_q);
739#endif
740#ifdef ALTQ_RED
741	if (q_is_red(cl->cl_q))
742		return red_getq(cl->cl_red, cl->cl_q);
743#endif
744	return _getq(cl->cl_q);
745}
746
747static struct mbuf *
748hfsc_pollq(cl)
749	struct hfsc_class *cl;
750{
751	return qhead(cl->cl_q);
752}
753
754static void
755hfsc_purgeq(cl)
756	struct hfsc_class *cl;
757{
758	struct mbuf *m;
759
760	if (qempty(cl->cl_q))
761		return;
762
763	while ((m = _getq(cl->cl_q)) != NULL) {
764		PKTCNTR_ADD(&cl->cl_stats.drop_cnt, m_pktlen(m));
765		m_freem(m);
766	}
767	ASSERT(qlen(cl->cl_q) == 0);
768
769	set_passive(cl);
770}
771
772static void
773set_active(cl, len)
774	struct hfsc_class *cl;
775	int len;
776{
777	if (cl->cl_rsc != NULL)
778		init_ed(cl, len);
779	if (cl->cl_fsc != NULL)
780		init_v(cl, len);
781
782	cl->cl_stats.period++;
783}
784
785static void
786set_passive(cl)
787	struct hfsc_class *cl;
788{
789	if (cl->cl_rsc != NULL)
790		ellist_remove(cl);
791
792	if (cl->cl_fsc != NULL) {
793		while (cl->cl_parent != NULL) {
794			if (--cl->cl_nactive == 0) {
795				/* remove this class from the vt list */
796				actlist_remove(cl);
797			} else
798				/* still has active children */
799				break;
800
801			/* go up to the parent class */
802			cl = cl->cl_parent;
803		}
804	}
805}
806
807static void
808init_ed(cl, next_len)
809	struct hfsc_class *cl;
810	int next_len;
811{
812	u_int64_t cur_time;
813
814	cur_time = read_machclk();
815
816	/* update the deadline curve */
817	rtsc_min(&cl->cl_deadline, cl->cl_rsc, cur_time, cl->cl_cumul);
818
819	/*
820	 * update the eligible curve.
821	 * for concave, it is equal to the deadline curve.
822	 * for convex, it is a linear curve with slope m2.
823	 */
824	cl->cl_eligible = cl->cl_deadline;
825	if (cl->cl_rsc->sm1 <= cl->cl_rsc->sm2) {
826		cl->cl_eligible.dx = 0;
827		cl->cl_eligible.dy = 0;
828	}
829
830	/* compute e and d */
831	cl->cl_e = rtsc_y2x(&cl->cl_eligible, cl->cl_cumul);
832	cl->cl_d = rtsc_y2x(&cl->cl_deadline, cl->cl_cumul + next_len);
833
834	ellist_insert(cl);
835}
836
837static void
838update_ed(cl, next_len)
839	struct hfsc_class *cl;
840	int next_len;
841{
842	cl->cl_e = rtsc_y2x(&cl->cl_eligible, cl->cl_cumul);
843	cl->cl_d = rtsc_y2x(&cl->cl_deadline, cl->cl_cumul + next_len);
844
845	ellist_update(cl);
846}
847
848static void
849update_d(cl, next_len)
850	struct hfsc_class *cl;
851	int next_len;
852{
853	cl->cl_d = rtsc_y2x(&cl->cl_deadline, cl->cl_cumul + next_len);
854}
855
856static void
857init_v(cl, len)
858	struct hfsc_class *cl;
859	int len;
860{
861	struct hfsc_class *min_cl, *max_cl;
862
863	while (cl->cl_parent != NULL) {
864
865		if (cl->cl_nactive++ > 0)
866			/* already active */
867			break;
868
869		min_cl = actlist_first(cl->cl_parent->cl_actc);
870		if (min_cl != NULL) {
871			u_int64_t vt;
872
873			/*
874			 * set vt to the average of the min and max classes.
875			 * if the parent's period didn't change,
876			 * don't decrease vt of the class.
877			 */
878			max_cl = actlist_last(cl->cl_parent->cl_actc);
879			vt = (min_cl->cl_vt + max_cl->cl_vt) / 2;
880			if (cl->cl_parent->cl_vtperiod == cl->cl_parentperiod)
881				vt = max(cl->cl_vt, vt);
882			cl->cl_vt = vt;
883		} else {
884			/* no packet is backlogged.  set vt to 0 */
885			cl->cl_vt = 0;
886		}
887
888		/* update the virtual curve */
889		rtsc_min(&cl->cl_virtual, cl->cl_fsc,
890			 cl->cl_vt, cl->cl_total);
891
892		cl->cl_vtperiod++;  /* increment vt period */
893		cl->cl_parentperiod = cl->cl_parent->cl_vtperiod;
894		if (cl->cl_parent->cl_nactive == 0)
895			cl->cl_parentperiod++;
896
897		actlist_insert(cl);
898
899		/* go up to the parent class */
900		cl = cl->cl_parent;
901	}
902}
903
904static void
905update_v(cl, len)
906	struct hfsc_class *cl;
907	int len;
908{
909	while (cl->cl_parent != NULL) {
910
911		cl->cl_total += len;
912
913		if (cl->cl_fsc != NULL) {
914			cl->cl_vt = rtsc_y2x(&cl->cl_virtual, cl->cl_total);
915
916			/* update the vt list */
917			actlist_update(cl);
918		}
919
920		/* go up to the parent class */
921		cl = cl->cl_parent;
922	}
923}
924
925/*
926 * TAILQ based ellist and actlist implementation
927 * (ion wanted to make a calendar queue based implementation)
928 */
929/*
930 * eligible list holds backlogged classes being sorted by their eligible times.
931 * there is one eligible list per interface.
932 */
933
934static ellist_t *
935ellist_alloc()
936{
937	ellist_t *head;
938
939	MALLOC(head, ellist_t *, sizeof(ellist_t), M_DEVBUF, M_WAITOK);
940	TAILQ_INIT(head);
941	return (head);
942}
943
944static void
945ellist_destroy(head)
946	ellist_t *head;
947{
948	FREE(head, M_DEVBUF);
949}
950
951static void
952ellist_insert(cl)
953	struct hfsc_class *cl;
954{
955	struct hfsc_if	*hif = cl->cl_hif;
956	struct hfsc_class *p;
957
958	/* check the last entry first */
959	if ((p = TAILQ_LAST(hif->hif_eligible, _eligible)) == NULL ||
960	    p->cl_e <= cl->cl_e) {
961		TAILQ_INSERT_TAIL(hif->hif_eligible, cl, cl_ellist);
962		return;
963	}
964
965	TAILQ_FOREACH(p, hif->hif_eligible, cl_ellist) {
966		if (cl->cl_e < p->cl_e) {
967			TAILQ_INSERT_BEFORE(p, cl, cl_ellist);
968			return;
969		}
970	}
971	ASSERT(0); /* should not reach here */
972}
973
974static void
975ellist_remove(cl)
976	struct hfsc_class *cl;
977{
978	struct hfsc_if	*hif = cl->cl_hif;
979
980	TAILQ_REMOVE(hif->hif_eligible, cl, cl_ellist);
981}
982
983static void
984ellist_update(cl)
985	struct hfsc_class *cl;
986{
987	struct hfsc_if	*hif = cl->cl_hif;
988	struct hfsc_class *p, *last;
989
990	/*
991	 * the eligible time of a class increases monotonically.
992	 * if the next entry has a larger eligible time, nothing to do.
993	 */
994	p = TAILQ_NEXT(cl, cl_ellist);
995	if (p == NULL || cl->cl_e <= p->cl_e)
996		return;
997
998	/* check the last entry */
999	last = TAILQ_LAST(hif->hif_eligible, _eligible);
1000	ASSERT(last != NULL);
1001	if (last->cl_e <= cl->cl_e) {
1002		TAILQ_REMOVE(hif->hif_eligible, cl, cl_ellist);
1003		TAILQ_INSERT_TAIL(hif->hif_eligible, cl, cl_ellist);
1004		return;
1005	}
1006
1007	/*
1008	 * the new position must be between the next entry
1009	 * and the last entry
1010	 */
1011	while ((p = TAILQ_NEXT(p, cl_ellist)) != NULL) {
1012		if (cl->cl_e < p->cl_e) {
1013			TAILQ_REMOVE(hif->hif_eligible, cl, cl_ellist);
1014			TAILQ_INSERT_BEFORE(p, cl, cl_ellist);
1015			return;
1016		}
1017	}
1018	ASSERT(0); /* should not reach here */
1019}
1020
1021/* find the class with the minimum deadline among the eligible classes */
1022struct hfsc_class *
1023ellist_get_mindl(head)
1024	ellist_t *head;
1025{
1026	struct hfsc_class *p, *cl = NULL;
1027	u_int64_t cur_time;
1028
1029	cur_time = read_machclk();
1030
1031	TAILQ_FOREACH(p, head, cl_ellist) {
1032		if (p->cl_e > cur_time)
1033			break;
1034		if (cl == NULL || p->cl_d < cl->cl_d)
1035			cl = p;
1036	}
1037	return (cl);
1038}
1039
1040/*
1041 * active children list holds backlogged child classes being sorted
1042 * by their virtual time.
1043 * each intermediate class has one active children list.
1044 */
1045static actlist_t *
1046actlist_alloc()
1047{
1048	actlist_t *head;
1049
1050	MALLOC(head, actlist_t *, sizeof(actlist_t), M_DEVBUF, M_WAITOK);
1051	TAILQ_INIT(head);
1052	return (head);
1053}
1054
1055static void
1056actlist_destroy(head)
1057	actlist_t *head;
1058{
1059	FREE(head, M_DEVBUF);
1060}
1061static void
1062actlist_insert(cl)
1063	struct hfsc_class *cl;
1064{
1065	struct hfsc_class *p;
1066
1067	/* check the last entry first */
1068	if ((p = TAILQ_LAST(cl->cl_parent->cl_actc, _active)) == NULL
1069	    || p->cl_vt <= cl->cl_vt) {
1070		TAILQ_INSERT_TAIL(cl->cl_parent->cl_actc, cl, cl_actlist);
1071		return;
1072	}
1073
1074	TAILQ_FOREACH(p, cl->cl_parent->cl_actc, cl_actlist) {
1075		if (cl->cl_vt < p->cl_vt) {
1076			TAILQ_INSERT_BEFORE(p, cl, cl_actlist);
1077			return;
1078		}
1079	}
1080	ASSERT(0); /* should not reach here */
1081}
1082
1083static void
1084actlist_remove(cl)
1085	struct hfsc_class *cl;
1086{
1087	TAILQ_REMOVE(cl->cl_parent->cl_actc, cl, cl_actlist);
1088}
1089
1090static void
1091actlist_update(cl)
1092	struct hfsc_class *cl;
1093{
1094	struct hfsc_class *p, *last;
1095
1096	/*
1097	 * the virtual time of a class increases monotonically during its
1098	 * backlogged period.
1099	 * if the next entry has a larger virtual time, nothing to do.
1100	 */
1101	p = TAILQ_NEXT(cl, cl_actlist);
1102	if (p == NULL || cl->cl_vt <= p->cl_vt)
1103		return;
1104
1105	/* check the last entry */
1106	last = TAILQ_LAST(cl->cl_parent->cl_actc, _active);
1107	ASSERT(last != NULL);
1108	if (last->cl_vt <= cl->cl_vt) {
1109		TAILQ_REMOVE(cl->cl_parent->cl_actc, cl, cl_actlist);
1110		TAILQ_INSERT_TAIL(cl->cl_parent->cl_actc, cl, cl_actlist);
1111		return;
1112	}
1113
1114	/*
1115	 * the new position must be between the next entry
1116	 * and the last entry
1117	 */
1118	while ((p = TAILQ_NEXT(p, cl_actlist)) != NULL) {
1119		if (cl->cl_vt < p->cl_vt) {
1120			TAILQ_REMOVE(cl->cl_parent->cl_actc, cl, cl_actlist);
1121			TAILQ_INSERT_BEFORE(p, cl, cl_actlist);
1122			return;
1123		}
1124	}
1125	ASSERT(0); /* should not reach here */
1126}
1127
1128/*
1129 * service curve support functions
1130 *
1131 *  external service curve parameters
1132 *	m: bits/sec
1133 *	d: msec
1134 *  internal service curve parameters
1135 *	sm: (bytes/tsc_interval) << SM_SHIFT
1136 *	ism: (tsc_count/byte) << ISM_SHIFT
1137 *	dx: tsc_count
1138 *
1139 * SM_SHIFT and ISM_SHIFT are scaled in order to keep effective digits.
1140 * we should be able to handle 100K-1Gbps linkspeed with 200Hz-1GHz CPU
1141 * speed.  SM_SHIFT and ISM_SHIFT are selected to have at least 3 effective
1142 * digits in decimal using the following table.
1143 *
1144 *  bits/set    100Kbps     1Mbps     10Mbps     100Mbps    1Gbps
1145 *  ----------+-------------------------------------------------------
1146 *  bytes/nsec  12.5e-6    125e-6     1250e-6    12500e-6   125000e-6
1147 *  sm(500MHz)  25.0e-6    250e-6     2500e-6    25000e-6   250000e-6
1148 *  sm(200MHz)  62.5e-6    625e-6     6250e-6    62500e-6   625000e-6
1149 *
1150 *  nsec/byte   80000      8000       800        80         8
1151 *  ism(500MHz) 40000      4000       400        40         4
1152 *  ism(200MHz) 16000      1600       160        16         1.6
1153 */
1154#define	SM_SHIFT	24
1155#define	ISM_SHIFT	10
1156
1157#define	SC_LARGEVAL	(1LL << 32)
1158#define	SC_INFINITY	0xffffffffffffffffLL
1159
1160static __inline u_int64_t
1161seg_x2y(x, sm)
1162	u_int64_t x;
1163	u_int64_t sm;
1164{
1165	u_int64_t y;
1166
1167	if (x < SC_LARGEVAL)
1168		y = x * sm >> SM_SHIFT;
1169	else
1170		y = (x >> SM_SHIFT) * sm;
1171	return (y);
1172}
1173
1174static __inline u_int64_t
1175seg_y2x(y, ism)
1176	u_int64_t y;
1177	u_int64_t ism;
1178{
1179	u_int64_t x;
1180
1181	if (y == 0)
1182		x = 0;
1183	else if (ism == SC_INFINITY)
1184		x = SC_INFINITY;
1185	else if (y < SC_LARGEVAL)
1186		x = y * ism >> ISM_SHIFT;
1187	else
1188		x = (y >> ISM_SHIFT) * ism;
1189	return (x);
1190}
1191
1192static __inline u_int64_t
1193m2sm(m)
1194	u_int m;
1195{
1196	u_int64_t sm;
1197
1198	sm = ((u_int64_t)m << SM_SHIFT) / 8 / machclk_freq;
1199	return (sm);
1200}
1201
1202static __inline u_int64_t
1203m2ism(m)
1204	u_int m;
1205{
1206	u_int64_t ism;
1207
1208	if (m == 0)
1209		ism = SC_INFINITY;
1210	else
1211		ism = ((u_int64_t)machclk_freq << ISM_SHIFT) * 8 / m;
1212	return (ism);
1213}
1214
1215static __inline u_int64_t
1216d2dx(d)
1217	u_int	d;
1218{
1219	u_int64_t dx;
1220
1221	dx = ((u_int64_t)d * machclk_freq) / 1000;
1222	return (dx);
1223}
1224
1225static u_int
1226sm2m(sm)
1227	u_int64_t sm;
1228{
1229	u_int64_t m;
1230
1231	m = (sm * 8 * machclk_freq) >> SM_SHIFT;
1232	return ((u_int)m);
1233}
1234
1235static u_int
1236dx2d(dx)
1237	u_int64_t dx;
1238{
1239	u_int64_t d;
1240
1241	d = dx * 1000 / machclk_freq;
1242	return ((u_int)d);
1243}
1244
1245static void
1246sc2isc(sc, isc)
1247	struct service_curve	*sc;
1248	struct internal_sc	*isc;
1249{
1250	isc->sm1 = m2sm(sc->m1);
1251	isc->ism1 = m2ism(sc->m1);
1252	isc->dx = d2dx(sc->d);
1253	isc->dy = seg_x2y(isc->dx, isc->sm1);
1254	isc->sm2 = m2sm(sc->m2);
1255	isc->ism2 = m2ism(sc->m2);
1256}
1257
1258/*
1259 * initialize the runtime service curve with the given internal
1260 * service curve starting at (x, y).
1261 */
1262static void
1263rtsc_init(rtsc, isc, x, y)
1264	struct runtime_sc	*rtsc;
1265	struct internal_sc	*isc;
1266	u_int64_t		x, y;
1267{
1268	rtsc->x =	x;
1269	rtsc->y =	y;
1270	rtsc->sm1 =	isc->sm1;
1271	rtsc->ism1 =	isc->ism1;
1272	rtsc->dx =	isc->dx;
1273	rtsc->dy =	isc->dy;
1274	rtsc->sm2 =	isc->sm2;
1275	rtsc->ism2 =	isc->ism2;
1276}
1277
1278/*
1279 * calculate the y-projection of the runtime service curve by the
1280 * given x-projection value
1281 */
1282static u_int64_t
1283rtsc_y2x(rtsc, y)
1284	struct runtime_sc	*rtsc;
1285	u_int64_t		y;
1286{
1287	u_int64_t	x;
1288
1289	if (y < rtsc->y)
1290		x = rtsc->x;
1291	else if (y <= rtsc->y + rtsc->dy) {
1292		/* x belongs to the 1st segment */
1293		if (rtsc->dy == 0)
1294			x = rtsc->x + rtsc->dx;
1295		else
1296			x = rtsc->x + seg_y2x(y - rtsc->y, rtsc->ism1);
1297	} else {
1298		/* x belongs to the 2nd segment */
1299		x = rtsc->x + rtsc->dx
1300		    + seg_y2x(y - rtsc->y - rtsc->dy, rtsc->ism2);
1301	}
1302	return (x);
1303}
1304
1305static u_int64_t
1306rtsc_x2y(rtsc, x)
1307	struct runtime_sc	*rtsc;
1308	u_int64_t		x;
1309{
1310	u_int64_t	y;
1311
1312	if (x <= rtsc->x)
1313		y = rtsc->y;
1314	else if (x <= rtsc->x + rtsc->dx)
1315		/* y belongs to the 1st segment */
1316		y = rtsc->y + seg_x2y(x - rtsc->x, rtsc->sm1);
1317	else
1318		/* y belongs to the 2nd segment */
1319		y = rtsc->y + rtsc->dy
1320		    + seg_x2y(x - rtsc->x - rtsc->dx, rtsc->sm2);
1321	return (y);
1322}
1323
1324/*
1325 * update the runtime service curve by taking the minimum of the current
1326 * runtime service curve and the service curve starting at (x, y).
1327 */
1328static void
1329rtsc_min(rtsc, isc, x, y)
1330	struct runtime_sc	*rtsc;
1331	struct internal_sc	*isc;
1332	u_int64_t		x, y;
1333{
1334	u_int64_t	y1, y2, dx, dy;
1335
1336	if (isc->sm1 <= isc->sm2) {
1337		/* service curve is convex */
1338		y1 = rtsc_x2y(rtsc, x);
1339		if (y1 < y)
1340			/* the current rtsc is smaller */
1341			return;
1342		rtsc->x = x;
1343		rtsc->y = y;
1344		return;
1345	}
1346
1347	/*
1348	 * service curve is concave
1349	 * compute the two y values of the current rtsc
1350	 *	y1: at x
1351	 *	y2: at (x + dx)
1352	 */
1353	y1 = rtsc_x2y(rtsc, x);
1354	if (y1 <= y) {
1355		/* rtsc is below isc, no change to rtsc */
1356		return;
1357	}
1358
1359	y2 = rtsc_x2y(rtsc, x + isc->dx);
1360	if (y2 >= y + isc->dy) {
1361		/* rtsc is above isc, replace rtsc by isc */
1362		rtsc->x = x;
1363		rtsc->y = y;
1364		rtsc->dx = isc->dx;
1365		rtsc->dy = isc->dy;
1366		return;
1367	}
1368
1369	/*
1370	 * the two curves intersect
1371	 * compute the offsets (dx, dy) using the reverse
1372	 * function of seg_x2y()
1373	 *	seg_x2y(dx, sm1) == seg_x2y(dx, sm2) + (y1 - y)
1374	 */
1375	dx = ((y1 - y) << SM_SHIFT) / (isc->sm1 - isc->sm2);
1376	/*
1377	 * check if (x, y1) belongs to the 1st segment of rtsc.
1378	 * if so, add the offset.
1379	 */
1380	if (rtsc->x + rtsc->dx > x)
1381		dx += rtsc->x + rtsc->dx - x;
1382	dy = seg_x2y(dx, isc->sm1);
1383
1384	rtsc->x = x;
1385	rtsc->y = y;
1386	rtsc->dx = dx;
1387	rtsc->dy = dy;
1388	return;
1389}
1390
1391/*
1392 * hfsc device interface
1393 */
1394int
1395hfscopen(dev, flag, fmt, p)
1396	dev_t dev;
1397	int flag, fmt;
1398	struct proc *p;
1399{
1400	if (machclk_freq == 0)
1401		init_machclk();
1402
1403	if (machclk_freq == 0) {
1404		printf("hfsc: no cpu clock available!\n");
1405		return (ENXIO);
1406	}
1407
1408	/* everything will be done when the queueing scheme is attached. */
1409	return 0;
1410}
1411
1412int
1413hfscclose(dev, flag, fmt, p)
1414	dev_t dev;
1415	int flag, fmt;
1416	struct proc *p;
1417{
1418	struct hfsc_if *hif;
1419	int err, error = 0;
1420
1421	while ((hif = hif_list) != NULL) {
1422		/* destroy all */
1423		if (ALTQ_IS_ENABLED(hif->hif_ifq))
1424			altq_disable(hif->hif_ifq);
1425
1426		err = altq_detach(hif->hif_ifq);
1427		if (err == 0)
1428			err = hfsc_detach(hif);
1429		if (err != 0 && error == 0)
1430			error = err;
1431	}
1432
1433	return error;
1434}
1435
1436int
1437hfscioctl(dev, cmd, addr, flag, p)
1438	dev_t dev;
1439	ioctlcmd_t cmd;
1440	caddr_t addr;
1441	int flag;
1442	struct proc *p;
1443{
1444	struct hfsc_if *hif;
1445	struct hfsc_interface *ifacep;
1446	int	error = 0;
1447
1448	/* check super-user privilege */
1449	switch (cmd) {
1450	case HFSC_GETSTATS:
1451		break;
1452	default:
1453#if (__FreeBSD_version > 400000)
1454		if ((error = suser(p)) != 0)
1455			return (error);
1456#else
1457		if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
1458			return (error);
1459#endif
1460		break;
1461	}
1462
1463	switch (cmd) {
1464
1465	case HFSC_IF_ATTACH:
1466		error = hfsccmd_if_attach((struct hfsc_attach *)addr);
1467		break;
1468
1469	case HFSC_IF_DETACH:
1470		error = hfsccmd_if_detach((struct hfsc_interface *)addr);
1471		break;
1472
1473	case HFSC_ENABLE:
1474	case HFSC_DISABLE:
1475	case HFSC_CLEAR_HIERARCHY:
1476		ifacep = (struct hfsc_interface *)addr;
1477		if ((hif = altq_lookup(ifacep->hfsc_ifname,
1478				       ALTQT_HFSC)) == NULL) {
1479			error = EBADF;
1480			break;
1481		}
1482
1483		switch (cmd) {
1484
1485		case HFSC_ENABLE:
1486			if (hif->hif_defaultclass == NULL) {
1487#if 1
1488				printf("hfsc: no default class\n");
1489#endif
1490				error = EINVAL;
1491				break;
1492			}
1493			error = altq_enable(hif->hif_ifq);
1494			break;
1495
1496		case HFSC_DISABLE:
1497			error = altq_disable(hif->hif_ifq);
1498			break;
1499
1500		case HFSC_CLEAR_HIERARCHY:
1501			hfsc_clear_interface(hif);
1502			break;
1503		}
1504		break;
1505
1506	case HFSC_ADD_CLASS:
1507		error = hfsccmd_add_class((struct hfsc_add_class *)addr);
1508		break;
1509
1510	case HFSC_DEL_CLASS:
1511		error = hfsccmd_delete_class((struct hfsc_delete_class *)addr);
1512		break;
1513
1514	case HFSC_MOD_CLASS:
1515		error = hfsccmd_modify_class((struct hfsc_modify_class *)addr);
1516		break;
1517
1518	case HFSC_ADD_FILTER:
1519		error = hfsccmd_add_filter((struct hfsc_add_filter *)addr);
1520		break;
1521
1522	case HFSC_DEL_FILTER:
1523		error = hfsccmd_delete_filter((struct hfsc_delete_filter *)addr);
1524		break;
1525
1526	case HFSC_GETSTATS:
1527		error = hfsccmd_class_stats((struct hfsc_class_stats *)addr);
1528		break;
1529
1530	default:
1531		error = EINVAL;
1532		break;
1533	}
1534	return error;
1535}
1536
1537static int
1538hfsccmd_if_attach(ap)
1539	struct hfsc_attach *ap;
1540{
1541	struct hfsc_if *hif;
1542	struct ifnet *ifp;
1543	int error;
1544
1545	if ((ifp = ifunit(ap->iface.hfsc_ifname)) == NULL)
1546		return (ENXIO);
1547
1548	if ((hif = hfsc_attach(&ifp->if_snd, ap->bandwidth)) == NULL)
1549		return (ENOMEM);
1550
1551	/*
1552	 * set HFSC to this ifnet structure.
1553	 */
1554	if ((error = altq_attach(&ifp->if_snd, ALTQT_HFSC, hif,
1555				 hfsc_enqueue, hfsc_dequeue, hfsc_request,
1556				 &hif->hif_classifier, acc_classify)) != 0)
1557		(void)hfsc_detach(hif);
1558
1559	return (error);
1560}
1561
1562static int
1563hfsccmd_if_detach(ap)
1564	struct hfsc_interface *ap;
1565{
1566	struct hfsc_if *hif;
1567	int error;
1568
1569	if ((hif = altq_lookup(ap->hfsc_ifname, ALTQT_HFSC)) == NULL)
1570		return (EBADF);
1571
1572	if (ALTQ_IS_ENABLED(hif->hif_ifq))
1573		altq_disable(hif->hif_ifq);
1574
1575	if ((error = altq_detach(hif->hif_ifq)))
1576		return (error);
1577
1578	return hfsc_detach(hif);
1579}
1580
1581static int
1582hfsccmd_add_class(ap)
1583	struct hfsc_add_class *ap;
1584{
1585	struct hfsc_if *hif;
1586	struct hfsc_class *cl, *parent;
1587
1588	if ((hif = altq_lookup(ap->iface.hfsc_ifname, ALTQT_HFSC)) == NULL)
1589		return (EBADF);
1590
1591	if ((parent = clh_to_clp(hif, ap->parent_handle)) == NULL) {
1592		if (ap->parent_handle == HFSC_ROOTCLASS_HANDLE)
1593			parent = hif->hif_rootclass;
1594		else
1595			return (EINVAL);
1596	}
1597
1598	if ((cl = hfsc_class_create(hif, &ap->service_curve, parent,
1599				    ap->qlimit, ap->flags)) == NULL)
1600		return (ENOMEM);
1601
1602	/* return a class handle to the user */
1603	ap->class_handle = clp_to_clh(cl);
1604	return (0);
1605}
1606
1607static int
1608hfsccmd_delete_class(ap)
1609	struct hfsc_delete_class *ap;
1610{
1611	struct hfsc_if *hif;
1612	struct hfsc_class *cl;
1613
1614	if ((hif = altq_lookup(ap->iface.hfsc_ifname, ALTQT_HFSC)) == NULL)
1615		return (EBADF);
1616
1617	if ((cl = clh_to_clp(hif, ap->class_handle)) == NULL)
1618		return (EINVAL);
1619
1620	return hfsc_class_destroy(cl);
1621}
1622
1623static int
1624hfsccmd_modify_class(ap)
1625	struct hfsc_modify_class *ap;
1626{
1627	struct hfsc_if *hif;
1628	struct hfsc_class *cl;
1629	struct service_curve *rsc = NULL;
1630	struct service_curve *fsc = NULL;
1631
1632	if ((hif = altq_lookup(ap->iface.hfsc_ifname, ALTQT_HFSC)) == NULL)
1633		return (EBADF);
1634
1635	if ((cl = clh_to_clp(hif, ap->class_handle)) == NULL)
1636		return (EINVAL);
1637
1638	if (ap->sctype & HFSC_REALTIMESC)
1639		rsc = &ap->service_curve;
1640	if (ap->sctype & HFSC_LINKSHARINGSC)
1641		fsc = &ap->service_curve;
1642
1643	return hfsc_class_modify(cl, rsc, fsc);
1644}
1645
1646static int
1647hfsccmd_add_filter(ap)
1648	struct hfsc_add_filter *ap;
1649{
1650	struct hfsc_if *hif;
1651	struct hfsc_class *cl;
1652
1653	if ((hif = altq_lookup(ap->iface.hfsc_ifname, ALTQT_HFSC)) == NULL)
1654		return (EBADF);
1655
1656	if ((cl = clh_to_clp(hif, ap->class_handle)) == NULL)
1657		return (EINVAL);
1658
1659	if (is_a_parent_class(cl)) {
1660#if 1
1661		printf("hfsccmd_add_filter: not a leaf class!\n");
1662#endif
1663		return (EINVAL);
1664	}
1665
1666	return acc_add_filter(&hif->hif_classifier, &ap->filter,
1667			      cl, &ap->filter_handle);
1668}
1669
1670static int
1671hfsccmd_delete_filter(ap)
1672	struct hfsc_delete_filter *ap;
1673{
1674	struct hfsc_if *hif;
1675
1676	if ((hif = altq_lookup(ap->iface.hfsc_ifname, ALTQT_HFSC)) == NULL)
1677		return (EBADF);
1678
1679	return acc_delete_filter(&hif->hif_classifier,
1680				 ap->filter_handle);
1681}
1682
1683static int
1684hfsccmd_class_stats(ap)
1685	struct hfsc_class_stats *ap;
1686{
1687	struct hfsc_if *hif;
1688	struct hfsc_class *cl;
1689	struct class_stats stats, *usp;
1690	int	n, nclasses, error;
1691
1692	if ((hif = altq_lookup(ap->iface.hfsc_ifname, ALTQT_HFSC)) == NULL)
1693		return (EBADF);
1694
1695	ap->cur_time = read_machclk();
1696	ap->hif_classes = hif->hif_classes;
1697	ap->hif_packets = hif->hif_packets;
1698
1699	/* skip the first N classes in the tree */
1700	nclasses = ap->nskip;
1701	for (cl = hif->hif_rootclass, n = 0; cl != NULL && n < nclasses;
1702	     cl = hfsc_nextclass(cl), n++)
1703		;
1704	if (n != nclasses)
1705		return (EINVAL);
1706
1707	/* then, read the next N classes in the tree */
1708	nclasses = ap->nclasses;
1709	usp = ap->stats;
1710	for (n = 0; cl != NULL && n < nclasses; cl = hfsc_nextclass(cl), n++) {
1711
1712		get_class_stats(&stats, cl);
1713
1714		if ((error = copyout((caddr_t)&stats, (caddr_t)usp++,
1715				     sizeof(stats))) != 0)
1716			return (error);
1717	}
1718
1719	ap->nclasses = n;
1720
1721	return (0);
1722}
1723
1724static void get_class_stats(sp, cl)
1725	struct class_stats *sp;
1726	struct hfsc_class *cl;
1727{
1728	sp->class_id = cl->cl_id;
1729	sp->class_handle = clp_to_clh(cl);
1730
1731	if (cl->cl_rsc != NULL) {
1732		sp->rsc.m1 = sm2m(cl->cl_rsc->sm1);
1733		sp->rsc.d = dx2d(cl->cl_rsc->dx);
1734		sp->rsc.m2 = sm2m(cl->cl_rsc->sm2);
1735	} else {
1736		sp->rsc.m1 = 0;
1737		sp->rsc.d = 0;
1738		sp->rsc.m2 = 0;
1739	}
1740	if (cl->cl_fsc != NULL) {
1741		sp->fsc.m1 = sm2m(cl->cl_fsc->sm1);
1742		sp->fsc.d = dx2d(cl->cl_fsc->dx);
1743		sp->fsc.m2 = sm2m(cl->cl_fsc->sm2);
1744	} else {
1745		sp->fsc.m1 = 0;
1746		sp->fsc.d = 0;
1747		sp->fsc.m2 = 0;
1748	}
1749
1750	sp->total = cl->cl_total;
1751	sp->cumul = cl->cl_cumul;
1752
1753	sp->d = cl->cl_d;
1754	sp->e = cl->cl_e;
1755	sp->vt = cl->cl_vt;
1756
1757	sp->qlength = qlen(cl->cl_q);
1758	sp->xmit_cnt = cl->cl_stats.xmit_cnt;
1759	sp->drop_cnt = cl->cl_stats.drop_cnt;
1760	sp->period = cl->cl_stats.period;
1761
1762	sp->qtype = qtype(cl->cl_q);
1763#ifdef ALTQ_RED
1764	if (q_is_red(cl->cl_q))
1765		red_getstats(cl->cl_red, &sp->red[0]);
1766#endif
1767#ifdef ALTQ_RIO
1768	if (q_is_rio(cl->cl_q))
1769		rio_getstats((rio_t *)cl->cl_red, &sp->red[0]);
1770#endif
1771}
1772
1773/* convert a class handle to the corresponding class pointer */
1774static struct hfsc_class *
1775clh_to_clp(hif, chandle)
1776	struct hfsc_if *hif;
1777	u_long chandle;
1778{
1779	struct hfsc_class *cl;
1780
1781	cl = (struct hfsc_class *)chandle;
1782	if (chandle != ALIGN(cl)) {
1783#if 1
1784		printf("clh_to_cl: unaligned pointer %p\n", cl);
1785#endif
1786		return (NULL);
1787	}
1788
1789	if (cl == NULL || cl->cl_handle != chandle || cl->cl_hif != hif)
1790		return (NULL);
1791
1792	return (cl);
1793}
1794
1795/* convert a class pointer to the corresponding class handle */
1796static u_long
1797clp_to_clh(cl)
1798	struct hfsc_class *cl;
1799{
1800	if (cl->cl_parent == NULL)
1801		return (HFSC_ROOTCLASS_HANDLE);  /* XXX */
1802	return (cl->cl_handle);
1803}
1804
1805#ifdef KLD_MODULE
1806
1807static struct altqsw hfsc_sw =
1808	{"hfsc", hfscopen, hfscclose, hfscioctl};
1809
1810ALTQ_MODULE(altq_hfsc, ALTQT_HFSC, &hfsc_sw);
1811
1812#endif /* KLD_MODULE */
1813
1814#endif /* ALTQ_HFSC */
1815