[PATCH RFC] fs: add FIEMAP_FLAG_DISCARD support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add the ability for a user who has write access to a file to issue a
discard request for blocks belonging to a file.  This can be done via
a new flag to the FIEMAP ioctl, or via the BLKDISCARD ioctl (which
previously only worked on block devices).

Signed-off-by: "Theodore Ts'o" <tytso@xxxxxxx>
---
 fs/ext4/extents.c           |  3 +-
 fs/ioctl.c                  | 99 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/fs.h          |  3 ++
 include/uapi/linux/fiemap.h |  4 +-
 4 files changed, 107 insertions(+), 2 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index c639f51..1e0744f 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -4797,7 +4797,8 @@ static int ext4_find_delayed_extent(struct inode *inode,
 	return next_del;
 }
 /* fiemap flags we can handle specified here */
-#define EXT4_FIEMAP_FLAGS	(FIEMAP_FLAG_SYNC|FIEMAP_FLAG_XATTR)
+#define EXT4_FIEMAP_FLAGS	(FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR |\
+				 FIEMAP_FLAG_CACHE | FIEMAP_FLAG_DISCARD)
 
 static int ext4_xattr_fiemap(struct inode *inode,
 				struct fiemap_extent_info *fieinfo)
diff --git a/fs/ioctl.c b/fs/ioctl.c
index fd507fb..d7698d1 100644
--- a/fs/ioctl.c
+++ b/fs/ioctl.c
@@ -15,6 +15,7 @@
 #include <linux/writeback.h>
 #include <linux/buffer_head.h>
 #include <linux/falloc.h>
+#include <linux/blkdev.h>
 
 #include <asm/ioctls.h>
 
@@ -64,6 +65,57 @@ static int ioctl_fibmap(struct file *filp, int __user *p)
 	return put_user(res, p);
 }
 
+/*
+ * fiemap_discuard - handle the FIEMAP_FLAG_DISCARD flagg
+ */
+static int fiemap_discard(struct fiemap_extent_info *fieinfo, u64 logical,
+			  u64 phys, u64 len, u32 flags)
+{
+	struct super_block *sb = fieinfo->fi_sb;
+	int	align;
+
+	/* pr_notice("fiemap_discard: %llu %llu %llu\n", logical, phys, len); */
+	if (flags & (FIEMAP_EXTENT_UNKNOWN |
+		     FIEMAP_EXTENT_ENCODED |
+		     FIEMAP_EXTENT_DATA_ENCRYPTED |
+		     FIEMAP_EXTENT_DELALLOC |
+		     FIEMAP_EXTENT_DATA_TAIL |
+		     FIEMAP_EXTENT_DATA_INLINE |
+		     FIEMAP_EXTENT_NOT_ALIGNED |
+		     FIEMAP_EXTENT_SHARED))
+		return 0;
+
+	if (logical < fieinfo->fi_logical_start) {
+		u64 d = fieinfo->fi_logical_start - logical;
+		if (d > len)
+			return 0;
+		logical += d;
+		phys += d;
+		len -= d;
+	}
+
+	if (logical + len > fieinfo->fi_logical_end)
+		len -= logical + len - fieinfo->fi_logical_end;
+
+	align = logical & (sb->s_blocksize - 1);
+	if ((phys < align) || (len < align))
+		return 0;
+	logical -= align;
+	phys -= align;
+	len -= align;
+	len &= ~(sb->s_blocksize - 1);
+	if (len == 0)
+		return 0;
+
+	/* pr_notice("fiemap_discard adjusted: %llu %llu %llu\n", logical, phys, len); */
+	/* pr_notice("Issuing discard: %llu %llu\n", phys >> sb->s_blocksize_bits,
+		     len >> sb->s_blocksize_bits); */
+
+	return sb_issue_discard(sb, phys >> sb->s_blocksize_bits,
+				len >> sb->s_blocksize_bits,
+				GFP_KERNEL, 0);
+}
+
 /**
  * fiemap_fill_next_extent - Fiemap helper function
  * @fieinfo:	Fiemap context passed into ->fiemap
@@ -88,6 +140,12 @@ int fiemap_fill_next_extent(struct fiemap_extent_info *fieinfo, u64 logical,
 	struct fiemap_extent extent;
 	struct fiemap_extent __user *dest = fieinfo->fi_extents_start;
 
+	if (fieinfo->fi_flags & FIEMAP_FLAG_DISCARD) {
+		int r = fiemap_discard(fieinfo, logical, phys, len, flags);
+		if (r)
+			return r;
+	}
+
 	/* only count the extents */
 	if (fieinfo->fi_extents_max == 0) {
 		fieinfo->fi_extents_mapped++;
@@ -197,6 +255,13 @@ static int ioctl_fiemap(struct file *filp, unsigned long arg)
 	fieinfo.fi_flags = fiemap.fm_flags;
 	fieinfo.fi_extents_max = fiemap.fm_extent_count;
 	fieinfo.fi_extents_start = ufiemap->fm_extents;
+	fieinfo.fi_sb = sb;
+	fieinfo.fi_logical_start = fiemap.fm_start;
+	fieinfo.fi_logical_end = fiemap.fm_start + len;
+
+	if ((fiemap.fm_flags & FIEMAP_FLAG_DISCARD) &&
+	    !(filp->f_mode & FMODE_WRITE))
+		return -EBADF;
 
 	if (fiemap.fm_extent_count != 0 &&
 	    !access_ok(VERIFY_WRITE, fieinfo.fi_extents_start,
@@ -588,6 +653,40 @@ int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd,
 	case FS_IOC_FIEMAP:
 		return ioctl_fiemap(filp, arg);
 
+	case BLKDISCARD: {
+		struct fiemap_extent_info fieinfo = { 0, };
+		struct inode *inode = file_inode(filp);
+		struct super_block *sb = inode->i_sb;
+		uint64_t range[2];
+		u64 len;
+		int error;
+
+		if (!inode->i_op->fiemap)
+			return -EOPNOTSUPP;
+
+		if (!(filp->f_mode & FMODE_WRITE))
+			return -EBADF;
+
+		if (copy_from_user(range, (void __user *)arg, sizeof(range)))
+			return -EFAULT;
+
+		error = fiemap_check_ranges(sb, range[0], range[1], &len);
+		if (error) {
+			if (error == -EBADF)
+				error = -ENOTTY;
+			return error;
+		}
+
+		fieinfo.fi_flags = FIEMAP_FLAG_DISCARD;
+		fieinfo.fi_extents_max = 0;
+		fieinfo.fi_extents_start = NULL;
+		fieinfo.fi_sb = inode->i_sb;
+		fieinfo.fi_logical_start = range[0];
+		fieinfo.fi_logical_end = range[0] + len;
+
+		error = inode->i_op->fiemap(inode, &fieinfo, range[0], len);
+	}
+
 	case FIGETBSZ:
 		return put_user(inode->i_sb->s_blocksize, argp);
 
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 3f40547..fec07ee 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1480,6 +1480,9 @@ struct fiemap_extent_info {
 	unsigned int fi_flags;		/* Flags as passed from user */
 	unsigned int fi_extents_mapped;	/* Number of mapped extents */
 	unsigned int fi_extents_max;	/* Size of fiemap_extent array */
+	struct super_block *fi_sb;
+	u64 fi_logical_start;
+	u64 fi_logical_end;
 	struct fiemap_extent __user *fi_extents_start; /* Start of
 							fiemap_extent array */
 };
diff --git a/include/uapi/linux/fiemap.h b/include/uapi/linux/fiemap.h
index 0c51d61..849d57f 100644
--- a/include/uapi/linux/fiemap.h
+++ b/include/uapi/linux/fiemap.h
@@ -41,8 +41,10 @@ struct fiemap {
 #define FIEMAP_FLAG_SYNC	0x00000001 /* sync file data before map */
 #define FIEMAP_FLAG_XATTR	0x00000002 /* map extended attribute tree */
 #define FIEMAP_FLAG_CACHE	0x00000004 /* request caching of the extents */
+#define FIEMAP_FLAG_DISCARD	0x00000008 /* issue discard */
 
-#define FIEMAP_FLAGS_COMPAT	(FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR)
+#define FIEMAP_FLAGS_COMPAT	(FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR |\
+				 FIEMAP_FLAG_CACHE | FIEMAP_FLAG_DISCARD)
 
 #define FIEMAP_EXTENT_LAST		0x00000001 /* Last extent in file. */
 #define FIEMAP_EXTENT_UNKNOWN		0x00000002 /* Data location unknown. */
-- 
1.7.12.rc0.22.gcdd159b

--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Reiser Filesystem Development]     [Ceph FS]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux FS]     [Yosemite National Park]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Device Mapper]     [Linux Media]

  Powered by Linux