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