On Mon, Aug 25, 2014 at 02:26:49PM +0900, Naohiro Aota wrote: > Hi, list > > I'm having trouble with my btrfs FS recently and running btrfs check to > try to fix the FS. Unfortunately, it aborted with: > > btrfsck: root-tree.c:81: btrfs_update_root: Assertion `!(ret != 0)' failed. > > It means that "extent tree root" is not found in "tree root tree"! Then > I added btrfs_print_leaf() there to see what is happening there. There > were "(... METADATA_ITEM 0)" keys listed. Well, I found "tree root > tree"'s root extent buffer is somewhat replaced by a extent buffer from > the extent tree. > > Reading the code, it seems that free_some_buffers() reclaim extent > buffers allocated to root trees because they are not > extent_buffer_get()ed (i.e. @refs == 1). > > To reproduce this problem, try running this code. This program first > print root tree node's bytenr, and scan some trees. If your FS is large > enough to run free_some_buffers(), tree root node's bytenr after the > scan would be different. > > #include <stdio.h> > #include "ctree.h" > #include "disk-io.h" > > void scan_tree(struct btrfs_root *root, struct extent_buffer *eb) > { > u32 i; > u32 nr; > nr = btrfs_header_nritems(eb); > if (btrfs_is_leaf(eb)) return; > u32 size = btrfs_level_size(root, btrfs_header_level(eb) - 1); > for (i = 0; i < nr; i++) { > if (btrfs_is_leaf(eb)) return; > u64 bytenr = btrfs_node_blockptr(eb, i); > struct extent_buffer *next = read_tree_block(root, bytenr, size, > btrfs_node_ptr_generation(eb, > i)); > if (!next) continue; > scan_tree(root, next); > } > } > > int main(int ac, char **av) > { > struct btrfs_fs_info *info; > struct btrfs_root *root; > info = open_ctree_fs_info(av[1], 0, 0, OPEN_CTREE_PARTIAL); > root = info->fs_root; > printf("tree root %lld\n", info->tree_root->node->start); > scan_tree(info->fs_root, info->extent_root->node); > scan_tree(info->fs_root, info->csum_root->node); > scan_tree(info->fs_root, info->fs_root->node); > printf("tree root %lld\n", info->tree_root->node->start); > return close_ctree(root); > } > > On my environment, the above code print the following result. "Tree root > tree variable" is eventually pointing to another extent! > > $ ./btrfs-reproduce /dev/sda3 > tree root 91393835008 > tree root 49102848 > > I found commit 53ee1bccf99cd5b474fe1aa857b7dd176e3a1407 changed the > initial @refs to 1, stating that "we don't give enough > free_extent_buffer() to reduce the eb's references to zero so that the > eb can finally be freed", but I don't think this is correct. Even if > initial @refs == 2, one free_extent_buffer() would make the @refs to 1 > and so let it reclaimed by free_some_buffer(), so it does not seems to > be a problem for me... > > I think there are some collides how to use extent buffer: should > __alloc_extent_buffer set @refs = 2 for the caller or should the code > call extent_buffer_get() by themselves everywhere you allocate eb before > any other eb allocation not to let the first eb reclaimed? How to fix > this problem? revert 53ee1bccf99cd5b474fe1aa857b7dd176e3a1407 is the > collect way? or add "missing" extent_buffer_get() everywhere allocating > is done?
You may think of it twice, commit 53ee1bccf99cd5b474fe1aa857b7dd176e3a1407 is to fix a bug of assigning a free block to two different extent buffers, ie. two different extent buffers' share the same eb->start, so it's not just bumping a reference cnt. Right now we want to be consistent with the kernel side, decreasing eb->refs=0 means it'd be freed, so droping free_some_buffer() can be a good choice. And for caching extent buffer, we've increased eb->refs by 1 to keep it in the cache rbtree. thanks, -liubo -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html