From: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
To: netdev-bot+sashiko@kernel.org, willemdebruijn.kernel@gmail.com
Cc: netdev@vger.kernel.org, davem@davemloft.net, kuba@kernel.org,
edumazet@google.com, pabeni@redhat.com, horms@kernel.org,
andrew+netdev@lunn.ch, willemb@google.com
Subject: Re: [PATCH net-next v9 3/7] net_sched: sch_fq: clear past skb->tstamp if offloading pacing
Date: Fri, 11 Sep 2026 20:36:37 -0400 [thread overview]
Message-ID: <willemdebruijn.kernel.1571be0acb9a@gmail.com> (raw)
In-Reply-To: <178914679635.219967.4563834866783696110@kernel.org>
netdev-bot+sashiko@ wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential
> issue(s) to consider:
>
> Critical: 0 · High: 0 · Medium: 1 · Low: 1
>
> - [Medium] The two new clearing sites use different predicates, and the
> dequeue-side one folds in a schedule the offloading device never sees.
> - [Low] Incomplete coverage of the new "clear an expired EDT before
> handing the skb to a pacing-offload device" logic on FQ's internal…
>
> --
>
> net_sched: sch_fq: clear past skb->tstamp if offloading pacing
>
> When pacing offload is active, sch_fq now clears skb->tstamp for packets
> whose EDT has already passed (or is within a 400 nsec slack window) so
> the device does not have to compare the timestamp against the current
> time. The clearing is done in two places: in fq_classify() for fastpath
> packets, and at the out: label of fq_dequeue() for regular flows.
>
> A couple of questions about the coverage of the new clearing below.
>
> > diff --git a/net/sched/sch_fq.c b/net/sched/sch_fq.c
> > index dfc7f98b600f8..a282812c192ee 100644
> > --- a/net/sched/sch_fq.c
> > +++ b/net/sched/sch_fq.c
> > @@ -159,6 +159,9 @@ struct fq_sched_data {
> > u64 stat_allocation_errors;
> > };
> >
> > +/* EDT timestamps to clear beyond now. */
> > +static const int fq_offload_slack_ns = 400;
> > +
> > /* return the i-th 2-bit value ("crumb") */
> > static u8 fq_prio2band(const u8 *prio2band, unsigned int prio)
> > {
> > @@ -421,6 +424,10 @@ static struct fq_flow *fq_classify(struct Qdisc *sch, struct sk_buff *skb,
> > smp_store_release(&sk->sk_pacing_status,
> > SK_PACING_FQ);
> >
> > + if (offload_horizon &&
> > + fq_skb_cb(skb)->time_to_send <= now + fq_offload_slack_ns)
> > + skb_set_delivery_time(skb, 0, SKB_CLOCK_REALTIME);
> > +
> > return &q->internal;
> > }
> >
>
> [ ... ]
>
> > @@ -745,7 +753,7 @@ static struct sk_buff *fq_dequeue(struct Qdisc *sch)
> > if (skb) {
> > q->internal.qlen--;
> > fq_dequeue_skb(sch, &q->internal, skb);
> > - goto out;
> > + return skb;
> > }
> >
> > now = ktime_get_ns();
>
> [Severity: Low]
> For packets on q->internal the clearing decision is now taken only once,
> at enqueue time in fq_classify(), using the enqueue-time now. With the
> goto out changed to return skb, internal-queue packets no longer reach the
> new block at the out: label, and they are returned before now is even
> sampled.
>
> fq_fastpath_check() admits a packet whose txtime is up to offload_horizon
> in the future:
>
> if (fq_skb_cb(skb)->time_to_send > now + offload_horizon)
> return false;
>
> and the internal queue is not single-slot:
>
> if (q->internal.qlen >= 8)
> return false;
>
> So a packet can be admitted with a future txtime, sit behind up to seven
> others (or behind a stopped tx queue), and by the time fq_dequeue()
> returns it the txtime has expired, yet skb->tstamp is still set and the
> device performs exactly the future-time comparison the change is meant to
> avoid. Is that gap intentional, or should the internal-queue path also
> re-test at dequeue?
Testing fast path only in fq_enqueue is intentional.
> Relatedly, the second exit to the internal queue in fq_classify(), the
> allocation failure path:
>
> f = kmem_cache_zalloc(fq_flow_cachep, GFP_ATOMIC | __GFP_NOWARN);
> if (unlikely(!f)) {
> q->stat_allocation_errors++;
> return &q->internal;
> }
>
> does no clearing at all, so even an already-expired EDT survives to the
> device there. Should that path get the same treatment?
>
> The predicate itself is conservative (nothing more than
> fq_offload_slack_ns in the future is ever cleared), so this looks like a
> missed optimization rather than a wrong transmit time, but the commit
> message reads as though all packets whose txtime has passed get
> skb->tstamp cleared.
>
> > @@ -782,8 +790,8 @@ static struct sk_buff *fq_dequeue(struct Qdisc *sch)
> >
> > skb = fq_peek(f);
> > if (skb) {
> > - u64 time_next_packet = max_t(u64, fq_skb_cb(skb)->time_to_send,
> > - f->time_next_packet);
> > + time_next_packet = max_t(u64, fq_skb_cb(skb)->time_to_send,
> > + f->time_next_packet);
> >
> > if (now + offload_horizon < time_next_packet) {
> > head->first = f->next;
>
> [ ... ]
>
> > @@ -862,6 +870,10 @@ static struct sk_buff *fq_dequeue(struct Qdisc *sch)
> > }
> >
> > out:
> > + if (offload_horizon &&
> > + time_next_packet && time_next_packet <= now + fq_offload_slack_ns)
> > + skb_set_delivery_time(skb, 0, SKB_CLOCK_REALTIME);
> > +
> > return skb;
> > }
>
> [Severity: Medium]
> Should this test the packet's own txtime rather than time_next_packet?
EDT calculations in FQ must use time_next_packet over skb->tstamp.
> The fq_classify() site uses fq_skb_cb(skb)->time_to_send, while here
> time_next_packet is:
>
> time_next_packet = max_t(u64, fq_skb_cb(skb)->time_to_send,
> f->time_next_packet);
>
> f->time_next_packet is FQ's own rate-pacing schedule. Grepping
> net/sched/sch_fq.c, skb->tstamp is only ever written by
> fq_mono_to_skb_tstamp() on the fq_enqueue() horizon-cap path, so
> f->time_next_packet is never propagated into skb->tstamp and the device
> never sees it.
>
> The effect shows up when FQ enforces its own rate, e.g.:
>
> tc qdisc replace dev X root fq maxrate 1gbit offload_horizon <= \
> dev->max_pacing_offload_horizon
>
> For an EDT packet the branch that consults sk_pacing_rate is skipped:
>
> if (!skb->tstamp) {
> if (skb->sk)
> rate = min(READ_ONCE(skb->sk->sk_pacing_rate), rate);
>
> so rate stays at q->flow_max_rate and the previous dequeue set
> f->time_next_packet = now + len. The flow is then throttled until
> f->time_next_packet - offload_horizon, so on release:
>
> now + 400 < f->time_next_packet <= now + offload_horizon
>
> and the predicate is false even though fq_skb_cb(skb)->time_to_send is
> already well in the past. In that configuration the clearing never
> happens, while the fq_classify() peer site would have cleared the same
> packet. Note fq_fastpath_check() rejects the fastpath when
> q->flow_max_rate != ~0UL, so maxrate flows always take this path.
>
> Since time_next_packet is always >= time_to_send, nothing with a genuinely
> future EDT gets cleared, so this looks like a silently ineffective
> optimization rather than a correctness problem. Was using
The interplay of EDT and FQ maxrate is complex. A "silently
ineffective optimization" is acceptable.
> fq_skb_cb(skb)->time_to_send here considered, to keep the two sites
> consistent?
>
> --
> Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260910171131.2532487-1-willemdebruijn.kernel%40gmail.com
next prev parent reply other threads:[~2026-09-12 0:36 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-10 17:10 [PATCH net-next v9 0/7] hardware pacing offload Willem de Bruijn
2026-09-10 17:10 ` [PATCH net-next v9 1/7] net: rtnetlink: add pacing_offload attribute to net_device Willem de Bruijn
2026-09-11 17:13 ` netdev-bot+sashiko
2026-09-12 0:25 ` Willem de Bruijn
2026-09-10 17:10 ` [PATCH net-next v9 2/7] net_sched: sch_fq: check device pacing offload Willem de Bruijn
2026-09-11 17:13 ` netdev-bot+sashiko
2026-09-12 0:33 ` Willem de Bruijn
2026-09-10 17:10 ` [PATCH net-next v9 3/7] net_sched: sch_fq: clear past skb->tstamp if offloading pacing Willem de Bruijn
2026-09-11 17:13 ` netdev-bot+sashiko
2026-09-12 0:36 ` Willem de Bruijn [this message]
2026-09-10 17:10 ` [PATCH net-next v9 4/7] idpf: support pacing offload Willem de Bruijn
2026-09-11 17:13 ` netdev-bot+sashiko
2026-09-12 0:46 ` Willem de Bruijn
2026-09-10 17:10 ` [PATCH net-next v9 5/7] selftests: drv-net: refactor so_txtime errqueue handling Willem de Bruijn
2026-09-10 17:10 ` [PATCH net-next v9 6/7] selftests: drv-net: in so_txtime tell apart sw from hw pacing Willem de Bruijn
2026-09-11 17:13 ` netdev-bot+sashiko
2026-09-12 0:47 ` Willem de Bruijn
2026-09-10 17:10 ` [PATCH net-next v9 7/7] selftests: drv-net: extend so_txtime with hw offload Willem de Bruijn
2026-09-11 17:13 ` netdev-bot+sashiko
2026-09-12 0:57 ` Willem de Bruijn
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=willemdebruijn.kernel.1571be0acb9a@gmail.com \
--to=willemdebruijn.kernel@gmail.com \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=netdev-bot+sashiko@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=willemb@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.