#if PF_ALTQ && PKTSCHED_CBQ
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/kernel.h>
#include <net/if.h>
#include <net/pfvar.h>
#include <net/net_osdep.h>
#include <net/altq/altq.h>
#include <net/altq/altq_cbq.h>
#include <netinet/in.h>
static int altq_cbq_request(struct ifaltq *, enum altrq, void *);
static int altq_cbq_enqueue(struct ifaltq *, struct mbuf *);
static struct mbuf *altq_cbq_dequeue(struct ifaltq *, enum altdq_op);
int
altq_cbq_pfattach(struct pf_altq *a)
{
struct ifnet *ifp;
int error;
lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED);
if ((ifp = ifunit(a->ifname)) == NULL || a->altq_disc == NULL)
return (EINVAL);
IFCQ_LOCK(&ifp->if_snd);
error = altq_attach(IFCQ_ALTQ(&ifp->if_snd), ALTQT_CBQ, a->altq_disc,
altq_cbq_enqueue, altq_cbq_dequeue, NULL, altq_cbq_request);
IFCQ_UNLOCK(&ifp->if_snd);
return (error);
}
int
altq_cbq_add(struct pf_altq *a)
{
cbq_state_t *cbqp;
struct ifnet *ifp;
lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED);
if ((ifp = ifunit(a->ifname)) == NULL)
return (EINVAL);
if (!ALTQ_IS_READY(IFCQ_ALTQ(&ifp->if_snd)))
return (ENODEV);
cbqp = cbq_alloc(ifp, M_WAITOK, TRUE);
if (cbqp == NULL)
return (ENOMEM);
a->altq_disc = cbqp;
return (0);
}
int
altq_cbq_remove(struct pf_altq *a)
{
cbq_state_t *cbqp;
lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED);
if ((cbqp = a->altq_disc) == NULL)
return (EINVAL);
a->altq_disc = NULL;
return (cbq_destroy(cbqp));
}
int
altq_cbq_add_queue(struct pf_altq *a)
{
struct cbq_opts *opts = &a->pq_u.cbq_opts;
cbq_state_t *cbqp;
int err;
lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED);
if ((cbqp = a->altq_disc) == NULL)
return (EINVAL);
IFCQ_LOCK(cbqp->ifnp.ifq_);
err = cbq_add_queue(cbqp, a->qlimit, a->priority,
opts->minburst, opts->maxburst, opts->pktsize, opts->maxpktsize,
opts->ns_per_byte, opts->maxidle, opts->minidle, opts->offtime,
opts->flags, a->parent_qid, a->qid, NULL);
IFCQ_UNLOCK(cbqp->ifnp.ifq_);
return (err);
}
int
altq_cbq_remove_queue(struct pf_altq *a)
{
cbq_state_t *cbqp;
int err;
lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED);
if ((cbqp = a->altq_disc) == NULL)
return (EINVAL);
IFCQ_LOCK(cbqp->ifnp.ifq_);
err = cbq_remove_queue(cbqp, a->qid);
IFCQ_UNLOCK(cbqp->ifnp.ifq_);
return (err);
}
int
altq_cbq_getqstats(struct pf_altq *a, void *ubuf, int *nbytes)
{
struct ifclassq *ifq = NULL;
cbq_state_t *cbqp;
class_stats_t stats;
int error = 0;
lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED);
if ((unsigned)*nbytes < sizeof (stats))
return (EINVAL);
if ((cbqp = altq_lookup(a->ifname, ALTQT_CBQ)) == NULL)
return (EBADF);
ifq = cbqp->ifnp.ifq_;
IFCQ_LOCK_ASSERT_HELD(ifq);
error = cbq_get_class_stats(cbqp, a->qid, &stats);
IFCQ_UNLOCK(ifq);
if (error != 0)
return (error);
if ((error = copyout((caddr_t)&stats, (user_addr_t)(uintptr_t)ubuf,
sizeof (stats))) != 0)
return (error);
*nbytes = sizeof (stats);
return (0);
}
static int
altq_cbq_request(struct ifaltq *altq, enum altrq req, void *arg)
{
cbq_state_t *cbqp = (cbq_state_t *)altq->altq_disc;
switch (req) {
case ALTRQ_PURGE:
cbq_purge(cbqp);
break;
case ALTRQ_PURGE_SC:
break;
case ALTRQ_EVENT:
cbq_event(cbqp, (cqev_t)arg);
break;
case ALTRQ_THROTTLE:
default:
break;
}
return (0);
}
static int
altq_cbq_enqueue(struct ifaltq *altq, struct mbuf *m)
{
if (!(m->m_flags & M_PKTHDR)) {
printf("%s: packet for %s does not have pkthdr\n", __func__,
if_name(altq->altq_ifcq->ifcq_ifp));
m_freem(m);
return (ENOBUFS);
}
return (cbq_enqueue(altq->altq_disc, NULL, m, m_pftag(m)));
}
static struct mbuf *
altq_cbq_dequeue(struct ifaltq *altq, enum altdq_op op)
{
return (cbq_dequeue(altq->altq_disc, (cqdq_op_t)op));
}
#endif