#include "opt_altq.h"
#include "opt_inet.h"
#include "opt_inet6.h"
#ifdef ALTQ_HFSC
#include <sys/param.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/queue.h>
#include <sys/thread.h>
#include <net/if.h>
#include <net/ifq_var.h>
#include <netinet/in.h>
#include <net/pf/pfvar.h>
#include <net/altq/altq.h>
#include <net/altq/altq_hfsc.h>
#include <sys/thread2.h>
#define HFSC_SUBQ_INDEX ALTQ_SUBQ_INDEX_DEFAULT
#define HFSC_LOCK(ifq) \
ALTQ_SQ_LOCK(&(ifq)->altq_subq[HFSC_SUBQ_INDEX])
#define HFSC_UNLOCK(ifq) \
ALTQ_SQ_UNLOCK(&(ifq)->altq_subq[HFSC_SUBQ_INDEX])
static int hfsc_clear_interface(struct hfsc_if *);
static int hfsc_request(struct ifaltq_subque *, int, void *);
static void hfsc_purge(struct hfsc_if *);
static struct hfsc_class *hfsc_class_create(struct hfsc_if *,
struct service_curve *,
struct service_curve *,
struct service_curve *,
struct hfsc_class *, int, int, int);
static int hfsc_class_destroy(struct hfsc_class *);
static struct hfsc_class *hfsc_nextclass(struct hfsc_class *);
static int hfsc_enqueue(struct ifaltq_subque *, struct mbuf *,
struct altq_pktattr *);
static struct mbuf *hfsc_dequeue(struct ifaltq_subque *, int);
static int hfsc_addq(struct hfsc_class *, struct mbuf *);
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_vf(struct hfsc_class *, int);
static void update_vf(struct hfsc_class *, int, uint64_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 *, uint64_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 *, uint64_t);
static __inline uint64_t seg_x2y(uint64_t, uint64_t);
static __inline uint64_t seg_y2x(uint64_t, uint64_t);
static __inline uint64_t m2sm(u_int);
static __inline uint64_t m2ism(u_int);
static __inline uint64_t d2dx(u_int);
static u_int sm2m(uint64_t);
static u_int dx2d(uint64_t);
static void sc2isc(struct service_curve *, struct internal_sc *);
static void rtsc_init(struct runtime_sc *, struct internal_sc *,
uint64_t, uint64_t);
static uint64_t rtsc_y2x(struct runtime_sc *, uint64_t);
static uint64_t rtsc_x2y(struct runtime_sc *, uint64_t);
static void rtsc_min(struct runtime_sc *, struct internal_sc *,
uint64_t, uint64_t);
static void get_class_stats(struct hfsc_classstats *, struct hfsc_class *);
static struct hfsc_class *clh_to_clp(struct hfsc_if *, uint32_t);
#define is_a_parent_class(cl) ((cl)->cl_children != NULL)
#define HT_INFINITY 0xffffffffffffffffLL
int
hfsc_pfattach(struct pf_altq *a, struct ifaltq *ifq)
{
return altq_attach(ifq, ALTQT_HFSC, a->altq_disc, ifq_mapsubq_default,
hfsc_enqueue, hfsc_dequeue, hfsc_request, NULL, NULL);
}
int
hfsc_add_altq(struct pf_altq *a)
{
struct hfsc_if *hif;
struct ifnet *ifp;
ifnet_lock();
if ((ifp = ifunit(a->ifname)) == NULL) {
ifnet_unlock();
return (EINVAL);
}
if (!ifq_is_ready(&ifp->if_snd)) {
ifnet_unlock();
return (ENODEV);
}
hif = kmalloc(sizeof(struct hfsc_if), M_ALTQ, M_WAITOK | M_ZERO);
hif->hif_eligible = ellist_alloc();
hif->hif_ifq = &ifp->if_snd;
ifq_purge_all(&ifp->if_snd);
ifnet_unlock();
a->altq_disc = hif;
return (0);
}
int
hfsc_remove_altq(struct pf_altq *a)
{
struct hfsc_if *hif;
if ((hif = a->altq_disc) == NULL)
return (EINVAL);
a->altq_disc = NULL;
hfsc_clear_interface(hif);
hfsc_class_destroy(hif->hif_rootclass);
ellist_destroy(hif->hif_eligible);
kfree(hif, M_ALTQ);
return (0);
}
static int
hfsc_add_queue_locked(struct pf_altq *a, struct hfsc_if *hif)
{
struct hfsc_class *cl, *parent;
struct hfsc_opts *opts;
struct service_curve rtsc, lssc, ulsc;
KKASSERT(a->qid != 0);
opts = &a->pq_u.hfsc_opts;
if (a->parent_qid == HFSC_NULLCLASS_HANDLE && hif->hif_rootclass == NULL)
parent = NULL;
else if ((parent = clh_to_clp(hif, a->parent_qid)) == NULL)
return (EINVAL);
if (clh_to_clp(hif, a->qid) != NULL)
return (EBUSY);
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, a->qid);
if (cl == NULL)
return (ENOMEM);
return (0);
}
int
hfsc_add_queue(struct pf_altq *a)
{
struct hfsc_if *hif;
struct ifaltq *ifq;
int error;
if (a->qid == 0)
return (EINVAL);
if ((hif = a->altq_disc) == NULL)
return (EINVAL);
ifq = hif->hif_ifq;
HFSC_LOCK(ifq);
error = hfsc_add_queue_locked(a, hif);
HFSC_UNLOCK(ifq);
return error;
}
static int
hfsc_remove_queue_locked(struct pf_altq *a, struct hfsc_if *hif)
{
struct hfsc_class *cl;
if ((cl = clh_to_clp(hif, a->qid)) == NULL)
return (EINVAL);
return (hfsc_class_destroy(cl));
}
int
hfsc_remove_queue(struct pf_altq *a)
{
struct hfsc_if *hif;
struct ifaltq *ifq;
int error;
if ((hif = a->altq_disc) == NULL)
return (EINVAL);
ifq = hif->hif_ifq;
HFSC_LOCK(ifq);
error = hfsc_remove_queue_locked(a, hif);
HFSC_UNLOCK(ifq);
return error;
}
int
hfsc_getqstats(struct pf_altq *a, void *ubuf, int *nbytes)
{
struct hfsc_if *hif;
struct hfsc_class *cl;
struct hfsc_classstats stats;
struct ifaltq *ifq;
int error = 0;
if (*nbytes < sizeof(stats))
return (EINVAL);
ifnet_lock();
if ((hif = altq_lookup(a->ifname, ALTQT_HFSC)) == NULL) {
ifnet_unlock();
return (EBADF);
}
ifq = hif->hif_ifq;
HFSC_LOCK(ifq);
if ((cl = clh_to_clp(hif, a->qid)) == NULL) {
HFSC_UNLOCK(ifq);
ifnet_unlock();
return (EINVAL);
}
get_class_stats(&stats, cl);
HFSC_UNLOCK(ifq);
ifnet_unlock();
if ((error = copyout((caddr_t)&stats, ubuf, sizeof(stats))) != 0)
return (error);
*nbytes = sizeof(stats);
return (0);
}
static int
hfsc_clear_interface(struct hfsc_if *hif)
{
struct hfsc_class *cl;
if (hif->hif_rootclass == NULL)
return (0);
while ((cl = hif->hif_rootclass->cl_children) != NULL) {
for (; cl != NULL; cl = hfsc_nextclass(cl)) {
if (!is_a_parent_class(cl)) {
hfsc_class_destroy(cl);
break;
}
}
}
return (0);
}
static int
hfsc_request(struct ifaltq_subque *ifsq, int req, void *arg)
{
struct ifaltq *ifq = ifsq->ifsq_altq;
struct hfsc_if *hif = (struct hfsc_if *)ifq->altq_disc;
crit_enter();
switch (req) {
case ALTRQ_PURGE:
if (ifsq_get_index(ifsq) == HFSC_SUBQ_INDEX) {
hfsc_purge(hif);
} else {
ifsq_classic_request(ifsq, ALTRQ_PURGE, NULL);
}
break;
}
crit_exit();
return (0);
}
static void
hfsc_purge(struct hfsc_if *hif)
{
struct hfsc_class *cl;
for (cl = hif->hif_rootclass; cl != NULL; cl = hfsc_nextclass(cl)) {
if (!qempty(cl->cl_q))
hfsc_purgeq(cl);
}
if (ifq_is_enabled(hif->hif_ifq))
ALTQ_SQ_CNTR_RESET(&hif->hif_ifq->altq_subq[HFSC_SUBQ_INDEX]);
}
static struct hfsc_class *
hfsc_class_create(struct hfsc_if *hif, struct service_curve *rsc,
struct service_curve *fsc, struct service_curve *usc,
struct hfsc_class *parent, int qlimit, int flags, int qid)
{
struct hfsc_class *cl, *p;
int i;
if (hif->hif_classes >= HFSC_MAX_CLASSES)
return (NULL);
#ifndef ALTQ_RED
if (flags & HFCF_RED) {
#ifdef ALTQ_DEBUG
kprintf("hfsc_class_create: RED not configured for HFSC!\n");
#endif
return (NULL);
}
#endif
cl = kmalloc(sizeof(*cl), M_ALTQ, M_WAITOK | M_ZERO);
cl->cl_q = kmalloc(sizeof(*cl->cl_q), M_ALTQ, M_WAITOK | M_ZERO);
cl->cl_actc = actlist_alloc();
if (qlimit == 0)
qlimit = 50;
qlimit(cl->cl_q) = qlimit;
qtype(cl->cl_q) = Q_DROPTAIL;
qlen(cl->cl_q) = 0;
cl->cl_flags = 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)
red_flags |= REDF_ECN;
#ifdef ALTQ_RIO
if (flags & HFCF_CLEARDSCP)
red_flags |= RIOF_CLEARDSCP;
#endif
if (m2 < 8)
red_pkttime = 1000 * 1000 * 1000;
else
red_pkttime = (int64_t)hif->hif_ifq->altq_ifp->if_mtu
* 1000 * 1000 * 1000 / (m2 / 8);
if (flags & HFCF_RED) {
cl->cl_red = red_alloc(0, 0,
qlimit(cl->cl_q) * 10/100,
qlimit(cl->cl_q) * 30/100,
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);
if (cl->cl_red != NULL)
qtype(cl->cl_q) = Q_RIO;
}
#endif
}
#endif
if (rsc != NULL && (rsc->m1 != 0 || rsc->m2 != 0)) {
cl->cl_rsc = kmalloc(sizeof(*cl->cl_rsc), M_ALTQ, M_WAITOK);
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)) {
cl->cl_fsc = kmalloc(sizeof(*cl->cl_fsc), M_ALTQ, M_WAITOK);
sc2isc(fsc, cl->cl_fsc);
rtsc_init(&cl->cl_virtual, cl->cl_fsc, 0, 0);
}
if (usc != NULL && (usc->m1 != 0 || usc->m2 != 0)) {
cl->cl_usc = kmalloc(sizeof(*cl->cl_usc), M_ALTQ, M_WAITOK);
sc2isc(usc, cl->cl_usc);
rtsc_init(&cl->cl_ulimit, cl->cl_usc, 0, 0);
}
cl->cl_id = hif->hif_classid++;
cl->cl_handle = qid;
cl->cl_hif = hif;
cl->cl_parent = parent;
crit_enter();
hif->hif_classes++;
i = qid % HFSC_MAX_CLASSES;
if (hif->hif_class_tbl[i] == NULL)
hif->hif_class_tbl[i] = cl;
else {
for (i = 0; i < HFSC_MAX_CLASSES; i++) {
if (hif->hif_class_tbl[i] == NULL) {
hif->hif_class_tbl[i] = cl;
break;
}
}
if (i == HFSC_MAX_CLASSES) {
crit_exit();
goto err_ret;
}
}
if (flags & HFCF_DEFAULTCLASS)
hif->hif_defaultclass = cl;
if (parent == NULL) {
hif->hif_rootclass = cl;
} else if (parent->cl_children == NULL) {
parent->cl_children = cl;
} else {
p = parent->cl_children;
while (p->cl_siblings != NULL)
p = p->cl_siblings;
p->cl_siblings = cl;
}
crit_exit();
return (cl);
err_ret:
if (cl->cl_actc != NULL)
actlist_destroy(cl->cl_actc);
if (cl->cl_red != NULL) {
#ifdef ALTQ_RIO
if (q_is_rio(cl->cl_q))
rio_destroy((rio_t *)cl->cl_red);
#endif
#ifdef ALTQ_RED
if (q_is_red(cl->cl_q))
red_destroy(cl->cl_red);
#endif
}
if (cl->cl_fsc != NULL)
kfree(cl->cl_fsc, M_ALTQ);
if (cl->cl_rsc != NULL)
kfree(cl->cl_rsc, M_ALTQ);
if (cl->cl_usc != NULL)
kfree(cl->cl_usc, M_ALTQ);
if (cl->cl_q != NULL)
kfree(cl->cl_q, M_ALTQ);
kfree(cl, M_ALTQ);
return (NULL);
}
static int
hfsc_class_destroy(struct hfsc_class *cl)
{
struct hfsc_if *hif;
int i;
if (cl == NULL)
return (0);
hif = cl->cl_hif;
if (is_a_parent_class(cl))
return (EBUSY);
crit_enter();
if (!qempty(cl->cl_q))
hfsc_purgeq(cl);
if (cl->cl_parent == NULL) {
} else {
struct hfsc_class *p = cl->cl_parent->cl_children;
if (p == cl) {
cl->cl_parent->cl_children = cl->cl_siblings;
} else {
do {
if (p->cl_siblings == cl) {
p->cl_siblings = cl->cl_siblings;
break;
}
} while ((p = p->cl_siblings) != NULL);
}
KKASSERT(p != NULL);
}
for (i = 0; i < HFSC_MAX_CLASSES; i++) {
if (hif->hif_class_tbl[i] == cl) {
hif->hif_class_tbl[i] = NULL;
break;
}
}
hif->hif_classes--;
crit_exit();
actlist_destroy(cl->cl_actc);
if (cl->cl_red != NULL) {
#ifdef ALTQ_RIO
if (q_is_rio(cl->cl_q))
rio_destroy((rio_t *)cl->cl_red);
#endif
#ifdef ALTQ_RED
if (q_is_red(cl->cl_q))
red_destroy(cl->cl_red);
#endif
}
if (cl == hif->hif_rootclass)
hif->hif_rootclass = NULL;
if (cl == hif->hif_defaultclass)
hif->hif_defaultclass = NULL;
if (cl == hif->hif_pollcache)
hif->hif_pollcache = NULL;
if (cl->cl_usc != NULL)
kfree(cl->cl_usc, M_ALTQ);
if (cl->cl_fsc != NULL)
kfree(cl->cl_fsc, M_ALTQ);
if (cl->cl_rsc != NULL)
kfree(cl->cl_rsc, M_ALTQ);
kfree(cl->cl_q, M_ALTQ);
kfree(cl, M_ALTQ);
return (0);
}
static struct hfsc_class *
hfsc_nextclass(struct hfsc_class *cl)
{
if (cl->cl_children != NULL) {
cl = cl->cl_children;
} else if (cl->cl_siblings != NULL) {
cl = cl->cl_siblings;
} else {
while ((cl = cl->cl_parent) != NULL) {
if (cl->cl_siblings != NULL) {
cl = cl->cl_siblings;
break;
}
}
}
return (cl);
}
static int
hfsc_enqueue(struct ifaltq_subque *ifsq, struct mbuf *m,
struct altq_pktattr *pktattr)
{
struct ifaltq *ifq = ifsq->ifsq_altq;
struct hfsc_if *hif = (struct hfsc_if *)ifq->altq_disc;
struct hfsc_class *cl;
int len;
if (ifsq_get_index(ifsq) != HFSC_SUBQ_INDEX) {
ifsq_classic_request(ifsq, ALTRQ_PURGE, NULL);
m_freem(m);
return ENOBUFS;
}
M_ASSERTPKTHDR(m);
crit_enter();
if (m->m_pkthdr.fw_flags & PF_MBUF_STRUCTURE)
cl = clh_to_clp(hif, m->m_pkthdr.pf.qid);
else
cl = NULL;
if (cl == NULL || is_a_parent_class(cl)) {
cl = hif->hif_defaultclass;
if (cl == NULL) {
m_freem(m);
crit_exit();
return (ENOBUFS);
}
}
cl->cl_pktattr = NULL;
len = m_pktlen(m);
if (hfsc_addq(cl, m) != 0) {
PKTCNTR_ADD(&cl->cl_stats.drop_cnt, len);
crit_exit();
return (ENOBUFS);
}
ALTQ_SQ_PKTCNT_INC(ifsq);
cl->cl_hif->hif_packets++;
if (qlen(cl->cl_q) == 1)
set_active(cl, m_pktlen(m));
crit_exit();
return (0);
}
static struct mbuf *
hfsc_dequeue(struct ifaltq_subque *ifsq, int op)
{
struct ifaltq *ifq = ifsq->ifsq_altq;
struct hfsc_if *hif = (struct hfsc_if *)ifq->altq_disc;
struct hfsc_class *cl;
struct mbuf *m;
int len, next_len;
int realtime = 0;
uint64_t cur_time;
if (ifsq_get_index(ifsq) != HFSC_SUBQ_INDEX) {
ifsq_classic_request(ifsq, ALTRQ_PURGE, NULL);
return NULL;
}
if (hif->hif_packets == 0) {
return (NULL);
}
crit_enter();
cur_time = read_machclk();
if (op == ALTDQ_REMOVE && hif->hif_pollcache != NULL) {
cl = hif->hif_pollcache;
hif->hif_pollcache = NULL;
if (cl->cl_rsc != NULL)
realtime = (cl->cl_e <= cur_time);
} else {
if ((cl = ellist_get_mindl(hif->hif_eligible, cur_time)) != NULL) {
realtime = 1;
} else {
#ifdef ALTQ_DEBUG
int fits = 0;
#endif
cl = hif->hif_rootclass;
while (is_a_parent_class(cl)) {
cl = actlist_firstfit(cl, cur_time);
if (cl == NULL) {
#ifdef ALTQ_DEBUG
if (fits > 0)
kprintf("%d fit but none found\n",fits);
#endif
m = NULL;
goto done;
}
if (cl->cl_parent->cl_cvtmin < cl->cl_vt)
cl->cl_parent->cl_cvtmin = cl->cl_vt;
#ifdef ALTQ_DEBUG
fits++;
#endif
}
}
if (op == ALTDQ_POLL) {
#ifdef foo
hif->hif_pollcache = cl;
#endif
m = hfsc_pollq(cl);
goto done;
}
}
m = hfsc_getq(cl);
if (m == NULL)
panic("hfsc_dequeue:");
len = m_pktlen(m);
cl->cl_hif->hif_packets--;
ALTQ_SQ_PKTCNT_DEC(ifsq);
PKTCNTR_ADD(&cl->cl_stats.xmit_cnt, len);
update_vf(cl, len, cur_time);
if (realtime)
cl->cl_cumul += len;
if (!qempty(cl->cl_q)) {
if (cl->cl_rsc != NULL) {
next_len = m_pktlen(qhead(cl->cl_q));
if (realtime)
update_ed(cl, next_len);
else
update_d(cl, next_len);
}
} else {
set_passive(cl);
}
done:
crit_exit();
return (m);
}
static int
hfsc_addq(struct hfsc_class *cl, struct mbuf *m)
{
#ifdef ALTQ_RIO
if (q_is_rio(cl->cl_q))
return rio_addq((rio_t *)cl->cl_red, cl->cl_q,
m, cl->cl_pktattr);
#endif
#ifdef ALTQ_RED
if (q_is_red(cl->cl_q))
return red_addq(cl->cl_red, cl->cl_q, m, cl->cl_pktattr);
#endif
if (qlen(cl->cl_q) >= qlimit(cl->cl_q)) {
m_freem(m);
return (-1);
}
if (cl->cl_flags & HFCF_CLEARDSCP)
write_dsfield(m, cl->cl_pktattr, 0);
_addq(cl->cl_q, m);
return (0);
}
static struct mbuf *
hfsc_getq(struct hfsc_class *cl)
{
#ifdef ALTQ_RIO
if (q_is_rio(cl->cl_q))
return rio_getq((rio_t *)cl->cl_red, cl->cl_q);
#endif
#ifdef ALTQ_RED
if (q_is_red(cl->cl_q))
return red_getq(cl->cl_red, cl->cl_q);
#endif
return _getq(cl->cl_q);
}
static struct mbuf *
hfsc_pollq(struct hfsc_class *cl)
{
return qhead(cl->cl_q);
}
static void
hfsc_purgeq(struct hfsc_class *cl)
{
struct mbuf *m;
if (qempty(cl->cl_q))
return;
while ((m = _getq(cl->cl_q)) != NULL) {
ALTQ_SQ_PKTCNT_DEC(
&cl->cl_hif->hif_ifq->altq_subq[HFSC_SUBQ_INDEX]);
PKTCNTR_ADD(&cl->cl_stats.drop_cnt, m_pktlen(m));
m_freem(m);
cl->cl_hif->hif_packets--;
}
KKASSERT(qlen(cl->cl_q) == 0);
update_vf(cl, 0, 0);
set_passive(cl);
}
static void
set_active(struct hfsc_class *cl, int len)
{
if (cl->cl_rsc != NULL)
init_ed(cl, len);
if (cl->cl_fsc != NULL)
init_vf(cl, len);
cl->cl_stats.period++;
}
static void
set_passive(struct hfsc_class *cl)
{
if (cl->cl_rsc != NULL)
ellist_remove(cl);
}
static void
init_ed(struct hfsc_class *cl, int next_len)
{
uint64_t cur_time;
cur_time = read_machclk();
rtsc_min(&cl->cl_deadline, cl->cl_rsc, cur_time, cl->cl_cumul);
cl->cl_eligible = cl->cl_deadline;
if (cl->cl_rsc->sm1 <= cl->cl_rsc->sm2) {
cl->cl_eligible.dx = 0;
cl->cl_eligible.dy = 0;
}
cl->cl_e = rtsc_y2x(&cl->cl_eligible, cl->cl_cumul);
cl->cl_d = rtsc_y2x(&cl->cl_deadline, cl->cl_cumul + next_len);
ellist_insert(cl);
}
static void
update_ed(struct hfsc_class *cl, int next_len)
{
cl->cl_e = rtsc_y2x(&cl->cl_eligible, cl->cl_cumul);
cl->cl_d = rtsc_y2x(&cl->cl_deadline, cl->cl_cumul + next_len);
ellist_update(cl);
}
static void
update_d(struct hfsc_class *cl, int next_len)
{
cl->cl_d = rtsc_y2x(&cl->cl_deadline, cl->cl_cumul + next_len);
}
static void
init_vf(struct hfsc_class *cl, int len)
{
struct hfsc_class *max_cl, *p;
uint64_t vt, f, cur_time;
int go_active;
cur_time = 0;
go_active = 1;
for ( ; cl->cl_parent != NULL; cl = cl->cl_parent) {
if (go_active && cl->cl_nactive++ == 0)
go_active = 1;
else
go_active = 0;
if (go_active) {
max_cl = actlist_last(cl->cl_parent->cl_actc);
if (max_cl != NULL) {
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 {
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;
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++;
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) {
if (cur_time == 0)
cur_time = read_machclk();
rtsc_min(&cl->cl_ulimit, cl->cl_usc, cur_time,
cl->cl_total);
cl->cl_myf = rtsc_y2x(&cl->cl_ulimit,
cl->cl_total);
cl->cl_myfadj = 0;
}
}
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_vf(struct hfsc_class *cl, int len, uint64_t cur_time)
{
uint64_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_nactive == 0)
continue;
if (go_passive && --cl->cl_nactive == 0)
go_passive = 1;
else
go_passive = 0;
if (go_passive) {
if (cl->cl_vt > cl->cl_parent->cl_cvtmax)
cl->cl_parent->cl_cvtmax = cl->cl_vt;
actlist_remove(cl);
update_cfmin(cl->cl_parent);
continue;
}
cl->cl_vt = rtsc_y2x(&cl->cl_virtual, cl->cl_total)
- cl->cl_vtoff + cl->cl_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;
}
actlist_update(cl);
if (cl->cl_usc != NULL) {
cl->cl_myf = cl->cl_myfadj
+ rtsc_y2x(&cl->cl_ulimit, cl->cl_total);
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;
}
}
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(struct hfsc_class *cl)
{
struct hfsc_class *p;
uint64_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;
}
static ellist_t *
ellist_alloc(void)
{
ellist_t *head;
head = kmalloc(sizeof(*head), M_ALTQ, M_WAITOK);
TAILQ_INIT(head);
return (head);
}
static void
ellist_destroy(ellist_t *head)
{
kfree(head, M_ALTQ);
}
static void
ellist_insert(struct hfsc_class *cl)
{
struct hfsc_if *hif = cl->cl_hif;
struct hfsc_class *p;
if ((p = TAILQ_LAST(hif->hif_eligible, _eligible)) == NULL ||
p->cl_e <= cl->cl_e) {
TAILQ_INSERT_TAIL(hif->hif_eligible, cl, cl_ellist);
return;
}
TAILQ_FOREACH(p, hif->hif_eligible, cl_ellist) {
if (cl->cl_e < p->cl_e) {
TAILQ_INSERT_BEFORE(p, cl, cl_ellist);
return;
}
}
KKASSERT(0);
}
static void
ellist_remove(struct hfsc_class *cl)
{
struct hfsc_if *hif = cl->cl_hif;
TAILQ_REMOVE(hif->hif_eligible, cl, cl_ellist);
}
static void
ellist_update(struct hfsc_class *cl)
{
struct hfsc_if *hif = cl->cl_hif;
struct hfsc_class *p, *last;
p = TAILQ_NEXT(cl, cl_ellist);
if (p == NULL || cl->cl_e <= p->cl_e)
return;
last = TAILQ_LAST(hif->hif_eligible, _eligible);
KKASSERT(last != NULL);
if (last->cl_e <= cl->cl_e) {
TAILQ_REMOVE(hif->hif_eligible, cl, cl_ellist);
TAILQ_INSERT_TAIL(hif->hif_eligible, cl, cl_ellist);
return;
}
while ((p = TAILQ_NEXT(p, cl_ellist)) != NULL) {
if (cl->cl_e < p->cl_e) {
TAILQ_REMOVE(hif->hif_eligible, cl, cl_ellist);
TAILQ_INSERT_BEFORE(p, cl, cl_ellist);
return;
}
}
KKASSERT(0);
}
struct hfsc_class *
ellist_get_mindl(ellist_t *head, uint64_t cur_time)
{
struct hfsc_class *p, *cl = NULL;
TAILQ_FOREACH(p, head, cl_ellist) {
if (p->cl_e > cur_time)
break;
if (cl == NULL || p->cl_d < cl->cl_d)
cl = p;
}
return (cl);
}
static actlist_t *
actlist_alloc(void)
{
actlist_t *head;
head = kmalloc(sizeof(*head), M_ALTQ, M_WAITOK);
TAILQ_INIT(head);
return (head);
}
static void
actlist_destroy(actlist_t *head)
{
kfree(head, M_ALTQ);
}
static void
actlist_insert(struct hfsc_class *cl)
{
struct hfsc_class *p;
if ((p = TAILQ_LAST(cl->cl_parent->cl_actc, _active)) == NULL
|| p->cl_vt <= cl->cl_vt) {
TAILQ_INSERT_TAIL(cl->cl_parent->cl_actc, cl, cl_actlist);
return;
}
TAILQ_FOREACH(p, cl->cl_parent->cl_actc, cl_actlist) {
if (cl->cl_vt < p->cl_vt) {
TAILQ_INSERT_BEFORE(p, cl, cl_actlist);
return;
}
}
KKASSERT(0);
}
static void
actlist_remove(struct hfsc_class *cl)
{
TAILQ_REMOVE(cl->cl_parent->cl_actc, cl, cl_actlist);
}
static void
actlist_update(struct hfsc_class *cl)
{
struct hfsc_class *p, *last;
p = TAILQ_NEXT(cl, cl_actlist);
if (p == NULL || cl->cl_vt < p->cl_vt)
return;
last = TAILQ_LAST(cl->cl_parent->cl_actc, _active);
KKASSERT(last != NULL);
if (last->cl_vt <= cl->cl_vt) {
TAILQ_REMOVE(cl->cl_parent->cl_actc, cl, cl_actlist);
TAILQ_INSERT_TAIL(cl->cl_parent->cl_actc, cl, cl_actlist);
return;
}
while ((p = TAILQ_NEXT(p, cl_actlist)) != NULL) {
if (cl->cl_vt < p->cl_vt) {
TAILQ_REMOVE(cl->cl_parent->cl_actc, cl, cl_actlist);
TAILQ_INSERT_BEFORE(p, cl, cl_actlist);
return;
}
}
KKASSERT(0);
}
static struct hfsc_class *
actlist_firstfit(struct hfsc_class *cl, uint64_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);
}
#define SM_SHIFT 24
#define ISM_SHIFT 10
#define SM_MASK ((1LL << SM_SHIFT) - 1)
#define ISM_MASK ((1LL << ISM_SHIFT) - 1)
static __inline uint64_t
seg_x2y(uint64_t x, uint64_t sm)
{
uint64_t y;
y = (x >> SM_SHIFT) * sm + (((x & SM_MASK) * sm) >> SM_SHIFT);
return (y);
}
static __inline uint64_t
seg_y2x(uint64_t y, uint64_t ism)
{
uint64_t x;
if (y == 0)
x = 0;
else if (ism == HT_INFINITY)
x = HT_INFINITY;
else
x = (y >> ISM_SHIFT) * ism + (((y & ISM_MASK) * ism) >> ISM_SHIFT);
return (x);
}
static __inline uint64_t
m2sm(u_int m)
{
uint64_t sm;
sm = ((uint64_t)m << SM_SHIFT) / 8 / machclk_freq;
return (sm);
}
static __inline uint64_t
m2ism(u_int m)
{
uint64_t ism;
if (m == 0)
ism = HT_INFINITY;
else
ism = ((uint64_t)machclk_freq << ISM_SHIFT) * 8 / m;
return (ism);
}
static __inline uint64_t
d2dx(u_int d)
{
uint64_t dx;
dx = ((uint64_t)d * machclk_freq) / 1000;
return (dx);
}
static u_int
sm2m(uint64_t sm)
{
uint64_t m;
m = (sm * 8 * machclk_freq) >> SM_SHIFT;
return ((u_int)m);
}
static u_int
dx2d(uint64_t dx)
{
uint64_t d;
d = dx * 1000 / machclk_freq;
return ((u_int)d);
}
static void
sc2isc(struct service_curve *sc, struct internal_sc *isc)
{
isc->sm1 = m2sm(sc->m1);
isc->ism1 = m2ism(sc->m1);
isc->dx = d2dx(sc->d);
isc->dy = seg_x2y(isc->dx, isc->sm1);
isc->sm2 = m2sm(sc->m2);
isc->ism2 = m2ism(sc->m2);
}
static void
rtsc_init(struct runtime_sc *rtsc, struct internal_sc *isc, uint64_t x, uint64_t y)
{
rtsc->x = x;
rtsc->y = y;
rtsc->sm1 = isc->sm1;
rtsc->ism1 = isc->ism1;
rtsc->dx = isc->dx;
rtsc->dy = isc->dy;
rtsc->sm2 = isc->sm2;
rtsc->ism2 = isc->ism2;
}
static uint64_t
rtsc_y2x(struct runtime_sc *rtsc, uint64_t y)
{
uint64_t x;
if (y < rtsc->y) {
x = rtsc->x;
} else if (y <= rtsc->y + rtsc->dy) {
if (rtsc->dy == 0)
x = rtsc->x + rtsc->dx;
else
x = rtsc->x + seg_y2x(y - rtsc->y, rtsc->ism1);
} else {
x = rtsc->x + rtsc->dx
+ seg_y2x(y - rtsc->y - rtsc->dy, rtsc->ism2);
}
return (x);
}
static uint64_t
rtsc_x2y(struct runtime_sc *rtsc, uint64_t x)
{
uint64_t y;
if (x <= rtsc->x) {
y = rtsc->y;
} else if (x <= rtsc->x + rtsc->dx) {
y = rtsc->y + seg_x2y(x - rtsc->x, rtsc->sm1);
} else
y = rtsc->y + rtsc->dy
+ seg_x2y(x - rtsc->x - rtsc->dx, rtsc->sm2);
return (y);
}
static void
rtsc_min(struct runtime_sc *rtsc, struct internal_sc *isc, uint64_t x, uint64_t y)
{
uint64_t y1, y2, dx, dy;
if (isc->sm1 <= isc->sm2) {
y1 = rtsc_x2y(rtsc, x);
if (y1 < y)
return;
rtsc->x = x;
rtsc->y = y;
return;
}
y1 = rtsc_x2y(rtsc, x);
if (y1 <= y) {
return;
}
y2 = rtsc_x2y(rtsc, x + isc->dx);
if (y2 >= y + isc->dy) {
rtsc->x = x;
rtsc->y = y;
rtsc->dx = isc->dx;
rtsc->dy = isc->dy;
return;
}
dx = ((y1 - y) << SM_SHIFT) / (isc->sm1 - isc->sm2);
if (rtsc->x + rtsc->dx > x)
dx += rtsc->x + rtsc->dx - x;
dy = seg_x2y(dx, isc->sm1);
rtsc->x = x;
rtsc->y = y;
rtsc->dx = dx;
rtsc->dy = dy;
}
static void
get_class_stats(struct hfsc_classstats *sp, struct hfsc_class *cl)
{
sp->class_id = cl->cl_id;
sp->class_handle = cl->cl_handle;
if (cl->cl_rsc != NULL) {
sp->rsc.m1 = sm2m(cl->cl_rsc->sm1);
sp->rsc.d = dx2d(cl->cl_rsc->dx);
sp->rsc.m2 = sm2m(cl->cl_rsc->sm2);
} else {
sp->rsc.m1 = 0;
sp->rsc.d = 0;
sp->rsc.m2 = 0;
}
if (cl->cl_fsc != NULL) {
sp->fsc.m1 = sm2m(cl->cl_fsc->sm1);
sp->fsc.d = dx2d(cl->cl_fsc->dx);
sp->fsc.m2 = sm2m(cl->cl_fsc->sm2);
} else {
sp->fsc.m1 = 0;
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;
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;
sp->qtype = qtype(cl->cl_q);
#ifdef ALTQ_RED
if (q_is_red(cl->cl_q))
red_getstats(cl->cl_red, &sp->red[0]);
#endif
#ifdef ALTQ_RIO
if (q_is_rio(cl->cl_q))
rio_getstats((rio_t *)cl->cl_red, &sp->red[0]);
#endif
}
static struct hfsc_class *
clh_to_clp(struct hfsc_if *hif, uint32_t chandle)
{
int i;
struct hfsc_class *cl;
if (chandle == 0)
return (NULL);
i = chandle % HFSC_MAX_CLASSES;
if ((cl = hif->hif_class_tbl[i]) != NULL && cl->cl_handle == chandle)
return (cl);
for (i = 0; i < HFSC_MAX_CLASSES; i++)
if ((cl = hif->hif_class_tbl[i]) != NULL &&
cl->cl_handle == chandle)
return (cl);
return (NULL);
}
#endif