NFS: Add support for fallocate(FALLOC_FL_ZERO_RANGE)
This implements a suggestion from Trond that we can mimic FALLOC_FL_ZERO_RANGE by sending a compound that first does a DEALLOCATE to punch a hole in a file, and then an ALLOCATE to fill the hole with zeroes. There might technically be a race here, but once the DEALLOCATE finishes any reads from the region would return zeroes anyway, so I don't expect it to cause problems. Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>pull/1285/head
parent
4c10fa44bc
commit
d2e1d783f2
|
|
@ -21,6 +21,7 @@ int nfs42_proc_allocate(struct file *, loff_t, loff_t);
|
||||||
ssize_t nfs42_proc_copy(struct file *, loff_t, struct file *, loff_t, size_t,
|
ssize_t nfs42_proc_copy(struct file *, loff_t, struct file *, loff_t, size_t,
|
||||||
struct nl4_server *, nfs4_stateid *, bool);
|
struct nl4_server *, nfs4_stateid *, bool);
|
||||||
int nfs42_proc_deallocate(struct file *, loff_t, loff_t);
|
int nfs42_proc_deallocate(struct file *, loff_t, loff_t);
|
||||||
|
int nfs42_proc_zero_range(struct file *, loff_t, loff_t);
|
||||||
loff_t nfs42_proc_llseek(struct file *, loff_t, int);
|
loff_t nfs42_proc_llseek(struct file *, loff_t, int);
|
||||||
int nfs42_proc_layoutstats_generic(struct nfs_server *,
|
int nfs42_proc_layoutstats_generic(struct nfs_server *,
|
||||||
struct nfs42_layoutstat_data *);
|
struct nfs42_layoutstat_data *);
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,8 @@ int nfs42_proc_allocate(struct file *filep, loff_t offset, loff_t len)
|
||||||
|
|
||||||
err = nfs42_proc_fallocate(&msg, filep, offset, len);
|
err = nfs42_proc_fallocate(&msg, filep, offset, len);
|
||||||
if (err == -EOPNOTSUPP)
|
if (err == -EOPNOTSUPP)
|
||||||
NFS_SERVER(inode)->caps &= ~NFS_CAP_ALLOCATE;
|
NFS_SERVER(inode)->caps &= ~(NFS_CAP_ALLOCATE |
|
||||||
|
NFS_CAP_ZERO_RANGE);
|
||||||
|
|
||||||
inode_unlock(inode);
|
inode_unlock(inode);
|
||||||
return err;
|
return err;
|
||||||
|
|
@ -169,7 +170,31 @@ int nfs42_proc_deallocate(struct file *filep, loff_t offset, loff_t len)
|
||||||
if (err == 0)
|
if (err == 0)
|
||||||
truncate_pagecache_range(inode, offset, (offset + len) -1);
|
truncate_pagecache_range(inode, offset, (offset + len) -1);
|
||||||
if (err == -EOPNOTSUPP)
|
if (err == -EOPNOTSUPP)
|
||||||
NFS_SERVER(inode)->caps &= ~NFS_CAP_DEALLOCATE;
|
NFS_SERVER(inode)->caps &= ~(NFS_CAP_DEALLOCATE |
|
||||||
|
NFS_CAP_ZERO_RANGE);
|
||||||
|
|
||||||
|
inode_unlock(inode);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nfs42_proc_zero_range(struct file *filep, loff_t offset, loff_t len)
|
||||||
|
{
|
||||||
|
struct rpc_message msg = {
|
||||||
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ZERO_RANGE],
|
||||||
|
};
|
||||||
|
struct inode *inode = file_inode(filep);
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!nfs_server_capable(inode, NFS_CAP_ZERO_RANGE))
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
inode_lock(inode);
|
||||||
|
|
||||||
|
err = nfs42_proc_fallocate(&msg, filep, offset, len);
|
||||||
|
if (err == 0)
|
||||||
|
truncate_pagecache_range(inode, offset, (offset + len) -1);
|
||||||
|
if (err == -EOPNOTSUPP)
|
||||||
|
NFS_SERVER(inode)->caps &= ~NFS_CAP_ZERO_RANGE;
|
||||||
|
|
||||||
inode_unlock(inode);
|
inode_unlock(inode);
|
||||||
return err;
|
return err;
|
||||||
|
|
|
||||||
|
|
@ -174,6 +174,18 @@
|
||||||
decode_putfh_maxsz + \
|
decode_putfh_maxsz + \
|
||||||
decode_deallocate_maxsz + \
|
decode_deallocate_maxsz + \
|
||||||
decode_getattr_maxsz)
|
decode_getattr_maxsz)
|
||||||
|
#define NFS4_enc_zero_range_sz (compound_encode_hdr_maxsz + \
|
||||||
|
encode_sequence_maxsz + \
|
||||||
|
encode_putfh_maxsz + \
|
||||||
|
encode_deallocate_maxsz + \
|
||||||
|
encode_allocate_maxsz + \
|
||||||
|
encode_getattr_maxsz)
|
||||||
|
#define NFS4_dec_zero_range_sz (compound_decode_hdr_maxsz + \
|
||||||
|
decode_sequence_maxsz + \
|
||||||
|
decode_putfh_maxsz + \
|
||||||
|
decode_deallocate_maxsz + \
|
||||||
|
decode_allocate_maxsz + \
|
||||||
|
decode_getattr_maxsz)
|
||||||
#define NFS4_enc_read_plus_sz (compound_encode_hdr_maxsz + \
|
#define NFS4_enc_read_plus_sz (compound_encode_hdr_maxsz + \
|
||||||
encode_sequence_maxsz + \
|
encode_sequence_maxsz + \
|
||||||
encode_putfh_maxsz + \
|
encode_putfh_maxsz + \
|
||||||
|
|
@ -648,6 +660,27 @@ static void nfs4_xdr_enc_deallocate(struct rpc_rqst *req,
|
||||||
encode_nops(&hdr);
|
encode_nops(&hdr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Encode ZERO_RANGE request
|
||||||
|
*/
|
||||||
|
static void nfs4_xdr_enc_zero_range(struct rpc_rqst *req,
|
||||||
|
struct xdr_stream *xdr,
|
||||||
|
const void *data)
|
||||||
|
{
|
||||||
|
const struct nfs42_falloc_args *args = data;
|
||||||
|
struct compound_hdr hdr = {
|
||||||
|
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
|
||||||
|
};
|
||||||
|
|
||||||
|
encode_compound_hdr(xdr, req, &hdr);
|
||||||
|
encode_sequence(xdr, &args->seq_args, &hdr);
|
||||||
|
encode_putfh(xdr, args->falloc_fh, &hdr);
|
||||||
|
encode_deallocate(xdr, args, &hdr);
|
||||||
|
encode_allocate(xdr, args, &hdr);
|
||||||
|
encode_getfattr(xdr, args->falloc_bitmask, &hdr);
|
||||||
|
encode_nops(&hdr);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Encode READ_PLUS request
|
* Encode READ_PLUS request
|
||||||
*/
|
*/
|
||||||
|
|
@ -1510,6 +1543,37 @@ out:
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Decode ZERO_RANGE request
|
||||||
|
*/
|
||||||
|
static int nfs4_xdr_dec_zero_range(struct rpc_rqst *rqstp,
|
||||||
|
struct xdr_stream *xdr,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct nfs42_falloc_res *res = data;
|
||||||
|
struct compound_hdr hdr;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
status = decode_compound_hdr(xdr, &hdr);
|
||||||
|
if (status)
|
||||||
|
goto out;
|
||||||
|
status = decode_sequence(xdr, &res->seq_res, rqstp);
|
||||||
|
if (status)
|
||||||
|
goto out;
|
||||||
|
status = decode_putfh(xdr);
|
||||||
|
if (status)
|
||||||
|
goto out;
|
||||||
|
status = decode_deallocate(xdr, res);
|
||||||
|
if (status)
|
||||||
|
goto out;
|
||||||
|
status = decode_allocate(xdr, res);
|
||||||
|
if (status)
|
||||||
|
goto out;
|
||||||
|
decode_getfattr(xdr, res->falloc_fattr, res->falloc_server);
|
||||||
|
out:
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Decode READ_PLUS request
|
* Decode READ_PLUS request
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -225,8 +225,14 @@ static long nfs42_fallocate(struct file *filep, int mode, loff_t offset, loff_t
|
||||||
if (!S_ISREG(inode->i_mode))
|
if (!S_ISREG(inode->i_mode))
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
if ((mode != 0) && (mode != (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE)))
|
switch (mode) {
|
||||||
|
case 0:
|
||||||
|
case FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE:
|
||||||
|
case FALLOC_FL_ZERO_RANGE:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
ret = inode_newsize_ok(inode, offset + len);
|
ret = inode_newsize_ok(inode, offset + len);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
|
@ -234,6 +240,8 @@ static long nfs42_fallocate(struct file *filep, int mode, loff_t offset, loff_t
|
||||||
|
|
||||||
if (mode & FALLOC_FL_PUNCH_HOLE)
|
if (mode & FALLOC_FL_PUNCH_HOLE)
|
||||||
return nfs42_proc_deallocate(filep, offset, len);
|
return nfs42_proc_deallocate(filep, offset, len);
|
||||||
|
else if (mode & FALLOC_FL_ZERO_RANGE)
|
||||||
|
return nfs42_proc_zero_range(filep, offset ,len);
|
||||||
return nfs42_proc_allocate(filep, offset, len);
|
return nfs42_proc_allocate(filep, offset, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10822,6 +10822,7 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = {
|
||||||
| NFS_CAP_OFFLOAD_CANCEL
|
| NFS_CAP_OFFLOAD_CANCEL
|
||||||
| NFS_CAP_COPY_NOTIFY
|
| NFS_CAP_COPY_NOTIFY
|
||||||
| NFS_CAP_DEALLOCATE
|
| NFS_CAP_DEALLOCATE
|
||||||
|
| NFS_CAP_ZERO_RANGE
|
||||||
| NFS_CAP_SEEK
|
| NFS_CAP_SEEK
|
||||||
| NFS_CAP_LAYOUTSTATS
|
| NFS_CAP_LAYOUTSTATS
|
||||||
| NFS_CAP_CLONE
|
| NFS_CAP_CLONE
|
||||||
|
|
|
||||||
|
|
@ -7711,6 +7711,7 @@ const struct rpc_procinfo nfs4_procedures[] = {
|
||||||
PROC42(LISTXATTRS, enc_listxattrs, dec_listxattrs),
|
PROC42(LISTXATTRS, enc_listxattrs, dec_listxattrs),
|
||||||
PROC42(REMOVEXATTR, enc_removexattr, dec_removexattr),
|
PROC42(REMOVEXATTR, enc_removexattr, dec_removexattr),
|
||||||
PROC42(READ_PLUS, enc_read_plus, dec_read_plus),
|
PROC42(READ_PLUS, enc_read_plus, dec_read_plus),
|
||||||
|
PROC42(ZERO_RANGE, enc_zero_range, dec_zero_range),
|
||||||
};
|
};
|
||||||
|
|
||||||
static unsigned int nfs_version4_counts[ARRAY_SIZE(nfs4_procedures)];
|
static unsigned int nfs_version4_counts[ARRAY_SIZE(nfs4_procedures)];
|
||||||
|
|
|
||||||
|
|
@ -678,6 +678,7 @@ enum {
|
||||||
NFSPROC4_CLNT_SEEK,
|
NFSPROC4_CLNT_SEEK,
|
||||||
NFSPROC4_CLNT_ALLOCATE,
|
NFSPROC4_CLNT_ALLOCATE,
|
||||||
NFSPROC4_CLNT_DEALLOCATE,
|
NFSPROC4_CLNT_DEALLOCATE,
|
||||||
|
NFSPROC4_CLNT_ZERO_RANGE,
|
||||||
NFSPROC4_CLNT_LAYOUTSTATS,
|
NFSPROC4_CLNT_LAYOUTSTATS,
|
||||||
NFSPROC4_CLNT_CLONE,
|
NFSPROC4_CLNT_CLONE,
|
||||||
NFSPROC4_CLNT_COPY,
|
NFSPROC4_CLNT_COPY,
|
||||||
|
|
|
||||||
|
|
@ -304,6 +304,7 @@ struct nfs_server {
|
||||||
#define NFS_CAP_CASE_PRESERVING (1U << 7)
|
#define NFS_CAP_CASE_PRESERVING (1U << 7)
|
||||||
#define NFS_CAP_REBOOT_LAYOUTRETURN (1U << 8)
|
#define NFS_CAP_REBOOT_LAYOUTRETURN (1U << 8)
|
||||||
#define NFS_CAP_OFFLOAD_STATUS (1U << 9)
|
#define NFS_CAP_OFFLOAD_STATUS (1U << 9)
|
||||||
|
#define NFS_CAP_ZERO_RANGE (1U << 10)
|
||||||
#define NFS_CAP_OPEN_XOR (1U << 12)
|
#define NFS_CAP_OPEN_XOR (1U << 12)
|
||||||
#define NFS_CAP_DELEGTIME (1U << 13)
|
#define NFS_CAP_DELEGTIME (1U << 13)
|
||||||
#define NFS_CAP_POSIX_LOCK (1U << 14)
|
#define NFS_CAP_POSIX_LOCK (1U << 14)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue