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_fq_codel: annotate data-races from fq_codel_dump_class_stats()

fq_codel_dump_class_stats() acquires qdisc spinlock only when requested
to follow flow->head chain.

As we did in sch_cake recently, add the missing READ_ONCE()/WRITE_ONCE()
annotations.

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/20260504163842.1162001-1-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Eric Dumazet and committed by
Jakub Kicinski
f83e07b2 40aa9fce

+20 -19
+20 -19
net/sched/sch_fq_codel.c
··· 117 117 { 118 118 struct sk_buff *skb = flow->head; 119 119 120 - flow->head = skb->next; 120 + WRITE_ONCE(flow->head, skb->next); 121 121 skb_mark_not_on_list(skb); 122 122 return skb; 123 123 } ··· 127 127 struct sk_buff *skb) 128 128 { 129 129 if (flow->head == NULL) 130 - flow->head = skb; 130 + WRITE_ONCE(flow->head, skb); 131 131 else 132 132 flow->tail->next = skb; 133 133 flow->tail = skb; ··· 173 173 } while (++i < max_packets && len < threshold); 174 174 175 175 /* Tell codel to increase its signal strength also */ 176 - flow->cvars.count += i; 177 - q->backlogs[idx] -= len; 176 + WRITE_ONCE(flow->cvars.count, flow->cvars.count + i); 177 + WRITE_ONCE(q->backlogs[idx], q->backlogs[idx] - len); 178 178 q->memory_usage -= mem; 179 179 sch->qstats.drops += i; 180 180 sch->qstats.backlog -= len; ··· 204 204 codel_set_enqueue_time(skb); 205 205 flow = &q->flows[idx]; 206 206 flow_queue_add(flow, skb); 207 - q->backlogs[idx] += qdisc_pkt_len(skb); 207 + WRITE_ONCE(q->backlogs[idx], q->backlogs[idx] + qdisc_pkt_len(skb)); 208 208 qdisc_qstats_backlog_inc(sch, skb); 209 209 210 210 if (list_empty(&flow->flowchain)) { 211 211 list_add_tail(&flow->flowchain, &q->new_flows); 212 212 q->new_flow_count++; 213 - flow->deficit = q->quantum; 213 + WRITE_ONCE(flow->deficit, q->quantum); 214 214 } 215 215 get_codel_cb(skb)->mem_usage = skb->truesize; 216 216 q->memory_usage += get_codel_cb(skb)->mem_usage; ··· 263 263 flow = container_of(vars, struct fq_codel_flow, cvars); 264 264 if (flow->head) { 265 265 skb = dequeue_head(flow); 266 - q->backlogs[flow - q->flows] -= qdisc_pkt_len(skb); 266 + WRITE_ONCE(q->backlogs[flow - q->flows], 267 + q->backlogs[flow - q->flows] - qdisc_pkt_len(skb)); 267 268 q->memory_usage -= get_codel_cb(skb)->mem_usage; 268 269 sch->q.qlen--; 269 270 sch->qstats.backlog -= qdisc_pkt_len(skb); ··· 297 296 flow = list_first_entry(head, struct fq_codel_flow, flowchain); 298 297 299 298 if (flow->deficit <= 0) { 300 - flow->deficit += q->quantum; 299 + WRITE_ONCE(flow->deficit, flow->deficit + q->quantum); 301 300 list_move_tail(&flow->flowchain, &q->old_flows); 302 301 goto begin; 303 302 } ··· 315 314 goto begin; 316 315 } 317 316 qdisc_bstats_update(sch, skb); 318 - flow->deficit -= qdisc_pkt_len(skb); 317 + WRITE_ONCE(flow->deficit, flow->deficit - qdisc_pkt_len(skb)); 319 318 320 319 if (q->cstats.drop_count) { 321 320 qdisc_tree_reduce_backlog(sch, q->cstats.drop_count, ··· 329 328 static void fq_codel_flow_purge(struct fq_codel_flow *flow) 330 329 { 331 330 rtnl_kfree_skbs(flow->head, flow->tail); 332 - flow->head = NULL; 331 + WRITE_ONCE(flow->head, NULL); 333 332 } 334 333 335 334 static void fq_codel_reset(struct Qdisc *sch) ··· 657 656 658 657 memset(&xstats, 0, sizeof(xstats)); 659 658 xstats.type = TCA_FQ_CODEL_XSTATS_CLASS; 660 - xstats.class_stats.deficit = flow->deficit; 659 + xstats.class_stats.deficit = READ_ONCE(flow->deficit); 661 660 xstats.class_stats.ldelay = 662 - codel_time_to_us(flow->cvars.ldelay); 663 - xstats.class_stats.count = flow->cvars.count; 664 - xstats.class_stats.lastcount = flow->cvars.lastcount; 665 - xstats.class_stats.dropping = flow->cvars.dropping; 666 - if (flow->cvars.dropping) { 667 - codel_tdiff_t delta = flow->cvars.drop_next - 661 + codel_time_to_us(READ_ONCE(flow->cvars.ldelay)); 662 + xstats.class_stats.count = READ_ONCE(flow->cvars.count); 663 + xstats.class_stats.lastcount = READ_ONCE(flow->cvars.lastcount); 664 + xstats.class_stats.dropping = READ_ONCE(flow->cvars.dropping); 665 + if (xstats.class_stats.dropping) { 666 + codel_tdiff_t delta = READ_ONCE(flow->cvars.drop_next) - 668 667 codel_get_time(); 669 668 670 669 xstats.class_stats.drop_next = (delta >= 0) ? 671 670 codel_time_to_us(delta) : 672 671 -codel_time_to_us(-delta); 673 672 } 674 - if (flow->head) { 673 + if (READ_ONCE(flow->head)) { 675 674 sch_tree_lock(sch); 676 675 skb = flow->head; 677 676 while (skb) { ··· 680 679 } 681 680 sch_tree_unlock(sch); 682 681 } 683 - qs.backlog = q->backlogs[idx]; 682 + qs.backlog = READ_ONCE(q->backlogs[idx]); 684 683 qs.drops = 0; 685 684 } 686 685 if (gnet_stats_copy_queue(d, NULL, &qs, qs.qlen) < 0)