13f054211SChuck Lever // SPDX-License-Identifier: GPL-2.0
265294c1fSJeff Layton /*
33f054211SChuck Lever * The NFSD open file cache.
465294c1fSJeff Layton *
565294c1fSJeff Layton * (c) 2015 - Jeff Layton <jeff.layton@primarydata.com>
6b3276c1fSChuck Lever *
7b3276c1fSChuck Lever * An nfsd_file object is a per-file collection of open state that binds
8b3276c1fSChuck Lever * together:
9b3276c1fSChuck Lever * - a struct file *
10b3276c1fSChuck Lever * - a user credential
11b3276c1fSChuck Lever * - a network namespace
12b3276c1fSChuck Lever * - a read-ahead context
13b3276c1fSChuck Lever * - monitoring for writeback errors
14b3276c1fSChuck Lever *
15b3276c1fSChuck Lever * nfsd_file objects are reference-counted. Consumers acquire a new
16b3276c1fSChuck Lever * object via the nfsd_file_acquire API. They manage their interest in
17b3276c1fSChuck Lever * the acquired object, and hence the object's reference count, via
18b3276c1fSChuck Lever * nfsd_file_get and nfsd_file_put. There are two varieties of nfsd_file
19b3276c1fSChuck Lever * object:
20b3276c1fSChuck Lever *
21b3276c1fSChuck Lever * * non-garbage-collected: When a consumer wants to precisely control
22b3276c1fSChuck Lever * the lifetime of a file's open state, it acquires a non-garbage-
23b3276c1fSChuck Lever * collected nfsd_file. The final nfsd_file_put releases the open
24b3276c1fSChuck Lever * state immediately.
25b3276c1fSChuck Lever *
26b3276c1fSChuck Lever * * garbage-collected: When a consumer does not control the lifetime
27b3276c1fSChuck Lever * of open state, it acquires a garbage-collected nfsd_file. The
28b3276c1fSChuck Lever * final nfsd_file_put allows the open state to linger for a period
29b3276c1fSChuck Lever * during which it may be re-used.
3065294c1fSJeff Layton */
3165294c1fSJeff Layton
3265294c1fSJeff Layton #include <linux/hash.h>
3365294c1fSJeff Layton #include <linux/slab.h>
3465294c1fSJeff Layton #include <linux/file.h>
35cbcc268bSMatthew Wilcox (Oracle) #include <linux/pagemap.h>
3665294c1fSJeff Layton #include <linux/sched.h>
3765294c1fSJeff Layton #include <linux/list_lru.h>
3865294c1fSJeff Layton #include <linux/fsnotify_backend.h>
3965294c1fSJeff Layton #include <linux/fsnotify.h>
4065294c1fSJeff Layton #include <linux/seq_file.h>
41fc22945eSChuck Lever #include <linux/rhashtable.h>
4265294c1fSJeff Layton
4365294c1fSJeff Layton #include "vfs.h"
4465294c1fSJeff Layton #include "nfsd.h"
4565294c1fSJeff Layton #include "nfsfh.h"
465e113224STrond Myklebust #include "netns.h"
4765294c1fSJeff Layton #include "filecache.h"
4865294c1fSJeff Layton #include "trace.h"
4965294c1fSJeff Layton
5065294c1fSJeff Layton #define NFSD_LAUNDRETTE_DELAY (2 * HZ)
5165294c1fSJeff Layton
52c7b824c3SChuck Lever #define NFSD_FILE_CACHE_UP (0)
5365294c1fSJeff Layton
5465294c1fSJeff Layton /* We only care about NFSD_MAY_READ/WRITE for this cache */
5565294c1fSJeff Layton #define NFSD_FILE_MAY_MASK (NFSD_MAY_READ|NFSD_MAY_WRITE)
5665294c1fSJeff Layton
5765294c1fSJeff Layton static DEFINE_PER_CPU(unsigned long, nfsd_file_cache_hits);
5829d4bdbbSChuck Lever static DEFINE_PER_CPU(unsigned long, nfsd_file_acquisitions);
59d6329327SChuck Lever static DEFINE_PER_CPU(unsigned long, nfsd_file_releases);
60904940e9SChuck Lever static DEFINE_PER_CPU(unsigned long, nfsd_file_total_age);
6194660cc1SChuck Lever static DEFINE_PER_CPU(unsigned long, nfsd_file_evictions);
6265294c1fSJeff Layton
639542e6a6STrond Myklebust struct nfsd_fcache_disposal {
649542e6a6STrond Myklebust struct work_struct work;
659542e6a6STrond Myklebust spinlock_t lock;
669542e6a6STrond Myklebust struct list_head freeme;
679542e6a6STrond Myklebust };
689542e6a6STrond Myklebust
6950d0def9SChen Zhou static struct workqueue_struct *nfsd_filecache_wq __read_mostly;
709542e6a6STrond Myklebust
7165294c1fSJeff Layton static struct kmem_cache *nfsd_file_slab;
7265294c1fSJeff Layton static struct kmem_cache *nfsd_file_mark_slab;
7365294c1fSJeff Layton static struct list_lru nfsd_file_lru;
74c7b824c3SChuck Lever static unsigned long nfsd_file_flags;
7565294c1fSJeff Layton static struct fsnotify_group *nfsd_file_fsnotify_group;
7665294c1fSJeff Layton static struct delayed_work nfsd_filecache_laundrette;
77c4c649abSChuck Lever static struct rhltable nfsd_file_rhltable
78fc22945eSChuck Lever ____cacheline_aligned_in_smp;
7965294c1fSJeff Layton
80fc22945eSChuck Lever static bool
nfsd_match_cred(const struct cred * c1,const struct cred * c2)81fc22945eSChuck Lever nfsd_match_cred(const struct cred *c1, const struct cred *c2)
82fc22945eSChuck Lever {
83fc22945eSChuck Lever int i;
84fc22945eSChuck Lever
85fc22945eSChuck Lever if (!uid_eq(c1->fsuid, c2->fsuid))
86fc22945eSChuck Lever return false;
87fc22945eSChuck Lever if (!gid_eq(c1->fsgid, c2->fsgid))
88fc22945eSChuck Lever return false;
89fc22945eSChuck Lever if (c1->group_info == NULL || c2->group_info == NULL)
90fc22945eSChuck Lever return c1->group_info == c2->group_info;
91fc22945eSChuck Lever if (c1->group_info->ngroups != c2->group_info->ngroups)
92fc22945eSChuck Lever return false;
93fc22945eSChuck Lever for (i = 0; i < c1->group_info->ngroups; i++) {
94fc22945eSChuck Lever if (!gid_eq(c1->group_info->gid[i], c2->group_info->gid[i]))
95fc22945eSChuck Lever return false;
96fc22945eSChuck Lever }
97fc22945eSChuck Lever return true;
98fc22945eSChuck Lever }
99fc22945eSChuck Lever
100fc22945eSChuck Lever static const struct rhashtable_params nfsd_file_rhash_params = {
101fc22945eSChuck Lever .key_len = sizeof_field(struct nfsd_file, nf_inode),
102fc22945eSChuck Lever .key_offset = offsetof(struct nfsd_file, nf_inode),
103c4c649abSChuck Lever .head_offset = offsetof(struct nfsd_file, nf_rlist),
104c4c649abSChuck Lever
105c4c649abSChuck Lever /*
106c4c649abSChuck Lever * Start with a single page hash table to reduce resizing churn
107c4c649abSChuck Lever * on light workloads.
108c4c649abSChuck Lever */
109c4c649abSChuck Lever .min_size = 256,
110fc22945eSChuck Lever .automatic_shrinking = true,
111fc22945eSChuck Lever };
11265294c1fSJeff Layton
11365294c1fSJeff Layton static void
nfsd_file_schedule_laundrette(void)1149542e6a6STrond Myklebust nfsd_file_schedule_laundrette(void)
11565294c1fSJeff Layton {
11622ae4c11SJeff Layton if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags))
1179542e6a6STrond Myklebust queue_delayed_work(system_wq, &nfsd_filecache_laundrette,
1189542e6a6STrond Myklebust NFSD_LAUNDRETTE_DELAY);
11965294c1fSJeff Layton }
12065294c1fSJeff Layton
12165294c1fSJeff Layton static void
nfsd_file_slab_free(struct rcu_head * rcu)12265294c1fSJeff Layton nfsd_file_slab_free(struct rcu_head *rcu)
12365294c1fSJeff Layton {
12465294c1fSJeff Layton struct nfsd_file *nf = container_of(rcu, struct nfsd_file, nf_rcu);
12565294c1fSJeff Layton
12665294c1fSJeff Layton put_cred(nf->nf_cred);
12765294c1fSJeff Layton kmem_cache_free(nfsd_file_slab, nf);
12865294c1fSJeff Layton }
12965294c1fSJeff Layton
13065294c1fSJeff Layton static void
nfsd_file_mark_free(struct fsnotify_mark * mark)13165294c1fSJeff Layton nfsd_file_mark_free(struct fsnotify_mark *mark)
13265294c1fSJeff Layton {
13365294c1fSJeff Layton struct nfsd_file_mark *nfm = container_of(mark, struct nfsd_file_mark,
13465294c1fSJeff Layton nfm_mark);
13565294c1fSJeff Layton
13665294c1fSJeff Layton kmem_cache_free(nfsd_file_mark_slab, nfm);
13765294c1fSJeff Layton }
13865294c1fSJeff Layton
13965294c1fSJeff Layton static struct nfsd_file_mark *
nfsd_file_mark_get(struct nfsd_file_mark * nfm)14065294c1fSJeff Layton nfsd_file_mark_get(struct nfsd_file_mark *nfm)
14165294c1fSJeff Layton {
142689827cdSTrond Myklebust if (!refcount_inc_not_zero(&nfm->nfm_ref))
14365294c1fSJeff Layton return NULL;
14465294c1fSJeff Layton return nfm;
14565294c1fSJeff Layton }
14665294c1fSJeff Layton
14765294c1fSJeff Layton static void
nfsd_file_mark_put(struct nfsd_file_mark * nfm)14865294c1fSJeff Layton nfsd_file_mark_put(struct nfsd_file_mark *nfm)
14965294c1fSJeff Layton {
150689827cdSTrond Myklebust if (refcount_dec_and_test(&nfm->nfm_ref)) {
15165294c1fSJeff Layton fsnotify_destroy_mark(&nfm->nfm_mark, nfsd_file_fsnotify_group);
15265294c1fSJeff Layton fsnotify_put_mark(&nfm->nfm_mark);
15365294c1fSJeff Layton }
15465294c1fSJeff Layton }
15565294c1fSJeff Layton
15665294c1fSJeff Layton static struct nfsd_file_mark *
nfsd_file_mark_find_or_create(struct nfsd_file * nf,struct inode * inode)157427f5f83SChuck Lever nfsd_file_mark_find_or_create(struct nfsd_file *nf, struct inode *inode)
15865294c1fSJeff Layton {
15965294c1fSJeff Layton int err;
16065294c1fSJeff Layton struct fsnotify_mark *mark;
16165294c1fSJeff Layton struct nfsd_file_mark *nfm = NULL, *new;
16265294c1fSJeff Layton
16365294c1fSJeff Layton do {
164b8962a9dSAmir Goldstein fsnotify_group_lock(nfsd_file_fsnotify_group);
16565294c1fSJeff Layton mark = fsnotify_find_mark(&inode->i_fsnotify_marks,
16665294c1fSJeff Layton nfsd_file_fsnotify_group);
16765294c1fSJeff Layton if (mark) {
16865294c1fSJeff Layton nfm = nfsd_file_mark_get(container_of(mark,
16965294c1fSJeff Layton struct nfsd_file_mark,
17065294c1fSJeff Layton nfm_mark));
171b8962a9dSAmir Goldstein fsnotify_group_unlock(nfsd_file_fsnotify_group);
17290d2f1daSTrond Myklebust if (nfm) {
17365294c1fSJeff Layton fsnotify_put_mark(mark);
17465294c1fSJeff Layton break;
17590d2f1daSTrond Myklebust }
17690d2f1daSTrond Myklebust /* Avoid soft lockup race with nfsd_file_mark_put() */
17790d2f1daSTrond Myklebust fsnotify_destroy_mark(mark, nfsd_file_fsnotify_group);
17890d2f1daSTrond Myklebust fsnotify_put_mark(mark);
179b8962a9dSAmir Goldstein } else {
180b8962a9dSAmir Goldstein fsnotify_group_unlock(nfsd_file_fsnotify_group);
181b8962a9dSAmir Goldstein }
18265294c1fSJeff Layton
18365294c1fSJeff Layton /* allocate a new nfm */
18465294c1fSJeff Layton new = kmem_cache_alloc(nfsd_file_mark_slab, GFP_KERNEL);
18565294c1fSJeff Layton if (!new)
18665294c1fSJeff Layton return NULL;
18765294c1fSJeff Layton fsnotify_init_mark(&new->nfm_mark, nfsd_file_fsnotify_group);
18865294c1fSJeff Layton new->nfm_mark.mask = FS_ATTRIB|FS_DELETE_SELF;
189689827cdSTrond Myklebust refcount_set(&new->nfm_ref, 1);
19065294c1fSJeff Layton
19165294c1fSJeff Layton err = fsnotify_add_inode_mark(&new->nfm_mark, inode, 0);
19265294c1fSJeff Layton
19365294c1fSJeff Layton /*
19465294c1fSJeff Layton * If the add was successful, then return the object.
19565294c1fSJeff Layton * Otherwise, we need to put the reference we hold on the
19665294c1fSJeff Layton * nfm_mark. The fsnotify code will take a reference and put
19765294c1fSJeff Layton * it on failure, so we can't just free it directly. It's also
19865294c1fSJeff Layton * not safe to call fsnotify_destroy_mark on it as the
19965294c1fSJeff Layton * mark->group will be NULL. Thus, we can't let the nfm_ref
20065294c1fSJeff Layton * counter drive the destruction at this point.
20165294c1fSJeff Layton */
20265294c1fSJeff Layton if (likely(!err))
20365294c1fSJeff Layton nfm = new;
20465294c1fSJeff Layton else
20565294c1fSJeff Layton fsnotify_put_mark(&new->nfm_mark);
20665294c1fSJeff Layton } while (unlikely(err == -EEXIST));
20765294c1fSJeff Layton
20865294c1fSJeff Layton return nfm;
20965294c1fSJeff Layton }
21065294c1fSJeff Layton
21165294c1fSJeff Layton static struct nfsd_file *
nfsd_file_alloc(struct net * net,struct inode * inode,unsigned char need,bool want_gc)212c4c649abSChuck Lever nfsd_file_alloc(struct net *net, struct inode *inode, unsigned char need,
213c4c649abSChuck Lever bool want_gc)
21465294c1fSJeff Layton {
21565294c1fSJeff Layton struct nfsd_file *nf;
21665294c1fSJeff Layton
21765294c1fSJeff Layton nf = kmem_cache_alloc(nfsd_file_slab, GFP_KERNEL);
218c4c649abSChuck Lever if (unlikely(!nf))
219c4c649abSChuck Lever return NULL;
220c4c649abSChuck Lever
22165294c1fSJeff Layton INIT_LIST_HEAD(&nf->nf_lru);
222904940e9SChuck Lever nf->nf_birthtime = ktime_get();
22365294c1fSJeff Layton nf->nf_file = NULL;
22465294c1fSJeff Layton nf->nf_cred = get_current_cred();
225c4c649abSChuck Lever nf->nf_net = net;
226c4c649abSChuck Lever nf->nf_flags = want_gc ?
227c4c649abSChuck Lever BIT(NFSD_FILE_HASHED) | BIT(NFSD_FILE_PENDING) | BIT(NFSD_FILE_GC) :
228c4c649abSChuck Lever BIT(NFSD_FILE_HASHED) | BIT(NFSD_FILE_PENDING);
229c4c649abSChuck Lever nf->nf_inode = inode;
230ac3a2585SJeff Layton refcount_set(&nf->nf_ref, 1);
231c4c649abSChuck Lever nf->nf_may = need;
23265294c1fSJeff Layton nf->nf_mark = NULL;
23365294c1fSJeff Layton return nf;
23465294c1fSJeff Layton }
23565294c1fSJeff Layton
2364c475eeeSJeff Layton /**
2374c475eeeSJeff Layton * nfsd_file_check_write_error - check for writeback errors on a file
2384c475eeeSJeff Layton * @nf: nfsd_file to check for writeback errors
2394c475eeeSJeff Layton *
2404c475eeeSJeff Layton * Check whether a nfsd_file has an unseen error. Reset the write
2414c475eeeSJeff Layton * verifier if so.
2424c475eeeSJeff Layton */
24382141185SJeff Layton static void
nfsd_file_check_write_error(struct nfsd_file * nf)24482141185SJeff Layton nfsd_file_check_write_error(struct nfsd_file *nf)
24582141185SJeff Layton {
24682141185SJeff Layton struct file *file = nf->nf_file;
24782141185SJeff Layton
2484c475eeeSJeff Layton if ((file->f_mode & FMODE_WRITE) &&
2494c475eeeSJeff Layton filemap_check_wb_err(file->f_mapping, READ_ONCE(file->f_wb_err)))
2504c475eeeSJeff Layton nfsd_reset_write_verifier(net_generic(nf->nf_net, nfsd_net_id));
25182141185SJeff Layton }
25282141185SJeff Layton
25382141185SJeff Layton static void
nfsd_file_hash_remove(struct nfsd_file * nf)25482141185SJeff Layton nfsd_file_hash_remove(struct nfsd_file *nf)
25582141185SJeff Layton {
25682141185SJeff Layton trace_nfsd_file_unhash(nf);
257c4c649abSChuck Lever rhltable_remove(&nfsd_file_rhltable, &nf->nf_rlist,
25882141185SJeff Layton nfsd_file_rhash_params);
25982141185SJeff Layton }
26082141185SJeff Layton
26182141185SJeff Layton static bool
nfsd_file_unhash(struct nfsd_file * nf)26282141185SJeff Layton nfsd_file_unhash(struct nfsd_file *nf)
26382141185SJeff Layton {
26482141185SJeff Layton if (test_and_clear_bit(NFSD_FILE_HASHED, &nf->nf_flags)) {
26582141185SJeff Layton nfsd_file_hash_remove(nf);
26682141185SJeff Layton return true;
26782141185SJeff Layton }
26882141185SJeff Layton return false;
26982141185SJeff Layton }
27082141185SJeff Layton
271ac3a2585SJeff Layton static void
nfsd_file_free(struct nfsd_file * nf)27265294c1fSJeff Layton nfsd_file_free(struct nfsd_file *nf)
27365294c1fSJeff Layton {
274904940e9SChuck Lever s64 age = ktime_to_ms(ktime_sub(ktime_get(), nf->nf_birthtime));
27565294c1fSJeff Layton
27682141185SJeff Layton trace_nfsd_file_free(nf);
27782141185SJeff Layton
278d6329327SChuck Lever this_cpu_inc(nfsd_file_releases);
279904940e9SChuck Lever this_cpu_add(nfsd_file_total_age, age);
280d6329327SChuck Lever
281ac3a2585SJeff Layton nfsd_file_unhash(nf);
28265294c1fSJeff Layton if (nf->nf_mark)
28365294c1fSJeff Layton nfsd_file_mark_put(nf->nf_mark);
28465294c1fSJeff Layton if (nf->nf_file) {
2854c475eeeSJeff Layton nfsd_file_check_write_error(nf);
286b2ff1bd7SJeff Layton filp_close(nf->nf_file, NULL);
28765294c1fSJeff Layton }
288668ed92eSChuck Lever
289668ed92eSChuck Lever /*
290668ed92eSChuck Lever * If this item is still linked via nf_lru, that's a bug.
291668ed92eSChuck Lever * WARN and leak it to preserve system stability.
292668ed92eSChuck Lever */
293668ed92eSChuck Lever if (WARN_ON_ONCE(!list_empty(&nf->nf_lru)))
294ac3a2585SJeff Layton return;
295668ed92eSChuck Lever
29665294c1fSJeff Layton call_rcu(&nf->nf_rcu, nfsd_file_slab_free);
29765294c1fSJeff Layton }
29865294c1fSJeff Layton
299055b24a8STrond Myklebust static bool
nfsd_file_check_writeback(struct nfsd_file * nf)300055b24a8STrond Myklebust nfsd_file_check_writeback(struct nfsd_file *nf)
301055b24a8STrond Myklebust {
302055b24a8STrond Myklebust struct file *file = nf->nf_file;
303055b24a8STrond Myklebust struct address_space *mapping;
304055b24a8STrond Myklebust
305dcb779fcSJeff Layton /* File not open for write? */
306dcb779fcSJeff Layton if (!(file->f_mode & FMODE_WRITE))
307055b24a8STrond Myklebust return false;
308dcb779fcSJeff Layton
309dcb779fcSJeff Layton /*
310dcb779fcSJeff Layton * Some filesystems (e.g. NFS) flush all dirty data on close.
311dcb779fcSJeff Layton * On others, there is no need to wait for writeback.
312dcb779fcSJeff Layton */
313dcb779fcSJeff Layton if (!(file_inode(file)->i_sb->s_export_op->flags & EXPORT_OP_FLUSH_ON_CLOSE))
314dcb779fcSJeff Layton return false;
315dcb779fcSJeff Layton
316055b24a8STrond Myklebust mapping = file->f_mapping;
317055b24a8STrond Myklebust return mapping_tagged(mapping, PAGECACHE_TAG_DIRTY) ||
318055b24a8STrond Myklebust mapping_tagged(mapping, PAGECACHE_TAG_WRITEBACK);
319055b24a8STrond Myklebust }
320055b24a8STrond Myklebust
321dcb779fcSJeff Layton
nfsd_file_lru_add(struct nfsd_file * nf)322ac3a2585SJeff Layton static bool nfsd_file_lru_add(struct nfsd_file *nf)
32365294c1fSJeff Layton {
3244a0e73e6SChuck Lever set_bit(NFSD_FILE_REFERENCED, &nf->nf_flags);
325ac3a2585SJeff Layton if (list_lru_add(&nfsd_file_lru, &nf->nf_lru)) {
326c46203acSChuck Lever trace_nfsd_file_lru_add(nf);
327ac3a2585SJeff Layton return true;
328ac3a2585SJeff Layton }
329ac3a2585SJeff Layton return false;
330c46203acSChuck Lever }
33165294c1fSJeff Layton
nfsd_file_lru_remove(struct nfsd_file * nf)332ac3a2585SJeff Layton static bool nfsd_file_lru_remove(struct nfsd_file *nf)
333c46203acSChuck Lever {
334ac3a2585SJeff Layton if (list_lru_del(&nfsd_file_lru, &nf->nf_lru)) {
335c46203acSChuck Lever trace_nfsd_file_lru_del(nf);
336ac3a2585SJeff Layton return true;
337ac3a2585SJeff Layton }
338ac3a2585SJeff Layton return false;
339c46203acSChuck Lever }
340c46203acSChuck Lever
34182141185SJeff Layton struct nfsd_file *
nfsd_file_get(struct nfsd_file * nf)34282141185SJeff Layton nfsd_file_get(struct nfsd_file *nf)
34365294c1fSJeff Layton {
34470f62231SJeff Layton if (nf && refcount_inc_not_zero(&nf->nf_ref))
34582141185SJeff Layton return nf;
34682141185SJeff Layton return NULL;
34765294c1fSJeff Layton }
34865294c1fSJeff Layton
349ac3a2585SJeff Layton /**
350ac3a2585SJeff Layton * nfsd_file_put - put the reference to a nfsd_file
351ac3a2585SJeff Layton * @nf: nfsd_file of which to put the reference
352ac3a2585SJeff Layton *
353ac3a2585SJeff Layton * Put a reference to a nfsd_file. In the non-GC case, we just put the
354ac3a2585SJeff Layton * reference immediately. In the GC case, if the reference would be
355ac3a2585SJeff Layton * the last one, the put it on the LRU instead to be cleaned up later.
356ac3a2585SJeff Layton */
35765294c1fSJeff Layton void
nfsd_file_put(struct nfsd_file * nf)35865294c1fSJeff Layton nfsd_file_put(struct nfsd_file *nf)
35965294c1fSJeff Layton {
36008af54b3SChuck Lever might_sleep();
361ac3a2585SJeff Layton trace_nfsd_file_put(nf);
36208af54b3SChuck Lever
363ac3a2585SJeff Layton if (test_bit(NFSD_FILE_GC, &nf->nf_flags) &&
364ac3a2585SJeff Layton test_bit(NFSD_FILE_HASHED, &nf->nf_flags)) {
365ac3a2585SJeff Layton /*
366ac3a2585SJeff Layton * If this is the last reference (nf_ref == 1), then try to
367ac3a2585SJeff Layton * transfer it to the LRU.
368ac3a2585SJeff Layton */
369ac3a2585SJeff Layton if (refcount_dec_not_one(&nf->nf_ref))
370ac3a2585SJeff Layton return;
3714d1ea845SChuck Lever
372ac3a2585SJeff Layton /* Try to add it to the LRU. If that fails, decrement. */
373ac3a2585SJeff Layton if (nfsd_file_lru_add(nf)) {
374ac3a2585SJeff Layton /* If it's still hashed, we're done */
375ac3a2585SJeff Layton if (test_bit(NFSD_FILE_HASHED, &nf->nf_flags)) {
3769542e6a6STrond Myklebust nfsd_file_schedule_laundrette();
377ac3a2585SJeff Layton return;
378ac3a2585SJeff Layton }
379ac3a2585SJeff Layton
380ac3a2585SJeff Layton /*
381ac3a2585SJeff Layton * We're racing with unhashing, so try to remove it from
382ac3a2585SJeff Layton * the LRU. If removal fails, then someone else already
383ac3a2585SJeff Layton * has our reference.
384ac3a2585SJeff Layton */
385ac3a2585SJeff Layton if (!nfsd_file_lru_remove(nf))
386ac3a2585SJeff Layton return;
387ac3a2585SJeff Layton }
388ac3a2585SJeff Layton }
389ac3a2585SJeff Layton if (refcount_dec_and_test(&nf->nf_ref))
390ac3a2585SJeff Layton nfsd_file_free(nf);
39165294c1fSJeff Layton }
39265294c1fSJeff Layton
39365294c1fSJeff Layton static void
nfsd_file_dispose_list(struct list_head * dispose)39465294c1fSJeff Layton nfsd_file_dispose_list(struct list_head *dispose)
39565294c1fSJeff Layton {
39665294c1fSJeff Layton struct nfsd_file *nf;
39765294c1fSJeff Layton
39865294c1fSJeff Layton while (!list_empty(dispose)) {
39965294c1fSJeff Layton nf = list_first_entry(dispose, struct nfsd_file, nf_lru);
400668ed92eSChuck Lever list_del_init(&nf->nf_lru);
401ac3a2585SJeff Layton nfsd_file_free(nf);
40265294c1fSJeff Layton }
40365294c1fSJeff Layton }
40465294c1fSJeff Layton
40592e4a673SJeff Layton /**
40692e4a673SJeff Layton * nfsd_file_dispose_list_delayed - move list of dead files to net's freeme list
40792e4a673SJeff Layton * @dispose: list of nfsd_files to be disposed
40892e4a673SJeff Layton *
40992e4a673SJeff Layton * Transfers each file to the "freeme" list for its nfsd_net, to eventually
41092e4a673SJeff Layton * be disposed of by the per-net garbage collector.
41192e4a673SJeff Layton */
4129542e6a6STrond Myklebust static void
nfsd_file_dispose_list_delayed(struct list_head * dispose)4139542e6a6STrond Myklebust nfsd_file_dispose_list_delayed(struct list_head *dispose)
4149542e6a6STrond Myklebust {
4159542e6a6STrond Myklebust while(!list_empty(dispose)) {
41692e4a673SJeff Layton struct nfsd_file *nf = list_first_entry(dispose,
41792e4a673SJeff Layton struct nfsd_file, nf_lru);
41892e4a673SJeff Layton struct nfsd_net *nn = net_generic(nf->nf_net, nfsd_net_id);
41992e4a673SJeff Layton struct nfsd_fcache_disposal *l = nn->fcache_disposal;
42092e4a673SJeff Layton
42192e4a673SJeff Layton spin_lock(&l->lock);
42292e4a673SJeff Layton list_move_tail(&nf->nf_lru, &l->freeme);
42392e4a673SJeff Layton spin_unlock(&l->lock);
42492e4a673SJeff Layton queue_work(nfsd_filecache_wq, &l->work);
4259542e6a6STrond Myklebust }
4269542e6a6STrond Myklebust }
4279542e6a6STrond Myklebust
4284a0e73e6SChuck Lever /**
4294a0e73e6SChuck Lever * nfsd_file_lru_cb - Examine an entry on the LRU list
4304a0e73e6SChuck Lever * @item: LRU entry to examine
4314a0e73e6SChuck Lever * @lru: controlling LRU
4324a0e73e6SChuck Lever * @lock: LRU list lock (unused)
4334a0e73e6SChuck Lever * @arg: dispose list
4344a0e73e6SChuck Lever *
4354a0e73e6SChuck Lever * Return values:
4364a0e73e6SChuck Lever * %LRU_REMOVED: @item was removed from the LRU
437edead3a5SChuck Lever * %LRU_ROTATE: @item is to be moved to the LRU tail
4384a0e73e6SChuck Lever * %LRU_SKIP: @item cannot be evicted
43965294c1fSJeff Layton */
44065294c1fSJeff Layton static enum lru_status
nfsd_file_lru_cb(struct list_head * item,struct list_lru_one * lru,spinlock_t * lock,void * arg)44165294c1fSJeff Layton nfsd_file_lru_cb(struct list_head *item, struct list_lru_one *lru,
44265294c1fSJeff Layton spinlock_t *lock, void *arg)
44365294c1fSJeff Layton __releases(lock)
44465294c1fSJeff Layton __acquires(lock)
44565294c1fSJeff Layton {
44665294c1fSJeff Layton struct list_head *head = arg;
44765294c1fSJeff Layton struct nfsd_file *nf = list_entry(item, struct nfsd_file, nf_lru);
44865294c1fSJeff Layton
449ac3a2585SJeff Layton /* We should only be dealing with GC entries here */
450ac3a2585SJeff Layton WARN_ON_ONCE(!test_bit(NFSD_FILE_GC, &nf->nf_flags));
451055b24a8STrond Myklebust
452055b24a8STrond Myklebust /*
453055b24a8STrond Myklebust * Don't throw out files that are still undergoing I/O or
454055b24a8STrond Myklebust * that have uncleared errors pending.
455055b24a8STrond Myklebust */
456c46203acSChuck Lever if (nfsd_file_check_writeback(nf)) {
457c46203acSChuck Lever trace_nfsd_file_gc_writeback(nf);
45865294c1fSJeff Layton return LRU_SKIP;
45965294c1fSJeff Layton }
46065294c1fSJeff Layton
461ac3a2585SJeff Layton /* If it was recently added to the list, skip it */
462c46203acSChuck Lever if (test_and_clear_bit(NFSD_FILE_REFERENCED, &nf->nf_flags)) {
463c46203acSChuck Lever trace_nfsd_file_gc_referenced(nf);
464edead3a5SChuck Lever return LRU_ROTATE;
46565294c1fSJeff Layton }
46665294c1fSJeff Layton
467ac3a2585SJeff Layton /*
468ac3a2585SJeff Layton * Put the reference held on behalf of the LRU. If it wasn't the last
469ac3a2585SJeff Layton * one, then just remove it from the LRU and ignore it.
470ac3a2585SJeff Layton */
471ac3a2585SJeff Layton if (!refcount_dec_and_test(&nf->nf_ref)) {
472ac3a2585SJeff Layton trace_nfsd_file_gc_in_use(nf);
473ac3a2585SJeff Layton list_lru_isolate(lru, &nf->nf_lru);
474ac3a2585SJeff Layton return LRU_REMOVED;
475c46203acSChuck Lever }
47665294c1fSJeff Layton
477ac3a2585SJeff Layton /* Refcount went to zero. Unhash it and queue it to the dispose list */
478ac3a2585SJeff Layton nfsd_file_unhash(nf);
47965294c1fSJeff Layton list_lru_isolate_move(lru, &nf->nf_lru, head);
48094660cc1SChuck Lever this_cpu_inc(nfsd_file_evictions);
481c46203acSChuck Lever trace_nfsd_file_gc_disposed(nf);
48265294c1fSJeff Layton return LRU_REMOVED;
48365294c1fSJeff Layton }
48465294c1fSJeff Layton
4859542e6a6STrond Myklebust static void
nfsd_file_gc(void)4869542e6a6STrond Myklebust nfsd_file_gc(void)
4879542e6a6STrond Myklebust {
4883bc6d347SChuck Lever LIST_HEAD(dispose);
48994660cc1SChuck Lever unsigned long ret;
4903bc6d347SChuck Lever
49194660cc1SChuck Lever ret = list_lru_walk(&nfsd_file_lru, nfsd_file_lru_cb,
492edead3a5SChuck Lever &dispose, list_lru_count(&nfsd_file_lru));
49394660cc1SChuck Lever trace_nfsd_file_gc_removed(ret, list_lru_count(&nfsd_file_lru));
494ac3a2585SJeff Layton nfsd_file_dispose_list_delayed(&dispose);
4959542e6a6STrond Myklebust }
4969542e6a6STrond Myklebust
4979542e6a6STrond Myklebust static void
nfsd_file_gc_worker(struct work_struct * work)4989542e6a6STrond Myklebust nfsd_file_gc_worker(struct work_struct *work)
4999542e6a6STrond Myklebust {
5009542e6a6STrond Myklebust nfsd_file_gc();
50122ae4c11SJeff Layton if (list_lru_count(&nfsd_file_lru))
5029542e6a6STrond Myklebust nfsd_file_schedule_laundrette();
50365294c1fSJeff Layton }
50465294c1fSJeff Layton
50565294c1fSJeff Layton static unsigned long
nfsd_file_lru_count(struct shrinker * s,struct shrink_control * sc)50665294c1fSJeff Layton nfsd_file_lru_count(struct shrinker *s, struct shrink_control *sc)
50765294c1fSJeff Layton {
50865294c1fSJeff Layton return list_lru_count(&nfsd_file_lru);
50965294c1fSJeff Layton }
51065294c1fSJeff Layton
51165294c1fSJeff Layton static unsigned long
nfsd_file_lru_scan(struct shrinker * s,struct shrink_control * sc)51265294c1fSJeff Layton nfsd_file_lru_scan(struct shrinker *s, struct shrink_control *sc)
51365294c1fSJeff Layton {
51439f1d1ffSChuck Lever LIST_HEAD(dispose);
51539f1d1ffSChuck Lever unsigned long ret;
51639f1d1ffSChuck Lever
51739f1d1ffSChuck Lever ret = list_lru_shrink_walk(&nfsd_file_lru, sc,
51839f1d1ffSChuck Lever nfsd_file_lru_cb, &dispose);
51994660cc1SChuck Lever trace_nfsd_file_shrinker_removed(ret, list_lru_count(&nfsd_file_lru));
520ac3a2585SJeff Layton nfsd_file_dispose_list_delayed(&dispose);
52139f1d1ffSChuck Lever return ret;
52265294c1fSJeff Layton }
52365294c1fSJeff Layton
52465294c1fSJeff Layton static struct shrinker nfsd_file_shrinker = {
52565294c1fSJeff Layton .scan_objects = nfsd_file_lru_scan,
52665294c1fSJeff Layton .count_objects = nfsd_file_lru_count,
52765294c1fSJeff Layton .seeks = 1,
52865294c1fSJeff Layton };
52965294c1fSJeff Layton
530ac3a2585SJeff Layton /**
5314bdbba54SJeff Layton * nfsd_file_cond_queue - conditionally unhash and queue a nfsd_file
5324bdbba54SJeff Layton * @nf: nfsd_file to attempt to queue
5334bdbba54SJeff Layton * @dispose: private list to queue successfully-put objects
5344bdbba54SJeff Layton *
5354bdbba54SJeff Layton * Unhash an nfsd_file, try to get a reference to it, and then put that
5364bdbba54SJeff Layton * reference. If it's the last reference, queue it to the dispose list.
5374bdbba54SJeff Layton */
5384bdbba54SJeff Layton static void
nfsd_file_cond_queue(struct nfsd_file * nf,struct list_head * dispose)5394bdbba54SJeff Layton nfsd_file_cond_queue(struct nfsd_file *nf, struct list_head *dispose)
5404bdbba54SJeff Layton __must_hold(RCU)
5414bdbba54SJeff Layton {
5424bdbba54SJeff Layton int decrement = 1;
5434bdbba54SJeff Layton
5444bdbba54SJeff Layton /* If we raced with someone else unhashing, ignore it */
5454bdbba54SJeff Layton if (!nfsd_file_unhash(nf))
5464bdbba54SJeff Layton return;
5474bdbba54SJeff Layton
5484bdbba54SJeff Layton /* If we can't get a reference, ignore it */
5494bdbba54SJeff Layton if (!nfsd_file_get(nf))
5504bdbba54SJeff Layton return;
5514bdbba54SJeff Layton
5524bdbba54SJeff Layton /* Extra decrement if we remove from the LRU */
5534bdbba54SJeff Layton if (nfsd_file_lru_remove(nf))
5544bdbba54SJeff Layton ++decrement;
5554bdbba54SJeff Layton
5564bdbba54SJeff Layton /* If refcount goes to 0, then put on the dispose list */
5574bdbba54SJeff Layton if (refcount_sub_and_test(decrement, &nf->nf_ref)) {
5584bdbba54SJeff Layton list_add(&nf->nf_lru, dispose);
5594bdbba54SJeff Layton trace_nfsd_file_closing(nf);
5604bdbba54SJeff Layton }
5614bdbba54SJeff Layton }
5624bdbba54SJeff Layton
5634bdbba54SJeff Layton /**
564ac3a2585SJeff Layton * nfsd_file_queue_for_close: try to close out any open nfsd_files for an inode
565ac3a2585SJeff Layton * @inode: inode on which to close out nfsd_files
566ac3a2585SJeff Layton * @dispose: list on which to gather nfsd_files to close out
567ac3a2585SJeff Layton *
568c4c649abSChuck Lever * An nfsd_file represents a struct file being held open on behalf of nfsd.
569c4c649abSChuck Lever * An open file however can block other activity (such as leases), or cause
570ac3a2585SJeff Layton * undesirable behavior (e.g. spurious silly-renames when reexporting NFS).
571ac3a2585SJeff Layton *
572ac3a2585SJeff Layton * This function is intended to find open nfsd_files when this sort of
573ac3a2585SJeff Layton * conflicting access occurs and then attempt to close those files out.
574ac3a2585SJeff Layton *
575ac3a2585SJeff Layton * Populates the dispose list with entries that have already had their
576ac3a2585SJeff Layton * refcounts go to zero. The actual free of an nfsd_file can be expensive,
577ac3a2585SJeff Layton * so we leave it up to the caller whether it wants to wait or not.
578a8455110SChuck Lever */
579ac3a2585SJeff Layton static void
nfsd_file_queue_for_close(struct inode * inode,struct list_head * dispose)580ac3a2585SJeff Layton nfsd_file_queue_for_close(struct inode *inode, struct list_head *dispose)
58165294c1fSJeff Layton {
582c4c649abSChuck Lever struct rhlist_head *tmp, *list;
58365294c1fSJeff Layton struct nfsd_file *nf;
58465294c1fSJeff Layton
585ce502f81SChuck Lever rcu_read_lock();
586c4c649abSChuck Lever list = rhltable_lookup(&nfsd_file_rhltable, &inode,
587ce502f81SChuck Lever nfsd_file_rhash_params);
588c4c649abSChuck Lever rhl_for_each_entry_rcu(nf, tmp, list, nf_rlist) {
589c4c649abSChuck Lever if (!test_bit(NFSD_FILE_GC, &nf->nf_flags))
590c4c649abSChuck Lever continue;
5914bdbba54SJeff Layton nfsd_file_cond_queue(nf, dispose);
592c4c649abSChuck Lever }
593ce502f81SChuck Lever rcu_read_unlock();
59465294c1fSJeff Layton }
59565294c1fSJeff Layton
59665294c1fSJeff Layton /**
59719598141STrond Myklebust * nfsd_file_close_inode - attempt a delayed close of a nfsd_file
59865294c1fSJeff Layton * @inode: inode of the file to attempt to remove
59965294c1fSJeff Layton *
600ac3a2585SJeff Layton * Close out any open nfsd_files that can be reaped for @inode. The
601ac3a2585SJeff Layton * actual freeing is deferred to the dispose_list_delayed infrastructure.
602ac3a2585SJeff Layton *
603ac3a2585SJeff Layton * This is used by the fsnotify callbacks and setlease notifier.
60465294c1fSJeff Layton */
60565294c1fSJeff Layton static void
nfsd_file_close_inode(struct inode * inode)60665294c1fSJeff Layton nfsd_file_close_inode(struct inode *inode)
60765294c1fSJeff Layton {
60865294c1fSJeff Layton LIST_HEAD(dispose);
60965294c1fSJeff Layton
610ac3a2585SJeff Layton nfsd_file_queue_for_close(inode, &dispose);
6119542e6a6STrond Myklebust nfsd_file_dispose_list_delayed(&dispose);
61265294c1fSJeff Layton }
61365294c1fSJeff Layton
61465294c1fSJeff Layton /**
615ac3a2585SJeff Layton * nfsd_file_close_inode_sync - attempt to forcibly close a nfsd_file
616ac3a2585SJeff Layton * @inode: inode of the file to attempt to remove
617ac3a2585SJeff Layton *
618ac3a2585SJeff Layton * Close out any open nfsd_files that can be reaped for @inode. The
619ac3a2585SJeff Layton * nfsd_files are closed out synchronously.
620ac3a2585SJeff Layton *
621ac3a2585SJeff Layton * This is called from nfsd_rename and nfsd_unlink to avoid silly-renames
622ac3a2585SJeff Layton * when reexporting NFS.
623ac3a2585SJeff Layton */
624ac3a2585SJeff Layton void
nfsd_file_close_inode_sync(struct inode * inode)625ac3a2585SJeff Layton nfsd_file_close_inode_sync(struct inode *inode)
626ac3a2585SJeff Layton {
627ac3a2585SJeff Layton struct nfsd_file *nf;
628ac3a2585SJeff Layton LIST_HEAD(dispose);
629ac3a2585SJeff Layton
630ac3a2585SJeff Layton trace_nfsd_file_close(inode);
631ac3a2585SJeff Layton
632ac3a2585SJeff Layton nfsd_file_queue_for_close(inode, &dispose);
633ac3a2585SJeff Layton while (!list_empty(&dispose)) {
634ac3a2585SJeff Layton nf = list_first_entry(&dispose, struct nfsd_file, nf_lru);
635ac3a2585SJeff Layton list_del_init(&nf->nf_lru);
636ac3a2585SJeff Layton nfsd_file_free(nf);
637ac3a2585SJeff Layton }
638ac3a2585SJeff Layton flush_delayed_fput();
639ac3a2585SJeff Layton }
640ac3a2585SJeff Layton
641ac3a2585SJeff Layton /**
64265294c1fSJeff Layton * nfsd_file_delayed_close - close unused nfsd_files
64365294c1fSJeff Layton * @work: dummy
64465294c1fSJeff Layton *
64592e4a673SJeff Layton * Scrape the freeme list for this nfsd_net, and then dispose of them
64692e4a673SJeff Layton * all.
64765294c1fSJeff Layton */
64865294c1fSJeff Layton static void
nfsd_file_delayed_close(struct work_struct * work)64965294c1fSJeff Layton nfsd_file_delayed_close(struct work_struct *work)
65065294c1fSJeff Layton {
65165294c1fSJeff Layton LIST_HEAD(head);
6529542e6a6STrond Myklebust struct nfsd_fcache_disposal *l = container_of(work,
6539542e6a6STrond Myklebust struct nfsd_fcache_disposal, work);
65465294c1fSJeff Layton
65592e4a673SJeff Layton spin_lock(&l->lock);
65692e4a673SJeff Layton list_splice_init(&l->freeme, &head);
65792e4a673SJeff Layton spin_unlock(&l->lock);
65892e4a673SJeff Layton
6599542e6a6STrond Myklebust nfsd_file_dispose_list(&head);
66065294c1fSJeff Layton }
66165294c1fSJeff Layton
66265294c1fSJeff Layton static int
nfsd_file_lease_notifier_call(struct notifier_block * nb,unsigned long arg,void * data)66365294c1fSJeff Layton nfsd_file_lease_notifier_call(struct notifier_block *nb, unsigned long arg,
66465294c1fSJeff Layton void *data)
66565294c1fSJeff Layton {
66665294c1fSJeff Layton struct file_lock *fl = data;
66765294c1fSJeff Layton
66865294c1fSJeff Layton /* Only close files for F_SETLEASE leases */
66965294c1fSJeff Layton if (fl->fl_flags & FL_LEASE)
670ac3a2585SJeff Layton nfsd_file_close_inode(file_inode(fl->fl_file));
67165294c1fSJeff Layton return 0;
67265294c1fSJeff Layton }
67365294c1fSJeff Layton
67465294c1fSJeff Layton static struct notifier_block nfsd_file_lease_notifier = {
67565294c1fSJeff Layton .notifier_call = nfsd_file_lease_notifier_call,
67665294c1fSJeff Layton };
67765294c1fSJeff Layton
67865294c1fSJeff Layton static int
nfsd_file_fsnotify_handle_event(struct fsnotify_mark * mark,u32 mask,struct inode * inode,struct inode * dir,const struct qstr * name,u32 cookie)679b9a1b977SAmir Goldstein nfsd_file_fsnotify_handle_event(struct fsnotify_mark *mark, u32 mask,
680b9a1b977SAmir Goldstein struct inode *inode, struct inode *dir,
681950cc0d2SAmir Goldstein const struct qstr *name, u32 cookie)
68265294c1fSJeff Layton {
68324dca905SGabriel Krisman Bertazi if (WARN_ON_ONCE(!inode))
68424dca905SGabriel Krisman Bertazi return 0;
68524dca905SGabriel Krisman Bertazi
68665294c1fSJeff Layton trace_nfsd_file_fsnotify_handle_event(inode, mask);
68765294c1fSJeff Layton
68865294c1fSJeff Layton /* Should be no marks on non-regular files */
68965294c1fSJeff Layton if (!S_ISREG(inode->i_mode)) {
69065294c1fSJeff Layton WARN_ON_ONCE(1);
69165294c1fSJeff Layton return 0;
69265294c1fSJeff Layton }
69365294c1fSJeff Layton
69465294c1fSJeff Layton /* don't close files if this was not the last link */
69565294c1fSJeff Layton if (mask & FS_ATTRIB) {
69665294c1fSJeff Layton if (inode->i_nlink)
69765294c1fSJeff Layton return 0;
69865294c1fSJeff Layton }
69965294c1fSJeff Layton
70065294c1fSJeff Layton nfsd_file_close_inode(inode);
70165294c1fSJeff Layton return 0;
70265294c1fSJeff Layton }
70365294c1fSJeff Layton
70465294c1fSJeff Layton
70565294c1fSJeff Layton static const struct fsnotify_ops nfsd_file_fsnotify_ops = {
706b9a1b977SAmir Goldstein .handle_inode_event = nfsd_file_fsnotify_handle_event,
70765294c1fSJeff Layton .free_mark = nfsd_file_mark_free,
70865294c1fSJeff Layton };
70965294c1fSJeff Layton
71065294c1fSJeff Layton int
nfsd_file_cache_init(void)71165294c1fSJeff Layton nfsd_file_cache_init(void)
71265294c1fSJeff Layton {
713fc22945eSChuck Lever int ret;
71465294c1fSJeff Layton
715c7b824c3SChuck Lever lockdep_assert_held(&nfsd_mutex);
716c7b824c3SChuck Lever if (test_and_set_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1)
71765294c1fSJeff Layton return 0;
71865294c1fSJeff Layton
719c4c649abSChuck Lever ret = rhltable_init(&nfsd_file_rhltable, &nfsd_file_rhash_params);
720fc22945eSChuck Lever if (ret)
721*4ab07232SChuck Lever goto out;
722fc22945eSChuck Lever
723fc22945eSChuck Lever ret = -ENOMEM;
7249542e6a6STrond Myklebust nfsd_filecache_wq = alloc_workqueue("nfsd_filecache", 0, 0);
7259542e6a6STrond Myklebust if (!nfsd_filecache_wq)
7269542e6a6STrond Myklebust goto out;
7279542e6a6STrond Myklebust
72865294c1fSJeff Layton nfsd_file_slab = kmem_cache_create("nfsd_file",
72965294c1fSJeff Layton sizeof(struct nfsd_file), 0, 0, NULL);
73065294c1fSJeff Layton if (!nfsd_file_slab) {
73165294c1fSJeff Layton pr_err("nfsd: unable to create nfsd_file_slab\n");
73265294c1fSJeff Layton goto out_err;
73365294c1fSJeff Layton }
73465294c1fSJeff Layton
73565294c1fSJeff Layton nfsd_file_mark_slab = kmem_cache_create("nfsd_file_mark",
73665294c1fSJeff Layton sizeof(struct nfsd_file_mark), 0, 0, NULL);
73765294c1fSJeff Layton if (!nfsd_file_mark_slab) {
73865294c1fSJeff Layton pr_err("nfsd: unable to create nfsd_file_mark_slab\n");
73965294c1fSJeff Layton goto out_err;
74065294c1fSJeff Layton }
74165294c1fSJeff Layton
74265294c1fSJeff Layton
74365294c1fSJeff Layton ret = list_lru_init(&nfsd_file_lru);
74465294c1fSJeff Layton if (ret) {
74565294c1fSJeff Layton pr_err("nfsd: failed to init nfsd_file_lru: %d\n", ret);
74665294c1fSJeff Layton goto out_err;
74765294c1fSJeff Layton }
74865294c1fSJeff Layton
749e33c267aSRoman Gushchin ret = register_shrinker(&nfsd_file_shrinker, "nfsd-filecache");
75065294c1fSJeff Layton if (ret) {
75165294c1fSJeff Layton pr_err("nfsd: failed to register nfsd_file_shrinker: %d\n", ret);
75265294c1fSJeff Layton goto out_lru;
75365294c1fSJeff Layton }
75465294c1fSJeff Layton
75565294c1fSJeff Layton ret = lease_register_notifier(&nfsd_file_lease_notifier);
75665294c1fSJeff Layton if (ret) {
75765294c1fSJeff Layton pr_err("nfsd: unable to register lease notifier: %d\n", ret);
75865294c1fSJeff Layton goto out_shrinker;
75965294c1fSJeff Layton }
76065294c1fSJeff Layton
761867a448dSAmir Goldstein nfsd_file_fsnotify_group = fsnotify_alloc_group(&nfsd_file_fsnotify_ops,
762b8962a9dSAmir Goldstein FSNOTIFY_GROUP_NOFS);
76365294c1fSJeff Layton if (IS_ERR(nfsd_file_fsnotify_group)) {
76465294c1fSJeff Layton pr_err("nfsd: unable to create fsnotify group: %ld\n",
76565294c1fSJeff Layton PTR_ERR(nfsd_file_fsnotify_group));
766231307dfSHuang Guobin ret = PTR_ERR(nfsd_file_fsnotify_group);
76765294c1fSJeff Layton nfsd_file_fsnotify_group = NULL;
76865294c1fSJeff Layton goto out_notifier;
76965294c1fSJeff Layton }
77065294c1fSJeff Layton
7719542e6a6STrond Myklebust INIT_DELAYED_WORK(&nfsd_filecache_laundrette, nfsd_file_gc_worker);
77265294c1fSJeff Layton out:
773*4ab07232SChuck Lever if (ret)
774*4ab07232SChuck Lever clear_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags);
77565294c1fSJeff Layton return ret;
77665294c1fSJeff Layton out_notifier:
77765294c1fSJeff Layton lease_unregister_notifier(&nfsd_file_lease_notifier);
77865294c1fSJeff Layton out_shrinker:
77965294c1fSJeff Layton unregister_shrinker(&nfsd_file_shrinker);
78065294c1fSJeff Layton out_lru:
78165294c1fSJeff Layton list_lru_destroy(&nfsd_file_lru);
78265294c1fSJeff Layton out_err:
78365294c1fSJeff Layton kmem_cache_destroy(nfsd_file_slab);
78465294c1fSJeff Layton nfsd_file_slab = NULL;
78565294c1fSJeff Layton kmem_cache_destroy(nfsd_file_mark_slab);
78665294c1fSJeff Layton nfsd_file_mark_slab = NULL;
7879542e6a6STrond Myklebust destroy_workqueue(nfsd_filecache_wq);
7889542e6a6STrond Myklebust nfsd_filecache_wq = NULL;
789c4c649abSChuck Lever rhltable_destroy(&nfsd_file_rhltable);
79065294c1fSJeff Layton goto out;
79165294c1fSJeff Layton }
79265294c1fSJeff Layton
793ac3a2585SJeff Layton /**
794ac3a2585SJeff Layton * __nfsd_file_cache_purge: clean out the cache for shutdown
795ac3a2585SJeff Layton * @net: net-namespace to shut down the cache (may be NULL)
796ac3a2585SJeff Layton *
797ac3a2585SJeff Layton * Walk the nfsd_file cache and close out any that match @net. If @net is NULL,
798972cc0e0SJeff Layton * then close out everything. Called when an nfsd instance is being shut down,
799972cc0e0SJeff Layton * and when the exports table is flushed.
800ac3a2585SJeff Layton */
801c7b824c3SChuck Lever static void
__nfsd_file_cache_purge(struct net * net)802c7b824c3SChuck Lever __nfsd_file_cache_purge(struct net *net)
80365294c1fSJeff Layton {
804ce502f81SChuck Lever struct rhashtable_iter iter;
80565294c1fSJeff Layton struct nfsd_file *nf;
80665294c1fSJeff Layton LIST_HEAD(dispose);
80765294c1fSJeff Layton
808c4c649abSChuck Lever rhltable_walk_enter(&nfsd_file_rhltable, &iter);
809ce502f81SChuck Lever do {
810ce502f81SChuck Lever rhashtable_walk_start(&iter);
81165294c1fSJeff Layton
812ce502f81SChuck Lever nf = rhashtable_walk_next(&iter);
813ce502f81SChuck Lever while (!IS_ERR_OR_NULL(nf)) {
8144bdbba54SJeff Layton if (!net || nf->nf_net == net)
8154bdbba54SJeff Layton nfsd_file_cond_queue(nf, &dispose);
816ce502f81SChuck Lever nf = rhashtable_walk_next(&iter);
81765294c1fSJeff Layton }
818ce502f81SChuck Lever
819ce502f81SChuck Lever rhashtable_walk_stop(&iter);
820ce502f81SChuck Lever } while (nf == ERR_PTR(-EAGAIN));
821ce502f81SChuck Lever rhashtable_walk_exit(&iter);
822ce502f81SChuck Lever
82365294c1fSJeff Layton nfsd_file_dispose_list(&dispose);
82465294c1fSJeff Layton }
82565294c1fSJeff Layton
8269542e6a6STrond Myklebust static struct nfsd_fcache_disposal *
nfsd_alloc_fcache_disposal(void)8271463b38eSNeilBrown nfsd_alloc_fcache_disposal(void)
8289542e6a6STrond Myklebust {
8299542e6a6STrond Myklebust struct nfsd_fcache_disposal *l;
8309542e6a6STrond Myklebust
8319542e6a6STrond Myklebust l = kmalloc(sizeof(*l), GFP_KERNEL);
8329542e6a6STrond Myklebust if (!l)
8339542e6a6STrond Myklebust return NULL;
8349542e6a6STrond Myklebust INIT_WORK(&l->work, nfsd_file_delayed_close);
8359542e6a6STrond Myklebust spin_lock_init(&l->lock);
8369542e6a6STrond Myklebust INIT_LIST_HEAD(&l->freeme);
8379542e6a6STrond Myklebust return l;
8389542e6a6STrond Myklebust }
8399542e6a6STrond Myklebust
8409542e6a6STrond Myklebust static void
nfsd_free_fcache_disposal(struct nfsd_fcache_disposal * l)8419542e6a6STrond Myklebust nfsd_free_fcache_disposal(struct nfsd_fcache_disposal *l)
8429542e6a6STrond Myklebust {
8439542e6a6STrond Myklebust cancel_work_sync(&l->work);
8449542e6a6STrond Myklebust nfsd_file_dispose_list(&l->freeme);
8451463b38eSNeilBrown kfree(l);
8469542e6a6STrond Myklebust }
8479542e6a6STrond Myklebust
8489542e6a6STrond Myklebust static void
nfsd_free_fcache_disposal_net(struct net * net)8499542e6a6STrond Myklebust nfsd_free_fcache_disposal_net(struct net *net)
8509542e6a6STrond Myklebust {
8511463b38eSNeilBrown struct nfsd_net *nn = net_generic(net, nfsd_net_id);
8521463b38eSNeilBrown struct nfsd_fcache_disposal *l = nn->fcache_disposal;
8539542e6a6STrond Myklebust
8549542e6a6STrond Myklebust nfsd_free_fcache_disposal(l);
8559542e6a6STrond Myklebust }
8569542e6a6STrond Myklebust
8579542e6a6STrond Myklebust int
nfsd_file_cache_start_net(struct net * net)8589542e6a6STrond Myklebust nfsd_file_cache_start_net(struct net *net)
8599542e6a6STrond Myklebust {
8601463b38eSNeilBrown struct nfsd_net *nn = net_generic(net, nfsd_net_id);
8611463b38eSNeilBrown
8621463b38eSNeilBrown nn->fcache_disposal = nfsd_alloc_fcache_disposal();
8631463b38eSNeilBrown return nn->fcache_disposal ? 0 : -ENOMEM;
8649542e6a6STrond Myklebust }
8659542e6a6STrond Myklebust
866c7b824c3SChuck Lever /**
867c7b824c3SChuck Lever * nfsd_file_cache_purge - Remove all cache items associated with @net
868c7b824c3SChuck Lever * @net: target net namespace
869c7b824c3SChuck Lever *
870c7b824c3SChuck Lever */
871c7b824c3SChuck Lever void
nfsd_file_cache_purge(struct net * net)872c7b824c3SChuck Lever nfsd_file_cache_purge(struct net *net)
873c7b824c3SChuck Lever {
874c7b824c3SChuck Lever lockdep_assert_held(&nfsd_mutex);
875c7b824c3SChuck Lever if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1)
876c7b824c3SChuck Lever __nfsd_file_cache_purge(net);
877c7b824c3SChuck Lever }
878c7b824c3SChuck Lever
8799542e6a6STrond Myklebust void
nfsd_file_cache_shutdown_net(struct net * net)8809542e6a6STrond Myklebust nfsd_file_cache_shutdown_net(struct net *net)
8819542e6a6STrond Myklebust {
8829542e6a6STrond Myklebust nfsd_file_cache_purge(net);
8839542e6a6STrond Myklebust nfsd_free_fcache_disposal_net(net);
8849542e6a6STrond Myklebust }
8859542e6a6STrond Myklebust
88665294c1fSJeff Layton void
nfsd_file_cache_shutdown(void)88765294c1fSJeff Layton nfsd_file_cache_shutdown(void)
88865294c1fSJeff Layton {
8898b330f78SChuck Lever int i;
8908b330f78SChuck Lever
891c7b824c3SChuck Lever lockdep_assert_held(&nfsd_mutex);
892c7b824c3SChuck Lever if (test_and_clear_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 0)
893c7b824c3SChuck Lever return;
89465294c1fSJeff Layton
89565294c1fSJeff Layton lease_unregister_notifier(&nfsd_file_lease_notifier);
89665294c1fSJeff Layton unregister_shrinker(&nfsd_file_shrinker);
89765294c1fSJeff Layton /*
89865294c1fSJeff Layton * make sure all callers of nfsd_file_lru_cb are done before
89965294c1fSJeff Layton * calling nfsd_file_cache_purge
90065294c1fSJeff Layton */
90165294c1fSJeff Layton cancel_delayed_work_sync(&nfsd_filecache_laundrette);
902c7b824c3SChuck Lever __nfsd_file_cache_purge(NULL);
90365294c1fSJeff Layton list_lru_destroy(&nfsd_file_lru);
90465294c1fSJeff Layton rcu_barrier();
90565294c1fSJeff Layton fsnotify_put_group(nfsd_file_fsnotify_group);
90665294c1fSJeff Layton nfsd_file_fsnotify_group = NULL;
90765294c1fSJeff Layton kmem_cache_destroy(nfsd_file_slab);
90865294c1fSJeff Layton nfsd_file_slab = NULL;
90965294c1fSJeff Layton fsnotify_wait_marks_destroyed();
91065294c1fSJeff Layton kmem_cache_destroy(nfsd_file_mark_slab);
91165294c1fSJeff Layton nfsd_file_mark_slab = NULL;
9129542e6a6STrond Myklebust destroy_workqueue(nfsd_filecache_wq);
9139542e6a6STrond Myklebust nfsd_filecache_wq = NULL;
914c4c649abSChuck Lever rhltable_destroy(&nfsd_file_rhltable);
91565294c1fSJeff Layton
9168b330f78SChuck Lever for_each_possible_cpu(i) {
9178b330f78SChuck Lever per_cpu(nfsd_file_cache_hits, i) = 0;
9188b330f78SChuck Lever per_cpu(nfsd_file_acquisitions, i) = 0;
9198b330f78SChuck Lever per_cpu(nfsd_file_releases, i) = 0;
9208b330f78SChuck Lever per_cpu(nfsd_file_total_age, i) = 0;
9218b330f78SChuck Lever per_cpu(nfsd_file_evictions, i) = 0;
92265294c1fSJeff Layton }
92365294c1fSJeff Layton }
92465294c1fSJeff Layton
925c4c649abSChuck Lever static struct nfsd_file *
nfsd_file_lookup_locked(const struct net * net,const struct cred * cred,struct inode * inode,unsigned char need,bool want_gc)926c4c649abSChuck Lever nfsd_file_lookup_locked(const struct net *net, const struct cred *cred,
927c4c649abSChuck Lever struct inode *inode, unsigned char need,
928c4c649abSChuck Lever bool want_gc)
929c4c649abSChuck Lever {
930c4c649abSChuck Lever struct rhlist_head *tmp, *list;
931c4c649abSChuck Lever struct nfsd_file *nf;
932c4c649abSChuck Lever
933c4c649abSChuck Lever list = rhltable_lookup(&nfsd_file_rhltable, &inode,
934c4c649abSChuck Lever nfsd_file_rhash_params);
935c4c649abSChuck Lever rhl_for_each_entry_rcu(nf, tmp, list, nf_rlist) {
936c4c649abSChuck Lever if (nf->nf_may != need)
937c4c649abSChuck Lever continue;
938c4c649abSChuck Lever if (nf->nf_net != net)
939c4c649abSChuck Lever continue;
940c4c649abSChuck Lever if (!nfsd_match_cred(nf->nf_cred, cred))
941c4c649abSChuck Lever continue;
942c4c649abSChuck Lever if (test_bit(NFSD_FILE_GC, &nf->nf_flags) != want_gc)
943c4c649abSChuck Lever continue;
944c4c649abSChuck Lever if (test_bit(NFSD_FILE_HASHED, &nf->nf_flags) == 0)
945c4c649abSChuck Lever continue;
946c4c649abSChuck Lever
947c4c649abSChuck Lever if (!nfsd_file_get(nf))
948c4c649abSChuck Lever continue;
949c4c649abSChuck Lever return nf;
950c4c649abSChuck Lever }
951c4c649abSChuck Lever return NULL;
952c4c649abSChuck Lever }
953c4c649abSChuck Lever
95465294c1fSJeff Layton /**
955ce502f81SChuck Lever * nfsd_file_is_cached - are there any cached open files for this inode?
956ce502f81SChuck Lever * @inode: inode to check
95765294c1fSJeff Layton *
958ce502f81SChuck Lever * The lookup matches inodes in all net namespaces and is atomic wrt
959ce502f81SChuck Lever * nfsd_file_acquire().
960ce502f81SChuck Lever *
961ce502f81SChuck Lever * Return values:
962ce502f81SChuck Lever * %true: filecache contains at least one file matching this inode
963ce502f81SChuck Lever * %false: filecache contains no files matching this inode
96465294c1fSJeff Layton */
96565294c1fSJeff Layton bool
nfsd_file_is_cached(struct inode * inode)96665294c1fSJeff Layton nfsd_file_is_cached(struct inode *inode)
96765294c1fSJeff Layton {
968c4c649abSChuck Lever struct rhlist_head *tmp, *list;
969c4c649abSChuck Lever struct nfsd_file *nf;
97065294c1fSJeff Layton bool ret = false;
97165294c1fSJeff Layton
972c4c649abSChuck Lever rcu_read_lock();
973c4c649abSChuck Lever list = rhltable_lookup(&nfsd_file_rhltable, &inode,
974c4c649abSChuck Lever nfsd_file_rhash_params);
975c4c649abSChuck Lever rhl_for_each_entry_rcu(nf, tmp, list, nf_rlist)
976c4c649abSChuck Lever if (test_bit(NFSD_FILE_GC, &nf->nf_flags)) {
97765294c1fSJeff Layton ret = true;
978c4c649abSChuck Lever break;
979c4c649abSChuck Lever }
980c4c649abSChuck Lever rcu_read_unlock();
981c4c649abSChuck Lever
98254f7df70SChuck Lever trace_nfsd_file_is_cached(inode, (int)ret);
98365294c1fSJeff Layton return ret;
98465294c1fSJeff Layton }
98565294c1fSJeff Layton
986fb70bf12SChuck Lever static __be32
nfsd_file_do_acquire(struct svc_rqst * rqstp,struct svc_fh * fhp,unsigned int may_flags,struct file * file,struct nfsd_file ** pnf,bool want_gc)987be023006SChuck Lever nfsd_file_do_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
9880b3a551fSJeff Layton unsigned int may_flags, struct file *file,
9890b3a551fSJeff Layton struct nfsd_file **pnf, bool want_gc)
99065294c1fSJeff Layton {
991c4c649abSChuck Lever unsigned char need = may_flags & NFSD_FILE_MAY_MASK;
992c4c649abSChuck Lever struct net *net = SVC_NET(rqstp);
993c4c649abSChuck Lever struct nfsd_file *new, *nf;
99483f8fc2cSTrond Myklebust bool stale_retry = true;
995243a5263SJeff Layton bool open_retry = true;
996c4c649abSChuck Lever struct inode *inode;
997ce502f81SChuck Lever __be32 status;
998243a5263SJeff Layton int ret;
99965294c1fSJeff Layton
100083f8fc2cSTrond Myklebust retry:
100165294c1fSJeff Layton status = fh_verify(rqstp, fhp, S_IFREG,
100265294c1fSJeff Layton may_flags|NFSD_MAY_OWNER_OVERRIDE);
100365294c1fSJeff Layton if (status != nfs_ok)
100465294c1fSJeff Layton return status;
1005c4c649abSChuck Lever inode = d_inode(fhp->fh_dentry);
100665294c1fSJeff Layton
1007243a5263SJeff Layton rcu_read_lock();
100883f8fc2cSTrond Myklebust nf = nfsd_file_lookup_locked(net, current_cred(), inode, need, want_gc);
1009243a5263SJeff Layton rcu_read_unlock();
1010ac3a2585SJeff Layton
1011ac3a2585SJeff Layton if (nf) {
1012b680cb9bSJeff Layton /*
1013b680cb9bSJeff Layton * If the nf is on the LRU then it holds an extra reference
1014b680cb9bSJeff Layton * that must be put if it's removed. It had better not be
1015b680cb9bSJeff Layton * the last one however, since we should hold another.
1016b680cb9bSJeff Layton */
1017ac3a2585SJeff Layton if (nfsd_file_lru_remove(nf))
1018ac3a2585SJeff Layton WARN_ON_ONCE(refcount_dec_and_test(&nf->nf_ref));
101965294c1fSJeff Layton goto wait_for_construction;
1020ac3a2585SJeff Layton }
102165294c1fSJeff Layton
1022c4c649abSChuck Lever new = nfsd_file_alloc(net, inode, need, want_gc);
1023c4c649abSChuck Lever if (!new) {
102454f7df70SChuck Lever status = nfserr_jukebox;
1025c6593366SJeff Layton goto out;
102665294c1fSJeff Layton }
102765294c1fSJeff Layton
1028c4c649abSChuck Lever rcu_read_lock();
1029c4c649abSChuck Lever spin_lock(&inode->i_lock);
103083f8fc2cSTrond Myklebust nf = nfsd_file_lookup_locked(net, current_cred(), inode, need, want_gc);
1031c4c649abSChuck Lever if (unlikely(nf)) {
1032c4c649abSChuck Lever spin_unlock(&inode->i_lock);
1033c4c649abSChuck Lever rcu_read_unlock();
1034c4c649abSChuck Lever nfsd_file_slab_free(&new->nf_rcu);
1035c4c649abSChuck Lever goto wait_for_construction;
1036c4c649abSChuck Lever }
1037c4c649abSChuck Lever nf = new;
1038c4c649abSChuck Lever ret = rhltable_insert(&nfsd_file_rhltable, &nf->nf_rlist,
1039ce502f81SChuck Lever nfsd_file_rhash_params);
1040c4c649abSChuck Lever spin_unlock(&inode->i_lock);
1041c4c649abSChuck Lever rcu_read_unlock();
1042243a5263SJeff Layton if (likely(ret == 0))
104365294c1fSJeff Layton goto open_file;
1044243a5263SJeff Layton
1045c4c649abSChuck Lever trace_nfsd_file_insert_err(rqstp, inode, may_flags, ret);
1046243a5263SJeff Layton status = nfserr_jukebox;
1047c6593366SJeff Layton goto construction_err;
104865294c1fSJeff Layton
104965294c1fSJeff Layton wait_for_construction:
105065294c1fSJeff Layton wait_on_bit(&nf->nf_flags, NFSD_FILE_PENDING, TASK_UNINTERRUPTIBLE);
105165294c1fSJeff Layton
105265294c1fSJeff Layton /* Did construction of this file fail? */
105365294c1fSJeff Layton if (!test_bit(NFSD_FILE_HASHED, &nf->nf_flags)) {
1054c4c649abSChuck Lever trace_nfsd_file_cons_err(rqstp, inode, may_flags, nf);
1055243a5263SJeff Layton if (!open_retry) {
105628c7d86bSTrond Myklebust status = nfserr_jukebox;
1057c6593366SJeff Layton goto construction_err;
105828c7d86bSTrond Myklebust }
1059a685bc35SJeff Layton nfsd_file_put(nf);
1060243a5263SJeff Layton open_retry = false;
106183f8fc2cSTrond Myklebust fh_put(fhp);
106265294c1fSJeff Layton goto retry;
106365294c1fSJeff Layton }
106465294c1fSJeff Layton this_cpu_inc(nfsd_file_cache_hits);
106565294c1fSJeff Layton
106623ba98deSJeff Layton status = nfserrno(nfsd_open_break_lease(file_inode(nf->nf_file), may_flags));
1067c6593366SJeff Layton if (status != nfs_ok) {
1068c6593366SJeff Layton nfsd_file_put(nf);
1069c6593366SJeff Layton nf = NULL;
1070c6593366SJeff Layton }
1071c6593366SJeff Layton
107265294c1fSJeff Layton out:
107365294c1fSJeff Layton if (status == nfs_ok) {
107429d4bdbbSChuck Lever this_cpu_inc(nfsd_file_acquisitions);
10754c475eeeSJeff Layton nfsd_file_check_write_error(nf);
107665294c1fSJeff Layton *pnf = nf;
107765294c1fSJeff Layton }
1078c4c649abSChuck Lever trace_nfsd_file_acquire(rqstp, inode, may_flags, nf, status);
107965294c1fSJeff Layton return status;
108065294c1fSJeff Layton
108165294c1fSJeff Layton open_file:
1082b40a2839SChuck Lever trace_nfsd_file_alloc(nf);
1083c4c649abSChuck Lever nf->nf_mark = nfsd_file_mark_find_or_create(nf, inode);
1084fb70bf12SChuck Lever if (nf->nf_mark) {
10850b3a551fSJeff Layton if (file) {
10860b3a551fSJeff Layton get_file(file);
10870b3a551fSJeff Layton nf->nf_file = file;
10880b3a551fSJeff Layton status = nfs_ok;
10890b3a551fSJeff Layton trace_nfsd_file_opened(nf, status);
10900b3a551fSJeff Layton } else {
109183f8fc2cSTrond Myklebust ret = nfsd_open_verified(rqstp, fhp, may_flags,
1092f4d84c52SChuck Lever &nf->nf_file);
109383f8fc2cSTrond Myklebust if (ret == -EOPENSTALE && stale_retry) {
109483f8fc2cSTrond Myklebust stale_retry = false;
109583f8fc2cSTrond Myklebust nfsd_file_unhash(nf);
109683f8fc2cSTrond Myklebust clear_and_wake_up_bit(NFSD_FILE_PENDING,
109783f8fc2cSTrond Myklebust &nf->nf_flags);
109883f8fc2cSTrond Myklebust if (refcount_dec_and_test(&nf->nf_ref))
109983f8fc2cSTrond Myklebust nfsd_file_free(nf);
110083f8fc2cSTrond Myklebust nf = NULL;
110183f8fc2cSTrond Myklebust fh_put(fhp);
110283f8fc2cSTrond Myklebust goto retry;
110383f8fc2cSTrond Myklebust }
110483f8fc2cSTrond Myklebust status = nfserrno(ret);
11050122e882SChuck Lever trace_nfsd_file_open(nf, status);
11060b3a551fSJeff Layton }
1107fb70bf12SChuck Lever } else
110865294c1fSJeff Layton status = nfserr_jukebox;
110965294c1fSJeff Layton /*
111065294c1fSJeff Layton * If construction failed, or we raced with a call to unlink()
111165294c1fSJeff Layton * then unhash.
111265294c1fSJeff Layton */
1113c4c649abSChuck Lever if (status != nfs_ok || inode->i_nlink == 0)
1114ac3a2585SJeff Layton nfsd_file_unhash(nf);
1115b8bea9f6SJeff Layton clear_and_wake_up_bit(NFSD_FILE_PENDING, &nf->nf_flags);
1116c6593366SJeff Layton if (status == nfs_ok)
1117c6593366SJeff Layton goto out;
1118c6593366SJeff Layton
1119c6593366SJeff Layton construction_err:
1120c6593366SJeff Layton if (refcount_dec_and_test(&nf->nf_ref))
1121c6593366SJeff Layton nfsd_file_free(nf);
1122c6593366SJeff Layton nf = NULL;
112365294c1fSJeff Layton goto out;
112465294c1fSJeff Layton }
112565294c1fSJeff Layton
1126fb70bf12SChuck Lever /**
11274d1ea845SChuck Lever * nfsd_file_acquire_gc - Get a struct nfsd_file with an open file
11284d1ea845SChuck Lever * @rqstp: the RPC transaction being executed
11294d1ea845SChuck Lever * @fhp: the NFS filehandle of the file to be opened
11304d1ea845SChuck Lever * @may_flags: NFSD_MAY_ settings for the file
11314d1ea845SChuck Lever * @pnf: OUT: new or found "struct nfsd_file" object
11324d1ea845SChuck Lever *
11334d1ea845SChuck Lever * The nfsd_file object returned by this API is reference-counted
11344d1ea845SChuck Lever * and garbage-collected. The object is retained for a few
11354d1ea845SChuck Lever * seconds after the final nfsd_file_put() in case the caller
11364d1ea845SChuck Lever * wants to re-use it.
11374d1ea845SChuck Lever *
1138c4c649abSChuck Lever * Return values:
1139c4c649abSChuck Lever * %nfs_ok - @pnf points to an nfsd_file with its reference
1140c4c649abSChuck Lever * count boosted.
1141c4c649abSChuck Lever *
1142c4c649abSChuck Lever * On error, an nfsstat value in network byte order is returned.
11434d1ea845SChuck Lever */
11444d1ea845SChuck Lever __be32
nfsd_file_acquire_gc(struct svc_rqst * rqstp,struct svc_fh * fhp,unsigned int may_flags,struct nfsd_file ** pnf)11454d1ea845SChuck Lever nfsd_file_acquire_gc(struct svc_rqst *rqstp, struct svc_fh *fhp,
11464d1ea845SChuck Lever unsigned int may_flags, struct nfsd_file **pnf)
11474d1ea845SChuck Lever {
11480b3a551fSJeff Layton return nfsd_file_do_acquire(rqstp, fhp, may_flags, NULL, pnf, true);
11494d1ea845SChuck Lever }
11504d1ea845SChuck Lever
11514d1ea845SChuck Lever /**
1152fb70bf12SChuck Lever * nfsd_file_acquire - Get a struct nfsd_file with an open file
1153fb70bf12SChuck Lever * @rqstp: the RPC transaction being executed
1154fb70bf12SChuck Lever * @fhp: the NFS filehandle of the file to be opened
1155fb70bf12SChuck Lever * @may_flags: NFSD_MAY_ settings for the file
1156fb70bf12SChuck Lever * @pnf: OUT: new or found "struct nfsd_file" object
1157fb70bf12SChuck Lever *
11584d1ea845SChuck Lever * The nfsd_file_object returned by this API is reference-counted
11594d1ea845SChuck Lever * but not garbage-collected. The object is unhashed after the
11604d1ea845SChuck Lever * final nfsd_file_put().
11614d1ea845SChuck Lever *
1162c4c649abSChuck Lever * Return values:
1163c4c649abSChuck Lever * %nfs_ok - @pnf points to an nfsd_file with its reference
1164c4c649abSChuck Lever * count boosted.
1165c4c649abSChuck Lever *
1166c4c649abSChuck Lever * On error, an nfsstat value in network byte order is returned.
1167fb70bf12SChuck Lever */
1168fb70bf12SChuck Lever __be32
nfsd_file_acquire(struct svc_rqst * rqstp,struct svc_fh * fhp,unsigned int may_flags,struct nfsd_file ** pnf)1169fb70bf12SChuck Lever nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
1170fb70bf12SChuck Lever unsigned int may_flags, struct nfsd_file **pnf)
1171fb70bf12SChuck Lever {
11720b3a551fSJeff Layton return nfsd_file_do_acquire(rqstp, fhp, may_flags, NULL, pnf, false);
1173fb70bf12SChuck Lever }
1174fb70bf12SChuck Lever
1175fb70bf12SChuck Lever /**
11760b3a551fSJeff Layton * nfsd_file_acquire_opened - Get a struct nfsd_file using existing open file
1177fb70bf12SChuck Lever * @rqstp: the RPC transaction being executed
1178fb70bf12SChuck Lever * @fhp: the NFS filehandle of the file just created
1179fb70bf12SChuck Lever * @may_flags: NFSD_MAY_ settings for the file
11800b3a551fSJeff Layton * @file: cached, already-open file (may be NULL)
1181fb70bf12SChuck Lever * @pnf: OUT: new or found "struct nfsd_file" object
1182fb70bf12SChuck Lever *
11830b3a551fSJeff Layton * Acquire a nfsd_file object that is not GC'ed. If one doesn't already exist,
11840b3a551fSJeff Layton * and @file is non-NULL, use it to instantiate a new nfsd_file instead of
11850b3a551fSJeff Layton * opening a new one.
11864d1ea845SChuck Lever *
1187c4c649abSChuck Lever * Return values:
1188c4c649abSChuck Lever * %nfs_ok - @pnf points to an nfsd_file with its reference
1189c4c649abSChuck Lever * count boosted.
1190c4c649abSChuck Lever *
1191c4c649abSChuck Lever * On error, an nfsstat value in network byte order is returned.
1192fb70bf12SChuck Lever */
1193fb70bf12SChuck Lever __be32
nfsd_file_acquire_opened(struct svc_rqst * rqstp,struct svc_fh * fhp,unsigned int may_flags,struct file * file,struct nfsd_file ** pnf)11940b3a551fSJeff Layton nfsd_file_acquire_opened(struct svc_rqst *rqstp, struct svc_fh *fhp,
11950b3a551fSJeff Layton unsigned int may_flags, struct file *file,
11960b3a551fSJeff Layton struct nfsd_file **pnf)
1197fb70bf12SChuck Lever {
11980b3a551fSJeff Layton return nfsd_file_do_acquire(rqstp, fhp, may_flags, file, pnf, false);
1199fb70bf12SChuck Lever }
1200fb70bf12SChuck Lever
120165294c1fSJeff Layton /*
120265294c1fSJeff Layton * Note that fields may be added, removed or reordered in the future. Programs
120365294c1fSJeff Layton * scraping this file for info should test the labels to ensure they're
120465294c1fSJeff Layton * getting the correct field.
120565294c1fSJeff Layton */
nfsd_file_cache_stats_show(struct seq_file * m,void * v)12061342f9ddSChenXiaoSong int nfsd_file_cache_stats_show(struct seq_file *m, void *v)
120765294c1fSJeff Layton {
12081f696e23SJeff Layton unsigned long releases = 0, evictions = 0;
1209df2aff52SChuck Lever unsigned long hits = 0, acquisitions = 0;
1210ce502f81SChuck Lever unsigned int i, count = 0, buckets = 0;
1211904940e9SChuck Lever unsigned long lru = 0, total_age = 0;
121265294c1fSJeff Layton
1213ce502f81SChuck Lever /* Serialize with server shutdown */
121465294c1fSJeff Layton mutex_lock(&nfsd_mutex);
1215c7b824c3SChuck Lever if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1) {
1216ce502f81SChuck Lever struct bucket_table *tbl;
1217ce502f81SChuck Lever struct rhashtable *ht;
1218ce502f81SChuck Lever
12190fd244c1SChuck Lever lru = list_lru_count(&nfsd_file_lru);
1220ce502f81SChuck Lever
1221ce502f81SChuck Lever rcu_read_lock();
1222c4c649abSChuck Lever ht = &nfsd_file_rhltable.ht;
1223ce502f81SChuck Lever count = atomic_read(&ht->nelems);
1224ce502f81SChuck Lever tbl = rht_dereference_rcu(ht->tbl, ht);
1225ce502f81SChuck Lever buckets = tbl->size;
1226ce502f81SChuck Lever rcu_read_unlock();
122765294c1fSJeff Layton }
122865294c1fSJeff Layton mutex_unlock(&nfsd_mutex);
122965294c1fSJeff Layton
123029d4bdbbSChuck Lever for_each_possible_cpu(i) {
123165294c1fSJeff Layton hits += per_cpu(nfsd_file_cache_hits, i);
123229d4bdbbSChuck Lever acquisitions += per_cpu(nfsd_file_acquisitions, i);
1233d6329327SChuck Lever releases += per_cpu(nfsd_file_releases, i);
1234904940e9SChuck Lever total_age += per_cpu(nfsd_file_total_age, i);
123594660cc1SChuck Lever evictions += per_cpu(nfsd_file_evictions, i);
123629d4bdbbSChuck Lever }
123765294c1fSJeff Layton
1238c4c649abSChuck Lever seq_printf(m, "total inodes: %u\n", count);
1239ce502f81SChuck Lever seq_printf(m, "hash buckets: %u\n", buckets);
12400fd244c1SChuck Lever seq_printf(m, "lru entries: %lu\n", lru);
124165294c1fSJeff Layton seq_printf(m, "cache hits: %lu\n", hits);
124229d4bdbbSChuck Lever seq_printf(m, "acquisitions: %lu\n", acquisitions);
1243d6329327SChuck Lever seq_printf(m, "releases: %lu\n", releases);
124494660cc1SChuck Lever seq_printf(m, "evictions: %lu\n", evictions);
1245904940e9SChuck Lever if (releases)
1246904940e9SChuck Lever seq_printf(m, "mean age (ms): %ld\n", total_age / releases);
1247904940e9SChuck Lever else
1248904940e9SChuck Lever seq_printf(m, "mean age (ms): -\n");
124965294c1fSJeff Layton return 0;
125065294c1fSJeff Layton }
1251