summaryrefslogtreecommitdiffstats
path: root/module/zfs/zpl_file.c
diff options
context:
space:
mode:
authorTim Chase <[email protected]>2014-08-20 17:35:13 -0500
committerBrian Behlendorf <[email protected]>2014-09-08 13:52:25 -0700
commit223df0161fad50f53a8fa5ffeea8cc4f8137d522 (patch)
tree4cb1ed2d880ab2c5c745e407267f2d6433213106 /module/zfs/zpl_file.c
parent4f68d7878fbed9e225022a1d435cfb7177234110 (diff)
Implement fallocate FALLOC_FL_PUNCH_HOLE
Add support for the FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE mode of fallocate(2). Mimic the behavior of other native file systems such as ext4 in cases where the file might be extended. If the offset is beyond the end of the file, return success without changing the file. If the extent of the punched hole would extend the file, only the existing tail of the file is punched. Add the zfs_zero_partial_page() function, modeled after update_page(), to handle zeroing partial pages in a hole-punching operation. It must be used under a range lock for the requested region in order that the ARC and page cache stay in sync. Move the existing page cache truncation via truncate_setsize() into zfs_freesp() for better source structure compatibility with upstream code. Add page cache truncation to zfs_freesp() and zfs_free_range() to handle hole punching. Signed-off-by: Brian Behlendorf <[email protected]> Signed-off-by: Tim Chase <[email protected]> Closes #2619
Diffstat (limited to 'module/zfs/zpl_file.c')
-rw-r--r--module/zfs/zpl_file.c43
1 files changed, 29 insertions, 14 deletions
diff --git a/module/zfs/zpl_file.c b/module/zfs/zpl_file.c
index 5ea892320..c72d5c947 100644
--- a/module/zfs/zpl_file.c
+++ b/module/zfs/zpl_file.c
@@ -558,38 +558,53 @@ zpl_writepage(struct page *pp, struct writeback_control *wbc)
/*
* The only flag combination which matches the behavior of zfs_space()
- * is FALLOC_FL_PUNCH_HOLE. This flag was introduced in the 2.6.38 kernel.
+ * is FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE. The FALLOC_FL_PUNCH_HOLE
+ * flag was introduced in the 2.6.38 kernel.
*/
+#if defined(HAVE_FILE_FALLOCATE) || defined(HAVE_INODE_FALLOCATE)
long
zpl_fallocate_common(struct inode *ip, int mode, loff_t offset, loff_t len)
{
- cred_t *cr = CRED();
int error = -EOPNOTSUPP;
- if (mode & FALLOC_FL_KEEP_SIZE)
- return (-EOPNOTSUPP);
+#if defined(FALLOC_FL_PUNCH_HOLE) && defined(FALLOC_FL_KEEP_SIZE)
+ cred_t *cr = CRED();
+ flock64_t bf;
+ loff_t olen;
+
+ if (mode != (FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
+ return (error);
crhold(cr);
-#ifdef FALLOC_FL_PUNCH_HOLE
- if (mode & FALLOC_FL_PUNCH_HOLE) {
- flock64_t bf;
+ if (offset < 0 || len <= 0)
+ return (-EINVAL);
- bf.l_type = F_WRLCK;
- bf.l_whence = 0;
- bf.l_start = offset;
- bf.l_len = len;
- bf.l_pid = 0;
+ spl_inode_lock(ip);
+ olen = i_size_read(ip);
- error = -zfs_space(ip, F_FREESP, &bf, FWRITE, offset, cr);
+ if (offset > olen) {
+ spl_inode_unlock(ip);
+ return (0);
}
-#endif /* FALLOC_FL_PUNCH_HOLE */
+ if (offset + len > olen)
+ len = olen - offset;
+ bf.l_type = F_WRLCK;
+ bf.l_whence = 0;
+ bf.l_start = offset;
+ bf.l_len = len;
+ bf.l_pid = 0;
+
+ error = -zfs_space(ip, F_FREESP, &bf, FWRITE, offset, cr);
+ spl_inode_unlock(ip);
crfree(cr);
+#endif /* defined(FALLOC_FL_PUNCH_HOLE) && defined(FALLOC_FL_KEEP_SIZE) */
ASSERT3S(error, <=, 0);
return (error);
}
+#endif /* defined(HAVE_FILE_FALLOCATE) || defined(HAVE_INODE_FALLOCATE) */
#ifdef HAVE_FILE_FALLOCATE
static long