|
Kmods SIG |
50e2b3 |
From 654762df2ec7d61b05acc788afbffaba52d658fe Mon Sep 17 00:00:00 2001
|
|
Kmods SIG |
50e2b3 |
From: Hyeongseok Kim <hyeongseok@gmail.com>
|
|
Kmods SIG |
50e2b3 |
Date: Thu, 4 Mar 2021 09:20:35 +0900
|
|
Kmods SIG |
50e2b3 |
Subject: [Backport 654762df2ec7] exfat: add support ioctl and FITRIM function
|
|
Kmods SIG |
50e2b3 |
|
|
Kmods SIG |
50e2b3 |
Add FITRIM ioctl to enable discarding unused blocks while mounted.
|
|
Kmods SIG |
50e2b3 |
As current exFAT doesn't have generic ioctl handler, add empty ioctl
|
|
Kmods SIG |
50e2b3 |
function first, and add FITRIM handler.
|
|
Kmods SIG |
50e2b3 |
|
|
Kmods SIG |
50e2b3 |
Signed-off-by: Hyeongseok Kim <hyeongseok@gmail.com>
|
|
Kmods SIG |
50e2b3 |
Reviewed-by: Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
|
|
Kmods SIG |
50e2b3 |
Acked-by: Sungjong Seo <sj1557.seo@samsung.com>
|
|
Kmods SIG |
50e2b3 |
Signed-off-by: Namjae Jeon <namjae.jeon@samsung.com>
|
|
Kmods SIG |
50e2b3 |
---
|
|
Kmods SIG |
50e2b3 |
src/balloc.c | 80 +++++++++++++++++++++++++++++++++++++++++++++
|
|
Kmods SIG |
50e2b3 |
src/dir.c | 5 +++
|
|
Kmods SIG |
50e2b3 |
src/exfat_fs.h | 4 +++
|
|
Kmods SIG |
50e2b3 |
src/file.c | 53 ++++++++++++++++++++++++++++++
|
|
Kmods SIG |
50e2b3 |
4 files changed, 142 insertions(+)
|
|
Kmods SIG |
50e2b3 |
|
|
Kmods SIG |
50e2b3 |
diff --git a/src/balloc.c b/src/balloc.c
|
|
Kmods SIG |
50e2b3 |
index 411fb0a8da10d40be5610f55f72280d44eb03ee7..78bc87d5a11bf9688dbef42ad9c3b4f0c232c0f1 100644
|
|
Kmods SIG |
50e2b3 |
--- a/src/balloc.c
|
|
Kmods SIG |
50e2b3 |
+++ b/src/balloc.c
|
|
Kmods SIG |
50e2b3 |
@@ -264,3 +264,83 @@ int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count)
|
|
Kmods SIG |
50e2b3 |
*ret_count = count;
|
|
Kmods SIG |
50e2b3 |
return 0;
|
|
Kmods SIG |
50e2b3 |
}
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+int exfat_trim_fs(struct inode *inode, struct fstrim_range *range)
|
|
Kmods SIG |
50e2b3 |
+{
|
|
Kmods SIG |
50e2b3 |
+ unsigned int trim_begin, trim_end, count, next_free_clu;
|
|
Kmods SIG |
50e2b3 |
+ u64 clu_start, clu_end, trim_minlen, trimmed_total = 0;
|
|
Kmods SIG |
50e2b3 |
+ struct super_block *sb = inode->i_sb;
|
|
Kmods SIG |
50e2b3 |
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
|
Kmods SIG |
50e2b3 |
+ int err = 0;
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+ clu_start = max_t(u64, range->start >> sbi->cluster_size_bits,
|
|
Kmods SIG |
50e2b3 |
+ EXFAT_FIRST_CLUSTER);
|
|
Kmods SIG |
50e2b3 |
+ clu_end = clu_start + (range->len >> sbi->cluster_size_bits) - 1;
|
|
Kmods SIG |
50e2b3 |
+ trim_minlen = range->minlen >> sbi->cluster_size_bits;
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+ if (clu_start >= sbi->num_clusters || range->len < sbi->cluster_size)
|
|
Kmods SIG |
50e2b3 |
+ return -EINVAL;
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+ if (clu_end >= sbi->num_clusters)
|
|
Kmods SIG |
50e2b3 |
+ clu_end = sbi->num_clusters - 1;
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+ mutex_lock(&sbi->bitmap_lock);
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+ trim_begin = trim_end = exfat_find_free_bitmap(sb, clu_start);
|
|
Kmods SIG |
50e2b3 |
+ if (trim_begin == EXFAT_EOF_CLUSTER)
|
|
Kmods SIG |
50e2b3 |
+ goto unlock;
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+ next_free_clu = exfat_find_free_bitmap(sb, trim_end + 1);
|
|
Kmods SIG |
50e2b3 |
+ if (next_free_clu == EXFAT_EOF_CLUSTER)
|
|
Kmods SIG |
50e2b3 |
+ goto unlock;
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+ do {
|
|
Kmods SIG |
50e2b3 |
+ if (next_free_clu == trim_end + 1) {
|
|
Kmods SIG |
50e2b3 |
+ /* extend trim range for continuous free cluster */
|
|
Kmods SIG |
50e2b3 |
+ trim_end++;
|
|
Kmods SIG |
50e2b3 |
+ } else {
|
|
Kmods SIG |
50e2b3 |
+ /* trim current range if it's larger than trim_minlen */
|
|
Kmods SIG |
50e2b3 |
+ count = trim_end - trim_begin + 1;
|
|
Kmods SIG |
50e2b3 |
+ if (count >= trim_minlen) {
|
|
Kmods SIG |
50e2b3 |
+ err = sb_issue_discard(sb,
|
|
Kmods SIG |
50e2b3 |
+ exfat_cluster_to_sector(sbi, trim_begin),
|
|
Kmods SIG |
50e2b3 |
+ count * sbi->sect_per_clus, GFP_NOFS, 0);
|
|
Kmods SIG |
50e2b3 |
+ if (err)
|
|
Kmods SIG |
50e2b3 |
+ goto unlock;
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+ trimmed_total += count;
|
|
Kmods SIG |
50e2b3 |
+ }
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+ /* set next start point of the free hole */
|
|
Kmods SIG |
50e2b3 |
+ trim_begin = trim_end = next_free_clu;
|
|
Kmods SIG |
50e2b3 |
+ }
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+ if (next_free_clu >= clu_end)
|
|
Kmods SIG |
50e2b3 |
+ break;
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+ if (fatal_signal_pending(current)) {
|
|
Kmods SIG |
50e2b3 |
+ err = -ERESTARTSYS;
|
|
Kmods SIG |
50e2b3 |
+ goto unlock;
|
|
Kmods SIG |
50e2b3 |
+ }
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+ next_free_clu = exfat_find_free_bitmap(sb, next_free_clu + 1);
|
|
Kmods SIG |
50e2b3 |
+ } while (next_free_clu != EXFAT_EOF_CLUSTER &&
|
|
Kmods SIG |
50e2b3 |
+ next_free_clu > trim_end);
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+ /* try to trim remainder */
|
|
Kmods SIG |
50e2b3 |
+ count = trim_end - trim_begin + 1;
|
|
Kmods SIG |
50e2b3 |
+ if (count >= trim_minlen) {
|
|
Kmods SIG |
50e2b3 |
+ err = sb_issue_discard(sb, exfat_cluster_to_sector(sbi, trim_begin),
|
|
Kmods SIG |
50e2b3 |
+ count * sbi->sect_per_clus, GFP_NOFS, 0);
|
|
Kmods SIG |
50e2b3 |
+ if (err)
|
|
Kmods SIG |
50e2b3 |
+ goto unlock;
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+ trimmed_total += count;
|
|
Kmods SIG |
50e2b3 |
+ }
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+unlock:
|
|
Kmods SIG |
50e2b3 |
+ mutex_unlock(&sbi->bitmap_lock);
|
|
Kmods SIG |
50e2b3 |
+ range->len = trimmed_total << sbi->cluster_size_bits;
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+ return err;
|
|
Kmods SIG |
50e2b3 |
+}
|
|
Kmods SIG |
50e2b3 |
diff --git a/src/dir.c b/src/dir.c
|
|
Kmods SIG |
50e2b3 |
index 916797077aad4ab45cd73c36a5012fd2653fef33..e1d5536de948b3dcd4667cf3074b51462ddb0697 100644
|
|
Kmods SIG |
50e2b3 |
--- a/src/dir.c
|
|
Kmods SIG |
50e2b3 |
+++ b/src/dir.c
|
|
Kmods SIG |
50e2b3 |
@@ -4,6 +4,7 @@
|
|
Kmods SIG |
50e2b3 |
*/
|
|
Kmods SIG |
50e2b3 |
|
|
Kmods SIG |
50e2b3 |
#include <linux/slab.h>
|
|
Kmods SIG |
50e2b3 |
+#include <linux/compat.h>
|
|
Kmods SIG |
50e2b3 |
#include <linux/bio.h>
|
|
Kmods SIG |
50e2b3 |
#include <linux/buffer_head.h>
|
|
Kmods SIG |
50e2b3 |
|
|
Kmods SIG |
50e2b3 |
@@ -306,6 +307,10 @@ const struct file_operations exfat_dir_operations = {
|
|
Kmods SIG |
50e2b3 |
.llseek = generic_file_llseek,
|
|
Kmods SIG |
50e2b3 |
.read = generic_read_dir,
|
|
Kmods SIG |
50e2b3 |
.iterate = exfat_iterate,
|
|
Kmods SIG |
50e2b3 |
+ .unlocked_ioctl = exfat_ioctl,
|
|
Kmods SIG |
50e2b3 |
+#ifdef CONFIG_COMPAT
|
|
Kmods SIG |
50e2b3 |
+ .compat_ioctl = exfat_compat_ioctl,
|
|
Kmods SIG |
50e2b3 |
+#endif
|
|
Kmods SIG |
50e2b3 |
.fsync = exfat_file_fsync,
|
|
Kmods SIG |
50e2b3 |
};
|
|
Kmods SIG |
50e2b3 |
|
|
Kmods SIG |
50e2b3 |
diff --git a/src/exfat_fs.h b/src/exfat_fs.h
|
|
Kmods SIG |
50e2b3 |
index e05e0a3152c0e5c415bd042e40d680194f02b49e..169d0b602f5e7c06c8889b38d83abcfad2e8382c 100644
|
|
Kmods SIG |
50e2b3 |
--- a/src/exfat_fs.h
|
|
Kmods SIG |
50e2b3 |
+++ b/src/exfat_fs.h
|
|
Kmods SIG |
50e2b3 |
@@ -412,6 +412,7 @@ int exfat_set_bitmap(struct inode *inode, unsigned int clu);
|
|
Kmods SIG |
50e2b3 |
void exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync);
|
|
Kmods SIG |
50e2b3 |
unsigned int exfat_find_free_bitmap(struct super_block *sb, unsigned int clu);
|
|
Kmods SIG |
50e2b3 |
int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count);
|
|
Kmods SIG |
50e2b3 |
+int exfat_trim_fs(struct inode *inode, struct fstrim_range *range);
|
|
Kmods SIG |
50e2b3 |
|
|
Kmods SIG |
50e2b3 |
/* file.c */
|
|
Kmods SIG |
50e2b3 |
extern const struct file_operations exfat_file_operations;
|
|
Kmods SIG |
50e2b3 |
@@ -421,6 +422,9 @@
|
|
Kmods SIG |
50e2b3 |
int exfat_getattr(const struct path *path, struct kstat *stat,
|
|
Kmods SIG |
50e2b3 |
unsigned int request_mask, unsigned int query_flags);
|
|
Kmods SIG |
50e2b3 |
int exfat_file_fsync(struct file *file, loff_t start, loff_t end, int datasync);
|
|
Kmods SIG |
50e2b3 |
+long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
|
|
Kmods SIG |
50e2b3 |
+long exfat_compat_ioctl(struct file *filp, unsigned int cmd,
|
|
Kmods SIG |
50e2b3 |
+ unsigned long arg);
|
|
Kmods SIG |
50e2b3 |
|
|
Kmods SIG |
50e2b3 |
/* namei.c */
|
|
Kmods SIG |
50e2b3 |
extern const struct dentry_operations exfat_dentry_ops;
|
|
Kmods SIG |
50e2b3 |
diff --git a/src/file.c b/src/file.c
|
|
Kmods SIG |
50e2b3 |
index f783cf38dd8e885aa1cf33c91ffeaa3b87a120eb..6af0191b648f1601890f32e3df0f5db81cce8ea4 100644
|
|
Kmods SIG |
50e2b3 |
--- a/src/file.c
|
|
Kmods SIG |
50e2b3 |
+++ b/src/file.c
|
|
Kmods SIG |
50e2b3 |
@@ -4,6 +4,7 @@
|
|
Kmods SIG |
50e2b3 |
*/
|
|
Kmods SIG |
50e2b3 |
|
|
Kmods SIG |
50e2b3 |
#include <linux/slab.h>
|
|
Kmods SIG |
50e2b3 |
+#include <linux/compat.h>
|
|
Kmods SIG |
50e2b3 |
#include <linux/cred.h>
|
|
Kmods SIG |
50e2b3 |
#include <linux/buffer_head.h>
|
|
Kmods SIG |
50e2b3 |
#include <linux/blkdev.h>
|
|
Kmods SIG |
50e2b3 |
@@ -350,6 +351,54 @@ int exfat_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
|
|
Kmods SIG |
50e2b3 |
return error;
|
|
Kmods SIG |
50e2b3 |
}
|
|
Kmods SIG |
50e2b3 |
|
|
Kmods SIG |
50e2b3 |
+static int exfat_ioctl_fitrim(struct inode *inode, unsigned long arg)
|
|
Kmods SIG |
50e2b3 |
+{
|
|
Kmods SIG |
50e2b3 |
+ struct request_queue *q = bdev_get_queue(inode->i_sb->s_bdev);
|
|
Kmods SIG |
50e2b3 |
+ struct fstrim_range range;
|
|
Kmods SIG |
50e2b3 |
+ int ret = 0;
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+ if (!capable(CAP_SYS_ADMIN))
|
|
Kmods SIG |
50e2b3 |
+ return -EPERM;
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+ if (!blk_queue_discard(q))
|
|
Kmods SIG |
50e2b3 |
+ return -EOPNOTSUPP;
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+ if (copy_from_user(&range, (struct fstrim_range __user *)arg, sizeof(range)))
|
|
Kmods SIG |
50e2b3 |
+ return -EFAULT;
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+ range.minlen = max_t(unsigned int, range.minlen,
|
|
Kmods SIG |
50e2b3 |
+ q->limits.discard_granularity);
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+ ret = exfat_trim_fs(inode, &range);
|
|
Kmods SIG |
50e2b3 |
+ if (ret < 0)
|
|
Kmods SIG |
50e2b3 |
+ return ret;
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+ if (copy_to_user((struct fstrim_range __user *)arg, &range, sizeof(range)))
|
|
Kmods SIG |
50e2b3 |
+ return -EFAULT;
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+ return 0;
|
|
Kmods SIG |
50e2b3 |
+}
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|
Kmods SIG |
50e2b3 |
+{
|
|
Kmods SIG |
50e2b3 |
+ struct inode *inode = file_inode(filp);
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+ switch (cmd) {
|
|
Kmods SIG |
50e2b3 |
+ case FITRIM:
|
|
Kmods SIG |
50e2b3 |
+ return exfat_ioctl_fitrim(inode, arg);
|
|
Kmods SIG |
50e2b3 |
+ default:
|
|
Kmods SIG |
50e2b3 |
+ return -ENOTTY;
|
|
Kmods SIG |
50e2b3 |
+ }
|
|
Kmods SIG |
50e2b3 |
+}
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
+#ifdef CONFIG_COMPAT
|
|
Kmods SIG |
50e2b3 |
+long exfat_compat_ioctl(struct file *filp, unsigned int cmd,
|
|
Kmods SIG |
50e2b3 |
+ unsigned long arg)
|
|
Kmods SIG |
50e2b3 |
+{
|
|
Kmods SIG |
50e2b3 |
+ return exfat_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
|
|
Kmods SIG |
50e2b3 |
+}
|
|
Kmods SIG |
50e2b3 |
+#endif
|
|
Kmods SIG |
50e2b3 |
+
|
|
Kmods SIG |
50e2b3 |
int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
|
|
Kmods SIG |
50e2b3 |
{
|
|
Kmods SIG |
50e2b3 |
struct inode *inode = filp->f_mapping->host;
|
|
Kmods SIG |
50e2b3 |
@@ -370,6 +419,10 @@ const struct file_operations exfat_file_operations = {
|
|
Kmods SIG |
50e2b3 |
.llseek = generic_file_llseek,
|
|
Kmods SIG |
50e2b3 |
.read_iter = generic_file_read_iter,
|
|
Kmods SIG |
50e2b3 |
.write_iter = generic_file_write_iter,
|
|
Kmods SIG |
50e2b3 |
+ .unlocked_ioctl = exfat_ioctl,
|
|
Kmods SIG |
50e2b3 |
+#ifdef CONFIG_COMPAT
|
|
Kmods SIG |
50e2b3 |
+ .compat_ioctl = exfat_compat_ioctl,
|
|
Kmods SIG |
50e2b3 |
+#endif
|
|
Kmods SIG |
50e2b3 |
.mmap = generic_file_mmap,
|
|
Kmods SIG |
50e2b3 |
.fsync = exfat_file_fsync,
|
|
Kmods SIG |
50e2b3 |
.splice_read = generic_file_splice_read,
|
|
Kmods SIG |
50e2b3 |
--
|
|
Kmods SIG |
50e2b3 |
2.31.1
|
|
Kmods SIG |
50e2b3 |
|