diff options
Diffstat (limited to 'fs/btrfs/disk-io.c')
| -rw-r--r-- | fs/btrfs/disk-io.c | 135 | 
1 files changed, 107 insertions, 28 deletions
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index c34c0c60935..3d4bf6833f2 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -154,41 +154,96 @@ static int __commit_transaction(struct btrfs_root *root)  	return ret;  } +static int commit_extent_and_tree_roots(struct btrfs_root *tree_root, +					struct btrfs_root *extent_root) +{ +	int ret; +	u64 old_extent_block; + +	while(1) { +		old_extent_block = btrfs_root_blocknr(&extent_root->root_item); +		if (old_extent_block == extent_root->node->blocknr) +			break; +		btrfs_set_root_blocknr(&extent_root->root_item, +				       extent_root->node->blocknr); +		ret = btrfs_update_root(tree_root, +					&extent_root->root_key, +					&extent_root->root_item); +		BUG_ON(ret); +	} +	__commit_transaction(extent_root); +	__commit_transaction(tree_root); +	return 0; +} +  int btrfs_commit_transaction(struct btrfs_root *root,  			     struct btrfs_super_block *s)  {  	int ret = 0; +	struct btrfs_buffer *snap = root->commit_root; +	struct btrfs_key snap_key;  	ret = __commit_transaction(root); -	if (!ret && root != root->extent_root) -		ret = __commit_transaction(root->extent_root);  	BUG_ON(ret); -	if (root->commit_root != root->node) { -		struct btrfs_buffer *snap = root->commit_root; -		root->commit_root = root->node; -		root->node->count++; -		ret = btrfs_drop_snapshot(root, snap); -		BUG_ON(ret); -		// btrfs_block_release(root, snap); -	} + +	if (root->commit_root == root->node) +		return 0; + +	memcpy(&snap_key, &root->root_key, sizeof(snap_key)); +	root->root_key.offset++; + +	btrfs_set_root_blocknr(&root->root_item, root->node->blocknr); +	ret = btrfs_insert_root(root->tree_root, &root->root_key, +				&root->root_item); +	BUG_ON(ret); + +	ret = commit_extent_and_tree_roots(root->tree_root, root->extent_root); +	BUG_ON(ret); +          write_ctree_super(root, s); -	btrfs_finish_extent_commit(root); +	btrfs_finish_extent_commit(root->extent_root); +	btrfs_finish_extent_commit(root->tree_root); + +	root->commit_root = root->node; +	root->node->count++; +	ret = btrfs_drop_snapshot(root, snap); +	BUG_ON(ret); + +	ret = btrfs_del_root(root->tree_root, &snap_key); +	BUG_ON(ret); +  	return ret;  } -static int __setup_root(struct btrfs_root *root, struct btrfs_root *extent_root, -			struct btrfs_root_info *info, int fp) +static int __setup_root(struct btrfs_root *root, u64 objectid, int fp)  {  	INIT_LIST_HEAD(&root->trans);  	INIT_LIST_HEAD(&root->cache);  	root->cache_size = 0;  	root->fp = fp;  	root->node = NULL; -	root->extent_root = extent_root;  	root->commit_root = NULL; -	root->node = read_tree_block(root, info->tree_root);  	memset(&root->current_insert, 0, sizeof(root->current_insert));  	memset(&root->last_insert, 0, sizeof(root->last_insert)); +	memset(&root->root_key, 0, sizeof(root->root_key)); +	memset(&root->root_item, 0, sizeof(root->root_item)); +	return 0; +} + +static int find_and_setup_root(struct btrfs_root *tree_root, u64 objectid, +			struct btrfs_root *root, int fp) +{ +	int ret; + +	__setup_root(root, objectid, fp); +	ret = btrfs_find_last_root(tree_root, objectid, +				   &root->root_item, &root->root_key); +	BUG_ON(ret); + +	root->node = read_tree_block(root, +				     btrfs_root_blocknr(&root->root_item)); +	root->ref_cows = 0; +	BUG_ON(!root->node);  	return 0;  } @@ -196,9 +251,19 @@ struct btrfs_root *open_ctree(char *filename, struct btrfs_super_block *super)  {  	struct btrfs_root *root = malloc(sizeof(struct btrfs_root));  	struct btrfs_root *extent_root = malloc(sizeof(struct btrfs_root)); +	struct btrfs_root *tree_root = malloc(sizeof(struct btrfs_root));  	int fp;  	int ret; +	root->extent_root = extent_root; +	root->tree_root = tree_root; + +	extent_root->extent_root = extent_root; +	extent_root->tree_root = tree_root; + +	tree_root->extent_root = extent_root; +	tree_root->tree_root = tree_root; +  	fp = open(filename, O_CREAT | O_RDWR, 0600);  	if (fp < 0) {  		free(root); @@ -208,11 +273,14 @@ struct btrfs_root *open_ctree(char *filename, struct btrfs_super_block *super)  	INIT_RADIX_TREE(&root->pinned_radix, GFP_KERNEL);  	INIT_RADIX_TREE(&extent_root->pinned_radix, GFP_KERNEL);  	INIT_RADIX_TREE(&extent_root->cache_radix, GFP_KERNEL); +	INIT_RADIX_TREE(&tree_root->pinned_radix, GFP_KERNEL); +	INIT_RADIX_TREE(&tree_root->cache_radix, GFP_KERNEL); +  	ret = pread(fp, super, sizeof(struct btrfs_super_block),  		     BTRFS_SUPER_INFO_OFFSET(BTRFS_BLOCKSIZE)); -	if (ret == 0 || super->root_info.tree_root == 0) { +	if (ret == 0 || btrfs_super_root(super) == 0) {  		printf("making new FS!\n"); -		ret = mkfs(fp); +		ret = mkfs(fp, 0, BTRFS_BLOCKSIZE);  		if (ret)  			return NULL;  		ret = pread(fp, super, sizeof(struct btrfs_super_block), @@ -221,24 +289,29 @@ struct btrfs_root *open_ctree(char *filename, struct btrfs_super_block *super)  			return NULL;  	}  	BUG_ON(ret < 0); -	__setup_root(root, extent_root, &super->root_info, fp); -	__setup_root(extent_root, extent_root, &super->extent_info, fp); + +	__setup_root(tree_root, BTRFS_ROOT_TREE_OBJECTID, fp); +	tree_root->node = read_tree_block(tree_root, btrfs_super_root(super)); +	BUG_ON(!tree_root->node); + +	ret = find_and_setup_root(tree_root, BTRFS_EXTENT_TREE_OBJECTID, +				  extent_root, fp); +	BUG_ON(ret); + +	ret = find_and_setup_root(tree_root, BTRFS_FS_TREE_OBJECTID, +				  root, fp); +	BUG_ON(ret); +  	root->commit_root = root->node;  	root->node->count++; +	root->ref_cows = 1;  	return root;  } -static int __update_root(struct btrfs_root *root, struct btrfs_root_info *info) -{ -	info->tree_root = root->node->blocknr; -	return 0; -} -  int write_ctree_super(struct btrfs_root *root, struct btrfs_super_block *s)  {  	int ret; -	__update_root(root, &s->root_info); -	__update_root(root->extent_root, &s->extent_info); +	btrfs_set_super_root(s, root->tree_root->node->blocknr);  	ret = pwrite(root->fp, s, sizeof(*s),  		     BTRFS_SUPER_INFO_OFFSET(BTRFS_BLOCKSIZE));  	if (ret != sizeof(*s)) { @@ -260,19 +333,25 @@ static int drop_cache(struct btrfs_root *root)  }  int close_ctree(struct btrfs_root *root, struct btrfs_super_block *s)  { +	int ret;  	btrfs_commit_transaction(root, s); -	__commit_transaction(root->extent_root); +	ret = commit_extent_and_tree_roots(root->tree_root, root->extent_root); +	BUG_ON(ret);  	write_ctree_super(root, s);  	drop_cache(root->extent_root); +	drop_cache(root->tree_root);  	drop_cache(root);  	BUG_ON(!list_empty(&root->trans));  	BUG_ON(!list_empty(&root->extent_root->trans)); +	BUG_ON(!list_empty(&root->tree_root->trans));  	close(root->fp);  	if (root->node)  		btrfs_block_release(root, root->node);  	if (root->extent_root->node)  		btrfs_block_release(root->extent_root, root->extent_root->node); +	if (root->tree_root->node) +		btrfs_block_release(root->tree_root, root->tree_root->node);  	btrfs_block_release(root, root->commit_root);  	free(root);  	printf("on close %d blocks are allocated\n", allocated_blocks);  |