* [PATCH 1/6] btrfs: extend trim callchain to pass the operation type
2018-04-20 14:21 [PATCH 0/6] Add ioctl to clear unused space David Sterba
@ 2018-04-20 14:21 ` David Sterba
2018-04-20 14:21 ` [PATCH 2/6] btrfs: add wrapper to switch clearing operation David Sterba
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: David Sterba @ 2018-04-20 14:21 UTC (permalink / raw)
To: linux-btrfs; +Cc: David Sterba
This is preparatory work to implement clearing free space by zeroing,
much in the same way the FITRIM ioctl works. This patch simply defines
a symbolic name for the current clearing operation and passes the
parameter around. No functional change.
Signed-off-by: David Sterba <dsterba@suse.com>
---
fs/btrfs/ctree.h | 11 ++++++++-
fs/btrfs/extent-tree.c | 54 +++++++++++++++++++++++++++++++--------------
fs/btrfs/free-space-cache.c | 22 ++++++++++--------
fs/btrfs/free-space-cache.h | 3 ++-
4 files changed, 62 insertions(+), 28 deletions(-)
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 5474ef14d6e6..7cde72683b8e 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -674,6 +674,14 @@ struct btrfs_stripe_hash {
spinlock_t lock;
};
+/*
+ * Type of operation that will be used to clear unused blocks.
+ */
+enum btrfs_clear_op_type {
+ BTRFS_CLEAR_OP_DISCARD = 0,
+ BTRFS_NR_CLEAR_OP_TYPES,
+};
+
/* used by the raid56 code to lock stripes for read/modify/write */
struct btrfs_stripe_hash_table {
struct list_head stripe_cache;
@@ -2789,7 +2797,8 @@ u64 btrfs_account_ro_block_groups_free_space(struct btrfs_space_info *sinfo);
int btrfs_error_unpin_extent_range(struct btrfs_fs_info *fs_info,
u64 start, u64 end);
int btrfs_discard_extent(struct btrfs_fs_info *fs_info, u64 bytenr,
- u64 num_bytes, u64 *actual_bytes);
+ u64 num_bytes, u64 *actual_bytes,
+ enum btrfs_clear_op_type clear);
int btrfs_force_chunk_alloc(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info, u64 type);
int btrfs_trim_fs(struct btrfs_fs_info *fs_info, struct fstrim_range *range);
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 5bdb5636d552..ffdd3aba508c 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -2036,7 +2036,8 @@ static int remove_extent_backref(struct btrfs_trans_handle *trans,
#define in_range(b, first, len) ((b) >= (first) && (b) < (first) + (len))
static int btrfs_issue_discard(struct block_device *bdev, u64 start, u64 len,
- u64 *discarded_bytes)
+ u64 *discarded_bytes,
+ enum btrfs_clear_op_type clear)
{
int j, ret = 0;
u64 bytes_left, end;
@@ -2082,8 +2083,11 @@ static int btrfs_issue_discard(struct block_device *bdev, u64 start, u64 len,
}
if (size) {
- ret = blkdev_issue_discard(bdev, start >> 9, size >> 9,
- GFP_NOFS, 0);
+ if (clear == BTRFS_CLEAR_OP_DISCARD)
+ ret = blkdev_issue_discard(bdev, start >> 9,
+ size >> 9, GFP_NOFS, 0);
+ else
+ ret = -EOPNOTSUPP;
if (!ret)
*discarded_bytes += size;
else if (ret != -EOPNOTSUPP)
@@ -2099,8 +2103,11 @@ static int btrfs_issue_discard(struct block_device *bdev, u64 start, u64 len,
}
if (bytes_left) {
- ret = blkdev_issue_discard(bdev, start >> 9, bytes_left >> 9,
- GFP_NOFS, 0);
+ if (clear == BTRFS_CLEAR_OP_DISCARD)
+ ret = blkdev_issue_discard(bdev, start >> 9,
+ bytes_left >> 9, GFP_NOFS, 0);
+ else
+ ret = -EOPNOTSUPP;
if (!ret)
*discarded_bytes += bytes_left;
}
@@ -2108,12 +2115,18 @@ static int btrfs_issue_discard(struct block_device *bdev, u64 start, u64 len,
}
int btrfs_discard_extent(struct btrfs_fs_info *fs_info, u64 bytenr,
- u64 num_bytes, u64 *actual_bytes)
+ u64 num_bytes, u64 *actual_bytes,
+ enum btrfs_clear_op_type clear)
{
int ret;
u64 discarded_bytes = 0;
struct btrfs_bio *bbio = NULL;
+ int rw;
+ if (clear == BTRFS_CLEAR_OP_DISCARD)
+ rw = BTRFS_MAP_DISCARD;
+ else
+ rw = BTRFS_MAP_WRITE;
/*
* Avoid races with device replace and make sure our bbio has devices
@@ -2121,8 +2134,7 @@ int btrfs_discard_extent(struct btrfs_fs_info *fs_info, u64 bytenr,
*/
btrfs_bio_counter_inc_blocked(fs_info);
/* Tell the block device(s) that the sectors can be discarded */
- ret = btrfs_map_block(fs_info, BTRFS_MAP_DISCARD, bytenr, &num_bytes,
- &bbio, 0);
+ ret = btrfs_map_block(fs_info, rw, bytenr, &num_bytes, &bbio, 0);
/* Error condition is -ENOMEM */
if (!ret) {
struct btrfs_bio_stripe *stripe = bbio->stripes;
@@ -2138,13 +2150,14 @@ int btrfs_discard_extent(struct btrfs_fs_info *fs_info, u64 bytenr,
continue;
}
req_q = bdev_get_queue(stripe->dev->bdev);
- if (!blk_queue_discard(req_q))
+ if (clear == BTRFS_CLEAR_OP_DISCARD &&
+ !blk_queue_discard(req_q))
continue;
ret = btrfs_issue_discard(stripe->dev->bdev,
stripe->physical,
stripe->length,
- &bytes);
+ &bytes, clear);
if (!ret)
discarded_bytes += bytes;
else if (ret != -EOPNOTSUPP)
@@ -6785,7 +6798,8 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans)
if (btrfs_test_opt(fs_info, DISCARD))
ret = btrfs_discard_extent(fs_info, start,
- end + 1 - start, NULL);
+ end + 1 - start, NULL,
+ BTRFS_CLEAR_OP_DISCARD);
clear_extent_dirty(unpin, start, end);
unpin_extent_range(fs_info, start, end, true);
@@ -6807,7 +6821,8 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans)
ret = btrfs_discard_extent(fs_info,
block_group->key.objectid,
block_group->key.offset,
- &trimmed);
+ &trimmed,
+ BTRFS_CLEAR_OP_DISCARD);
list_del_init(&block_group->bg_list);
btrfs_put_block_group_trimming(block_group);
@@ -8056,7 +8071,8 @@ static int __btrfs_free_reserved_extent(struct btrfs_fs_info *fs_info,
pin_down_extent(fs_info, cache, start, len, 1);
else {
if (btrfs_test_opt(fs_info, DISCARD))
- ret = btrfs_discard_extent(fs_info, start, len, NULL);
+ ret = btrfs_discard_extent(fs_info, start, len, NULL,
+ BTRFS_CLEAR_OP_DISCARD);
btrfs_add_free_space(cache, start, len);
btrfs_free_reserved_bytes(cache, len, delalloc);
trace_btrfs_reserved_extent_free(fs_info, start, len);
@@ -10892,7 +10908,8 @@ int btrfs_error_unpin_extent_range(struct btrfs_fs_info *fs_info,
* transaction.
*/
static int btrfs_trim_free_extents(struct btrfs_device *device,
- u64 minlen, u64 *trimmed)
+ u64 minlen, u64 *trimmed,
+ enum btrfs_clear_op_type clear)
{
u64 start = 0, len = 0;
int ret;
@@ -10939,7 +10956,8 @@ static int btrfs_trim_free_extents(struct btrfs_device *device,
break;
}
- ret = btrfs_issue_discard(device->bdev, start, len, &bytes);
+ ret = btrfs_issue_discard(device->bdev, start, len, &bytes,
+ clear);
up_read(&fs_info->commit_root_sem);
mutex_unlock(&fs_info->chunk_mutex);
@@ -11007,7 +11025,8 @@ int btrfs_trim_fs(struct btrfs_fs_info *fs_info, struct fstrim_range *range)
&group_trimmed,
start,
end,
- range->minlen);
+ range->minlen,
+ BTRFS_CLEAR_OP_DISCARD);
trimmed += group_trimmed;
if (ret) {
@@ -11023,7 +11042,8 @@ int btrfs_trim_fs(struct btrfs_fs_info *fs_info, struct fstrim_range *range)
devices = &fs_info->fs_devices->alloc_list;
list_for_each_entry(device, devices, dev_alloc_list) {
ret = btrfs_trim_free_extents(device, range->minlen,
- &group_trimmed);
+ &group_trimmed,
+ BTRFS_CLEAR_OP_DISCARD);
if (ret)
break;
diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c
index e5b569bebc73..be7bfb99df9a 100644
--- a/fs/btrfs/free-space-cache.c
+++ b/fs/btrfs/free-space-cache.c
@@ -3102,7 +3102,8 @@ void btrfs_init_free_cluster(struct btrfs_free_cluster *cluster)
static int do_trimming(struct btrfs_block_group_cache *block_group,
u64 *total_trimmed, u64 start, u64 bytes,
u64 reserved_start, u64 reserved_bytes,
- struct btrfs_trim_range *trim_entry)
+ struct btrfs_trim_range *trim_entry,
+ enum btrfs_clear_op_type clear)
{
struct btrfs_space_info *space_info = block_group->space_info;
struct btrfs_fs_info *fs_info = block_group->fs_info;
@@ -3121,7 +3122,7 @@ static int do_trimming(struct btrfs_block_group_cache *block_group,
spin_unlock(&block_group->lock);
spin_unlock(&space_info->lock);
- ret = btrfs_discard_extent(fs_info, start, bytes, &trimmed);
+ ret = btrfs_discard_extent(fs_info, start, bytes, &trimmed, clear);
if (!ret)
*total_trimmed += trimmed;
@@ -3145,7 +3146,8 @@ static int do_trimming(struct btrfs_block_group_cache *block_group,
}
static int trim_no_bitmap(struct btrfs_block_group_cache *block_group,
- u64 *total_trimmed, u64 start, u64 end, u64 minlen)
+ u64 *total_trimmed, u64 start, u64 end, u64 minlen,
+ enum btrfs_clear_op_type clear)
{
struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl;
struct btrfs_free_space *entry;
@@ -3212,7 +3214,7 @@ static int trim_no_bitmap(struct btrfs_block_group_cache *block_group,
mutex_unlock(&ctl->cache_writeout_mutex);
ret = do_trimming(block_group, total_trimmed, start, bytes,
- extent_start, extent_bytes, &trim_entry);
+ extent_start, extent_bytes, &trim_entry, clear);
if (ret)
break;
next:
@@ -3230,7 +3232,8 @@ static int trim_no_bitmap(struct btrfs_block_group_cache *block_group,
}
static int trim_bitmaps(struct btrfs_block_group_cache *block_group,
- u64 *total_trimmed, u64 start, u64 end, u64 minlen)
+ u64 *total_trimmed, u64 start, u64 end, u64 minlen,
+ enum btrfs_clear_op_type clear)
{
struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl;
struct btrfs_free_space *entry;
@@ -3287,7 +3290,7 @@ static int trim_bitmaps(struct btrfs_block_group_cache *block_group,
mutex_unlock(&ctl->cache_writeout_mutex);
ret = do_trimming(block_group, total_trimmed, start, bytes,
- start, bytes, &trim_entry);
+ start, bytes, &trim_entry, clear);
if (ret)
break;
next:
@@ -3355,7 +3358,8 @@ void btrfs_put_block_group_trimming(struct btrfs_block_group_cache *block_group)
}
int btrfs_trim_block_group(struct btrfs_block_group_cache *block_group,
- u64 *trimmed, u64 start, u64 end, u64 minlen)
+ u64 *trimmed, u64 start, u64 end, u64 minlen,
+ enum btrfs_clear_op_type clear)
{
int ret;
@@ -3369,11 +3373,11 @@ int btrfs_trim_block_group(struct btrfs_block_group_cache *block_group,
btrfs_get_block_group_trimming(block_group);
spin_unlock(&block_group->lock);
- ret = trim_no_bitmap(block_group, trimmed, start, end, minlen);
+ ret = trim_no_bitmap(block_group, trimmed, start, end, minlen, clear);
if (ret)
goto out;
- ret = trim_bitmaps(block_group, trimmed, start, end, minlen);
+ ret = trim_bitmaps(block_group, trimmed, start, end, minlen, clear);
out:
btrfs_put_block_group_trimming(block_group);
return ret;
diff --git a/fs/btrfs/free-space-cache.h b/fs/btrfs/free-space-cache.h
index 15e30b93db0d..794a444c3f73 100644
--- a/fs/btrfs/free-space-cache.h
+++ b/fs/btrfs/free-space-cache.h
@@ -107,7 +107,8 @@ int btrfs_return_cluster_to_free_space(
struct btrfs_block_group_cache *block_group,
struct btrfs_free_cluster *cluster);
int btrfs_trim_block_group(struct btrfs_block_group_cache *block_group,
- u64 *trimmed, u64 start, u64 end, u64 minlen);
+ u64 *trimmed, u64 start, u64 end, u64 minlen,
+ enum btrfs_clear_op_type clear);
/* Support functions for running our sanity tests */
#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
--
2.16.2
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 4/6] btrfs: add new ioctl BTRFS_IOC_CLEAR_FREE
2018-04-20 14:21 [PATCH 0/6] Add ioctl to clear unused space David Sterba
` (2 preceding siblings ...)
2018-04-20 14:21 ` [PATCH 3/6] btrfs: add zeroing clear operation David Sterba
@ 2018-04-20 14:21 ` David Sterba
2018-04-20 14:21 ` [PATCH 5/6] btrfs: add discard secure to clear unused space David Sterba
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: David Sterba @ 2018-04-20 14:21 UTC (permalink / raw)
To: linux-btrfs; +Cc: David Sterba
A new ioctl that will clear the free space (by writing zeros) given by
the user range. Similar to the TRIM ioctl.
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>
---
fs/btrfs/ctree.h | 11 ++---------
fs/btrfs/extent-tree.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++
fs/btrfs/ioctl.c | 42 ++++++++++++++++++++++++++++++++++++++++
include/uapi/linux/btrfs.h | 20 +++++++++++++++++++
4 files changed, 112 insertions(+), 9 deletions(-)
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 772cb4ccc5f7..d8cc70a6bef7 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -674,15 +674,6 @@ struct btrfs_stripe_hash {
spinlock_t lock;
};
-/*
- * Type of operation that will be used to clear unused blocks.
- */
-enum btrfs_clear_op_type {
- BTRFS_CLEAR_OP_DISCARD = 0,
- BTRFS_CLEAR_OP_ZERO,
- BTRFS_NR_CLEAR_OP_TYPES,
-};
-
/* used by the raid56 code to lock stripes for read/modify/write */
struct btrfs_stripe_hash_table {
struct list_head stripe_cache;
@@ -2800,6 +2791,8 @@ int btrfs_error_unpin_extent_range(struct btrfs_fs_info *fs_info,
int btrfs_discard_extent(struct btrfs_fs_info *fs_info, u64 bytenr,
u64 num_bytes, u64 *actual_bytes,
enum btrfs_clear_op_type clear);
+int btrfs_clear_free_space(struct btrfs_root *root,
+ struct btrfs_ioctl_clear_free_args *args);
int btrfs_force_chunk_alloc(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info, u64 type);
int btrfs_trim_fs(struct btrfs_fs_info *fs_info, struct fstrim_range *range);
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 800aaf45e6bd..21a24fff32dd 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -11061,6 +11061,54 @@ int btrfs_trim_fs(struct btrfs_fs_info *fs_info, struct fstrim_range *range)
return ret;
}
+int btrfs_clear_free_space(struct btrfs_root *root,
+ struct btrfs_ioctl_clear_free_args *args)
+{
+ struct btrfs_fs_info *fs_info = root->fs_info;
+ struct btrfs_block_group_cache *cache = NULL;
+ u64 group_cleared;
+ u64 start;
+ u64 cleared = 0;
+ u64 end;
+ int ret = 0;
+
+ cache = btrfs_lookup_first_block_group(fs_info, args->start);
+
+ while (cache) {
+ if (cache->key.objectid >= (args->start + args->length)) {
+ btrfs_put_block_group(cache);
+ break;
+ }
+
+ start = max(args->start, cache->key.objectid);
+ end = min(args->start + args->length,
+ cache->key.objectid + cache->key.offset);
+
+ if (end - start >= args->minlen) {
+ if (!block_group_cache_done(cache)) {
+ ret = cache_block_group(cache, 0);
+ if (!ret)
+ wait_block_group_cache_done(cache);
+ }
+ ret = btrfs_trim_block_group(cache, &group_cleared,
+ start, end, args->minlen,
+ args->type);
+
+ if (ret) {
+ btrfs_put_block_group(cache);
+ break;
+ }
+ cleared += group_cleared;
+ }
+
+ cache = next_block_group(fs_info, cache);
+ }
+
+ args->length = cleared;
+
+ return ret;
+}
+
/*
* btrfs_{start,end}_write_no_snapshotting() are similar to
* mnt_{want,drop}_write(), they are used to prevent some tasks from writing
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 9cc4fd25f83d..a56d4fb3ae82 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -5429,6 +5429,46 @@ static int _btrfs_ioctl_send(struct file *file, void __user *argp, bool compat)
return ret;
}
+static int btrfs_ioctl_clear_free(struct file *file, void __user *arg)
+{
+ struct btrfs_fs_info *fs_info;
+ struct btrfs_ioctl_clear_free_args args;
+ u64 total_bytes;
+ int ret;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (copy_from_user(&args, arg, sizeof(args)))
+ return -EFAULT;
+
+ if (args.type >= BTRFS_NR_CLEAR_OP_TYPES)
+ return -EOPNOTSUPP;
+
+ ret = mnt_want_write_file(file);
+ if (ret)
+ return ret;
+
+ fs_info = btrfs_sb(file_inode(file)->i_sb);
+ total_bytes = btrfs_super_total_bytes(fs_info->super_copy);
+ if (args.start > total_bytes) {
+ ret = -EINVAL;
+ goto out_drop_write;
+ }
+
+ ret = btrfs_clear_free_space(fs_info->tree_root, &args);
+ if (ret < 0)
+ goto out_drop_write;
+
+ if (copy_to_user(arg, &args, sizeof(args)))
+ ret = -EFAULT;
+
+out_drop_write:
+ mnt_drop_write_file(file);
+
+ return 0;
+}
+
long btrfs_ioctl(struct file *file, unsigned int
cmd, unsigned long arg)
{
@@ -5565,6 +5605,8 @@ long btrfs_ioctl(struct file *file, unsigned int
return btrfs_ioctl_get_features(file, argp);
case BTRFS_IOC_SET_FEATURES:
return btrfs_ioctl_set_features(file, argp);
+ case BTRFS_IOC_CLEAR_FREE:
+ return btrfs_ioctl_clear_free(file, argp);
}
return -ENOTTY;
diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h
index c8d99b9ca550..860f9df01614 100644
--- a/include/uapi/linux/btrfs.h
+++ b/include/uapi/linux/btrfs.h
@@ -737,6 +737,24 @@ enum btrfs_err_code {
BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS
};
+/*
+ * Type of operation that will be used to clear unused blocks.
+ */
+enum btrfs_clear_op_type {
+ BTRFS_CLEAR_OP_DISCARD = 0,
+ BTRFS_CLEAR_OP_ZERO,
+ BTRFS_NR_CLEAR_OP_TYPES,
+};
+
+struct btrfs_ioctl_clear_free_args {
+ __u32 type; /* in, btrfs_clear_free_op_type */
+ __u32 reserved1; /* padding, must be zero */
+ __u64 start; /* in */
+ __u64 length; /* in, out */
+ __u64 minlen; /* in */
+ __u64 reserved2[4];
+};
+
#define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \
struct btrfs_ioctl_vol_args)
#define BTRFS_IOC_DEFRAG _IOW(BTRFS_IOCTL_MAGIC, 2, \
@@ -843,5 +861,7 @@ enum btrfs_err_code {
struct btrfs_ioctl_vol_args_v2)
#define BTRFS_IOC_LOGICAL_INO_V2 _IOWR(BTRFS_IOCTL_MAGIC, 59, \
struct btrfs_ioctl_logical_ino_args)
+#define BTRFS_IOC_CLEAR_FREE _IOW(BTRFS_IOCTL_MAGIC, 90, \
+ struct btrfs_ioctl_clear_free_args)
#endif /* _UAPI_LINUX_BTRFS_H */
--
2.16.2
^ permalink raw reply related [flat|nested] 9+ messages in thread