mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 04:31:50 +00:00
btrfs: subpage: fix race between prepare_pages() and btrfs_releasepage()
[BUG] When running generic/095, there is a high chance to crash with subpage data RW support: assertion failed: PagePrivate(page) && page->private ------------[ cut here ]------------ kernel BUG at fs/btrfs/ctree.h:3403! Internal error: Oops - BUG: 0 [#1] SMP CPU: 1 PID: 3567 Comm: fio Tainted: 5.12.0-rc7-custom+ #17 Hardware name: Khadas VIM3 (DT) Call trace: assertfail.constprop.0+0x28/0x2c [btrfs] btrfs_subpage_assert+0x80/0xa0 [btrfs] btrfs_subpage_set_uptodate+0x34/0xec [btrfs] btrfs_page_clamp_set_uptodate+0x74/0xa4 [btrfs] btrfs_dirty_pages+0x160/0x270 [btrfs] btrfs_buffered_write+0x444/0x630 [btrfs] btrfs_direct_write+0x1cc/0x2d0 [btrfs] btrfs_file_write_iter+0xc0/0x160 [btrfs] new_sync_write+0xe8/0x180 vfs_write+0x1b4/0x210 ksys_pwrite64+0x7c/0xc0 __arm64_sys_pwrite64+0x24/0x30 el0_svc_common.constprop.0+0x70/0x140 do_el0_svc+0x28/0x90 el0_svc+0x2c/0x54 el0_sync_handler+0x1a8/0x1ac el0_sync+0x170/0x180 Code: f0000160 913be042 913c4000 955444bc (d4210000) ---[ end trace 3fdd39f4cccedd68 ]--- [CAUSE] Although prepare_pages() calls find_or_create_page(), which returns the page locked, but in later prepare_uptodate_page() calls, we may call btrfs_readpage() which will unlock the page before it returns. This leaves a window where btrfs_releasepage() can sneak in and release the page, clearing page->private and causing above ASSERT(). [FIX] In prepare_uptodate_page(), we should not only check page->mapping, but also PagePrivate() to ensure we are still holding the correct page which has proper fs context setup. Reported-by: Ritesh Harjani <riteshh@linux.ibm.com> Tested-by: Ritesh Harjani <riteshh@linux.ibm.com> Reviewed-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: Qu Wenruo <wqu@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
c8050b3b7f
commit
e046786619
@ -1340,7 +1340,18 @@ static int prepare_uptodate_page(struct inode *inode,
|
||||
unlock_page(page);
|
||||
return -EIO;
|
||||
}
|
||||
if (page->mapping != inode->i_mapping) {
|
||||
|
||||
/*
|
||||
* Since btrfs_readpage() will unlock the page before it
|
||||
* returns, there is a window where btrfs_releasepage() can
|
||||
* be called to release the page.
|
||||
* Here we check both inode mapping and PagePrivate() to
|
||||
* make sure the page was not released.
|
||||
*
|
||||
* The private flag check is essential for subpage as we need
|
||||
* to store extra bitmap using page->private.
|
||||
*/
|
||||
if (page->mapping != inode->i_mapping || !PagePrivate(page)) {
|
||||
unlock_page(page);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user