Skip to content

Commit 982cb5c

Browse files
committed
btrfs: add new ioctl CLEAR_FREE
Add a new ioctl that is an extensible version of FITRIM. It currently does only the trim/discard and will be extended by other modes like zeroing or block unmapping. We need a new ioctl for that because struct fstrim_range does not provide any existing or reserved member for extensions. The new ioctl also supports TRIM as the operation type. Signed-off-by: David Sterba <dsterba@suse.com>
1 parent 81d9200 commit 982cb5c

File tree

4 files changed

+163
-0
lines changed

4 files changed

+163
-0
lines changed

fs/btrfs/extent-tree.c

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6617,3 +6617,95 @@ int btrfs_trim_fs(struct btrfs_fs_info *fs_info, struct fstrim_range *range)
66176617
return bg_ret;
66186618
return dev_ret;
66196619
}
6620+
6621+
int btrfs_clear_free_space(struct btrfs_fs_info *fs_info,
6622+
struct btrfs_ioctl_clear_free_args *args)
6623+
{
6624+
struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
6625+
struct btrfs_device *device;
6626+
struct btrfs_block_group *cache = NULL;
6627+
u64 group_cleared;
6628+
u64 range_end = U64_MAX;
6629+
u64 start;
6630+
u64 end;
6631+
u64 cleared = 0;
6632+
u64 bg_failed = 0;
6633+
u64 dev_failed = 0;
6634+
int bg_ret = 0;
6635+
int dev_ret = 0;
6636+
int ret = 0;
6637+
6638+
if (args->start == U64_MAX)
6639+
return -EINVAL;
6640+
6641+
/*
6642+
* Check range overflow if args->length is set. The default args->length
6643+
* is U64_MAX.
6644+
*/
6645+
if (args->length != U64_MAX &&
6646+
check_add_overflow(args->start, args->length, &range_end))
6647+
return -EINVAL;
6648+
6649+
cache = btrfs_lookup_first_block_group(fs_info, args->start);
6650+
for (; cache; cache = btrfs_next_block_group(cache)) {
6651+
if (cache->start >= range_end) {
6652+
btrfs_put_block_group(cache);
6653+
break;
6654+
}
6655+
6656+
start = max(args->start, cache->start);
6657+
end = min(range_end, cache->start + cache->length);
6658+
6659+
if (end - start >= args->minlen) {
6660+
if (!btrfs_block_group_done(cache)) {
6661+
ret = btrfs_cache_block_group(cache, true);
6662+
if (ret) {
6663+
bg_failed++;
6664+
bg_ret = ret;
6665+
continue;
6666+
}
6667+
}
6668+
ret = btrfs_trim_block_group(cache, &group_cleared,
6669+
start, end, args->minlen,
6670+
args->type);
6671+
6672+
cleared += group_cleared;
6673+
if (ret) {
6674+
bg_failed++;
6675+
bg_ret = ret;
6676+
continue;
6677+
}
6678+
}
6679+
}
6680+
6681+
if (bg_failed)
6682+
btrfs_warn(fs_info,
6683+
"failed to clear %llu block group(s), last error %d",
6684+
bg_failed, bg_ret);
6685+
6686+
mutex_lock(&fs_devices->device_list_mutex);
6687+
list_for_each_entry(device, &fs_devices->devices, dev_list) {
6688+
if (test_bit(BTRFS_DEV_STATE_MISSING, &device->dev_state))
6689+
continue;
6690+
6691+
ret = btrfs_trim_free_extents(device, &group_cleared, args->type);
6692+
if (ret) {
6693+
dev_failed++;
6694+
dev_ret = ret;
6695+
break;
6696+
}
6697+
6698+
cleared += group_cleared;
6699+
}
6700+
mutex_unlock(&fs_devices->device_list_mutex);
6701+
6702+
if (dev_failed)
6703+
btrfs_warn(fs_info,
6704+
"failed to trim %llu device(s), last error %d",
6705+
dev_failed, dev_ret);
6706+
args->length = cleared;
6707+
if (bg_ret)
6708+
return bg_ret;
6709+
6710+
return dev_ret;
6711+
}

fs/btrfs/extent-tree.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,5 +166,7 @@ int btrfs_discard_extent(struct btrfs_fs_info *fs_info, u64 bytenr,
166166
u64 num_bytes, u64 *actual_bytes,
167167
enum btrfs_clear_op_type clear);
168168
int btrfs_trim_fs(struct btrfs_fs_info *fs_info, struct fstrim_range *range);
169+
int btrfs_clear_free_space(struct btrfs_fs_info *fs_info,
170+
struct btrfs_ioctl_clear_free_args *args);
169171

170172
#endif

fs/btrfs/ioctl.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5210,6 +5210,53 @@ static int btrfs_ioctl_subvol_sync(struct btrfs_fs_info *fs_info, void __user *a
52105210
return 0;
52115211
}
52125212

5213+
static int btrfs_ioctl_clear_free(struct file *file, void __user *arg)
5214+
{
5215+
struct btrfs_fs_info *fs_info = inode_to_fs_info(file_inode(file));
5216+
struct btrfs_ioctl_clear_free_args args;
5217+
u64 total_bytes;
5218+
int ret;
5219+
5220+
if (!capable(CAP_SYS_ADMIN))
5221+
return -EPERM;
5222+
5223+
/*
5224+
* This can be relaxed to support conventional zones or zones that can
5225+
* be reset. Otherwise the assumptions of write pointer are not
5226+
* compatible with zeroout or trim.
5227+
*/
5228+
if (btrfs_is_zoned(fs_info))
5229+
return -EOPNOTSUPP;
5230+
5231+
if (copy_from_user(&args, arg, sizeof(args)))
5232+
return -EFAULT;
5233+
5234+
if (args.type >= BTRFS_NR_CLEAR_OP_TYPES)
5235+
return -EOPNOTSUPP;
5236+
5237+
ret = mnt_want_write_file(file);
5238+
if (ret)
5239+
return ret;
5240+
5241+
total_bytes = btrfs_super_total_bytes(fs_info->super_copy);
5242+
if (args.start > total_bytes) {
5243+
ret = -EINVAL;
5244+
goto out_drop_write;
5245+
}
5246+
5247+
ret = btrfs_clear_free_space(fs_info, &args);
5248+
if (ret < 0)
5249+
goto out_drop_write;
5250+
5251+
if (copy_to_user(arg, &args, sizeof(args)))
5252+
ret = -EFAULT;
5253+
5254+
out_drop_write:
5255+
mnt_drop_write_file(file);
5256+
5257+
return ret;
5258+
}
5259+
52135260
long btrfs_ioctl(struct file *file, unsigned int
52145261
cmd, unsigned long arg)
52155262
{
@@ -5365,6 +5412,8 @@ long btrfs_ioctl(struct file *file, unsigned int
53655412
#endif
53665413
case BTRFS_IOC_SUBVOL_SYNC_WAIT:
53675414
return btrfs_ioctl_subvol_sync(fs_info, argp);
5415+
case BTRFS_IOC_CLEAR_FREE:
5416+
return btrfs_ioctl_clear_free(file, argp);
53685417
}
53695418

53705419
return -ENOTTY;

include/uapi/linux/btrfs.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,6 +1104,24 @@ enum btrfs_clear_op_type {
11041104
BTRFS_NR_CLEAR_OP_TYPES,
11051105
};
11061106

1107+
struct btrfs_ioctl_clear_free_args {
1108+
/* In, type of clearing operation, enumerated in btrfs_clear_free_op_type. */
1109+
__u32 type;
1110+
/* Reserved must be zero. */
1111+
__u32 reserved1;
1112+
/*
1113+
* In. Starting offset to clear from in the logical address space (same
1114+
* as fstrim_range::start).
1115+
*/
1116+
__u64 start; /* in */
1117+
/* In, out. Length from the start to clear (same as fstrim_range::length). */
1118+
__u64 length;
1119+
/* In. Minimal length to clear (same as fstrim_range::minlen). */
1120+
__u64 minlen;
1121+
/* Reserved, must be zero. */
1122+
__u64 reserved2[4];
1123+
};
1124+
11071125
#define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \
11081126
struct btrfs_ioctl_vol_args)
11091127
#define BTRFS_IOC_DEFRAG _IOW(BTRFS_IOCTL_MAGIC, 2, \
@@ -1224,6 +1242,8 @@ enum btrfs_clear_op_type {
12241242
struct btrfs_ioctl_encoded_io_args)
12251243
#define BTRFS_IOC_SUBVOL_SYNC_WAIT _IOW(BTRFS_IOCTL_MAGIC, 65, \
12261244
struct btrfs_ioctl_subvol_wait)
1245+
#define BTRFS_IOC_CLEAR_FREE _IOWR(BTRFS_IOCTL_MAGIC, 66, \
1246+
struct btrfs_ioctl_clear_free_args)
12271247

12281248
#ifdef __cplusplus
12291249
}

0 commit comments

Comments
 (0)