Skip to content

Commit fbc2bd7

Browse files
fdmananakdave
authored andcommitted
btrfs: release old extent maps during page release
When removing an extent map at try_release_extent_mapping(), called through the page release callback (btrfs_releasepage()), we never release an extent map that is in the list of modified extents. This is to prevent races with a concurrent fsync using the fast path, which could lead to not logging an extent created in the current transaction. However we can safely remove an extent map created in a past transaction that is still in the list of modified extents (because no one fsynced yet the inode after that transaction got commited), because such extents are skipped during an fsync as it is pointless to log them. This change does that. Signed-off-by: Filipe Manana <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent 3d6448e commit fbc2bd7

File tree

1 file changed

+24
-7
lines changed

1 file changed

+24
-7
lines changed

fs/btrfs/extent_io.c

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4481,6 +4481,9 @@ int try_release_extent_mapping(struct page *page, gfp_t mask)
44814481
page->mapping->host->i_size > SZ_16M) {
44824482
u64 len;
44834483
while (start <= end) {
4484+
struct btrfs_fs_info *fs_info;
4485+
u64 cur_gen;
4486+
44844487
len = end - start + 1;
44854488
write_lock(&map->lock);
44864489
em = lookup_extent_mapping(map, start, len);
@@ -4505,13 +4508,27 @@ int try_release_extent_mapping(struct page *page, gfp_t mask)
45054508
* extra reference on the em.
45064509
*/
45074510
if (list_empty(&em->list) ||
4508-
test_bit(EXTENT_FLAG_LOGGING, &em->flags)) {
4509-
set_bit(BTRFS_INODE_NEEDS_FULL_SYNC,
4510-
&btrfs_inode->runtime_flags);
4511-
remove_extent_mapping(map, em);
4512-
/* once for the rb tree */
4513-
free_extent_map(em);
4514-
}
4511+
test_bit(EXTENT_FLAG_LOGGING, &em->flags))
4512+
goto remove_em;
4513+
/*
4514+
* If it's in the list of modified extents, remove it
4515+
* only if its generation is older then the current one,
4516+
* in which case we don't need it for a fast fsync.
4517+
* Otherwise don't remove it, we could be racing with an
4518+
* ongoing fast fsync that could miss the new extent.
4519+
*/
4520+
fs_info = btrfs_inode->root->fs_info;
4521+
spin_lock(&fs_info->trans_lock);
4522+
cur_gen = fs_info->generation;
4523+
spin_unlock(&fs_info->trans_lock);
4524+
if (em->generation >= cur_gen)
4525+
goto next;
4526+
remove_em:
4527+
set_bit(BTRFS_INODE_NEEDS_FULL_SYNC,
4528+
&btrfs_inode->runtime_flags);
4529+
remove_extent_mapping(map, em);
4530+
/* once for the rb tree */
4531+
free_extent_map(em);
45154532
next:
45164533
start = extent_map_end(em);
45174534
write_unlock(&map->lock);

0 commit comments

Comments
 (0)