Skip to content

Commit a6fc88b

Browse files
Joel FernandesBoqun Feng
authored andcommitted
srcu: Use irq_work to start GP in tiny SRCU
Tiny SRCU's srcu_gp_start_if_needed() directly calls schedule_work(), which acquires the workqueue pool->lock. This causes a lockdep splat when call_srcu() is called with a scheduler lock held, due to: call_srcu() [holding pi_lock] srcu_gp_start_if_needed() schedule_work() -> pool->lock workqueue_init() / create_worker() [holding pool->lock] wake_up_process() -> try_to_wake_up() -> pi_lock Also add irq_work_sync() to cleanup_srcu_struct() to prevent a use-after-free if a queued irq_work fires after cleanup begins. Tested with rcutorture SRCU-T and no lockdep warnings. [ Thanks to Boqun for similar fix in patch "rcu: Use an intermediate irq_work to start process_srcu()" ] Signed-off-by: Joel Fernandes <[email protected]> Reviewed-by: Paul E. McKenney <[email protected]> Signed-off-by: Boqun Feng <[email protected]>
1 parent 7c405fb commit a6fc88b

2 files changed

Lines changed: 22 additions & 1 deletion

File tree

include/linux/srcutiny.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#ifndef _LINUX_SRCU_TINY_H
1212
#define _LINUX_SRCU_TINY_H
1313

14+
#include <linux/irq_work_types.h>
1415
#include <linux/swait.h>
1516

1617
struct srcu_struct {
@@ -24,18 +25,21 @@ struct srcu_struct {
2425
struct rcu_head *srcu_cb_head; /* Pending callbacks: Head. */
2526
struct rcu_head **srcu_cb_tail; /* Pending callbacks: Tail. */
2627
struct work_struct srcu_work; /* For driving grace periods. */
28+
struct irq_work srcu_irq_work; /* Defer schedule_work() to irq work. */
2729
#ifdef CONFIG_DEBUG_LOCK_ALLOC
2830
struct lockdep_map dep_map;
2931
#endif /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
3032
};
3133

3234
void srcu_drive_gp(struct work_struct *wp);
35+
void srcu_tiny_irq_work(struct irq_work *irq_work);
3336

3437
#define __SRCU_STRUCT_INIT(name, __ignored, ___ignored, ____ignored) \
3538
{ \
3639
.srcu_wq = __SWAIT_QUEUE_HEAD_INITIALIZER(name.srcu_wq), \
3740
.srcu_cb_tail = &name.srcu_cb_head, \
3841
.srcu_work = __WORK_INITIALIZER(name.srcu_work, srcu_drive_gp), \
42+
.srcu_irq_work = { .func = srcu_tiny_irq_work }, \
3943
__SRCU_DEP_MAP_INIT(name) \
4044
}
4145

kernel/rcu/srcutiny.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
*/
1010

1111
#include <linux/export.h>
12+
#include <linux/irq_work.h>
1213
#include <linux/mutex.h>
1314
#include <linux/preempt.h>
1415
#include <linux/rcupdate_wait.h>
@@ -41,6 +42,7 @@ static int init_srcu_struct_fields(struct srcu_struct *ssp)
4142
ssp->srcu_idx_max = 0;
4243
INIT_WORK(&ssp->srcu_work, srcu_drive_gp);
4344
INIT_LIST_HEAD(&ssp->srcu_work.entry);
45+
init_irq_work(&ssp->srcu_irq_work, srcu_tiny_irq_work);
4446
return 0;
4547
}
4648

@@ -84,6 +86,7 @@ EXPORT_SYMBOL_GPL(init_srcu_struct);
8486
void cleanup_srcu_struct(struct srcu_struct *ssp)
8587
{
8688
WARN_ON(ssp->srcu_lock_nesting[0] || ssp->srcu_lock_nesting[1]);
89+
irq_work_sync(&ssp->srcu_irq_work);
8790
flush_work(&ssp->srcu_work);
8891
WARN_ON(ssp->srcu_gp_running);
8992
WARN_ON(ssp->srcu_gp_waiting);
@@ -177,6 +180,20 @@ void srcu_drive_gp(struct work_struct *wp)
177180
}
178181
EXPORT_SYMBOL_GPL(srcu_drive_gp);
179182

183+
/*
184+
* Use an irq_work to defer schedule_work() to avoid acquiring the workqueue
185+
* pool->lock while the caller might hold scheduler locks, causing lockdep
186+
* splats due to workqueue_init() doing a wakeup.
187+
*/
188+
void srcu_tiny_irq_work(struct irq_work *irq_work)
189+
{
190+
struct srcu_struct *ssp;
191+
192+
ssp = container_of(irq_work, struct srcu_struct, srcu_irq_work);
193+
schedule_work(&ssp->srcu_work);
194+
}
195+
EXPORT_SYMBOL_GPL(srcu_tiny_irq_work);
196+
180197
static void srcu_gp_start_if_needed(struct srcu_struct *ssp)
181198
{
182199
unsigned long cookie;
@@ -189,7 +206,7 @@ static void srcu_gp_start_if_needed(struct srcu_struct *ssp)
189206
WRITE_ONCE(ssp->srcu_idx_max, cookie);
190207
if (!READ_ONCE(ssp->srcu_gp_running)) {
191208
if (likely(srcu_init_done))
192-
schedule_work(&ssp->srcu_work);
209+
irq_work_queue(&ssp->srcu_irq_work);
193210
else if (list_empty(&ssp->srcu_work.entry))
194211
list_add(&ssp->srcu_work.entry, &srcu_boot_list);
195212
}

0 commit comments

Comments
 (0)