Blame SOURCES/0072-exfat-add-support-ioctl-and-FITRIM-function.patch

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