Skip to content

Commit 2280d42

Browse files
fdmananakdave
authored andcommitted
btrfs: ignore fiemap path cache when there are multiple paths for a node
During fiemap, when walking backreferences to determine if a b+tree node/leaf is shared, we may find a tree block (leaf or node) for which two parents were added to the references ulist. This happens if we get for example one direct ref (shared tree block ref) and one indirect ref (non-shared tree block ref) for the tree block at the current level, which can happen during relocation. In that case the fiemap path cache can not be used since it's meant for a single path, with one tree block at each possible level, so having multiple references for a tree block at any level may result in getting the level counter exceed BTRFS_MAX_LEVEL and eventually trigger the warning: WARN_ON_ONCE(level >= BTRFS_MAX_LEVEL) at lookup_backref_shared_cache() and at store_backref_shared_cache(). This is harmless since the code ignores any level >= BTRFS_MAX_LEVEL, the warning is there just to catch any unexpected case like the one described above. However if a user finds this it may be scary and get reported. So just ignore the path cache once we find a tree block for which there are more than one reference, which is the less common case, and update the cache with the sharedness check result for all levels below the level for which we found multiple references. Reported-by: Jarno Pelkonen <[email protected]> Link: https://lore.kernel.org/linux-btrfs/CAKv8qLmDNAGJGCtsevxx_VZ_YOvvs1L83iEJkTgyA4joJertng@mail.gmail.com/ Fixes: 12a824d ("btrfs: speedup checking for extent sharedness during fiemap") CC: [email protected] # 6.1+ Signed-off-by: Filipe Manana <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent 2d82a40 commit 2280d42

File tree

1 file changed

+63
-22
lines changed

1 file changed

+63
-22
lines changed

fs/btrfs/backref.c

+63-22
Original file line numberDiff line numberDiff line change
@@ -1921,8 +1921,7 @@ int btrfs_is_data_extent_shared(struct btrfs_inode *inode, u64 bytenr,
19211921
level = -1;
19221922
ULIST_ITER_INIT(&uiter);
19231923
while (1) {
1924-
bool is_shared;
1925-
bool cached;
1924+
const unsigned long prev_ref_count = ctx->refs.nnodes;
19261925

19271926
walk_ctx.bytenr = bytenr;
19281927
ret = find_parent_nodes(&walk_ctx, &shared);
@@ -1940,21 +1939,36 @@ int btrfs_is_data_extent_shared(struct btrfs_inode *inode, u64 bytenr,
19401939
ret = 0;
19411940

19421941
/*
1943-
* If our data extent was not directly shared (without multiple
1944-
* reference items), than it might have a single reference item
1945-
* with a count > 1 for the same offset, which means there are 2
1946-
* (or more) file extent items that point to the data extent -
1947-
* this happens when a file extent item needs to be split and
1948-
* then one item gets moved to another leaf due to a b+tree leaf
1949-
* split when inserting some item. In this case the file extent
1950-
* items may be located in different leaves and therefore some
1951-
* of the leaves may be referenced through shared subtrees while
1952-
* others are not. Since our extent buffer cache only works for
1953-
* a single path (by far the most common case and simpler to
1954-
* deal with), we can not use it if we have multiple leaves
1955-
* (which implies multiple paths).
1942+
* More than one extent buffer (bytenr) may have been added to
1943+
* the ctx->refs ulist, in which case we have to check multiple
1944+
* tree paths in case the first one is not shared, so we can not
1945+
* use the path cache which is made for a single path. Multiple
1946+
* extent buffers at the current level happen when:
1947+
*
1948+
* 1) level -1, the data extent: If our data extent was not
1949+
* directly shared (without multiple reference items), then
1950+
* it might have a single reference item with a count > 1 for
1951+
* the same offset, which means there are 2 (or more) file
1952+
* extent items that point to the data extent - this happens
1953+
* when a file extent item needs to be split and then one
1954+
* item gets moved to another leaf due to a b+tree leaf split
1955+
* when inserting some item. In this case the file extent
1956+
* items may be located in different leaves and therefore
1957+
* some of the leaves may be referenced through shared
1958+
* subtrees while others are not. Since our extent buffer
1959+
* cache only works for a single path (by far the most common
1960+
* case and simpler to deal with), we can not use it if we
1961+
* have multiple leaves (which implies multiple paths).
1962+
*
1963+
* 2) level >= 0, a tree node/leaf: We can have a mix of direct
1964+
* and indirect references on a b+tree node/leaf, so we have
1965+
* to check multiple paths, and the extent buffer (the
1966+
* current bytenr) may be shared or not. One example is
1967+
* during relocation as we may get a shared tree block ref
1968+
* (direct ref) and a non-shared tree block ref (indirect
1969+
* ref) for the same node/leaf.
19561970
*/
1957-
if (level == -1 && ctx->refs.nnodes > 1)
1971+
if ((ctx->refs.nnodes - prev_ref_count) > 1)
19581972
ctx->use_path_cache = false;
19591973

19601974
if (level >= 0)
@@ -1964,18 +1978,45 @@ int btrfs_is_data_extent_shared(struct btrfs_inode *inode, u64 bytenr,
19641978
if (!node)
19651979
break;
19661980
bytenr = node->val;
1967-
level++;
1968-
cached = lookup_backref_shared_cache(ctx, root, bytenr, level,
1969-
&is_shared);
1970-
if (cached) {
1971-
ret = (is_shared ? 1 : 0);
1972-
break;
1981+
if (ctx->use_path_cache) {
1982+
bool is_shared;
1983+
bool cached;
1984+
1985+
level++;
1986+
cached = lookup_backref_shared_cache(ctx, root, bytenr,
1987+
level, &is_shared);
1988+
if (cached) {
1989+
ret = (is_shared ? 1 : 0);
1990+
break;
1991+
}
19731992
}
19741993
shared.share_count = 0;
19751994
shared.have_delayed_delete_refs = false;
19761995
cond_resched();
19771996
}
19781997

1998+
/*
1999+
* If the path cache is disabled, then it means at some tree level we
2000+
* got multiple parents due to a mix of direct and indirect backrefs or
2001+
* multiple leaves with file extent items pointing to the same data
2002+
* extent. We have to invalidate the cache and cache only the sharedness
2003+
* result for the levels where we got only one node/reference.
2004+
*/
2005+
if (!ctx->use_path_cache) {
2006+
int i = 0;
2007+
2008+
level--;
2009+
if (ret >= 0 && level >= 0) {
2010+
bytenr = ctx->path_cache_entries[level].bytenr;
2011+
ctx->use_path_cache = true;
2012+
store_backref_shared_cache(ctx, root, bytenr, level, ret);
2013+
i = level + 1;
2014+
}
2015+
2016+
for ( ; i < BTRFS_MAX_LEVEL; i++)
2017+
ctx->path_cache_entries[i].bytenr = 0;
2018+
}
2019+
19792020
/*
19802021
* Cache the sharedness result for the data extent if we know our inode
19812022
* has more than 1 file extent item that refers to the data extent.

0 commit comments

Comments
 (0)