]> git.proxmox.com Git - mirror_spl.git/commitdiff
Add interface for file hole punching.
authorEtienne Dechamps <etienne.dechamps@ovh.net>
Mon, 3 Sep 2012 12:56:26 +0000 (14:56 +0200)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Thu, 4 Oct 2012 23:22:07 +0000 (16:22 -0700)
This adds an interface to "punch holes" (deallocate space) in VFS
files. The interface is identical to the Solaris VOP_SPACE interface.
This interface is necessary for TRIM support on file vdevs.

This is implemented using Linux fallocate(FALLOC_FL_PUNCH_HOLE), which
was introduced in 2.6.38. For a brief time before 2.6.38 this was done
using the truncate_range inode operation, which was quickly deprecated.
This patch only supports FALLOC_FL_PUNCH_HOLE.

This adds support for the truncate_range() inode operation to
VOP_SPACE() for file hole punching. This API is deprecated and removed
in 3.5, so it's only useful for old kernels.

On tmpfs, the truncate_range() inode operation translates to
shmem_truncate_range(). Unfortunately, this function expects the end
offset to be inclusive and aligned to the end of a page. If it is not,
the kernel will stop with a BUG_ON().

This patch fixes the issue by adapting to the constraints set forth by
shmem_truncate_range().

Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #168

config/spl-build.m4
include/sys/vnode.h
module/spl/spl-vnode.c

index b7ab54d32b1d9b2fb9db3d53772cd4d9b97b5139..0c7a03cf147066d9cbff979167f3861622b7b1e2 100644 (file)
@@ -72,6 +72,7 @@ AC_DEFUN([SPL_AC_CONFIG_KERNEL], [
        SPL_AC_4ARGS_VFS_RENAME
        SPL_AC_VFS_FSYNC
        SPL_AC_2ARGS_VFS_FSYNC
+       SPL_AC_INODE_TRUNCATE_RANGE
        SPL_AC_FS_STRUCT_SPINLOCK
        SPL_AC_CRED_STRUCT
        SPL_AC_GROUPS_SEARCH
@@ -1992,6 +1993,27 @@ AC_DEFUN([SPL_AC_2ARGS_VFS_FSYNC], [
        ])
 ])
 
+dnl #
+dnl # 3.5 API change,
+dnl # inode_operations.truncate_range removed
+dnl # (deprecated in favor of FALLOC_FL_PUNCH_HOLE)
+dnl #
+AC_DEFUN([SPL_AC_INODE_TRUNCATE_RANGE], [
+       AC_MSG_CHECKING([whether truncate_range() inode operation is available])
+       SPL_LINUX_TRY_COMPILE([
+               #include <linux/fs.h>
+       ],[
+               struct inode_operations ops;
+               ops.truncate_range = NULL;
+       ],[
+               AC_MSG_RESULT(yes)
+               AC_DEFINE(HAVE_INODE_TRUNCATE_RANGE, 1,
+                       [truncate_range() inode operation is available])
+       ],[
+               AC_MSG_RESULT(no)
+       ])
+]))
+
 dnl #
 dnl # 2.6.33 API change. Also backported in RHEL5 as of 2.6.18-190.el5.
 dnl # Earlier versions of rwsem_is_locked() were inline and had a race
index 03462bd7091a7a4b196bb69e31abe24778783798..99614d55b4fe20aa929125322e1b30027c044568 100644 (file)
@@ -66,6 +66,9 @@
 #define FNODSYNC       0x10000 /* fsync pseudo flag */
 #define FNOFOLLOW      0x20000 /* don't follow symlinks */
 
+#define F_FREESP       11      /* Free file space */
+
+
 /*
  * The vnode AT_ flags are mapped to the Linux ATTR_* flags.
  * This allows them to be used safely with an iattr structure.
@@ -185,6 +188,8 @@ extern int vn_remove(const char *path, uio_seg_t seg, int flags);
 extern int vn_rename(const char *path1, const char *path2, int x1);
 extern int vn_getattr(vnode_t *vp, vattr_t *vap, int flags, void *x3, void *x4);
 extern int vn_fsync(vnode_t *vp, int flags, void *x3, void *x4);
+extern int vn_space(vnode_t *vp, int cmd, struct flock *bfp, int flag,
+    offset_t offset, void *x6, void *x7);
 extern file_t *vn_getf(int fd);
 extern void vn_releasef(int fd);
 extern int vn_set_pwd(const char *filename);
@@ -197,6 +202,7 @@ void spl_vn_fini(void);
 #define VOP_SEEK                               vn_seek
 #define VOP_GETATTR                            vn_getattr
 #define VOP_FSYNC                              vn_fsync
+#define VOP_SPACE                              vn_space
 #define VOP_PUTPAGE(vp, o, s, f, x1, x2)       ((void)0)
 #define vn_is_readonly(vp)                     0
 #define getf                                   vn_getf
index 2e55b007b506b203d15b189eabe730b71d44fac2..f5fc65d264b3d76707ad610ca4a0fbd4bc518340 100644 (file)
@@ -25,6 +25,7 @@
 \*****************************************************************************/
 
 #include <sys/vnode.h>
+#include <linux/falloc.h>
 #include <spl-debug.h>
 
 #ifdef SS_DEBUG_SUBSYS
@@ -510,6 +511,58 @@ int vn_fsync(vnode_t *vp, int flags, void *x3, void *x4)
 } /* vn_fsync() */
 EXPORT_SYMBOL(vn_fsync);
 
+int vn_space(vnode_t *vp, int cmd, struct flock *bfp, int flag,
+    offset_t offset, void *x6, void *x7)
+{
+       int error = EOPNOTSUPP;
+       SENTRY;
+
+       if (cmd != F_FREESP || bfp->l_whence != 0)
+               SRETURN(EOPNOTSUPP);
+
+       ASSERT(vp);
+       ASSERT(vp->v_file);
+       ASSERT(bfp->l_start >= 0 && bfp->l_len > 0);
+
+#ifdef FALLOC_FL_PUNCH_HOLE
+       if (vp->v_file->f_op->fallocate) {
+               error = -vp->v_file->f_op->fallocate(vp->v_file,
+                   FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE,
+                   bfp->l_start, bfp->l_len);
+               if (!error)
+                       SRETURN(0);
+       }
+#endif
+
+#ifdef HAVE_INODE_TRUNCATE_RANGE
+       if (vp->v_file->f_dentry && vp->v_file->f_dentry->d_inode &&
+           vp->v_file->f_dentry->d_inode->i_op &&
+           vp->v_file->f_dentry->d_inode->i_op->truncate_range) {
+               off_t end = bfp->l_start + bfp->l_len;
+               /*
+                * Judging from the code in shmem_truncate_range(),
+                * it seems the kernel expects the end offset to be
+                * inclusive and aligned to the end of a page.
+                */
+               if (end % PAGE_SIZE != 0) {
+                       end &= ~(off_t)(PAGE_SIZE - 1);
+                       if (end <= bfp->l_start)
+                               SRETURN(0);
+               }
+               --end;
+
+               vp->v_file->f_dentry->d_inode->i_op->truncate_range(
+                       vp->v_file->f_dentry->d_inode,
+                       bfp->l_start, end
+               );
+               SRETURN(0);
+       }
+#endif
+
+       SRETURN(error);
+}
+EXPORT_SYMBOL(vn_space);
+
 /* Function must be called while holding the vn_file_lock */
 static file_t *
 file_find(int fd)