diff options
Diffstat (limited to 'fs/btrfs/volumes.c')
| -rw-r--r-- | fs/btrfs/volumes.c | 41 | 
1 files changed, 32 insertions, 9 deletions
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 33ca36b37a6..31f7af878d9 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -518,8 +518,9 @@ again:  	/* This is the initialized path, it is safe to release the devices. */  	list_for_each_entry_safe(device, next, &fs_devices->devices, dev_list) {  		if (device->in_fs_metadata) { -			if (!latest_transid || -			    device->generation > latest_transid) { +			if (!device->is_tgtdev_for_dev_replace && +			    (!latest_transid || +			     device->generation > latest_transid)) {  				latest_devid = device->devid;  				latest_transid = device->generation;  				latest_bdev = device->bdev; @@ -814,7 +815,7 @@ int btrfs_account_dev_extents_size(struct btrfs_device *device, u64 start,  	*length = 0; -	if (start >= device->total_bytes) +	if (start >= device->total_bytes || device->is_tgtdev_for_dev_replace)  		return 0;  	path = btrfs_alloc_path(); @@ -931,7 +932,7 @@ int find_free_dev_extent(struct btrfs_device *device, u64 num_bytes,  	max_hole_size = 0;  	hole_size = 0; -	if (search_start >= search_end) { +	if (search_start >= search_end || device->is_tgtdev_for_dev_replace) {  		ret = -ENOSPC;  		goto error;  	} @@ -1114,6 +1115,7 @@ int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans,  	struct btrfs_key key;  	WARN_ON(!device->in_fs_metadata); +	WARN_ON(device->is_tgtdev_for_dev_replace);  	path = btrfs_alloc_path();  	if (!path)  		return -ENOMEM; @@ -1375,7 +1377,9 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)  		 * is held.  		 */  		list_for_each_entry(tmp, devices, dev_list) { -			if (tmp->in_fs_metadata && !tmp->bdev) { +			if (tmp->in_fs_metadata && +			    !tmp->is_tgtdev_for_dev_replace && +			    !tmp->bdev) {  				device = tmp;  				break;  			} @@ -1406,6 +1410,12 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)  		}  	} +	if (device->is_tgtdev_for_dev_replace) { +		pr_err("btrfs: unable to remove the dev_replace target dev\n"); +		ret = -EINVAL; +		goto error_brelse; +	} +  	if (device->writeable && root->fs_info->fs_devices->rw_devices == 1) {  		printk(KERN_ERR "btrfs: unable to remove the only writeable "  		       "device\n"); @@ -1425,6 +1435,11 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)  	if (ret)  		goto error_undo; +	/* +	 * TODO: the superblock still includes this device in its num_devices +	 * counter although write_all_supers() is not locked out. This +	 * could give a filesystem state which requires a degraded mount. +	 */  	ret = btrfs_rm_dev_item(root->fs_info->chunk_root, device);  	if (ret)  		goto error_undo; @@ -1808,6 +1823,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)  	device->dev_root = root->fs_info->dev_root;  	device->bdev = bdev;  	device->in_fs_metadata = 1; +	device->is_tgtdev_for_dev_replace = 0;  	device->mode = FMODE_EXCL;  	set_blocksize(device->bdev, 4096); @@ -1971,7 +1987,8 @@ static int __btrfs_grow_device(struct btrfs_trans_handle *trans,  	if (!device->writeable)  		return -EACCES; -	if (new_size <= device->total_bytes) +	if (new_size <= device->total_bytes || +	    device->is_tgtdev_for_dev_replace)  		return -EINVAL;  	btrfs_set_super_total_bytes(super_copy, old_total + diff); @@ -2600,7 +2617,8 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_info)  		size_to_free = div_factor(old_size, 1);  		size_to_free = min(size_to_free, (u64)1 * 1024 * 1024);  		if (!device->writeable || -		    device->total_bytes - device->bytes_used > size_to_free) +		    device->total_bytes - device->bytes_used > size_to_free || +		    device->is_tgtdev_for_dev_replace)  			continue;  		ret = btrfs_shrink_device(device, old_size - size_to_free); @@ -3132,6 +3150,9 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size)  	u64 old_size = device->total_bytes;  	u64 diff = device->total_bytes - new_size; +	if (device->is_tgtdev_for_dev_replace) +		return -EINVAL; +  	path = btrfs_alloc_path();  	if (!path)  		return -ENOMEM; @@ -3401,7 +3422,8 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,  			continue;  		} -		if (!device->in_fs_metadata) +		if (!device->in_fs_metadata || +		    device->is_tgtdev_for_dev_replace)  			continue;  		if (device->total_bytes > device->bytes_used) @@ -4612,6 +4634,7 @@ static void fill_device_from_item(struct extent_buffer *leaf,  	device->io_align = btrfs_device_io_align(leaf, dev_item);  	device->io_width = btrfs_device_io_width(leaf, dev_item);  	device->sector_size = btrfs_device_sector_size(leaf, dev_item); +	device->is_tgtdev_for_dev_replace = 0;  	ptr = (unsigned long)btrfs_device_uuid(dev_item);  	read_extent_buffer(leaf, device->uuid, ptr, BTRFS_UUID_SIZE); @@ -4722,7 +4745,7 @@ static int read_one_dev(struct btrfs_root *root,  	fill_device_from_item(leaf, dev_item, device);  	device->dev_root = root->fs_info->dev_root;  	device->in_fs_metadata = 1; -	if (device->writeable) { +	if (device->writeable && !device->is_tgtdev_for_dev_replace) {  		device->fs_devices->total_rw_bytes += device->total_bytes;  		spin_lock(&root->fs_info->free_chunk_lock);  		root->fs_info->free_chunk_space += device->total_bytes -  |