Skip to content

Commit 87e1d78

Browse files
htejunaxboe
authored andcommitted
writeback: implement [locked_]inode_to_wb_and_lock_list()
cgroup writeback currently assumes that inode to wb association doesn't change; however, with the planned foreign inode wb switching mechanism, the association will change dynamically. When an inode needs to be put on one of the IO lists of its wb, the current code simply calls inode_to_wb() and locks the returned wb; however, with the planned wb switching, the association may change before locking the wb and may even get released. This patch implements [locked_]inode_to_wb_and_lock_list() which pins the associated wb while holding i_lock, releases it, acquires wb->list_lock and verifies that the association hasn't changed inbetween. As the association will be protected by both locks among other things, this guarantees that the wb is the inode's associated wb until the list_lock is released. Signed-off-by: Tejun Heo <tj@kernel.org> Cc: Jens Axboe <axboe@kernel.dk> Cc: Jan Kara <jack@suse.cz> Cc: Wu Fengguang <fengguang.wu@intel.com> Cc: Greg Thelen <gthelen@google.com> Signed-off-by: Jens Axboe <axboe@fb.com>
1 parent 2a81490 commit 87e1d78

File tree

1 file changed

+75
-5
lines changed

1 file changed

+75
-5
lines changed

fs/fs-writeback.c

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,56 @@ void __inode_attach_wb(struct inode *inode, struct page *page)
258258
wb_put(wb);
259259
}
260260

261+
/**
262+
* locked_inode_to_wb_and_lock_list - determine a locked inode's wb and lock it
263+
* @inode: inode of interest with i_lock held
264+
*
265+
* Returns @inode's wb with its list_lock held. @inode->i_lock must be
266+
* held on entry and is released on return. The returned wb is guaranteed
267+
* to stay @inode's associated wb until its list_lock is released.
268+
*/
269+
static struct bdi_writeback *
270+
locked_inode_to_wb_and_lock_list(struct inode *inode)
271+
__releases(&inode->i_lock)
272+
__acquires(&wb->list_lock)
273+
{
274+
while (true) {
275+
struct bdi_writeback *wb = inode_to_wb(inode);
276+
277+
/*
278+
* inode_to_wb() association is protected by both
279+
* @inode->i_lock and @wb->list_lock but list_lock nests
280+
* outside i_lock. Drop i_lock and verify that the
281+
* association hasn't changed after acquiring list_lock.
282+
*/
283+
wb_get(wb);
284+
spin_unlock(&inode->i_lock);
285+
spin_lock(&wb->list_lock);
286+
wb_put(wb); /* not gonna deref it anymore */
287+
288+
if (likely(wb == inode_to_wb(inode)))
289+
return wb; /* @inode already has ref */
290+
291+
spin_unlock(&wb->list_lock);
292+
cpu_relax();
293+
spin_lock(&inode->i_lock);
294+
}
295+
}
296+
297+
/**
298+
* inode_to_wb_and_lock_list - determine an inode's wb and lock it
299+
* @inode: inode of interest
300+
*
301+
* Same as locked_inode_to_wb_and_lock_list() but @inode->i_lock isn't held
302+
* on entry.
303+
*/
304+
static struct bdi_writeback *inode_to_wb_and_lock_list(struct inode *inode)
305+
__acquires(&wb->list_lock)
306+
{
307+
spin_lock(&inode->i_lock);
308+
return locked_inode_to_wb_and_lock_list(inode);
309+
}
310+
261311
/**
262312
* wbc_attach_and_unlock_inode - associate wbc with target inode and unlock it
263313
* @wbc: writeback_control of interest
@@ -603,6 +653,27 @@ static void bdi_split_work_to_wbs(struct backing_dev_info *bdi,
603653

604654
#else /* CONFIG_CGROUP_WRITEBACK */
605655

656+
static struct bdi_writeback *
657+
locked_inode_to_wb_and_lock_list(struct inode *inode)
658+
__releases(&inode->i_lock)
659+
__acquires(&wb->list_lock)
660+
{
661+
struct bdi_writeback *wb = inode_to_wb(inode);
662+
663+
spin_unlock(&inode->i_lock);
664+
spin_lock(&wb->list_lock);
665+
return wb;
666+
}
667+
668+
static struct bdi_writeback *inode_to_wb_and_lock_list(struct inode *inode)
669+
__acquires(&wb->list_lock)
670+
{
671+
struct bdi_writeback *wb = inode_to_wb(inode);
672+
673+
spin_lock(&wb->list_lock);
674+
return wb;
675+
}
676+
606677
static long wb_split_bdi_pages(struct bdi_writeback *wb, long nr_pages)
607678
{
608679
return nr_pages;
@@ -678,9 +749,9 @@ void wb_start_background_writeback(struct bdi_writeback *wb)
678749
*/
679750
void inode_wb_list_del(struct inode *inode)
680751
{
681-
struct bdi_writeback *wb = inode_to_wb(inode);
752+
struct bdi_writeback *wb;
682753

683-
spin_lock(&wb->list_lock);
754+
wb = inode_to_wb_and_lock_list(inode);
684755
inode_wb_list_del_locked(inode, wb);
685756
spin_unlock(&wb->list_lock);
686757
}
@@ -1784,12 +1855,11 @@ void __mark_inode_dirty(struct inode *inode, int flags)
17841855
* reposition it (that would break b_dirty time-ordering).
17851856
*/
17861857
if (!was_dirty) {
1787-
struct bdi_writeback *wb = inode_to_wb(inode);
1858+
struct bdi_writeback *wb;
17881859
struct list_head *dirty_list;
17891860
bool wakeup_bdi = false;
17901861

1791-
spin_unlock(&inode->i_lock);
1792-
spin_lock(&wb->list_lock);
1862+
wb = locked_inode_to_wb_and_lock_list(inode);
17931863

17941864
WARN(bdi_cap_writeback_dirty(wb->bdi) &&
17951865
!test_bit(WB_registered, &wb->state),

0 commit comments

Comments
 (0)