diff options
Diffstat (limited to 'fs/btrfs')
| -rw-r--r-- | fs/btrfs/ctree.h | 9 | ||||
| -rw-r--r-- | fs/btrfs/extent-tree.c | 45 | ||||
| -rw-r--r-- | fs/btrfs/volumes.c | 8 | 
3 files changed, 59 insertions, 3 deletions
| diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 82491ba8fa40..5e1d4e30e9d8 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -784,7 +784,14 @@ struct btrfs_fs_info {  	struct list_head dirty_cowonly_roots;  	struct btrfs_fs_devices *fs_devices; + +	/* +	 * the space_info list is almost entirely read only.  It only changes +	 * when we add a new raid type to the FS, and that happens +	 * very rarely.  RCU is used to protect it. +	 */  	struct list_head space_info; +  	spinlock_t delalloc_lock;  	spinlock_t new_trans_lock;  	u64 delalloc_bytes; @@ -1797,6 +1804,8 @@ int btrfs_cleanup_reloc_trees(struct btrfs_root *root);  int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len);  u64 btrfs_reduce_alloc_profile(struct btrfs_root *root, u64 flags);  void btrfs_set_inode_space_info(struct btrfs_root *root, struct inode *ionde); +void btrfs_clear_space_info_full(struct btrfs_fs_info *info); +  int btrfs_check_metadata_free_space(struct btrfs_root *root);  int btrfs_check_data_free_space(struct btrfs_root *root, struct inode *inode,  				u64 bytes); diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 9abf81f71c46..fefe83ad2059 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -20,6 +20,7 @@  #include <linux/writeback.h>  #include <linux/blkdev.h>  #include <linux/sort.h> +#include <linux/rcupdate.h>  #include "compat.h"  #include "hash.h"  #include "crc32c.h" @@ -330,13 +331,33 @@ static struct btrfs_space_info *__find_space_info(struct btrfs_fs_info *info,  {  	struct list_head *head = &info->space_info;  	struct btrfs_space_info *found; -	list_for_each_entry(found, head, list) { -		if (found->flags == flags) + +	rcu_read_lock(); +	list_for_each_entry_rcu(found, head, list) { +		if (found->flags == flags) { +			rcu_read_unlock();  			return found; +		}  	} +	rcu_read_unlock();  	return NULL;  } +/* + * after adding space to the filesystem, we need to clear the full flags + * on all the space infos. + */ +void btrfs_clear_space_info_full(struct btrfs_fs_info *info) +{ +	struct list_head *head = &info->space_info; +	struct btrfs_space_info *found; + +	rcu_read_lock(); +	list_for_each_entry_rcu(found, head, list) +		found->full = 0; +	rcu_read_unlock(); +} +  static u64 div_factor(u64 num, int factor)  {  	if (factor == 10) @@ -1903,7 +1924,6 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags,  	if (!found)  		return -ENOMEM; -	list_add(&found->list, &info->space_info);  	INIT_LIST_HEAD(&found->block_groups);  	init_rwsem(&found->groups_sem);  	spin_lock_init(&found->lock); @@ -1917,6 +1937,7 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags,  	found->full = 0;  	found->force_alloc = 0;  	*space_info = found; +	list_add_rcu(&found->list, &info->space_info);  	return 0;  } @@ -6320,6 +6341,7 @@ out:  int btrfs_free_block_groups(struct btrfs_fs_info *info)  {  	struct btrfs_block_group_cache *block_group; +	struct btrfs_space_info *space_info;  	struct rb_node *n;  	spin_lock(&info->block_group_cache_lock); @@ -6341,6 +6363,23 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)  		spin_lock(&info->block_group_cache_lock);  	}  	spin_unlock(&info->block_group_cache_lock); + +	/* now that all the block groups are freed, go through and +	 * free all the space_info structs.  This is only called during +	 * the final stages of unmount, and so we know nobody is +	 * using them.  We call synchronize_rcu() once before we start, +	 * just to be on the safe side. +	 */ +	synchronize_rcu(); + +	while(!list_empty(&info->space_info)) { +		space_info = list_entry(info->space_info.next, +					struct btrfs_space_info, +					list); + +		list_del(&space_info->list); +		kfree(space_info); +	}  	return 0;  } diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 1316139bf9e8..dd06e18e5aac 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1374,6 +1374,12 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)  		ret = btrfs_add_device(trans, root, device);  	} +	/* +	 * we've got more storage, clear any full flags on the space +	 * infos +	 */ +	btrfs_clear_space_info_full(root->fs_info); +  	unlock_chunks(root);  	btrfs_commit_transaction(trans, root); @@ -1459,6 +1465,8 @@ static int __btrfs_grow_device(struct btrfs_trans_handle *trans,  	device->fs_devices->total_rw_bytes += diff;  	device->total_bytes = new_size; +	btrfs_clear_space_info_full(device->dev_root->fs_info); +  	return btrfs_update_device(trans, device);  } |