From 057a24e1b3744c716e4956eb34c2d15ed719db23 Mon Sep 17 00:00:00 2001 From: Frank van der Linden Date: Fri, 26 Jun 2020 22:35:01 +0000 Subject: [PATCH 1/2] nfsd: don't put nfsd_files with long term refs on the LRU list Files with long term references, as created by v4 OPENs, will just clutter the LRU list without a chance of being reaped. So, don't put them there at all. When finding a file in the hash table for a long term ref, remove it from the LRU list. When dropping the last long term ref, add it back to the LRU list. Signed-off-by: Frank van der Linden --- fs/nfsd/filecache.c | 81 ++++++++++++++++++++++++++++++++++++++++----- fs/nfsd/filecache.h | 6 ++++ fs/nfsd/nfs4state.c | 2 +- 3 files changed, 79 insertions(+), 10 deletions(-) diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c index 82198d747c4c..5ef6bb802f24 100644 --- a/fs/nfsd/filecache.c +++ b/fs/nfsd/filecache.c @@ -186,6 +186,7 @@ nfsd_file_alloc(struct inode *inode, unsigned int may, unsigned int hashval, nf->nf_inode = inode; nf->nf_hashval = hashval; refcount_set(&nf->nf_ref, 1); + atomic_set(&nf->nf_lref, 0); nf->nf_may = may & NFSD_FILE_MAY_MASK; if (may & NFSD_MAY_NOT_BREAK_LEASE) { if (may & NFSD_MAY_WRITE) @@ -297,13 +298,26 @@ nfsd_file_put_noref(struct nfsd_file *nf) } } -void -nfsd_file_put(struct nfsd_file *nf) +static void +__nfsd_file_put(struct nfsd_file *nf, unsigned int flags) { bool is_hashed; + int refs; + + refs = refcount_read(&nf->nf_ref); + + if (flags & NFSD_ACQ_FILE_LONGTERM) { + /* + * If we're dropping the last long term ref, and there + * are other references, put the file on the LRU list, + * as it now makes sense for it to be there. + */ + if (atomic_dec_return(&nf->nf_lref) == 0 && refs > 2) + list_lru_add(&nfsd_file_lru, &nf->nf_lru); + } else + set_bit(NFSD_FILE_REFERENCED, &nf->nf_flags); - set_bit(NFSD_FILE_REFERENCED, &nf->nf_flags); - if (refcount_read(&nf->nf_ref) > 2 || !nf->nf_file) { + if (refs > 2 || !nf->nf_file) { nfsd_file_put_noref(nf); return; } @@ -317,6 +331,18 @@ nfsd_file_put(struct nfsd_file *nf) nfsd_file_gc(); } +void +nfsd_file_put(struct nfsd_file *nf) +{ + __nfsd_file_put(nf, 0); +} + +void +nfsd_file_put_longterm(struct nfsd_file *nf) +{ + __nfsd_file_put(nf, NFSD_ACQ_FILE_LONGTERM); +} + struct nfsd_file * nfsd_file_get(struct nfsd_file *nf) { @@ -934,13 +960,14 @@ nfsd_file_is_cached(struct inode *inode) return ret; } -__be32 -nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp, - unsigned int may_flags, struct nfsd_file **pnf) +static __be32 +__nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp, + unsigned int may_flags, struct nfsd_file **pnf, + unsigned int flags) { __be32 status; struct net *net = SVC_NET(rqstp); - struct nfsd_file *nf, *new; + struct nfsd_file *nf, *new = NULL; struct inode *inode; unsigned int hashval; bool retry = true; @@ -1006,6 +1033,16 @@ nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp, } } out: + if (flags & NFSD_ACQ_FILE_LONGTERM) { + /* + * A file with long term (v4) references will needlessly + * clutter the LRU, so remove it when adding the first + * long term ref. + */ + if (!new && atomic_inc_return(&nf->nf_lref) == 1) + list_lru_del(&nfsd_file_lru, &nf->nf_lru); + } + if (status == nfs_ok) { *pnf = nf; } else { @@ -1021,7 +1058,18 @@ nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp, refcount_inc(&nf->nf_ref); __set_bit(NFSD_FILE_HASHED, &nf->nf_flags); __set_bit(NFSD_FILE_PENDING, &nf->nf_flags); - list_lru_add(&nfsd_file_lru, &nf->nf_lru); + + /* + * Don't add a new file to the LRU if it's a long term reference. + * It is still added to the hash table, so it may be added to the + * LRU later, when the number of long term references drops back + * to zero, and there are other references. + */ + if (flags & NFSD_ACQ_FILE_LONGTERM) + atomic_inc(&nf->nf_lref); + else + list_lru_add(&nfsd_file_lru, &nf->nf_lru); + hlist_add_head_rcu(&nf->nf_node, &nfsd_file_hashtbl[hashval].nfb_head); ++nfsd_file_hashtbl[hashval].nfb_count; nfsd_file_hashtbl[hashval].nfb_maxcount = max(nfsd_file_hashtbl[hashval].nfb_maxcount, @@ -1054,6 +1102,21 @@ nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp, goto out; } +__be32 +nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp, + unsigned int may_flags, struct nfsd_file **pnf) +{ + return __nfsd_file_acquire(rqstp, fhp, may_flags, pnf, 0); +} + +__be32 +nfsd_file_acquire_longterm(struct svc_rqst *rqstp, struct svc_fh *fhp, + unsigned int may_flags, struct nfsd_file **pnf) +{ + return __nfsd_file_acquire(rqstp, fhp, may_flags, pnf, + NFSD_ACQ_FILE_LONGTERM); +} + /* * Note that fields may be added, removed or reordered in the future. Programs * scraping this file for info should test the labels to ensure they're diff --git a/fs/nfsd/filecache.h b/fs/nfsd/filecache.h index 7872df5a0fe3..6e1db77d7148 100644 --- a/fs/nfsd/filecache.h +++ b/fs/nfsd/filecache.h @@ -44,21 +44,27 @@ struct nfsd_file { struct inode *nf_inode; unsigned int nf_hashval; refcount_t nf_ref; + atomic_t nf_lref; unsigned char nf_may; struct nfsd_file_mark *nf_mark; struct rw_semaphore nf_rwsem; }; +#define NFSD_ACQ_FILE_LONGTERM 0x0001 + int nfsd_file_cache_init(void); void nfsd_file_cache_purge(struct net *); void nfsd_file_cache_shutdown(void); int nfsd_file_cache_start_net(struct net *net); void nfsd_file_cache_shutdown_net(struct net *net); void nfsd_file_put(struct nfsd_file *nf); +void nfsd_file_put_longterm(struct nfsd_file *nf); struct nfsd_file *nfsd_file_get(struct nfsd_file *nf); void nfsd_file_close_inode_sync(struct inode *inode); bool nfsd_file_is_cached(struct inode *inode); __be32 nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned int may_flags, struct nfsd_file **nfp); +__be32 nfsd_file_acquire_longterm(struct svc_rqst *rqstp, struct svc_fh *fhp, + unsigned int may_flags, struct nfsd_file **nfp); int nfsd_file_cache_stats_open(struct inode *, struct file *); #endif /* _FS_NFSD_FILECACHE_H */ diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index bb3d2c32664a..451a1071daf4 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4838,7 +4838,7 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp, if (!fp->fi_fds[oflag]) { spin_unlock(&fp->fi_lock); - status = nfsd_file_acquire(rqstp, cur_fh, access, &nf); + status = nfsd_file_acquire_longterm(rqstp, cur_fh, access, &nf); if (status) goto out_put_access; spin_lock(&fp->fi_lock); -- 2.17.2