diff options
author | Etienne Dechamps <[email protected]> | 2012-09-03 14:56:26 +0200 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2012-10-04 16:22:07 -0700 |
commit | bbdc6ae49518a4be7230ab673370e9231e2f72e7 (patch) | |
tree | f1e0522a75b9272b1da8a6f5382e0a3b9fbdffe4 /module | |
parent | a6c6839a88b7dc344f3cd8e875a01802b34645cd (diff) |
Add interface for file hole punching.
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 <[email protected]>
Closes #168
Diffstat (limited to 'module')
-rw-r--r-- | module/spl/spl-vnode.c | 53 |
1 files changed, 53 insertions, 0 deletions
diff --git a/module/spl/spl-vnode.c b/module/spl/spl-vnode.c index 2e55b007b..f5fc65d26 100644 --- a/module/spl/spl-vnode.c +++ b/module/spl/spl-vnode.c @@ -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) |