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

pkt_sched: fq: do not hold qdisc lock while allocating memory

Resizing fq hash table allocates memory while holding qdisc spinlock,
with BH disabled.

This is definitely not good, as allocation might sleep.

We can drop the lock and get it when needed, we hold RTNL so no other
changes can happen at the same time.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Fixes: afe4fd062416 ("pkt_sched: fq: Fair Queue packet scheduler")
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Eric Dumazet and committed by
David S. Miller
2d8d40af d85ea93f

+15 -6
+15 -6
net/sched/sch_fq.c
··· 601 601 { 602 602 struct fq_sched_data *q = qdisc_priv(sch); 603 603 struct rb_root *array; 604 + void *old_fq_root; 604 605 u32 idx; 605 606 606 607 if (q->fq_root && log == q->fq_trees_log) ··· 616 615 for (idx = 0; idx < (1U << log); idx++) 617 616 array[idx] = RB_ROOT; 618 617 619 - if (q->fq_root) { 620 - fq_rehash(q, q->fq_root, q->fq_trees_log, array, log); 621 - fq_free(q->fq_root); 622 - } 618 + sch_tree_lock(sch); 619 + 620 + old_fq_root = q->fq_root; 621 + if (old_fq_root) 622 + fq_rehash(q, old_fq_root, q->fq_trees_log, array, log); 623 + 623 624 q->fq_root = array; 624 625 q->fq_trees_log = log; 626 + 627 + sch_tree_unlock(sch); 628 + 629 + fq_free(old_fq_root); 625 630 626 631 return 0; 627 632 } ··· 704 697 q->flow_refill_delay = usecs_to_jiffies(usecs_delay); 705 698 } 706 699 707 - if (!err) 700 + if (!err) { 701 + sch_tree_unlock(sch); 708 702 err = fq_resize(sch, fq_log); 709 - 703 + sch_tree_lock(sch); 704 + } 710 705 while (sch->q.qlen > sch->limit) { 711 706 struct sk_buff *skb = fq_dequeue(sch); 712 707