Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Chris Mason <chris.mason@oracle.com>

+79 -38
+70 -33
fs/btrfs/disk-io.c
··· 100 100 struct btrfs_work work; 101 101 }; 102 102 103 - /* These are used to set the lockdep class on the extent buffer locks. 104 - * The class is set by the readpage_end_io_hook after the buffer has 105 - * passed csum validation but before the pages are unlocked. 103 + /* 104 + * Lockdep class keys for extent_buffer->lock's in this root. For a given 105 + * eb, the lockdep key is determined by the btrfs_root it belongs to and 106 + * the level the eb occupies in the tree. 106 107 * 107 - * The lockdep class is also set by btrfs_init_new_buffer on freshly 108 - * allocated blocks. 108 + * Different roots are used for different purposes and may nest inside each 109 + * other and they require separate keysets. As lockdep keys should be 110 + * static, assign keysets according to the purpose of the root as indicated 111 + * by btrfs_root->objectid. This ensures that all special purpose roots 112 + * have separate keysets. 109 113 * 110 - * The class is based on the level in the tree block, which allows lockdep 111 - * to know that lower nodes nest inside the locks of higher nodes. 114 + * Lock-nesting across peer nodes is always done with the immediate parent 115 + * node locked thus preventing deadlock. As lockdep doesn't know this, use 116 + * subclass to avoid triggering lockdep warning in such cases. 112 117 * 113 - * We also add a check to make sure the highest level of the tree is 114 - * the same as our lockdep setup here. If BTRFS_MAX_LEVEL changes, this 115 - * code needs update as well. 118 + * The key is set by the readpage_end_io_hook after the buffer has passed 119 + * csum validation but before the pages are unlocked. It is also set by 120 + * btrfs_init_new_buffer on freshly allocated blocks. 121 + * 122 + * We also add a check to make sure the highest level of the tree is the 123 + * same as our lockdep setup here. If BTRFS_MAX_LEVEL changes, this code 124 + * needs update as well. 116 125 */ 117 126 #ifdef CONFIG_DEBUG_LOCK_ALLOC 118 127 # if BTRFS_MAX_LEVEL != 8 119 128 # error 120 129 # endif 121 - static struct lock_class_key btrfs_eb_class[BTRFS_MAX_LEVEL + 1]; 122 - static const char *btrfs_eb_name[BTRFS_MAX_LEVEL + 1] = { 123 - /* leaf */ 124 - "btrfs-extent-00", 125 - "btrfs-extent-01", 126 - "btrfs-extent-02", 127 - "btrfs-extent-03", 128 - "btrfs-extent-04", 129 - "btrfs-extent-05", 130 - "btrfs-extent-06", 131 - "btrfs-extent-07", 132 - /* highest possible level */ 133 - "btrfs-extent-08", 130 + 131 + static struct btrfs_lockdep_keyset { 132 + u64 id; /* root objectid */ 133 + const char *name_stem; /* lock name stem */ 134 + char names[BTRFS_MAX_LEVEL + 1][20]; 135 + struct lock_class_key keys[BTRFS_MAX_LEVEL + 1]; 136 + } btrfs_lockdep_keysets[] = { 137 + { .id = BTRFS_ROOT_TREE_OBJECTID, .name_stem = "root" }, 138 + { .id = BTRFS_EXTENT_TREE_OBJECTID, .name_stem = "extent" }, 139 + { .id = BTRFS_CHUNK_TREE_OBJECTID, .name_stem = "chunk" }, 140 + { .id = BTRFS_DEV_TREE_OBJECTID, .name_stem = "dev" }, 141 + { .id = BTRFS_FS_TREE_OBJECTID, .name_stem = "fs" }, 142 + { .id = BTRFS_CSUM_TREE_OBJECTID, .name_stem = "csum" }, 143 + { .id = BTRFS_ORPHAN_OBJECTID, .name_stem = "orphan" }, 144 + { .id = BTRFS_TREE_LOG_OBJECTID, .name_stem = "log" }, 145 + { .id = BTRFS_TREE_RELOC_OBJECTID, .name_stem = "treloc" }, 146 + { .id = BTRFS_DATA_RELOC_TREE_OBJECTID, .name_stem = "dreloc" }, 147 + { .id = 0, .name_stem = "tree" }, 134 148 }; 149 + 150 + void __init btrfs_init_lockdep(void) 151 + { 152 + int i, j; 153 + 154 + /* initialize lockdep class names */ 155 + for (i = 0; i < ARRAY_SIZE(btrfs_lockdep_keysets); i++) { 156 + struct btrfs_lockdep_keyset *ks = &btrfs_lockdep_keysets[i]; 157 + 158 + for (j = 0; j < ARRAY_SIZE(ks->names); j++) 159 + snprintf(ks->names[j], sizeof(ks->names[j]), 160 + "btrfs-%s-%02d", ks->name_stem, j); 161 + } 162 + } 163 + 164 + void btrfs_set_buffer_lockdep_class(u64 objectid, struct extent_buffer *eb, 165 + int level) 166 + { 167 + struct btrfs_lockdep_keyset *ks; 168 + 169 + BUG_ON(level >= ARRAY_SIZE(ks->keys)); 170 + 171 + /* find the matching keyset, id 0 is the default entry */ 172 + for (ks = btrfs_lockdep_keysets; ks->id; ks++) 173 + if (ks->id == objectid) 174 + break; 175 + 176 + lockdep_set_class_and_name(&eb->lock, 177 + &ks->keys[level], ks->names[level]); 178 + } 179 + 135 180 #endif 136 181 137 182 /* ··· 536 491 return 0; 537 492 } 538 493 539 - #ifdef CONFIG_DEBUG_LOCK_ALLOC 540 - void btrfs_set_buffer_lockdep_class(struct extent_buffer *eb, int level) 541 - { 542 - lockdep_set_class_and_name(&eb->lock, 543 - &btrfs_eb_class[level], 544 - btrfs_eb_name[level]); 545 - } 546 - #endif 547 - 548 494 static int btree_readpage_end_io_hook(struct page *page, u64 start, u64 end, 549 495 struct extent_state *state) 550 496 { ··· 586 550 } 587 551 found_level = btrfs_header_level(eb); 588 552 589 - btrfs_set_buffer_lockdep_class(eb, found_level); 553 + btrfs_set_buffer_lockdep_class(btrfs_header_owner(eb), 554 + eb, found_level); 590 555 591 556 ret = csum_tree_block(root, eb, 1); 592 557 if (ret) {
+7 -3
fs/btrfs/disk-io.h
··· 87 87 88 88 89 89 #ifdef CONFIG_DEBUG_LOCK_ALLOC 90 - void btrfs_set_buffer_lockdep_class(struct extent_buffer *eb, int level); 90 + void btrfs_init_lockdep(void); 91 + void btrfs_set_buffer_lockdep_class(u64 objectid, 92 + struct extent_buffer *eb, int level); 91 93 #else 92 - static inline void btrfs_set_buffer_lockdep_class(struct extent_buffer *eb, 93 - int level) 94 + static inline void btrfs_init_lockdep(void) 95 + { } 96 + static inline void btrfs_set_buffer_lockdep_class(u64 objectid, 97 + struct extent_buffer *eb, int level) 94 98 { 95 99 } 96 100 #endif
+1 -1
fs/btrfs/extent-tree.c
··· 5625 5625 if (!buf) 5626 5626 return ERR_PTR(-ENOMEM); 5627 5627 btrfs_set_header_generation(buf, trans->transid); 5628 - btrfs_set_buffer_lockdep_class(buf, level); 5628 + btrfs_set_buffer_lockdep_class(root->root_key.objectid, buf, level); 5629 5629 btrfs_tree_lock(buf); 5630 5630 clean_tree_block(trans, root, buf); 5631 5631
+1 -1
fs/btrfs/volumes.c
··· 3595 3595 if (!sb) 3596 3596 return -ENOMEM; 3597 3597 btrfs_set_buffer_uptodate(sb); 3598 - btrfs_set_buffer_lockdep_class(sb, 0); 3598 + btrfs_set_buffer_lockdep_class(root->root_key.objectid, sb, 0); 3599 3599 3600 3600 write_extent_buffer(sb, super_copy, 0, BTRFS_SUPER_INFO_SIZE); 3601 3601 array_size = btrfs_super_sys_array_size(super_copy);