Commit 85d4e461 authored by Chris Mason's avatar Chris Mason

Btrfs: make a lockdep class for each root

This patch was originally from Tejun Heo.  lockdep complains about the btrfs
locking because we sometimes take btree locks from two different trees at the
same time.  The current classes are based only on level in the btree, which
isn't enough information for lockdep to figure out if the lock is safe.

This patch makes a class for each type of tree, and lumps all the FS trees that
actually have files and directories into the same class.
Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent bd681513
...@@ -100,38 +100,83 @@ struct async_submit_bio { ...@@ -100,38 +100,83 @@ struct async_submit_bio {
struct btrfs_work work; struct btrfs_work work;
}; };
/* These are used to set the lockdep class on the extent buffer locks. /*
* The class is set by the readpage_end_io_hook after the buffer has * Lockdep class keys for extent_buffer->lock's in this root. For a given
* passed csum validation but before the pages are unlocked. * eb, the lockdep key is determined by the btrfs_root it belongs to and
* the level the eb occupies in the tree.
*
* Different roots are used for different purposes and may nest inside each
* other and they require separate keysets. As lockdep keys should be
* static, assign keysets according to the purpose of the root as indicated
* by btrfs_root->objectid. This ensures that all special purpose roots
* have separate keysets.
* *
* The lockdep class is also set by btrfs_init_new_buffer on freshly * Lock-nesting across peer nodes is always done with the immediate parent
* allocated blocks. * node locked thus preventing deadlock. As lockdep doesn't know this, use
* subclass to avoid triggering lockdep warning in such cases.
* *
* The class is based on the level in the tree block, which allows lockdep * The key is set by the readpage_end_io_hook after the buffer has passed
* to know that lower nodes nest inside the locks of higher nodes. * csum validation but before the pages are unlocked. It is also set by
* btrfs_init_new_buffer on freshly allocated blocks.
* *
* We also add a check to make sure the highest level of the tree is * We also add a check to make sure the highest level of the tree is the
* the same as our lockdep setup here. If BTRFS_MAX_LEVEL changes, this * same as our lockdep setup here. If BTRFS_MAX_LEVEL changes, this code
* code needs update as well. * needs update as well.
*/ */
#ifdef CONFIG_DEBUG_LOCK_ALLOC #ifdef CONFIG_DEBUG_LOCK_ALLOC
# if BTRFS_MAX_LEVEL != 8 # if BTRFS_MAX_LEVEL != 8
# error # error
# endif # endif
static struct lock_class_key btrfs_eb_class[BTRFS_MAX_LEVEL + 1];
static const char *btrfs_eb_name[BTRFS_MAX_LEVEL + 1] = { static struct btrfs_lockdep_keyset {
/* leaf */ u64 id; /* root objectid */
"btrfs-extent-00", const char *name_stem; /* lock name stem */
"btrfs-extent-01", char names[BTRFS_MAX_LEVEL + 1][20];
"btrfs-extent-02", struct lock_class_key keys[BTRFS_MAX_LEVEL + 1];
"btrfs-extent-03", } btrfs_lockdep_keysets[] = {
"btrfs-extent-04", { .id = BTRFS_ROOT_TREE_OBJECTID, .name_stem = "root" },
"btrfs-extent-05", { .id = BTRFS_EXTENT_TREE_OBJECTID, .name_stem = "extent" },
"btrfs-extent-06", { .id = BTRFS_CHUNK_TREE_OBJECTID, .name_stem = "chunk" },
"btrfs-extent-07", { .id = BTRFS_DEV_TREE_OBJECTID, .name_stem = "dev" },
/* highest possible level */ { .id = BTRFS_FS_TREE_OBJECTID, .name_stem = "fs" },
"btrfs-extent-08", { .id = BTRFS_CSUM_TREE_OBJECTID, .name_stem = "csum" },
{ .id = BTRFS_ORPHAN_OBJECTID, .name_stem = "orphan" },
{ .id = BTRFS_TREE_LOG_OBJECTID, .name_stem = "log" },
{ .id = BTRFS_TREE_RELOC_OBJECTID, .name_stem = "treloc" },
{ .id = BTRFS_DATA_RELOC_TREE_OBJECTID, .name_stem = "dreloc" },
{ .id = 0, .name_stem = "tree" },
}; };
void __init btrfs_init_lockdep(void)
{
int i, j;
/* initialize lockdep class names */
for (i = 0; i < ARRAY_SIZE(btrfs_lockdep_keysets); i++) {
struct btrfs_lockdep_keyset *ks = &btrfs_lockdep_keysets[i];
for (j = 0; j < ARRAY_SIZE(ks->names); j++)
snprintf(ks->names[j], sizeof(ks->names[j]),
"btrfs-%s-%02d", ks->name_stem, j);
}
}
void btrfs_set_buffer_lockdep_class(u64 objectid, struct extent_buffer *eb,
int level)
{
struct btrfs_lockdep_keyset *ks;
BUG_ON(level >= ARRAY_SIZE(ks->keys));
/* find the matching keyset, id 0 is the default entry */
for (ks = btrfs_lockdep_keysets; ks->id; ks++)
if (ks->id == objectid)
break;
lockdep_set_class_and_name(&eb->lock,
&ks->keys[level], ks->names[level]);
}
#endif #endif
/* /*
...@@ -491,15 +536,6 @@ static noinline int check_leaf(struct btrfs_root *root, ...@@ -491,15 +536,6 @@ static noinline int check_leaf(struct btrfs_root *root,
return 0; return 0;
} }
#ifdef CONFIG_DEBUG_LOCK_ALLOC
void btrfs_set_buffer_lockdep_class(struct extent_buffer *eb, int level)
{
lockdep_set_class_and_name(&eb->lock,
&btrfs_eb_class[level],
btrfs_eb_name[level]);
}
#endif
static int btree_readpage_end_io_hook(struct page *page, u64 start, u64 end, static int btree_readpage_end_io_hook(struct page *page, u64 start, u64 end,
struct extent_state *state) struct extent_state *state)
{ {
...@@ -550,7 +586,8 @@ static int btree_readpage_end_io_hook(struct page *page, u64 start, u64 end, ...@@ -550,7 +586,8 @@ static int btree_readpage_end_io_hook(struct page *page, u64 start, u64 end,
} }
found_level = btrfs_header_level(eb); found_level = btrfs_header_level(eb);
btrfs_set_buffer_lockdep_class(eb, found_level); btrfs_set_buffer_lockdep_class(btrfs_header_owner(eb),
eb, found_level);
ret = csum_tree_block(root, eb, 1); ret = csum_tree_block(root, eb, 1);
if (ret) { if (ret) {
......
...@@ -87,10 +87,14 @@ int btree_lock_page_hook(struct page *page); ...@@ -87,10 +87,14 @@ int btree_lock_page_hook(struct page *page);
#ifdef CONFIG_DEBUG_LOCK_ALLOC #ifdef CONFIG_DEBUG_LOCK_ALLOC
void btrfs_set_buffer_lockdep_class(struct extent_buffer *eb, int level); void btrfs_init_lockdep(void);
void btrfs_set_buffer_lockdep_class(u64 objectid,
struct extent_buffer *eb, int level);
#else #else
static inline void btrfs_set_buffer_lockdep_class(struct extent_buffer *eb, static inline void btrfs_init_lockdep(void)
int level) { }
static inline void btrfs_set_buffer_lockdep_class(u64 objectid,
struct extent_buffer *eb, int level)
{ {
} }
#endif #endif
......
...@@ -5625,7 +5625,7 @@ struct extent_buffer *btrfs_init_new_buffer(struct btrfs_trans_handle *trans, ...@@ -5625,7 +5625,7 @@ struct extent_buffer *btrfs_init_new_buffer(struct btrfs_trans_handle *trans,
if (!buf) if (!buf)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
btrfs_set_header_generation(buf, trans->transid); btrfs_set_header_generation(buf, trans->transid);
btrfs_set_buffer_lockdep_class(buf, level); btrfs_set_buffer_lockdep_class(root->root_key.objectid, buf, level);
btrfs_tree_lock(buf); btrfs_tree_lock(buf);
clean_tree_block(trans, root, buf); clean_tree_block(trans, root, buf);
......
...@@ -3595,7 +3595,7 @@ int btrfs_read_sys_array(struct btrfs_root *root) ...@@ -3595,7 +3595,7 @@ int btrfs_read_sys_array(struct btrfs_root *root)
if (!sb) if (!sb)
return -ENOMEM; return -ENOMEM;
btrfs_set_buffer_uptodate(sb); btrfs_set_buffer_uptodate(sb);
btrfs_set_buffer_lockdep_class(sb, 0); btrfs_set_buffer_lockdep_class(root->root_key.objectid, sb, 0);
write_extent_buffer(sb, super_copy, 0, BTRFS_SUPER_INFO_SIZE); write_extent_buffer(sb, super_copy, 0, BTRFS_SUPER_INFO_SIZE);
array_size = btrfs_super_sys_array_size(super_copy); array_size = btrfs_super_sys_array_size(super_copy);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment