fallocate should be a file operation
Currently all filesystems except XFS implement fallocate asynchronously, while XFS forced a commit. Both of these are suboptimal - in case of O_SYNC I/O we really want our allocation on disk, especially for the !KEEP_SIZE case where we actually grow the file with user-visible zeroes. On the other hand always commiting the transaction is a bad idea for fast-path uses of fallocate like for example in recent Samba versions. Given that block allocation is a data plane operation anyway change it from an inode operation to a file operation so that we have the file structure available that lets us check for O_SYNC. This also includes moving the code around for a few of the filesystems, and remove the already unnedded S_ISDIR checks given that we only wire up fallocate for regular files. Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:

committed by
Al Viro

parent
64c23e8687
commit
2fe17c1075
@@ -37,6 +37,7 @@
|
||||
#include "xfs_trace.h"
|
||||
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/falloc.h>
|
||||
|
||||
static const struct vm_operations_struct xfs_file_vm_ops;
|
||||
|
||||
@@ -882,6 +883,60 @@ out_unlock:
|
||||
return ret;
|
||||
}
|
||||
|
||||
STATIC long
|
||||
xfs_file_fallocate(
|
||||
struct file *file,
|
||||
int mode,
|
||||
loff_t offset,
|
||||
loff_t len)
|
||||
{
|
||||
struct inode *inode = file->f_path.dentry->d_inode;
|
||||
long error;
|
||||
loff_t new_size = 0;
|
||||
xfs_flock64_t bf;
|
||||
xfs_inode_t *ip = XFS_I(inode);
|
||||
int cmd = XFS_IOC_RESVSP;
|
||||
|
||||
if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
bf.l_whence = 0;
|
||||
bf.l_start = offset;
|
||||
bf.l_len = len;
|
||||
|
||||
xfs_ilock(ip, XFS_IOLOCK_EXCL);
|
||||
|
||||
if (mode & FALLOC_FL_PUNCH_HOLE)
|
||||
cmd = XFS_IOC_UNRESVSP;
|
||||
|
||||
/* check the new inode size is valid before allocating */
|
||||
if (!(mode & FALLOC_FL_KEEP_SIZE) &&
|
||||
offset + len > i_size_read(inode)) {
|
||||
new_size = offset + len;
|
||||
error = inode_newsize_ok(inode, new_size);
|
||||
if (error)
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
error = -xfs_change_file_space(ip, cmd, &bf, 0, XFS_ATTR_NOLOCK);
|
||||
if (error)
|
||||
goto out_unlock;
|
||||
|
||||
/* Change file size if needed */
|
||||
if (new_size) {
|
||||
struct iattr iattr;
|
||||
|
||||
iattr.ia_valid = ATTR_SIZE;
|
||||
iattr.ia_size = new_size;
|
||||
error = -xfs_setattr(ip, &iattr, XFS_ATTR_NOLOCK);
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
xfs_iunlock(ip, XFS_IOLOCK_EXCL);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
STATIC int
|
||||
xfs_file_open(
|
||||
struct inode *inode,
|
||||
@@ -1000,6 +1055,7 @@ const struct file_operations xfs_file_operations = {
|
||||
.open = xfs_file_open,
|
||||
.release = xfs_file_release,
|
||||
.fsync = xfs_file_fsync,
|
||||
.fallocate = xfs_file_fallocate,
|
||||
};
|
||||
|
||||
const struct file_operations xfs_dir_file_operations = {
|
||||
|
Reference in New Issue
Block a user