root/sys/altq/altq_jobs.c
/*      $NetBSD: altq_jobs.c,v 1.15 2025/08/18 20:59:56 andvar Exp $    */
/*      $KAME: altq_jobs.c,v 1.11 2005/04/13 03:44:25 suz Exp $ */
/*
 * Copyright (c) 2001, the Rector and Board of Visitors of the
 * University of Virginia.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms,
 * with or without modification, are permitted provided
 * that the following conditions are met:
 *
 * Redistributions of source code must retain the above
 * copyright notice, this list of conditions and the following
 * disclaimer.
 *
 * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following
 * disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *
 * Neither the name of the University of Virginia nor the names
 * of its contributors may be used to endorse or promote products
 * derived from this software without specific prior written
 * permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */
/*                                                                     
 * JoBS - altq prototype implementation                                
 *                                                                     
 * Author: Nicolas Christin <nicolas@cs.virginia.edu>
 *
 * JoBS algorithms originally devised and proposed by                  
 * Nicolas Christin and Jorg Liebeherr.
 * Grateful acknowledgments to Tarek Abdelzaher for his help and       
 * comments, and to Kenjiro Cho for some helpful advice.
 * Contributed by the Multimedia Networks Group at the University
 * of Virginia. 
 *
 * Papers and additional info can be found at 
 * http://qosbox.cs.virginia.edu
 *                                                                      
 */                                                                    

/*
 * JoBS queue
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: altq_jobs.c,v 1.15 2025/08/18 20:59:56 andvar Exp $");

#ifdef _KERNEL_OPT
#include "opt_altq.h"
#include "opt_inet.h"
#endif

#ifdef ALTQ_JOBS  /* jobs is enabled by ALTQ_JOBS option in opt_altq.h */

#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 <sys/kauth.h>

#ifdef __FreeBSD__
#include <sys/limits.h>
#endif

#include <net/if.h>
#include <net/if_types.h>

#include <altq/altq.h>
#include <altq/altq_conf.h>
#include <altq/altq_jobs.h>

#ifdef ALTQ3_COMPAT
/*
 * function prototypes
 */
static struct jobs_if *jobs_attach(struct ifaltq *, u_int, u_int, u_int);
static void jobs_detach(struct jobs_if *);
static int jobs_clear_interface(struct jobs_if *);
static int jobs_request(struct ifaltq *, int, void *);
static void jobs_purge(struct jobs_if *);
static struct jobs_class *jobs_class_create(struct jobs_if *,
    int, int64_t, int64_t, int64_t, int64_t, int64_t, int);
static int jobs_class_destroy(struct jobs_class *);
static int jobs_enqueue(struct ifaltq *, struct mbuf *);
static struct mbuf *jobs_dequeue(struct ifaltq *, int);

static int jobs_addq(struct jobs_class *, struct mbuf *, struct jobs_if*);
static struct mbuf *jobs_getq(struct jobs_class *);
static struct mbuf *jobs_pollq(struct jobs_class *);
static void jobs_purgeq(struct jobs_class *);

static int jobscmd_if_attach(struct jobs_attach *);
static int jobscmd_if_detach(struct jobs_interface *);
static int jobscmd_add_class(struct jobs_add_class *);
static int jobscmd_delete_class(struct jobs_delete_class *);
static int jobscmd_modify_class(struct jobs_modify_class *);
static int jobscmd_add_filter(struct jobs_add_filter *);
static int jobscmd_delete_filter(struct jobs_delete_filter *);
static int jobscmd_class_stats(struct jobs_class_stats *);
static void get_class_stats(struct class_stats *, struct jobs_class *);
static struct jobs_class *clh_to_clp(struct jobs_if *, u_long);
static u_long clp_to_clh(struct jobs_class *);

static TSLIST *tslist_alloc(void);
static void tslist_destroy(struct jobs_class *);
static int tslist_enqueue(struct jobs_class *, u_int64_t);
static void tslist_dequeue(struct jobs_class *);
static void tslist_drop(struct jobs_class *);

static int enforce_wc(struct jobs_if *);
static int64_t* adjust_rates_rdc(struct jobs_if *);
static int64_t* assign_rate_drops_adc(struct jobs_if *);
static int64_t* update_error(struct jobs_if *);
static int min_rates_adc(struct jobs_if *);
static int64_t proj_delay(struct jobs_if *, int);
static int pick_dropped_rlc(struct jobs_if *);

altqdev_decl(jobs);

/* jif_list keeps all jobs_if's allocated. */
static struct jobs_if *jif_list = NULL;

typedef unsigned long long ull;

/* setup functions */

static struct jobs_if *
jobs_attach(struct ifaltq *ifq, u_int bandwidth, u_int qlimit, u_int separate)
{
        struct jobs_if *jif;

        jif = malloc(sizeof(struct jobs_if), M_DEVBUF, M_WAITOK|M_ZERO);
        if (jif == NULL)
                return NULL;

        jif->jif_bandwidth = bandwidth;
        jif->jif_qlimit = qlimit;
        jif->jif_separate = separate;
#ifdef ALTQ_DEBUG
        printf("JoBS bandwidth = %d bps\n", (int)bandwidth);
        printf("JoBS buffer size = %d pkts [%s]\n",
               (int)qlimit, separate?"separate buffers":"shared buffer");
#endif
        jif->jif_maxpri = -1;
        jif->jif_ifq = ifq;

        jif->wc_cycles_enqueue = 0;
        jif->avg_cycles_enqueue = 0;
        jif->avg_cycles2_enqueue = 0;
        jif->bc_cycles_enqueue = ALTQ_INFINITY;
        jif->wc_cycles_dequeue = 0;
        jif->avg_cycles_dequeue = 0;
        jif->avg_cycles2_dequeue = 0;
        jif->bc_cycles_dequeue = ALTQ_INFINITY;
        jif->total_enqueued = 0;
        jif->total_dequeued = 0;

        /* add this state to the jobs list */
        jif->jif_next = jif_list;
        jif_list = jif;

        return jif;
}

static void
jobs_detach(struct jobs_if *jif)
{
        (void)jobs_clear_interface(jif);

        /* remove this interface from the jif list */
        if (jif_list == jif)
                jif_list = jif->jif_next;
        else {
                struct jobs_if *p;

                for (p = jif_list; p != NULL; p = p->jif_next)
                        if (p->jif_next == jif) {
                                p->jif_next = jif->jif_next;
                                break;
                        }
                ASSERT(p != NULL);
        }
        free(jif, M_DEVBUF);
}

/*
 * bring the interface back to the initial state by discarding
 * all the filters and classes.
 */
static int
jobs_clear_interface(struct jobs_if *jif)
{
        struct jobs_class       *cl;
        int     pri;

        /* free the filters for this interface */
        acc_discard_filters(&jif->jif_classifier, NULL, 1);

        /* clear out the classes */
        for (pri = 0; pri <= jif->jif_maxpri; pri++)
                if ((cl = jif->jif_classes[pri]) != NULL)
                        jobs_class_destroy(cl);

        return 0;
}

static int
jobs_request(struct ifaltq *ifq, int req, void *arg)
{
        struct jobs_if  *jif = (struct jobs_if *)ifq->altq_disc;

        switch (req) {
        case ALTRQ_PURGE:
                jobs_purge(jif);
                break;
        }
        return 0;
}

/* discard all the queued packets on the interface */
static void
jobs_purge(struct jobs_if *jif)
{
        struct jobs_class *cl;
        int pri;

        for (pri = 0; pri <= jif->jif_maxpri; pri++) {
                if ((cl = jif->jif_classes[pri]) != NULL && !qempty(cl->cl_q))
                        jobs_purgeq(cl);
        }
        if (ALTQ_IS_ENABLED(jif->jif_ifq))
                jif->jif_ifq->ifq_len = 0;
}

static struct jobs_class *
jobs_class_create(struct jobs_if *jif, int pri, int64_t adc, int64_t rdc,
    int64_t alc, int64_t rlc, int64_t arc, int flags)
{
        struct jobs_class *cl, *scan1, *scan2;
        int s;
        int class_exists1, class_exists2;
        int i, j;
        int64_t tmp[JOBS_MAXPRI];
        u_int64_t now;

        if ((cl = jif->jif_classes[pri]) != NULL) {
                /* modify the class instead of creating a new one */
                s = splnet();
                if (!qempty(cl->cl_q))
                        jobs_purgeq(cl);
                splx(s);
        } else {
                cl = malloc(sizeof(struct jobs_class), M_DEVBUF,
                    M_WAITOK|M_ZERO);
                if (cl == NULL)
                        return NULL;

                cl->cl_q = malloc(sizeof(class_queue_t), M_DEVBUF,
                    M_WAITOK|M_ZERO);
                if (cl->cl_q == NULL)
                        goto err_ret;

                cl->arv_tm = tslist_alloc();
                if (cl->arv_tm == NULL)
                        goto err_ret;
        }

        jif->jif_classes[pri] = cl;

        if (flags & JOCF_DEFAULTCLASS)
                jif->jif_default = cl;

        qtype(cl->cl_q) = Q_DROPTAIL;
        qlen(cl->cl_q) = 0;
        cl->service_rate = 0;
        cl->min_rate_adc = 0;
        cl->current_loss = 0;
        cl->cl_period = 0;
        PKTCNTR_RESET(&cl->cl_arrival);
        PKTCNTR_RESET(&cl->cl_rin);
        PKTCNTR_RESET(&cl->cl_rout);
        PKTCNTR_RESET(&cl->cl_rout_th);
        PKTCNTR_RESET(&cl->cl_dropcnt);
        PKTCNTR_RESET(&cl->st_arrival);
        PKTCNTR_RESET(&cl->st_rin);
        PKTCNTR_RESET(&cl->st_rout);
        PKTCNTR_RESET(&cl->st_dropcnt);
        cl->st_service_rate = 0;
        cl->cl_lastdel = 0;
        cl->cl_avgdel = 0;
        cl->adc_violations = 0;

        if (adc == -1) {
                cl->concerned_adc = 0;
                adc = ALTQ_INFINITY;
        } else 
                cl->concerned_adc = 1;

        if (alc == -1) {
                cl->concerned_alc = 0;
                alc = ALTQ_INFINITY;
        } else 
                cl->concerned_alc = 1;

        if (rdc == -1) {
                rdc = 0;
                cl->concerned_rdc = 0;
        } else 
                cl->concerned_rdc = 1;

        if (rlc == -1) {
                rlc = 0;
                cl->concerned_rlc = 0;
        } else 
                cl->concerned_rlc = 1;

        if (arc == -1) {
                arc = 0;
                cl->concerned_arc = 0;
        } else
                cl->concerned_arc = 1;

        cl->cl_rdc=rdc;

        if (cl->concerned_adc) {
                /* adc is given in us, convert it to clock ticks */
                cl->cl_adc = (u_int64_t)(adc*machclk_freq/GRANULARITY);
        } else
                cl->cl_adc = adc;

        if (cl->concerned_arc) {
                /* arc is given in bps, convert it to internal unit */
                cl->cl_arc = (u_int64_t)(bps_to_internal(arc));
        } else
                cl->cl_arc = arc;

        cl->cl_rlc=rlc;
        cl->cl_alc=alc;
        cl->delay_prod_others = 0;
        cl->loss_prod_others = 0;
        cl->cl_flags = flags;
        cl->cl_pri = pri;
        if (pri > jif->jif_maxpri)
                jif->jif_maxpri = pri;
        cl->cl_jif = jif;
        cl->cl_handle = (u_long)cl;  /* just a pointer to this class */

        /*
         * update delay_prod_others and loss_prod_others
         * in all classes if needed
         */

        if (cl->concerned_rdc) {
                for (i = 0; i <= jif->jif_maxpri; i++) {
                        scan1 = jif->jif_classes[i];
                        class_exists1 = (scan1 != NULL);
                        if (class_exists1) {
                                tmp[i] = 1;
                                for (j = 0; j <= i-1; j++) {
                                        scan2 = jif->jif_classes[j];
                                        class_exists2 = (scan2 != NULL);
                                        if (class_exists2
                                            && scan2->concerned_rdc)
                                                tmp[i] *= scan2->cl_rdc;
                                }
                        } else
                                tmp[i] = 0;
                }

                for (i = 0; i <= jif->jif_maxpri; i++) {
                        scan1 = jif->jif_classes[i];
                        class_exists1 = (scan1 != NULL);
                        if (class_exists1) {
                                scan1->delay_prod_others = 1;
                                for (j = 0; j <= jif->jif_maxpri; j++) {
                                        scan2 = jif->jif_classes[j];
                                        class_exists2 = (scan2 != NULL);
                                        if (class_exists2 && j != i
                                            && scan2->concerned_rdc)
                                                scan1->delay_prod_others *= tmp[j];
                                }
                        }
                }
        }

        if (cl->concerned_rlc) {
                for (i = 0; i <= jif->jif_maxpri; i++) {
                        scan1 = jif->jif_classes[i];
                        class_exists1 = (scan1 != NULL);
                        if (class_exists1) {
                                tmp[i] = 1;
                                for (j = 0; j <= i-1; j++) {
                                        scan2 = jif->jif_classes[j];
                                        class_exists2 = (scan2 != NULL);
                                        if (class_exists2
                                            && scan2->concerned_rlc)
                                                tmp[i] *= scan2->cl_rlc;
                                }
                        } else
                                tmp[i] = 0;
                }

                for (i = 0; i <= jif->jif_maxpri; i++) {
                        scan1 = jif->jif_classes[i];
                        class_exists1 = (scan1 != NULL);
                        if (class_exists1) {
                                scan1->loss_prod_others = 1;
                                for (j = 0; j <= jif->jif_maxpri; j++) {
                                        scan2 = jif->jif_classes[j];
                                        class_exists2 = (scan2 != NULL);
                                        if (class_exists2 && j != i
                                            && scan2->concerned_rlc)
                                                scan1->loss_prod_others *= tmp[j];
                                }
                        }
                }
        }

        now = read_machclk();
        cl->idletime = now;
        return cl;

 err_ret:
        if (cl->cl_q != NULL)
                free(cl->cl_q, M_DEVBUF);
        if (cl->arv_tm != NULL)
                free(cl->arv_tm, M_DEVBUF);

        free(cl, M_DEVBUF);
        return NULL;
}

static int
jobs_class_destroy(struct jobs_class *cl)
{
        struct jobs_if *jif;
        int s, pri;

        s = splnet();

        /* delete filters referencing to this class */
        acc_discard_filters(&cl->cl_jif->jif_classifier, cl, 0);

        if (!qempty(cl->cl_q))
                jobs_purgeq(cl);

        jif = cl->cl_jif;
        jif->jif_classes[cl->cl_pri] = NULL;
        if (jif->jif_maxpri == cl->cl_pri) {
                for (pri = cl->cl_pri; pri >= 0; pri--)
                        if (jif->jif_classes[pri] != NULL) {
                                jif->jif_maxpri = pri;
                                break;
                        }
                if (pri < 0)
                        jif->jif_maxpri = -1;
        }
        splx(s);

        tslist_destroy(cl);
        free(cl->cl_q, M_DEVBUF);
        free(cl, M_DEVBUF);
        return 0;
}

/*
 * jobs_enqueue is an enqueue function to be registered to
 * (*altq_enqueue) in struct ifaltq.
 */
static int
jobs_enqueue(struct ifaltq *ifq, struct mbuf *m)
{
        struct jobs_if  *jif = (struct jobs_if *)ifq->altq_disc;
        struct jobs_class *cl, *scan;
        int len;
        int return_flag;
        int pri;
        u_int64_t now;
        u_int64_t old_arv;
        int64_t* delta_rate;
        u_int64_t tstamp1, tstamp2, cycles; /* used for benchmarking only */

        jif->total_enqueued++;
        now = read_machclk();
        tstamp1 = now;

        return_flag = 0;

        /* proceed with packet enqueuing */

        if (IFQ_IS_EMPTY(ifq)) {
                for (pri=0; pri <= jif->jif_maxpri; pri++) {
                        scan = jif->jif_classes[pri];
                        if (scan != NULL) {
                                /*
                                 * reset all quantities, except:
                                 * average delay, number of violations
                                 */
                                PKTCNTR_RESET(&scan->cl_rin);
                                PKTCNTR_RESET(&scan->cl_rout);
                                PKTCNTR_RESET(&scan->cl_rout_th);
                                PKTCNTR_RESET(&scan->cl_arrival);
                                PKTCNTR_RESET(&scan->cl_dropcnt);       
                                scan->cl_lastdel = 0;
                                scan->current_loss = 0;
                                scan->service_rate = 0;
                                scan->idletime = now;
                                scan->cl_last_rate_update = now;
                        }
                }
        }

        /* grab class set by classifier */
        if ((cl = m->m_pkthdr.pattr_class) == NULL)
                cl = jif->jif_default;

        len = m_pktlen(m);
        old_arv = cl->cl_arrival.bytes;
        PKTCNTR_ADD(&cl->cl_arrival, (int)len);
        PKTCNTR_ADD(&cl->cl_rin, (int)len);
        PKTCNTR_ADD(&cl->st_arrival, (int)len);
        PKTCNTR_ADD(&cl->st_rin, (int)len);

        if (cl->cl_arrival.bytes < old_arv) {
                /* deals w/ overflow */
                for (pri=0; pri <= jif->jif_maxpri; pri++) {
                        scan = jif->jif_classes[pri];
                        if (scan != NULL) {
                                /*
                                 * reset all quantities, except:
                                 * average delay, number of violations
                                 */
                                PKTCNTR_RESET(&scan->cl_rin);
                                PKTCNTR_RESET(&scan->cl_rout);
                                PKTCNTR_RESET(&scan->cl_rout_th);
                                PKTCNTR_RESET(&scan->cl_arrival);
                                PKTCNTR_RESET(&scan->cl_dropcnt);       
                                scan->current_loss = 0;
                                scan->service_rate = 0;
                                scan->idletime = now;
                                scan->cl_last_rate_update = now;
                        }
                }
                PKTCNTR_ADD(&cl->cl_arrival, (int)len);
                PKTCNTR_ADD(&cl->cl_rin, (int)len);
        }

        if (cl->cl_arrival.bytes > cl->cl_rin.bytes)
                cl->current_loss =
                    ((cl->cl_arrival.bytes - cl->cl_rin.bytes) << SCALE_LOSS)
                    / cl->cl_arrival.bytes;
        else
                cl->current_loss = 0;

        /* for MDRR: update theoretical value of the output curve */

        for (pri=0; pri <= jif->jif_maxpri; pri++) {
                scan = jif->jif_classes[pri];
                if (scan != NULL) {
                        if (scan->cl_last_rate_update == scan->idletime
                            || scan->cl_last_rate_update == 0)
                                scan->cl_last_rate_update = now; /* initial case */
                        else
                                scan->cl_rout_th.bytes +=
                                    delay_diff(now, scan->cl_last_rate_update)
                                    * scan->service_rate;

                        /*
                         * we don't really care about packets here
                         * WARNING: rout_th is SCALED
                         * (b/c of the service rate)
                         * for precision, as opposed to rout.
                         */

                        scan->cl_last_rate_update = now;
                }
        }

        if (jobs_addq(cl, m, jif) != 0)
                return_flag = ENOBUFS; /* signals there's a buffer overflow */
        else
                IFQ_INC_LEN(ifq);

        /* successfully queued. */

        enforce_wc(jif);

        if (!min_rates_adc(jif)) {
                delta_rate = assign_rate_drops_adc(jif);
                if (delta_rate != NULL) {
                        for (pri = 0; pri <= jif->jif_maxpri; pri++)
                          if ((cl = jif->jif_classes[pri]) != NULL &&
                              !qempty(cl->cl_q))
                                cl->service_rate += delta_rate[pri];
                        free(delta_rate, M_DEVBUF);
                }
        }

        delta_rate = adjust_rates_rdc(jif);

        if (delta_rate != NULL) {
                for (pri = 0; pri <= jif->jif_maxpri; pri++)
                        if ((cl = jif->jif_classes[pri]) != NULL &&
                            !qempty(cl->cl_q))
                                cl->service_rate += delta_rate[pri];
                free(delta_rate, M_DEVBUF);
        }

        tstamp2 = read_machclk();
        cycles = delay_diff(tstamp2, tstamp1);
        if (cycles > jif->wc_cycles_enqueue)
                jif->wc_cycles_enqueue=cycles;
        if (cycles < jif->bc_cycles_enqueue)
                jif->bc_cycles_enqueue=cycles;

        jif->avg_cycles_enqueue += cycles;
        jif->avg_cycles2_enqueue += cycles * cycles;

        return return_flag;
}

/*
 * jobs_dequeue is a dequeue function to be registered to
 * (*altq_dequeue) in struct ifaltq.
 *
 * note: ALTDQ_POLL returns the next packet without removing the packet
 *      from the queue.  ALTDQ_REMOVE is a normal dequeue operation.
 *      ALTDQ_REMOVE must return the same packet if called immediately
 *      after ALTDQ_POLL.
 */

static struct mbuf *
jobs_dequeue(struct ifaltq *ifq, int op)
{
        struct jobs_if  *jif = (struct jobs_if *)ifq->altq_disc;
        struct jobs_class *cl;
        struct mbuf *m;
        int pri;
        int svc_class;
        int64_t max_error;
        int64_t error;
        u_int64_t now;
        u_int64_t tstamp1, tstamp2, cycles;

        jif->total_dequeued++;

        now = read_machclk();
        tstamp1 = now;

        if (IFQ_IS_EMPTY(ifq)) {
                /* no packet in the queue */
                for (pri=0; pri <= jif->jif_maxpri; pri++) {
                  cl = jif->jif_classes[pri];
                  if (cl != NULL)
                    cl->idletime = now;
                }

                tstamp2 = read_machclk();
                cycles = delay_diff(tstamp2, tstamp1);
                if (cycles > jif->wc_cycles_dequeue)
                        jif->wc_cycles_dequeue = cycles;
                if (cycles < jif->bc_cycles_dequeue)
                        jif->bc_cycles_dequeue = cycles;

                jif->avg_cycles_dequeue += cycles;
                jif->avg_cycles2_dequeue += cycles * cycles;

                return NULL;
        }

        /*
         * select the class whose actual transmissions are the furthest
         * from the promised transmissions
         */

        max_error = -1;
        svc_class = -1;

        for (pri=0; pri <= jif->jif_maxpri; pri++) {
                if (((cl = jif->jif_classes[pri]) != NULL)
                    && !qempty(cl->cl_q)) {
                        error = (int64_t)cl->cl_rout_th.bytes
                            -(int64_t)scale_rate(cl->cl_rout.bytes);
                        if (max_error == -1) {
                                max_error = error;
                                svc_class = pri;
                        } else if (error > max_error) {
                                max_error = error;
                                svc_class = pri;
                        }
                }
        }

        if (svc_class != -1)
                cl = jif->jif_classes[svc_class];
        else
                cl = NULL;

        if (op == ALTDQ_POLL) {
                tstamp2 = read_machclk();
                cycles = delay_diff(tstamp2, tstamp1);
                if (cycles > jif->wc_cycles_dequeue)
                        jif->wc_cycles_dequeue = cycles;
                if (cycles < jif->bc_cycles_dequeue)
                        jif->bc_cycles_dequeue = cycles;

                jif->avg_cycles_dequeue += cycles;
                jif->avg_cycles2_dequeue += cycles * cycles;

                return (jobs_pollq(cl));
        }

        if (cl != NULL)
                m = jobs_getq(cl);
        else
                m = NULL;

        if (m != NULL) {
                IFQ_DEC_LEN(ifq);
                if (qempty(cl->cl_q))
                        cl->cl_period++;

                cl->cl_lastdel = (u_int64_t)delay_diff(now,
                    tslist_first(cl->arv_tm)->timestamp);
                if (cl->concerned_adc
                    && (int64_t)cl->cl_lastdel > cl->cl_adc)
                        cl->adc_violations++;
                cl->cl_avgdel  += ticks_to_secs(GRANULARITY*cl->cl_lastdel);

                PKTCNTR_ADD(&cl->cl_rout, m_pktlen(m));
                PKTCNTR_ADD(&cl->st_rout, m_pktlen(m));
        }
        if (cl != NULL)
                tslist_dequeue(cl);             /* dequeue the timestamp */

        tstamp2 = read_machclk();
        cycles = delay_diff(tstamp2, tstamp1);
        if (cycles > jif->wc_cycles_dequeue)
                jif->wc_cycles_dequeue = cycles;
        if (cycles < jif->bc_cycles_dequeue)
                jif->bc_cycles_dequeue = cycles;

        jif->avg_cycles_dequeue += cycles;
        jif->avg_cycles2_dequeue += cycles * cycles;

        return m;
}

static int
jobs_addq(struct jobs_class *cl, struct mbuf *m, struct jobs_if *jif)
{
        int victim;
        u_int64_t len;
        u_int64_t now;
        struct jobs_class* victim_class;

        victim = -1;
        victim_class = NULL;
        len = 0;

        now = read_machclk();

        if (jif->jif_separate && qlen(cl->cl_q) >= jif->jif_qlimit) {
                /*
                 * separate buffers: no guarantees on packet drops
                 * can be offered
                 * thus we drop the incoming packet
                 */
                len = (u_int64_t)m_pktlen(m);
                PKTCNTR_ADD(&cl->cl_dropcnt, (int)len);
                PKTCNTR_SUB(&cl->cl_rin, (int)len);
                PKTCNTR_ADD(&cl->st_dropcnt, (int)len);
                PKTCNTR_SUB(&cl->st_rin, (int)len);
                cl->current_loss += (len << SCALE_LOSS)
                    /cl->cl_arrival.bytes;
                m_freem(m);
                return (-1);

        } else if (!jif->jif_separate
                   && jif->jif_ifq->ifq_len >= jif->jif_qlimit) {
                /* shared buffer: supports guarantees on losses */
                if (!cl->concerned_rlc) {
                        if (!cl->concerned_alc) {
                                /*
                                 * no ALC, no RLC on this class:
                                 * drop the incoming packet
                                 */
                                len = (u_int64_t)m_pktlen(m);
                                PKTCNTR_ADD(&cl->cl_dropcnt, (int)len);
                                PKTCNTR_SUB(&cl->cl_rin, (int)len);
                                PKTCNTR_ADD(&cl->st_dropcnt, (int)len);
                                PKTCNTR_SUB(&cl->st_rin, (int)len);
                                cl->current_loss += (len << SCALE_LOSS)/cl->cl_arrival.bytes;
                                m_freem(m);
                                return (-1);
                        } else {
                                /*
                                 * no RLC, but an ALC:
                                 * drop the incoming packet if possible
                                 */
                                len = (u_int64_t)m_pktlen(m);
                                if (cl->current_loss + (len << SCALE_LOSS)
                                    / cl->cl_arrival.bytes <= cl->cl_alc) {
                                        PKTCNTR_ADD(&cl->cl_dropcnt, (int)len);
                                        PKTCNTR_SUB(&cl->cl_rin, (int)len);
                                        PKTCNTR_ADD(&cl->st_dropcnt, (int)len);
                                        PKTCNTR_SUB(&cl->st_rin, (int)len);
                                        cl->current_loss += (len << SCALE_LOSS)/cl->cl_arrival.bytes;
                                        m_freem(m);
                                        return (-1);
                                } else {
                                        /*
                                         * the ALC would be violated:
                                         * pick another class
                                         */
                                        _addq(cl->cl_q, m);
                                        tslist_enqueue(cl, now);

                                        victim = pick_dropped_rlc(jif);

                                        if (victim == -1) {
                                                /*
                                                 * something went wrong
                                                 * let us discard
                                                 * the incoming packet,
                                                 * regardless of what
                                                 * may happen...
                                                 */
                                                victim_class = cl;
                                        } else
                                                victim_class = jif->jif_classes[victim];

                                        if (victim_class != NULL) {
                                                /*
                                                 * test for safety
                                                 * purposes...
                                                 * it must be true
                                                 */
                                                m = _getq_tail(victim_class->cl_q);
                                                len = (u_int64_t)m_pktlen(m);
                                                PKTCNTR_ADD(&victim_class->cl_dropcnt, (int)len);
                                                PKTCNTR_SUB(&victim_class->cl_rin, (int)len);
                                                PKTCNTR_ADD(&victim_class->st_dropcnt, (int)len);
                                                PKTCNTR_SUB(&victim_class->st_rin, (int)len);
                                                victim_class->current_loss += (len << SCALE_LOSS)/victim_class->cl_arrival.bytes;
                                                m_freem(m); /* the packet is trashed here */
                                                tslist_drop(victim_class); /* and its timestamp as well */
                                        }
                                        return (-1);
                                }
                        }
                } else {
                        /*
                         * RLC on that class:
                         * pick class according to RLCs
                         */
                        _addq(cl->cl_q, m);
                        tslist_enqueue(cl, now);

                        victim = pick_dropped_rlc(jif);
                        if (victim == -1) {
                                /*
                                 * something went wrong
                                 * let us discard the incoming packet,
                                 * regardless of what may happen...
                                 */
                                victim_class = cl;
                        } else
                                victim_class = jif->jif_classes[victim];

                        if (victim_class != NULL) {
                                /*
                                 * test for safety purposes...
                                 * it must be true
                                 */
                                m = _getq_tail(victim_class->cl_q);
                                len = (u_int64_t)m_pktlen(m);
                                PKTCNTR_ADD(&victim_class->cl_dropcnt, (int)len);
                                PKTCNTR_SUB(&victim_class->cl_rin, (int)len);
                                PKTCNTR_ADD(&victim_class->st_dropcnt, (int)len);
                                PKTCNTR_SUB(&victim_class->st_rin, (int)len);
                                victim_class->current_loss += (len << SCALE_LOSS)/victim_class->cl_arrival.bytes;
                                m_freem(m); /* the packet is trashed here */
                                tslist_drop(victim_class); /* and its timestamp as well */
                        }
                        return -1;
                }
        }
        /* else: no drop */

        _addq(cl->cl_q, m);
        tslist_enqueue(cl, now);

        return 0;
}

static struct mbuf *
jobs_getq(struct jobs_class *cl)
{
        return _getq(cl->cl_q);
}

static struct mbuf *
jobs_pollq(struct jobs_class *cl)
{
        return qhead(cl->cl_q);
}

static void
jobs_purgeq(struct jobs_class *cl)
{
        struct mbuf *m;

        if (qempty(cl->cl_q))
                return;

        while ((m = _getq(cl->cl_q)) != NULL) {
                PKTCNTR_ADD(&cl->cl_dropcnt, m_pktlen(m));
                PKTCNTR_ADD(&cl->st_dropcnt, m_pktlen(m));
                m_freem(m);
                tslist_drop(cl);
        }
        ASSERT(qlen(cl->cl_q) == 0);
}

/*
 * timestamp list support routines
 *
 * this implementation has been revamped and
 * now uses a TAILQ structure.
 * timestamp list holds class timestamps
 * there is one timestamp list per class.
 */
static TSLIST *
tslist_alloc(void)
{
        TSLIST *list_init;

        list_init = malloc(sizeof(TSLIST), M_DEVBUF, M_WAITOK);
        TAILQ_INIT(list_init);
        return list_init;
}

static void
tslist_destroy(struct jobs_class *cl)
{
        while (tslist_first(cl->arv_tm) != NULL)
                tslist_dequeue(cl);

        free(cl->arv_tm, M_DEVBUF);
}

static int
tslist_enqueue(struct jobs_class *cl, u_int64_t arv)
{
        TSENTRY *pushed;
        pushed = malloc(sizeof(TSENTRY), M_DEVBUF, M_WAITOK);
        if (pushed == NULL)
                return 0;       

        pushed->timestamp = arv;
        TAILQ_INSERT_TAIL(cl->arv_tm, pushed, ts_list);
        return 1;
}

static void
tslist_dequeue(struct jobs_class *cl)
{
        TSENTRY *popped;
        popped = tslist_first(cl->arv_tm);
        if (popped != NULL) {
                  TAILQ_REMOVE(cl->arv_tm, popped, ts_list);
                  free(popped, M_DEVBUF);
        }
        return;
}

static void
tslist_drop(struct jobs_class *cl)
{
        TSENTRY *popped;
        popped = tslist_last(cl->arv_tm);
        if (popped != NULL) {
                  TAILQ_REMOVE(cl->arv_tm, popped, ts_list);
                  free(popped, M_DEVBUF);
        }
        return;
}

/*
 * rate allocation support routines
 */
/*
 * enforce_wc: enforce that backlogged classes have non-zero
 * service rate, and that non-backlogged classes have zero
 * service rate.
 */

static int
enforce_wc(struct jobs_if *jif)
{
        struct jobs_class *cl;

        int64_t active_classes;
        int pri;
        int is_backlogged, class_exists, updated;

        updated = 0;
        active_classes = 0;

        for (pri = 0; pri <= jif->jif_maxpri; pri++) {
                cl = jif->jif_classes[pri];
                class_exists = (cl != NULL);
                is_backlogged = (class_exists && !qempty(cl->cl_q));

                if (is_backlogged)
                        active_classes++;
                if ((is_backlogged && cl->service_rate <= 0)
                    ||(class_exists
                       && !is_backlogged && cl->service_rate > 0))
                        updated = 1;
        }

        if (updated) {
                for (pri = 0; pri <= jif->jif_maxpri; pri++) {
                        cl = jif->jif_classes[pri];
                        class_exists = (cl != NULL);
                        is_backlogged = (class_exists && !qempty(cl->cl_q));

                        if (class_exists && !is_backlogged)
                                cl->service_rate = 0;
                        else if (is_backlogged)
                                cl->service_rate = (int64_t)(bps_to_internal((u_int64_t)jif->jif_bandwidth)/active_classes);
                }
        }

        return (updated);
}

/*
 * adjust_rates_rdc: compute the service rates adjustments
 * needed to realize the desired proportional delay differentiation.
 * essentially, the rate adjustement delta_rate = prop_control*error,
 * where error is the difference between the measured "weighted"
 * delay and the mean of the weighted delays. see paper for more
 * information.
 * prop_control has slightly changed since the INFOCOM paper,
 * this condition seems to provide better results.
 */

static int64_t *
adjust_rates_rdc(struct jobs_if *jif)
{
        int64_t *result;
        int64_t credit, available, lower_bound, upper_bound;
        int64_t bk;
        int i, j;
        int rdc_classes, active_classes;
        int class_exists, is_backlogged;
        struct jobs_class *cl;
        int64_t *error;
        int64_t prop_control;
        u_int64_t max_prod;
        u_int64_t min_share;
        u_int64_t max_avg_pkt_size;

        /*
         * min_share is scaled
         * to avoid dealing with doubles
         */
        active_classes = 0;
        rdc_classes = 0;
        max_prod = 0;
        max_avg_pkt_size = 0;

        upper_bound = (int64_t)jif->jif_bandwidth;

        for (i = 0; i <= jif->jif_maxpri; i++) {
                cl = jif->jif_classes[i];
                class_exists = (cl != NULL);
                is_backlogged = (class_exists && !qempty(cl->cl_q));
                if (is_backlogged) {
                        active_classes++;
                        if (cl->concerned_rdc)
                                rdc_classes++;
                        else
                                upper_bound -=
                                    internal_to_bps(cl->service_rate);
                }
        }

        result = malloc((jif->jif_maxpri+1)*sizeof(int64_t),
            M_DEVBUF, M_WAITOK);

        if (result == NULL)
                return NULL;

        for (i = 0; i <= jif->jif_maxpri; i++)
                result[i] = 0;

        if (upper_bound <= 0 || rdc_classes == 0)
                return result;

        credit = 0;
        lower_bound = 0;
        min_share = ((u_int64_t)1 << SCALE_SHARE);
        bk = 0;

        for (i = 0; i <= jif->jif_maxpri; i++) {
                cl = jif->jif_classes[i];
                class_exists = (cl != NULL);
                is_backlogged = (class_exists && !qempty(cl->cl_q));
                if (is_backlogged && cl->concerned_rdc)
                        bk += cl->cl_rin.bytes;
        }

        if (bk == 0)
                return result;

        for (i = 0; i <= jif->jif_maxpri; i++) {
                cl = jif->jif_classes[i];
                class_exists = (cl != NULL);
                is_backlogged = (class_exists && !qempty(cl->cl_q));
                if (is_backlogged
                    && (cl->cl_rin.bytes << SCALE_SHARE)/bk < min_share)
                        min_share = (cl->cl_rin.bytes << SCALE_SHARE)/bk;
                if (is_backlogged && cl->concerned_rdc
                    && cl->delay_prod_others > max_prod)
                        max_prod = cl->delay_prod_others;

                if (is_backlogged && cl->concerned_rdc
                    && cl->cl_rin.bytes > max_avg_pkt_size*cl->cl_rin.packets)
                        max_avg_pkt_size = (u_int64_t)((u_int)cl->cl_rin.bytes/(u_int)cl->cl_rin.packets);
        }

        error = update_error(jif);
        if (!error)
                goto fail;

        prop_control = (upper_bound*upper_bound*min_share)
            /(max_prod*(max_avg_pkt_size << 2));
  
        prop_control = bps_to_internal(ticks_to_secs(prop_control)); /* in BT-1 */

        credit = 0;
        for (i = 0; i <= jif->jif_maxpri; i++) {
                cl = jif->jif_classes[i];
                class_exists = (cl != NULL);
                is_backlogged = (class_exists && !qempty(cl->cl_q));
                if (is_backlogged && cl->concerned_rdc) {
                        result[i] = -prop_control*error[i]; /* in BT-1 */
                        result[i] >>= (SCALE_SHARE);
                }
        }

        free(error, M_DEVBUF); /* we don't need these anymore */

        /* saturation */

        for (i = 0; i <= jif->jif_maxpri; i++) {
                cl = jif->jif_classes[i];
                class_exists = (cl != NULL);
                is_backlogged = (class_exists && !qempty(cl->cl_q));

                if (is_backlogged && cl->concerned_rdc)
                        lower_bound += cl->min_rate_adc;
                /*
                 * note: if there's no ADC or ARC on cl,
                 * this is equal to zero, which is fine
                 */
        }

        for (i = 0; i <= jif->jif_maxpri; i++) {
                cl = jif->jif_classes[i];
                class_exists = (cl != NULL);
                is_backlogged = (class_exists && !qempty(cl->cl_q));

                if (is_backlogged && cl->concerned_rdc
                    && result[i] + cl->service_rate > upper_bound) {
                        for (j = 0; j <= jif->jif_maxpri; j++) {
                                cl = jif->jif_classes[j];
                                class_exists = (cl != NULL);
                                is_backlogged = (class_exists
                                                 && !qempty(cl->cl_q));
                                if (is_backlogged && cl->concerned_rdc) {
                                        if (j == i)
                                                result[j] = upper_bound
                                                    -cl->service_rate
                                                    + cl->min_rate_adc
                                                    - lower_bound;
                                        else
                                                result[j] =
                                                    -cl->service_rate
                                                    +cl->min_rate_adc;
                                }
                        }
                        return result;
                }

                cl = jif->jif_classes[i];
                /* do this again since it may have been modified */
                class_exists = (cl != NULL);
                is_backlogged = (class_exists && !qempty(cl->cl_q));

                if (is_backlogged && cl->concerned_rdc
                    && result[i] + cl->service_rate < cl->min_rate_adc) {
                        credit += cl->service_rate+result[i]
                            -cl->min_rate_adc;
                        /* "credit" is in fact a negative number */
                        result[i] = -cl->service_rate+cl->min_rate_adc;
                }
        }

        for (i = jif->jif_maxpri; (i >= 0 && credit < 0); i--) {
                cl = jif->jif_classes[i];
                class_exists = (cl != NULL);
                is_backlogged = (class_exists && !qempty(cl->cl_q));
 
                if (is_backlogged && cl->concerned_rdc) {
                        available = result[i]
                            + cl->service_rate-cl->min_rate_adc;
                        if (available >= -credit) {
                                result[i] += credit;
                                credit = 0;
                        } else {
                                result[i] -= available;
                                credit += available;
                        }
                }
        }
        return result;

fail:   free(result, M_DEVBUF);
        return NULL;
}

/*
 * assign_rate_drops_adc: returns the adjustment needed to
 * the service rates to meet the absolute delay/rate constraints
 * (delay/throughput bounds) and drops traffic if need be.
 * see tech. report UVA/T.R. CS-2000-24/CS-2001-21 for more info.
 */

static int64_t *
assign_rate_drops_adc(struct jobs_if *jif)
{
        int64_t *result;
        int class_exists, is_backlogged;
        struct jobs_class *cl;

        int64_t *c, *n, *k;
        int64_t *available;

        int lowest, highest;
        int keep_going;
        int i;
        u_int64_t now, oldest_arv;
        int64_t remaining_time;
        struct mbuf* pkt;
        u_int64_t len;

        now = read_machclk();
        oldest_arv = now;

        result = malloc((jif->jif_maxpri+1)*sizeof(int64_t), M_DEVBUF, M_WAITOK);
        if (result == NULL)
                goto fail0;
        c = malloc((jif->jif_maxpri+1)*sizeof(u_int64_t), M_DEVBUF, M_WAITOK);
        if (c == NULL)
                goto fail1;
        n = malloc((jif->jif_maxpri+1)*sizeof(u_int64_t), M_DEVBUF, M_WAITOK);
        if (n == NULL)
                goto fail2;
        k = malloc((jif->jif_maxpri+1)*sizeof(u_int64_t), M_DEVBUF, M_WAITOK);
        if (k == NULL)
                goto fail3;
        available = malloc((jif->jif_maxpri+1)*sizeof(int64_t), M_DEVBUF, M_WAITOK);
        if (available == NULL)
                goto fail4;

        for (i = 0; i <= jif->jif_maxpri; i++)
                result[i] = 0;

        keep_going = 1;

        for (i = 0; i <= jif->jif_maxpri; i++) {
                cl = jif->jif_classes[i];
                class_exists = (cl != NULL);
                is_backlogged = (class_exists && !qempty(cl->cl_q));

                if (is_backlogged) {
                        if (cl->concerned_adc) {
                                /*
                                 * get the arrival time of the oldest
                                 * class-i packet
                                 */
                                if (tslist_first(cl->arv_tm) == NULL)
                                        oldest_arv = now; /* NOTREACHED */
                                else
                                        oldest_arv = (tslist_first(cl->arv_tm))->timestamp;

                                n[i] = cl->service_rate;
                                k[i] = scale_rate((int64_t)(cl->cl_rin.bytes - cl->cl_rout.bytes));

                                remaining_time = cl->cl_adc
                                    - (int64_t)delay_diff(now, oldest_arv);
                                if (remaining_time > 0) {
                                        c[i] = remaining_time;
                                        /*
                                         * c is the remaining time before
                                         * the deadline is violated
                                         * (in ticks)
                                         */
                                        available[i] = n[i]-k[i]/c[i];
                                } else {
                                        /*
                                         * deadline has passed...
                                         * we allocate the whole link
                                         * capacity to hopefully
                                         * solve the problem
                                         */
                                        c[i] = 0;
                                        available[i] = -((int64_t)bps_to_internal((u_int64_t)jif->jif_bandwidth));
                                }
                                if (cl->concerned_arc) {
                                        /*
                                         * there's an ARC in addition
                                         * to the ADC
                                         */
                                        if (n[i] - cl->cl_arc < available[i])
                                                available[i] = n[i]
                                                    - cl->cl_arc;
                                }
                        } else if (cl->concerned_arc) {
                                /*
                                 * backlogged, concerned by ARC
                                 * but not by ADC
                                 */
                                n[i] = cl->service_rate;
                                available[i] = n[i] - cl->cl_arc;
                        } else {
                                /*
                                 * backlogged but not concerned by ADC
                                 * or ARC -> can give everything
                                 */
                                n[i] = cl->service_rate;
                                available[i] = n[i];
                        }
                } else {
                        /* not backlogged */
                        n[i] = 0;
                        k[i] = 0;
                        c[i] = 0;
                        if (class_exists)
                                available[i] = cl->service_rate;
                        else
                                available[i] = 0;
                }
        }

        /* step 1: adjust rates (greedy algorithm) */

        highest = 0;
        lowest  = jif->jif_maxpri;

        while (highest < jif->jif_maxpri+1 && available[highest] >= 0)
                highest++; /* which is the highest class that needs more service? */
        while (lowest > 0 && available[lowest] <= 0)
                lowest--;  /* which is the lowest class that needs less service? */

        while (highest != jif->jif_maxpri+1 && lowest != -1) {
                /* give the excess service from lowest to highest */
                if (available[lowest]+available[highest] > 0) {
                        /*
                         * still some "credit" left
                         * give all that is needed by "highest"
                         */
                        n[lowest]  += available[highest];
                        n[highest] -= available[highest];
                        available[lowest]  += available[highest];
                        available[highest] = 0;

                        while (highest < jif->jif_maxpri+1
                               && available[highest] >= 0)
                                highest++;  /* which is the highest class that needs more service now? */

                } else if (available[lowest]+available[highest] == 0) {
                        /* no more credit left but it's fine */
                        n[lowest]  += available[highest];
                        n[highest] -= available[highest];
                        available[highest] = 0;
                        available[lowest]  = 0;

                        while (highest < jif->jif_maxpri+1
                               && available[highest] >= 0)
                                highest++;  /* which is the highest class that needs more service? */
                        while (lowest >= 0 && available[lowest] <= 0)
                                lowest--;   /* which is the lowest class that needs less service? */

                } else if (available[lowest]+available[highest] < 0) {
                        /*
                         * no more credit left and we need to switch
                         * to another class
                         */
                        n[lowest]  -= available[lowest];
                        n[highest] += available[lowest];
                        available[highest] += available[lowest];
                        available[lowest]  = 0;

                        while ((lowest >= 0)&&(available[lowest] <= 0))
                                lowest--;  /* which is the lowest class that needs less service? */
                }
        }

        for (i = 0; i <= jif->jif_maxpri; i++) {
                cl = jif->jif_classes[i];
                class_exists = (cl != NULL);
                is_backlogged = (class_exists && !qempty(cl->cl_q));
                if (is_backlogged) {
                        result[i] = n[i] - cl->service_rate;
                } else {
                        if (class_exists)
                                result[i] = - cl->service_rate;
                        else
                                result[i] = 0;
                }
        }

        /* step 2: adjust drops (for ADC) */

        if (highest != jif->jif_maxpri+1) {
                /* some class(es) still need(s) additional service */
                for (i = 0; i <= jif->jif_maxpri; i++) {
                        cl = jif->jif_classes[i];
                        class_exists = (cl != NULL);
                        is_backlogged = (class_exists
                                         && !qempty(cl->cl_q));
                        if (is_backlogged && available[i] < 0) {
                                if (cl->concerned_adc) {
                                        k[i] = c[i]*n[i];
                                        while (keep_going && scale_rate((int64_t)(cl->cl_rin.bytes-cl->cl_rout.bytes)) > k[i]) {
                                                pkt = qtail(cl->cl_q);
                                                if (pkt != NULL) {
                                                        /* "safeguard" test (a packet SHOULD be in there) */
                                                        len = (u_int64_t)m_pktlen(pkt);
                                                        /* access packet at the tail */
                                                        if (cl->concerned_alc
                                                            && cl->current_loss+(len << SCALE_LOSS)/cl->cl_arrival.bytes > cl->cl_alc) {
                                                                keep_going = 0; /* relax ADC in favor of ALC */
                                                        } else {
                                                                /* drop packet at the tail of the class-i queue, update values */
                                                                pkt = _getq_tail(cl->cl_q);
                                                                len = (u_int64_t)m_pktlen(pkt);
                                                                PKTCNTR_ADD(&cl->cl_dropcnt, (int)len);
                                                                PKTCNTR_SUB(&cl->cl_rin, (int)len);
                                                                PKTCNTR_ADD(&cl->st_dropcnt, (int)len);
                                                                PKTCNTR_SUB(&cl->st_rin, (int)len);
                                                                cl->current_loss += (len << SCALE_LOSS)/cl->cl_arrival.bytes;
                                                                m_freem(pkt); /* the packet is trashed here */
                                                                tslist_drop(cl);
                                                                IFQ_DEC_LEN(cl->cl_jif->jif_ifq);
                                                        }
                                                } else
                                                        keep_going = 0; /* NOTREACHED */
                                        }
                                        k[i] = scale_rate((int64_t)(cl->cl_rin.bytes-cl->cl_rout.bytes));
                                }
                                /*
                                 * n[i] is the max rate we can give.
                                 * the above drops as much as possible
                                 * to respect a delay bound.
                                 * for throughput bounds,
                                 * there's nothing that can be done
                                 * after the greedy reallocation.
                                 */
                        }
                }
        }

        /* update the values of min_rate_adc */
        for (i = 0; i <= jif->jif_maxpri; i++) {
                cl = jif->jif_classes[i];
                class_exists = (cl != NULL);
                is_backlogged = (class_exists && !qempty(cl->cl_q));
                if (is_backlogged && cl->concerned_adc) {
                        if (c[i] != 0) {
                                if (cl->concerned_adc
                                    && !cl->concerned_arc)
                                        cl->min_rate_adc = k[i]/c[i];
                                else
                                        cl->min_rate_adc = n[i];
                        } else
                                cl->min_rate_adc = (int64_t)bps_to_internal((u_int64_t)jif->jif_bandwidth);
                } else if (is_backlogged && cl->concerned_arc)
                        cl->min_rate_adc = n[i]; /* the best we can give */
                else {
                        if (class_exists)
                                cl->min_rate_adc = 0;
                }
        }

        free(c, M_DEVBUF);
        free(n, M_DEVBUF);
        free(k, M_DEVBUF);
        free(available, M_DEVBUF);

        return result;

fail5: __unused
        free(available, M_DEVBUF);
fail4:  free(k, M_DEVBUF);
fail3:  free(n, M_DEVBUF);
fail2:  free(c, M_DEVBUF);
fail1:  free(result, M_DEVBUF);
fail0:  return NULL;
}

/*
 * update_error: returns the difference between the mean weighted
 * delay and the weighted delay for each class. if proportional
 * delay differentiation is perfectly achieved, it should return
 * zero for each class.
 */
static int64_t *
update_error(struct jobs_if *jif)
{
        int i;
        int active_classes;
        u_int64_t mean_weighted_delay;
        u_int64_t delays[JOBS_MAXPRI];
        int64_t* error;
        int class_exists, is_backlogged;
        struct jobs_class *cl;

        error = malloc(sizeof(int64_t)*(jif->jif_maxpri+1), M_DEVBUF,
            M_WAITOK|M_ZERO);

        if (error == NULL)
                return NULL;

        mean_weighted_delay = 0;
        active_classes = 0;

        for (i = 0; i <= jif->jif_maxpri; i++) {
                cl = jif->jif_classes[i];
                class_exists = (cl != NULL);
                is_backlogged = (class_exists && !qempty(cl->cl_q));

                if (is_backlogged) {
                        if (cl->concerned_rdc) {
                                delays[i] = proj_delay(jif, i);
                                mean_weighted_delay += cl->delay_prod_others*delays[i];
                                active_classes ++;
                        }
                }
        }

        if (active_classes == 0)
                return error;
        else
                mean_weighted_delay /= active_classes;

        for (i = 0; i <= jif->jif_maxpri; i++) {
                cl = jif->jif_classes[i];
                class_exists = (cl != NULL);
                is_backlogged = (class_exists && !qempty(cl->cl_q));

                if (is_backlogged && cl->concerned_rdc)
                        error[i] = ((int64_t)mean_weighted_delay)-((int64_t)cl->delay_prod_others*delays[i]);
                else
                        error[i] = 0; /*
                                       * either the class isn't concerned,
                                       * or it's not backlogged.
                                       * in any case, the rate shouldn't
                                       * be adjusted.
                                       */
        }
        return error;
}

/*
 * min_rates_adc: computes the minimum service rates needed in
 * each class to meet the absolute delay bounds. if, for any
 * class i, the current service rate of class i is less than
 * the computed minimum service rate, this function returns
 * false, true otherwise.
 */
static int
min_rates_adc(struct jobs_if *jif)
{
        int result;
        int i;
        int class_exists, is_backlogged;
        int64_t remaining_time;
        struct jobs_class *cl;
        result = 1;

        for (i = 0; i <= jif->jif_maxpri; i++) {
                cl = jif->jif_classes[i];
                class_exists = (cl != NULL);
                is_backlogged = (class_exists && !qempty(cl->cl_q));
                if (is_backlogged && cl->concerned_adc) { 
                        remaining_time = cl->cl_adc - proj_delay(jif, i);
                        if (remaining_time > 0 ) {
                                /* min rate needed for ADC */
                                cl->min_rate_adc = scale_rate((int64_t)(cl->cl_rin.bytes-cl->cl_rout.bytes))/remaining_time;
                                if (cl->concerned_arc
                                    && cl->cl_arc > cl->min_rate_adc) {
                                        /* min rate needed for ADC + ARC */
                                        cl->min_rate_adc = cl->cl_arc;
                                }
                        } else {
                                /* the deadline has been exceeded: give the whole link capacity to hopefully fix the situation */
                                cl->min_rate_adc = (int64_t)bps_to_internal((u_int64_t)jif->jif_bandwidth);
                        }
                } else if (is_backlogged && cl->concerned_arc)
                        cl->min_rate_adc = cl->cl_arc;                  /* no ADC, an ARC */
                else if (class_exists)
                        cl->min_rate_adc = 0;   /*
                                                 * either the class is not
                                                 * backlogged
                                                 * or there is no ADC and
                                                 * no ARC
                                                 */
                if (is_backlogged && cl->min_rate_adc > cl->service_rate)
                        result = 0;
        }

        return result;
}

/*
 * proj_delay: computes the difference between the current time
 * and the time the oldest class-i packet still in the class-i
 * queue i arrived in the system.
 */
static int64_t
proj_delay(struct jobs_if *jif, int i)
{
        u_int64_t now;
        int class_exists, is_backlogged;
        struct jobs_class *cl;

        now = read_machclk();
        cl = jif->jif_classes[i];
        class_exists = (cl != NULL);
        is_backlogged = (class_exists && !qempty(cl->cl_q));

        if (is_backlogged)
                return ((int64_t)delay_diff(now, tslist_first(cl->arv_tm)->timestamp));

        return 0; /* NOTREACHED */
}

/*
 * pick_dropped_rlc: returns the class index of the class to be
 * dropped for meeting the relative loss constraints.
 */
static int
pick_dropped_rlc(struct jobs_if *jif)
{
        int64_t mean;
        int64_t* loss_error;
        int i, active_classes;
        int class_exists, is_backlogged;
        int class_dropped;
        int64_t max_error;
        int64_t max_alc;
        struct mbuf* pkt;
        struct jobs_class *cl;
        u_int64_t len;

        loss_error = malloc(sizeof(int64_t)*(jif->jif_maxpri+1),
            M_DEVBUF, M_WAITOK);

        if (loss_error == NULL)
                return -1;

        class_dropped = -1;
        max_error = 0;
        mean = 0;
        active_classes = 0;

        for (i = 0; i <= jif->jif_maxpri; i++) {
                cl = jif->jif_classes[i];
                class_exists = (cl != NULL);
                is_backlogged = (class_exists && !qempty(cl->cl_q));
                if (is_backlogged) {
                        if (cl->concerned_rlc) {
                                mean += cl->loss_prod_others
                                    * cl->current_loss;
                                active_classes++;
                        }
                }
        }

        if (active_classes > 0)
                mean /= active_classes;

        if (active_classes == 0)
                class_dropped = JOBS_MAXPRI+1; /*
                                                * no classes are concerned
                                                * by RLCs (JOBS_MAXPRI+1
                                                * means "ignore RLC" here)
                                                */
        else {
                for (i = 0; i <= jif->jif_maxpri; i++) {
                        cl = jif->jif_classes[i];
                        class_exists = (cl != NULL);
                        is_backlogged = (class_exists
                                         && !qempty(cl->cl_q));

                        if ((is_backlogged)&&(cl->cl_rlc))
                                loss_error[i]=cl->loss_prod_others
                                    *cl->current_loss-mean;
                        else
                                loss_error[i] = ALTQ_INFINITY;
                }

                for (i = 0; i <= jif->jif_maxpri; i++) {
                        cl = jif->jif_classes[i];
                        class_exists = (cl != NULL);
                        is_backlogged = (class_exists
                                         && !qempty(cl->cl_q));
                        if (is_backlogged && loss_error[i] <= max_error) {
                                /*
                                 * find out which class is the most
                                 * below the mean.
                                 * it's the one that needs to be dropped
                                 * ties are broken in favor of the higher
                                 * priority classes (i.e., if two classes
                                 * present the same deviation, the lower
                                 * priority class will get dropped).
                                 */
                                max_error = loss_error[i];
                                class_dropped = i;
                        }
                }

                if (class_dropped != -1) {
                        cl = jif->jif_classes[class_dropped];
                        pkt = qtail(cl->cl_q);
                        if (pkt != NULL) {
                                /*
                                 * "safeguard" test (a packet SHOULD be
                                 * in there)
                                 */
                                len = (u_int64_t)m_pktlen(pkt);
                                /* access packet at the tail */
                                if (cl->current_loss+(len << SCALE_LOSS)/cl->cl_arrival.bytes > cl->cl_alc) {
                                        /*
                                         * the class to drop for meeting
                                         * the RLC will defeat the ALC:
                                         * ignore RLC.
                                         */
                                        class_dropped = JOBS_MAXPRI+1;
                                }
                        } else
                                class_dropped = JOBS_MAXPRI+1; /* NOTREACHED */
                } else
                        class_dropped = JOBS_MAXPRI+1;
        }

        if (class_dropped == JOBS_MAXPRI+1) {
                max_alc = -((int64_t)1 << SCALE_LOSS);
                for (i = jif->jif_maxpri; i >= 0; i--) {
                        cl = jif->jif_classes[i];
                        class_exists = (cl != NULL);
                        is_backlogged = (class_exists
                                         && !qempty(cl->cl_q));
                        if (is_backlogged) {
                                if (cl->concerned_alc && cl->cl_alc - cl->current_loss > max_alc) {
                                        max_alc = cl->cl_alc-cl->current_loss; /* pick the class which is the furthest from its ALC */
                                        class_dropped = i;
                                } else if (!cl->concerned_alc && ((int64_t) 1 << SCALE_LOSS)-cl->current_loss > max_alc) {
                                        max_alc = ((int64_t) 1 << SCALE_LOSS)-cl->current_loss;
                                        class_dropped = i;
                                }
                        }
                }
        }

        free(loss_error, M_DEVBUF);
        return (class_dropped);
}

/*
 * ALTQ binding/setup functions
 */
/*
 * jobs device interface
 */
int
jobsopen(dev_t dev, int flag, int fmt,
    struct lwp *l)
{
        if (machclk_freq == 0)
                init_machclk();

        if (machclk_freq == 0) {
                printf("jobs: no CPU clock available!\n");
                return ENXIO;
        }
        /* everything will be done when the queueing scheme is attached. */
        return 0;
}

int
jobsclose(dev_t dev, int flag, int fmt,
    struct lwp *l)
{
        struct jobs_if *jif;

        while ((jif = jif_list) != NULL) {
                /* destroy all */
                if (ALTQ_IS_ENABLED(jif->jif_ifq))
                        altq_disable(jif->jif_ifq);

                int error = altq_detach(jif->jif_ifq);
                switch (error) {
                case 0:
                case ENXIO:     /* already disabled */
                        break;
                default:
                        return error;
                }
                jobs_detach(jif);
        }

        return 0;
}

int
jobsioctl(dev_t dev, ioctlcmd_t cmd, void *addr, int flag,
    struct lwp *l)
{
        struct jobs_if *jif;
        struct jobs_interface *ifacep;
        int     error = 0;

        /* check super-user privilege */
        switch (cmd) {
        case JOBS_GETSTATS:
                break;
        default:
                if ((error = kauth_authorize_network(l->l_cred,
                    KAUTH_NETWORK_ALTQ, KAUTH_REQ_NETWORK_ALTQ_JOBS, NULL,
                    NULL, NULL)) != 0)
                        return (error);
                break;
        }

        switch (cmd) {

        case JOBS_IF_ATTACH:
                error = jobscmd_if_attach((struct jobs_attach *)addr);
                break;

        case JOBS_IF_DETACH:
                error = jobscmd_if_detach((struct jobs_interface *)addr);
                break;

        case JOBS_ENABLE:
        case JOBS_DISABLE:
        case JOBS_CLEAR:
                ifacep = (struct jobs_interface *)addr;
                if ((jif = altq_lookup(ifacep->jobs_ifname,
                                       ALTQT_JOBS)) == NULL) {
                        error = EBADF;
                        break;
                }

                switch (cmd) {
                case JOBS_ENABLE:
                        if (jif->jif_default == NULL) {
#if 1
                                printf("jobs: no default class\n");
#endif
                                error = EINVAL;
                                break;
                        }
                        error = altq_enable(jif->jif_ifq);
                        break;

                case JOBS_DISABLE:
                        error = altq_disable(jif->jif_ifq);
                        break;

                case JOBS_CLEAR:
                        jobs_clear_interface(jif);
                        break;
                }
                break;

                case JOBS_ADD_CLASS:
                        error = jobscmd_add_class((struct jobs_add_class *)addr);
                        break;

        case JOBS_DEL_CLASS:
                error = jobscmd_delete_class((struct jobs_delete_class *)addr);
                break;

        case JOBS_MOD_CLASS:
                error = jobscmd_modify_class((struct jobs_modify_class *)addr);
                break;

        case JOBS_ADD_FILTER:
                error = jobscmd_add_filter((struct jobs_add_filter *)addr);
                break;

        case JOBS_DEL_FILTER:
                error = jobscmd_delete_filter((struct jobs_delete_filter *)addr);
                break;

        case JOBS_GETSTATS:
                error = jobscmd_class_stats((struct jobs_class_stats *)addr);
                break;

        default:
                error = EINVAL;
                break;
        }
        return error;
}

static int
jobscmd_if_attach(struct jobs_attach *ap)
{
        struct jobs_if *jif;
        struct ifnet *ifp;
        int error;

        if ((ifp = ifunit(ap->iface.jobs_ifname)) == NULL)
                return ENXIO;
        if ((jif = jobs_attach(&ifp->if_snd, ap->bandwidth, ap->qlimit, ap->separate)) == NULL)
                return ENOMEM;

        /*
         * set JOBS to this ifnet structure.
         */
        if ((error = altq_attach(&ifp->if_snd, ALTQT_JOBS, jif,
                                 jobs_enqueue, jobs_dequeue, jobs_request,
                                 &jif->jif_classifier, acc_classify)) != 0)
                jobs_detach(jif);

        return error;
}

static int
jobscmd_if_detach(struct jobs_interface *ap)
{
        struct jobs_if *jif;
        int error;

        if ((jif = altq_lookup(ap->jobs_ifname, ALTQT_JOBS)) == NULL)
                return EBADF;

        if (ALTQ_IS_ENABLED(jif->jif_ifq))
                altq_disable(jif->jif_ifq);

        if ((error = altq_detach(jif->jif_ifq)))
                return error;

        jobs_detach(jif);
        return 0;
}

static int
jobscmd_add_class(struct jobs_add_class *ap)
{
        struct jobs_if *jif;
        struct jobs_class *cl;

        if ((jif = altq_lookup(ap->iface.jobs_ifname, ALTQT_JOBS)) == NULL)
                return EBADF;

        if (ap->pri < 0 || ap->pri >= JOBS_MAXPRI)
                return EINVAL;

        if ((cl = jobs_class_create(jif, ap->pri,
                                    ap->cl_adc, ap->cl_rdc,
                                    ap->cl_alc, ap->cl_rlc, ap-> cl_arc,
                                    ap->flags)) == NULL)
                return ENOMEM;

        /* return a class handle to the user */
        ap->class_handle = clp_to_clh(cl);
        return 0;
}

static int
jobscmd_delete_class(struct jobs_delete_class *ap)
{
        struct jobs_if *jif;
        struct jobs_class *cl;

        if ((jif = altq_lookup(ap->iface.jobs_ifname, ALTQT_JOBS)) == NULL)
                return EBADF;

        if ((cl = clh_to_clp(jif, ap->class_handle)) == NULL)
                return EINVAL;

        return jobs_class_destroy(cl);
}

static int
jobscmd_modify_class(struct jobs_modify_class *ap)
{
        struct jobs_if *jif;
        struct jobs_class *cl;

        if ((jif = altq_lookup(ap->iface.jobs_ifname, ALTQT_JOBS)) == NULL)
                return EBADF;

        if (ap->pri < 0 || ap->pri >= JOBS_MAXPRI)
                return EINVAL;

        if ((cl = clh_to_clp(jif, ap->class_handle)) == NULL)
                return EINVAL;

        /*
         * if priority is changed, move the class to the new priority
         */
        if (jif->jif_classes[ap->pri] != cl) {
                if (jif->jif_classes[ap->pri] != NULL)
                        return EEXIST;
                jif->jif_classes[cl->cl_pri] = NULL;
                jif->jif_classes[ap->pri] = cl;
                cl->cl_pri = ap->pri;
        }

        /* call jobs_class_create to change class parameters */
        if ((cl = jobs_class_create(jif, ap->pri,
                                    ap->cl_adc, ap->cl_rdc,
                                    ap->cl_alc, ap->cl_rlc, ap->cl_arc,
                                    ap->flags)) == NULL)
                return ENOMEM;
        return 0;
}

static int
jobscmd_add_filter(struct jobs_add_filter *ap)
{
        struct jobs_if *jif;
        struct jobs_class *cl;

        if ((jif = altq_lookup(ap->iface.jobs_ifname, ALTQT_JOBS)) == NULL)
                return EBADF;

        if ((cl = clh_to_clp(jif, ap->class_handle)) == NULL)
                return EINVAL;

        return acc_add_filter(&jif->jif_classifier, &ap->filter,
                              cl, &ap->filter_handle);
}

static int
jobscmd_delete_filter(struct jobs_delete_filter *ap)
{
        struct jobs_if *jif;

        if ((jif = altq_lookup(ap->iface.jobs_ifname, ALTQT_JOBS)) == NULL)
                return EBADF;

        return acc_delete_filter(&jif->jif_classifier, ap->filter_handle);
}

static int
jobscmd_class_stats(struct jobs_class_stats *ap)
{
        struct jobs_if *jif;
        struct jobs_class *cl;
        struct class_stats stats, *usp;
        int pri, error;

        if ((jif = altq_lookup(ap->iface.jobs_ifname, ALTQT_JOBS)) == NULL)
                return EBADF;

        ap->maxpri = jif->jif_maxpri;

        /* then, read the next N classes */
        usp = ap->stats;
        for (pri = 0; pri <= jif->jif_maxpri; pri++) {
                cl = jif->jif_classes[pri];
                (void)memset(&stats, 0, sizeof(stats));
                if (cl != NULL)
                        get_class_stats(&stats, cl);
                if ((error = copyout((void *)&stats, (void *)usp++,
                                     sizeof(stats))) != 0)
                        return error;
        }
        return 0;
}

static void
get_class_stats(struct class_stats *sp, struct jobs_class *cl)
{
        u_int64_t now;
        now = read_machclk();

        sp->class_handle = clp_to_clh(cl);
        sp->qlength = qlen(cl->cl_q);

        sp->period = cl->cl_period;
        sp->rin = cl->st_rin;
        sp->arrival = cl->st_arrival;
        sp->arrivalbusy = cl->cl_arrival;
        sp->rout = cl->st_rout;
        sp->dropcnt = cl->cl_dropcnt;

        /*  PKTCNTR_RESET(&cl->st_arrival);*/
        PKTCNTR_RESET(&cl->st_rin);
        PKTCNTR_RESET(&cl->st_rout);

        sp->totallength = cl->cl_jif->jif_ifq->ifq_len;
        sp->lastdel = ticks_to_secs(GRANULARITY*cl->cl_lastdel);
        sp->avgdel = cl->cl_avgdel;

        cl->cl_avgdel = 0;

        sp->busylength = ticks_to_secs(1000*delay_diff(now, cl->idletime));
        sp->adc_violations = cl->adc_violations;

        sp->wc_cycles_enqueue = cl->cl_jif->wc_cycles_enqueue;
        sp->wc_cycles_dequeue = cl->cl_jif->wc_cycles_dequeue;
        sp->bc_cycles_enqueue = cl->cl_jif->bc_cycles_enqueue;
        sp->bc_cycles_dequeue = cl->cl_jif->bc_cycles_dequeue;
        sp->avg_cycles_enqueue = cl->cl_jif->avg_cycles_enqueue;
        sp->avg_cycles_dequeue = cl->cl_jif->avg_cycles_dequeue;
        sp->avg_cycles2_enqueue = cl->cl_jif->avg_cycles2_enqueue;
        sp->avg_cycles2_dequeue = cl->cl_jif->avg_cycles2_dequeue;
        sp->total_enqueued = cl->cl_jif->total_enqueued;
        sp->total_dequeued = cl->cl_jif->total_dequeued;
}

/* convert a class handle to the corresponding class pointer */
static struct jobs_class *
clh_to_clp(struct jobs_if *jif, u_long chandle)
{
        struct jobs_class *cl;

        cl = (struct jobs_class *)chandle;
        if (chandle != ALIGN(cl)) {
#if 1
                printf("clh_to_cl: unaligned pointer %p\n", cl);
#endif
                return NULL;
        }

        if (cl == NULL || cl->cl_handle != chandle || cl->cl_jif != jif)
                return NULL;
        return cl;
}

/* convert a class pointer to the corresponding class handle */
static u_long
clp_to_clh(struct jobs_class *cl)
{
        return (cl->cl_handle);
}

#ifdef KLD_MODULE

static struct altqsw jobs_sw =
        {"jobs", jobsopen, jobsclose, jobsioctl};

ALTQ_MODULE(altq_jobs, ALTQT_JOBS, &jobs_sw);

#endif /* KLD_MODULE */

#endif /* ALTQ3_COMPAT */
#endif /* ALTQ_JOBS */