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

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