diff options
author | Kenjiro Cho <kjc@cvs.openbsd.org> | 2002-12-16 09:18:07 +0000 |
---|---|---|
committer | Kenjiro Cho <kjc@cvs.openbsd.org> | 2002-12-16 09:18:07 +0000 |
commit | b832c86cc52bdbf120e78217d21df3af5977a61a (patch) | |
tree | ffa7692cbbba583d4becd2d93be35cbe22f09192 /sys/altq/altq_hfsc.c | |
parent | 322f5fb510ece19f248fa04cf2639fabfa5b74ea (diff) |
switchover to pf-based altq.
- remove files which are no longer used, or we don't have plans to support
in pf in the near future.
- remove altq ioctl related stuff.
- convert the PRIQ, HFSC and RIO modules to pf-based altq.
(these are not enabled in GENERIC, CDNR is not converted yet.)
Diffstat (limited to 'sys/altq/altq_hfsc.c')
-rw-r--r-- | sys/altq/altq_hfsc.c | 1092 |
1 files changed, 478 insertions, 614 deletions
diff --git a/sys/altq/altq_hfsc.c b/sys/altq/altq_hfsc.c index 5646337e007..f29989b476f 100644 --- a/sys/altq/altq_hfsc.c +++ b/sys/altq/altq_hfsc.c @@ -1,5 +1,5 @@ -/* $OpenBSD: altq_hfsc.c,v 1.7 2002/11/29 07:52:31 kjc Exp $ */ -/* $KAME: altq_hfsc.c,v 1.13 2002/05/16 11:02:58 kjc Exp $ */ +/* $OpenBSD: altq_hfsc.c,v 1.8 2002/12/16 09:18:05 kjc Exp $ */ +/* $KAME: altq_hfsc.c,v 1.17 2002/11/29 07:48:33 kjc Exp $ */ /* * Copyright (c) 1997-1999 Carnegie Mellon University. All Rights Reserved. @@ -38,41 +38,39 @@ * "A Hierarchical Fair Service Curve Algorithm for Link-Sharing, * Real-Time and Priority Service" * by Ion Stoica, Hui Zhang, and T. S. Eugene Ng. + * + * Oleg Cherevko <olwi@aq.ml.com.ua> added the upperlimit for link-sharing. + * when a class has an upperlimit, the fit-time is computed from the + * upperlimit service curve. the link-sharing scheduler does not schedule + * a class whose fit-time exceeds the current time. */ #include <sys/param.h> #include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/socket.h> -#include <sys/sockio.h> #include <sys/systm.h> -#include <sys/proc.h> #include <sys/errno.h> -#include <sys/kernel.h> #include <sys/queue.h> #include <net/if.h> -#include <net/if_types.h> +#include <netinet/in.h> +#include <net/pfvar.h> #include <altq/altq.h> -#include <altq/altq_conf.h> #include <altq/altq_hfsc.h> /* * function prototypes */ -static struct hfsc_if *hfsc_attach(struct ifaltq *, u_int); -static int hfsc_detach(struct hfsc_if *); static int hfsc_clear_interface(struct hfsc_if *); static int hfsc_request(struct ifaltq *, int, void *); static void hfsc_purge(struct hfsc_if *); static struct hfsc_class *hfsc_class_create(struct hfsc_if *, - struct service_curve *, struct hfsc_class *, int, int); + struct service_curve *, struct service_curve *, struct service_curve *, + struct hfsc_class *, int, int); static int hfsc_class_destroy(struct hfsc_class *); -static int hfsc_class_modify(struct hfsc_class *, - struct service_curve *, struct service_curve *); static struct hfsc_class *hfsc_nextclass(struct hfsc_class *); - static int hfsc_enqueue(struct ifaltq *, struct mbuf *, struct altq_pktattr *); static struct mbuf *hfsc_dequeue(struct ifaltq *, int); @@ -81,25 +79,27 @@ static struct mbuf *hfsc_getq(struct hfsc_class *); static struct mbuf *hfsc_pollq(struct hfsc_class *); static void hfsc_purgeq(struct hfsc_class *); +static void update_cfmin(struct hfsc_class *); static void set_active(struct hfsc_class *, int); static void set_passive(struct hfsc_class *); static void init_ed(struct hfsc_class *, int); static void update_ed(struct hfsc_class *, int); static void update_d(struct hfsc_class *, int); -static void init_v(struct hfsc_class *, int); -static void update_v(struct hfsc_class *, int); +static void init_vf(struct hfsc_class *, int); +static void update_vf(struct hfsc_class *, int, u_int64_t); static ellist_t *ellist_alloc(void); static void ellist_destroy(ellist_t *); static void ellist_insert(struct hfsc_class *); static void ellist_remove(struct hfsc_class *); static void ellist_update(struct hfsc_class *); -struct hfsc_class *ellist_get_mindl(ellist_t *); +struct hfsc_class *ellist_get_mindl(ellist_t *, u_int64_t); static actlist_t *actlist_alloc(void); static void actlist_destroy(actlist_t *); static void actlist_insert(struct hfsc_class *); static void actlist_remove(struct hfsc_class *); static void actlist_update(struct hfsc_class *); +static struct hfsc_class *actlist_firstfit(struct hfsc_class *, u_int64_t); static __inline u_int64_t seg_x2y(u_int64_t, u_int64_t); static __inline u_int64_t seg_y2x(u_int64_t, u_int64_t); @@ -117,39 +117,45 @@ static u_int64_t rtsc_x2y(struct runtime_sc *, u_int64_t); static void rtsc_min(struct runtime_sc *, struct internal_sc *, u_int64_t, u_int64_t); -int hfscopen(dev_t, int, int, struct proc *); -int hfscclose(dev_t, int, int, struct proc *); -int hfscioctl(dev_t, ioctlcmd_t, caddr_t, int, struct proc *); -static int hfsccmd_if_attach(struct hfsc_attach *); -static int hfsccmd_if_detach(struct hfsc_interface *); -static int hfsccmd_add_class(struct hfsc_add_class *); -static int hfsccmd_delete_class(struct hfsc_delete_class *); -static int hfsccmd_modify_class(struct hfsc_modify_class *); -static int hfsccmd_add_filter(struct hfsc_add_filter *); -static int hfsccmd_delete_filter(struct hfsc_delete_filter *); -static int hfsccmd_class_stats(struct hfsc_class_stats *); -static void get_class_stats(struct class_stats *, struct hfsc_class *); -static struct hfsc_class *clh_to_clp(struct hfsc_if *, u_long); -static u_long clp_to_clh(struct hfsc_class *); +static void get_class_stats(struct hfsc_classstats *, struct hfsc_class *); +static struct hfsc_class *clh_to_clp(struct hfsc_if *, u_int32_t); /* * macros */ #define is_a_parent_class(cl) ((cl)->cl_children != NULL) -/* hif_list keeps all hfsc_if's allocated. */ -static struct hfsc_if *hif_list = NULL; +#define HT_INFINITY 0xffffffffffffffffLL /* infinite time value */ -static struct hfsc_if * -hfsc_attach(ifq, bandwidth) - struct ifaltq *ifq; - u_int bandwidth; +int +hfsc_pfattach(struct pf_altq *a) +{ + struct ifnet *ifp; + int s, error; + + if ((ifp = ifunit(a->ifname)) == NULL || a->altq_disc == NULL) + return (EINVAL); + s = splimp(); + error = altq_attach(&ifp->if_snd, ALTQT_HFSC, a->altq_disc, + hfsc_enqueue, hfsc_dequeue, hfsc_request, NULL, NULL); + splx(s); + return (error); +} + +int +hfsc_add_altq(struct pf_altq *a) { struct hfsc_if *hif; + struct ifnet *ifp; struct service_curve root_sc; + if ((ifp = ifunit(a->ifname)) == NULL) + return (EINVAL); + if (!ALTQ_IS_READY(&ifp->if_snd)) + return (ENODEV); + MALLOC(hif, struct hfsc_if *, sizeof(struct hfsc_if), - M_DEVBUF, M_WAITOK); + M_DEVBUF, M_WAITOK); if (hif == NULL) return (NULL); bzero(hif, sizeof(struct hfsc_if)); @@ -160,47 +166,37 @@ hfsc_attach(ifq, bandwidth) return NULL; } - hif->hif_ifq = ifq; + hif->hif_ifq = &ifp->if_snd; /* * create root class */ - root_sc.m1 = bandwidth; + root_sc.m1 = a->ifbandwidth; root_sc.d = 0; - root_sc.m2 = bandwidth; - if ((hif->hif_rootclass = - hfsc_class_create(hif, &root_sc, NULL, 0, 0)) == NULL) { + root_sc.m2 = a->ifbandwidth; + if ((hif->hif_rootclass = hfsc_class_create(hif, + &root_sc, &root_sc, NULL, NULL, 0, 0)) == NULL) { FREE(hif, M_DEVBUF); - return (NULL); + return (ENOMEM); } - /* add this state to the hfsc list */ - hif->hif_next = hif_list; - hif_list = hif; + /* keep the state in pf_altq */ + a->altq_disc = hif; - return (hif); + return (0); } -static int -hfsc_detach(hif) - struct hfsc_if *hif; +int +hfsc_remove_altq(struct pf_altq *a) { - (void)hfsc_clear_interface(hif); - (void)hfsc_class_destroy(hif->hif_rootclass); + struct hfsc_if *hif; - /* remove this interface from the hif list */ - if (hif_list == hif) - hif_list = hif->hif_next; - else { - struct hfsc_if *h; + if ((hif = a->altq_disc) == NULL) + return (EINVAL); + a->altq_disc = NULL; - for (h = hif_list; h != NULL; h = h->hif_next) - if (h->hif_next == hif) { - h->hif_next = hif->hif_next; - break; - } - ASSERT(h != NULL); - } + (void)hfsc_clear_interface(hif); + (void)hfsc_class_destroy(hif->hif_rootclass); ellist_destroy(hif->hif_eligible); @@ -209,6 +205,83 @@ hfsc_detach(hif) return (0); } +int +hfsc_add_queue(struct pf_altq *a) +{ + struct hfsc_if *hif; + struct hfsc_class *cl, *parent; + struct hfsc_opts *opts; + struct service_curve rtsc, lssc, ulsc; + + if ((hif = a->altq_disc) == NULL) + return (EINVAL); + + opts = &a->pq_u.hfsc_opts; + + parent = clh_to_clp(hif, a->parent_qid); + if (parent == NULL) + return (EINVAL); + rtsc.m1 = opts->rtsc_m1; + rtsc.d = opts->rtsc_d; + rtsc.m2 = opts->rtsc_m2; + lssc.m1 = opts->lssc_m1; + lssc.d = opts->lssc_d; + lssc.m2 = opts->lssc_m2; + ulsc.m1 = opts->ulsc_m1; + ulsc.d = opts->ulsc_d; + ulsc.m2 = opts->ulsc_m2; + + cl = hfsc_class_create(hif, &rtsc, &lssc, &ulsc, + parent, a->qlimit, opts->flags); + if (cl == NULL) + return (ENOMEM); + + /* return handle to user space. */ + a->qid = cl->cl_handle; + + return (0); +} + +int +hfsc_remove_queue(struct pf_altq *a) +{ + struct hfsc_if *hif; + struct hfsc_class *cl; + + if ((hif = a->altq_disc) == NULL) + return (EINVAL); + + if ((cl = clh_to_clp(hif, a->qid)) == NULL) + return (EINVAL); + + return (hfsc_class_destroy(cl)); +} + +int +hfsc_getqstats(struct pf_altq *a, void *ubuf, int *nbytes) +{ + struct hfsc_if *hif; + struct hfsc_class *cl; + struct hfsc_classstats stats; + int error = 0; + + if ((hif = altq_lookup(a->ifname, ALTQT_HFSC)) == NULL) + return (EBADF); + + if ((cl = clh_to_clp(hif, a->qid)) == NULL) + return (EINVAL); + + if (*nbytes < sizeof(stats)) + return (EINVAL); + + get_class_stats(&stats, cl); + + if ((error = copyout((caddr_t)&stats, ubuf, sizeof(stats))) != 0) + return (error); + *nbytes = sizeof(stats); + return (0); +} + /* * bring the interface back to the initial state by discarding * all the filters and classes except the root class. @@ -219,9 +292,6 @@ hfsc_clear_interface(hif) { struct hfsc_class *cl; - /* free the filters for this interface */ - acc_discard_filters(&hif->hif_classifier, NULL, 1); - /* clear out the classes */ while ((cl = hif->hif_rootclass->cl_children) != NULL) { /* @@ -270,14 +340,14 @@ hfsc_purge(hif) } struct hfsc_class * -hfsc_class_create(hif, sc, parent, qlimit, flags) +hfsc_class_create(hif, rsc, fsc, usc, parent, qlimit, flags) struct hfsc_if *hif; - struct service_curve *sc; + struct service_curve *rsc, *fsc, *usc; struct hfsc_class *parent; int qlimit, flags; { struct hfsc_class *cl, *p; - int s; + int i, s, chandle; #ifndef ALTQ_RED if (flags & HFCF_RED) { @@ -288,6 +358,14 @@ hfsc_class_create(hif, sc, parent, qlimit, flags) } #endif + /* find a free class slot. */ + for (i = 0; i < HFSC_MAX_CLASSES; i++) + if (hif->hif_class_tbl[i] == NULL) + break; + if (i == HFSC_MAX_CLASSES) + return (NULL); + chandle = i + 1; + MALLOC(cl, struct hfsc_class *, sizeof(struct hfsc_class), M_DEVBUF, M_WAITOK); if (cl == NULL) @@ -313,6 +391,15 @@ hfsc_class_create(hif, sc, parent, qlimit, flags) #ifdef ALTQ_RED if (flags & (HFCF_RED|HFCF_RIO)) { int red_flags, red_pkttime; + u_int m2; + + m2 = 0; + if (rsc != NULL && rsc->m2 > m2) + m2 = rsc->m2; + if (fsc != NULL && fsc->m2 > m2) + m2 = fsc->m2; + if (usc != NULL && usc->m2 > m2) + m2 = usc->m2; red_flags = 0; if (flags & HFCF_ECN) @@ -321,21 +408,21 @@ hfsc_class_create(hif, sc, parent, qlimit, flags) if (flags & HFCF_CLEARDSCP) red_flags |= RIOF_CLEARDSCP; #endif - if (sc->m2 < 8) + if (m2 < 8) red_pkttime = 1000 * 1000 * 1000; /* 1 sec */ else red_pkttime = (int64_t)hif->hif_ifq->altq_ifp->if_mtu - * 1000 * 1000 * 1000 / (sc->m2 / 8); + * 1000 * 1000 * 1000 / (m2 / 8); if (flags & HFCF_RED) { cl->cl_red = red_alloc(0, 0, 0, 0, - red_flags, red_pkttime); + red_flags, red_pkttime); if (cl->cl_red != NULL) qtype(cl->cl_q) = Q_RED; } #ifdef ALTQ_RIO else { cl->cl_red = (red_t *)rio_alloc(0, NULL, - red_flags, red_pkttime); + red_flags, red_pkttime); if (cl->cl_red != NULL) qtype(cl->cl_q) = Q_RIO; } @@ -343,32 +430,40 @@ hfsc_class_create(hif, sc, parent, qlimit, flags) } #endif /* ALTQ_RED */ - if (sc != NULL && (sc->m1 != 0 || sc->m2 != 0)) { + if (rsc != NULL && (rsc->m1 != 0 || rsc->m2 != 0)) { MALLOC(cl->cl_rsc, struct internal_sc *, - sizeof(struct internal_sc), M_DEVBUF, M_WAITOK); + sizeof(struct internal_sc), M_DEVBUF, M_WAITOK); if (cl->cl_rsc == NULL) goto err_ret; - bzero(cl->cl_rsc, sizeof(struct internal_sc)); - sc2isc(sc, cl->cl_rsc); + sc2isc(rsc, cl->cl_rsc); rtsc_init(&cl->cl_deadline, cl->cl_rsc, 0, 0); rtsc_init(&cl->cl_eligible, cl->cl_rsc, 0, 0); - + } + if (fsc != NULL && (fsc->m1 != 0 || fsc->m2 != 0)) { MALLOC(cl->cl_fsc, struct internal_sc *, - sizeof(struct internal_sc), M_DEVBUF, M_WAITOK); + sizeof(struct internal_sc), M_DEVBUF, M_WAITOK); if (cl->cl_fsc == NULL) goto err_ret; - bzero(cl->cl_fsc, sizeof(struct internal_sc)); - sc2isc(sc, cl->cl_fsc); + sc2isc(fsc, cl->cl_fsc); rtsc_init(&cl->cl_virtual, cl->cl_fsc, 0, 0); } + if (usc != NULL && (usc->m1 != 0 || usc->m2 != 0)) { + MALLOC(cl->cl_usc, struct internal_sc *, + sizeof(struct internal_sc), M_DEVBUF, M_WAITOK); + if (cl->cl_usc == NULL) + goto err_ret; + sc2isc(usc, cl->cl_usc); + rtsc_init(&cl->cl_ulimit, cl->cl_usc, 0, 0); + } cl->cl_id = hif->hif_classid++; - cl->cl_handle = (u_long)cl; /* XXX: just a pointer to this class */ + cl->cl_handle = chandle; cl->cl_hif = hif; cl->cl_parent = parent; s = splimp(); hif->hif_classes++; + hif->hif_class_tbl[chandle - 1] = cl; if (flags & HFCF_DEFAULTCLASS) hif->hif_defaultclass = cl; @@ -404,6 +499,8 @@ hfsc_class_create(hif, sc, parent, qlimit, flags) FREE(cl->cl_fsc, M_DEVBUF); if (cl->cl_rsc != NULL) FREE(cl->cl_rsc, M_DEVBUF); + if (cl->cl_usc != NULL) + FREE(cl->cl_usc, M_DEVBUF); if (cl->cl_q != NULL) FREE(cl->cl_q, M_DEVBUF); FREE(cl, M_DEVBUF); @@ -421,9 +518,6 @@ hfsc_class_destroy(cl) s = splimp(); - /* delete filters referencing to this class */ - acc_discard_filters(&cl->cl_hif->hif_classifier, cl, 0); - if (!qempty(cl->cl_q)) hfsc_purgeq(cl); @@ -442,6 +536,7 @@ hfsc_class_destroy(cl) } while ((p = p->cl_siblings) != NULL); ASSERT(p != NULL); } + cl->cl_hif->hif_class_tbl[cl->cl_handle - 1] = NULL; cl->cl_hif->hif_classes--; splx(s); @@ -457,6 +552,8 @@ hfsc_class_destroy(cl) red_destroy(cl->cl_red); #endif } + if (cl->cl_usc != NULL) + FREE(cl->cl_usc, M_DEVBUF); if (cl->cl_fsc != NULL) FREE(cl->cl_fsc, M_DEVBUF); if (cl->cl_rsc != NULL) @@ -467,68 +564,6 @@ hfsc_class_destroy(cl) return (0); } -static int -hfsc_class_modify(cl, rsc, fsc) - struct hfsc_class *cl; - struct service_curve *rsc, *fsc; -{ - struct internal_sc *rsc_tmp, *fsc_tmp; - int s; - - if (rsc != NULL && (rsc->m1 != 0 || rsc->m2 != 0) && - cl->cl_rsc == NULL) { - MALLOC(rsc_tmp, struct internal_sc *, - sizeof(struct internal_sc), M_DEVBUF, M_WAITOK); - if (rsc_tmp == NULL) - return (ENOMEM); - } - if (fsc != NULL && (fsc->m1 != 0 || fsc->m2 != 0) && - cl->cl_fsc == NULL) { - MALLOC(fsc_tmp, struct internal_sc *, - sizeof(struct internal_sc), M_DEVBUF, M_WAITOK); - if (fsc_tmp == NULL) - return (ENOMEM); - } - - s = splimp(); - if (!qempty(cl->cl_q)) - hfsc_purgeq(cl); - - if (rsc != NULL) { - if (rsc->m1 == 0 && rsc->m2 == 0) { - if (cl->cl_rsc != NULL) { - FREE(cl->cl_rsc, M_DEVBUF); - cl->cl_rsc = NULL; - } - } else { - if (cl->cl_rsc == NULL) - cl->cl_rsc = rsc_tmp; - bzero(cl->cl_rsc, sizeof(struct internal_sc)); - sc2isc(rsc, cl->cl_rsc); - rtsc_init(&cl->cl_deadline, cl->cl_rsc, 0, 0); - rtsc_init(&cl->cl_eligible, cl->cl_rsc, 0, 0); - } - } - - if (fsc != NULL) { - if (fsc->m1 == 0 && fsc->m2 == 0) { - if (cl->cl_fsc != NULL) { - FREE(cl->cl_fsc, M_DEVBUF); - cl->cl_fsc = NULL; - } - } else { - if (cl->cl_fsc == NULL) - cl->cl_fsc = fsc_tmp; - bzero(cl->cl_fsc, sizeof(struct internal_sc)); - sc2isc(fsc, cl->cl_fsc); - rtsc_init(&cl->cl_virtual, cl->cl_fsc, 0, 0); - } - } - splx(s); - - return (0); -} - /* * hfsc_nextclass returns the next class in the tree. * usage: @@ -566,12 +601,21 @@ hfsc_enqueue(ifq, m, pktattr) { struct hfsc_if *hif = (struct hfsc_if *)ifq->altq_disc; struct hfsc_class *cl; + struct m_tag *t; int len; /* grab class set by classifier */ - if (pktattr == NULL || (cl = pktattr->pattr_class) == NULL) + t = m_tag_find(m, PACKET_TAG_PF_QID, NULL); + if (t == NULL || + (cl = clh_to_clp(hif, ((struct altq_tag *)(t+1))->qid)) == NULL || + is_a_parent_class(cl)) { cl = hif->hif_defaultclass; - cl->cl_pktattr = pktattr; /* save proto hdr used by ECN */ + if (cl == NULL) { + m_freem(m); + return (ENOBUFS); + } + cl->cl_pktattr = NULL; + } len = m_pktlen(m); if (hfsc_addq(cl, m) != 0) { @@ -586,9 +630,6 @@ hfsc_enqueue(ifq, m, pktattr) if (qlen(cl->cl_q) == 1) set_active(cl, m_pktlen(m)); -#ifdef HFSC_PKTLOG - /* put the logging_hook here */ -#endif return (0); } @@ -611,39 +652,58 @@ hfsc_dequeue(ifq, op) struct mbuf *m; int len, next_len; int realtime = 0; + u_int64_t cur_time; if (hif->hif_packets == 0) /* no packet in the tree */ return (NULL); + cur_time = read_machclk(); + if (op == ALTDQ_REMOVE && hif->hif_pollcache != NULL) { - u_int64_t cur_time; cl = hif->hif_pollcache; hif->hif_pollcache = NULL; /* check if the class was scheduled by real-time criteria */ - if (cl->cl_rsc != NULL) { - cur_time = read_machclk(); + if (cl->cl_rsc != NULL) realtime = (cl->cl_e <= cur_time); - } } else { /* * if there are eligible classes, use real-time criteria. * find the class with the minimum deadline among * the eligible classes. */ - if ((cl = ellist_get_mindl(hif->hif_eligible)) != NULL) { + if ((cl = ellist_get_mindl(hif->hif_eligible, cur_time)) + != NULL) { realtime = 1; } else { +#ifdef ALTQ_DEBUG + int fits = 0; +#endif /* * use link-sharing criteria * get the class with the minimum vt in the hierarchy */ cl = hif->hif_rootclass; while (is_a_parent_class(cl)) { - cl = actlist_first(cl->cl_actc); - if (cl == NULL) + + cl = actlist_firstfit(cl, cur_time); + if (cl == NULL) { +#ifdef ALTQ_DEBUG + if (fits > 0) + printf("%d fit but none found\n",fits); +#endif return (NULL); + } + /* + * update parent's cl_cvtmin. + * don't update if the new vt is smaller. + */ + if (cl->cl_parent->cl_cvtmin < cl->cl_vt) + cl->cl_parent->cl_cvtmin = cl->cl_vt; +#ifdef ALTQ_DEBUG + fits++; +#endif } } @@ -655,12 +715,14 @@ hfsc_dequeue(ifq, op) } m = hfsc_getq(cl); + if (m == NULL) + panic("hfsc_dequeue:"); len = m_pktlen(m); cl->cl_hif->hif_packets--; IFQ_DEC_LEN(ifq); PKTCNTR_ADD(&cl->cl_stats.xmit_cnt, len); - update_v(cl, len); + update_vf(cl, len, cur_time); if (realtime) cl->cl_cumul += len; @@ -679,10 +741,6 @@ hfsc_dequeue(ifq, op) set_passive(cl); } -#ifdef HFSC_PKTLOG - /* put the logging_hook here */ -#endif - return (m); } @@ -748,9 +806,12 @@ hfsc_purgeq(cl) while ((m = _getq(cl->cl_q)) != NULL) { PKTCNTR_ADD(&cl->cl_stats.drop_cnt, m_pktlen(m)); m_freem(m); + cl->cl_hif->hif_packets--; + IFQ_DEC_LEN(cl->cl_hif->hif_ifq); } ASSERT(qlen(cl->cl_q) == 0); + update_vf(cl, 0, 0); /* remove cl from the actlist */ set_passive(cl); } @@ -762,7 +823,7 @@ set_active(cl, len) if (cl->cl_rsc != NULL) init_ed(cl, len); if (cl->cl_fsc != NULL) - init_v(cl, len); + init_vf(cl, len); cl->cl_stats.period++; } @@ -774,19 +835,10 @@ set_passive(cl) if (cl->cl_rsc != NULL) ellist_remove(cl); - if (cl->cl_fsc != NULL) { - while (cl->cl_parent != NULL) { - if (--cl->cl_nactive == 0) { - /* remove this class from the vt list */ - actlist_remove(cl); - } else - /* still has active children */ - break; - - /* go up to the parent class */ - cl = cl->cl_parent; - } - } + /* + * actlist is now handled in update_vf() so that update_vf(cl, 0, 0) + * needs to be called explicitly to remove a class from actlist + */ } static void @@ -839,78 +891,207 @@ update_d(cl, next_len) } static void -init_v(cl, len) +init_vf(cl, len) struct hfsc_class *cl; int len; { - struct hfsc_class *min_cl, *max_cl; + struct hfsc_class *max_cl, *p; + u_int64_t vt, f, cur_time; + int go_active; - while (cl->cl_parent != NULL) { + cur_time = 0; + go_active = 1; + for ( ; cl->cl_parent != NULL; cl = cl->cl_parent) { - if (cl->cl_nactive++ > 0) - /* already active */ - break; - - /* - * if parent became idle while this class was idle. - * reset vt and the runtime service curve. - */ - if (cl->cl_parent->cl_nactive == 0 || - cl->cl_parent->cl_vtperiod != cl->cl_parentperiod) { - cl->cl_vt = 0; - rtsc_init(&cl->cl_virtual, cl->cl_fsc, - 0, cl->cl_total); - } - min_cl = actlist_first(cl->cl_parent->cl_actc); - if (min_cl != NULL) { - u_int64_t vt; + if (go_active && cl->cl_nactive++ == 0) + go_active = 1; + else + go_active = 0; - /* - * set vt to the average of the min and max classes. - * if the parent's period didn't change, - * don't decrease vt of the class. - */ + if (go_active) { max_cl = actlist_last(cl->cl_parent->cl_actc); - vt = (min_cl->cl_vt + max_cl->cl_vt) / 2; - if (cl->cl_parent->cl_vtperiod != cl->cl_parentperiod - || vt > cl->cl_vt) - cl->cl_vt = vt; + if (max_cl != NULL) { + /* + * set vt to the average of the min and max + * classes. if the parent's period didn't + * change, don't decrease vt of the class. + */ + vt = max_cl->cl_vt; + if (cl->cl_parent->cl_cvtmin != 0) + vt = (cl->cl_parent->cl_cvtmin + vt)/2; + + if (cl->cl_parent->cl_vtperiod != + cl->cl_parentperiod || vt > cl->cl_vt) + cl->cl_vt = vt; + } else { + /* + * first child for a new parent backlog period. + * add parent's cvtmax to vtoff of children + * to make a new vt (vtoff + vt) larger than + * the vt in the last period for all children. + */ + vt = cl->cl_parent->cl_cvtmax; + for (p = cl->cl_parent->cl_children; p != NULL; + p = p->cl_siblings) + p->cl_vtoff += vt; + cl->cl_vt = 0; + cl->cl_parent->cl_cvtmax = 0; + cl->cl_parent->cl_cvtmin = 0; + } + cl->cl_initvt = cl->cl_vt; + + /* update the virtual curve */ + vt = cl->cl_vt + cl->cl_vtoff; + rtsc_min(&cl->cl_virtual, cl->cl_fsc, vt, cl->cl_total); + if (cl->cl_virtual.x == vt) { + cl->cl_virtual.x -= cl->cl_vtoff; + cl->cl_vtoff = 0; + } + cl->cl_vtadj = 0; + + cl->cl_vtperiod++; /* increment vt period */ + cl->cl_parentperiod = cl->cl_parent->cl_vtperiod; + if (cl->cl_parent->cl_nactive == 0) + cl->cl_parentperiod++; + cl->cl_f = 0; + + actlist_insert(cl); + + if (cl->cl_usc != NULL) { + /* class has upper limit curve */ + if (cur_time == 0) + cur_time = read_machclk(); + + /* update the ulimit curve */ + rtsc_min(&cl->cl_ulimit, cl->cl_usc, cur_time, + cl->cl_total); + /* compute myf */ + cl->cl_myf = rtsc_y2x(&cl->cl_ulimit, + cl->cl_total); + cl->cl_myfadj = 0; + } } - /* update the virtual curve */ - rtsc_min(&cl->cl_virtual, cl->cl_fsc, cl->cl_vt, cl->cl_total); - - cl->cl_vtperiod++; /* increment vt period */ - cl->cl_parentperiod = cl->cl_parent->cl_vtperiod; - if (cl->cl_parent->cl_nactive == 0) - cl->cl_parentperiod++; - - actlist_insert(cl); - - /* go up to the parent class */ - cl = cl->cl_parent; + if (cl->cl_myf > cl->cl_cfmin) + f = cl->cl_myf; + else + f = cl->cl_cfmin; + if (f != cl->cl_f) { + cl->cl_f = f; + update_cfmin(cl->cl_parent); + } } } static void -update_v(cl, len) +update_vf(cl, len, cur_time) struct hfsc_class *cl; int len; + u_int64_t cur_time; { - while (cl->cl_parent != NULL) { + u_int64_t f, myf_bound, delta; + int go_passive; + + go_passive = qempty(cl->cl_q); + + for (; cl->cl_parent != NULL; cl = cl->cl_parent) { cl->cl_total += len; - if (cl->cl_fsc != NULL) { - cl->cl_vt = rtsc_y2x(&cl->cl_virtual, cl->cl_total); + if (cl->cl_fsc == NULL || cl->cl_nactive == 0) + continue; + + if (go_passive && --cl->cl_nactive == 0) + go_passive = 1; + else + go_passive = 0; + + if (go_passive) { + /* no more active child, going passive */ + + /* update cvtmax of the parent class */ + if (cl->cl_vt > cl->cl_parent->cl_cvtmax) + cl->cl_parent->cl_cvtmax = cl->cl_vt; + + /* remove this class from the vt list */ + actlist_remove(cl); + + update_cfmin(cl->cl_parent); - /* update the vt list */ - actlist_update(cl); + continue; } - /* go up to the parent class */ - cl = cl->cl_parent; + /* + * update vt and f + */ + cl->cl_vt = rtsc_y2x(&cl->cl_virtual, cl->cl_total) + - cl->cl_vtoff + cl->cl_vtadj; + + /* + * if vt of the class is smaller than cvtmin, + * the class was skipped in the past due to non-fit. + * if so, we need to adjust vtadj. + */ + if (cl->cl_vt < cl->cl_parent->cl_cvtmin) { + cl->cl_vtadj += cl->cl_parent->cl_cvtmin - cl->cl_vt; + cl->cl_vt = cl->cl_parent->cl_cvtmin; + } + + /* update the vt list */ + actlist_update(cl); + + if (cl->cl_usc != NULL) { + cl->cl_myf = cl->cl_myfadj + + rtsc_y2x(&cl->cl_ulimit, cl->cl_total); + + /* + * if myf lags behind by more than one clock tick + * from the current time, adjust myfadj to prevent + * a rate-limited class from going greedy. + * in a steady state under rate-limiting, myf + * fluctuates within one clock tick. + */ + myf_bound = cur_time - machclk_per_tick; + if (cl->cl_myf < myf_bound) { + delta = cur_time - cl->cl_myf; + cl->cl_myfadj += delta; + cl->cl_myf += delta; + } + } + + /* cl_f is max(cl_myf, cl_cfmin) */ + if (cl->cl_myf > cl->cl_cfmin) + f = cl->cl_myf; + else + f = cl->cl_cfmin; + if (f != cl->cl_f) { + cl->cl_f = f; + update_cfmin(cl->cl_parent); + } + } +} + +static void +update_cfmin(cl) + struct hfsc_class *cl; +{ + struct hfsc_class *p; + u_int64_t cfmin; + + if (TAILQ_EMPTY(cl->cl_actc)) { + cl->cl_cfmin = 0; + return; + } + cfmin = HT_INFINITY; + TAILQ_FOREACH(p, cl->cl_actc, cl_actlist) { + if (p->cl_f == 0) { + cl->cl_cfmin = 0; + return; + } + if (p->cl_f < cfmin) + cfmin = p->cl_f; } + cl->cl_cfmin = cfmin; } /* @@ -1011,13 +1192,11 @@ ellist_update(cl) /* find the class with the minimum deadline among the eligible classes */ struct hfsc_class * -ellist_get_mindl(head) +ellist_get_mindl(head, cur_time) ellist_t *head; + u_int64_t cur_time; { struct hfsc_class *p, *cl = NULL; - u_int64_t cur_time; - - cur_time = read_machclk(); TAILQ_FOREACH(p, head, cl_ellist) { if (p->cl_e > cur_time) @@ -1090,7 +1269,7 @@ actlist_update(cl) * if the next entry has a larger virtual time, nothing to do. */ p = TAILQ_NEXT(cl, cl_actlist); - if (p == NULL || cl->cl_vt <= p->cl_vt) + if (p == NULL || cl->cl_vt < p->cl_vt) return; /* check the last entry */ @@ -1116,6 +1295,20 @@ actlist_update(cl) ASSERT(0); /* should not reach here */ } +static struct hfsc_class * +actlist_firstfit(cl, cur_time) + struct hfsc_class *cl; + u_int64_t cur_time; +{ + struct hfsc_class *p; + + TAILQ_FOREACH(p, cl->cl_actc, cl_actlist) { + if (p->cl_f <= cur_time) + return (p); + } + return (NULL); +} + /* * service curve support functions * @@ -1132,7 +1325,7 @@ actlist_update(cl) * speed. SM_SHIFT and ISM_SHIFT are selected to have at least 3 effective * digits in decimal using the following table. * - * bits/set 100Kbps 1Mbps 10Mbps 100Mbps 1Gbps + * bits/sec 100Kbps 1Mbps 10Mbps 100Mbps 1Gbps * ----------+------------------------------------------------------- * bytes/nsec 12.5e-6 125e-6 1250e-6 12500e-6 125000e-6 * sm(500MHz) 25.0e-6 250e-6 2500e-6 25000e-6 250000e-6 @@ -1145,8 +1338,8 @@ actlist_update(cl) #define SM_SHIFT 24 #define ISM_SHIFT 10 -#define SC_LARGEVAL (1LL << 32) -#define SC_INFINITY 0xffffffffffffffffLL +#define SM_MASK ((1LL << SM_SHIFT) - 1) +#define ISM_MASK ((1LL << ISM_SHIFT) - 1) static __inline u_int64_t seg_x2y(x, sm) @@ -1155,10 +1348,12 @@ seg_x2y(x, sm) { u_int64_t y; - if (x < SC_LARGEVAL) - y = x * sm >> SM_SHIFT; - else - y = (x >> SM_SHIFT) * sm; + /* + * compute + * y = x * sm >> SM_SHIFT + * but divide it for the upper and lower bits to avoid overflow + */ + y = (x >> SM_SHIFT) * sm + (((x & SM_MASK) * sm) >> SM_SHIFT); return (y); } @@ -1171,12 +1366,12 @@ seg_y2x(y, ism) if (y == 0) x = 0; - else if (ism == SC_INFINITY) - x = SC_INFINITY; - else if (y < SC_LARGEVAL) - x = y * ism >> ISM_SHIFT; - else - x = (y >> ISM_SHIFT) * ism; + else if (ism == HT_INFINITY) + x = HT_INFINITY; + else { + x = (y >> ISM_SHIFT) * ism + + (((y & ISM_MASK) * ism) >> ISM_SHIFT); + } return (x); } @@ -1197,7 +1392,7 @@ m2ism(m) u_int64_t ism; if (m == 0) - ism = SC_INFINITY; + ism = HT_INFINITY; else ism = ((u_int64_t)machclk_freq << ISM_SHIFT) * 8 / m; return (ism); @@ -1379,345 +1574,13 @@ rtsc_min(rtsc, isc, x, y) return; } -/* - * hfsc device interface - */ -int -hfscopen(dev, flag, fmt, p) - dev_t dev; - int flag, fmt; - struct proc *p; -{ - if (machclk_freq == 0) - init_machclk(); - - if (machclk_freq == 0) { - printf("hfsc: no cpu clock available!\n"); - return (ENXIO); - } - - /* everything will be done when the queueing scheme is attached. */ - return 0; -} - -int -hfscclose(dev, flag, fmt, p) - dev_t dev; - int flag, fmt; - struct proc *p; -{ - struct hfsc_if *hif; - int err, error = 0; - - while ((hif = hif_list) != NULL) { - /* destroy all */ - if (ALTQ_IS_ENABLED(hif->hif_ifq)) - altq_disable(hif->hif_ifq); - - err = altq_detach(hif->hif_ifq); - if (err == 0) - err = hfsc_detach(hif); - if (err != 0 && error == 0) - error = err; - } - - return error; -} - -int -hfscioctl(dev, cmd, addr, flag, p) - dev_t dev; - ioctlcmd_t cmd; - caddr_t addr; - int flag; - struct proc *p; -{ - struct hfsc_if *hif; - struct hfsc_interface *ifacep; - int error = 0; - - /* check super-user privilege */ - switch (cmd) { - case HFSC_GETSTATS: - break; - default: -#if (__FreeBSD_version > 400000) - if ((error = suser(p)) != 0) - return (error); -#else - if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) - return (error); -#endif - break; - } - - switch (cmd) { - - case HFSC_IF_ATTACH: - error = hfsccmd_if_attach((struct hfsc_attach *)addr); - break; - - case HFSC_IF_DETACH: - error = hfsccmd_if_detach((struct hfsc_interface *)addr); - break; - - case HFSC_ENABLE: - case HFSC_DISABLE: - case HFSC_CLEAR_HIERARCHY: - ifacep = (struct hfsc_interface *)addr; - if ((hif = altq_lookup(ifacep->hfsc_ifname, - ALTQT_HFSC)) == NULL) { - error = EBADF; - break; - } - - switch (cmd) { - - case HFSC_ENABLE: - if (hif->hif_defaultclass == NULL) { -#ifdef ALTQ_DEBUG - printf("hfsc: no default class\n"); -#endif - error = EINVAL; - break; - } - error = altq_enable(hif->hif_ifq); - break; - - case HFSC_DISABLE: - error = altq_disable(hif->hif_ifq); - break; - - case HFSC_CLEAR_HIERARCHY: - hfsc_clear_interface(hif); - break; - } - break; - - case HFSC_ADD_CLASS: - error = hfsccmd_add_class((struct hfsc_add_class *)addr); - break; - - case HFSC_DEL_CLASS: - error = hfsccmd_delete_class((struct hfsc_delete_class *)addr); - break; - - case HFSC_MOD_CLASS: - error = hfsccmd_modify_class((struct hfsc_modify_class *)addr); - break; - - case HFSC_ADD_FILTER: - error = hfsccmd_add_filter((struct hfsc_add_filter *)addr); - break; - - case HFSC_DEL_FILTER: - error = hfsccmd_delete_filter((struct hfsc_delete_filter *)addr); - break; - - case HFSC_GETSTATS: - error = hfsccmd_class_stats((struct hfsc_class_stats *)addr); - break; - - default: - error = EINVAL; - break; - } - return error; -} - -static int -hfsccmd_if_attach(ap) - struct hfsc_attach *ap; -{ - struct hfsc_if *hif; - struct ifnet *ifp; - int error; - - if ((ifp = ifunit(ap->iface.hfsc_ifname)) == NULL) - return (ENXIO); - - if ((hif = hfsc_attach(&ifp->if_snd, ap->bandwidth)) == NULL) - return (ENOMEM); - - /* - * set HFSC to this ifnet structure. - */ - if ((error = altq_attach(&ifp->if_snd, ALTQT_HFSC, hif, - hfsc_enqueue, hfsc_dequeue, hfsc_request, - &hif->hif_classifier, acc_classify)) != 0) - (void)hfsc_detach(hif); - - return (error); -} - -static int -hfsccmd_if_detach(ap) - struct hfsc_interface *ap; -{ - struct hfsc_if *hif; - int error; - - if ((hif = altq_lookup(ap->hfsc_ifname, ALTQT_HFSC)) == NULL) - return (EBADF); - - if (ALTQ_IS_ENABLED(hif->hif_ifq)) - altq_disable(hif->hif_ifq); - - if ((error = altq_detach(hif->hif_ifq))) - return (error); - - return hfsc_detach(hif); -} - -static int -hfsccmd_add_class(ap) - struct hfsc_add_class *ap; -{ - struct hfsc_if *hif; - struct hfsc_class *cl, *parent; - - if ((hif = altq_lookup(ap->iface.hfsc_ifname, ALTQT_HFSC)) == NULL) - return (EBADF); - - if ((parent = clh_to_clp(hif, ap->parent_handle)) == NULL) { - if (ap->parent_handle == HFSC_ROOTCLASS_HANDLE) - parent = hif->hif_rootclass; - else - return (EINVAL); - } - - if ((cl = hfsc_class_create(hif, &ap->service_curve, parent, - ap->qlimit, ap->flags)) == NULL) - return (ENOMEM); - - /* return a class handle to the user */ - ap->class_handle = clp_to_clh(cl); - return (0); -} - -static int -hfsccmd_delete_class(ap) - struct hfsc_delete_class *ap; -{ - struct hfsc_if *hif; - struct hfsc_class *cl; - - if ((hif = altq_lookup(ap->iface.hfsc_ifname, ALTQT_HFSC)) == NULL) - return (EBADF); - - if ((cl = clh_to_clp(hif, ap->class_handle)) == NULL) - return (EINVAL); - - return hfsc_class_destroy(cl); -} - -static int -hfsccmd_modify_class(ap) - struct hfsc_modify_class *ap; -{ - struct hfsc_if *hif; - struct hfsc_class *cl; - struct service_curve *rsc = NULL; - struct service_curve *fsc = NULL; - - if ((hif = altq_lookup(ap->iface.hfsc_ifname, ALTQT_HFSC)) == NULL) - return (EBADF); - - if ((cl = clh_to_clp(hif, ap->class_handle)) == NULL) - return (EINVAL); - - if (ap->sctype & HFSC_REALTIMESC) - rsc = &ap->service_curve; - if (ap->sctype & HFSC_LINKSHARINGSC) - fsc = &ap->service_curve; - - return hfsc_class_modify(cl, rsc, fsc); -} - -static int -hfsccmd_add_filter(ap) - struct hfsc_add_filter *ap; -{ - struct hfsc_if *hif; - struct hfsc_class *cl; - - if ((hif = altq_lookup(ap->iface.hfsc_ifname, ALTQT_HFSC)) == NULL) - return (EBADF); - - if ((cl = clh_to_clp(hif, ap->class_handle)) == NULL) - return (EINVAL); - - if (is_a_parent_class(cl)) { -#ifdef ALTQ_DEBUG - printf("hfsccmd_add_filter: not a leaf class!\n"); -#endif - return (EINVAL); - } - - return acc_add_filter(&hif->hif_classifier, &ap->filter, - cl, &ap->filter_handle); -} - -static int -hfsccmd_delete_filter(ap) - struct hfsc_delete_filter *ap; -{ - struct hfsc_if *hif; - - if ((hif = altq_lookup(ap->iface.hfsc_ifname, ALTQT_HFSC)) == NULL) - return (EBADF); - - return acc_delete_filter(&hif->hif_classifier, - ap->filter_handle); -} - -static int -hfsccmd_class_stats(ap) - struct hfsc_class_stats *ap; -{ - struct hfsc_if *hif; - struct hfsc_class *cl; - struct class_stats stats, *usp; - int n, nclasses, error; - - if ((hif = altq_lookup(ap->iface.hfsc_ifname, ALTQT_HFSC)) == NULL) - return (EBADF); - - ap->cur_time = read_machclk(); - ap->hif_classes = hif->hif_classes; - ap->hif_packets = hif->hif_packets; - - /* skip the first N classes in the tree */ - nclasses = ap->nskip; - for (cl = hif->hif_rootclass, n = 0; cl != NULL && n < nclasses; - cl = hfsc_nextclass(cl), n++) - ; - if (n != nclasses) - return (EINVAL); - - /* then, read the next N classes in the tree */ - nclasses = ap->nclasses; - usp = ap->stats; - for (n = 0; cl != NULL && n < nclasses; cl = hfsc_nextclass(cl), n++) { - - get_class_stats(&stats, cl); - - if ((error = copyout((caddr_t)&stats, (caddr_t)usp++, - sizeof(stats))) != 0) - return (error); - } - - ap->nclasses = n; - - return (0); -} - -static void get_class_stats(sp, cl) - struct class_stats *sp; +static void +get_class_stats(sp, cl) + struct hfsc_classstats *sp; struct hfsc_class *cl; { sp->class_id = cl->cl_id; - sp->class_handle = clp_to_clh(cl); + sp->class_handle = cl->cl_handle; if (cl->cl_rsc != NULL) { sp->rsc.m1 = sm2m(cl->cl_rsc->sm1); @@ -1737,6 +1600,15 @@ static void get_class_stats(sp, cl) sp->fsc.d = 0; sp->fsc.m2 = 0; } + if (cl->cl_usc != NULL) { + sp->usc.m1 = sm2m(cl->cl_usc->sm1); + sp->usc.d = dx2d(cl->cl_usc->dx); + sp->usc.m2 = sm2m(cl->cl_usc->sm2); + } else { + sp->usc.m1 = 0; + sp->usc.d = 0; + sp->usc.m2 = 0; + } sp->total = cl->cl_total; sp->cumul = cl->cl_cumul; @@ -1744,8 +1616,25 @@ static void get_class_stats(sp, cl) sp->d = cl->cl_d; sp->e = cl->cl_e; sp->vt = cl->cl_vt; + sp->f = cl->cl_f; + + sp->initvt = cl->cl_initvt; + sp->vtperiod = cl->cl_vtperiod; + sp->parentperiod = cl->cl_parentperiod; + sp->nactive = cl->cl_nactive; + sp->vtoff = cl->cl_vtoff; + sp->cvtmax = cl->cl_cvtmax; + sp->myf = cl->cl_myf; + sp->cfmin = cl->cl_cfmin; + sp->cvtmin = cl->cl_cvtmin; + sp->myfadj = cl->cl_myfadj; + sp->vtadj = cl->cl_vtadj; + + sp->cur_time = read_machclk(); + sp->machclk_freq = machclk_freq; sp->qlength = qlen(cl->cl_q); + sp->qlimit = qlimit(cl->cl_q); sp->xmit_cnt = cl->cl_stats.xmit_cnt; sp->drop_cnt = cl->cl_stats.drop_cnt; sp->period = cl->cl_stats.period; @@ -1765,39 +1654,14 @@ static void get_class_stats(sp, cl) static struct hfsc_class * clh_to_clp(hif, chandle) struct hfsc_if *hif; - u_long chandle; + u_int32_t chandle; { - struct hfsc_class *cl; + int idx; - cl = (struct hfsc_class *)chandle; - if (chandle != ALIGN(cl)) { -#ifdef ALTQ_DEBUG - printf("clh_to_cl: unaligned pointer %p\n", cl); -#endif + if (chandle == 0) return (NULL); - } - - if (cl == NULL || cl->cl_handle != chandle || cl->cl_hif != hif) + idx = chandle - 1; + if (idx >= HFSC_MAX_CLASSES) return (NULL); - - return (cl); -} - -/* convert a class pointer to the corresponding class handle */ -static u_long -clp_to_clh(cl) - struct hfsc_class *cl; -{ - if (cl->cl_parent == NULL) - return (HFSC_ROOTCLASS_HANDLE); /* XXX */ - return (cl->cl_handle); + return (hif->hif_class_tbl[idx]); } - -#ifdef KLD_MODULE - -static struct altqsw hfsc_sw = - {"hfsc", hfscopen, hfscclose, hfscioctl}; - -ALTQ_MODULE(altq_hfsc, ALTQT_HFSC, &hfsc_sw); - -#endif /* KLD_MODULE */ |