diff options
Diffstat (limited to 'fs/btrfs/ioctl.c')
| -rw-r--r-- | fs/btrfs/ioctl.c | 71 | 
1 files changed, 54 insertions, 17 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index edfecfe62b4b..a5bd6926f7ff 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -387,6 +387,7 @@ bool btrfs_exclop_start(struct btrfs_fs_info *fs_info,   *   * Compatibility:   * - the same type is already running + * - when trying to add a device and balance has been paused   * - not BTRFS_EXCLOP_NONE - this is intentionally incompatible and the caller   *   must check the condition first that would allow none -> @type   */ @@ -394,7 +395,9 @@ bool btrfs_exclop_start_try_lock(struct btrfs_fs_info *fs_info,  				 enum btrfs_exclusive_operation type)  {  	spin_lock(&fs_info->super_lock); -	if (fs_info->exclusive_operation == type) +	if (fs_info->exclusive_operation == type || +	    (fs_info->exclusive_operation == BTRFS_EXCLOP_BALANCE_PAUSED && +	     type == BTRFS_EXCLOP_DEV_ADD))  		return true;  	spin_unlock(&fs_info->super_lock); @@ -414,6 +417,29 @@ void btrfs_exclop_finish(struct btrfs_fs_info *fs_info)  	sysfs_notify(&fs_info->fs_devices->fsid_kobj, NULL, "exclusive_operation");  } +void btrfs_exclop_balance(struct btrfs_fs_info *fs_info, +			  enum btrfs_exclusive_operation op) +{ +	switch (op) { +	case BTRFS_EXCLOP_BALANCE_PAUSED: +		spin_lock(&fs_info->super_lock); +		ASSERT(fs_info->exclusive_operation == BTRFS_EXCLOP_BALANCE || +		       fs_info->exclusive_operation == BTRFS_EXCLOP_DEV_ADD); +		fs_info->exclusive_operation = BTRFS_EXCLOP_BALANCE_PAUSED; +		spin_unlock(&fs_info->super_lock); +		break; +	case BTRFS_EXCLOP_BALANCE: +		spin_lock(&fs_info->super_lock); +		ASSERT(fs_info->exclusive_operation == BTRFS_EXCLOP_BALANCE_PAUSED); +		fs_info->exclusive_operation = BTRFS_EXCLOP_BALANCE; +		spin_unlock(&fs_info->super_lock); +		break; +	default: +		btrfs_warn(fs_info, +			"invalid exclop balance operation %d requested", op); +	} +} +  static int btrfs_ioctl_getversion(struct file *file, int __user *arg)  {  	struct inode *inode = file_inode(file); @@ -518,7 +544,6 @@ static noinline int create_subvol(struct user_namespace *mnt_userns,  	struct timespec64 cur_time = current_time(dir);  	struct inode *inode;  	int ret; -	int err;  	dev_t anon_dev = 0;  	u64 objectid;  	u64 index = 0; @@ -698,9 +723,10 @@ fail:  	trans->bytes_reserved = 0;  	btrfs_subvolume_release_metadata(root, &block_rsv); -	err = btrfs_commit_transaction(trans); -	if (err && !ret) -		ret = err; +	if (ret) +		btrfs_end_transaction(trans); +	else +		ret = btrfs_commit_transaction(trans);  	if (!ret) {  		inode = btrfs_lookup_dentry(dir, dentry); @@ -2084,7 +2110,7 @@ static noinline int copy_to_sk(struct btrfs_path *path,  	for (i = slot; i < nritems; i++) {  		item_off = btrfs_item_ptr_offset(leaf, i); -		item_len = btrfs_item_size_nr(leaf, i); +		item_len = btrfs_item_size(leaf, i);  		btrfs_item_key_to_cpu(leaf, key, i);  		if (!key_in_sk(key, sk)) @@ -2538,7 +2564,7 @@ static int btrfs_search_path_in_tree_user(struct user_namespace *mnt_userns,  	btrfs_item_key_to_cpu(leaf, &key, slot);  	item_off = btrfs_item_ptr_offset(leaf, slot); -	item_len = btrfs_item_size_nr(leaf, slot); +	item_len = btrfs_item_size(leaf, slot);  	/* Check if dirid in ROOT_REF corresponds to passed dirid */  	rref = btrfs_item_ptr(leaf, slot, struct btrfs_root_ref);  	if (args->dirid != btrfs_root_ref_dirid(leaf, rref)) { @@ -2740,7 +2766,7 @@ static int btrfs_ioctl_get_subvol_info(struct file *file, void __user *argp)  			item_off = btrfs_item_ptr_offset(leaf, slot)  					+ sizeof(struct btrfs_root_ref); -			item_len = btrfs_item_size_nr(leaf, slot) +			item_len = btrfs_item_size(leaf, slot)  					- sizeof(struct btrfs_root_ref);  			read_extent_buffer(leaf, subvol_info->name,  					   item_off, item_len); @@ -3148,13 +3174,25 @@ out:  static long btrfs_ioctl_add_dev(struct btrfs_fs_info *fs_info, void __user *arg)  {  	struct btrfs_ioctl_vol_args *vol_args; +	bool restore_op = false;  	int ret;  	if (!capable(CAP_SYS_ADMIN))  		return -EPERM; -	if (!btrfs_exclop_start(fs_info, BTRFS_EXCLOP_DEV_ADD)) -		return BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS; +	if (!btrfs_exclop_start(fs_info, BTRFS_EXCLOP_DEV_ADD)) { +		if (!btrfs_exclop_start_try_lock(fs_info, BTRFS_EXCLOP_DEV_ADD)) +			return BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS; + +		/* +		 * We can do the device add because we have a paused balanced, +		 * change the exclusive op type and remember we should bring +		 * back the paused balance +		 */ +		fs_info->exclusive_operation = BTRFS_EXCLOP_DEV_ADD; +		btrfs_exclop_start_unlock(fs_info); +		restore_op = true; +	}  	vol_args = memdup_user(arg, sizeof(*vol_args));  	if (IS_ERR(vol_args)) { @@ -3170,7 +3208,10 @@ static long btrfs_ioctl_add_dev(struct btrfs_fs_info *fs_info, void __user *arg)  	kfree(vol_args);  out: -	btrfs_exclop_finish(fs_info); +	if (restore_op) +		btrfs_exclop_balance(fs_info, BTRFS_EXCLOP_BALANCE_PAUSED); +	else +		btrfs_exclop_finish(fs_info);  	return ret;  } @@ -3622,7 +3663,6 @@ static noinline long btrfs_ioctl_start_sync(struct btrfs_root *root,  {  	struct btrfs_trans_handle *trans;  	u64 transid; -	int ret;  	trans = btrfs_attach_transaction_barrier(root);  	if (IS_ERR(trans)) { @@ -3634,11 +3674,7 @@ static noinline long btrfs_ioctl_start_sync(struct btrfs_root *root,  		goto out;  	}  	transid = trans->transid; -	ret = btrfs_commit_transaction_async(trans); -	if (ret) { -		btrfs_end_transaction(trans); -		return ret; -	} +	btrfs_commit_transaction_async(trans);  out:  	if (argp)  		if (copy_to_user(argp, &transid, sizeof(transid))) @@ -4061,6 +4097,7 @@ locked:  			spin_lock(&fs_info->balance_lock);  			bctl->flags |= BTRFS_BALANCE_RESUME;  			spin_unlock(&fs_info->balance_lock); +			btrfs_exclop_balance(fs_info, BTRFS_EXCLOP_BALANCE);  			goto do_balance;  		}  | 
