| 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 |