ALTQ(9) | Kernel Developer's Manual | ALTQ(9) |
void
IFQ_ENQUEUE(struct ifaltq *ifq, struct mbuf *m, struct altq_pktattr *pattr, int err);
void
IFQ_DEQUEUE(struct ifaltq *ifq, struct mbuf *m);
void
IFQ_POLL(struct ifaltq *ifq, struct mbuf *m);
void
IFQ_PURGE(struct ifaltq *ifq);
void
IFQ_CLASSIFY(struct ifaltq *ifq, struct mbuf *m, int af, struct altq_pktattr *pattr);
void
IFQ_IS_EMPTY(struct ifaltq *ifq);
void
IFQ_SET_MAXLEN(struct ifaltq *ifq, int len);
void
IFQ_INC_LEN(struct ifaltq *ifq);
void
IFQ_DEC_LEN(struct ifaltq *ifq);
void
IFQ_INC_DROPS(struct ifaltq *ifq);
void
IFQ_SET_READY(struct ifaltq *ifq);
IFQ_ENQUEUE() enqueues a packet m to the queue ifq. The underlying queueing discipline may discard the packet. err is set to 0 on success, or ENOBUFS if the packet is discarded. m will be freed by the device driver on success or by the queueing discipline on failure, so that the caller should not touch m after calling IFQ_ENQUEUE().
IFQ_DEQUEUE() dequeues a packet from the queue. The dequeued packet is returned in m, or m is set to NULL if no packet is dequeued. The caller must always check m since a non-empty queue could return NULL under rate-limiting.
IFQ_POLL() returns the next packet without removing it from the queue. It is guaranteed by the underlying queueing discipline that IFQ_DEQUEUE() immediately after IFQ_POLL() returns the same packet.
IFQ_PURGE() discards all the packets in the queue. The purge operation is needed since a non-work conserving queue cannot be emptied by a dequeue loop.
IFQ_CLASSIFY() classifies a packet to a scheduling class, and returns the result in pattr.
IFQ_IS_EMPTY() can be used to check if the queue is empty. Note that IFQ_DEQUEUE() could still return NULL if the queueing discipline is non-work conserving.
IFQ_SET_MAXLEN() sets the queue length limit to the default FIFO queue.
IFQ_INC_LEN() and IFQ_DEC_LEN() increment or decrement the current queue length in packets.
IFQ_INC_DROPS() increments the drop counter and is equal to IF_DROP(). It is defined for naming consistency.
IFQ_SET_READY() sets a flag to indicate this driver is converted to use the new macros. ALTQ can be enabled only on interfaces with this flag.
##old-style## ##new-style## | struct ifqueue { | struct ifaltq { struct mbuf *ifq_head; | struct mbuf *ifq_head; struct mbuf *ifq_tail; | struct mbuf *ifq_tail; int ifq_len; | int ifq_len; int ifq_maxlen; | int ifq_maxlen; int ifq_drops; | int ifq_drops; }; | /* altq related fields */ | ...... | }; |The new structure replaces struct ifqueue in struct ifnet.
##old-style## ##new-style## | struct ifnet { | struct ifnet { .... | .... | struct ifqueue if_snd; | struct ifaltq if_snd; | .... | .... }; | }; |The (simplified) new IFQ_XXX() macros looks like:
#ifdef ALTQ #define IFQ_DEQUEUE(ifq, m) \ if (ALTQ_IS_ENABLED((ifq)) \ ALTQ_DEQUEUE((ifq), (m)); \ else \ IF_DEQUEUE((ifq), (m)); #else #define IFQ_DEQUEUE(ifq, m) IF_DEQUEUE((ifq), (m)); #endif
#define IFQ_ENQUEUE(ifq, m, pattr, err) \ do { \ if (ALTQ_IS_ENABLED((ifq))) \ ALTQ_ENQUEUE((ifq), (m), (pattr), (err)); \ else { \ if (IF_QFULL((ifq))) { \ m_freem((m)); \ (err) = ENOBUFS; \ } else { \ IF_ENQUEUE((ifq), (m)); \ (err) = 0; \ } \ } \ if ((err)) \ (ifq)->ifq_drops++; \ } while (/*CONSTCOND*/ 0)
IFQ_ENQUEUE() does the following:
The new style if_output() looks as follows:
##old-style## ##new-style## | int | int ether_output(ifp, m0, dst, rt0) | ether_output(ifp, m0, dst, rt0) { | { ...... | ...... | | mflags = m->m_flags; | len = m->m_pkthdr.len; s = splimp(); | s = splimp(); if (IF_QFULL(&ifp->if_snd)) { | IFQ_ENQUEUE(&ifp->if_snd, m, | NULL, error); IF_DROP(&ifp->if_snd); | if (error != 0) { splx(s); | splx(s); senderr(ENOBUFS); | return (error); } | } IF_ENQUEUE(&ifp->if_snd, m); | ifp->if_obytes += | ifp->if_obytes += len; m->m_pkthdr.len; | if (m->m_flags & M_MCAST) | if (mflags & M_MCAST) ifp->if_omcasts++; | ifp->if_omcasts++; | if ((ifp->if_flags & IFF_OACTIVE) | if ((ifp->if_flags & IFF_OACTIVE) == 0) | == 0) (*ifp->if_start)(ifp); | (*ifp->if_start)(ifp); splx(s); | splx(s); return (error); | return (error); | bad: | bad: if (m) | if (m) m_freem(m); | m_freem(m); return (error); | return (error); } | } |
int ether_output(ifp, m0, dst, rt0) { ...... struct altq_pktattr pktattr; ...... /* classify the packet before prepending link-headers */ IFQ_CLASSIFY(&ifp->if_snd, m, dst->sa_family, &pktattr); /* prepend link-level headers */ ...... IFQ_ENQUEUE(&ifp->if_snd, m, &pktattr, error); ...... }
Look for if_snd in the driver. You will probably need to make changes to the lines that include if_snd.
##old-style## ##new-style## | if (ifp->if_snd.ifq_head != NULL) | if (IFQ_IS_EMPTY(&ifp->if_snd) == 0) |Note that IFQ_POLL() can be used for the same purpose, but IFQ_POLL() could be costly for a complex scheduling algorithm since IFQ_POLL() needs to run the scheduling algorithm to select the next packet. On the other hand, IFQ_IS_EMPTY() checks only if there is any packet stored in the queue. Another difference is that even when IFQ_IS_EMPTY() is false, IFQ_DEQUEUE() could still return NULL if the queue is under rate-limiting.
##old-style## ##new-style## | IF_DEQUEUE(&ifp->if_snd, m); | IFQ_DEQUEUE(&ifp->if_snd, m); | if (m == NULL) | return; |A driver is supposed to call if_start() from transmission complete interrupts in order to trigger the next dequeue.
##old-style## ##new-style## | m = ifp->if_snd.ifq_head; | IFQ_POLL(&ifp->if_snd, m); if (m != NULL) { | if (m != NULL) { | /* use m to get resources */ | /* use m to get resources */ if (something goes wrong) | if (something goes wrong) return; | return; | IF_DEQUEUE(&ifp->if_snd, m); | IFQ_DEQUEUE(&ifp->if_snd, m); | /* kick the hardware */ | /* kick the hardware */ } | } |It is guaranteed that IFQ_DEQUEUE() immediately after IFQ_POLL() returns the same packet. Note that they need to be guarded by splimp() if called from outside of if_start().
##old-style## ##new-style## | IF_DEQUEUE(&ifp->if_snd, m); | IFQ_POLL(&ifp->if_snd, m); if (m != NULL) { | if (m != NULL) { | if (something_goes_wrong) { | if (something_goes_wrong) { IF_PREPEND(&ifp->if_snd, m); | return; | return; } | } | | /* at this point, the driver | * is committed to send this | * packet. | */ | IFQ_DEQUEUE(&ifp->if_snd, m); | /* kick the hardware */ | /* kick the hardware */ } | } |
##old-style## ##new-style## | while (ifp->if_snd.ifq_head != NULL) {| IFQ_PURGE(&ifp->if_snd); IF_DEQUEUE(&ifp->if_snd, m); | m_freem(m); | } | |
##old-style## ##new-style## | ifp->if_snd.ifq_maxlen = qsize; | IFQ_SET_MAXLEN(&ifp->if_snd, qsize); | IFQ_SET_READY(&ifp->if_snd); if_attach(ifp); | if_attach(ifp); |
##old-style## ##new-style## | IF_DROP(&ifp->if_snd); | IFQ_INC_DROPS(&ifp->if_snd); | ifp->if_snd.ifq_len++; | IFQ_INC_LEN(&ifp->if_snd); | ifp->if_snd.ifq_len--; | IFQ_DEC_LEN(&ifp->if_snd); |Some drivers instruct the hardware to invoke transmission complete interrupts only when it thinks necessary. Rate-limiting breaks its assumption.
struct sl_softc { struct ifnet sc_if; /* network-visible interface */ ... struct ifqueue sc_fastq; /* interactive output queue */ ... };The driver doesn't compile in the new model since it has the following line (if_snd is no longer a type of struct ifqueue).
struct ifqueue *ifq = &ifp->if_snd;A simple way is to use the original IF_XXX() macros for sc_fastq and use the new IFQ_XXX() macros for if_snd. The enqueue operation looks like:
##old-style## ##new-style## | struct ifqueue *ifq = &ifp->if_snd; | struct ifqueue *ifq = NULL; | if (ip->ip_tos & IPTOS_LOWDELAY) | if ((ip->ip_tos & IPTOS_LOWDELAY) && ifq = &sc->sc_fastq; | !ALTQ_IS_ENABLED(&sc->sc_if.if_snd)) { | ifq = &sc->sc_fastq; if (IF_QFULL(ifq)) { | if (IF_QFULL(ifq)) { IF_DROP(ifq); | IF_DROP(ifq); m_freem(m); | m_freem(m); splx(s); | error = ENOBUFS; sc->sc_if.if_oerrors++; | } else { return (ENOBUFS); | IF_ENQUEUE(ifq, m); } | error = 0; IF_ENQUEUE(ifq, m); | } | } else | IFQ_ENQUEUE(&sc->sc_if.if_snd, | m, NULL, error); | | if (error) { | splx(s); | sc->sc_if.if_oerrors++; | return (error); | } if ((sc->sc_oqlen = | if ((sc->sc_oqlen = sc->sc_ttyp->t_outq.c_cc) == 0) | sc->sc_ttyp->t_outq.c_cc) == 0) slstart(sc->sc_ttyp); | slstart(sc->sc_ttyp); splx(s); | splx(s); |The dequeue operations looks like:
##old-style## ##new-style## | s = splimp(); | s = splimp(); IF_DEQUEUE(&sc->sc_fastq, m); | IF_DEQUEUE(&sc->sc_fastq, m); if (m == NULL) | if (m == NULL) IF_DEQUEUE(&sc->sc_if.if_snd, m); | IFQ_DEQUEUE(&sc->sc_if.if_snd, m); splx(s); | splx(s); |
October 12, 2006 | NetBSD 6.1 |