Index: sys/net/altq/if_altq.h =================================================================== --- sys/net/altq/if_altq.h +++ sys/net/altq/if_altq.h @@ -143,7 +143,11 @@ #define ALTRQ_PURGE 1 /* purge all packets */ #define ALTQ_IS_READY(ifq) ((ifq)->altq_flags & ALTQF_READY) +#ifdef ALTQ #define ALTQ_IS_ENABLED(ifq) ((ifq)->altq_flags & ALTQF_ENABLED) +#else +#define ALTQ_IS_ENABLED(ifq) 0 +#endif #define ALTQ_NEEDS_CLASSIFY(ifq) ((ifq)->altq_flags & ALTQF_CLASSIFY) #define ALTQ_IS_CNDTNING(ifq) ((ifq)->altq_flags & ALTQF_CNDTNING) Index: sys/net/iflib.c =================================================================== --- sys/net/iflib.c +++ sys/net/iflib.c @@ -739,6 +739,10 @@ static int iflib_legacy_setup(if_ctx_t ctx, driver_filter_t filter, void *filterarg, int *rid, const char *str); static void iflib_txq_check_drain(iflib_txq_t txq, int budget); static uint32_t iflib_txq_can_drain(struct ifmp_ring *); +#ifdef ALTQ +static void iflib_altq_if_start(if_t ifp); +static int iflib_altq_if_transmit(if_t ifp, struct mbuf *m); +#endif static int iflib_register(if_ctx_t); static void iflib_init_locked(if_ctx_t ctx); static void iflib_add_device_sysctl_pre(if_ctx_t ctx); @@ -3774,6 +3778,10 @@ IFDI_TX_QUEUE_INTR_ENABLE(ctx, txq->ift_id); return; } +#ifdef ALTQ + if (ALTQ_IS_ENABLED(&ifp->if_snd)) + iflib_altq_if_start(ifp); +#endif if (txq->ift_db_pending) ifmp_ring_enqueue(txq->ift_br, (void **)&txq, 1, TX_BATCH_SIZE, abdicate); else if (!abdicate) @@ -3962,8 +3970,9 @@ } MPASS(m->m_nextpkt == NULL); + /* ALTQ-enabled interfaces always use queue 0. */ qidx = 0; - if ((NTXQSETS(ctx) > 1) && M_HASHTYPE_GET(m)) + if ((NTXQSETS(ctx) > 1) && M_HASHTYPE_GET(m) && !ALTQ_IS_ENABLED(&ifp->if_snd)) qidx = QIDX(ctx, m); /* * XXX calculate buf_ring based on flowid (divvy up bits?) @@ -4023,7 +4032,60 @@ return (err); } +#ifdef ALTQ +/* + * The overall approach to integrating iflib with ALTQ is to continue to use + * the iflib mp_ring machinery between the ALTQ queue(s) and the hardware + * ring. Technically, when using ALTQ, queueing to an intermediate mp_ring + * is redundant/unnecessary, but doing so minmizes the amount of + * ALTQ-specific code required in iflib. It is assumed that the overhead of + * redundantly queuing to an intermediate mp_ring is swamped by the + * performance limitations inherent in using ALTQ. + * + * When ALTQ support is compiled in, all iflib drivers will use a transmit + * routine, iflib_altq_if_transmit(), that checks if ALTQ is enabled for the + * given interface. If ALTQ is enabled for an interface, then all + * transmitted packets for that interface will be submitted to the ALTQ + * subsystem via IFQ_ENQUEUE(). We don't use the legacy if_transmit() + * implementation because it uses IFQ_HANDOFF(), which will duplicatively + * update stats that the iflib machinery handles, and which is sensitve to + * the disused IFF_DRV_OACTIVE flag. Additionally, iflib_altq_if_start() + * will be installed as the start routine for use by ALTQ facilities that + * need to trigger queue drains on a scheduled basis. + * + */ static void +iflib_altq_if_start(if_t ifp) +{ + struct ifaltq *ifq = &ifp->if_snd; + struct mbuf *m; + + IFQ_LOCK(ifq); + IFQ_DEQUEUE_NOLOCK(ifq, m); + while (m != NULL) { + iflib_if_transmit(ifp, m); + IFQ_DEQUEUE_NOLOCK(ifq, m); + } + IFQ_UNLOCK(ifq); +} + +static int +iflib_altq_if_transmit(if_t ifp, struct mbuf *m) +{ + int err; + + if (ALTQ_IS_ENABLED(&ifp->if_snd)) { + IFQ_ENQUEUE(&ifp->if_snd, m, err); + if (err == 0) + iflib_altq_if_start(ifp); + } else + err = iflib_if_transmit(ifp, m); + + return (err); +} +#endif /* ALTQ */ + +static void iflib_if_qflush(if_t ifp) { if_ctx_t ctx = if_getsoftc(ifp); @@ -4040,6 +4102,10 @@ ctx->ifc_flags &= ~IFC_QFLUSH; STATE_UNLOCK(ctx); + /* + * When ALTQ is enabled, this will also take care of purging the + * ALTQ queue(s). + */ if_qflush(ifp); } @@ -5164,7 +5230,12 @@ if_setdev(ifp, dev); if_setinitfn(ifp, iflib_if_init); if_setioctlfn(ifp, iflib_if_ioctl); +#ifdef ALTQ + if_setstartfn(ifp, iflib_altq_if_start); + if_settransmitfn(ifp, iflib_altq_if_transmit); +#else if_settransmitfn(ifp, iflib_if_transmit); +#endif if_setqflushfn(ifp, iflib_if_qflush); if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);