btrfs: introduce BTRFS_NESTING_COW for cow'ing blocks
When we COW a block we are holding a lock on the original block, and then we lock the new COW block. Because our lockdep maps are based on root + level, this will make lockdep complain. We need a way to indicate a subclass for locking the COW'ed block, so plumb through our btrfs_lock_nesting from btrfs_cow_block down to the btrfs_init_buffer, and then introduce BTRFS_NESTING_COW to be used for cow'ing blocks. The reason I've added all this extra infrastructure is because there will be need of different nesting classes in follow up patches. Signed-off-by: Josef Bacik <josef@toxicpanda.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Родитель
fd7ba1c120
Коммит
9631e4cc1a
|
@ -198,7 +198,7 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans,
|
|||
btrfs_node_key(buf, &disk_key, 0);
|
||||
|
||||
cow = btrfs_alloc_tree_block(trans, root, 0, new_root_objectid,
|
||||
&disk_key, level, buf->start, 0);
|
||||
&disk_key, level, buf->start, 0, BTRFS_NESTING_NORMAL);
|
||||
if (IS_ERR(cow))
|
||||
return PTR_ERR(cow);
|
||||
|
||||
|
@ -957,7 +957,8 @@ static struct extent_buffer *alloc_tree_block_no_bg_flush(
|
|||
const struct btrfs_disk_key *disk_key,
|
||||
int level,
|
||||
u64 hint,
|
||||
u64 empty_size)
|
||||
u64 empty_size,
|
||||
enum btrfs_lock_nesting nest)
|
||||
{
|
||||
struct btrfs_fs_info *fs_info = root->fs_info;
|
||||
struct extent_buffer *ret;
|
||||
|
@ -986,7 +987,7 @@ static struct extent_buffer *alloc_tree_block_no_bg_flush(
|
|||
|
||||
ret = btrfs_alloc_tree_block(trans, root, parent_start,
|
||||
root->root_key.objectid, disk_key, level,
|
||||
hint, empty_size);
|
||||
hint, empty_size, nest);
|
||||
trans->can_flush_pending_bgs = true;
|
||||
|
||||
return ret;
|
||||
|
@ -1009,7 +1010,8 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
|
|||
struct extent_buffer *buf,
|
||||
struct extent_buffer *parent, int parent_slot,
|
||||
struct extent_buffer **cow_ret,
|
||||
u64 search_start, u64 empty_size)
|
||||
u64 search_start, u64 empty_size,
|
||||
enum btrfs_lock_nesting nest)
|
||||
{
|
||||
struct btrfs_fs_info *fs_info = root->fs_info;
|
||||
struct btrfs_disk_key disk_key;
|
||||
|
@ -1040,7 +1042,7 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
|
|||
parent_start = parent->start;
|
||||
|
||||
cow = alloc_tree_block_no_bg_flush(trans, root, parent_start, &disk_key,
|
||||
level, search_start, empty_size);
|
||||
level, search_start, empty_size, nest);
|
||||
if (IS_ERR(cow))
|
||||
return PTR_ERR(cow);
|
||||
|
||||
|
@ -1446,7 +1448,8 @@ static inline int should_cow_block(struct btrfs_trans_handle *trans,
|
|||
noinline int btrfs_cow_block(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct extent_buffer *buf,
|
||||
struct extent_buffer *parent, int parent_slot,
|
||||
struct extent_buffer **cow_ret)
|
||||
struct extent_buffer **cow_ret,
|
||||
enum btrfs_lock_nesting nest)
|
||||
{
|
||||
struct btrfs_fs_info *fs_info = root->fs_info;
|
||||
u64 search_start;
|
||||
|
@ -1485,7 +1488,7 @@ noinline int btrfs_cow_block(struct btrfs_trans_handle *trans,
|
|||
*/
|
||||
btrfs_qgroup_trace_subtree_after_cow(trans, root, buf);
|
||||
ret = __btrfs_cow_block(trans, root, buf, parent,
|
||||
parent_slot, cow_ret, search_start, 0);
|
||||
parent_slot, cow_ret, search_start, 0, nest);
|
||||
|
||||
trace_btrfs_cow_block(root, buf, *cow_ret);
|
||||
|
||||
|
@ -1657,7 +1660,8 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans,
|
|||
err = __btrfs_cow_block(trans, root, cur, parent, i,
|
||||
&cur, search_start,
|
||||
min(16 * blocksize,
|
||||
(end_slot - i) * blocksize));
|
||||
(end_slot - i) * blocksize),
|
||||
BTRFS_NESTING_COW);
|
||||
if (err) {
|
||||
btrfs_tree_unlock(cur);
|
||||
free_extent_buffer(cur);
|
||||
|
@ -1855,7 +1859,8 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
|
|||
|
||||
btrfs_tree_lock(child);
|
||||
btrfs_set_lock_blocking_write(child);
|
||||
ret = btrfs_cow_block(trans, root, child, mid, 0, &child);
|
||||
ret = btrfs_cow_block(trans, root, child, mid, 0, &child,
|
||||
BTRFS_NESTING_COW);
|
||||
if (ret) {
|
||||
btrfs_tree_unlock(child);
|
||||
free_extent_buffer(child);
|
||||
|
@ -1894,7 +1899,8 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
|
|||
btrfs_tree_lock(left);
|
||||
btrfs_set_lock_blocking_write(left);
|
||||
wret = btrfs_cow_block(trans, root, left,
|
||||
parent, pslot - 1, &left);
|
||||
parent, pslot - 1, &left,
|
||||
BTRFS_NESTING_COW);
|
||||
if (wret) {
|
||||
ret = wret;
|
||||
goto enospc;
|
||||
|
@ -1909,7 +1915,8 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
|
|||
btrfs_tree_lock(right);
|
||||
btrfs_set_lock_blocking_write(right);
|
||||
wret = btrfs_cow_block(trans, root, right,
|
||||
parent, pslot + 1, &right);
|
||||
parent, pslot + 1, &right,
|
||||
BTRFS_NESTING_COW);
|
||||
if (wret) {
|
||||
ret = wret;
|
||||
goto enospc;
|
||||
|
@ -2077,7 +2084,8 @@ static noinline int push_nodes_for_insert(struct btrfs_trans_handle *trans,
|
|||
wret = 1;
|
||||
} else {
|
||||
ret = btrfs_cow_block(trans, root, left, parent,
|
||||
pslot - 1, &left);
|
||||
pslot - 1, &left,
|
||||
BTRFS_NESTING_COW);
|
||||
if (ret)
|
||||
wret = 1;
|
||||
else {
|
||||
|
@ -2132,7 +2140,7 @@ static noinline int push_nodes_for_insert(struct btrfs_trans_handle *trans,
|
|||
} else {
|
||||
ret = btrfs_cow_block(trans, root, right,
|
||||
parent, pslot + 1,
|
||||
&right);
|
||||
&right, BTRFS_NESTING_COW);
|
||||
if (ret)
|
||||
wret = 1;
|
||||
else {
|
||||
|
@ -2740,11 +2748,13 @@ again:
|
|||
btrfs_set_path_blocking(p);
|
||||
if (last_level)
|
||||
err = btrfs_cow_block(trans, root, b, NULL, 0,
|
||||
&b);
|
||||
&b,
|
||||
BTRFS_NESTING_COW);
|
||||
else
|
||||
err = btrfs_cow_block(trans, root, b,
|
||||
p->nodes[level + 1],
|
||||
p->slots[level + 1], &b);
|
||||
p->slots[level + 1], &b,
|
||||
BTRFS_NESTING_COW);
|
||||
if (err) {
|
||||
ret = err;
|
||||
goto done;
|
||||
|
@ -3396,7 +3406,8 @@ static noinline int insert_new_root(struct btrfs_trans_handle *trans,
|
|||
btrfs_node_key(lower, &lower_key, 0);
|
||||
|
||||
c = alloc_tree_block_no_bg_flush(trans, root, 0, &lower_key, level,
|
||||
root->node->start, 0);
|
||||
root->node->start, 0,
|
||||
BTRFS_NESTING_NORMAL);
|
||||
if (IS_ERR(c))
|
||||
return PTR_ERR(c);
|
||||
|
||||
|
@ -3526,7 +3537,7 @@ static noinline int split_node(struct btrfs_trans_handle *trans,
|
|||
btrfs_node_key(c, &disk_key, mid);
|
||||
|
||||
split = alloc_tree_block_no_bg_flush(trans, root, 0, &disk_key, level,
|
||||
c->start, 0);
|
||||
c->start, 0, BTRFS_NESTING_NORMAL);
|
||||
if (IS_ERR(split))
|
||||
return PTR_ERR(split);
|
||||
|
||||
|
@ -3804,7 +3815,7 @@ static int push_leaf_right(struct btrfs_trans_handle *trans, struct btrfs_root
|
|||
|
||||
/* cow and double check */
|
||||
ret = btrfs_cow_block(trans, root, right, upper,
|
||||
slot + 1, &right);
|
||||
slot + 1, &right, BTRFS_NESTING_COW);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
|
@ -4045,7 +4056,8 @@ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root
|
|||
|
||||
/* cow and double check */
|
||||
ret = btrfs_cow_block(trans, root, left,
|
||||
path->nodes[1], slot - 1, &left);
|
||||
path->nodes[1], slot - 1, &left,
|
||||
BTRFS_NESTING_COW);
|
||||
if (ret) {
|
||||
/* we hit -ENOSPC, but it isn't fatal here */
|
||||
if (ret == -ENOSPC)
|
||||
|
@ -4312,7 +4324,7 @@ again:
|
|||
btrfs_item_key(l, &disk_key, mid);
|
||||
|
||||
right = alloc_tree_block_no_bg_flush(trans, root, 0, &disk_key, 0,
|
||||
l->start, 0);
|
||||
l->start, 0, BTRFS_NESTING_NORMAL);
|
||||
if (IS_ERR(right))
|
||||
return PTR_ERR(right);
|
||||
|
||||
|
|
|
@ -2526,7 +2526,8 @@ struct extent_buffer *btrfs_alloc_tree_block(struct btrfs_trans_handle *trans,
|
|||
u64 parent, u64 root_objectid,
|
||||
const struct btrfs_disk_key *key,
|
||||
int level, u64 hint,
|
||||
u64 empty_size);
|
||||
u64 empty_size,
|
||||
enum btrfs_lock_nesting nest);
|
||||
void btrfs_free_tree_block(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct extent_buffer *buf,
|
||||
|
@ -2667,7 +2668,8 @@ struct extent_buffer *btrfs_read_node_slot(struct extent_buffer *parent,
|
|||
int btrfs_cow_block(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct extent_buffer *buf,
|
||||
struct extent_buffer *parent, int parent_slot,
|
||||
struct extent_buffer **cow_ret);
|
||||
struct extent_buffer **cow_ret,
|
||||
enum btrfs_lock_nesting nest);
|
||||
int btrfs_copy_root(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct extent_buffer *buf,
|
||||
|
|
|
@ -1205,7 +1205,8 @@ struct btrfs_root *btrfs_create_tree(struct btrfs_trans_handle *trans,
|
|||
root->root_key.type = BTRFS_ROOT_ITEM_KEY;
|
||||
root->root_key.offset = 0;
|
||||
|
||||
leaf = btrfs_alloc_tree_block(trans, root, 0, objectid, NULL, 0, 0, 0);
|
||||
leaf = btrfs_alloc_tree_block(trans, root, 0, objectid, NULL, 0, 0, 0,
|
||||
BTRFS_NESTING_NORMAL);
|
||||
if (IS_ERR(leaf)) {
|
||||
ret = PTR_ERR(leaf);
|
||||
leaf = NULL;
|
||||
|
@ -1277,7 +1278,7 @@ static struct btrfs_root *alloc_log_tree(struct btrfs_trans_handle *trans,
|
|||
*/
|
||||
|
||||
leaf = btrfs_alloc_tree_block(trans, root, 0, BTRFS_TREE_LOG_OBJECTID,
|
||||
NULL, 0, 0, 0);
|
||||
NULL, 0, 0, 0, BTRFS_NESTING_NORMAL);
|
||||
if (IS_ERR(leaf)) {
|
||||
btrfs_put_root(root);
|
||||
return ERR_CAST(leaf);
|
||||
|
|
|
@ -4656,7 +4656,8 @@ int btrfs_alloc_logged_file_extent(struct btrfs_trans_handle *trans,
|
|||
|
||||
static struct extent_buffer *
|
||||
btrfs_init_new_buffer(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
u64 bytenr, int level, u64 owner)
|
||||
u64 bytenr, int level, u64 owner,
|
||||
enum btrfs_lock_nesting nest)
|
||||
{
|
||||
struct btrfs_fs_info *fs_info = root->fs_info;
|
||||
struct extent_buffer *buf;
|
||||
|
@ -4679,7 +4680,7 @@ btrfs_init_new_buffer(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
|||
}
|
||||
|
||||
btrfs_set_buffer_lockdep_class(owner, buf, level);
|
||||
btrfs_tree_lock(buf);
|
||||
__btrfs_tree_lock(buf, nest);
|
||||
btrfs_clean_tree_block(buf);
|
||||
clear_bit(EXTENT_BUFFER_STALE, &buf->bflags);
|
||||
|
||||
|
@ -4725,7 +4726,8 @@ struct extent_buffer *btrfs_alloc_tree_block(struct btrfs_trans_handle *trans,
|
|||
u64 parent, u64 root_objectid,
|
||||
const struct btrfs_disk_key *key,
|
||||
int level, u64 hint,
|
||||
u64 empty_size)
|
||||
u64 empty_size,
|
||||
enum btrfs_lock_nesting nest)
|
||||
{
|
||||
struct btrfs_fs_info *fs_info = root->fs_info;
|
||||
struct btrfs_key ins;
|
||||
|
@ -4741,7 +4743,7 @@ struct extent_buffer *btrfs_alloc_tree_block(struct btrfs_trans_handle *trans,
|
|||
#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
|
||||
if (btrfs_is_testing(fs_info)) {
|
||||
buf = btrfs_init_new_buffer(trans, root, root->alloc_bytenr,
|
||||
level, root_objectid);
|
||||
level, root_objectid, nest);
|
||||
if (!IS_ERR(buf))
|
||||
root->alloc_bytenr += blocksize;
|
||||
return buf;
|
||||
|
@ -4758,7 +4760,7 @@ struct extent_buffer *btrfs_alloc_tree_block(struct btrfs_trans_handle *trans,
|
|||
goto out_unuse;
|
||||
|
||||
buf = btrfs_init_new_buffer(trans, root, ins.objectid, level,
|
||||
root_objectid);
|
||||
root_objectid, nest);
|
||||
if (IS_ERR(buf)) {
|
||||
ret = PTR_ERR(buf);
|
||||
goto out_free_reserved;
|
||||
|
|
|
@ -628,7 +628,8 @@ static noinline int create_subvol(struct inode *dir,
|
|||
if (ret)
|
||||
goto fail;
|
||||
|
||||
leaf = btrfs_alloc_tree_block(trans, root, 0, objectid, NULL, 0, 0, 0);
|
||||
leaf = btrfs_alloc_tree_block(trans, root, 0, objectid, NULL, 0, 0, 0,
|
||||
BTRFS_NESTING_NORMAL);
|
||||
if (IS_ERR(leaf)) {
|
||||
ret = PTR_ERR(leaf);
|
||||
goto fail;
|
||||
|
|
|
@ -24,6 +24,14 @@
|
|||
enum btrfs_lock_nesting {
|
||||
BTRFS_NESTING_NORMAL,
|
||||
|
||||
/*
|
||||
* When we COW a block we are holding the lock on the original block,
|
||||
* and since our lockdep maps are rootid+level, this confuses lockdep
|
||||
* when we lock the newly allocated COW'd block. Handle this by having
|
||||
* a subclass for COW'ed blocks so that lockdep doesn't complain.
|
||||
*/
|
||||
BTRFS_NESTING_COW,
|
||||
|
||||
/*
|
||||
* We are limited to MAX_LOCKDEP_SUBLCLASSES number of subclasses, so
|
||||
* add this in here and add a static_assert to keep us from going over
|
||||
|
|
|
@ -1206,7 +1206,8 @@ again:
|
|||
}
|
||||
|
||||
if (cow) {
|
||||
ret = btrfs_cow_block(trans, dest, eb, NULL, 0, &eb);
|
||||
ret = btrfs_cow_block(trans, dest, eb, NULL, 0, &eb,
|
||||
BTRFS_NESTING_COW);
|
||||
BUG_ON(ret);
|
||||
}
|
||||
btrfs_set_lock_blocking_write(eb);
|
||||
|
@ -1274,7 +1275,8 @@ again:
|
|||
btrfs_tree_lock(eb);
|
||||
if (cow) {
|
||||
ret = btrfs_cow_block(trans, dest, eb, parent,
|
||||
slot, &eb);
|
||||
slot, &eb,
|
||||
BTRFS_NESTING_COW);
|
||||
BUG_ON(ret);
|
||||
}
|
||||
btrfs_set_lock_blocking_write(eb);
|
||||
|
@ -1781,7 +1783,8 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc,
|
|||
* relocated and the block is tree root.
|
||||
*/
|
||||
leaf = btrfs_lock_root_node(root);
|
||||
ret = btrfs_cow_block(trans, root, leaf, NULL, 0, &leaf);
|
||||
ret = btrfs_cow_block(trans, root, leaf, NULL, 0, &leaf,
|
||||
BTRFS_NESTING_COW);
|
||||
btrfs_tree_unlock(leaf);
|
||||
free_extent_buffer(leaf);
|
||||
if (ret < 0)
|
||||
|
@ -2308,7 +2311,7 @@ static int do_relocation(struct btrfs_trans_handle *trans,
|
|||
|
||||
if (!node->eb) {
|
||||
ret = btrfs_cow_block(trans, root, eb, upper->eb,
|
||||
slot, &eb);
|
||||
slot, &eb, BTRFS_NESTING_COW);
|
||||
btrfs_tree_unlock(eb);
|
||||
free_extent_buffer(eb);
|
||||
if (ret < 0) {
|
||||
|
|
|
@ -1184,7 +1184,7 @@ static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans)
|
|||
|
||||
eb = btrfs_lock_root_node(fs_info->tree_root);
|
||||
ret = btrfs_cow_block(trans, fs_info->tree_root, eb, NULL,
|
||||
0, &eb);
|
||||
0, &eb, BTRFS_NESTING_COW);
|
||||
btrfs_tree_unlock(eb);
|
||||
free_extent_buffer(eb);
|
||||
|
||||
|
@ -1589,7 +1589,8 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
|
|||
btrfs_set_root_otransid(new_root_item, trans->transid);
|
||||
|
||||
old = btrfs_lock_root_node(root);
|
||||
ret = btrfs_cow_block(trans, root, old, NULL, 0, &old);
|
||||
ret = btrfs_cow_block(trans, root, old, NULL, 0, &old,
|
||||
BTRFS_NESTING_COW);
|
||||
if (ret) {
|
||||
btrfs_tree_unlock(old);
|
||||
free_extent_buffer(old);
|
||||
|
|
Загрузка…
Ссылка в новой задаче