Priority queueing is the default policy in OpenBSD and it
distributes outgoing packets in 8 lists by priority (0-7) with
an aggregate queue depth set by the interface: pseudo interfaces
use IFQ_MAXLEN defined equal to 256, hardware device drivers
normally size it by their TX ring minus 1 (therefore 127, 255,
511 are common values).

Unless a producer generating packets with altered priority is
used (such as "set prio" pf directive, PPPoE management frames,
ping -T lowdelay, VLAN priority, and so on) all outgoing traffic
is sent with a priority of 3 hitting the same list.

The drop policy used here is called tail drop because it drops
the packet that we're trying to enqueue when there's no more
space left on the queue.  The obvious downside is that if our
queue is full of packets representing low priority traffic,
trying to enqueue a packet with a higher priority will still
result in a drop.  In my opinion, this defeats the purpose of
priority queueing.

The diff below changes the policy to a head drop from the queue
with the lowest priority than the packet we're trying to
enqueue.  If there's no such queue (e.g. the default case where
all traffic has priority of 3) only then the packet is dropped.
This ensures that high priority traffic will almost always find
the place on the queue and low priority bulk traffic gets a
better chance at regulating its throughput.  By performing a
head drop instead a tail drop we also drop the oldest packet on
the queue.  This technique is akin to Active Queue Management
algorithms.

I'd like to stress again, that this doesn't change much for the
default Ethernet-to-Ethernet case, but provides noticeable
difference if different priorities are actually used, e.g. via
pf.

More tests are always welcome.  This should go on top of the priq
mbuf list diff, but here's a combined diff for convenience:
http://gir.theapt.org/~mike/priq.diff

---
 sys/net/ifq.c | 23 ++++++++++++++++++++---
 1 file changed, 20 insertions(+), 3 deletions(-)

diff --git sys/net/ifq.c sys/net/ifq.c
index 896b373c454..f678c2b01fd 100644
--- sys/net/ifq.c
+++ sys/net/ifq.c
@@ -407,18 +407,35 @@ priq_free(unsigned int idx, void *pq)
 int
 priq_enq(struct ifqueue *ifq, struct mbuf *m)
 {
        struct priq *pq;
        struct mbuf_list *pl;
-
-       if (ifq_len(ifq) >= ifq->ifq_maxlen)
-               return (ENOBUFS);
+       unsigned int prio;
 
        pq = ifq->ifq_q;
        KASSERT(m->m_pkthdr.pf.prio <= IFQ_MAXPRIO);
        pl = &pq->pq_lists[m->m_pkthdr.pf.prio];
 
+       /* Find a lower priority queue to drop from */
+       if (ifq_len(ifq) >= ifq->ifq_maxlen) {
+               for (prio = 0; prio < m->m_pkthdr.pf.prio; prio++) {
+                       pl = &pq->pq_lists[prio];
+                       if (ml_len(pl) > 0) {
+                               m_freem(ml_dequeue(pl));
+                               ifq->ifq_len--;
+                               ifq->ifq_qdrops++;
+                               break;
+                       }
+               }
+               /*
+                * There's no lower priority queue that we can
+                * drop from so don't enqueue this one.
+                */
+               if (prio == m->m_pkthdr.pf.prio)
+                       return (ENOBUFS);
+       }
+
        ml_enqueue(pl, m);
 
        return (0);
 }
 
-- 
2.12.0

Reply via email to