Btrfs: Metadata reservation for orphan inodes
reserve metadata space for handling orphan inodes Signed-off-by: Yan Zheng <zheng.yan@oracle.com> Signed-off-by: Chris Mason <chris.mason@oracle.com>
This commit is contained in:
		
							parent
							
								
									8929ecfa50
								
							
						
					
					
						commit
						d68fc57b7e
					
				| @ -151,6 +151,7 @@ struct btrfs_inode { | ||||
| 	 * of these. | ||||
| 	 */ | ||||
| 	unsigned ordered_data_close:1; | ||||
| 	unsigned orphan_meta_reserved:1; | ||||
| 	unsigned dummy_inode:1; | ||||
| 
 | ||||
| 	/*
 | ||||
|  | ||||
| @ -1069,7 +1069,6 @@ struct btrfs_root { | ||||
| 	int ref_cows; | ||||
| 	int track_dirty; | ||||
| 	int in_radix; | ||||
| 	int clean_orphans; | ||||
| 
 | ||||
| 	u64 defrag_trans_start; | ||||
| 	struct btrfs_key defrag_progress; | ||||
| @ -1083,8 +1082,11 @@ struct btrfs_root { | ||||
| 
 | ||||
| 	struct list_head root_list; | ||||
| 
 | ||||
| 	spinlock_t list_lock; | ||||
| 	spinlock_t orphan_lock; | ||||
| 	struct list_head orphan_list; | ||||
| 	struct btrfs_block_rsv *orphan_block_rsv; | ||||
| 	int orphan_item_inserted; | ||||
| 	int orphan_cleanup_state; | ||||
| 
 | ||||
| 	spinlock_t inode_lock; | ||||
| 	/* red-black tree that keeps track of in-memory inodes */ | ||||
| @ -2080,6 +2082,9 @@ int btrfs_trans_reserve_metadata(struct btrfs_trans_handle *trans, | ||||
| 				int num_items, int *retries); | ||||
| void btrfs_trans_release_metadata(struct btrfs_trans_handle *trans, | ||||
| 				struct btrfs_root *root); | ||||
| int btrfs_orphan_reserve_metadata(struct btrfs_trans_handle *trans, | ||||
| 				  struct inode *inode); | ||||
| void btrfs_orphan_release_metadata(struct inode *inode); | ||||
| int btrfs_snap_reserve_metadata(struct btrfs_trans_handle *trans, | ||||
| 				struct btrfs_pending_snapshot *pending); | ||||
| int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes); | ||||
| @ -2404,6 +2409,13 @@ int btrfs_update_inode(struct btrfs_trans_handle *trans, | ||||
| int btrfs_orphan_add(struct btrfs_trans_handle *trans, struct inode *inode); | ||||
| int btrfs_orphan_del(struct btrfs_trans_handle *trans, struct inode *inode); | ||||
| void btrfs_orphan_cleanup(struct btrfs_root *root); | ||||
| void btrfs_orphan_pre_snapshot(struct btrfs_trans_handle *trans, | ||||
| 				struct btrfs_pending_snapshot *pending, | ||||
| 				u64 *bytes_to_reserve); | ||||
| void btrfs_orphan_post_snapshot(struct btrfs_trans_handle *trans, | ||||
| 				struct btrfs_pending_snapshot *pending); | ||||
| void btrfs_orphan_commit_root(struct btrfs_trans_handle *trans, | ||||
| 			      struct btrfs_root *root); | ||||
| int btrfs_cont_expand(struct inode *inode, loff_t size); | ||||
| int btrfs_invalidate_inodes(struct btrfs_root *root); | ||||
| void btrfs_add_delayed_iput(struct inode *inode); | ||||
|  | ||||
| @ -894,7 +894,8 @@ static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize, | ||||
| 	root->ref_cows = 0; | ||||
| 	root->track_dirty = 0; | ||||
| 	root->in_radix = 0; | ||||
| 	root->clean_orphans = 0; | ||||
| 	root->orphan_item_inserted = 0; | ||||
| 	root->orphan_cleanup_state = 0; | ||||
| 
 | ||||
| 	root->fs_info = fs_info; | ||||
| 	root->objectid = objectid; | ||||
| @ -904,12 +905,13 @@ static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize, | ||||
| 	root->in_sysfs = 0; | ||||
| 	root->inode_tree = RB_ROOT; | ||||
| 	root->block_rsv = NULL; | ||||
| 	root->orphan_block_rsv = NULL; | ||||
| 
 | ||||
| 	INIT_LIST_HEAD(&root->dirty_list); | ||||
| 	INIT_LIST_HEAD(&root->orphan_list); | ||||
| 	INIT_LIST_HEAD(&root->root_list); | ||||
| 	spin_lock_init(&root->node_lock); | ||||
| 	spin_lock_init(&root->list_lock); | ||||
| 	spin_lock_init(&root->orphan_lock); | ||||
| 	spin_lock_init(&root->inode_lock); | ||||
| 	spin_lock_init(&root->accounting_lock); | ||||
| 	mutex_init(&root->objectid_mutex); | ||||
| @ -1193,19 +1195,23 @@ again: | ||||
| 	if (root) | ||||
| 		return root; | ||||
| 
 | ||||
| 	ret = btrfs_find_orphan_item(fs_info->tree_root, location->objectid); | ||||
| 	if (ret == 0) | ||||
| 		ret = -ENOENT; | ||||
| 	if (ret < 0) | ||||
| 		return ERR_PTR(ret); | ||||
| 
 | ||||
| 	root = btrfs_read_fs_root_no_radix(fs_info->tree_root, location); | ||||
| 	if (IS_ERR(root)) | ||||
| 		return root; | ||||
| 
 | ||||
| 	WARN_ON(btrfs_root_refs(&root->root_item) == 0); | ||||
| 	set_anon_super(&root->anon_super, NULL); | ||||
| 
 | ||||
| 	if (btrfs_root_refs(&root->root_item) == 0) { | ||||
| 		ret = -ENOENT; | ||||
| 		goto fail; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = btrfs_find_orphan_item(fs_info->tree_root, location->objectid); | ||||
| 	if (ret < 0) | ||||
| 		goto fail; | ||||
| 	if (ret == 0) | ||||
| 		root->orphan_item_inserted = 1; | ||||
| 
 | ||||
| 	ret = radix_tree_preload(GFP_NOFS & ~__GFP_HIGHMEM); | ||||
| 	if (ret) | ||||
| 		goto fail; | ||||
| @ -1214,10 +1220,9 @@ again: | ||||
| 	ret = radix_tree_insert(&fs_info->fs_roots_radix, | ||||
| 				(unsigned long)root->root_key.objectid, | ||||
| 				root); | ||||
| 	if (ret == 0) { | ||||
| 	if (ret == 0) | ||||
| 		root->in_radix = 1; | ||||
| 		root->clean_orphans = 1; | ||||
| 	} | ||||
| 
 | ||||
| 	spin_unlock(&fs_info->fs_roots_radix_lock); | ||||
| 	radix_tree_preload_end(); | ||||
| 	if (ret) { | ||||
| @ -1981,6 +1986,9 @@ struct btrfs_root *open_ctree(struct super_block *sb, | ||||
| 	BUG_ON(ret); | ||||
| 
 | ||||
| 	if (!(sb->s_flags & MS_RDONLY)) { | ||||
| 		ret = btrfs_cleanup_fs_roots(fs_info); | ||||
| 		BUG_ON(ret); | ||||
| 
 | ||||
| 		ret = btrfs_recover_relocation(tree_root); | ||||
| 		if (ret < 0) { | ||||
| 			printk(KERN_WARNING | ||||
|  | ||||
| @ -3626,6 +3626,34 @@ void btrfs_trans_release_metadata(struct btrfs_trans_handle *trans, | ||||
| 	trans->bytes_reserved = 0; | ||||
| } | ||||
| 
 | ||||
| int btrfs_orphan_reserve_metadata(struct btrfs_trans_handle *trans, | ||||
| 				  struct inode *inode) | ||||
| { | ||||
| 	struct btrfs_root *root = BTRFS_I(inode)->root; | ||||
| 	struct btrfs_block_rsv *src_rsv = get_block_rsv(trans, root); | ||||
| 	struct btrfs_block_rsv *dst_rsv = root->orphan_block_rsv; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * one for deleting orphan item, one for updating inode and | ||||
| 	 * two for calling btrfs_truncate_inode_items. | ||||
| 	 * | ||||
| 	 * btrfs_truncate_inode_items is a delete operation, it frees | ||||
| 	 * more space than it uses in most cases. So two units of | ||||
| 	 * metadata space should be enough for calling it many times. | ||||
| 	 * If all of the metadata space is used, we can commit | ||||
| 	 * transaction and use space it freed. | ||||
| 	 */ | ||||
| 	u64 num_bytes = calc_trans_metadata_size(root, 4); | ||||
| 	return block_rsv_migrate_bytes(src_rsv, dst_rsv, num_bytes); | ||||
| } | ||||
| 
 | ||||
| void btrfs_orphan_release_metadata(struct inode *inode) | ||||
| { | ||||
| 	struct btrfs_root *root = BTRFS_I(inode)->root; | ||||
| 	u64 num_bytes = calc_trans_metadata_size(root, 4); | ||||
| 	btrfs_block_rsv_release(root, root->orphan_block_rsv, num_bytes); | ||||
| } | ||||
| 
 | ||||
| int btrfs_snap_reserve_metadata(struct btrfs_trans_handle *trans, | ||||
| 				struct btrfs_pending_snapshot *pending) | ||||
| { | ||||
|  | ||||
							
								
								
									
										303
									
								
								fs/btrfs/inode.c
									
									
									
									
									
								
							
							
						
						
									
										303
									
								
								fs/btrfs/inode.c
									
									
									
									
									
								
							| @ -1981,33 +1981,197 @@ void btrfs_run_delayed_iputs(struct btrfs_root *root) | ||||
| 	up_read(&root->fs_info->cleanup_work_sem); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * calculate extra metadata reservation when snapshotting a subvolume | ||||
|  * contains orphan files. | ||||
|  */ | ||||
| void btrfs_orphan_pre_snapshot(struct btrfs_trans_handle *trans, | ||||
| 				struct btrfs_pending_snapshot *pending, | ||||
| 				u64 *bytes_to_reserve) | ||||
| { | ||||
| 	struct btrfs_root *root; | ||||
| 	struct btrfs_block_rsv *block_rsv; | ||||
| 	u64 num_bytes; | ||||
| 	int index; | ||||
| 
 | ||||
| 	root = pending->root; | ||||
| 	if (!root->orphan_block_rsv || list_empty(&root->orphan_list)) | ||||
| 		return; | ||||
| 
 | ||||
| 	block_rsv = root->orphan_block_rsv; | ||||
| 
 | ||||
| 	/* orphan block reservation for the snapshot */ | ||||
| 	num_bytes = block_rsv->size; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * after the snapshot is created, COWing tree blocks may use more | ||||
| 	 * space than it frees. So we should make sure there is enough | ||||
| 	 * reserved space. | ||||
| 	 */ | ||||
| 	index = trans->transid & 0x1; | ||||
| 	if (block_rsv->reserved + block_rsv->freed[index] < block_rsv->size) { | ||||
| 		num_bytes += block_rsv->size - | ||||
| 			     (block_rsv->reserved + block_rsv->freed[index]); | ||||
| 	} | ||||
| 
 | ||||
| 	*bytes_to_reserve += num_bytes; | ||||
| } | ||||
| 
 | ||||
| void btrfs_orphan_post_snapshot(struct btrfs_trans_handle *trans, | ||||
| 				struct btrfs_pending_snapshot *pending) | ||||
| { | ||||
| 	struct btrfs_root *root = pending->root; | ||||
| 	struct btrfs_root *snap = pending->snap; | ||||
| 	struct btrfs_block_rsv *block_rsv; | ||||
| 	u64 num_bytes; | ||||
| 	int index; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (!root->orphan_block_rsv || list_empty(&root->orphan_list)) | ||||
| 		return; | ||||
| 
 | ||||
| 	/* refill source subvolume's orphan block reservation */ | ||||
| 	block_rsv = root->orphan_block_rsv; | ||||
| 	index = trans->transid & 0x1; | ||||
| 	if (block_rsv->reserved + block_rsv->freed[index] < block_rsv->size) { | ||||
| 		num_bytes = block_rsv->size - | ||||
| 			    (block_rsv->reserved + block_rsv->freed[index]); | ||||
| 		ret = btrfs_block_rsv_migrate(&pending->block_rsv, | ||||
| 					      root->orphan_block_rsv, | ||||
| 					      num_bytes); | ||||
| 		BUG_ON(ret); | ||||
| 	} | ||||
| 
 | ||||
| 	/* setup orphan block reservation for the snapshot */ | ||||
| 	block_rsv = btrfs_alloc_block_rsv(snap); | ||||
| 	BUG_ON(!block_rsv); | ||||
| 
 | ||||
| 	btrfs_add_durable_block_rsv(root->fs_info, block_rsv); | ||||
| 	snap->orphan_block_rsv = block_rsv; | ||||
| 
 | ||||
| 	num_bytes = root->orphan_block_rsv->size; | ||||
| 	ret = btrfs_block_rsv_migrate(&pending->block_rsv, | ||||
| 				      block_rsv, num_bytes); | ||||
| 	BUG_ON(ret); | ||||
| 
 | ||||
| #if 0 | ||||
| 	/* insert orphan item for the snapshot */ | ||||
| 	WARN_ON(!root->orphan_item_inserted); | ||||
| 	ret = btrfs_insert_orphan_item(trans, root->fs_info->tree_root, | ||||
| 				       snap->root_key.objectid); | ||||
| 	BUG_ON(ret); | ||||
| 	snap->orphan_item_inserted = 1; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| enum btrfs_orphan_cleanup_state { | ||||
| 	ORPHAN_CLEANUP_STARTED	= 1, | ||||
| 	ORPHAN_CLEANUP_DONE	= 2, | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * This is called in transaction commmit time. If there are no orphan | ||||
|  * files in the subvolume, it removes orphan item and frees block_rsv | ||||
|  * structure. | ||||
|  */ | ||||
| void btrfs_orphan_commit_root(struct btrfs_trans_handle *trans, | ||||
| 			      struct btrfs_root *root) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (!list_empty(&root->orphan_list) || | ||||
| 	    root->orphan_cleanup_state != ORPHAN_CLEANUP_DONE) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (root->orphan_item_inserted && | ||||
| 	    btrfs_root_refs(&root->root_item) > 0) { | ||||
| 		ret = btrfs_del_orphan_item(trans, root->fs_info->tree_root, | ||||
| 					    root->root_key.objectid); | ||||
| 		BUG_ON(ret); | ||||
| 		root->orphan_item_inserted = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	if (root->orphan_block_rsv) { | ||||
| 		WARN_ON(root->orphan_block_rsv->size > 0); | ||||
| 		btrfs_free_block_rsv(root, root->orphan_block_rsv); | ||||
| 		root->orphan_block_rsv = NULL; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * This creates an orphan entry for the given inode in case something goes | ||||
|  * wrong in the middle of an unlink/truncate. | ||||
|  * | ||||
|  * NOTE: caller of this function should reserve 5 units of metadata for | ||||
|  *	 this function. | ||||
|  */ | ||||
| int btrfs_orphan_add(struct btrfs_trans_handle *trans, struct inode *inode) | ||||
| { | ||||
| 	struct btrfs_root *root = BTRFS_I(inode)->root; | ||||
| 	int ret = 0; | ||||
| 	struct btrfs_block_rsv *block_rsv = NULL; | ||||
| 	int reserve = 0; | ||||
| 	int insert = 0; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	spin_lock(&root->list_lock); | ||||
| 
 | ||||
| 	/* already on the orphan list, we're good */ | ||||
| 	if (!list_empty(&BTRFS_I(inode)->i_orphan)) { | ||||
| 		spin_unlock(&root->list_lock); | ||||
| 		return 0; | ||||
| 	if (!root->orphan_block_rsv) { | ||||
| 		block_rsv = btrfs_alloc_block_rsv(root); | ||||
| 		BUG_ON(!block_rsv); | ||||
| 	} | ||||
| 
 | ||||
| 	list_add(&BTRFS_I(inode)->i_orphan, &root->orphan_list); | ||||
| 	spin_lock(&root->orphan_lock); | ||||
| 	if (!root->orphan_block_rsv) { | ||||
| 		root->orphan_block_rsv = block_rsv; | ||||
| 	} else if (block_rsv) { | ||||
| 		btrfs_free_block_rsv(root, block_rsv); | ||||
| 		block_rsv = NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	spin_unlock(&root->list_lock); | ||||
| 	if (list_empty(&BTRFS_I(inode)->i_orphan)) { | ||||
| 		list_add(&BTRFS_I(inode)->i_orphan, &root->orphan_list); | ||||
| #if 0 | ||||
| 		/*
 | ||||
| 		 * For proper ENOSPC handling, we should do orphan | ||||
| 		 * cleanup when mounting. But this introduces backward | ||||
| 		 * compatibility issue. | ||||
| 		 */ | ||||
| 		if (!xchg(&root->orphan_item_inserted, 1)) | ||||
| 			insert = 2; | ||||
| 		else | ||||
| 			insert = 1; | ||||
| #endif | ||||
| 		insert = 1; | ||||
| 	} else { | ||||
| 		WARN_ON(!BTRFS_I(inode)->orphan_meta_reserved); | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * insert an orphan item to track this unlinked/truncated file | ||||
| 	 */ | ||||
| 	ret = btrfs_insert_orphan_item(trans, root, inode->i_ino); | ||||
| 	if (!BTRFS_I(inode)->orphan_meta_reserved) { | ||||
| 		BTRFS_I(inode)->orphan_meta_reserved = 1; | ||||
| 		reserve = 1; | ||||
| 	} | ||||
| 	spin_unlock(&root->orphan_lock); | ||||
| 
 | ||||
| 	return ret; | ||||
| 	if (block_rsv) | ||||
| 		btrfs_add_durable_block_rsv(root->fs_info, block_rsv); | ||||
| 
 | ||||
| 	/* grab metadata reservation from transaction handle */ | ||||
| 	if (reserve) { | ||||
| 		ret = btrfs_orphan_reserve_metadata(trans, inode); | ||||
| 		BUG_ON(ret); | ||||
| 	} | ||||
| 
 | ||||
| 	/* insert an orphan item to track this unlinked/truncated file */ | ||||
| 	if (insert >= 1) { | ||||
| 		ret = btrfs_insert_orphan_item(trans, root, inode->i_ino); | ||||
| 		BUG_ON(ret); | ||||
| 	} | ||||
| 
 | ||||
| 	/* insert an orphan item to track subvolume contains orphan files */ | ||||
| 	if (insert >= 2) { | ||||
| 		ret = btrfs_insert_orphan_item(trans, root->fs_info->tree_root, | ||||
| 					       root->root_key.objectid); | ||||
| 		BUG_ON(ret); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
| @ -2017,26 +2181,31 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans, struct inode *inode) | ||||
| int btrfs_orphan_del(struct btrfs_trans_handle *trans, struct inode *inode) | ||||
| { | ||||
| 	struct btrfs_root *root = BTRFS_I(inode)->root; | ||||
| 	int delete_item = 0; | ||||
| 	int release_rsv = 0; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	spin_lock(&root->list_lock); | ||||
| 
 | ||||
| 	if (list_empty(&BTRFS_I(inode)->i_orphan)) { | ||||
| 		spin_unlock(&root->list_lock); | ||||
| 		return 0; | ||||
| 	spin_lock(&root->orphan_lock); | ||||
| 	if (!list_empty(&BTRFS_I(inode)->i_orphan)) { | ||||
| 		list_del_init(&BTRFS_I(inode)->i_orphan); | ||||
| 		delete_item = 1; | ||||
| 	} | ||||
| 
 | ||||
| 	list_del_init(&BTRFS_I(inode)->i_orphan); | ||||
| 	if (!trans) { | ||||
| 		spin_unlock(&root->list_lock); | ||||
| 		return 0; | ||||
| 	if (BTRFS_I(inode)->orphan_meta_reserved) { | ||||
| 		BTRFS_I(inode)->orphan_meta_reserved = 0; | ||||
| 		release_rsv = 1; | ||||
| 	} | ||||
| 	spin_unlock(&root->orphan_lock); | ||||
| 
 | ||||
| 	if (trans && delete_item) { | ||||
| 		ret = btrfs_del_orphan_item(trans, root, inode->i_ino); | ||||
| 		BUG_ON(ret); | ||||
| 	} | ||||
| 
 | ||||
| 	spin_unlock(&root->list_lock); | ||||
| 	if (release_rsv) | ||||
| 		btrfs_orphan_release_metadata(inode); | ||||
| 
 | ||||
| 	ret = btrfs_del_orphan_item(trans, root, inode->i_ino); | ||||
| 
 | ||||
| 	return ret; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
| @ -2053,7 +2222,7 @@ void btrfs_orphan_cleanup(struct btrfs_root *root) | ||||
| 	struct inode *inode; | ||||
| 	int ret = 0, nr_unlink = 0, nr_truncate = 0; | ||||
| 
 | ||||
| 	if (!xchg(&root->clean_orphans, 0)) | ||||
| 	if (cmpxchg(&root->orphan_cleanup_state, 0, ORPHAN_CLEANUP_STARTED)) | ||||
| 		return; | ||||
| 
 | ||||
| 	path = btrfs_alloc_path(); | ||||
| @ -2106,16 +2275,15 @@ void btrfs_orphan_cleanup(struct btrfs_root *root) | ||||
| 		found_key.type = BTRFS_INODE_ITEM_KEY; | ||||
| 		found_key.offset = 0; | ||||
| 		inode = btrfs_iget(root->fs_info->sb, &found_key, root, NULL); | ||||
| 		if (IS_ERR(inode)) | ||||
| 			break; | ||||
| 		BUG_ON(IS_ERR(inode)); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * add this inode to the orphan list so btrfs_orphan_del does | ||||
| 		 * the proper thing when we hit it | ||||
| 		 */ | ||||
| 		spin_lock(&root->list_lock); | ||||
| 		spin_lock(&root->orphan_lock); | ||||
| 		list_add(&BTRFS_I(inode)->i_orphan, &root->orphan_list); | ||||
| 		spin_unlock(&root->list_lock); | ||||
| 		spin_unlock(&root->orphan_lock); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * if this is a bad inode, means we actually succeeded in | ||||
| @ -2142,13 +2310,23 @@ void btrfs_orphan_cleanup(struct btrfs_root *root) | ||||
| 		/* this will do delete_inode and everything for us */ | ||||
| 		iput(inode); | ||||
| 	} | ||||
| 	btrfs_free_path(path); | ||||
| 
 | ||||
| 	root->orphan_cleanup_state = ORPHAN_CLEANUP_DONE; | ||||
| 
 | ||||
| 	if (root->orphan_block_rsv) | ||||
| 		btrfs_block_rsv_release(root, root->orphan_block_rsv, | ||||
| 					(u64)-1); | ||||
| 
 | ||||
| 	if (root->orphan_block_rsv || root->orphan_item_inserted) { | ||||
| 		trans = btrfs_join_transaction(root, 1); | ||||
| 		btrfs_end_transaction(trans, root); | ||||
| 	} | ||||
| 
 | ||||
| 	if (nr_unlink) | ||||
| 		printk(KERN_INFO "btrfs: unlinked %d orphans\n", nr_unlink); | ||||
| 	if (nr_truncate) | ||||
| 		printk(KERN_INFO "btrfs: truncated %d orphans\n", nr_truncate); | ||||
| 
 | ||||
| 	btrfs_free_path(path); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
| @ -3181,6 +3359,7 @@ out: | ||||
| 	if (pending_del_nr) { | ||||
| 		ret = btrfs_del_items(trans, root, path, pending_del_slot, | ||||
| 				      pending_del_nr); | ||||
| 		BUG_ON(ret); | ||||
| 	} | ||||
| 	btrfs_free_path(path); | ||||
| 	return err; | ||||
| @ -3386,7 +3565,10 @@ static int btrfs_setattr_size(struct inode *inode, struct iattr *attr) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	trans = btrfs_start_transaction(root, 1); | ||||
| 	trans = btrfs_start_transaction(root, 5); | ||||
| 	if (IS_ERR(trans)) | ||||
| 		return PTR_ERR(trans); | ||||
| 
 | ||||
| 	btrfs_set_trans_block_group(trans, inode); | ||||
| 
 | ||||
| 	ret = btrfs_orphan_add(trans, inode); | ||||
| @ -3406,8 +3588,11 @@ static int btrfs_setattr_size(struct inode *inode, struct iattr *attr) | ||||
| 		i_size_write(inode, attr->ia_size); | ||||
| 		btrfs_ordered_update_i_size(inode, inode->i_size, NULL); | ||||
| 
 | ||||
| 		trans = btrfs_start_transaction(root, 1); | ||||
| 		trans = btrfs_start_transaction(root, 0); | ||||
| 		BUG_ON(IS_ERR(trans)); | ||||
| 		btrfs_set_trans_block_group(trans, inode); | ||||
| 		trans->block_rsv = root->orphan_block_rsv; | ||||
| 		BUG_ON(!trans->block_rsv); | ||||
| 
 | ||||
| 		ret = btrfs_update_inode(trans, root, inode); | ||||
| 		BUG_ON(ret); | ||||
| @ -3487,10 +3672,21 @@ void btrfs_delete_inode(struct inode *inode) | ||||
| 	btrfs_i_size_write(inode, 0); | ||||
| 
 | ||||
| 	while (1) { | ||||
| 		trans = btrfs_start_transaction(root, 1); | ||||
| 		trans = btrfs_start_transaction(root, 0); | ||||
| 		BUG_ON(IS_ERR(trans)); | ||||
| 		btrfs_set_trans_block_group(trans, inode); | ||||
| 		ret = btrfs_truncate_inode_items(trans, root, inode, 0, 0); | ||||
| 		trans->block_rsv = root->orphan_block_rsv; | ||||
| 
 | ||||
| 		ret = btrfs_block_rsv_check(trans, root, | ||||
| 					    root->orphan_block_rsv, 0, 5); | ||||
| 		if (ret) { | ||||
| 			BUG_ON(ret != -EAGAIN); | ||||
| 			ret = btrfs_commit_transaction(trans, root); | ||||
| 			BUG_ON(ret); | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		ret = btrfs_truncate_inode_items(trans, root, inode, 0, 0); | ||||
| 		if (ret != -EAGAIN) | ||||
| 			break; | ||||
| 
 | ||||
| @ -3498,6 +3694,7 @@ void btrfs_delete_inode(struct inode *inode) | ||||
| 		btrfs_end_transaction(trans, root); | ||||
| 		trans = NULL; | ||||
| 		btrfs_btree_balance_dirty(root, nr); | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	if (ret == 0) { | ||||
| @ -5247,8 +5444,10 @@ static void btrfs_truncate(struct inode *inode) | ||||
| 	btrfs_wait_ordered_range(inode, inode->i_size & (~mask), (u64)-1); | ||||
| 	btrfs_ordered_update_i_size(inode, inode->i_size, NULL); | ||||
| 
 | ||||
| 	trans = btrfs_start_transaction(root, 1); | ||||
| 	trans = btrfs_start_transaction(root, 0); | ||||
| 	BUG_ON(IS_ERR(trans)); | ||||
| 	btrfs_set_trans_block_group(trans, inode); | ||||
| 	trans->block_rsv = root->orphan_block_rsv; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * setattr is responsible for setting the ordered_data_close flag, | ||||
| @ -5271,6 +5470,23 @@ static void btrfs_truncate(struct inode *inode) | ||||
| 		btrfs_add_ordered_operation(trans, root, inode); | ||||
| 
 | ||||
| 	while (1) { | ||||
| 		if (!trans) { | ||||
| 			trans = btrfs_start_transaction(root, 0); | ||||
| 			BUG_ON(IS_ERR(trans)); | ||||
| 			btrfs_set_trans_block_group(trans, inode); | ||||
| 			trans->block_rsv = root->orphan_block_rsv; | ||||
| 		} | ||||
| 
 | ||||
| 		ret = btrfs_block_rsv_check(trans, root, | ||||
| 					    root->orphan_block_rsv, 0, 5); | ||||
| 		if (ret) { | ||||
| 			BUG_ON(ret != -EAGAIN); | ||||
| 			ret = btrfs_commit_transaction(trans, root); | ||||
| 			BUG_ON(ret); | ||||
| 			trans = NULL; | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		ret = btrfs_truncate_inode_items(trans, root, inode, | ||||
| 						 inode->i_size, | ||||
| 						 BTRFS_EXTENT_DATA_KEY); | ||||
| @ -5282,10 +5498,8 @@ static void btrfs_truncate(struct inode *inode) | ||||
| 
 | ||||
| 		nr = trans->blocks_used; | ||||
| 		btrfs_end_transaction(trans, root); | ||||
| 		trans = NULL; | ||||
| 		btrfs_btree_balance_dirty(root, nr); | ||||
| 
 | ||||
| 		trans = btrfs_start_transaction(root, 1); | ||||
| 		btrfs_set_trans_block_group(trans, inode); | ||||
| 	} | ||||
| 
 | ||||
| 	if (ret == 0 && inode->i_nlink > 0) { | ||||
| @ -5371,6 +5585,7 @@ struct inode *btrfs_alloc_inode(struct super_block *sb) | ||||
| 	ei->reserved_extents = 0; | ||||
| 
 | ||||
| 	ei->ordered_data_close = 0; | ||||
| 	ei->orphan_meta_reserved = 0; | ||||
| 	ei->dummy_inode = 0; | ||||
| 	ei->force_compress = 0; | ||||
| 
 | ||||
| @ -5417,13 +5632,13 @@ void btrfs_destroy_inode(struct inode *inode) | ||||
| 		spin_unlock(&root->fs_info->ordered_extent_lock); | ||||
| 	} | ||||
| 
 | ||||
| 	spin_lock(&root->list_lock); | ||||
| 	spin_lock(&root->orphan_lock); | ||||
| 	if (!list_empty(&BTRFS_I(inode)->i_orphan)) { | ||||
| 		printk(KERN_INFO "BTRFS: inode %lu still on the orphan list\n", | ||||
| 		       inode->i_ino); | ||||
| 		list_del_init(&BTRFS_I(inode)->i_orphan); | ||||
| 	} | ||||
| 	spin_unlock(&root->list_lock); | ||||
| 	spin_unlock(&root->orphan_lock); | ||||
| 
 | ||||
| 	while (1) { | ||||
| 		ordered = btrfs_lookup_first_ordered_extent(inode, (u64)-1); | ||||
|  | ||||
| @ -1297,10 +1297,12 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, | ||||
| 	dest->root_item.drop_level = 0; | ||||
| 	btrfs_set_root_refs(&dest->root_item, 0); | ||||
| 
 | ||||
| 	ret = btrfs_insert_orphan_item(trans, | ||||
| 				root->fs_info->tree_root, | ||||
| 				dest->root_key.objectid); | ||||
| 	BUG_ON(ret); | ||||
| 	if (!xchg(&dest->orphan_item_inserted, 1)) { | ||||
| 		ret = btrfs_insert_orphan_item(trans, | ||||
| 					root->fs_info->tree_root, | ||||
| 					dest->root_key.objectid); | ||||
| 		BUG_ON(ret); | ||||
| 	} | ||||
| 
 | ||||
| 	ret = btrfs_commit_transaction(trans, root); | ||||
| 	BUG_ON(ret); | ||||
|  | ||||
| @ -259,6 +259,8 @@ int btrfs_find_orphan_roots(struct btrfs_root *tree_root) | ||||
| 	struct extent_buffer *leaf; | ||||
| 	struct btrfs_path *path; | ||||
| 	struct btrfs_key key; | ||||
| 	struct btrfs_key root_key; | ||||
| 	struct btrfs_root *root; | ||||
| 	int err = 0; | ||||
| 	int ret; | ||||
| 
 | ||||
| @ -270,6 +272,9 @@ int btrfs_find_orphan_roots(struct btrfs_root *tree_root) | ||||
| 	key.type = BTRFS_ORPHAN_ITEM_KEY; | ||||
| 	key.offset = 0; | ||||
| 
 | ||||
| 	root_key.type = BTRFS_ROOT_ITEM_KEY; | ||||
| 	root_key.offset = (u64)-1; | ||||
| 
 | ||||
| 	while (1) { | ||||
| 		ret = btrfs_search_slot(NULL, tree_root, &key, path, 0, 0); | ||||
| 		if (ret < 0) { | ||||
| @ -294,13 +299,25 @@ int btrfs_find_orphan_roots(struct btrfs_root *tree_root) | ||||
| 		    key.type != BTRFS_ORPHAN_ITEM_KEY) | ||||
| 			break; | ||||
| 
 | ||||
| 		ret = btrfs_find_dead_roots(tree_root, key.offset); | ||||
| 		if (ret) { | ||||
| 		root_key.objectid = key.offset; | ||||
| 		key.offset++; | ||||
| 
 | ||||
| 		root = btrfs_read_fs_root_no_name(tree_root->fs_info, | ||||
| 						  &root_key); | ||||
| 		if (!IS_ERR(root)) | ||||
| 			continue; | ||||
| 
 | ||||
| 		ret = PTR_ERR(root); | ||||
| 		if (ret != -ENOENT) { | ||||
| 			err = ret; | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		key.offset++; | ||||
| 		ret = btrfs_find_dead_roots(tree_root, root_key.objectid); | ||||
| 		if (ret) { | ||||
| 			err = ret; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	btrfs_free_path(path); | ||||
|  | ||||
| @ -694,11 +694,11 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data) | ||||
| 		if (btrfs_super_log_root(&root->fs_info->super_copy) != 0) | ||||
| 			return -EINVAL; | ||||
| 
 | ||||
| 		/* recover relocation */ | ||||
| 		ret = btrfs_recover_relocation(root); | ||||
| 		ret = btrfs_cleanup_fs_roots(root->fs_info); | ||||
| 		WARN_ON(ret); | ||||
| 
 | ||||
| 		ret = btrfs_cleanup_fs_roots(root->fs_info); | ||||
| 		/* recover relocation */ | ||||
| 		ret = btrfs_recover_relocation(root); | ||||
| 		WARN_ON(ret); | ||||
| 
 | ||||
| 		sb->s_flags &= ~MS_RDONLY; | ||||
|  | ||||
| @ -676,6 +676,7 @@ static noinline int commit_fs_roots(struct btrfs_trans_handle *trans, | ||||
| 
 | ||||
| 			btrfs_free_log(trans, root); | ||||
| 			btrfs_update_reloc_root(trans, root); | ||||
| 			btrfs_orphan_commit_root(trans, root); | ||||
| 
 | ||||
| 			if (root->commit_root != root->node) { | ||||
| 				switch_commit_root(root); | ||||
| @ -835,6 +836,8 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, | ||||
| 	struct extent_buffer *tmp; | ||||
| 	struct extent_buffer *old; | ||||
| 	int ret; | ||||
| 	int retries = 0; | ||||
| 	u64 to_reserve = 0; | ||||
| 	u64 index = 0; | ||||
| 	u64 objectid; | ||||
| 
 | ||||
| @ -850,6 +853,17 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, | ||||
| 		goto fail; | ||||
| 	} | ||||
| 
 | ||||
| 	btrfs_orphan_pre_snapshot(trans, pending, &to_reserve); | ||||
| 
 | ||||
| 	if (to_reserve > 0) { | ||||
| 		ret = btrfs_block_rsv_add(trans, root, &pending->block_rsv, | ||||
| 					  to_reserve, &retries); | ||||
| 		if (ret) { | ||||
| 			pending->error = ret; | ||||
| 			goto fail; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	key.objectid = objectid; | ||||
| 	key.offset = (u64)-1; | ||||
| 	key.type = BTRFS_ROOT_ITEM_KEY; | ||||
| @ -909,6 +923,8 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, | ||||
| 	key.offset = (u64)-1; | ||||
| 	pending->snap = btrfs_read_fs_root_no_name(root->fs_info, &key); | ||||
| 	BUG_ON(IS_ERR(pending->snap)); | ||||
| 
 | ||||
| 	btrfs_orphan_post_snapshot(trans, pending); | ||||
| fail: | ||||
| 	kfree(new_root_item); | ||||
| 	btrfs_block_rsv_release(root, &pending->block_rsv, (u64)-1); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user