diff options
Diffstat (limited to 'fs/btrfs')
| -rw-r--r-- | fs/btrfs/btrfs_inode.h | 4 | ||||
| -rw-r--r-- | fs/btrfs/delayed-inode.c | 65 | ||||
| -rw-r--r-- | fs/btrfs/extent-tree.c | 22 | ||||
| -rw-r--r-- | fs/btrfs/inode.c | 1 | 
4 files changed, 83 insertions, 9 deletions
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index 5a5d325a393..634608d2a6d 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -147,14 +147,12 @@ struct btrfs_inode {  	 * the btrfs file release call will add this inode to the  	 * ordered operations list so that we make sure to flush out any  	 * new data the application may have written before commit. -	 * -	 * yes, its silly to have a single bitflag, but we might grow more -	 * of these.  	 */  	unsigned ordered_data_close:1;  	unsigned orphan_meta_reserved:1;  	unsigned dummy_inode:1;  	unsigned in_defrag:1; +	unsigned delalloc_meta_reserved:1;  	/*  	 * always compress this one file diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c index bbe8496d533..313ee14cf3b 100644 --- a/fs/btrfs/delayed-inode.c +++ b/fs/btrfs/delayed-inode.c @@ -617,12 +617,14 @@ static void btrfs_delayed_item_release_metadata(struct btrfs_root *root,  static int btrfs_delayed_inode_reserve_metadata(  					struct btrfs_trans_handle *trans,  					struct btrfs_root *root, +					struct inode *inode,  					struct btrfs_delayed_node *node)  {  	struct btrfs_block_rsv *src_rsv;  	struct btrfs_block_rsv *dst_rsv;  	u64 num_bytes;  	int ret; +	int release = false;  	src_rsv = trans->block_rsv;  	dst_rsv = &root->fs_info->delayed_block_rsv; @@ -652,11 +654,67 @@ static int btrfs_delayed_inode_reserve_metadata(  		if (!ret)  			node->bytes_reserved = num_bytes;  		return ret; +	} else if (src_rsv == &root->fs_info->delalloc_block_rsv) { +		spin_lock(&BTRFS_I(inode)->lock); +		if (BTRFS_I(inode)->delalloc_meta_reserved) { +			BTRFS_I(inode)->delalloc_meta_reserved = 0; +			spin_unlock(&BTRFS_I(inode)->lock); +			release = true; +			goto migrate; +		} +		spin_unlock(&BTRFS_I(inode)->lock); + +		/* Ok we didn't have space pre-reserved.  This shouldn't happen +		 * too often but it can happen if we do delalloc to an existing +		 * inode which gets dirtied because of the time update, and then +		 * isn't touched again until after the transaction commits and +		 * then we try to write out the data.  First try to be nice and +		 * reserve something strictly for us.  If not be a pain and try +		 * to steal from the delalloc block rsv. +		 */ +		ret = btrfs_block_rsv_add_noflush(root, dst_rsv, num_bytes); +		if (!ret) +			goto out; + +		ret = btrfs_block_rsv_migrate(src_rsv, dst_rsv, num_bytes); +		if (!ret) +			goto out; + +		/* +		 * Ok this is a problem, let's just steal from the global rsv +		 * since this really shouldn't happen that often. +		 */ +		WARN_ON(1); +		ret = btrfs_block_rsv_migrate(&root->fs_info->global_block_rsv, +					      dst_rsv, num_bytes); +		goto out;  	} +migrate:  	ret = btrfs_block_rsv_migrate(src_rsv, dst_rsv, num_bytes); -	if (!ret) -		node->bytes_reserved = num_bytes; +	if (unlikely(ret)) { +		/* This shouldn't happen */ +		BUG_ON(release); +		return ret; +	} + +out: +	/* +	 * Migrate only takes a reservation, it doesn't touch the size of the +	 * block_rsv.  This is to simplify people who don't normally have things +	 * migrated from their block rsv.  If they go to release their +	 * reservation, that will decrease the size as well, so if migrate +	 * reduced size we'd end up with a negative size.  But for the +	 * delalloc_meta_reserved stuff we will only know to drop 1 reservation, +	 * but we could in fact do this reserve/migrate dance several times +	 * between the time we did the original reservation and we'd clean it +	 * up.  So to take care of this, release the space for the meta +	 * reservation here.  I think it may be time for a documentation page on +	 * how block rsvs. work. +	 */ +	if (release) +		btrfs_block_rsv_release(root, src_rsv, num_bytes); +	node->bytes_reserved = num_bytes;  	return ret;  } @@ -1708,7 +1766,8 @@ int btrfs_delayed_update_inode(struct btrfs_trans_handle *trans,  		goto release_node;  	} -	ret = btrfs_delayed_inode_reserve_metadata(trans, root, delayed_node); +	ret = btrfs_delayed_inode_reserve_metadata(trans, root, inode, +						   delayed_node);  	if (ret)  		goto release_node; diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 18ea90c8943..0b044e509e9 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -4063,23 +4063,30 @@ int btrfs_snap_reserve_metadata(struct btrfs_trans_handle *trans,   */  static unsigned drop_outstanding_extent(struct inode *inode)  { +	unsigned drop_inode_space = 0;  	unsigned dropped_extents = 0;  	BUG_ON(!BTRFS_I(inode)->outstanding_extents);  	BTRFS_I(inode)->outstanding_extents--; +	if (BTRFS_I(inode)->outstanding_extents == 0 && +	    BTRFS_I(inode)->delalloc_meta_reserved) { +		drop_inode_space = 1; +		BTRFS_I(inode)->delalloc_meta_reserved = 0; +	} +  	/*  	 * If we have more or the same amount of outsanding extents than we have  	 * reserved then we need to leave the reserved extents count alone.  	 */  	if (BTRFS_I(inode)->outstanding_extents >=  	    BTRFS_I(inode)->reserved_extents) -		return 0; +		return drop_inode_space;  	dropped_extents = BTRFS_I(inode)->reserved_extents -  		BTRFS_I(inode)->outstanding_extents;  	BTRFS_I(inode)->reserved_extents -= dropped_extents; -	return dropped_extents; +	return dropped_extents + drop_inode_space;  }  /** @@ -4165,9 +4172,18 @@ int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes)  		nr_extents = BTRFS_I(inode)->outstanding_extents -  			BTRFS_I(inode)->reserved_extents;  		BTRFS_I(inode)->reserved_extents += nr_extents; +	} -		to_reserve = btrfs_calc_trans_metadata_size(root, nr_extents); +	/* +	 * Add an item to reserve for updating the inode when we complete the +	 * delalloc io. +	 */ +	if (!BTRFS_I(inode)->delalloc_meta_reserved) { +		nr_extents++; +		BTRFS_I(inode)->delalloc_meta_reserved = 1;  	} + +	to_reserve = btrfs_calc_trans_metadata_size(root, nr_extents);  	to_reserve += calc_csum_metadata_size(inode, num_bytes, 1);  	spin_unlock(&BTRFS_I(inode)->lock); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index f60e2490bd0..2b920596c12 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6607,6 +6607,7 @@ struct inode *btrfs_alloc_inode(struct super_block *sb)  	ei->orphan_meta_reserved = 0;  	ei->dummy_inode = 0;  	ei->in_defrag = 0; +	ei->delalloc_meta_reserved = 0;  	ei->force_compress = BTRFS_COMPRESS_NONE;  	ei->delayed_node = NULL;  |