Skip to content

Commit bc2dbc4

Browse files
adam900710kdave
authored andcommitted
btrfs: avoid page_lockend underflow in btrfs_punch_hole_lock_range()
[BUG] When running btrfs/004 with 4K fs block size and 64K page size, sometimes fsstress workload can take 100% CPU for a while, but not long enough to trigger a 120s hang warning. [CAUSE] When such 100% CPU usage happens, btrfs_punch_hole_lock_range() is always in the call trace. One example when this problem happens, the function btrfs_punch_hole_lock_range() got the following parameters: lock_start = 4096, lockend = 20469 Then we calculate @page_lockstart by rounding up lock_start to page boundary, which is 64K (page size is 64K). For @page_lockend, we round down the value towards page boundary, which result 0. Then since we need to pass an inclusive end to filemap_range_has_page(), we subtract 1 from the rounded down value, resulting in (u64)-1. In the above case, the range is inside the same page, and we do not even need to call filemap_range_has_page(), not to mention to call it with (u64)-1 at the end. This behavior will cause btrfs_punch_hole_lock_range() to busy loop waiting for irrelevant range to have its pages dropped. [FIX] Calculate @page_lockend by just rounding down @LockEnd, without decreasing the value by one. So @page_lockend will no longer overflow. Then exit early if @page_lockend is no larger than @page_lockstart. As it means either the range is inside the same page, or the two pages are adjacent already. Finally only decrease @page_lockend when calling filemap_range_has_page(). Fixes: 0528476 ("btrfs: fix the filemap_range_has_page() call in btrfs_punch_hole_lock_range()") Reviewed-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: Qu Wenruo <wqu@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent cf6ae7e commit bc2dbc4

File tree

1 file changed

+7
-2
lines changed

1 file changed

+7
-2
lines changed

fs/btrfs/file.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2104,15 +2104,20 @@ static void btrfs_punch_hole_lock_range(struct inode *inode,
21042104
* will always return true.
21052105
* So here we need to do extra page alignment for
21062106
* filemap_range_has_page().
2107+
*
2108+
* And do not decrease page_lockend right now, as it can be 0.
21072109
*/
21082110
const u64 page_lockstart = round_up(lockstart, PAGE_SIZE);
2109-
const u64 page_lockend = round_down(lockend + 1, PAGE_SIZE) - 1;
2111+
const u64 page_lockend = round_down(lockend + 1, PAGE_SIZE);
21102112

21112113
while (1) {
21122114
truncate_pagecache_range(inode, lockstart, lockend);
21132115

21142116
lock_extent(&BTRFS_I(inode)->io_tree, lockstart, lockend,
21152117
cached_state);
2118+
/* The same page or adjacent pages. */
2119+
if (page_lockend <= page_lockstart)
2120+
break;
21162121
/*
21172122
* We can't have ordered extents in the range, nor dirty/writeback
21182123
* pages, because we have locked the inode's VFS lock in exclusive
@@ -2124,7 +2129,7 @@ static void btrfs_punch_hole_lock_range(struct inode *inode,
21242129
* we do, unlock the range and retry.
21252130
*/
21262131
if (!filemap_range_has_page(inode->i_mapping, page_lockstart,
2127-
page_lockend))
2132+
page_lockend - 1))
21282133
break;
21292134

21302135
unlock_extent(&BTRFS_I(inode)->io_tree, lockstart, lockend,

0 commit comments

Comments
 (0)