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_sfb: annotate data-races in sfb_dump_stats()

sfb_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_sfb_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>
Link: https://patch.msgid.link/20260421141655.3953721-1-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Eric Dumazet and committed by
Jakub Kicinski
1ada03fd a8f51928

+32 -22
+32 -22
net/sched/sch_sfb.c
··· 130 130 131 131 sfbhash >>= SFB_BUCKET_SHIFT; 132 132 if (b[hash].qlen < 0xFFFF) 133 - b[hash].qlen++; 133 + WRITE_ONCE(b[hash].qlen, b[hash].qlen + 1); 134 134 b += SFB_NUMBUCKETS; /* next level */ 135 135 } 136 136 } ··· 159 159 160 160 sfbhash >>= SFB_BUCKET_SHIFT; 161 161 if (b[hash].qlen > 0) 162 - b[hash].qlen--; 162 + WRITE_ONCE(b[hash].qlen, b[hash].qlen - 1); 163 163 b += SFB_NUMBUCKETS; /* next level */ 164 164 } 165 165 } ··· 179 179 180 180 static void decrement_prob(struct sfb_bucket *b, struct sfb_sched_data *q) 181 181 { 182 - b->p_mark = prob_minus(b->p_mark, q->decrement); 182 + WRITE_ONCE(b->p_mark, prob_minus(b->p_mark, q->decrement)); 183 183 } 184 184 185 185 static void increment_prob(struct sfb_bucket *b, struct sfb_sched_data *q) 186 186 { 187 - b->p_mark = prob_plus(b->p_mark, q->increment); 187 + WRITE_ONCE(b->p_mark, prob_plus(b->p_mark, q->increment)); 188 188 } 189 189 190 190 static void sfb_zero_all_buckets(struct sfb_sched_data *q) ··· 202 202 const struct sfb_bucket *b = &q->bins[q->slot].bins[0][0]; 203 203 204 204 for (i = 0; i < SFB_LEVELS * SFB_NUMBUCKETS; i++) { 205 - if (qlen < b->qlen) 206 - qlen = b->qlen; 207 - totalpm += b->p_mark; 208 - if (prob < b->p_mark) 209 - prob = b->p_mark; 205 + u32 b_qlen = READ_ONCE(b->qlen); 206 + u32 b_mark = READ_ONCE(b->p_mark); 207 + 208 + if (qlen < b_qlen) 209 + qlen = b_qlen; 210 + totalpm += b_mark; 211 + if (prob < b_mark) 212 + prob = b_mark; 210 213 b++; 211 214 } 212 215 *prob_r = prob; ··· 298 295 299 296 if (unlikely(sch->q.qlen >= q->limit)) { 300 297 qdisc_qstats_overlimit(sch); 301 - q->stats.queuedrop++; 298 + WRITE_ONCE(q->stats.queuedrop, 299 + q->stats.queuedrop + 1); 302 300 goto drop; 303 301 } 304 302 ··· 352 348 353 349 if (unlikely(minqlen >= q->max)) { 354 350 qdisc_qstats_overlimit(sch); 355 - q->stats.bucketdrop++; 351 + WRITE_ONCE(q->stats.bucketdrop, 352 + q->stats.bucketdrop + 1); 356 353 goto drop; 357 354 } 358 355 ··· 379 374 } 380 375 if (sfb_rate_limit(skb, q)) { 381 376 qdisc_qstats_overlimit(sch); 382 - q->stats.penaltydrop++; 377 + WRITE_ONCE(q->stats.penaltydrop, 378 + q->stats.penaltydrop + 1); 383 379 goto drop; 384 380 } 385 381 goto enqueue; ··· 396 390 * In either case, we want to start dropping packets. 397 391 */ 398 392 if (r < (p_min - SFB_MAX_PROB / 2) * 2) { 399 - q->stats.earlydrop++; 393 + WRITE_ONCE(q->stats.earlydrop, 394 + q->stats.earlydrop + 1); 400 395 goto drop; 401 396 } 402 397 } 403 398 if (INET_ECN_set_ce(skb)) { 404 - q->stats.marked++; 399 + WRITE_ONCE(q->stats.marked, 400 + q->stats.marked + 1); 405 401 } else { 406 - q->stats.earlydrop++; 402 + WRITE_ONCE(q->stats.earlydrop, 403 + q->stats.earlydrop + 1); 407 404 goto drop; 408 405 } 409 406 } ··· 419 410 sch->q.qlen++; 420 411 increment_qlen(&cb, q); 421 412 } else if (net_xmit_drop_count(ret)) { 422 - q->stats.childdrop++; 413 + WRITE_ONCE(q->stats.childdrop, 414 + q->stats.childdrop + 1); 423 415 qdisc_qstats_drop(sch); 424 416 } 425 417 return ret; ··· 609 599 { 610 600 struct sfb_sched_data *q = qdisc_priv(sch); 611 601 struct tc_sfb_xstats st = { 612 - .earlydrop = q->stats.earlydrop, 613 - .penaltydrop = q->stats.penaltydrop, 614 - .bucketdrop = q->stats.bucketdrop, 615 - .queuedrop = q->stats.queuedrop, 616 - .childdrop = q->stats.childdrop, 617 - .marked = q->stats.marked, 602 + .earlydrop = READ_ONCE(q->stats.earlydrop), 603 + .penaltydrop = READ_ONCE(q->stats.penaltydrop), 604 + .bucketdrop = READ_ONCE(q->stats.bucketdrop), 605 + .queuedrop = READ_ONCE(q->stats.queuedrop), 606 + .childdrop = READ_ONCE(q->stats.childdrop), 607 + .marked = READ_ONCE(q->stats.marked), 618 608 }; 619 609 620 610 st.maxqlen = sfb_compute_qlen(&st.maxprob, &st.avgprob, q);