ifq: real ifq_start (drain+transmit), dequeue cookie fix, pf_kifq_input hook in the data path; 14/14 suite unchanged

This commit is contained in:
lgcosta committed 2026-11-12 01:15:26 -03:00
1 parent f34c4f92a9
commit 14ff7d1c39
5 files changed
+94 -1

No files matched your search

+38 -1
View File
@@ -360,14 +360,16 @@ ifq_deq_begin(struct ifqueue *ifq)
if (!ifq->ifq_ops || !ifq->ifq_ops->ifqop_deq_begin)
return NULL;
m = ifq->ifq_ops->ifqop_deq_begin(ifq, &cookie);
ifq->ifq_dequeue = cookie;
return m;
}
void
ifq_deq_commit(struct ifqueue *ifq, struct mbuf *m)
{
void *cookie = NULL;
void *cookie = ifq->ifq_dequeue;
ifq->ifq_dequeue = NULL;
if (ifq->ifq_ops && ifq->ifq_ops->ifqop_deq_commit)
ifq->ifq_ops->ifqop_deq_commit(ifq, m, cookie);
}
@@ -389,12 +391,47 @@ ifq_dequeue(struct ifqueue *ifq)
return m;
}
/*
* Drain the queue as far as the scheduler allows and hand each packet to the
* stack's output path. OpenBSD calls if_start from softint context; here it
* runs either from the pf work queue (enqueue side) or from the scheduler's
* deferred timeout (pacing side), which is what makes hfsc's bandwidth
* limits take effect.
*/
void
ifq_start(struct ifqueue *ifq)
{
struct mbuf *m;
ifq->ifq_oactive = 1;
while ((m = ifq_dequeue(ifq)) != NULL) {
if (pfk_ifq_transmit(ifq, m))
break; /* device busy: packet kept */
}
ifq->ifq_oactive = 0;
}
/*
* pf data-path entry: hand a packet to the interface's scheduler when one is
* attached (a pf `queue' rule is configured). Returns 1 when the queue took
* ownership of m, 0 when the caller must keep going (priq/no scheduler).
*/
int
pf_kifq_input(struct pfk_ifnet *ifp, struct mbuf *m)
{
struct ifqueue *ifq;
if (ifp == NULL || m == NULL)
return 0;
ifq = &ifp->if_snd;
if (ifq->ifq_ops == NULL || ifq->ifq_ops == ifq_priq_ops)
return 0; /* no scheduler: fast path */
if (ifq_enqueue(ifq, m) != 0)
return 1; /* dropped by the queue: owned */
ifq_start(ifq);
return 1;
}
unsigned int
ifq_purge(struct ifqueue *ifq)
{
+37
View File
@@ -270,3 +270,40 @@ pfk_if_output(struct pfk_ifnet *ifp, struct mbuf *m, void *dst,
m_freem(mp);
return err;
}
/*
* Transmit one queued packet. Called from ifq_start() - i.e. from the pf
* work queue or from a scheduler's deferred timeout - and always consumes the
* mbuf. Returns 0 (keep draining); a scheduler that wants to defer does so by
* simply not returning a packet from ifqop_deq_begin().
*/
int
pfk_ifq_transmit(struct ifqueue *ifq, struct mbuf *m)
{
struct sk_buff *skb = m_to_skb(m);
struct dst_entry *d;
(void)ifq;
if (skb == NULL) {
m_freem(m);
return 0;
}
d = skb_dst(skb);
if (d == NULL || IS_ERR(d) || d->dev == NULL || !netif_running(d->dev)) {
m_freem(m);
return 0;
}
skb->dev = d->dev;
skb_reset_network_header(skb);
if (skb->protocol == 0)
skb->protocol = htons(ip_hdr(skb)->protocol == IPPROTO_IPV6 ?
ETH_P_IPV6 : ETH_P_IP);
/* the stack owns skb->cb (IPCB) from here; pf already tested it */
memset(skb->cb, 0, sizeof(skb->cb));
skb->mark |= PFK_DONE_OUT;
skb_get(skb); /* the output path consumes one reference */
m->skb = NULL; /* the mbuf's reference moves to the stack */
m_freem(m);
d->output(&init_net, NULL, skb);
return 0;
}
+1
View File
@@ -27,6 +27,7 @@ struct ifqueue {
struct mbuf_list *ifq_free; /* free list (unused in the port) */
unsigned int ifq_len;
unsigned int ifq_oactive;
void *ifq_dequeue; /* cookie between deq_begin/commit */
/* statistics */
u_int64_t ifq_packets;
+2
View File
@@ -1237,6 +1237,8 @@ struct fqcodel_stats {
* queue when it owns the interface queue, NULL otherwise. */
void *ifq_q_enter(struct ifqueue *, const struct ifq_ops *);
void ifq_q_leave(struct ifqueue *, void *);
int pfk_ifq_transmit(struct ifqueue *, struct mbuf *);
int pf_kifq_input(struct pfk_ifnet *, struct mbuf *);
/*
+16
View File
@@ -204,6 +204,22 @@ pfk_pkt_work(struct work_struct *w)
if (m == m0) {
/*
* Queueing: when a pf `queue' rule attached a scheduler to the
* egress interface, hand the packet to it instead of sending it
* straight out - this is what makes bandwidth limits real.
*/
if (action == PF_PASS && pk->dir == PF_OUT &&
pskb_may_pull(pk->skb, sizeof(struct iphdr)) &&
skb_dst(pk->skb) && !IS_ERR(skb_dst(pk->skb)) &&
pf_kifq_input(skb_dst(pk->skb)->dev ?
pfk_ifp_bydev(skb_dst(pk->skb)->dev) : NULL, m)) {
orig = pk->skb;
pk->skb = NULL; /* the queue owns the packet */
kfree_skb(orig);
kfree(pk);
return;
}
/* same buffer, possibly rewritten in place: our wrapper
* held one extra skb reference — release it */
m_freem(m);