#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/kernel.h>
#include <sys/errno.h>
#include <sys/syslog.h>
#include <sys/sysctl.h>
#include <sys/queue.h>
#include <sys/mcache.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/pfvar.h>
#include <net/altq/altq.h>
#include <net/pktsched/pktsched.h>
#include <pexpert/pexpert.h>
SYSCTL_NODE(_net, OID_AUTO, altq, CTLFLAG_RW|CTLFLAG_LOCKED, 0, "ALTQ");
static u_int32_t altq_debug;
SYSCTL_UINT(_net_altq, OID_AUTO, debug, CTLFLAG_RW, &altq_debug, 0,
"Enable ALTQ debugging");
void *
altq_lookup(char *name, u_int32_t type)
{
struct ifnet *ifp;
void *state = NULL;
if ((ifp = ifunit(name)) != NULL) {
IFCQ_LOCK(&ifp->if_snd);
if (type != ALTQT_NONE &&
IFCQ_ALTQ(&ifp->if_snd)->altq_type == type)
state = IFCQ_ALTQ(&ifp->if_snd)->altq_disc;
if (state == NULL)
IFCQ_UNLOCK(&ifp->if_snd);
}
if (state != NULL)
IFCQ_LOCK_ASSERT_HELD(&ifp->if_snd);
return (state);
}
int
altq_attach(struct ifaltq *altq, u_int32_t type, void *discipline,
altq_enq_func enqueue, altq_deq_func dequeue,
altq_deq_sc_func dequeue_sc, altq_req_func request)
{
IFCQ_LOCK_ASSERT_HELD(altq->altq_ifcq);
if (!ALTQ_IS_READY(altq))
return (ENXIO);
VERIFY(enqueue != NULL);
VERIFY(!(dequeue != NULL && dequeue_sc != NULL));
VERIFY(request != NULL);
altq->altq_type = type;
altq->altq_disc = discipline;
altq->altq_enqueue = enqueue;
altq->altq_dequeue = dequeue;
altq->altq_dequeue_sc = dequeue_sc;
altq->altq_request = request;
altq->altq_flags &= (ALTQF_CANTCHANGE|ALTQF_ENABLED);
return (0);
}
int
altq_detach(struct ifaltq *altq)
{
IFCQ_LOCK_ASSERT_HELD(altq->altq_ifcq);
if (!ALTQ_IS_READY(altq))
return (ENXIO);
if (ALTQ_IS_ENABLED(altq))
return (EBUSY);
if (!ALTQ_IS_ATTACHED(altq))
return (0);
altq->altq_type = ALTQT_NONE;
altq->altq_disc = NULL;
altq->altq_enqueue = NULL;
altq->altq_dequeue = NULL;
altq->altq_dequeue_sc = NULL;
altq->altq_request = NULL;
altq->altq_flags &= ALTQF_CANTCHANGE;
return (0);
}
int
altq_enable(struct ifaltq *altq)
{
struct ifclassq *ifq = altq->altq_ifcq;
IFCQ_LOCK_ASSERT_HELD(ifq);
if (!ALTQ_IS_READY(altq))
return (ENXIO);
if (ALTQ_IS_ENABLED(altq))
return (0);
altq->altq_flags |= ALTQF_ENABLED;
return (0);
}
int
altq_disable(struct ifaltq *altq)
{
struct ifclassq *ifq = altq->altq_ifcq;
IFCQ_LOCK_ASSERT_HELD(ifq);
if (!ALTQ_IS_ENABLED(altq))
return (0);
if_qflush(ifq->ifcq_ifp, 1);
altq->altq_flags &= ~ALTQF_ENABLED;
return (0);
}
int
altq_add(struct pf_altq *a)
{
int error = 0;
VERIFY(machclk_freq != 0);
lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED);
if (a->qname[0] != 0)
return (altq_add_queue(a));
switch (a->scheduler) {
#if PKTSCHED_CBQ
case ALTQT_CBQ:
error = altq_cbq_add(a);
break;
#endif
#if PKTSCHED_PRIQ
case ALTQT_PRIQ:
error = altq_priq_add(a);
break;
#endif
#if PKTSCHED_HFSC
case ALTQT_HFSC:
error = altq_hfsc_add(a);
break;
#endif
#if PKTSCHED_FAIRQ
case ALTQT_FAIRQ:
error = altq_fairq_add(a);
break;
#endif
case ALTQT_QFQ:
error = altq_qfq_add(a);
break;
default:
error = ENXIO;
}
return (error);
}
int
altq_remove(struct pf_altq *a)
{
int error = 0;
lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED);
if (a->qname[0] != 0)
return (altq_remove_queue(a));
switch (a->scheduler) {
#if PKTSCHED_CBQ
case ALTQT_CBQ:
error = altq_cbq_remove(a);
break;
#endif
#if PKTSCHED_PRIQ
case ALTQT_PRIQ:
error = altq_priq_remove(a);
break;
#endif
#if PKTSCHED_HFSC
case ALTQT_HFSC:
error = altq_hfsc_remove(a);
break;
#endif
#if PKTSCHED_FAIRQ
case ALTQT_FAIRQ:
error = altq_fairq_remove(a);
break;
#endif
case ALTQT_QFQ:
error = altq_qfq_remove(a);
break;
default:
error = ENXIO;
}
return (error);
}
int
altq_add_queue(struct pf_altq *a)
{
int error = 0;
lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED);
switch (a->scheduler) {
#if PKTSCHED_CBQ
case ALTQT_CBQ:
error = altq_cbq_add_queue(a);
break;
#endif
#if PKTSCHED_PRIQ
case ALTQT_PRIQ:
error = altq_priq_add_queue(a);
break;
#endif
#if PKTSCHED_HFSC
case ALTQT_HFSC:
error = altq_hfsc_add_queue(a);
break;
#endif
#if PKTSCHED_FAIRQ
case ALTQT_FAIRQ:
error = altq_fairq_add_queue(a);
break;
#endif
case ALTQT_QFQ:
error = altq_qfq_add_queue(a);
break;
default:
error = ENXIO;
}
return (error);
}
int
altq_remove_queue(struct pf_altq *a)
{
int error = 0;
lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED);
switch (a->scheduler) {
#if PKTSCHED_CBQ
case ALTQT_CBQ:
error = altq_cbq_remove_queue(a);
break;
#endif
#if PKTSCHED_PRIQ
case ALTQT_PRIQ:
error = altq_priq_remove_queue(a);
break;
#endif
#if PKTSCHED_HFSC
case ALTQT_HFSC:
error = altq_hfsc_remove_queue(a);
break;
#endif
#if PKTSCHED_FAIRQ
case ALTQT_FAIRQ:
error = altq_fairq_remove_queue(a);
break;
#endif
case ALTQT_QFQ:
error = altq_qfq_remove_queue(a);
break;
default:
error = ENXIO;
}
return (error);
}
int
altq_getqstats(struct pf_altq *a, void *ubuf, int *nbytes)
{
int error = 0;
lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED);
switch (a->scheduler) {
#if PKTSCHED_CBQ
case ALTQT_CBQ:
error = altq_cbq_getqstats(a, ubuf, nbytes);
break;
#endif
#if PKTSCHED_PRIQ
case ALTQT_PRIQ:
error = altq_priq_getqstats(a, ubuf, nbytes);
break;
#endif
#if PKTSCHED_HFSC
case ALTQT_HFSC:
error = altq_hfsc_getqstats(a, ubuf, nbytes);
break;
#endif
#if PKTSCHED_FAIRQ
case ALTQT_FAIRQ:
error = altq_fairq_getqstats(a, ubuf, nbytes);
break;
#endif
case ALTQT_QFQ:
error = altq_qfq_getqstats(a, ubuf, nbytes);
break;
default:
error = ENXIO;
}
return (error);
}
int
altq_pfattach(struct pf_altq *a)
{
int error = 0;
lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED);
switch (a->scheduler) {
case ALTQT_NONE:
break;
#if PKTSCHED_CBQ
case ALTQT_CBQ:
error = altq_cbq_pfattach(a);
break;
#endif
#if PKTSCHED_PRIQ
case ALTQT_PRIQ:
error = altq_priq_pfattach(a);
break;
#endif
#if PKTSCHED_HFSC
case ALTQT_HFSC:
error = altq_hfsc_pfattach(a);
break;
#endif
#if PKTSCHED_FAIRQ
case ALTQT_FAIRQ:
error = altq_fairq_pfattach(a);
break;
#endif
case ALTQT_QFQ:
error = altq_qfq_pfattach(a);
break;
default:
error = ENXIO;
}
return (error);
}
int
altq_pfdetach(struct pf_altq *a)
{
struct ifnet *ifp;
int error = 0;
lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED);
if ((ifp = ifunit(a->ifname)) == NULL)
return (EINVAL);
IFCQ_LOCK(&ifp->if_snd);
if (a->altq_disc == NULL ||
a->altq_disc != IFCQ_ALTQ(&ifp->if_snd)->altq_disc) {
IFCQ_UNLOCK(&ifp->if_snd);
return (0);
}
if (ALTQ_IS_ENABLED(IFCQ_ALTQ(&ifp->if_snd)))
error = altq_disable(IFCQ_ALTQ(&ifp->if_snd));
if (error == 0)
error = altq_detach(IFCQ_ALTQ(&ifp->if_snd));
IFCQ_UNLOCK(&ifp->if_snd);
return (error);
}