Skip to content

Commit 4c475ee

Browse files
jtlaytonchucklever
authored andcommitted
nfsd: don't fsync nfsd_files on last close
Most of the time, NFSv4 clients issue a COMMIT before the final CLOSE of an open stateid, so with NFSv4, the fsync in the nfsd_file_free path is usually a no-op and doesn't block. We have a customer running knfsd over very slow storage (XFS over Ceph RBD). They were using the "async" export option because performance was more important than data integrity for this application. That export option turns NFSv4 COMMIT calls into no-ops. Due to the fsync in this codepath however, their final CLOSE calls would still stall (since a CLOSE effectively became a COMMIT). I think this fsync is not strictly necessary. We only use that result to reset the write verifier. Instead of fsync'ing all of the data when we free an nfsd_file, we can just check for writeback errors when one is acquired and when it is freed. If the client never comes back, then it'll never see the error anyway and there is no point in resetting it. If an error occurs after the nfsd_file is removed from the cache but before the inode is evicted, then it will reset the write verifier on the next nfsd_file_acquire, (since there will be an unseen error). The only exception here is if something else opens and fsyncs the file during that window. Given that local applications work with this limitation today, I don't see that as an issue. Link: https://bugzilla.redhat.com/show_bug.cgi?id=2166658 Fixes: ac3a258 ("nfsd: rework refcounting in filecache") Reported-and-tested-by: Pierguido Lambri <[email protected]> Signed-off-by: Jeff Layton <[email protected]> Signed-off-by: Chuck Lever <[email protected]>
1 parent 2172e84 commit 4c475ee

File tree

2 files changed

+12
-63
lines changed

2 files changed

+12
-63
lines changed

fs/nfsd/filecache.c

Lines changed: 12 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -331,37 +331,27 @@ nfsd_file_alloc(struct nfsd_file_lookup_key *key, unsigned int may)
331331
return nf;
332332
}
333333

334+
/**
335+
* nfsd_file_check_write_error - check for writeback errors on a file
336+
* @nf: nfsd_file to check for writeback errors
337+
*
338+
* Check whether a nfsd_file has an unseen error. Reset the write
339+
* verifier if so.
340+
*/
334341
static void
335-
nfsd_file_fsync(struct nfsd_file *nf)
336-
{
337-
struct file *file = nf->nf_file;
338-
int ret;
339-
340-
if (!file || !(file->f_mode & FMODE_WRITE))
341-
return;
342-
ret = vfs_fsync(file, 1);
343-
trace_nfsd_file_fsync(nf, ret);
344-
if (ret)
345-
nfsd_reset_write_verifier(net_generic(nf->nf_net, nfsd_net_id));
346-
}
347-
348-
static int
349342
nfsd_file_check_write_error(struct nfsd_file *nf)
350343
{
351344
struct file *file = nf->nf_file;
352345

353-
if (!file || !(file->f_mode & FMODE_WRITE))
354-
return 0;
355-
return filemap_check_wb_err(file->f_mapping, READ_ONCE(file->f_wb_err));
346+
if ((file->f_mode & FMODE_WRITE) &&
347+
filemap_check_wb_err(file->f_mapping, READ_ONCE(file->f_wb_err)))
348+
nfsd_reset_write_verifier(net_generic(nf->nf_net, nfsd_net_id));
356349
}
357350

358351
static void
359352
nfsd_file_hash_remove(struct nfsd_file *nf)
360353
{
361354
trace_nfsd_file_unhash(nf);
362-
363-
if (nfsd_file_check_write_error(nf))
364-
nfsd_reset_write_verifier(net_generic(nf->nf_net, nfsd_net_id));
365355
rhashtable_remove_fast(&nfsd_file_rhash_tbl, &nf->nf_rhash,
366356
nfsd_file_rhash_params);
367357
}
@@ -387,23 +377,12 @@ nfsd_file_free(struct nfsd_file *nf)
387377
this_cpu_add(nfsd_file_total_age, age);
388378

389379
nfsd_file_unhash(nf);
390-
391-
/*
392-
* We call fsync here in order to catch writeback errors. It's not
393-
* strictly required by the protocol, but an nfsd_file could get
394-
* evicted from the cache before a COMMIT comes in. If another
395-
* task were to open that file in the interim and scrape the error,
396-
* then the client may never see it. By calling fsync here, we ensure
397-
* that writeback happens before the entry is freed, and that any
398-
* errors reported result in the write verifier changing.
399-
*/
400-
nfsd_file_fsync(nf);
401-
402380
if (nf->nf_mark)
403381
nfsd_file_mark_put(nf->nf_mark);
404382
if (nf->nf_file) {
405383
get_file(nf->nf_file);
406384
filp_close(nf->nf_file, NULL);
385+
nfsd_file_check_write_error(nf);
407386
fput(nf->nf_file);
408387
}
409388

@@ -1158,6 +1137,7 @@ nfsd_file_do_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
11581137
out:
11591138
if (status == nfs_ok) {
11601139
this_cpu_inc(nfsd_file_acquisitions);
1140+
nfsd_file_check_write_error(nf);
11611141
*pnf = nf;
11621142
} else {
11631143
if (refcount_dec_and_test(&nf->nf_ref))

fs/nfsd/trace.h

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,37 +1202,6 @@ TRACE_EVENT(nfsd_file_close,
12021202
)
12031203
);
12041204

1205-
TRACE_EVENT(nfsd_file_fsync,
1206-
TP_PROTO(
1207-
const struct nfsd_file *nf,
1208-
int ret
1209-
),
1210-
TP_ARGS(nf, ret),
1211-
TP_STRUCT__entry(
1212-
__field(void *, nf_inode)
1213-
__field(int, nf_ref)
1214-
__field(int, ret)
1215-
__field(unsigned long, nf_flags)
1216-
__field(unsigned char, nf_may)
1217-
__field(struct file *, nf_file)
1218-
),
1219-
TP_fast_assign(
1220-
__entry->nf_inode = nf->nf_inode;
1221-
__entry->nf_ref = refcount_read(&nf->nf_ref);
1222-
__entry->ret = ret;
1223-
__entry->nf_flags = nf->nf_flags;
1224-
__entry->nf_may = nf->nf_may;
1225-
__entry->nf_file = nf->nf_file;
1226-
),
1227-
TP_printk("inode=%p ref=%d flags=%s may=%s nf_file=%p ret=%d",
1228-
__entry->nf_inode,
1229-
__entry->nf_ref,
1230-
show_nf_flags(__entry->nf_flags),
1231-
show_nfsd_may_flags(__entry->nf_may),
1232-
__entry->nf_file, __entry->ret
1233-
)
1234-
);
1235-
12361205
#include "cache.h"
12371206

12381207
TRACE_DEFINE_ENUM(RC_DROPIT);

0 commit comments

Comments
 (0)