2021-02-17 06:09:04

by Hyeongseok Kim

[permalink] [raw]
Subject: [PATCH v3 0/1] Add FITRIM ioctl support for exFAT filesystem

This is for adding FITRIM ioctl functionality to exFAT filesystem.
To do that, add generic ioctl function and FITRIM handler.

Changelog
=========
v2->v3:
- Remove unnecessary local variable
- Merge all changes to a single patch

v1->v2:
- Change variable declaration order as reverse tree style.
- Return -EOPNOTSUPP from sb_issue_discard() just as it is.
- Remove cond_resched() in while loop.
- Move ioctl related code into it's helper function.

Hyeongseok Kim (1):
exfat: add support ioctl and FITRIM function

fs/exfat/balloc.c | 81 +++++++++++++++++++++++++++++++++++++++++++++
fs/exfat/dir.c | 5 +++
fs/exfat/exfat_fs.h | 4 +++
fs/exfat/file.c | 53 +++++++++++++++++++++++++++++
4 files changed, 143 insertions(+)

--
2.27.0.83.g0313f36


2021-02-17 06:09:11

by Hyeongseok Kim

[permalink] [raw]
Subject: [PATCH v3 1/1] exfat: add support ioctl and FITRIM function

Add FITRIM ioctl to enable discarding unused blocks while mounted.
As current exFAT doesn't have generic ioctl handler, add empty ioctl
function first, and add FITRIM handler.

Signed-off-by: Hyeongseok Kim <[email protected]>
---
fs/exfat/balloc.c | 81 +++++++++++++++++++++++++++++++++++++++++++++
fs/exfat/dir.c | 5 +++
fs/exfat/exfat_fs.h | 4 +++
fs/exfat/file.c | 53 +++++++++++++++++++++++++++++
4 files changed, 143 insertions(+)

diff --git a/fs/exfat/balloc.c b/fs/exfat/balloc.c
index 761c79c3a4ba..d47beef66892 100644
--- a/fs/exfat/balloc.c
+++ b/fs/exfat/balloc.c
@@ -273,3 +273,84 @@ int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count)
*ret_count = count;
return 0;
}
+
+int exfat_trim_fs(struct inode *inode, struct fstrim_range *range)
+{
+ unsigned int trim_begin, trim_end, count, next_free_clu;
+ u64 clu_start, clu_end, trim_minlen, trimmed_total = 0;
+ struct super_block *sb = inode->i_sb;
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ int err = 0;
+
+ clu_start = max_t(u64, range->start >> sbi->cluster_size_bits,
+ EXFAT_FIRST_CLUSTER);
+ clu_end = clu_start + (range->len >> sbi->cluster_size_bits) - 1;
+ trim_minlen = range->minlen >> sbi->cluster_size_bits;
+
+ if (clu_start >= sbi->num_clusters || range->len < sbi->cluster_size)
+ return -EINVAL;
+
+ if (clu_end >= sbi->num_clusters)
+ clu_end = sbi->num_clusters - 1;
+
+ mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock);
+
+ trim_begin = trim_end = exfat_find_free_bitmap(sb, clu_start);
+ if (trim_begin == EXFAT_EOF_CLUSTER)
+ goto unlock;
+
+ next_free_clu = exfat_find_free_bitmap(sb, trim_end + 1);
+ if (next_free_clu == EXFAT_EOF_CLUSTER)
+ goto unlock;
+
+ do {
+ if (next_free_clu == trim_end + 1)
+ /* extend trim range for continuous free cluster */
+ trim_end++;
+ else {
+ /* trim current range if it's larger than trim_minlen */
+ count = trim_end - trim_begin + 1;
+ if (count >= trim_minlen) {
+ err = sb_issue_discard(sb,
+ exfat_cluster_to_sector(sbi, trim_begin),
+ count * sbi->sect_per_clus, GFP_NOFS, 0);
+ if (err)
+ goto unlock;
+
+ trimmed_total += count;
+ }
+
+ /* set next start point of the free hole */
+ trim_begin = trim_end = next_free_clu;
+ }
+
+ if (next_free_clu >= clu_end)
+ break;
+
+ if (fatal_signal_pending(current)) {
+ err = -ERESTARTSYS;
+ goto unlock;
+ }
+
+ next_free_clu = exfat_find_free_bitmap(sb, next_free_clu + 1);
+
+ } while (next_free_clu != EXFAT_EOF_CLUSTER &&
+ next_free_clu > trim_end);
+
+ /* try to trim remainder */
+ count = trim_end - trim_begin + 1;
+ if (count >= trim_minlen) {
+ err = sb_issue_discard(sb, exfat_cluster_to_sector(sbi, trim_begin),
+ count * sbi->sect_per_clus, GFP_NOFS, 0);
+ if (err)
+ goto unlock;
+
+ trimmed_total += count;
+ }
+
+unlock:
+ mutex_unlock(&EXFAT_SB(inode->i_sb)->s_lock);
+ range->len = trimmed_total << sbi->cluster_size_bits;
+
+ return err;
+}
diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 916797077aad..e1d5536de948 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -4,6 +4,7 @@
*/

#include <linux/slab.h>
+#include <linux/compat.h>
#include <linux/bio.h>
#include <linux/buffer_head.h>

@@ -306,6 +307,10 @@ const struct file_operations exfat_dir_operations = {
.llseek = generic_file_llseek,
.read = generic_read_dir,
.iterate = exfat_iterate,
+ .unlocked_ioctl = exfat_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = exfat_compat_ioctl,
+#endif
.fsync = exfat_file_fsync,
};

diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 764bc645241e..e050aea0b639 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -411,6 +411,7 @@ int exfat_set_bitmap(struct inode *inode, unsigned int clu);
void exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync);
unsigned int exfat_find_free_bitmap(struct super_block *sb, unsigned int clu);
int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count);
+int exfat_trim_fs(struct inode *inode, struct fstrim_range *range);

/* file.c */
extern const struct file_operations exfat_file_operations;
@@ -420,6 +421,9 @@ int exfat_setattr(struct dentry *dentry, struct iattr *attr);
int exfat_getattr(const struct path *path, struct kstat *stat,
unsigned int request_mask, unsigned int query_flags);
int exfat_file_fsync(struct file *file, loff_t start, loff_t end, int datasync);
+long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
+long exfat_compat_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg);

/* namei.c */
extern const struct dentry_operations exfat_dentry_ops;
diff --git a/fs/exfat/file.c b/fs/exfat/file.c
index a92478eabfa4..3f88fccabe46 100644
--- a/fs/exfat/file.c
+++ b/fs/exfat/file.c
@@ -4,6 +4,7 @@
*/

#include <linux/slab.h>
+#include <linux/compat.h>
#include <linux/cred.h>
#include <linux/buffer_head.h>
#include <linux/blkdev.h>
@@ -348,6 +349,54 @@ int exfat_setattr(struct dentry *dentry, struct iattr *attr)
return error;
}

+static int exfat_ioctl_fitrim(struct inode *inode, unsigned long arg)
+{
+ struct request_queue *q = bdev_get_queue(inode->i_sb->s_bdev);
+ struct fstrim_range range;
+ int ret = 0;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (!blk_queue_discard(q))
+ return -EOPNOTSUPP;
+
+ if (copy_from_user(&range, (struct fstrim_range __user *)arg, sizeof(range)))
+ return -EFAULT;
+
+ range.minlen = max_t(unsigned int, range.minlen,
+ q->limits.discard_granularity);
+
+ ret = exfat_trim_fs(inode, &range);
+ if (ret < 0)
+ return ret;
+
+ if (copy_to_user((struct fstrim_range __user *)arg, &range, sizeof(range)))
+ return -EFAULT;
+
+ return 0;
+}
+
+long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct inode *inode = file_inode(filp);
+
+ switch (cmd) {
+ case FITRIM:
+ return exfat_ioctl_fitrim(inode, arg);
+ default:
+ return -ENOTTY;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+long exfat_compat_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ return exfat_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
{
struct inode *inode = filp->f_mapping->host;
@@ -368,6 +417,10 @@ const struct file_operations exfat_file_operations = {
.llseek = generic_file_llseek,
.read_iter = generic_file_read_iter,
.write_iter = generic_file_write_iter,
+ .unlocked_ioctl = exfat_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = exfat_compat_ioctl,
+#endif
.mmap = generic_file_mmap,
.fsync = exfat_file_fsync,
.splice_read = generic_file_splice_read,
--
2.27.0.83.g0313f36

2021-02-18 01:30:14

by Chaitanya Kulkarni

[permalink] [raw]
Subject: Re: [PATCH v3 1/1] exfat: add support ioctl and FITRIM function

On 2/16/21 22:07, Hyeongseok Kim wrote:
> Add FITRIM ioctl to enable discarding unused blocks while mounted.
> As current exFAT doesn't have generic ioctl handler, add empty ioctl
> function first, and add FITRIM handler.
>
> Signed-off-by: Hyeongseok Kim <[email protected]>
Looks good.

Reviewed-by: Chaitanya Kulkarni <[email protected]>