Skip to content

Commit a0fd1c6

Browse files
boryaskdave
authored andcommitted
btrfs: fix folio leak in submit_one_async_extent()
If btrfs_reserve_extent() fails while submitting an async_extent for a compressed write, then we fail to call free_async_extent_pages() on the async_extent and leak its folios. A likely cause for such a failure would be btrfs_reserve_extent() failing to find a large enough contiguous free extent for the compressed extent. I was able to reproduce this by: 1. mount with compress-force=zstd:3 2. fallocating most of a filesystem to a big file 3. fragmenting the remaining free space 4. trying to copy in a file which zstd would generate large compressed extents for (vmlinux worked well for this) Step 4. hits the memory leak and can be repeated ad nauseam to eventually exhaust the system memory. Fix this by detecting the case where we fallback to uncompressed submission for a compressed async_extent and ensuring that we call free_async_extent_pages(). Fixes: 131a821 ("btrfs: fallback if compressed IO fails for ENOSPC") CC: [email protected] # 6.1+ Reviewed-by: Filipe Manana <[email protected]> Co-developed-by: Josef Bacik <[email protected]> Signed-off-by: Boris Burkov <[email protected]> Reviewed-by: David Sterba <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent 54db6d1 commit a0fd1c6

File tree

1 file changed

+7
-0
lines changed

1 file changed

+7
-0
lines changed

fs/btrfs/inode.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,6 +1109,7 @@ static void submit_one_async_extent(struct async_chunk *async_chunk,
11091109
struct extent_state *cached = NULL;
11101110
struct extent_map *em;
11111111
int ret = 0;
1112+
bool free_pages = false;
11121113
u64 start = async_extent->start;
11131114
u64 end = async_extent->start + async_extent->ram_size - 1;
11141115

@@ -1129,7 +1130,10 @@ static void submit_one_async_extent(struct async_chunk *async_chunk,
11291130
}
11301131

11311132
if (async_extent->compress_type == BTRFS_COMPRESS_NONE) {
1133+
ASSERT(!async_extent->folios);
1134+
ASSERT(async_extent->nr_folios == 0);
11321135
submit_uncompressed_range(inode, async_extent, locked_folio);
1136+
free_pages = true;
11331137
goto done;
11341138
}
11351139

@@ -1145,6 +1149,7 @@ static void submit_one_async_extent(struct async_chunk *async_chunk,
11451149
* fall back to uncompressed.
11461150
*/
11471151
submit_uncompressed_range(inode, async_extent, locked_folio);
1152+
free_pages = true;
11481153
goto done;
11491154
}
11501155

@@ -1186,6 +1191,8 @@ static void submit_one_async_extent(struct async_chunk *async_chunk,
11861191
done:
11871192
if (async_chunk->blkcg_css)
11881193
kthread_associate_blkcg(NULL);
1194+
if (free_pages)
1195+
free_async_extent_pages(async_extent);
11891196
kfree(async_extent);
11901197
return;
11911198

0 commit comments

Comments
 (0)