Skip to content

Commit d2e1d78

Browse files
author
Anna Schumaker
committed
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 <[email protected]>
1 parent 4c10fa4 commit d2e1d78

File tree

8 files changed

+105
-3
lines changed

8 files changed

+105
-3
lines changed

fs/nfs/nfs42.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ int nfs42_proc_allocate(struct file *, loff_t, loff_t);
2121
ssize_t nfs42_proc_copy(struct file *, loff_t, struct file *, loff_t, size_t,
2222
struct nl4_server *, nfs4_stateid *, bool);
2323
int nfs42_proc_deallocate(struct file *, loff_t, loff_t);
24+
int nfs42_proc_zero_range(struct file *, loff_t, loff_t);
2425
loff_t nfs42_proc_llseek(struct file *, loff_t, int);
2526
int nfs42_proc_layoutstats_generic(struct nfs_server *,
2627
struct nfs42_layoutstat_data *);

fs/nfs/nfs42proc.c

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,8 @@ int nfs42_proc_allocate(struct file *filep, loff_t offset, loff_t len)
146146

147147
err = nfs42_proc_fallocate(&msg, filep, offset, len);
148148
if (err == -EOPNOTSUPP)
149-
NFS_SERVER(inode)->caps &= ~NFS_CAP_ALLOCATE;
149+
NFS_SERVER(inode)->caps &= ~(NFS_CAP_ALLOCATE |
150+
NFS_CAP_ZERO_RANGE);
150151

151152
inode_unlock(inode);
152153
return err;
@@ -169,7 +170,31 @@ int nfs42_proc_deallocate(struct file *filep, loff_t offset, loff_t len)
169170
if (err == 0)
170171
truncate_pagecache_range(inode, offset, (offset + len) -1);
171172
if (err == -EOPNOTSUPP)
172-
NFS_SERVER(inode)->caps &= ~NFS_CAP_DEALLOCATE;
173+
NFS_SERVER(inode)->caps &= ~(NFS_CAP_DEALLOCATE |
174+
NFS_CAP_ZERO_RANGE);
175+
176+
inode_unlock(inode);
177+
return err;
178+
}
179+
180+
int nfs42_proc_zero_range(struct file *filep, loff_t offset, loff_t len)
181+
{
182+
struct rpc_message msg = {
183+
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ZERO_RANGE],
184+
};
185+
struct inode *inode = file_inode(filep);
186+
int err;
187+
188+
if (!nfs_server_capable(inode, NFS_CAP_ZERO_RANGE))
189+
return -EOPNOTSUPP;
190+
191+
inode_lock(inode);
192+
193+
err = nfs42_proc_fallocate(&msg, filep, offset, len);
194+
if (err == 0)
195+
truncate_pagecache_range(inode, offset, (offset + len) -1);
196+
if (err == -EOPNOTSUPP)
197+
NFS_SERVER(inode)->caps &= ~NFS_CAP_ZERO_RANGE;
173198

174199
inode_unlock(inode);
175200
return err;

fs/nfs/nfs42xdr.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,18 @@
174174
decode_putfh_maxsz + \
175175
decode_deallocate_maxsz + \
176176
decode_getattr_maxsz)
177+
#define NFS4_enc_zero_range_sz (compound_encode_hdr_maxsz + \
178+
encode_sequence_maxsz + \
179+
encode_putfh_maxsz + \
180+
encode_deallocate_maxsz + \
181+
encode_allocate_maxsz + \
182+
encode_getattr_maxsz)
183+
#define NFS4_dec_zero_range_sz (compound_decode_hdr_maxsz + \
184+
decode_sequence_maxsz + \
185+
decode_putfh_maxsz + \
186+
decode_deallocate_maxsz + \
187+
decode_allocate_maxsz + \
188+
decode_getattr_maxsz)
177189
#define NFS4_enc_read_plus_sz (compound_encode_hdr_maxsz + \
178190
encode_sequence_maxsz + \
179191
encode_putfh_maxsz + \
@@ -648,6 +660,27 @@ static void nfs4_xdr_enc_deallocate(struct rpc_rqst *req,
648660
encode_nops(&hdr);
649661
}
650662

663+
/*
664+
* Encode ZERO_RANGE request
665+
*/
666+
static void nfs4_xdr_enc_zero_range(struct rpc_rqst *req,
667+
struct xdr_stream *xdr,
668+
const void *data)
669+
{
670+
const struct nfs42_falloc_args *args = data;
671+
struct compound_hdr hdr = {
672+
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
673+
};
674+
675+
encode_compound_hdr(xdr, req, &hdr);
676+
encode_sequence(xdr, &args->seq_args, &hdr);
677+
encode_putfh(xdr, args->falloc_fh, &hdr);
678+
encode_deallocate(xdr, args, &hdr);
679+
encode_allocate(xdr, args, &hdr);
680+
encode_getfattr(xdr, args->falloc_bitmask, &hdr);
681+
encode_nops(&hdr);
682+
}
683+
651684
/*
652685
* Encode READ_PLUS request
653686
*/
@@ -1510,6 +1543,37 @@ static int nfs4_xdr_dec_deallocate(struct rpc_rqst *rqstp,
15101543
return status;
15111544
}
15121545

1546+
/*
1547+
* Decode ZERO_RANGE request
1548+
*/
1549+
static int nfs4_xdr_dec_zero_range(struct rpc_rqst *rqstp,
1550+
struct xdr_stream *xdr,
1551+
void *data)
1552+
{
1553+
struct nfs42_falloc_res *res = data;
1554+
struct compound_hdr hdr;
1555+
int status;
1556+
1557+
status = decode_compound_hdr(xdr, &hdr);
1558+
if (status)
1559+
goto out;
1560+
status = decode_sequence(xdr, &res->seq_res, rqstp);
1561+
if (status)
1562+
goto out;
1563+
status = decode_putfh(xdr);
1564+
if (status)
1565+
goto out;
1566+
status = decode_deallocate(xdr, res);
1567+
if (status)
1568+
goto out;
1569+
status = decode_allocate(xdr, res);
1570+
if (status)
1571+
goto out;
1572+
decode_getfattr(xdr, res->falloc_fattr, res->falloc_server);
1573+
out:
1574+
return status;
1575+
}
1576+
15131577
/*
15141578
* Decode READ_PLUS request
15151579
*/

fs/nfs/nfs4file.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,15 +225,23 @@ static long nfs42_fallocate(struct file *filep, int mode, loff_t offset, loff_t
225225
if (!S_ISREG(inode->i_mode))
226226
return -EOPNOTSUPP;
227227

228-
if ((mode != 0) && (mode != (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE)))
228+
switch (mode) {
229+
case 0:
230+
case FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE:
231+
case FALLOC_FL_ZERO_RANGE:
232+
break;
233+
default:
229234
return -EOPNOTSUPP;
235+
}
230236

231237
ret = inode_newsize_ok(inode, offset + len);
232238
if (ret < 0)
233239
return ret;
234240

235241
if (mode & FALLOC_FL_PUNCH_HOLE)
236242
return nfs42_proc_deallocate(filep, offset, len);
243+
else if (mode & FALLOC_FL_ZERO_RANGE)
244+
return nfs42_proc_zero_range(filep, offset ,len);
237245
return nfs42_proc_allocate(filep, offset, len);
238246
}
239247

fs/nfs/nfs4proc.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10822,6 +10822,7 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = {
1082210822
| NFS_CAP_OFFLOAD_CANCEL
1082310823
| NFS_CAP_COPY_NOTIFY
1082410824
| NFS_CAP_DEALLOCATE
10825+
| NFS_CAP_ZERO_RANGE
1082510826
| NFS_CAP_SEEK
1082610827
| NFS_CAP_LAYOUTSTATS
1082710828
| NFS_CAP_CLONE

fs/nfs/nfs4xdr.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7711,6 +7711,7 @@ const struct rpc_procinfo nfs4_procedures[] = {
77117711
PROC42(LISTXATTRS, enc_listxattrs, dec_listxattrs),
77127712
PROC42(REMOVEXATTR, enc_removexattr, dec_removexattr),
77137713
PROC42(READ_PLUS, enc_read_plus, dec_read_plus),
7714+
PROC42(ZERO_RANGE, enc_zero_range, dec_zero_range),
77147715
};
77157716

77167717
static unsigned int nfs_version4_counts[ARRAY_SIZE(nfs4_procedures)];

include/linux/nfs4.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,7 @@ enum {
678678
NFSPROC4_CLNT_SEEK,
679679
NFSPROC4_CLNT_ALLOCATE,
680680
NFSPROC4_CLNT_DEALLOCATE,
681+
NFSPROC4_CLNT_ZERO_RANGE,
681682
NFSPROC4_CLNT_LAYOUTSTATS,
682683
NFSPROC4_CLNT_CLONE,
683684
NFSPROC4_CLNT_COPY,

include/linux/nfs_fs_sb.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ struct nfs_server {
304304
#define NFS_CAP_CASE_PRESERVING (1U << 7)
305305
#define NFS_CAP_REBOOT_LAYOUTRETURN (1U << 8)
306306
#define NFS_CAP_OFFLOAD_STATUS (1U << 9)
307+
#define NFS_CAP_ZERO_RANGE (1U << 10)
307308
#define NFS_CAP_OPEN_XOR (1U << 12)
308309
#define NFS_CAP_DELEGTIME (1U << 13)
309310
#define NFS_CAP_POSIX_LOCK (1U << 14)

0 commit comments

Comments
 (0)