Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

net/sched: sch_red: annotate data-races in red_dump_stats()

red_dump_stats() only runs with RTNL held,
reading fields that can be changed in qdisc fast path.

Add READ_ONCE()/WRITE_ONCE() annotations.

Alternative would be to acquire the qdisc spinlock, but our long-term
goal is to make qdisc dump operations lockless as much as we can.

tc_red_xstats fields don't need to be latched atomically,
otherwise this bug would have been caught earlier.

Fixes: edb09eb17ed8 ("net: sched: do not acquire qdisc spinlock in qdisc/class stats dump")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Jamal Hadi Salim <jhs@mojatatu.com>
Link: https://patch.msgid.link/20260421142309.3964322-1-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Eric Dumazet and committed by
Jakub Kicinski
a8f51928 bbfaa73e

+21 -10
+21 -10
net/sched/sch_red.c
··· 90 90 case RED_PROB_MARK: 91 91 qdisc_qstats_overlimit(sch); 92 92 if (!red_use_ecn(q)) { 93 - q->stats.prob_drop++; 93 + WRITE_ONCE(q->stats.prob_drop, 94 + q->stats.prob_drop + 1); 94 95 goto congestion_drop; 95 96 } 96 97 97 98 if (INET_ECN_set_ce(skb)) { 98 - q->stats.prob_mark++; 99 + WRITE_ONCE(q->stats.prob_mark, 100 + q->stats.prob_mark + 1); 99 101 skb = tcf_qevent_handle(&q->qe_mark, sch, skb, to_free, &ret); 100 102 if (!skb) 101 103 return NET_XMIT_CN | ret; 102 104 } else if (!red_use_nodrop(q)) { 103 - q->stats.prob_drop++; 105 + WRITE_ONCE(q->stats.prob_drop, 106 + q->stats.prob_drop + 1); 104 107 goto congestion_drop; 105 108 } 106 109 ··· 114 111 reason = QDISC_DROP_OVERLIMIT; 115 112 qdisc_qstats_overlimit(sch); 116 113 if (red_use_harddrop(q) || !red_use_ecn(q)) { 117 - q->stats.forced_drop++; 114 + WRITE_ONCE(q->stats.forced_drop, 115 + q->stats.forced_drop + 1); 118 116 goto congestion_drop; 119 117 } 120 118 121 119 if (INET_ECN_set_ce(skb)) { 122 - q->stats.forced_mark++; 120 + WRITE_ONCE(q->stats.forced_mark, 121 + q->stats.forced_mark + 1); 123 122 skb = tcf_qevent_handle(&q->qe_mark, sch, skb, to_free, &ret); 124 123 if (!skb) 125 124 return NET_XMIT_CN | ret; 126 125 } else if (!red_use_nodrop(q)) { 127 - q->stats.forced_drop++; 126 + WRITE_ONCE(q->stats.forced_drop, 127 + q->stats.forced_drop + 1); 128 128 goto congestion_drop; 129 129 } 130 130 ··· 141 135 sch->qstats.backlog += len; 142 136 sch->q.qlen++; 143 137 } else if (net_xmit_drop_count(ret)) { 144 - q->stats.pdrop++; 138 + WRITE_ONCE(q->stats.pdrop, 139 + q->stats.pdrop + 1); 145 140 qdisc_qstats_drop(sch); 146 141 } 147 142 return ret; ··· 470 463 dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_RED, 471 464 &hw_stats_request); 472 465 } 473 - st.early = q->stats.prob_drop + q->stats.forced_drop; 474 - st.pdrop = q->stats.pdrop; 475 - st.marked = q->stats.prob_mark + q->stats.forced_mark; 466 + st.early = READ_ONCE(q->stats.prob_drop) + 467 + READ_ONCE(q->stats.forced_drop); 468 + 469 + st.pdrop = READ_ONCE(q->stats.pdrop); 470 + 471 + st.marked = READ_ONCE(q->stats.prob_mark) + 472 + READ_ONCE(q->stats.forced_mark); 476 473 477 474 return gnet_stats_copy_app(d, &st, sizeof(st)); 478 475 }