summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <trondmy@gmail.com>2019-09-02 13:02:57 -0400
committerJ. Bruce Fields <bfields@redhat.com>2019-09-10 09:23:41 -0400
commit055b24a8f23090043b2ce0c30f03e12c5f2c9e72 (patch)
tree22beaa6baaf7537b40a9efebefcda119477f994a
parent27c438f53e79b81dc8805a81f6cd74824ba57290 (diff)
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 <trond.myklebust@hammerspace.com> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
-rw-r--r--fs/nfsd/filecache.c43
1 files changed, 42 insertions, 1 deletions
diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c
index da9e790a055e..ef55e9b1cd4e 100644
--- a/fs/nfsd/filecache.c
+++ b/fs/nfsd/filecache.c
@@ -215,6 +215,36 @@ nfsd_file_free(struct nfsd_file *nf)
215 return flush; 215 return flush;
216} 216}
217 217
218static bool
219nfsd_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
231static int
232nfsd_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
241static bool
242nfsd_file_in_use(struct nfsd_file *nf)
243{
244 return nfsd_file_check_writeback(nf) ||
245 nfsd_file_check_write_error(nf);
246}
247
218static void 248static void
219nfsd_file_do_unhash(struct nfsd_file *nf) 249nfsd_file_do_unhash(struct nfsd_file *nf)
220{ 250{
@@ -222,6 +252,8 @@ nfsd_file_do_unhash(struct nfsd_file *nf)
222 252
223 trace_nfsd_file_unhash(nf); 253 trace_nfsd_file_unhash(nf);
224 254
255 if (nfsd_file_check_write_error(nf))
256 nfsd_reset_boot_verifier(net_generic(nf->nf_net, nfsd_net_id));
225 --nfsd_file_hashtbl[nf->nf_hashval].nfb_count; 257 --nfsd_file_hashtbl[nf->nf_hashval].nfb_count;
226 hlist_del_rcu(&nf->nf_node); 258 hlist_del_rcu(&nf->nf_node);
227 if (!list_empty(&nf->nf_lru)) 259 if (!list_empty(&nf->nf_lru))
@@ -276,9 +308,10 @@ void
276nfsd_file_put(struct nfsd_file *nf) 308nfsd_file_put(struct nfsd_file *nf)
277{ 309{
278 bool is_hashed = test_bit(NFSD_FILE_HASHED, &nf->nf_flags) != 0; 310 bool is_hashed = test_bit(NFSD_FILE_HASHED, &nf->nf_flags) != 0;
311 bool unused = !nfsd_file_in_use(nf);
279 312
280 set_bit(NFSD_FILE_REFERENCED, &nf->nf_flags); 313 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)
282 nfsd_file_schedule_laundrette(NFSD_FILE_LAUNDRETTE_MAY_FLUSH); 315 nfsd_file_schedule_laundrette(NFSD_FILE_LAUNDRETTE_MAY_FLUSH);
283} 316}
284 317
@@ -344,6 +377,14 @@ nfsd_file_lru_cb(struct list_head *item, struct list_lru_one *lru,
344 */ 377 */
345 if (atomic_read(&nf->nf_ref) > 1) 378 if (atomic_read(&nf->nf_ref) > 1)
346 goto out_skip; 379 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
347 if (test_and_clear_bit(NFSD_FILE_REFERENCED, &nf->nf_flags)) 388 if (test_and_clear_bit(NFSD_FILE_REFERENCED, &nf->nf_flags))
348 goto out_rescan; 389 goto out_rescan;
349 390