162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * The NFSD open file cache.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * (c) 2015 - Jeff Layton <jeff.layton@primarydata.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * An nfsd_file object is a per-file collection of open state that binds
862306a36Sopenharmony_ci * together:
962306a36Sopenharmony_ci *   - a struct file *
1062306a36Sopenharmony_ci *   - a user credential
1162306a36Sopenharmony_ci *   - a network namespace
1262306a36Sopenharmony_ci *   - a read-ahead context
1362306a36Sopenharmony_ci *   - monitoring for writeback errors
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * nfsd_file objects are reference-counted. Consumers acquire a new
1662306a36Sopenharmony_ci * object via the nfsd_file_acquire API. They manage their interest in
1762306a36Sopenharmony_ci * the acquired object, and hence the object's reference count, via
1862306a36Sopenharmony_ci * nfsd_file_get and nfsd_file_put. There are two varieties of nfsd_file
1962306a36Sopenharmony_ci * object:
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci *  * non-garbage-collected: When a consumer wants to precisely control
2262306a36Sopenharmony_ci *    the lifetime of a file's open state, it acquires a non-garbage-
2362306a36Sopenharmony_ci *    collected nfsd_file. The final nfsd_file_put releases the open
2462306a36Sopenharmony_ci *    state immediately.
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci *  * garbage-collected: When a consumer does not control the lifetime
2762306a36Sopenharmony_ci *    of open state, it acquires a garbage-collected nfsd_file. The
2862306a36Sopenharmony_ci *    final nfsd_file_put allows the open state to linger for a period
2962306a36Sopenharmony_ci *    during which it may be re-used.
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#include <linux/hash.h>
3362306a36Sopenharmony_ci#include <linux/slab.h>
3462306a36Sopenharmony_ci#include <linux/file.h>
3562306a36Sopenharmony_ci#include <linux/pagemap.h>
3662306a36Sopenharmony_ci#include <linux/sched.h>
3762306a36Sopenharmony_ci#include <linux/list_lru.h>
3862306a36Sopenharmony_ci#include <linux/fsnotify_backend.h>
3962306a36Sopenharmony_ci#include <linux/fsnotify.h>
4062306a36Sopenharmony_ci#include <linux/seq_file.h>
4162306a36Sopenharmony_ci#include <linux/rhashtable.h>
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#include "vfs.h"
4462306a36Sopenharmony_ci#include "nfsd.h"
4562306a36Sopenharmony_ci#include "nfsfh.h"
4662306a36Sopenharmony_ci#include "netns.h"
4762306a36Sopenharmony_ci#include "filecache.h"
4862306a36Sopenharmony_ci#include "trace.h"
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#define NFSD_LAUNDRETTE_DELAY		     (2 * HZ)
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define NFSD_FILE_CACHE_UP		     (0)
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/* We only care about NFSD_MAY_READ/WRITE for this cache */
5562306a36Sopenharmony_ci#define NFSD_FILE_MAY_MASK	(NFSD_MAY_READ|NFSD_MAY_WRITE)
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic DEFINE_PER_CPU(unsigned long, nfsd_file_cache_hits);
5862306a36Sopenharmony_cistatic DEFINE_PER_CPU(unsigned long, nfsd_file_acquisitions);
5962306a36Sopenharmony_cistatic DEFINE_PER_CPU(unsigned long, nfsd_file_releases);
6062306a36Sopenharmony_cistatic DEFINE_PER_CPU(unsigned long, nfsd_file_total_age);
6162306a36Sopenharmony_cistatic DEFINE_PER_CPU(unsigned long, nfsd_file_evictions);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistruct nfsd_fcache_disposal {
6462306a36Sopenharmony_ci	struct work_struct work;
6562306a36Sopenharmony_ci	spinlock_t lock;
6662306a36Sopenharmony_ci	struct list_head freeme;
6762306a36Sopenharmony_ci};
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic struct workqueue_struct *nfsd_filecache_wq __read_mostly;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic struct kmem_cache		*nfsd_file_slab;
7262306a36Sopenharmony_cistatic struct kmem_cache		*nfsd_file_mark_slab;
7362306a36Sopenharmony_cistatic struct list_lru			nfsd_file_lru;
7462306a36Sopenharmony_cistatic unsigned long			nfsd_file_flags;
7562306a36Sopenharmony_cistatic struct fsnotify_group		*nfsd_file_fsnotify_group;
7662306a36Sopenharmony_cistatic struct delayed_work		nfsd_filecache_laundrette;
7762306a36Sopenharmony_cistatic struct rhltable			nfsd_file_rhltable
7862306a36Sopenharmony_ci						____cacheline_aligned_in_smp;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic bool
8162306a36Sopenharmony_cinfsd_match_cred(const struct cred *c1, const struct cred *c2)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	int i;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	if (!uid_eq(c1->fsuid, c2->fsuid))
8662306a36Sopenharmony_ci		return false;
8762306a36Sopenharmony_ci	if (!gid_eq(c1->fsgid, c2->fsgid))
8862306a36Sopenharmony_ci		return false;
8962306a36Sopenharmony_ci	if (c1->group_info == NULL || c2->group_info == NULL)
9062306a36Sopenharmony_ci		return c1->group_info == c2->group_info;
9162306a36Sopenharmony_ci	if (c1->group_info->ngroups != c2->group_info->ngroups)
9262306a36Sopenharmony_ci		return false;
9362306a36Sopenharmony_ci	for (i = 0; i < c1->group_info->ngroups; i++) {
9462306a36Sopenharmony_ci		if (!gid_eq(c1->group_info->gid[i], c2->group_info->gid[i]))
9562306a36Sopenharmony_ci			return false;
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci	return true;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic const struct rhashtable_params nfsd_file_rhash_params = {
10162306a36Sopenharmony_ci	.key_len		= sizeof_field(struct nfsd_file, nf_inode),
10262306a36Sopenharmony_ci	.key_offset		= offsetof(struct nfsd_file, nf_inode),
10362306a36Sopenharmony_ci	.head_offset		= offsetof(struct nfsd_file, nf_rlist),
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	/*
10662306a36Sopenharmony_ci	 * Start with a single page hash table to reduce resizing churn
10762306a36Sopenharmony_ci	 * on light workloads.
10862306a36Sopenharmony_ci	 */
10962306a36Sopenharmony_ci	.min_size		= 256,
11062306a36Sopenharmony_ci	.automatic_shrinking	= true,
11162306a36Sopenharmony_ci};
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic void
11462306a36Sopenharmony_cinfsd_file_schedule_laundrette(void)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags))
11762306a36Sopenharmony_ci		queue_delayed_work(system_wq, &nfsd_filecache_laundrette,
11862306a36Sopenharmony_ci				   NFSD_LAUNDRETTE_DELAY);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic void
12262306a36Sopenharmony_cinfsd_file_slab_free(struct rcu_head *rcu)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	struct nfsd_file *nf = container_of(rcu, struct nfsd_file, nf_rcu);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	put_cred(nf->nf_cred);
12762306a36Sopenharmony_ci	kmem_cache_free(nfsd_file_slab, nf);
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic void
13162306a36Sopenharmony_cinfsd_file_mark_free(struct fsnotify_mark *mark)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	struct nfsd_file_mark *nfm = container_of(mark, struct nfsd_file_mark,
13462306a36Sopenharmony_ci						  nfm_mark);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	kmem_cache_free(nfsd_file_mark_slab, nfm);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic struct nfsd_file_mark *
14062306a36Sopenharmony_cinfsd_file_mark_get(struct nfsd_file_mark *nfm)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	if (!refcount_inc_not_zero(&nfm->nfm_ref))
14362306a36Sopenharmony_ci		return NULL;
14462306a36Sopenharmony_ci	return nfm;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic void
14862306a36Sopenharmony_cinfsd_file_mark_put(struct nfsd_file_mark *nfm)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	if (refcount_dec_and_test(&nfm->nfm_ref)) {
15162306a36Sopenharmony_ci		fsnotify_destroy_mark(&nfm->nfm_mark, nfsd_file_fsnotify_group);
15262306a36Sopenharmony_ci		fsnotify_put_mark(&nfm->nfm_mark);
15362306a36Sopenharmony_ci	}
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic struct nfsd_file_mark *
15762306a36Sopenharmony_cinfsd_file_mark_find_or_create(struct nfsd_file *nf, struct inode *inode)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	int			err;
16062306a36Sopenharmony_ci	struct fsnotify_mark	*mark;
16162306a36Sopenharmony_ci	struct nfsd_file_mark	*nfm = NULL, *new;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	do {
16462306a36Sopenharmony_ci		fsnotify_group_lock(nfsd_file_fsnotify_group);
16562306a36Sopenharmony_ci		mark = fsnotify_find_mark(&inode->i_fsnotify_marks,
16662306a36Sopenharmony_ci					  nfsd_file_fsnotify_group);
16762306a36Sopenharmony_ci		if (mark) {
16862306a36Sopenharmony_ci			nfm = nfsd_file_mark_get(container_of(mark,
16962306a36Sopenharmony_ci						 struct nfsd_file_mark,
17062306a36Sopenharmony_ci						 nfm_mark));
17162306a36Sopenharmony_ci			fsnotify_group_unlock(nfsd_file_fsnotify_group);
17262306a36Sopenharmony_ci			if (nfm) {
17362306a36Sopenharmony_ci				fsnotify_put_mark(mark);
17462306a36Sopenharmony_ci				break;
17562306a36Sopenharmony_ci			}
17662306a36Sopenharmony_ci			/* Avoid soft lockup race with nfsd_file_mark_put() */
17762306a36Sopenharmony_ci			fsnotify_destroy_mark(mark, nfsd_file_fsnotify_group);
17862306a36Sopenharmony_ci			fsnotify_put_mark(mark);
17962306a36Sopenharmony_ci		} else {
18062306a36Sopenharmony_ci			fsnotify_group_unlock(nfsd_file_fsnotify_group);
18162306a36Sopenharmony_ci		}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci		/* allocate a new nfm */
18462306a36Sopenharmony_ci		new = kmem_cache_alloc(nfsd_file_mark_slab, GFP_KERNEL);
18562306a36Sopenharmony_ci		if (!new)
18662306a36Sopenharmony_ci			return NULL;
18762306a36Sopenharmony_ci		fsnotify_init_mark(&new->nfm_mark, nfsd_file_fsnotify_group);
18862306a36Sopenharmony_ci		new->nfm_mark.mask = FS_ATTRIB|FS_DELETE_SELF;
18962306a36Sopenharmony_ci		refcount_set(&new->nfm_ref, 1);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci		err = fsnotify_add_inode_mark(&new->nfm_mark, inode, 0);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci		/*
19462306a36Sopenharmony_ci		 * If the add was successful, then return the object.
19562306a36Sopenharmony_ci		 * Otherwise, we need to put the reference we hold on the
19662306a36Sopenharmony_ci		 * nfm_mark. The fsnotify code will take a reference and put
19762306a36Sopenharmony_ci		 * it on failure, so we can't just free it directly. It's also
19862306a36Sopenharmony_ci		 * not safe to call fsnotify_destroy_mark on it as the
19962306a36Sopenharmony_ci		 * mark->group will be NULL. Thus, we can't let the nfm_ref
20062306a36Sopenharmony_ci		 * counter drive the destruction at this point.
20162306a36Sopenharmony_ci		 */
20262306a36Sopenharmony_ci		if (likely(!err))
20362306a36Sopenharmony_ci			nfm = new;
20462306a36Sopenharmony_ci		else
20562306a36Sopenharmony_ci			fsnotify_put_mark(&new->nfm_mark);
20662306a36Sopenharmony_ci	} while (unlikely(err == -EEXIST));
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	return nfm;
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic struct nfsd_file *
21262306a36Sopenharmony_cinfsd_file_alloc(struct net *net, struct inode *inode, unsigned char need,
21362306a36Sopenharmony_ci		bool want_gc)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	struct nfsd_file *nf;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	nf = kmem_cache_alloc(nfsd_file_slab, GFP_KERNEL);
21862306a36Sopenharmony_ci	if (unlikely(!nf))
21962306a36Sopenharmony_ci		return NULL;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	INIT_LIST_HEAD(&nf->nf_lru);
22262306a36Sopenharmony_ci	nf->nf_birthtime = ktime_get();
22362306a36Sopenharmony_ci	nf->nf_file = NULL;
22462306a36Sopenharmony_ci	nf->nf_cred = get_current_cred();
22562306a36Sopenharmony_ci	nf->nf_net = net;
22662306a36Sopenharmony_ci	nf->nf_flags = want_gc ?
22762306a36Sopenharmony_ci		BIT(NFSD_FILE_HASHED) | BIT(NFSD_FILE_PENDING) | BIT(NFSD_FILE_GC) :
22862306a36Sopenharmony_ci		BIT(NFSD_FILE_HASHED) | BIT(NFSD_FILE_PENDING);
22962306a36Sopenharmony_ci	nf->nf_inode = inode;
23062306a36Sopenharmony_ci	refcount_set(&nf->nf_ref, 1);
23162306a36Sopenharmony_ci	nf->nf_may = need;
23262306a36Sopenharmony_ci	nf->nf_mark = NULL;
23362306a36Sopenharmony_ci	return nf;
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci/**
23762306a36Sopenharmony_ci * nfsd_file_check_write_error - check for writeback errors on a file
23862306a36Sopenharmony_ci * @nf: nfsd_file to check for writeback errors
23962306a36Sopenharmony_ci *
24062306a36Sopenharmony_ci * Check whether a nfsd_file has an unseen error. Reset the write
24162306a36Sopenharmony_ci * verifier if so.
24262306a36Sopenharmony_ci */
24362306a36Sopenharmony_cistatic void
24462306a36Sopenharmony_cinfsd_file_check_write_error(struct nfsd_file *nf)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	struct file *file = nf->nf_file;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if ((file->f_mode & FMODE_WRITE) &&
24962306a36Sopenharmony_ci	    filemap_check_wb_err(file->f_mapping, READ_ONCE(file->f_wb_err)))
25062306a36Sopenharmony_ci		nfsd_reset_write_verifier(net_generic(nf->nf_net, nfsd_net_id));
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistatic void
25462306a36Sopenharmony_cinfsd_file_hash_remove(struct nfsd_file *nf)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	trace_nfsd_file_unhash(nf);
25762306a36Sopenharmony_ci	rhltable_remove(&nfsd_file_rhltable, &nf->nf_rlist,
25862306a36Sopenharmony_ci			nfsd_file_rhash_params);
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic bool
26262306a36Sopenharmony_cinfsd_file_unhash(struct nfsd_file *nf)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	if (test_and_clear_bit(NFSD_FILE_HASHED, &nf->nf_flags)) {
26562306a36Sopenharmony_ci		nfsd_file_hash_remove(nf);
26662306a36Sopenharmony_ci		return true;
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci	return false;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic void
27262306a36Sopenharmony_cinfsd_file_free(struct nfsd_file *nf)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	s64 age = ktime_to_ms(ktime_sub(ktime_get(), nf->nf_birthtime));
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	trace_nfsd_file_free(nf);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	this_cpu_inc(nfsd_file_releases);
27962306a36Sopenharmony_ci	this_cpu_add(nfsd_file_total_age, age);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	nfsd_file_unhash(nf);
28262306a36Sopenharmony_ci	if (nf->nf_mark)
28362306a36Sopenharmony_ci		nfsd_file_mark_put(nf->nf_mark);
28462306a36Sopenharmony_ci	if (nf->nf_file) {
28562306a36Sopenharmony_ci		nfsd_file_check_write_error(nf);
28662306a36Sopenharmony_ci		filp_close(nf->nf_file, NULL);
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	/*
29062306a36Sopenharmony_ci	 * If this item is still linked via nf_lru, that's a bug.
29162306a36Sopenharmony_ci	 * WARN and leak it to preserve system stability.
29262306a36Sopenharmony_ci	 */
29362306a36Sopenharmony_ci	if (WARN_ON_ONCE(!list_empty(&nf->nf_lru)))
29462306a36Sopenharmony_ci		return;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	call_rcu(&nf->nf_rcu, nfsd_file_slab_free);
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic bool
30062306a36Sopenharmony_cinfsd_file_check_writeback(struct nfsd_file *nf)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	struct file *file = nf->nf_file;
30362306a36Sopenharmony_ci	struct address_space *mapping;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	/* File not open for write? */
30662306a36Sopenharmony_ci	if (!(file->f_mode & FMODE_WRITE))
30762306a36Sopenharmony_ci		return false;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	/*
31062306a36Sopenharmony_ci	 * Some filesystems (e.g. NFS) flush all dirty data on close.
31162306a36Sopenharmony_ci	 * On others, there is no need to wait for writeback.
31262306a36Sopenharmony_ci	 */
31362306a36Sopenharmony_ci	if (!(file_inode(file)->i_sb->s_export_op->flags & EXPORT_OP_FLUSH_ON_CLOSE))
31462306a36Sopenharmony_ci		return false;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	mapping = file->f_mapping;
31762306a36Sopenharmony_ci	return mapping_tagged(mapping, PAGECACHE_TAG_DIRTY) ||
31862306a36Sopenharmony_ci		mapping_tagged(mapping, PAGECACHE_TAG_WRITEBACK);
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic bool nfsd_file_lru_add(struct nfsd_file *nf)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	set_bit(NFSD_FILE_REFERENCED, &nf->nf_flags);
32562306a36Sopenharmony_ci	if (list_lru_add(&nfsd_file_lru, &nf->nf_lru)) {
32662306a36Sopenharmony_ci		trace_nfsd_file_lru_add(nf);
32762306a36Sopenharmony_ci		return true;
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci	return false;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic bool nfsd_file_lru_remove(struct nfsd_file *nf)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	if (list_lru_del(&nfsd_file_lru, &nf->nf_lru)) {
33562306a36Sopenharmony_ci		trace_nfsd_file_lru_del(nf);
33662306a36Sopenharmony_ci		return true;
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci	return false;
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistruct nfsd_file *
34262306a36Sopenharmony_cinfsd_file_get(struct nfsd_file *nf)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	if (nf && refcount_inc_not_zero(&nf->nf_ref))
34562306a36Sopenharmony_ci		return nf;
34662306a36Sopenharmony_ci	return NULL;
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci/**
35062306a36Sopenharmony_ci * nfsd_file_put - put the reference to a nfsd_file
35162306a36Sopenharmony_ci * @nf: nfsd_file of which to put the reference
35262306a36Sopenharmony_ci *
35362306a36Sopenharmony_ci * Put a reference to a nfsd_file. In the non-GC case, we just put the
35462306a36Sopenharmony_ci * reference immediately. In the GC case, if the reference would be
35562306a36Sopenharmony_ci * the last one, the put it on the LRU instead to be cleaned up later.
35662306a36Sopenharmony_ci */
35762306a36Sopenharmony_civoid
35862306a36Sopenharmony_cinfsd_file_put(struct nfsd_file *nf)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	might_sleep();
36162306a36Sopenharmony_ci	trace_nfsd_file_put(nf);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	if (test_bit(NFSD_FILE_GC, &nf->nf_flags) &&
36462306a36Sopenharmony_ci	    test_bit(NFSD_FILE_HASHED, &nf->nf_flags)) {
36562306a36Sopenharmony_ci		/*
36662306a36Sopenharmony_ci		 * If this is the last reference (nf_ref == 1), then try to
36762306a36Sopenharmony_ci		 * transfer it to the LRU.
36862306a36Sopenharmony_ci		 */
36962306a36Sopenharmony_ci		if (refcount_dec_not_one(&nf->nf_ref))
37062306a36Sopenharmony_ci			return;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci		/* Try to add it to the LRU.  If that fails, decrement. */
37362306a36Sopenharmony_ci		if (nfsd_file_lru_add(nf)) {
37462306a36Sopenharmony_ci			/* If it's still hashed, we're done */
37562306a36Sopenharmony_ci			if (test_bit(NFSD_FILE_HASHED, &nf->nf_flags)) {
37662306a36Sopenharmony_ci				nfsd_file_schedule_laundrette();
37762306a36Sopenharmony_ci				return;
37862306a36Sopenharmony_ci			}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci			/*
38162306a36Sopenharmony_ci			 * We're racing with unhashing, so try to remove it from
38262306a36Sopenharmony_ci			 * the LRU. If removal fails, then someone else already
38362306a36Sopenharmony_ci			 * has our reference.
38462306a36Sopenharmony_ci			 */
38562306a36Sopenharmony_ci			if (!nfsd_file_lru_remove(nf))
38662306a36Sopenharmony_ci				return;
38762306a36Sopenharmony_ci		}
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_ci	if (refcount_dec_and_test(&nf->nf_ref))
39062306a36Sopenharmony_ci		nfsd_file_free(nf);
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic void
39462306a36Sopenharmony_cinfsd_file_dispose_list(struct list_head *dispose)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	struct nfsd_file *nf;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	while (!list_empty(dispose)) {
39962306a36Sopenharmony_ci		nf = list_first_entry(dispose, struct nfsd_file, nf_lru);
40062306a36Sopenharmony_ci		list_del_init(&nf->nf_lru);
40162306a36Sopenharmony_ci		nfsd_file_free(nf);
40262306a36Sopenharmony_ci	}
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci/**
40662306a36Sopenharmony_ci * nfsd_file_dispose_list_delayed - move list of dead files to net's freeme list
40762306a36Sopenharmony_ci * @dispose: list of nfsd_files to be disposed
40862306a36Sopenharmony_ci *
40962306a36Sopenharmony_ci * Transfers each file to the "freeme" list for its nfsd_net, to eventually
41062306a36Sopenharmony_ci * be disposed of by the per-net garbage collector.
41162306a36Sopenharmony_ci */
41262306a36Sopenharmony_cistatic void
41362306a36Sopenharmony_cinfsd_file_dispose_list_delayed(struct list_head *dispose)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	while(!list_empty(dispose)) {
41662306a36Sopenharmony_ci		struct nfsd_file *nf = list_first_entry(dispose,
41762306a36Sopenharmony_ci						struct nfsd_file, nf_lru);
41862306a36Sopenharmony_ci		struct nfsd_net *nn = net_generic(nf->nf_net, nfsd_net_id);
41962306a36Sopenharmony_ci		struct nfsd_fcache_disposal *l = nn->fcache_disposal;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci		spin_lock(&l->lock);
42262306a36Sopenharmony_ci		list_move_tail(&nf->nf_lru, &l->freeme);
42362306a36Sopenharmony_ci		spin_unlock(&l->lock);
42462306a36Sopenharmony_ci		queue_work(nfsd_filecache_wq, &l->work);
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci/**
42962306a36Sopenharmony_ci * nfsd_file_lru_cb - Examine an entry on the LRU list
43062306a36Sopenharmony_ci * @item: LRU entry to examine
43162306a36Sopenharmony_ci * @lru: controlling LRU
43262306a36Sopenharmony_ci * @lock: LRU list lock (unused)
43362306a36Sopenharmony_ci * @arg: dispose list
43462306a36Sopenharmony_ci *
43562306a36Sopenharmony_ci * Return values:
43662306a36Sopenharmony_ci *   %LRU_REMOVED: @item was removed from the LRU
43762306a36Sopenharmony_ci *   %LRU_ROTATE: @item is to be moved to the LRU tail
43862306a36Sopenharmony_ci *   %LRU_SKIP: @item cannot be evicted
43962306a36Sopenharmony_ci */
44062306a36Sopenharmony_cistatic enum lru_status
44162306a36Sopenharmony_cinfsd_file_lru_cb(struct list_head *item, struct list_lru_one *lru,
44262306a36Sopenharmony_ci		 spinlock_t *lock, void *arg)
44362306a36Sopenharmony_ci	__releases(lock)
44462306a36Sopenharmony_ci	__acquires(lock)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	struct list_head *head = arg;
44762306a36Sopenharmony_ci	struct nfsd_file *nf = list_entry(item, struct nfsd_file, nf_lru);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	/* We should only be dealing with GC entries here */
45062306a36Sopenharmony_ci	WARN_ON_ONCE(!test_bit(NFSD_FILE_GC, &nf->nf_flags));
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	/*
45362306a36Sopenharmony_ci	 * Don't throw out files that are still undergoing I/O or
45462306a36Sopenharmony_ci	 * that have uncleared errors pending.
45562306a36Sopenharmony_ci	 */
45662306a36Sopenharmony_ci	if (nfsd_file_check_writeback(nf)) {
45762306a36Sopenharmony_ci		trace_nfsd_file_gc_writeback(nf);
45862306a36Sopenharmony_ci		return LRU_SKIP;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	/* If it was recently added to the list, skip it */
46262306a36Sopenharmony_ci	if (test_and_clear_bit(NFSD_FILE_REFERENCED, &nf->nf_flags)) {
46362306a36Sopenharmony_ci		trace_nfsd_file_gc_referenced(nf);
46462306a36Sopenharmony_ci		return LRU_ROTATE;
46562306a36Sopenharmony_ci	}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	/*
46862306a36Sopenharmony_ci	 * Put the reference held on behalf of the LRU. If it wasn't the last
46962306a36Sopenharmony_ci	 * one, then just remove it from the LRU and ignore it.
47062306a36Sopenharmony_ci	 */
47162306a36Sopenharmony_ci	if (!refcount_dec_and_test(&nf->nf_ref)) {
47262306a36Sopenharmony_ci		trace_nfsd_file_gc_in_use(nf);
47362306a36Sopenharmony_ci		list_lru_isolate(lru, &nf->nf_lru);
47462306a36Sopenharmony_ci		return LRU_REMOVED;
47562306a36Sopenharmony_ci	}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	/* Refcount went to zero. Unhash it and queue it to the dispose list */
47862306a36Sopenharmony_ci	nfsd_file_unhash(nf);
47962306a36Sopenharmony_ci	list_lru_isolate_move(lru, &nf->nf_lru, head);
48062306a36Sopenharmony_ci	this_cpu_inc(nfsd_file_evictions);
48162306a36Sopenharmony_ci	trace_nfsd_file_gc_disposed(nf);
48262306a36Sopenharmony_ci	return LRU_REMOVED;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic void
48662306a36Sopenharmony_cinfsd_file_gc(void)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	LIST_HEAD(dispose);
48962306a36Sopenharmony_ci	unsigned long ret;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	ret = list_lru_walk(&nfsd_file_lru, nfsd_file_lru_cb,
49262306a36Sopenharmony_ci			    &dispose, list_lru_count(&nfsd_file_lru));
49362306a36Sopenharmony_ci	trace_nfsd_file_gc_removed(ret, list_lru_count(&nfsd_file_lru));
49462306a36Sopenharmony_ci	nfsd_file_dispose_list_delayed(&dispose);
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistatic void
49862306a36Sopenharmony_cinfsd_file_gc_worker(struct work_struct *work)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	nfsd_file_gc();
50162306a36Sopenharmony_ci	if (list_lru_count(&nfsd_file_lru))
50262306a36Sopenharmony_ci		nfsd_file_schedule_laundrette();
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_cistatic unsigned long
50662306a36Sopenharmony_cinfsd_file_lru_count(struct shrinker *s, struct shrink_control *sc)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	return list_lru_count(&nfsd_file_lru);
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_cistatic unsigned long
51262306a36Sopenharmony_cinfsd_file_lru_scan(struct shrinker *s, struct shrink_control *sc)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	LIST_HEAD(dispose);
51562306a36Sopenharmony_ci	unsigned long ret;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	ret = list_lru_shrink_walk(&nfsd_file_lru, sc,
51862306a36Sopenharmony_ci				   nfsd_file_lru_cb, &dispose);
51962306a36Sopenharmony_ci	trace_nfsd_file_shrinker_removed(ret, list_lru_count(&nfsd_file_lru));
52062306a36Sopenharmony_ci	nfsd_file_dispose_list_delayed(&dispose);
52162306a36Sopenharmony_ci	return ret;
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic struct shrinker	nfsd_file_shrinker = {
52562306a36Sopenharmony_ci	.scan_objects = nfsd_file_lru_scan,
52662306a36Sopenharmony_ci	.count_objects = nfsd_file_lru_count,
52762306a36Sopenharmony_ci	.seeks = 1,
52862306a36Sopenharmony_ci};
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci/**
53162306a36Sopenharmony_ci * nfsd_file_cond_queue - conditionally unhash and queue a nfsd_file
53262306a36Sopenharmony_ci * @nf: nfsd_file to attempt to queue
53362306a36Sopenharmony_ci * @dispose: private list to queue successfully-put objects
53462306a36Sopenharmony_ci *
53562306a36Sopenharmony_ci * Unhash an nfsd_file, try to get a reference to it, and then put that
53662306a36Sopenharmony_ci * reference. If it's the last reference, queue it to the dispose list.
53762306a36Sopenharmony_ci */
53862306a36Sopenharmony_cistatic void
53962306a36Sopenharmony_cinfsd_file_cond_queue(struct nfsd_file *nf, struct list_head *dispose)
54062306a36Sopenharmony_ci	__must_hold(RCU)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	int decrement = 1;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	/* If we raced with someone else unhashing, ignore it */
54562306a36Sopenharmony_ci	if (!nfsd_file_unhash(nf))
54662306a36Sopenharmony_ci		return;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	/* If we can't get a reference, ignore it */
54962306a36Sopenharmony_ci	if (!nfsd_file_get(nf))
55062306a36Sopenharmony_ci		return;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	/* Extra decrement if we remove from the LRU */
55362306a36Sopenharmony_ci	if (nfsd_file_lru_remove(nf))
55462306a36Sopenharmony_ci		++decrement;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	/* If refcount goes to 0, then put on the dispose list */
55762306a36Sopenharmony_ci	if (refcount_sub_and_test(decrement, &nf->nf_ref)) {
55862306a36Sopenharmony_ci		list_add(&nf->nf_lru, dispose);
55962306a36Sopenharmony_ci		trace_nfsd_file_closing(nf);
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci/**
56462306a36Sopenharmony_ci * nfsd_file_queue_for_close: try to close out any open nfsd_files for an inode
56562306a36Sopenharmony_ci * @inode:   inode on which to close out nfsd_files
56662306a36Sopenharmony_ci * @dispose: list on which to gather nfsd_files to close out
56762306a36Sopenharmony_ci *
56862306a36Sopenharmony_ci * An nfsd_file represents a struct file being held open on behalf of nfsd.
56962306a36Sopenharmony_ci * An open file however can block other activity (such as leases), or cause
57062306a36Sopenharmony_ci * undesirable behavior (e.g. spurious silly-renames when reexporting NFS).
57162306a36Sopenharmony_ci *
57262306a36Sopenharmony_ci * This function is intended to find open nfsd_files when this sort of
57362306a36Sopenharmony_ci * conflicting access occurs and then attempt to close those files out.
57462306a36Sopenharmony_ci *
57562306a36Sopenharmony_ci * Populates the dispose list with entries that have already had their
57662306a36Sopenharmony_ci * refcounts go to zero. The actual free of an nfsd_file can be expensive,
57762306a36Sopenharmony_ci * so we leave it up to the caller whether it wants to wait or not.
57862306a36Sopenharmony_ci */
57962306a36Sopenharmony_cistatic void
58062306a36Sopenharmony_cinfsd_file_queue_for_close(struct inode *inode, struct list_head *dispose)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	struct rhlist_head *tmp, *list;
58362306a36Sopenharmony_ci	struct nfsd_file *nf;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	rcu_read_lock();
58662306a36Sopenharmony_ci	list = rhltable_lookup(&nfsd_file_rhltable, &inode,
58762306a36Sopenharmony_ci			       nfsd_file_rhash_params);
58862306a36Sopenharmony_ci	rhl_for_each_entry_rcu(nf, tmp, list, nf_rlist) {
58962306a36Sopenharmony_ci		if (!test_bit(NFSD_FILE_GC, &nf->nf_flags))
59062306a36Sopenharmony_ci			continue;
59162306a36Sopenharmony_ci		nfsd_file_cond_queue(nf, dispose);
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ci	rcu_read_unlock();
59462306a36Sopenharmony_ci}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci/**
59762306a36Sopenharmony_ci * nfsd_file_close_inode - attempt a delayed close of a nfsd_file
59862306a36Sopenharmony_ci * @inode: inode of the file to attempt to remove
59962306a36Sopenharmony_ci *
60062306a36Sopenharmony_ci * Close out any open nfsd_files that can be reaped for @inode. The
60162306a36Sopenharmony_ci * actual freeing is deferred to the dispose_list_delayed infrastructure.
60262306a36Sopenharmony_ci *
60362306a36Sopenharmony_ci * This is used by the fsnotify callbacks and setlease notifier.
60462306a36Sopenharmony_ci */
60562306a36Sopenharmony_cistatic void
60662306a36Sopenharmony_cinfsd_file_close_inode(struct inode *inode)
60762306a36Sopenharmony_ci{
60862306a36Sopenharmony_ci	LIST_HEAD(dispose);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	nfsd_file_queue_for_close(inode, &dispose);
61162306a36Sopenharmony_ci	nfsd_file_dispose_list_delayed(&dispose);
61262306a36Sopenharmony_ci}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci/**
61562306a36Sopenharmony_ci * nfsd_file_close_inode_sync - attempt to forcibly close a nfsd_file
61662306a36Sopenharmony_ci * @inode: inode of the file to attempt to remove
61762306a36Sopenharmony_ci *
61862306a36Sopenharmony_ci * Close out any open nfsd_files that can be reaped for @inode. The
61962306a36Sopenharmony_ci * nfsd_files are closed out synchronously.
62062306a36Sopenharmony_ci *
62162306a36Sopenharmony_ci * This is called from nfsd_rename and nfsd_unlink to avoid silly-renames
62262306a36Sopenharmony_ci * when reexporting NFS.
62362306a36Sopenharmony_ci */
62462306a36Sopenharmony_civoid
62562306a36Sopenharmony_cinfsd_file_close_inode_sync(struct inode *inode)
62662306a36Sopenharmony_ci{
62762306a36Sopenharmony_ci	struct nfsd_file *nf;
62862306a36Sopenharmony_ci	LIST_HEAD(dispose);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	trace_nfsd_file_close(inode);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	nfsd_file_queue_for_close(inode, &dispose);
63362306a36Sopenharmony_ci	while (!list_empty(&dispose)) {
63462306a36Sopenharmony_ci		nf = list_first_entry(&dispose, struct nfsd_file, nf_lru);
63562306a36Sopenharmony_ci		list_del_init(&nf->nf_lru);
63662306a36Sopenharmony_ci		nfsd_file_free(nf);
63762306a36Sopenharmony_ci	}
63862306a36Sopenharmony_ci	flush_delayed_fput();
63962306a36Sopenharmony_ci}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci/**
64262306a36Sopenharmony_ci * nfsd_file_delayed_close - close unused nfsd_files
64362306a36Sopenharmony_ci * @work: dummy
64462306a36Sopenharmony_ci *
64562306a36Sopenharmony_ci * Scrape the freeme list for this nfsd_net, and then dispose of them
64662306a36Sopenharmony_ci * all.
64762306a36Sopenharmony_ci */
64862306a36Sopenharmony_cistatic void
64962306a36Sopenharmony_cinfsd_file_delayed_close(struct work_struct *work)
65062306a36Sopenharmony_ci{
65162306a36Sopenharmony_ci	LIST_HEAD(head);
65262306a36Sopenharmony_ci	struct nfsd_fcache_disposal *l = container_of(work,
65362306a36Sopenharmony_ci			struct nfsd_fcache_disposal, work);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	spin_lock(&l->lock);
65662306a36Sopenharmony_ci	list_splice_init(&l->freeme, &head);
65762306a36Sopenharmony_ci	spin_unlock(&l->lock);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	nfsd_file_dispose_list(&head);
66062306a36Sopenharmony_ci}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_cistatic int
66362306a36Sopenharmony_cinfsd_file_lease_notifier_call(struct notifier_block *nb, unsigned long arg,
66462306a36Sopenharmony_ci			    void *data)
66562306a36Sopenharmony_ci{
66662306a36Sopenharmony_ci	struct file_lock *fl = data;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	/* Only close files for F_SETLEASE leases */
66962306a36Sopenharmony_ci	if (fl->fl_flags & FL_LEASE)
67062306a36Sopenharmony_ci		nfsd_file_close_inode(file_inode(fl->fl_file));
67162306a36Sopenharmony_ci	return 0;
67262306a36Sopenharmony_ci}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_cistatic struct notifier_block nfsd_file_lease_notifier = {
67562306a36Sopenharmony_ci	.notifier_call = nfsd_file_lease_notifier_call,
67662306a36Sopenharmony_ci};
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_cistatic int
67962306a36Sopenharmony_cinfsd_file_fsnotify_handle_event(struct fsnotify_mark *mark, u32 mask,
68062306a36Sopenharmony_ci				struct inode *inode, struct inode *dir,
68162306a36Sopenharmony_ci				const struct qstr *name, u32 cookie)
68262306a36Sopenharmony_ci{
68362306a36Sopenharmony_ci	if (WARN_ON_ONCE(!inode))
68462306a36Sopenharmony_ci		return 0;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	trace_nfsd_file_fsnotify_handle_event(inode, mask);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	/* Should be no marks on non-regular files */
68962306a36Sopenharmony_ci	if (!S_ISREG(inode->i_mode)) {
69062306a36Sopenharmony_ci		WARN_ON_ONCE(1);
69162306a36Sopenharmony_ci		return 0;
69262306a36Sopenharmony_ci	}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	/* don't close files if this was not the last link */
69562306a36Sopenharmony_ci	if (mask & FS_ATTRIB) {
69662306a36Sopenharmony_ci		if (inode->i_nlink)
69762306a36Sopenharmony_ci			return 0;
69862306a36Sopenharmony_ci	}
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	nfsd_file_close_inode(inode);
70162306a36Sopenharmony_ci	return 0;
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_cistatic const struct fsnotify_ops nfsd_file_fsnotify_ops = {
70662306a36Sopenharmony_ci	.handle_inode_event = nfsd_file_fsnotify_handle_event,
70762306a36Sopenharmony_ci	.free_mark = nfsd_file_mark_free,
70862306a36Sopenharmony_ci};
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ciint
71162306a36Sopenharmony_cinfsd_file_cache_init(void)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	int ret;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	lockdep_assert_held(&nfsd_mutex);
71662306a36Sopenharmony_ci	if (test_and_set_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1)
71762306a36Sopenharmony_ci		return 0;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	ret = rhltable_init(&nfsd_file_rhltable, &nfsd_file_rhash_params);
72062306a36Sopenharmony_ci	if (ret)
72162306a36Sopenharmony_ci		return ret;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	ret = -ENOMEM;
72462306a36Sopenharmony_ci	nfsd_filecache_wq = alloc_workqueue("nfsd_filecache", 0, 0);
72562306a36Sopenharmony_ci	if (!nfsd_filecache_wq)
72662306a36Sopenharmony_ci		goto out;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	nfsd_file_slab = kmem_cache_create("nfsd_file",
72962306a36Sopenharmony_ci				sizeof(struct nfsd_file), 0, 0, NULL);
73062306a36Sopenharmony_ci	if (!nfsd_file_slab) {
73162306a36Sopenharmony_ci		pr_err("nfsd: unable to create nfsd_file_slab\n");
73262306a36Sopenharmony_ci		goto out_err;
73362306a36Sopenharmony_ci	}
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	nfsd_file_mark_slab = kmem_cache_create("nfsd_file_mark",
73662306a36Sopenharmony_ci					sizeof(struct nfsd_file_mark), 0, 0, NULL);
73762306a36Sopenharmony_ci	if (!nfsd_file_mark_slab) {
73862306a36Sopenharmony_ci		pr_err("nfsd: unable to create nfsd_file_mark_slab\n");
73962306a36Sopenharmony_ci		goto out_err;
74062306a36Sopenharmony_ci	}
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	ret = list_lru_init(&nfsd_file_lru);
74462306a36Sopenharmony_ci	if (ret) {
74562306a36Sopenharmony_ci		pr_err("nfsd: failed to init nfsd_file_lru: %d\n", ret);
74662306a36Sopenharmony_ci		goto out_err;
74762306a36Sopenharmony_ci	}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	ret = register_shrinker(&nfsd_file_shrinker, "nfsd-filecache");
75062306a36Sopenharmony_ci	if (ret) {
75162306a36Sopenharmony_ci		pr_err("nfsd: failed to register nfsd_file_shrinker: %d\n", ret);
75262306a36Sopenharmony_ci		goto out_lru;
75362306a36Sopenharmony_ci	}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	ret = lease_register_notifier(&nfsd_file_lease_notifier);
75662306a36Sopenharmony_ci	if (ret) {
75762306a36Sopenharmony_ci		pr_err("nfsd: unable to register lease notifier: %d\n", ret);
75862306a36Sopenharmony_ci		goto out_shrinker;
75962306a36Sopenharmony_ci	}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	nfsd_file_fsnotify_group = fsnotify_alloc_group(&nfsd_file_fsnotify_ops,
76262306a36Sopenharmony_ci							FSNOTIFY_GROUP_NOFS);
76362306a36Sopenharmony_ci	if (IS_ERR(nfsd_file_fsnotify_group)) {
76462306a36Sopenharmony_ci		pr_err("nfsd: unable to create fsnotify group: %ld\n",
76562306a36Sopenharmony_ci			PTR_ERR(nfsd_file_fsnotify_group));
76662306a36Sopenharmony_ci		ret = PTR_ERR(nfsd_file_fsnotify_group);
76762306a36Sopenharmony_ci		nfsd_file_fsnotify_group = NULL;
76862306a36Sopenharmony_ci		goto out_notifier;
76962306a36Sopenharmony_ci	}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	INIT_DELAYED_WORK(&nfsd_filecache_laundrette, nfsd_file_gc_worker);
77262306a36Sopenharmony_ciout:
77362306a36Sopenharmony_ci	return ret;
77462306a36Sopenharmony_ciout_notifier:
77562306a36Sopenharmony_ci	lease_unregister_notifier(&nfsd_file_lease_notifier);
77662306a36Sopenharmony_ciout_shrinker:
77762306a36Sopenharmony_ci	unregister_shrinker(&nfsd_file_shrinker);
77862306a36Sopenharmony_ciout_lru:
77962306a36Sopenharmony_ci	list_lru_destroy(&nfsd_file_lru);
78062306a36Sopenharmony_ciout_err:
78162306a36Sopenharmony_ci	kmem_cache_destroy(nfsd_file_slab);
78262306a36Sopenharmony_ci	nfsd_file_slab = NULL;
78362306a36Sopenharmony_ci	kmem_cache_destroy(nfsd_file_mark_slab);
78462306a36Sopenharmony_ci	nfsd_file_mark_slab = NULL;
78562306a36Sopenharmony_ci	destroy_workqueue(nfsd_filecache_wq);
78662306a36Sopenharmony_ci	nfsd_filecache_wq = NULL;
78762306a36Sopenharmony_ci	rhltable_destroy(&nfsd_file_rhltable);
78862306a36Sopenharmony_ci	goto out;
78962306a36Sopenharmony_ci}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci/**
79262306a36Sopenharmony_ci * __nfsd_file_cache_purge: clean out the cache for shutdown
79362306a36Sopenharmony_ci * @net: net-namespace to shut down the cache (may be NULL)
79462306a36Sopenharmony_ci *
79562306a36Sopenharmony_ci * Walk the nfsd_file cache and close out any that match @net. If @net is NULL,
79662306a36Sopenharmony_ci * then close out everything. Called when an nfsd instance is being shut down,
79762306a36Sopenharmony_ci * and when the exports table is flushed.
79862306a36Sopenharmony_ci */
79962306a36Sopenharmony_cistatic void
80062306a36Sopenharmony_ci__nfsd_file_cache_purge(struct net *net)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	struct rhashtable_iter iter;
80362306a36Sopenharmony_ci	struct nfsd_file *nf;
80462306a36Sopenharmony_ci	LIST_HEAD(dispose);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	rhltable_walk_enter(&nfsd_file_rhltable, &iter);
80762306a36Sopenharmony_ci	do {
80862306a36Sopenharmony_ci		rhashtable_walk_start(&iter);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci		nf = rhashtable_walk_next(&iter);
81162306a36Sopenharmony_ci		while (!IS_ERR_OR_NULL(nf)) {
81262306a36Sopenharmony_ci			if (!net || nf->nf_net == net)
81362306a36Sopenharmony_ci				nfsd_file_cond_queue(nf, &dispose);
81462306a36Sopenharmony_ci			nf = rhashtable_walk_next(&iter);
81562306a36Sopenharmony_ci		}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci		rhashtable_walk_stop(&iter);
81862306a36Sopenharmony_ci	} while (nf == ERR_PTR(-EAGAIN));
81962306a36Sopenharmony_ci	rhashtable_walk_exit(&iter);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	nfsd_file_dispose_list(&dispose);
82262306a36Sopenharmony_ci}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_cistatic struct nfsd_fcache_disposal *
82562306a36Sopenharmony_cinfsd_alloc_fcache_disposal(void)
82662306a36Sopenharmony_ci{
82762306a36Sopenharmony_ci	struct nfsd_fcache_disposal *l;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	l = kmalloc(sizeof(*l), GFP_KERNEL);
83062306a36Sopenharmony_ci	if (!l)
83162306a36Sopenharmony_ci		return NULL;
83262306a36Sopenharmony_ci	INIT_WORK(&l->work, nfsd_file_delayed_close);
83362306a36Sopenharmony_ci	spin_lock_init(&l->lock);
83462306a36Sopenharmony_ci	INIT_LIST_HEAD(&l->freeme);
83562306a36Sopenharmony_ci	return l;
83662306a36Sopenharmony_ci}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_cistatic void
83962306a36Sopenharmony_cinfsd_free_fcache_disposal(struct nfsd_fcache_disposal *l)
84062306a36Sopenharmony_ci{
84162306a36Sopenharmony_ci	cancel_work_sync(&l->work);
84262306a36Sopenharmony_ci	nfsd_file_dispose_list(&l->freeme);
84362306a36Sopenharmony_ci	kfree(l);
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_cistatic void
84762306a36Sopenharmony_cinfsd_free_fcache_disposal_net(struct net *net)
84862306a36Sopenharmony_ci{
84962306a36Sopenharmony_ci	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
85062306a36Sopenharmony_ci	struct nfsd_fcache_disposal *l = nn->fcache_disposal;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	nfsd_free_fcache_disposal(l);
85362306a36Sopenharmony_ci}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ciint
85662306a36Sopenharmony_cinfsd_file_cache_start_net(struct net *net)
85762306a36Sopenharmony_ci{
85862306a36Sopenharmony_ci	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	nn->fcache_disposal = nfsd_alloc_fcache_disposal();
86162306a36Sopenharmony_ci	return nn->fcache_disposal ? 0 : -ENOMEM;
86262306a36Sopenharmony_ci}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci/**
86562306a36Sopenharmony_ci * nfsd_file_cache_purge - Remove all cache items associated with @net
86662306a36Sopenharmony_ci * @net: target net namespace
86762306a36Sopenharmony_ci *
86862306a36Sopenharmony_ci */
86962306a36Sopenharmony_civoid
87062306a36Sopenharmony_cinfsd_file_cache_purge(struct net *net)
87162306a36Sopenharmony_ci{
87262306a36Sopenharmony_ci	lockdep_assert_held(&nfsd_mutex);
87362306a36Sopenharmony_ci	if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1)
87462306a36Sopenharmony_ci		__nfsd_file_cache_purge(net);
87562306a36Sopenharmony_ci}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_civoid
87862306a36Sopenharmony_cinfsd_file_cache_shutdown_net(struct net *net)
87962306a36Sopenharmony_ci{
88062306a36Sopenharmony_ci	nfsd_file_cache_purge(net);
88162306a36Sopenharmony_ci	nfsd_free_fcache_disposal_net(net);
88262306a36Sopenharmony_ci}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_civoid
88562306a36Sopenharmony_cinfsd_file_cache_shutdown(void)
88662306a36Sopenharmony_ci{
88762306a36Sopenharmony_ci	int i;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	lockdep_assert_held(&nfsd_mutex);
89062306a36Sopenharmony_ci	if (test_and_clear_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 0)
89162306a36Sopenharmony_ci		return;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	lease_unregister_notifier(&nfsd_file_lease_notifier);
89462306a36Sopenharmony_ci	unregister_shrinker(&nfsd_file_shrinker);
89562306a36Sopenharmony_ci	/*
89662306a36Sopenharmony_ci	 * make sure all callers of nfsd_file_lru_cb are done before
89762306a36Sopenharmony_ci	 * calling nfsd_file_cache_purge
89862306a36Sopenharmony_ci	 */
89962306a36Sopenharmony_ci	cancel_delayed_work_sync(&nfsd_filecache_laundrette);
90062306a36Sopenharmony_ci	__nfsd_file_cache_purge(NULL);
90162306a36Sopenharmony_ci	list_lru_destroy(&nfsd_file_lru);
90262306a36Sopenharmony_ci	rcu_barrier();
90362306a36Sopenharmony_ci	fsnotify_put_group(nfsd_file_fsnotify_group);
90462306a36Sopenharmony_ci	nfsd_file_fsnotify_group = NULL;
90562306a36Sopenharmony_ci	kmem_cache_destroy(nfsd_file_slab);
90662306a36Sopenharmony_ci	nfsd_file_slab = NULL;
90762306a36Sopenharmony_ci	fsnotify_wait_marks_destroyed();
90862306a36Sopenharmony_ci	kmem_cache_destroy(nfsd_file_mark_slab);
90962306a36Sopenharmony_ci	nfsd_file_mark_slab = NULL;
91062306a36Sopenharmony_ci	destroy_workqueue(nfsd_filecache_wq);
91162306a36Sopenharmony_ci	nfsd_filecache_wq = NULL;
91262306a36Sopenharmony_ci	rhltable_destroy(&nfsd_file_rhltable);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	for_each_possible_cpu(i) {
91562306a36Sopenharmony_ci		per_cpu(nfsd_file_cache_hits, i) = 0;
91662306a36Sopenharmony_ci		per_cpu(nfsd_file_acquisitions, i) = 0;
91762306a36Sopenharmony_ci		per_cpu(nfsd_file_releases, i) = 0;
91862306a36Sopenharmony_ci		per_cpu(nfsd_file_total_age, i) = 0;
91962306a36Sopenharmony_ci		per_cpu(nfsd_file_evictions, i) = 0;
92062306a36Sopenharmony_ci	}
92162306a36Sopenharmony_ci}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_cistatic struct nfsd_file *
92462306a36Sopenharmony_cinfsd_file_lookup_locked(const struct net *net, const struct cred *cred,
92562306a36Sopenharmony_ci			struct inode *inode, unsigned char need,
92662306a36Sopenharmony_ci			bool want_gc)
92762306a36Sopenharmony_ci{
92862306a36Sopenharmony_ci	struct rhlist_head *tmp, *list;
92962306a36Sopenharmony_ci	struct nfsd_file *nf;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	list = rhltable_lookup(&nfsd_file_rhltable, &inode,
93262306a36Sopenharmony_ci			       nfsd_file_rhash_params);
93362306a36Sopenharmony_ci	rhl_for_each_entry_rcu(nf, tmp, list, nf_rlist) {
93462306a36Sopenharmony_ci		if (nf->nf_may != need)
93562306a36Sopenharmony_ci			continue;
93662306a36Sopenharmony_ci		if (nf->nf_net != net)
93762306a36Sopenharmony_ci			continue;
93862306a36Sopenharmony_ci		if (!nfsd_match_cred(nf->nf_cred, cred))
93962306a36Sopenharmony_ci			continue;
94062306a36Sopenharmony_ci		if (test_bit(NFSD_FILE_GC, &nf->nf_flags) != want_gc)
94162306a36Sopenharmony_ci			continue;
94262306a36Sopenharmony_ci		if (test_bit(NFSD_FILE_HASHED, &nf->nf_flags) == 0)
94362306a36Sopenharmony_ci			continue;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci		if (!nfsd_file_get(nf))
94662306a36Sopenharmony_ci			continue;
94762306a36Sopenharmony_ci		return nf;
94862306a36Sopenharmony_ci	}
94962306a36Sopenharmony_ci	return NULL;
95062306a36Sopenharmony_ci}
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci/**
95362306a36Sopenharmony_ci * nfsd_file_is_cached - are there any cached open files for this inode?
95462306a36Sopenharmony_ci * @inode: inode to check
95562306a36Sopenharmony_ci *
95662306a36Sopenharmony_ci * The lookup matches inodes in all net namespaces and is atomic wrt
95762306a36Sopenharmony_ci * nfsd_file_acquire().
95862306a36Sopenharmony_ci *
95962306a36Sopenharmony_ci * Return values:
96062306a36Sopenharmony_ci *   %true: filecache contains at least one file matching this inode
96162306a36Sopenharmony_ci *   %false: filecache contains no files matching this inode
96262306a36Sopenharmony_ci */
96362306a36Sopenharmony_cibool
96462306a36Sopenharmony_cinfsd_file_is_cached(struct inode *inode)
96562306a36Sopenharmony_ci{
96662306a36Sopenharmony_ci	struct rhlist_head *tmp, *list;
96762306a36Sopenharmony_ci	struct nfsd_file *nf;
96862306a36Sopenharmony_ci	bool ret = false;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	rcu_read_lock();
97162306a36Sopenharmony_ci	list = rhltable_lookup(&nfsd_file_rhltable, &inode,
97262306a36Sopenharmony_ci			       nfsd_file_rhash_params);
97362306a36Sopenharmony_ci	rhl_for_each_entry_rcu(nf, tmp, list, nf_rlist)
97462306a36Sopenharmony_ci		if (test_bit(NFSD_FILE_GC, &nf->nf_flags)) {
97562306a36Sopenharmony_ci			ret = true;
97662306a36Sopenharmony_ci			break;
97762306a36Sopenharmony_ci		}
97862306a36Sopenharmony_ci	rcu_read_unlock();
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	trace_nfsd_file_is_cached(inode, (int)ret);
98162306a36Sopenharmony_ci	return ret;
98262306a36Sopenharmony_ci}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_cistatic __be32
98562306a36Sopenharmony_cinfsd_file_do_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
98662306a36Sopenharmony_ci		     unsigned int may_flags, struct file *file,
98762306a36Sopenharmony_ci		     struct nfsd_file **pnf, bool want_gc)
98862306a36Sopenharmony_ci{
98962306a36Sopenharmony_ci	unsigned char need = may_flags & NFSD_FILE_MAY_MASK;
99062306a36Sopenharmony_ci	struct net *net = SVC_NET(rqstp);
99162306a36Sopenharmony_ci	struct nfsd_file *new, *nf;
99262306a36Sopenharmony_ci	bool stale_retry = true;
99362306a36Sopenharmony_ci	bool open_retry = true;
99462306a36Sopenharmony_ci	struct inode *inode;
99562306a36Sopenharmony_ci	__be32 status;
99662306a36Sopenharmony_ci	int ret;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ciretry:
99962306a36Sopenharmony_ci	status = fh_verify(rqstp, fhp, S_IFREG,
100062306a36Sopenharmony_ci				may_flags|NFSD_MAY_OWNER_OVERRIDE);
100162306a36Sopenharmony_ci	if (status != nfs_ok)
100262306a36Sopenharmony_ci		return status;
100362306a36Sopenharmony_ci	inode = d_inode(fhp->fh_dentry);
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	rcu_read_lock();
100662306a36Sopenharmony_ci	nf = nfsd_file_lookup_locked(net, current_cred(), inode, need, want_gc);
100762306a36Sopenharmony_ci	rcu_read_unlock();
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	if (nf) {
101062306a36Sopenharmony_ci		/*
101162306a36Sopenharmony_ci		 * If the nf is on the LRU then it holds an extra reference
101262306a36Sopenharmony_ci		 * that must be put if it's removed. It had better not be
101362306a36Sopenharmony_ci		 * the last one however, since we should hold another.
101462306a36Sopenharmony_ci		 */
101562306a36Sopenharmony_ci		if (nfsd_file_lru_remove(nf))
101662306a36Sopenharmony_ci			WARN_ON_ONCE(refcount_dec_and_test(&nf->nf_ref));
101762306a36Sopenharmony_ci		goto wait_for_construction;
101862306a36Sopenharmony_ci	}
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	new = nfsd_file_alloc(net, inode, need, want_gc);
102162306a36Sopenharmony_ci	if (!new) {
102262306a36Sopenharmony_ci		status = nfserr_jukebox;
102362306a36Sopenharmony_ci		goto out;
102462306a36Sopenharmony_ci	}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	rcu_read_lock();
102762306a36Sopenharmony_ci	spin_lock(&inode->i_lock);
102862306a36Sopenharmony_ci	nf = nfsd_file_lookup_locked(net, current_cred(), inode, need, want_gc);
102962306a36Sopenharmony_ci	if (unlikely(nf)) {
103062306a36Sopenharmony_ci		spin_unlock(&inode->i_lock);
103162306a36Sopenharmony_ci		rcu_read_unlock();
103262306a36Sopenharmony_ci		nfsd_file_slab_free(&new->nf_rcu);
103362306a36Sopenharmony_ci		goto wait_for_construction;
103462306a36Sopenharmony_ci	}
103562306a36Sopenharmony_ci	nf = new;
103662306a36Sopenharmony_ci	ret = rhltable_insert(&nfsd_file_rhltable, &nf->nf_rlist,
103762306a36Sopenharmony_ci			      nfsd_file_rhash_params);
103862306a36Sopenharmony_ci	spin_unlock(&inode->i_lock);
103962306a36Sopenharmony_ci	rcu_read_unlock();
104062306a36Sopenharmony_ci	if (likely(ret == 0))
104162306a36Sopenharmony_ci		goto open_file;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	if (ret == -EEXIST)
104462306a36Sopenharmony_ci		goto retry;
104562306a36Sopenharmony_ci	trace_nfsd_file_insert_err(rqstp, inode, may_flags, ret);
104662306a36Sopenharmony_ci	status = nfserr_jukebox;
104762306a36Sopenharmony_ci	goto construction_err;
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ciwait_for_construction:
105062306a36Sopenharmony_ci	wait_on_bit(&nf->nf_flags, NFSD_FILE_PENDING, TASK_UNINTERRUPTIBLE);
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	/* Did construction of this file fail? */
105362306a36Sopenharmony_ci	if (!test_bit(NFSD_FILE_HASHED, &nf->nf_flags)) {
105462306a36Sopenharmony_ci		trace_nfsd_file_cons_err(rqstp, inode, may_flags, nf);
105562306a36Sopenharmony_ci		if (!open_retry) {
105662306a36Sopenharmony_ci			status = nfserr_jukebox;
105762306a36Sopenharmony_ci			goto construction_err;
105862306a36Sopenharmony_ci		}
105962306a36Sopenharmony_ci		open_retry = false;
106062306a36Sopenharmony_ci		fh_put(fhp);
106162306a36Sopenharmony_ci		goto retry;
106262306a36Sopenharmony_ci	}
106362306a36Sopenharmony_ci	this_cpu_inc(nfsd_file_cache_hits);
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	status = nfserrno(nfsd_open_break_lease(file_inode(nf->nf_file), may_flags));
106662306a36Sopenharmony_ci	if (status != nfs_ok) {
106762306a36Sopenharmony_ci		nfsd_file_put(nf);
106862306a36Sopenharmony_ci		nf = NULL;
106962306a36Sopenharmony_ci	}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ciout:
107262306a36Sopenharmony_ci	if (status == nfs_ok) {
107362306a36Sopenharmony_ci		this_cpu_inc(nfsd_file_acquisitions);
107462306a36Sopenharmony_ci		nfsd_file_check_write_error(nf);
107562306a36Sopenharmony_ci		*pnf = nf;
107662306a36Sopenharmony_ci	}
107762306a36Sopenharmony_ci	trace_nfsd_file_acquire(rqstp, inode, may_flags, nf, status);
107862306a36Sopenharmony_ci	return status;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ciopen_file:
108162306a36Sopenharmony_ci	trace_nfsd_file_alloc(nf);
108262306a36Sopenharmony_ci	nf->nf_mark = nfsd_file_mark_find_or_create(nf, inode);
108362306a36Sopenharmony_ci	if (nf->nf_mark) {
108462306a36Sopenharmony_ci		if (file) {
108562306a36Sopenharmony_ci			get_file(file);
108662306a36Sopenharmony_ci			nf->nf_file = file;
108762306a36Sopenharmony_ci			status = nfs_ok;
108862306a36Sopenharmony_ci			trace_nfsd_file_opened(nf, status);
108962306a36Sopenharmony_ci		} else {
109062306a36Sopenharmony_ci			ret = nfsd_open_verified(rqstp, fhp, may_flags,
109162306a36Sopenharmony_ci						 &nf->nf_file);
109262306a36Sopenharmony_ci			if (ret == -EOPENSTALE && stale_retry) {
109362306a36Sopenharmony_ci				stale_retry = false;
109462306a36Sopenharmony_ci				nfsd_file_unhash(nf);
109562306a36Sopenharmony_ci				clear_and_wake_up_bit(NFSD_FILE_PENDING,
109662306a36Sopenharmony_ci						      &nf->nf_flags);
109762306a36Sopenharmony_ci				if (refcount_dec_and_test(&nf->nf_ref))
109862306a36Sopenharmony_ci					nfsd_file_free(nf);
109962306a36Sopenharmony_ci				nf = NULL;
110062306a36Sopenharmony_ci				fh_put(fhp);
110162306a36Sopenharmony_ci				goto retry;
110262306a36Sopenharmony_ci			}
110362306a36Sopenharmony_ci			status = nfserrno(ret);
110462306a36Sopenharmony_ci			trace_nfsd_file_open(nf, status);
110562306a36Sopenharmony_ci		}
110662306a36Sopenharmony_ci	} else
110762306a36Sopenharmony_ci		status = nfserr_jukebox;
110862306a36Sopenharmony_ci	/*
110962306a36Sopenharmony_ci	 * If construction failed, or we raced with a call to unlink()
111062306a36Sopenharmony_ci	 * then unhash.
111162306a36Sopenharmony_ci	 */
111262306a36Sopenharmony_ci	if (status != nfs_ok || inode->i_nlink == 0)
111362306a36Sopenharmony_ci		nfsd_file_unhash(nf);
111462306a36Sopenharmony_ci	clear_and_wake_up_bit(NFSD_FILE_PENDING, &nf->nf_flags);
111562306a36Sopenharmony_ci	if (status == nfs_ok)
111662306a36Sopenharmony_ci		goto out;
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ciconstruction_err:
111962306a36Sopenharmony_ci	if (refcount_dec_and_test(&nf->nf_ref))
112062306a36Sopenharmony_ci		nfsd_file_free(nf);
112162306a36Sopenharmony_ci	nf = NULL;
112262306a36Sopenharmony_ci	goto out;
112362306a36Sopenharmony_ci}
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci/**
112662306a36Sopenharmony_ci * nfsd_file_acquire_gc - Get a struct nfsd_file with an open file
112762306a36Sopenharmony_ci * @rqstp: the RPC transaction being executed
112862306a36Sopenharmony_ci * @fhp: the NFS filehandle of the file to be opened
112962306a36Sopenharmony_ci * @may_flags: NFSD_MAY_ settings for the file
113062306a36Sopenharmony_ci * @pnf: OUT: new or found "struct nfsd_file" object
113162306a36Sopenharmony_ci *
113262306a36Sopenharmony_ci * The nfsd_file object returned by this API is reference-counted
113362306a36Sopenharmony_ci * and garbage-collected. The object is retained for a few
113462306a36Sopenharmony_ci * seconds after the final nfsd_file_put() in case the caller
113562306a36Sopenharmony_ci * wants to re-use it.
113662306a36Sopenharmony_ci *
113762306a36Sopenharmony_ci * Return values:
113862306a36Sopenharmony_ci *   %nfs_ok - @pnf points to an nfsd_file with its reference
113962306a36Sopenharmony_ci *   count boosted.
114062306a36Sopenharmony_ci *
114162306a36Sopenharmony_ci * On error, an nfsstat value in network byte order is returned.
114262306a36Sopenharmony_ci */
114362306a36Sopenharmony_ci__be32
114462306a36Sopenharmony_cinfsd_file_acquire_gc(struct svc_rqst *rqstp, struct svc_fh *fhp,
114562306a36Sopenharmony_ci		     unsigned int may_flags, struct nfsd_file **pnf)
114662306a36Sopenharmony_ci{
114762306a36Sopenharmony_ci	return nfsd_file_do_acquire(rqstp, fhp, may_flags, NULL, pnf, true);
114862306a36Sopenharmony_ci}
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci/**
115162306a36Sopenharmony_ci * nfsd_file_acquire - Get a struct nfsd_file with an open file
115262306a36Sopenharmony_ci * @rqstp: the RPC transaction being executed
115362306a36Sopenharmony_ci * @fhp: the NFS filehandle of the file to be opened
115462306a36Sopenharmony_ci * @may_flags: NFSD_MAY_ settings for the file
115562306a36Sopenharmony_ci * @pnf: OUT: new or found "struct nfsd_file" object
115662306a36Sopenharmony_ci *
115762306a36Sopenharmony_ci * The nfsd_file_object returned by this API is reference-counted
115862306a36Sopenharmony_ci * but not garbage-collected. The object is unhashed after the
115962306a36Sopenharmony_ci * final nfsd_file_put().
116062306a36Sopenharmony_ci *
116162306a36Sopenharmony_ci * Return values:
116262306a36Sopenharmony_ci *   %nfs_ok - @pnf points to an nfsd_file with its reference
116362306a36Sopenharmony_ci *   count boosted.
116462306a36Sopenharmony_ci *
116562306a36Sopenharmony_ci * On error, an nfsstat value in network byte order is returned.
116662306a36Sopenharmony_ci */
116762306a36Sopenharmony_ci__be32
116862306a36Sopenharmony_cinfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
116962306a36Sopenharmony_ci		  unsigned int may_flags, struct nfsd_file **pnf)
117062306a36Sopenharmony_ci{
117162306a36Sopenharmony_ci	return nfsd_file_do_acquire(rqstp, fhp, may_flags, NULL, pnf, false);
117262306a36Sopenharmony_ci}
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci/**
117562306a36Sopenharmony_ci * nfsd_file_acquire_opened - Get a struct nfsd_file using existing open file
117662306a36Sopenharmony_ci * @rqstp: the RPC transaction being executed
117762306a36Sopenharmony_ci * @fhp: the NFS filehandle of the file just created
117862306a36Sopenharmony_ci * @may_flags: NFSD_MAY_ settings for the file
117962306a36Sopenharmony_ci * @file: cached, already-open file (may be NULL)
118062306a36Sopenharmony_ci * @pnf: OUT: new or found "struct nfsd_file" object
118162306a36Sopenharmony_ci *
118262306a36Sopenharmony_ci * Acquire a nfsd_file object that is not GC'ed. If one doesn't already exist,
118362306a36Sopenharmony_ci * and @file is non-NULL, use it to instantiate a new nfsd_file instead of
118462306a36Sopenharmony_ci * opening a new one.
118562306a36Sopenharmony_ci *
118662306a36Sopenharmony_ci * Return values:
118762306a36Sopenharmony_ci *   %nfs_ok - @pnf points to an nfsd_file with its reference
118862306a36Sopenharmony_ci *   count boosted.
118962306a36Sopenharmony_ci *
119062306a36Sopenharmony_ci * On error, an nfsstat value in network byte order is returned.
119162306a36Sopenharmony_ci */
119262306a36Sopenharmony_ci__be32
119362306a36Sopenharmony_cinfsd_file_acquire_opened(struct svc_rqst *rqstp, struct svc_fh *fhp,
119462306a36Sopenharmony_ci			 unsigned int may_flags, struct file *file,
119562306a36Sopenharmony_ci			 struct nfsd_file **pnf)
119662306a36Sopenharmony_ci{
119762306a36Sopenharmony_ci	return nfsd_file_do_acquire(rqstp, fhp, may_flags, file, pnf, false);
119862306a36Sopenharmony_ci}
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci/*
120162306a36Sopenharmony_ci * Note that fields may be added, removed or reordered in the future. Programs
120262306a36Sopenharmony_ci * scraping this file for info should test the labels to ensure they're
120362306a36Sopenharmony_ci * getting the correct field.
120462306a36Sopenharmony_ci */
120562306a36Sopenharmony_ciint nfsd_file_cache_stats_show(struct seq_file *m, void *v)
120662306a36Sopenharmony_ci{
120762306a36Sopenharmony_ci	unsigned long releases = 0, evictions = 0;
120862306a36Sopenharmony_ci	unsigned long hits = 0, acquisitions = 0;
120962306a36Sopenharmony_ci	unsigned int i, count = 0, buckets = 0;
121062306a36Sopenharmony_ci	unsigned long lru = 0, total_age = 0;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	/* Serialize with server shutdown */
121362306a36Sopenharmony_ci	mutex_lock(&nfsd_mutex);
121462306a36Sopenharmony_ci	if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1) {
121562306a36Sopenharmony_ci		struct bucket_table *tbl;
121662306a36Sopenharmony_ci		struct rhashtable *ht;
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci		lru = list_lru_count(&nfsd_file_lru);
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci		rcu_read_lock();
122162306a36Sopenharmony_ci		ht = &nfsd_file_rhltable.ht;
122262306a36Sopenharmony_ci		count = atomic_read(&ht->nelems);
122362306a36Sopenharmony_ci		tbl = rht_dereference_rcu(ht->tbl, ht);
122462306a36Sopenharmony_ci		buckets = tbl->size;
122562306a36Sopenharmony_ci		rcu_read_unlock();
122662306a36Sopenharmony_ci	}
122762306a36Sopenharmony_ci	mutex_unlock(&nfsd_mutex);
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	for_each_possible_cpu(i) {
123062306a36Sopenharmony_ci		hits += per_cpu(nfsd_file_cache_hits, i);
123162306a36Sopenharmony_ci		acquisitions += per_cpu(nfsd_file_acquisitions, i);
123262306a36Sopenharmony_ci		releases += per_cpu(nfsd_file_releases, i);
123362306a36Sopenharmony_ci		total_age += per_cpu(nfsd_file_total_age, i);
123462306a36Sopenharmony_ci		evictions += per_cpu(nfsd_file_evictions, i);
123562306a36Sopenharmony_ci	}
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	seq_printf(m, "total inodes:  %u\n", count);
123862306a36Sopenharmony_ci	seq_printf(m, "hash buckets:  %u\n", buckets);
123962306a36Sopenharmony_ci	seq_printf(m, "lru entries:   %lu\n", lru);
124062306a36Sopenharmony_ci	seq_printf(m, "cache hits:    %lu\n", hits);
124162306a36Sopenharmony_ci	seq_printf(m, "acquisitions:  %lu\n", acquisitions);
124262306a36Sopenharmony_ci	seq_printf(m, "releases:      %lu\n", releases);
124362306a36Sopenharmony_ci	seq_printf(m, "evictions:     %lu\n", evictions);
124462306a36Sopenharmony_ci	if (releases)
124562306a36Sopenharmony_ci		seq_printf(m, "mean age (ms): %ld\n", total_age / releases);
124662306a36Sopenharmony_ci	else
124762306a36Sopenharmony_ci		seq_printf(m, "mean age (ms): -\n");
124862306a36Sopenharmony_ci	return 0;
124962306a36Sopenharmony_ci}
1250