Skip to content

Commit f96b9f7

Browse files
khazhykgregkh
authored andcommitted
writeback: avoid use-after-free after removing device
commit f87904c upstream. When a disk is removed, bdi_unregister gets called to stop further writeback and wait for associated delayed work to complete. However, wb_inode_writeback_end() may schedule bandwidth estimation dwork after this has completed, which can result in the timer attempting to access the just freed bdi_writeback. Fix this by checking if the bdi_writeback is alive, similar to when scheduling writeback work. Since this requires wb->work_lock, and wb_inode_writeback_end() may get called from interrupt, switch wb->work_lock to an irqsafe lock. Link: https://lkml.kernel.org/r/[email protected] Fixes: 45a2966 ("writeback: fix bandwidth estimate for spiky workload") Signed-off-by: Khazhismel Kumykov <[email protected]> Reviewed-by: Jan Kara <[email protected]> Cc: Michael Stapelberg <[email protected]> Cc: Wu Fengguang <[email protected]> Cc: Alexander Viro <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 0455bef commit f96b9f7

File tree

3 files changed

+16
-12
lines changed

3 files changed

+16
-12
lines changed

fs/fs-writeback.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,10 @@ static bool inode_io_list_move_locked(struct inode *inode,
134134

135135
static void wb_wakeup(struct bdi_writeback *wb)
136136
{
137-
spin_lock_bh(&wb->work_lock);
137+
spin_lock_irq(&wb->work_lock);
138138
if (test_bit(WB_registered, &wb->state))
139139
mod_delayed_work(bdi_wq, &wb->dwork, 0);
140-
spin_unlock_bh(&wb->work_lock);
140+
spin_unlock_irq(&wb->work_lock);
141141
}
142142

143143
static void finish_writeback_work(struct bdi_writeback *wb,
@@ -164,15 +164,15 @@ static void wb_queue_work(struct bdi_writeback *wb,
164164
if (work->done)
165165
atomic_inc(&work->done->cnt);
166166

167-
spin_lock_bh(&wb->work_lock);
167+
spin_lock_irq(&wb->work_lock);
168168

169169
if (test_bit(WB_registered, &wb->state)) {
170170
list_add_tail(&work->list, &wb->work_list);
171171
mod_delayed_work(bdi_wq, &wb->dwork, 0);
172172
} else
173173
finish_writeback_work(wb, work);
174174

175-
spin_unlock_bh(&wb->work_lock);
175+
spin_unlock_irq(&wb->work_lock);
176176
}
177177

178178
/**
@@ -2109,13 +2109,13 @@ static struct wb_writeback_work *get_next_work_item(struct bdi_writeback *wb)
21092109
{
21102110
struct wb_writeback_work *work = NULL;
21112111

2112-
spin_lock_bh(&wb->work_lock);
2112+
spin_lock_irq(&wb->work_lock);
21132113
if (!list_empty(&wb->work_list)) {
21142114
work = list_entry(wb->work_list.next,
21152115
struct wb_writeback_work, list);
21162116
list_del_init(&work->list);
21172117
}
2118-
spin_unlock_bh(&wb->work_lock);
2118+
spin_unlock_irq(&wb->work_lock);
21192119
return work;
21202120
}
21212121

mm/backing-dev.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -258,10 +258,10 @@ void wb_wakeup_delayed(struct bdi_writeback *wb)
258258
unsigned long timeout;
259259

260260
timeout = msecs_to_jiffies(dirty_writeback_interval * 10);
261-
spin_lock_bh(&wb->work_lock);
261+
spin_lock_irq(&wb->work_lock);
262262
if (test_bit(WB_registered, &wb->state))
263263
queue_delayed_work(bdi_wq, &wb->dwork, timeout);
264-
spin_unlock_bh(&wb->work_lock);
264+
spin_unlock_irq(&wb->work_lock);
265265
}
266266

267267
static void wb_update_bandwidth_workfn(struct work_struct *work)
@@ -337,12 +337,12 @@ static void cgwb_remove_from_bdi_list(struct bdi_writeback *wb);
337337
static void wb_shutdown(struct bdi_writeback *wb)
338338
{
339339
/* Make sure nobody queues further work */
340-
spin_lock_bh(&wb->work_lock);
340+
spin_lock_irq(&wb->work_lock);
341341
if (!test_and_clear_bit(WB_registered, &wb->state)) {
342-
spin_unlock_bh(&wb->work_lock);
342+
spin_unlock_irq(&wb->work_lock);
343343
return;
344344
}
345-
spin_unlock_bh(&wb->work_lock);
345+
spin_unlock_irq(&wb->work_lock);
346346

347347
cgwb_remove_from_bdi_list(wb);
348348
/*

mm/page-writeback.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2755,6 +2755,7 @@ static void wb_inode_writeback_start(struct bdi_writeback *wb)
27552755

27562756
static void wb_inode_writeback_end(struct bdi_writeback *wb)
27572757
{
2758+
unsigned long flags;
27582759
atomic_dec(&wb->writeback_inodes);
27592760
/*
27602761
* Make sure estimate of writeback throughput gets updated after
@@ -2763,7 +2764,10 @@ static void wb_inode_writeback_end(struct bdi_writeback *wb)
27632764
* that if multiple inodes end writeback at a similar time, they get
27642765
* batched into one bandwidth update.
27652766
*/
2766-
queue_delayed_work(bdi_wq, &wb->bw_dwork, BANDWIDTH_INTERVAL);
2767+
spin_lock_irqsave(&wb->work_lock, flags);
2768+
if (test_bit(WB_registered, &wb->state))
2769+
queue_delayed_work(bdi_wq, &wb->bw_dwork, BANDWIDTH_INTERVAL);
2770+
spin_unlock_irqrestore(&wb->work_lock, flags);
27672771
}
27682772

27692773
int test_clear_page_writeback(struct page *page)

0 commit comments

Comments
 (0)