From 9707bc985f32ec71e2fc38ca2b6d964b15db169d Mon Sep 17 00:00:00 2001 From: "randomizedcoder dave.seddon.ca@gmail.com" Date: Sat, 31 Oct 2026 09:15:18 -0700 Subject: [PATCH] Annotate lockless reads in homa_qdisc_deferred_offset homa_qdisc_deferred_offset reads the length of an RPC's deferred packet queue and its tx_left without holding qdev->defer_lock, while the pacer modifies both under that lock. The race is intentional, but the reads were plain loads, which KCSAN reports as a data race. Use skb_queue_len_lockless and READ_ONCE/WRITE_ONCE. Co-Authored-By: Claude Opus 5.5 --- homa_qdisc.c | 4 ++-- homa_qdisc.h | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/homa_qdisc.c b/homa_qdisc.c index b84ef03c..a352faa9 100755 --- a/homa_qdisc.c +++ b/homa_qdisc.c @@ -753,7 +753,7 @@ void homa_qdisc_defer_homa(struct homa_qdisc_dev *qdev, struct sk_buff *skb) h = (struct homa_data_hdr *)skb_transport_header(skb); bytes_left = rpc->msgout.length - ntohl(h->seg.offset); if (bytes_left < rpc->qrpc.tx_left) - rpc->qrpc.tx_left = bytes_left; + WRITE_ONCE(rpc->qrpc.tx_left, bytes_left); rpc->qrpc.qdev = qdev; homa_qdisc_insert_rb(qdev, rpc); } @@ -950,7 +950,7 @@ struct sk_buff *homa_qdisc_get_deferred_homa(struct homa_qdisc_dev *qdev) bytes_left = rpc->msgout.length - (ntohl(h->seg.offset) + info->data_bytes); if (bytes_left < qrpc->tx_left) - qrpc->tx_left = bytes_left; + WRITE_ONCE(qrpc->tx_left, bytes_left); if (fifo) { if (skb_queue_len(&qrpc->packets) > 0) { rb_erase_cached(node, &qdev->deferred_rpcs); diff --git a/homa_qdisc.h b/homa_qdisc.h index c5451644..eedd1e6b 100644 --- a/homa_qdisc.h +++ b/homa_qdisc.h @@ -484,10 +484,11 @@ static inline bool homa_qdisc_precedes(struct homa_rpc *rpc1, static inline int homa_qdisc_deferred_offset(struct homa_rpc *rpc) { /* This doesn't synchronize, but the worst that can happen is - * to return a slightly out-of-date result. + * to return a slightly out-of-date result. The pacer modifies + * these fields concurrently, under qdev->defer_lock. */ - if (skb_queue_len(&rpc->qrpc.packets) > 0) - return rpc->msgout.length - rpc->qrpc.tx_left; + if (skb_queue_len_lockless(&rpc->qrpc.packets) > 0) + return rpc->msgout.length - READ_ONCE(rpc->qrpc.tx_left); return -1; }