diff options
Diffstat (limited to 'fs/btrfs/disk-io.c')
| -rw-r--r-- | fs/btrfs/disk-io.c | 189 | 
1 files changed, 158 insertions, 31 deletions
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 7b845ff4af99..62e0cafd6e25 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -407,7 +407,7 @@ static int btree_read_extent_buffer_pages(struct btrfs_root *root,  			break;  	} -	if (failed && !ret) +	if (failed && !ret && failed_mirror)  		repair_eb_io_failure(root, eb, failed_mirror);  	return ret; @@ -1114,7 +1114,7 @@ void clean_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root,  				spin_unlock(&root->fs_info->delalloc_lock);  				btrfs_panic(root->fs_info, -EOVERFLOW,  					  "Can't clear %lu bytes from " -					  " dirty_mdatadata_bytes (%lu)", +					  " dirty_mdatadata_bytes (%llu)",  					  buf->len,  					  root->fs_info->dirty_metadata_bytes);  			} @@ -1182,6 +1182,8 @@ static void __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,  	root->defrag_running = 0;  	root->root_key.objectid = objectid;  	root->anon_dev = 0; + +	spin_lock_init(&root->root_times_lock);  }  static int __must_check find_and_setup_root(struct btrfs_root *tree_root, @@ -1225,6 +1227,82 @@ static struct btrfs_root *btrfs_alloc_root(struct btrfs_fs_info *fs_info)  	return root;  } +struct btrfs_root *btrfs_create_tree(struct btrfs_trans_handle *trans, +				     struct btrfs_fs_info *fs_info, +				     u64 objectid) +{ +	struct extent_buffer *leaf; +	struct btrfs_root *tree_root = fs_info->tree_root; +	struct btrfs_root *root; +	struct btrfs_key key; +	int ret = 0; +	u64 bytenr; + +	root = btrfs_alloc_root(fs_info); +	if (!root) +		return ERR_PTR(-ENOMEM); + +	__setup_root(tree_root->nodesize, tree_root->leafsize, +		     tree_root->sectorsize, tree_root->stripesize, +		     root, fs_info, objectid); +	root->root_key.objectid = objectid; +	root->root_key.type = BTRFS_ROOT_ITEM_KEY; +	root->root_key.offset = 0; + +	leaf = btrfs_alloc_free_block(trans, root, root->leafsize, +				      0, objectid, NULL, 0, 0, 0); +	if (IS_ERR(leaf)) { +		ret = PTR_ERR(leaf); +		goto fail; +	} + +	bytenr = leaf->start; +	memset_extent_buffer(leaf, 0, 0, sizeof(struct btrfs_header)); +	btrfs_set_header_bytenr(leaf, leaf->start); +	btrfs_set_header_generation(leaf, trans->transid); +	btrfs_set_header_backref_rev(leaf, BTRFS_MIXED_BACKREF_REV); +	btrfs_set_header_owner(leaf, objectid); +	root->node = leaf; + +	write_extent_buffer(leaf, fs_info->fsid, +			    (unsigned long)btrfs_header_fsid(leaf), +			    BTRFS_FSID_SIZE); +	write_extent_buffer(leaf, fs_info->chunk_tree_uuid, +			    (unsigned long)btrfs_header_chunk_tree_uuid(leaf), +			    BTRFS_UUID_SIZE); +	btrfs_mark_buffer_dirty(leaf); + +	root->commit_root = btrfs_root_node(root); +	root->track_dirty = 1; + + +	root->root_item.flags = 0; +	root->root_item.byte_limit = 0; +	btrfs_set_root_bytenr(&root->root_item, leaf->start); +	btrfs_set_root_generation(&root->root_item, trans->transid); +	btrfs_set_root_level(&root->root_item, 0); +	btrfs_set_root_refs(&root->root_item, 1); +	btrfs_set_root_used(&root->root_item, leaf->len); +	btrfs_set_root_last_snapshot(&root->root_item, 0); +	btrfs_set_root_dirid(&root->root_item, 0); +	root->root_item.drop_level = 0; + +	key.objectid = objectid; +	key.type = BTRFS_ROOT_ITEM_KEY; +	key.offset = 0; +	ret = btrfs_insert_root(trans, tree_root, &key, &root->root_item); +	if (ret) +		goto fail; + +	btrfs_tree_unlock(leaf); + +fail: +	if (ret) +		return ERR_PTR(ret); + +	return root; +} +  static struct btrfs_root *alloc_log_tree(struct btrfs_trans_handle *trans,  					 struct btrfs_fs_info *fs_info)  { @@ -1326,6 +1404,7 @@ struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_root *tree_root,  	u64 generation;  	u32 blocksize;  	int ret = 0; +	int slot;  	root = btrfs_alloc_root(fs_info);  	if (!root) @@ -1352,9 +1431,8 @@ struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_root *tree_root,  	ret = btrfs_search_slot(NULL, tree_root, location, path, 0, 0);  	if (ret == 0) {  		l = path->nodes[0]; -		read_extent_buffer(l, &root->root_item, -				btrfs_item_ptr_offset(l, path->slots[0]), -				sizeof(root->root_item)); +		slot = path->slots[0]; +		btrfs_read_root_item(tree_root, l, slot, &root->root_item);  		memcpy(&root->root_key, location, sizeof(*location));  	}  	btrfs_free_path(path); @@ -1396,6 +1474,9 @@ struct btrfs_root *btrfs_read_fs_root_no_name(struct btrfs_fs_info *fs_info,  		return fs_info->dev_root;  	if (location->objectid == BTRFS_CSUM_TREE_OBJECTID)  		return fs_info->csum_root; +	if (location->objectid == BTRFS_QUOTA_TREE_OBJECTID) +		return fs_info->quota_root ? fs_info->quota_root : +					     ERR_PTR(-ENOENT);  again:  	spin_lock(&fs_info->fs_roots_radix_lock);  	root = radix_tree_lookup(&fs_info->fs_roots_radix, @@ -1533,8 +1614,6 @@ static int cleaner_kthread(void *arg)  	struct btrfs_root *root = arg;  	do { -		vfs_check_frozen(root->fs_info->sb, SB_FREEZE_WRITE); -  		if (!(root->fs_info->sb->s_flags & MS_RDONLY) &&  		    mutex_trylock(&root->fs_info->cleaner_mutex)) {  			btrfs_run_delayed_iputs(root); @@ -1566,7 +1645,6 @@ static int transaction_kthread(void *arg)  	do {  		cannot_commit = false;  		delay = HZ * 30; -		vfs_check_frozen(root->fs_info->sb, SB_FREEZE_WRITE);  		mutex_lock(&root->fs_info->transaction_kthread_mutex);  		spin_lock(&root->fs_info->trans_lock); @@ -1823,6 +1901,10 @@ static void free_root_pointers(struct btrfs_fs_info *info, int chunk_root)  	free_extent_buffer(info->extent_root->commit_root);  	free_extent_buffer(info->csum_root->node);  	free_extent_buffer(info->csum_root->commit_root); +	if (info->quota_root) { +		free_extent_buffer(info->quota_root->node); +		free_extent_buffer(info->quota_root->commit_root); +	}  	info->tree_root->node = NULL;  	info->tree_root->commit_root = NULL; @@ -1832,6 +1914,10 @@ static void free_root_pointers(struct btrfs_fs_info *info, int chunk_root)  	info->extent_root->commit_root = NULL;  	info->csum_root->node = NULL;  	info->csum_root->commit_root = NULL; +	if (info->quota_root) { +		info->quota_root->node = NULL; +		info->quota_root->commit_root = NULL; +	}  	if (chunk_root) {  		free_extent_buffer(info->chunk_root->node); @@ -1862,6 +1948,7 @@ int open_ctree(struct super_block *sb,  	struct btrfs_root *csum_root;  	struct btrfs_root *chunk_root;  	struct btrfs_root *dev_root; +	struct btrfs_root *quota_root;  	struct btrfs_root *log_tree_root;  	int ret;  	int err = -EINVAL; @@ -1873,9 +1960,10 @@ int open_ctree(struct super_block *sb,  	csum_root = fs_info->csum_root = btrfs_alloc_root(fs_info);  	chunk_root = fs_info->chunk_root = btrfs_alloc_root(fs_info);  	dev_root = fs_info->dev_root = btrfs_alloc_root(fs_info); +	quota_root = fs_info->quota_root = btrfs_alloc_root(fs_info);  	if (!tree_root || !extent_root || !csum_root || -	    !chunk_root || !dev_root) { +	    !chunk_root || !dev_root || !quota_root) {  		err = -ENOMEM;  		goto fail;  	} @@ -1944,6 +2032,8 @@ int open_ctree(struct super_block *sb,  	fs_info->free_chunk_space = 0;  	fs_info->tree_mod_log = RB_ROOT; +	init_waitqueue_head(&fs_info->tree_mod_seq_wait); +  	/* readahead state */  	INIT_RADIX_TREE(&fs_info->reada_tree, GFP_NOFS & ~__GFP_WAIT);  	spin_lock_init(&fs_info->reada_lock); @@ -2032,6 +2122,13 @@ int open_ctree(struct super_block *sb,  	init_rwsem(&fs_info->cleanup_work_sem);  	init_rwsem(&fs_info->subvol_sem); +	spin_lock_init(&fs_info->qgroup_lock); +	fs_info->qgroup_tree = RB_ROOT; +	INIT_LIST_HEAD(&fs_info->dirty_qgroups); +	fs_info->qgroup_seq = 1; +	fs_info->quota_enabled = 0; +	fs_info->pending_quota_state = 0; +  	btrfs_init_free_cluster(&fs_info->meta_alloc_cluster);  	btrfs_init_free_cluster(&fs_info->data_alloc_cluster); @@ -2244,7 +2341,7 @@ int open_ctree(struct super_block *sb,  	ret |= btrfs_start_workers(&fs_info->caching_workers);  	ret |= btrfs_start_workers(&fs_info->readahead_workers);  	if (ret) { -		ret = -ENOMEM; +		err = -ENOMEM;  		goto fail_sb_buffer;  	} @@ -2354,12 +2451,28 @@ retry_root_backup:  				  BTRFS_CSUM_TREE_OBJECTID, csum_root);  	if (ret)  		goto recovery_tree_root; -  	csum_root->track_dirty = 1; +	ret = find_and_setup_root(tree_root, fs_info, +				  BTRFS_QUOTA_TREE_OBJECTID, quota_root); +	if (ret) { +		kfree(quota_root); +		quota_root = fs_info->quota_root = NULL; +	} else { +		quota_root->track_dirty = 1; +		fs_info->quota_enabled = 1; +		fs_info->pending_quota_state = 1; +	} +  	fs_info->generation = generation;  	fs_info->last_trans_committed = generation; +	ret = btrfs_recover_balance(fs_info); +	if (ret) { +		printk(KERN_WARNING "btrfs: failed to recover balance\n"); +		goto fail_block_groups; +	} +  	ret = btrfs_init_dev_stats(fs_info);  	if (ret) {  		printk(KERN_ERR "btrfs: failed to init dev_stats: %d\n", @@ -2410,6 +2523,9 @@ retry_root_backup:  			       " integrity check module %s\n", sb->s_id);  	}  #endif +	ret = btrfs_read_qgroup_config(fs_info); +	if (ret) +		goto fail_trans_kthread;  	/* do not make disk changes in broken FS */  	if (btrfs_super_log_root(disk_super) != 0 && @@ -2420,7 +2536,7 @@ retry_root_backup:  			printk(KERN_WARNING "Btrfs log replay required "  			       "on RO media\n");  			err = -EIO; -			goto fail_trans_kthread; +			goto fail_qgroup;  		}  		blocksize =  		     btrfs_level_size(tree_root, @@ -2429,7 +2545,7 @@ retry_root_backup:  		log_tree_root = btrfs_alloc_root(fs_info);  		if (!log_tree_root) {  			err = -ENOMEM; -			goto fail_trans_kthread; +			goto fail_qgroup;  		}  		__setup_root(nodesize, leafsize, sectorsize, stripesize, @@ -2461,15 +2577,15 @@ retry_root_backup:  	if (!(sb->s_flags & MS_RDONLY)) {  		ret = btrfs_cleanup_fs_roots(fs_info); -		if (ret) { -			} +		if (ret) +			goto fail_trans_kthread;  		ret = btrfs_recover_relocation(tree_root);  		if (ret < 0) {  			printk(KERN_WARNING  			       "btrfs: failed to recover relocation\n");  			err = -EINVAL; -			goto fail_trans_kthread; +			goto fail_qgroup;  		}  	} @@ -2479,30 +2595,35 @@ retry_root_backup:  	fs_info->fs_root = btrfs_read_fs_root_no_name(fs_info, &location);  	if (!fs_info->fs_root) -		goto fail_trans_kthread; +		goto fail_qgroup;  	if (IS_ERR(fs_info->fs_root)) {  		err = PTR_ERR(fs_info->fs_root); -		goto fail_trans_kthread; +		goto fail_qgroup;  	} -	if (!(sb->s_flags & MS_RDONLY)) { -		down_read(&fs_info->cleanup_work_sem); -		err = btrfs_orphan_cleanup(fs_info->fs_root); -		if (!err) -			err = btrfs_orphan_cleanup(fs_info->tree_root); -		up_read(&fs_info->cleanup_work_sem); +	if (sb->s_flags & MS_RDONLY) +		return 0; -		if (!err) -			err = btrfs_recover_balance(fs_info->tree_root); +	down_read(&fs_info->cleanup_work_sem); +	if ((ret = btrfs_orphan_cleanup(fs_info->fs_root)) || +	    (ret = btrfs_orphan_cleanup(fs_info->tree_root))) { +		up_read(&fs_info->cleanup_work_sem); +		close_ctree(tree_root); +		return ret; +	} +	up_read(&fs_info->cleanup_work_sem); -		if (err) { -			close_ctree(tree_root); -			return err; -		} +	ret = btrfs_resume_balance_async(fs_info); +	if (ret) { +		printk(KERN_WARNING "btrfs: failed to resume balance\n"); +		close_ctree(tree_root); +		return ret;  	}  	return 0; +fail_qgroup: +	btrfs_free_qgroup_config(fs_info);  fail_trans_kthread:  	kthread_stop(fs_info->transaction_kthread);  fail_cleaner: @@ -3101,6 +3222,8 @@ int close_ctree(struct btrfs_root *root)  	fs_info->closing = 2;  	smp_mb(); +	btrfs_free_qgroup_config(root->fs_info); +  	if (fs_info->delalloc_bytes) {  		printk(KERN_INFO "btrfs: at unmount delalloc count %llu\n",  		       (unsigned long long)fs_info->delalloc_bytes); @@ -3120,6 +3243,10 @@ int close_ctree(struct btrfs_root *root)  	free_extent_buffer(fs_info->dev_root->commit_root);  	free_extent_buffer(fs_info->csum_root->node);  	free_extent_buffer(fs_info->csum_root->commit_root); +	if (fs_info->quota_root) { +		free_extent_buffer(fs_info->quota_root->node); +		free_extent_buffer(fs_info->quota_root->commit_root); +	}  	btrfs_free_block_groups(fs_info); @@ -3250,7 +3377,7 @@ int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid)  	return btree_read_extent_buffer_pages(root, buf, 0, parent_transid);  } -static int btree_lock_page_hook(struct page *page, void *data, +int btree_lock_page_hook(struct page *page, void *data,  				void (*flush_fn)(void *))  {  	struct inode *inode = page->mapping->host;  | 
