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 <behlendorf1@llnl.gov>
Closes #168
This commit is contained in:
Etienne Dechamps
2012-09-03 14:56:26 +02:00
committed by Brian Behlendorf
parent a6c6839a88
commit bbdc6ae495
3 changed files with 81 additions and 0 deletions
+53
View 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)