Skip to content

Commit 055b24a

Browse files
trondmyJ. Bruce Fields
authored andcommitted
nfsd: Don't garbage collect files that might contain write errors
If a file may contain unstable writes that can error out, then we want to avoid garbage collecting the struct nfsd_file that may be tracking those errors. So in the garbage collector, we try to avoid collecting files that aren't clean. Furthermore, we avoid immediately kicking off the garbage collector in the case where the reference drops to zero for the case where there is a write error that is being tracked. If the file is unhashed while an error is pending, then declare a reboot, to ensure the client resends any unstable writes. Signed-off-by: Trond Myklebust <[email protected]> Signed-off-by: J. Bruce Fields <[email protected]>
1 parent 27c438f commit 055b24a

File tree

1 file changed

+42
-1
lines changed

1 file changed

+42
-1
lines changed

fs/nfsd/filecache.c

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,13 +215,45 @@ nfsd_file_free(struct nfsd_file *nf)
215215
return flush;
216216
}
217217

218+
static bool
219+
nfsd_file_check_writeback(struct nfsd_file *nf)
220+
{
221+
struct file *file = nf->nf_file;
222+
struct address_space *mapping;
223+
224+
if (!file || !(file->f_mode & FMODE_WRITE))
225+
return false;
226+
mapping = file->f_mapping;
227+
return mapping_tagged(mapping, PAGECACHE_TAG_DIRTY) ||
228+
mapping_tagged(mapping, PAGECACHE_TAG_WRITEBACK);
229+
}
230+
231+
static int
232+
nfsd_file_check_write_error(struct nfsd_file *nf)
233+
{
234+
struct file *file = nf->nf_file;
235+
236+
if (!file || !(file->f_mode & FMODE_WRITE))
237+
return 0;
238+
return filemap_check_wb_err(file->f_mapping, READ_ONCE(file->f_wb_err));
239+
}
240+
241+
static bool
242+
nfsd_file_in_use(struct nfsd_file *nf)
243+
{
244+
return nfsd_file_check_writeback(nf) ||
245+
nfsd_file_check_write_error(nf);
246+
}
247+
218248
static void
219249
nfsd_file_do_unhash(struct nfsd_file *nf)
220250
{
221251
lockdep_assert_held(&nfsd_file_hashtbl[nf->nf_hashval].nfb_lock);
222252

223253
trace_nfsd_file_unhash(nf);
224254

255+
if (nfsd_file_check_write_error(nf))
256+
nfsd_reset_boot_verifier(net_generic(nf->nf_net, nfsd_net_id));
225257
--nfsd_file_hashtbl[nf->nf_hashval].nfb_count;
226258
hlist_del_rcu(&nf->nf_node);
227259
if (!list_empty(&nf->nf_lru))
@@ -276,9 +308,10 @@ void
276308
nfsd_file_put(struct nfsd_file *nf)
277309
{
278310
bool is_hashed = test_bit(NFSD_FILE_HASHED, &nf->nf_flags) != 0;
311+
bool unused = !nfsd_file_in_use(nf);
279312

280313
set_bit(NFSD_FILE_REFERENCED, &nf->nf_flags);
281-
if (nfsd_file_put_noref(nf) == 1 && is_hashed)
314+
if (nfsd_file_put_noref(nf) == 1 && is_hashed && unused)
282315
nfsd_file_schedule_laundrette(NFSD_FILE_LAUNDRETTE_MAY_FLUSH);
283316
}
284317

@@ -344,6 +377,14 @@ nfsd_file_lru_cb(struct list_head *item, struct list_lru_one *lru,
344377
*/
345378
if (atomic_read(&nf->nf_ref) > 1)
346379
goto out_skip;
380+
381+
/*
382+
* Don't throw out files that are still undergoing I/O or
383+
* that have uncleared errors pending.
384+
*/
385+
if (nfsd_file_check_writeback(nf))
386+
goto out_skip;
387+
347388
if (test_and_clear_bit(NFSD_FILE_REFERENCED, &nf->nf_flags))
348389
goto out_rescan;
349390

0 commit comments

Comments
 (0)