From patchwork Sat Nov 2 23:45:30 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Theodore Ts'o X-Patchwork-Id: 288036 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id C32962C007C for ; Sun, 3 Nov 2013 10:45:53 +1100 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752519Ab3KBXpf (ORCPT ); Sat, 2 Nov 2013 19:45:35 -0400 Received: from imap.thunk.org ([74.207.234.97]:53598 "EHLO imap.thunk.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752482Ab3KBXpb (ORCPT ); Sat, 2 Nov 2013 19:45:31 -0400 Received: from root (helo=closure.thunk.org) by imap.thunk.org with local-esmtp (Exim 4.80) (envelope-from ) id 1Vcksl-0003GZ-6L; Sat, 02 Nov 2013 23:45:31 +0000 Received: by closure.thunk.org (Postfix, from userid 15806) id 4204A580702; Sat, 2 Nov 2013 19:45:30 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=thunk.org; s=mail; t=1383435930; bh=ZGzBgL3iw+zb6LwduCf5Jac5zK1jUEBtcI4fODWCNwQ=; h=From:To:Cc:Subject:Date:From; b=BmALq/oAB9e3dpKDKk/cQEkwpdmjqrOiZffc+2/xyUbqCVkXvgnFdf14sJ+HZRVGA Oh17M6QNw5RnKbiqL2S1z+wdD7k8HpS8Xw7kGxrcxKG9j3pSlSvU3wM+0RVQVqALB5 hTaiAAHoGAOsLz67kGpniNXB//Kbcv0v56Ogk6jg= From: Theodore Ts'o To: linux-fsdevel@vger.kernel.org Cc: Ext4 Developers List , Theodore Ts'o Subject: [PATCH RFC] fs: add FIEMAP_FLAG_DISCARD support Date: Sat, 2 Nov 2013 19:45:30 -0400 Message-Id: <1383435930-29295-1-git-send-email-tytso@mit.edu> X-Mailer: git-send-email 1.7.12.rc0.22.gcdd159b X-SA-Exim-Connect-IP: X-SA-Exim-Mail-From: tytso@thunk.org X-SA-Exim-Scanned: No (on imap.thunk.org); SAEximRunCond expanded to false Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org 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" --- 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 #include #include +#include #include @@ -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. */