|
@@ -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
|
|
|
|
- * passed csum validation but before the pages are unlocked.
|
|
|
|
|
|
+/*
|
|
|
|
+ * Lockdep class keys for extent_buffer->lock's in this root. For a given
|
|
|
|
+ * 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
|
|
|
|
- * allocated blocks.
|
|
|
|
|
|
+ * Lock-nesting across peer nodes is always done with the immediate parent
|
|
|
|
+ * 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
|
|
|
|
- * to know that lower nodes nest inside the locks of higher nodes.
|
|
|
|
|
|
+ * The key is set by the readpage_end_io_hook after the buffer has passed
|
|
|
|
+ * 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
|
|
|
|
- * the same as our lockdep setup here. If BTRFS_MAX_LEVEL changes, this
|
|
|
|
- * code needs update as well.
|
|
|
|
|
|
+ * We also add a check to make sure the highest level of the tree is the
|
|
|
|
+ * same as our lockdep setup here. If BTRFS_MAX_LEVEL changes, this code
|
|
|
|
+ * 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] = {
|
|
|
|
- /* leaf */
|
|
|
|
- "btrfs-extent-00",
|
|
|
|
- "btrfs-extent-01",
|
|
|
|
- "btrfs-extent-02",
|
|
|
|
- "btrfs-extent-03",
|
|
|
|
- "btrfs-extent-04",
|
|
|
|
- "btrfs-extent-05",
|
|
|
|
- "btrfs-extent-06",
|
|
|
|
- "btrfs-extent-07",
|
|
|
|
- /* highest possible level */
|
|
|
|
- "btrfs-extent-08",
|
|
|
|
|
|
+
|
|
|
|
+static struct btrfs_lockdep_keyset {
|
|
|
|
+ u64 id; /* root objectid */
|
|
|
|
+ const char *name_stem; /* lock name stem */
|
|
|
|
+ char names[BTRFS_MAX_LEVEL + 1][20];
|
|
|
|
+ struct lock_class_key keys[BTRFS_MAX_LEVEL + 1];
|
|
|
|
+} btrfs_lockdep_keysets[] = {
|
|
|
|
+ { .id = BTRFS_ROOT_TREE_OBJECTID, .name_stem = "root" },
|
|
|
|
+ { .id = BTRFS_EXTENT_TREE_OBJECTID, .name_stem = "extent" },
|
|
|
|
+ { .id = BTRFS_CHUNK_TREE_OBJECTID, .name_stem = "chunk" },
|
|
|
|
+ { .id = BTRFS_DEV_TREE_OBJECTID, .name_stem = "dev" },
|
|
|
|
+ { .id = BTRFS_FS_TREE_OBJECTID, .name_stem = "fs" },
|
|
|
|
+ { .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,
|
|
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,
|
|
}
|
|
}
|
|
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) {
|