1*3f054211SChuck Lever // SPDX-License-Identifier: GPL-2.0 265294c1fSJeff Layton /* 3*3f054211SChuck 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); 61df2aff52SChuck Lever static DEFINE_PER_CPU(unsigned long, nfsd_file_pages_flushed); 6294660cc1SChuck Lever static DEFINE_PER_CPU(unsigned long, nfsd_file_evictions); 6365294c1fSJeff Layton 649542e6a6STrond Myklebust struct nfsd_fcache_disposal { 659542e6a6STrond Myklebust struct work_struct work; 669542e6a6STrond Myklebust spinlock_t lock; 679542e6a6STrond Myklebust struct list_head freeme; 689542e6a6STrond Myklebust }; 699542e6a6STrond Myklebust 7050d0def9SChen Zhou static struct workqueue_struct *nfsd_filecache_wq __read_mostly; 719542e6a6STrond Myklebust 7265294c1fSJeff Layton static struct kmem_cache *nfsd_file_slab; 7365294c1fSJeff Layton static struct kmem_cache *nfsd_file_mark_slab; 7465294c1fSJeff Layton static struct list_lru nfsd_file_lru; 75c7b824c3SChuck Lever static unsigned long nfsd_file_flags; 7665294c1fSJeff Layton static struct fsnotify_group *nfsd_file_fsnotify_group; 7765294c1fSJeff Layton static struct delayed_work nfsd_filecache_laundrette; 78fc22945eSChuck Lever static struct rhashtable nfsd_file_rhash_tbl 79fc22945eSChuck Lever ____cacheline_aligned_in_smp; 8065294c1fSJeff Layton 81fc22945eSChuck Lever enum nfsd_file_lookup_type { 82fc22945eSChuck Lever NFSD_FILE_KEY_INODE, 83fc22945eSChuck Lever NFSD_FILE_KEY_FULL, 84fc22945eSChuck Lever }; 85fc22945eSChuck Lever 86fc22945eSChuck Lever struct nfsd_file_lookup_key { 87fc22945eSChuck Lever struct inode *inode; 88fc22945eSChuck Lever struct net *net; 89fc22945eSChuck Lever const struct cred *cred; 90fc22945eSChuck Lever unsigned char need; 914d1ea845SChuck Lever bool gc; 92fc22945eSChuck Lever enum nfsd_file_lookup_type type; 93fc22945eSChuck Lever }; 94fc22945eSChuck Lever 95fc22945eSChuck Lever /* 96fc22945eSChuck Lever * The returned hash value is based solely on the address of an in-code 97fc22945eSChuck Lever * inode, a pointer to a slab-allocated object. The entropy in such a 98fc22945eSChuck Lever * pointer is concentrated in its middle bits. 99fc22945eSChuck Lever */ 100fc22945eSChuck Lever static u32 nfsd_file_inode_hash(const struct inode *inode, u32 seed) 101fc22945eSChuck Lever { 102fc22945eSChuck Lever unsigned long ptr = (unsigned long)inode; 103fc22945eSChuck Lever u32 k; 104fc22945eSChuck Lever 105fc22945eSChuck Lever k = ptr >> L1_CACHE_SHIFT; 106fc22945eSChuck Lever k &= 0x00ffffff; 107fc22945eSChuck Lever return jhash2(&k, 1, seed); 108fc22945eSChuck Lever } 109fc22945eSChuck Lever 110fc22945eSChuck Lever /** 111fc22945eSChuck Lever * nfsd_file_key_hashfn - Compute the hash value of a lookup key 112fc22945eSChuck Lever * @data: key on which to compute the hash value 113fc22945eSChuck Lever * @len: rhash table's key_len parameter (unused) 114fc22945eSChuck Lever * @seed: rhash table's random seed of the day 115fc22945eSChuck Lever * 116fc22945eSChuck Lever * Return value: 117fc22945eSChuck Lever * Computed 32-bit hash value 118fc22945eSChuck Lever */ 119fc22945eSChuck Lever static u32 nfsd_file_key_hashfn(const void *data, u32 len, u32 seed) 120fc22945eSChuck Lever { 121fc22945eSChuck Lever const struct nfsd_file_lookup_key *key = data; 122fc22945eSChuck Lever 123fc22945eSChuck Lever return nfsd_file_inode_hash(key->inode, seed); 124fc22945eSChuck Lever } 125fc22945eSChuck Lever 126fc22945eSChuck Lever /** 127fc22945eSChuck Lever * nfsd_file_obj_hashfn - Compute the hash value of an nfsd_file 128fc22945eSChuck Lever * @data: object on which to compute the hash value 129fc22945eSChuck Lever * @len: rhash table's key_len parameter (unused) 130fc22945eSChuck Lever * @seed: rhash table's random seed of the day 131fc22945eSChuck Lever * 132fc22945eSChuck Lever * Return value: 133fc22945eSChuck Lever * Computed 32-bit hash value 134fc22945eSChuck Lever */ 135fc22945eSChuck Lever static u32 nfsd_file_obj_hashfn(const void *data, u32 len, u32 seed) 136fc22945eSChuck Lever { 137fc22945eSChuck Lever const struct nfsd_file *nf = data; 138fc22945eSChuck Lever 139fc22945eSChuck Lever return nfsd_file_inode_hash(nf->nf_inode, seed); 140fc22945eSChuck Lever } 141fc22945eSChuck Lever 142fc22945eSChuck Lever static bool 143fc22945eSChuck Lever nfsd_match_cred(const struct cred *c1, const struct cred *c2) 144fc22945eSChuck Lever { 145fc22945eSChuck Lever int i; 146fc22945eSChuck Lever 147fc22945eSChuck Lever if (!uid_eq(c1->fsuid, c2->fsuid)) 148fc22945eSChuck Lever return false; 149fc22945eSChuck Lever if (!gid_eq(c1->fsgid, c2->fsgid)) 150fc22945eSChuck Lever return false; 151fc22945eSChuck Lever if (c1->group_info == NULL || c2->group_info == NULL) 152fc22945eSChuck Lever return c1->group_info == c2->group_info; 153fc22945eSChuck Lever if (c1->group_info->ngroups != c2->group_info->ngroups) 154fc22945eSChuck Lever return false; 155fc22945eSChuck Lever for (i = 0; i < c1->group_info->ngroups; i++) { 156fc22945eSChuck Lever if (!gid_eq(c1->group_info->gid[i], c2->group_info->gid[i])) 157fc22945eSChuck Lever return false; 158fc22945eSChuck Lever } 159fc22945eSChuck Lever return true; 160fc22945eSChuck Lever } 161fc22945eSChuck Lever 162fc22945eSChuck Lever /** 163fc22945eSChuck Lever * nfsd_file_obj_cmpfn - Match a cache item against search criteria 164fc22945eSChuck Lever * @arg: search criteria 165fc22945eSChuck Lever * @ptr: cache item to check 166fc22945eSChuck Lever * 167fc22945eSChuck Lever * Return values: 168fc22945eSChuck Lever * %0 - Item matches search criteria 169fc22945eSChuck Lever * %1 - Item does not match search criteria 170fc22945eSChuck Lever */ 171fc22945eSChuck Lever static int nfsd_file_obj_cmpfn(struct rhashtable_compare_arg *arg, 172fc22945eSChuck Lever const void *ptr) 173fc22945eSChuck Lever { 174fc22945eSChuck Lever const struct nfsd_file_lookup_key *key = arg->key; 175fc22945eSChuck Lever const struct nfsd_file *nf = ptr; 176fc22945eSChuck Lever 177fc22945eSChuck Lever switch (key->type) { 178fc22945eSChuck Lever case NFSD_FILE_KEY_INODE: 179fc22945eSChuck Lever if (nf->nf_inode != key->inode) 180fc22945eSChuck Lever return 1; 181fc22945eSChuck Lever break; 182fc22945eSChuck Lever case NFSD_FILE_KEY_FULL: 183fc22945eSChuck Lever if (nf->nf_inode != key->inode) 184fc22945eSChuck Lever return 1; 185fc22945eSChuck Lever if (nf->nf_may != key->need) 186fc22945eSChuck Lever return 1; 187fc22945eSChuck Lever if (nf->nf_net != key->net) 188fc22945eSChuck Lever return 1; 189fc22945eSChuck Lever if (!nfsd_match_cred(nf->nf_cred, key->cred)) 190fc22945eSChuck Lever return 1; 1914d1ea845SChuck Lever if (!!test_bit(NFSD_FILE_GC, &nf->nf_flags) != key->gc) 1924d1ea845SChuck Lever return 1; 193fc22945eSChuck Lever if (test_bit(NFSD_FILE_HASHED, &nf->nf_flags) == 0) 194fc22945eSChuck Lever return 1; 195fc22945eSChuck Lever break; 196fc22945eSChuck Lever } 197fc22945eSChuck Lever return 0; 198fc22945eSChuck Lever } 199fc22945eSChuck Lever 200fc22945eSChuck Lever static const struct rhashtable_params nfsd_file_rhash_params = { 201fc22945eSChuck Lever .key_len = sizeof_field(struct nfsd_file, nf_inode), 202fc22945eSChuck Lever .key_offset = offsetof(struct nfsd_file, nf_inode), 203fc22945eSChuck Lever .head_offset = offsetof(struct nfsd_file, nf_rhash), 204fc22945eSChuck Lever .hashfn = nfsd_file_key_hashfn, 205fc22945eSChuck Lever .obj_hashfn = nfsd_file_obj_hashfn, 206fc22945eSChuck Lever .obj_cmpfn = nfsd_file_obj_cmpfn, 207fc22945eSChuck Lever /* Reduce resizing churn on light workloads */ 208fc22945eSChuck Lever .min_size = 512, /* buckets */ 209fc22945eSChuck Lever .automatic_shrinking = true, 210fc22945eSChuck Lever }; 21165294c1fSJeff Layton 21265294c1fSJeff Layton static void 2139542e6a6STrond Myklebust nfsd_file_schedule_laundrette(void) 21465294c1fSJeff Layton { 215ce502f81SChuck Lever if ((atomic_read(&nfsd_file_rhash_tbl.nelems) == 0) || 216c7b824c3SChuck Lever test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 0) 21765294c1fSJeff Layton return; 21865294c1fSJeff Layton 2199542e6a6STrond Myklebust queue_delayed_work(system_wq, &nfsd_filecache_laundrette, 2209542e6a6STrond Myklebust NFSD_LAUNDRETTE_DELAY); 22165294c1fSJeff Layton } 22265294c1fSJeff Layton 22365294c1fSJeff Layton static void 22465294c1fSJeff Layton nfsd_file_slab_free(struct rcu_head *rcu) 22565294c1fSJeff Layton { 22665294c1fSJeff Layton struct nfsd_file *nf = container_of(rcu, struct nfsd_file, nf_rcu); 22765294c1fSJeff Layton 22865294c1fSJeff Layton put_cred(nf->nf_cred); 22965294c1fSJeff Layton kmem_cache_free(nfsd_file_slab, nf); 23065294c1fSJeff Layton } 23165294c1fSJeff Layton 23265294c1fSJeff Layton static void 23365294c1fSJeff Layton nfsd_file_mark_free(struct fsnotify_mark *mark) 23465294c1fSJeff Layton { 23565294c1fSJeff Layton struct nfsd_file_mark *nfm = container_of(mark, struct nfsd_file_mark, 23665294c1fSJeff Layton nfm_mark); 23765294c1fSJeff Layton 23865294c1fSJeff Layton kmem_cache_free(nfsd_file_mark_slab, nfm); 23965294c1fSJeff Layton } 24065294c1fSJeff Layton 24165294c1fSJeff Layton static struct nfsd_file_mark * 24265294c1fSJeff Layton nfsd_file_mark_get(struct nfsd_file_mark *nfm) 24365294c1fSJeff Layton { 244689827cdSTrond Myklebust if (!refcount_inc_not_zero(&nfm->nfm_ref)) 24565294c1fSJeff Layton return NULL; 24665294c1fSJeff Layton return nfm; 24765294c1fSJeff Layton } 24865294c1fSJeff Layton 24965294c1fSJeff Layton static void 25065294c1fSJeff Layton nfsd_file_mark_put(struct nfsd_file_mark *nfm) 25165294c1fSJeff Layton { 252689827cdSTrond Myklebust if (refcount_dec_and_test(&nfm->nfm_ref)) { 25365294c1fSJeff Layton fsnotify_destroy_mark(&nfm->nfm_mark, nfsd_file_fsnotify_group); 25465294c1fSJeff Layton fsnotify_put_mark(&nfm->nfm_mark); 25565294c1fSJeff Layton } 25665294c1fSJeff Layton } 25765294c1fSJeff Layton 25865294c1fSJeff Layton static struct nfsd_file_mark * 259427f5f83SChuck Lever nfsd_file_mark_find_or_create(struct nfsd_file *nf, struct inode *inode) 26065294c1fSJeff Layton { 26165294c1fSJeff Layton int err; 26265294c1fSJeff Layton struct fsnotify_mark *mark; 26365294c1fSJeff Layton struct nfsd_file_mark *nfm = NULL, *new; 26465294c1fSJeff Layton 26565294c1fSJeff Layton do { 266b8962a9dSAmir Goldstein fsnotify_group_lock(nfsd_file_fsnotify_group); 26765294c1fSJeff Layton mark = fsnotify_find_mark(&inode->i_fsnotify_marks, 26865294c1fSJeff Layton nfsd_file_fsnotify_group); 26965294c1fSJeff Layton if (mark) { 27065294c1fSJeff Layton nfm = nfsd_file_mark_get(container_of(mark, 27165294c1fSJeff Layton struct nfsd_file_mark, 27265294c1fSJeff Layton nfm_mark)); 273b8962a9dSAmir Goldstein fsnotify_group_unlock(nfsd_file_fsnotify_group); 27490d2f1daSTrond Myklebust if (nfm) { 27565294c1fSJeff Layton fsnotify_put_mark(mark); 27665294c1fSJeff Layton break; 27790d2f1daSTrond Myklebust } 27890d2f1daSTrond Myklebust /* Avoid soft lockup race with nfsd_file_mark_put() */ 27990d2f1daSTrond Myklebust fsnotify_destroy_mark(mark, nfsd_file_fsnotify_group); 28090d2f1daSTrond Myklebust fsnotify_put_mark(mark); 281b8962a9dSAmir Goldstein } else { 282b8962a9dSAmir Goldstein fsnotify_group_unlock(nfsd_file_fsnotify_group); 283b8962a9dSAmir Goldstein } 28465294c1fSJeff Layton 28565294c1fSJeff Layton /* allocate a new nfm */ 28665294c1fSJeff Layton new = kmem_cache_alloc(nfsd_file_mark_slab, GFP_KERNEL); 28765294c1fSJeff Layton if (!new) 28865294c1fSJeff Layton return NULL; 28965294c1fSJeff Layton fsnotify_init_mark(&new->nfm_mark, nfsd_file_fsnotify_group); 29065294c1fSJeff Layton new->nfm_mark.mask = FS_ATTRIB|FS_DELETE_SELF; 291689827cdSTrond Myklebust refcount_set(&new->nfm_ref, 1); 29265294c1fSJeff Layton 29365294c1fSJeff Layton err = fsnotify_add_inode_mark(&new->nfm_mark, inode, 0); 29465294c1fSJeff Layton 29565294c1fSJeff Layton /* 29665294c1fSJeff Layton * If the add was successful, then return the object. 29765294c1fSJeff Layton * Otherwise, we need to put the reference we hold on the 29865294c1fSJeff Layton * nfm_mark. The fsnotify code will take a reference and put 29965294c1fSJeff Layton * it on failure, so we can't just free it directly. It's also 30065294c1fSJeff Layton * not safe to call fsnotify_destroy_mark on it as the 30165294c1fSJeff Layton * mark->group will be NULL. Thus, we can't let the nfm_ref 30265294c1fSJeff Layton * counter drive the destruction at this point. 30365294c1fSJeff Layton */ 30465294c1fSJeff Layton if (likely(!err)) 30565294c1fSJeff Layton nfm = new; 30665294c1fSJeff Layton else 30765294c1fSJeff Layton fsnotify_put_mark(&new->nfm_mark); 30865294c1fSJeff Layton } while (unlikely(err == -EEXIST)); 30965294c1fSJeff Layton 31065294c1fSJeff Layton return nfm; 31165294c1fSJeff Layton } 31265294c1fSJeff Layton 31365294c1fSJeff Layton static struct nfsd_file * 314ce502f81SChuck Lever nfsd_file_alloc(struct nfsd_file_lookup_key *key, unsigned int may) 31565294c1fSJeff Layton { 31665294c1fSJeff Layton struct nfsd_file *nf; 31765294c1fSJeff Layton 31865294c1fSJeff Layton nf = kmem_cache_alloc(nfsd_file_slab, GFP_KERNEL); 31965294c1fSJeff Layton if (nf) { 32065294c1fSJeff Layton INIT_LIST_HEAD(&nf->nf_lru); 321904940e9SChuck Lever nf->nf_birthtime = ktime_get(); 32265294c1fSJeff Layton nf->nf_file = NULL; 32365294c1fSJeff Layton nf->nf_cred = get_current_cred(); 324ce502f81SChuck Lever nf->nf_net = key->net; 32565294c1fSJeff Layton nf->nf_flags = 0; 326ce502f81SChuck Lever __set_bit(NFSD_FILE_HASHED, &nf->nf_flags); 327ce502f81SChuck Lever __set_bit(NFSD_FILE_PENDING, &nf->nf_flags); 3284d1ea845SChuck Lever if (key->gc) 3294d1ea845SChuck Lever __set_bit(NFSD_FILE_GC, &nf->nf_flags); 330ce502f81SChuck Lever nf->nf_inode = key->inode; 331ce502f81SChuck Lever /* nf_ref is pre-incremented for hash table */ 332ce502f81SChuck Lever refcount_set(&nf->nf_ref, 2); 333ce502f81SChuck Lever nf->nf_may = key->need; 33465294c1fSJeff Layton nf->nf_mark = NULL; 33565294c1fSJeff Layton } 33665294c1fSJeff Layton return nf; 33765294c1fSJeff Layton } 33865294c1fSJeff Layton 33965294c1fSJeff Layton static bool 34065294c1fSJeff Layton nfsd_file_free(struct nfsd_file *nf) 34165294c1fSJeff Layton { 342904940e9SChuck Lever s64 age = ktime_to_ms(ktime_sub(ktime_get(), nf->nf_birthtime)); 34365294c1fSJeff Layton bool flush = false; 34465294c1fSJeff Layton 345d6329327SChuck Lever this_cpu_inc(nfsd_file_releases); 346904940e9SChuck Lever this_cpu_add(nfsd_file_total_age, age); 347d6329327SChuck Lever 34865294c1fSJeff Layton trace_nfsd_file_put_final(nf); 34965294c1fSJeff Layton if (nf->nf_mark) 35065294c1fSJeff Layton nfsd_file_mark_put(nf->nf_mark); 35165294c1fSJeff Layton if (nf->nf_file) { 35265294c1fSJeff Layton get_file(nf->nf_file); 35365294c1fSJeff Layton filp_close(nf->nf_file, NULL); 35465294c1fSJeff Layton fput(nf->nf_file); 35565294c1fSJeff Layton flush = true; 35665294c1fSJeff Layton } 357668ed92eSChuck Lever 358668ed92eSChuck Lever /* 359668ed92eSChuck Lever * If this item is still linked via nf_lru, that's a bug. 360668ed92eSChuck Lever * WARN and leak it to preserve system stability. 361668ed92eSChuck Lever */ 362668ed92eSChuck Lever if (WARN_ON_ONCE(!list_empty(&nf->nf_lru))) 363668ed92eSChuck Lever return flush; 364668ed92eSChuck Lever 36565294c1fSJeff Layton call_rcu(&nf->nf_rcu, nfsd_file_slab_free); 36665294c1fSJeff Layton return flush; 36765294c1fSJeff Layton } 36865294c1fSJeff Layton 369055b24a8STrond Myklebust static bool 370055b24a8STrond Myklebust nfsd_file_check_writeback(struct nfsd_file *nf) 371055b24a8STrond Myklebust { 372055b24a8STrond Myklebust struct file *file = nf->nf_file; 373055b24a8STrond Myklebust struct address_space *mapping; 374055b24a8STrond Myklebust 375055b24a8STrond Myklebust if (!file || !(file->f_mode & FMODE_WRITE)) 376055b24a8STrond Myklebust return false; 377055b24a8STrond Myklebust mapping = file->f_mapping; 378055b24a8STrond Myklebust return mapping_tagged(mapping, PAGECACHE_TAG_DIRTY) || 379055b24a8STrond Myklebust mapping_tagged(mapping, PAGECACHE_TAG_WRITEBACK); 380055b24a8STrond Myklebust } 381055b24a8STrond Myklebust 382055b24a8STrond Myklebust static int 383055b24a8STrond Myklebust nfsd_file_check_write_error(struct nfsd_file *nf) 384055b24a8STrond Myklebust { 385055b24a8STrond Myklebust struct file *file = nf->nf_file; 386055b24a8STrond Myklebust 387055b24a8STrond Myklebust if (!file || !(file->f_mode & FMODE_WRITE)) 388055b24a8STrond Myklebust return 0; 389055b24a8STrond Myklebust return filemap_check_wb_err(file->f_mapping, READ_ONCE(file->f_wb_err)); 390055b24a8STrond Myklebust } 391055b24a8STrond Myklebust 39265294c1fSJeff Layton static void 3936b8a9433STrond Myklebust nfsd_file_flush(struct nfsd_file *nf) 3946b8a9433STrond Myklebust { 395df2aff52SChuck Lever struct file *file = nf->nf_file; 396df2aff52SChuck Lever 397df2aff52SChuck Lever if (!file || !(file->f_mode & FMODE_WRITE)) 398df2aff52SChuck Lever return; 399df2aff52SChuck Lever this_cpu_add(nfsd_file_pages_flushed, file->f_mapping->nrpages); 400df2aff52SChuck Lever if (vfs_fsync(file, 1) != 0) 4016b8a9433STrond Myklebust nfsd_reset_write_verifier(net_generic(nf->nf_net, nfsd_net_id)); 4026b8a9433STrond Myklebust } 4036b8a9433STrond Myklebust 404c46203acSChuck Lever static void nfsd_file_lru_add(struct nfsd_file *nf) 40565294c1fSJeff Layton { 4064a0e73e6SChuck Lever set_bit(NFSD_FILE_REFERENCED, &nf->nf_flags); 407c46203acSChuck Lever if (list_lru_add(&nfsd_file_lru, &nf->nf_lru)) 408c46203acSChuck Lever trace_nfsd_file_lru_add(nf); 409c46203acSChuck Lever } 41065294c1fSJeff Layton 411c46203acSChuck Lever static void nfsd_file_lru_remove(struct nfsd_file *nf) 412c46203acSChuck Lever { 413c46203acSChuck Lever if (list_lru_del(&nfsd_file_lru, &nf->nf_lru)) 414c46203acSChuck Lever trace_nfsd_file_lru_del(nf); 415c46203acSChuck Lever } 416c46203acSChuck Lever 41765294c1fSJeff Layton static void 418ce502f81SChuck Lever nfsd_file_hash_remove(struct nfsd_file *nf) 41965294c1fSJeff Layton { 42065294c1fSJeff Layton trace_nfsd_file_unhash(nf); 42165294c1fSJeff Layton 422055b24a8STrond Myklebust if (nfsd_file_check_write_error(nf)) 4233988a578SChuck Lever nfsd_reset_write_verifier(net_generic(nf->nf_net, nfsd_net_id)); 424ce502f81SChuck Lever rhashtable_remove_fast(&nfsd_file_rhash_tbl, &nf->nf_rhash, 425ce502f81SChuck Lever nfsd_file_rhash_params); 42665294c1fSJeff Layton } 42765294c1fSJeff Layton 42865294c1fSJeff Layton static bool 42965294c1fSJeff Layton nfsd_file_unhash(struct nfsd_file *nf) 43065294c1fSJeff Layton { 43165294c1fSJeff Layton if (test_and_clear_bit(NFSD_FILE_HASHED, &nf->nf_flags)) { 432ce502f81SChuck Lever nfsd_file_hash_remove(nf); 43365294c1fSJeff Layton return true; 43465294c1fSJeff Layton } 43565294c1fSJeff Layton return false; 43665294c1fSJeff Layton } 43765294c1fSJeff Layton 4388d0d254bSJeff Layton static void 439ce502f81SChuck Lever nfsd_file_unhash_and_dispose(struct nfsd_file *nf, struct list_head *dispose) 44065294c1fSJeff Layton { 441ce502f81SChuck Lever trace_nfsd_file_unhash_and_dispose(nf); 4428d0d254bSJeff Layton if (nfsd_file_unhash(nf)) { 4438d0d254bSJeff Layton /* caller must call nfsd_file_dispose_list() later */ 4444a0e73e6SChuck Lever nfsd_file_lru_remove(nf); 44565294c1fSJeff Layton list_add(&nf->nf_lru, dispose); 4468d0d254bSJeff Layton } 44765294c1fSJeff Layton } 44865294c1fSJeff Layton 449b6669305STrond Myklebust static void 45065294c1fSJeff Layton nfsd_file_put_noref(struct nfsd_file *nf) 45165294c1fSJeff Layton { 45265294c1fSJeff Layton trace_nfsd_file_put(nf); 45365294c1fSJeff Layton 454689827cdSTrond Myklebust if (refcount_dec_and_test(&nf->nf_ref)) { 45565294c1fSJeff Layton WARN_ON(test_bit(NFSD_FILE_HASHED, &nf->nf_flags)); 4564a0e73e6SChuck Lever nfsd_file_lru_remove(nf); 45765294c1fSJeff Layton nfsd_file_free(nf); 45865294c1fSJeff Layton } 45965294c1fSJeff Layton } 46065294c1fSJeff Layton 4614d1ea845SChuck Lever static void 4624d1ea845SChuck Lever nfsd_file_unhash_and_put(struct nfsd_file *nf) 4634d1ea845SChuck Lever { 4644d1ea845SChuck Lever if (nfsd_file_unhash(nf)) 4654d1ea845SChuck Lever nfsd_file_put_noref(nf); 4664d1ea845SChuck Lever } 4674d1ea845SChuck Lever 46865294c1fSJeff Layton void 46965294c1fSJeff Layton nfsd_file_put(struct nfsd_file *nf) 47065294c1fSJeff Layton { 47108af54b3SChuck Lever might_sleep(); 47208af54b3SChuck Lever 4734d1ea845SChuck Lever if (test_bit(NFSD_FILE_GC, &nf->nf_flags)) 4744a0e73e6SChuck Lever nfsd_file_lru_add(nf); 4754d1ea845SChuck Lever else if (refcount_read(&nf->nf_ref) == 2) 4764d1ea845SChuck Lever nfsd_file_unhash_and_put(nf); 4774d1ea845SChuck Lever 4784d1ea845SChuck Lever if (!test_bit(NFSD_FILE_HASHED, &nf->nf_flags)) { 4796b8a9433STrond Myklebust nfsd_file_flush(nf); 480b6669305STrond Myklebust nfsd_file_put_noref(nf); 4814d1ea845SChuck Lever } else if (nf->nf_file && test_bit(NFSD_FILE_GC, &nf->nf_flags)) { 482b6669305STrond Myklebust nfsd_file_put_noref(nf); 4839542e6a6STrond Myklebust nfsd_file_schedule_laundrette(); 484b6c71c66SChuck Lever } else 485b6c71c66SChuck Lever nfsd_file_put_noref(nf); 48665294c1fSJeff Layton } 48765294c1fSJeff Layton 48865294c1fSJeff Layton struct nfsd_file * 48965294c1fSJeff Layton nfsd_file_get(struct nfsd_file *nf) 49065294c1fSJeff Layton { 491689827cdSTrond Myklebust if (likely(refcount_inc_not_zero(&nf->nf_ref))) 49265294c1fSJeff Layton return nf; 49365294c1fSJeff Layton return NULL; 49465294c1fSJeff Layton } 49565294c1fSJeff Layton 49665294c1fSJeff Layton static void 49765294c1fSJeff Layton nfsd_file_dispose_list(struct list_head *dispose) 49865294c1fSJeff Layton { 49965294c1fSJeff Layton struct nfsd_file *nf; 50065294c1fSJeff Layton 50165294c1fSJeff Layton while(!list_empty(dispose)) { 50265294c1fSJeff Layton nf = list_first_entry(dispose, struct nfsd_file, nf_lru); 503668ed92eSChuck Lever list_del_init(&nf->nf_lru); 5046b8a9433STrond Myklebust nfsd_file_flush(nf); 50565294c1fSJeff Layton nfsd_file_put_noref(nf); 50665294c1fSJeff Layton } 50765294c1fSJeff Layton } 50865294c1fSJeff Layton 50965294c1fSJeff Layton static void 51065294c1fSJeff Layton nfsd_file_dispose_list_sync(struct list_head *dispose) 51165294c1fSJeff Layton { 51265294c1fSJeff Layton bool flush = false; 51365294c1fSJeff Layton struct nfsd_file *nf; 51465294c1fSJeff Layton 51565294c1fSJeff Layton while(!list_empty(dispose)) { 51665294c1fSJeff Layton nf = list_first_entry(dispose, struct nfsd_file, nf_lru); 517668ed92eSChuck Lever list_del_init(&nf->nf_lru); 5186b8a9433STrond Myklebust nfsd_file_flush(nf); 519689827cdSTrond Myklebust if (!refcount_dec_and_test(&nf->nf_ref)) 52065294c1fSJeff Layton continue; 52165294c1fSJeff Layton if (nfsd_file_free(nf)) 52265294c1fSJeff Layton flush = true; 52365294c1fSJeff Layton } 52465294c1fSJeff Layton if (flush) 52565294c1fSJeff Layton flush_delayed_fput(); 52665294c1fSJeff Layton } 52765294c1fSJeff Layton 5289542e6a6STrond Myklebust static void 5299542e6a6STrond Myklebust nfsd_file_list_remove_disposal(struct list_head *dst, 5309542e6a6STrond Myklebust struct nfsd_fcache_disposal *l) 5319542e6a6STrond Myklebust { 5329542e6a6STrond Myklebust spin_lock(&l->lock); 5339542e6a6STrond Myklebust list_splice_init(&l->freeme, dst); 5349542e6a6STrond Myklebust spin_unlock(&l->lock); 5359542e6a6STrond Myklebust } 5369542e6a6STrond Myklebust 5379542e6a6STrond Myklebust static void 5389542e6a6STrond Myklebust nfsd_file_list_add_disposal(struct list_head *files, struct net *net) 5399542e6a6STrond Myklebust { 5401463b38eSNeilBrown struct nfsd_net *nn = net_generic(net, nfsd_net_id); 5411463b38eSNeilBrown struct nfsd_fcache_disposal *l = nn->fcache_disposal; 5429542e6a6STrond Myklebust 5439542e6a6STrond Myklebust spin_lock(&l->lock); 5449542e6a6STrond Myklebust list_splice_tail_init(files, &l->freeme); 5459542e6a6STrond Myklebust spin_unlock(&l->lock); 5469542e6a6STrond Myklebust queue_work(nfsd_filecache_wq, &l->work); 5479542e6a6STrond Myklebust } 5489542e6a6STrond Myklebust 5499542e6a6STrond Myklebust static void 5509542e6a6STrond Myklebust nfsd_file_list_add_pernet(struct list_head *dst, struct list_head *src, 5519542e6a6STrond Myklebust struct net *net) 5529542e6a6STrond Myklebust { 5539542e6a6STrond Myklebust struct nfsd_file *nf, *tmp; 5549542e6a6STrond Myklebust 5559542e6a6STrond Myklebust list_for_each_entry_safe(nf, tmp, src, nf_lru) { 5569542e6a6STrond Myklebust if (nf->nf_net == net) 5579542e6a6STrond Myklebust list_move_tail(&nf->nf_lru, dst); 5589542e6a6STrond Myklebust } 5599542e6a6STrond Myklebust } 5609542e6a6STrond Myklebust 5619542e6a6STrond Myklebust static void 5629542e6a6STrond Myklebust nfsd_file_dispose_list_delayed(struct list_head *dispose) 5639542e6a6STrond Myklebust { 5649542e6a6STrond Myklebust LIST_HEAD(list); 5659542e6a6STrond Myklebust struct nfsd_file *nf; 5669542e6a6STrond Myklebust 5679542e6a6STrond Myklebust while(!list_empty(dispose)) { 5689542e6a6STrond Myklebust nf = list_first_entry(dispose, struct nfsd_file, nf_lru); 5699542e6a6STrond Myklebust nfsd_file_list_add_pernet(&list, dispose, nf->nf_net); 5709542e6a6STrond Myklebust nfsd_file_list_add_disposal(&list, nf->nf_net); 5719542e6a6STrond Myklebust } 5729542e6a6STrond Myklebust } 5739542e6a6STrond Myklebust 5744a0e73e6SChuck Lever /** 5754a0e73e6SChuck Lever * nfsd_file_lru_cb - Examine an entry on the LRU list 5764a0e73e6SChuck Lever * @item: LRU entry to examine 5774a0e73e6SChuck Lever * @lru: controlling LRU 5784a0e73e6SChuck Lever * @lock: LRU list lock (unused) 5794a0e73e6SChuck Lever * @arg: dispose list 5804a0e73e6SChuck Lever * 5814a0e73e6SChuck Lever * Return values: 5824a0e73e6SChuck Lever * %LRU_REMOVED: @item was removed from the LRU 583edead3a5SChuck Lever * %LRU_ROTATE: @item is to be moved to the LRU tail 5844a0e73e6SChuck Lever * %LRU_SKIP: @item cannot be evicted 58565294c1fSJeff Layton */ 58665294c1fSJeff Layton static enum lru_status 58765294c1fSJeff Layton nfsd_file_lru_cb(struct list_head *item, struct list_lru_one *lru, 58865294c1fSJeff Layton spinlock_t *lock, void *arg) 58965294c1fSJeff Layton __releases(lock) 59065294c1fSJeff Layton __acquires(lock) 59165294c1fSJeff Layton { 59265294c1fSJeff Layton struct list_head *head = arg; 59365294c1fSJeff Layton struct nfsd_file *nf = list_entry(item, struct nfsd_file, nf_lru); 59465294c1fSJeff Layton 59565294c1fSJeff Layton /* 59665294c1fSJeff Layton * Do a lockless refcount check. The hashtable holds one reference, so 59765294c1fSJeff Layton * we look to see if anything else has a reference, or if any have 59865294c1fSJeff Layton * been put since the shrinker last ran. Those don't get unhashed and 59965294c1fSJeff Layton * released. 60065294c1fSJeff Layton * 60165294c1fSJeff Layton * Note that in the put path, we set the flag and then decrement the 60265294c1fSJeff Layton * counter. Here we check the counter and then test and clear the flag. 60365294c1fSJeff Layton * That order is deliberate to ensure that we can do this locklessly. 60465294c1fSJeff Layton */ 605c46203acSChuck Lever if (refcount_read(&nf->nf_ref) > 1) { 6064a0e73e6SChuck Lever list_lru_isolate(lru, &nf->nf_lru); 607c46203acSChuck Lever trace_nfsd_file_gc_in_use(nf); 6084a0e73e6SChuck Lever return LRU_REMOVED; 609c46203acSChuck Lever } 610055b24a8STrond Myklebust 611055b24a8STrond Myklebust /* 612055b24a8STrond Myklebust * Don't throw out files that are still undergoing I/O or 613055b24a8STrond Myklebust * that have uncleared errors pending. 614055b24a8STrond Myklebust */ 615c46203acSChuck Lever if (nfsd_file_check_writeback(nf)) { 616c46203acSChuck Lever trace_nfsd_file_gc_writeback(nf); 61765294c1fSJeff Layton return LRU_SKIP; 61865294c1fSJeff Layton } 61965294c1fSJeff Layton 620c46203acSChuck Lever if (test_and_clear_bit(NFSD_FILE_REFERENCED, &nf->nf_flags)) { 621c46203acSChuck Lever trace_nfsd_file_gc_referenced(nf); 622edead3a5SChuck Lever return LRU_ROTATE; 62365294c1fSJeff Layton } 62465294c1fSJeff Layton 625c46203acSChuck Lever if (!test_and_clear_bit(NFSD_FILE_HASHED, &nf->nf_flags)) { 626c46203acSChuck Lever trace_nfsd_file_gc_hashed(nf); 627c46203acSChuck Lever return LRU_SKIP; 628c46203acSChuck Lever } 62965294c1fSJeff Layton 63065294c1fSJeff Layton list_lru_isolate_move(lru, &nf->nf_lru, head); 63194660cc1SChuck Lever this_cpu_inc(nfsd_file_evictions); 632c46203acSChuck Lever trace_nfsd_file_gc_disposed(nf); 63365294c1fSJeff Layton return LRU_REMOVED; 63465294c1fSJeff Layton } 63565294c1fSJeff Layton 6360bac5a26SChuck Lever /* 6370bac5a26SChuck Lever * Unhash items on @dispose immediately, then queue them on the 6380bac5a26SChuck Lever * disposal workqueue to finish releasing them in the background. 6390bac5a26SChuck Lever * 6400bac5a26SChuck Lever * cel: Note that between the time list_lru_shrink_walk runs and 6410bac5a26SChuck Lever * now, these items are in the hash table but marked unhashed. 6420bac5a26SChuck Lever * Why release these outside of lru_cb ? There's no lock ordering 6430bac5a26SChuck Lever * problem since lru_cb currently takes no lock. 6440bac5a26SChuck Lever */ 6450bac5a26SChuck Lever static void nfsd_file_gc_dispose_list(struct list_head *dispose) 6460bac5a26SChuck Lever { 6470bac5a26SChuck Lever struct nfsd_file *nf; 6480bac5a26SChuck Lever 649cb7ec76eSChuck Lever list_for_each_entry(nf, dispose, nf_lru) 650cb7ec76eSChuck Lever nfsd_file_hash_remove(nf); 6510bac5a26SChuck Lever nfsd_file_dispose_list_delayed(dispose); 6529542e6a6STrond Myklebust } 6539542e6a6STrond Myklebust 6549542e6a6STrond Myklebust static void 6559542e6a6STrond Myklebust nfsd_file_gc(void) 6569542e6a6STrond Myklebust { 6573bc6d347SChuck Lever LIST_HEAD(dispose); 65894660cc1SChuck Lever unsigned long ret; 6593bc6d347SChuck Lever 66094660cc1SChuck Lever ret = list_lru_walk(&nfsd_file_lru, nfsd_file_lru_cb, 661edead3a5SChuck Lever &dispose, list_lru_count(&nfsd_file_lru)); 66294660cc1SChuck Lever trace_nfsd_file_gc_removed(ret, list_lru_count(&nfsd_file_lru)); 6633bc6d347SChuck Lever nfsd_file_gc_dispose_list(&dispose); 6649542e6a6STrond Myklebust } 6659542e6a6STrond Myklebust 6669542e6a6STrond Myklebust static void 6679542e6a6STrond Myklebust nfsd_file_gc_worker(struct work_struct *work) 6689542e6a6STrond Myklebust { 6699542e6a6STrond Myklebust nfsd_file_gc(); 6709542e6a6STrond Myklebust nfsd_file_schedule_laundrette(); 67165294c1fSJeff Layton } 67265294c1fSJeff Layton 67365294c1fSJeff Layton static unsigned long 67465294c1fSJeff Layton nfsd_file_lru_count(struct shrinker *s, struct shrink_control *sc) 67565294c1fSJeff Layton { 67665294c1fSJeff Layton return list_lru_count(&nfsd_file_lru); 67765294c1fSJeff Layton } 67865294c1fSJeff Layton 67965294c1fSJeff Layton static unsigned long 68065294c1fSJeff Layton nfsd_file_lru_scan(struct shrinker *s, struct shrink_control *sc) 68165294c1fSJeff Layton { 68239f1d1ffSChuck Lever LIST_HEAD(dispose); 68339f1d1ffSChuck Lever unsigned long ret; 68439f1d1ffSChuck Lever 68539f1d1ffSChuck Lever ret = list_lru_shrink_walk(&nfsd_file_lru, sc, 68639f1d1ffSChuck Lever nfsd_file_lru_cb, &dispose); 68794660cc1SChuck Lever trace_nfsd_file_shrinker_removed(ret, list_lru_count(&nfsd_file_lru)); 68839f1d1ffSChuck Lever nfsd_file_gc_dispose_list(&dispose); 68939f1d1ffSChuck Lever return ret; 69065294c1fSJeff Layton } 69165294c1fSJeff Layton 69265294c1fSJeff Layton static struct shrinker nfsd_file_shrinker = { 69365294c1fSJeff Layton .scan_objects = nfsd_file_lru_scan, 69465294c1fSJeff Layton .count_objects = nfsd_file_lru_count, 69565294c1fSJeff Layton .seeks = 1, 69665294c1fSJeff Layton }; 69765294c1fSJeff Layton 698a8455110SChuck Lever /* 699a8455110SChuck Lever * Find all cache items across all net namespaces that match @inode and 700a8455110SChuck Lever * move them to @dispose. The lookup is atomic wrt nfsd_file_acquire(). 701a8455110SChuck Lever */ 702a8455110SChuck Lever static unsigned int 703a8455110SChuck Lever __nfsd_file_close_inode(struct inode *inode, struct list_head *dispose) 70465294c1fSJeff Layton { 705ce502f81SChuck Lever struct nfsd_file_lookup_key key = { 706ce502f81SChuck Lever .type = NFSD_FILE_KEY_INODE, 707ce502f81SChuck Lever .inode = inode, 708ce502f81SChuck Lever }; 709a8455110SChuck Lever unsigned int count = 0; 71065294c1fSJeff Layton struct nfsd_file *nf; 71165294c1fSJeff Layton 712ce502f81SChuck Lever rcu_read_lock(); 713ce502f81SChuck Lever do { 714ce502f81SChuck Lever nf = rhashtable_lookup(&nfsd_file_rhash_tbl, &key, 715ce502f81SChuck Lever nfsd_file_rhash_params); 716ce502f81SChuck Lever if (!nf) 717ce502f81SChuck Lever break; 718ce502f81SChuck Lever nfsd_file_unhash_and_dispose(nf, dispose); 719a8455110SChuck Lever count++; 720ce502f81SChuck Lever } while (1); 721ce502f81SChuck Lever rcu_read_unlock(); 722a8455110SChuck Lever return count; 72365294c1fSJeff Layton } 72465294c1fSJeff Layton 72565294c1fSJeff Layton /** 72665294c1fSJeff Layton * nfsd_file_close_inode_sync - attempt to forcibly close a nfsd_file 72765294c1fSJeff Layton * @inode: inode of the file to attempt to remove 72865294c1fSJeff Layton * 729a8455110SChuck Lever * Unhash and put, then flush and fput all cache items associated with @inode. 73065294c1fSJeff Layton */ 73165294c1fSJeff Layton void 73265294c1fSJeff Layton nfsd_file_close_inode_sync(struct inode *inode) 73365294c1fSJeff Layton { 73465294c1fSJeff Layton LIST_HEAD(dispose); 735a8455110SChuck Lever unsigned int count; 73665294c1fSJeff Layton 737a8455110SChuck Lever count = __nfsd_file_close_inode(inode, &dispose); 738a8455110SChuck Lever trace_nfsd_file_close_inode_sync(inode, count); 73965294c1fSJeff Layton nfsd_file_dispose_list_sync(&dispose); 74065294c1fSJeff Layton } 74165294c1fSJeff Layton 74265294c1fSJeff Layton /** 74319598141STrond Myklebust * nfsd_file_close_inode - attempt a delayed close of a nfsd_file 74465294c1fSJeff Layton * @inode: inode of the file to attempt to remove 74565294c1fSJeff Layton * 746a8455110SChuck Lever * Unhash and put all cache item associated with @inode. 74765294c1fSJeff Layton */ 74865294c1fSJeff Layton static void 74965294c1fSJeff Layton nfsd_file_close_inode(struct inode *inode) 75065294c1fSJeff Layton { 75165294c1fSJeff Layton LIST_HEAD(dispose); 752a8455110SChuck Lever unsigned int count; 75365294c1fSJeff Layton 754a8455110SChuck Lever count = __nfsd_file_close_inode(inode, &dispose); 755a8455110SChuck Lever trace_nfsd_file_close_inode(inode, count); 7569542e6a6STrond Myklebust nfsd_file_dispose_list_delayed(&dispose); 75765294c1fSJeff Layton } 75865294c1fSJeff Layton 75965294c1fSJeff Layton /** 76065294c1fSJeff Layton * nfsd_file_delayed_close - close unused nfsd_files 76165294c1fSJeff Layton * @work: dummy 76265294c1fSJeff Layton * 76365294c1fSJeff Layton * Walk the LRU list and close any entries that have not been used since 76465294c1fSJeff Layton * the last scan. 76565294c1fSJeff Layton */ 76665294c1fSJeff Layton static void 76765294c1fSJeff Layton nfsd_file_delayed_close(struct work_struct *work) 76865294c1fSJeff Layton { 76965294c1fSJeff Layton LIST_HEAD(head); 7709542e6a6STrond Myklebust struct nfsd_fcache_disposal *l = container_of(work, 7719542e6a6STrond Myklebust struct nfsd_fcache_disposal, work); 77265294c1fSJeff Layton 7739542e6a6STrond Myklebust nfsd_file_list_remove_disposal(&head, l); 7749542e6a6STrond Myklebust nfsd_file_dispose_list(&head); 77565294c1fSJeff Layton } 77665294c1fSJeff Layton 77765294c1fSJeff Layton static int 77865294c1fSJeff Layton nfsd_file_lease_notifier_call(struct notifier_block *nb, unsigned long arg, 77965294c1fSJeff Layton void *data) 78065294c1fSJeff Layton { 78165294c1fSJeff Layton struct file_lock *fl = data; 78265294c1fSJeff Layton 78365294c1fSJeff Layton /* Only close files for F_SETLEASE leases */ 78465294c1fSJeff Layton if (fl->fl_flags & FL_LEASE) 78565294c1fSJeff Layton nfsd_file_close_inode_sync(file_inode(fl->fl_file)); 78665294c1fSJeff Layton return 0; 78765294c1fSJeff Layton } 78865294c1fSJeff Layton 78965294c1fSJeff Layton static struct notifier_block nfsd_file_lease_notifier = { 79065294c1fSJeff Layton .notifier_call = nfsd_file_lease_notifier_call, 79165294c1fSJeff Layton }; 79265294c1fSJeff Layton 79365294c1fSJeff Layton static int 794b9a1b977SAmir Goldstein nfsd_file_fsnotify_handle_event(struct fsnotify_mark *mark, u32 mask, 795b9a1b977SAmir Goldstein struct inode *inode, struct inode *dir, 796950cc0d2SAmir Goldstein const struct qstr *name, u32 cookie) 79765294c1fSJeff Layton { 79824dca905SGabriel Krisman Bertazi if (WARN_ON_ONCE(!inode)) 79924dca905SGabriel Krisman Bertazi return 0; 80024dca905SGabriel Krisman Bertazi 80165294c1fSJeff Layton trace_nfsd_file_fsnotify_handle_event(inode, mask); 80265294c1fSJeff Layton 80365294c1fSJeff Layton /* Should be no marks on non-regular files */ 80465294c1fSJeff Layton if (!S_ISREG(inode->i_mode)) { 80565294c1fSJeff Layton WARN_ON_ONCE(1); 80665294c1fSJeff Layton return 0; 80765294c1fSJeff Layton } 80865294c1fSJeff Layton 80965294c1fSJeff Layton /* don't close files if this was not the last link */ 81065294c1fSJeff Layton if (mask & FS_ATTRIB) { 81165294c1fSJeff Layton if (inode->i_nlink) 81265294c1fSJeff Layton return 0; 81365294c1fSJeff Layton } 81465294c1fSJeff Layton 81565294c1fSJeff Layton nfsd_file_close_inode(inode); 81665294c1fSJeff Layton return 0; 81765294c1fSJeff Layton } 81865294c1fSJeff Layton 81965294c1fSJeff Layton 82065294c1fSJeff Layton static const struct fsnotify_ops nfsd_file_fsnotify_ops = { 821b9a1b977SAmir Goldstein .handle_inode_event = nfsd_file_fsnotify_handle_event, 82265294c1fSJeff Layton .free_mark = nfsd_file_mark_free, 82365294c1fSJeff Layton }; 82465294c1fSJeff Layton 82565294c1fSJeff Layton int 82665294c1fSJeff Layton nfsd_file_cache_init(void) 82765294c1fSJeff Layton { 828fc22945eSChuck Lever int ret; 82965294c1fSJeff Layton 830c7b824c3SChuck Lever lockdep_assert_held(&nfsd_mutex); 831c7b824c3SChuck Lever if (test_and_set_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1) 83265294c1fSJeff Layton return 0; 83365294c1fSJeff Layton 834fc22945eSChuck Lever ret = rhashtable_init(&nfsd_file_rhash_tbl, &nfsd_file_rhash_params); 835fc22945eSChuck Lever if (ret) 836fc22945eSChuck Lever return ret; 837fc22945eSChuck Lever 838fc22945eSChuck Lever ret = -ENOMEM; 8399542e6a6STrond Myklebust nfsd_filecache_wq = alloc_workqueue("nfsd_filecache", 0, 0); 8409542e6a6STrond Myklebust if (!nfsd_filecache_wq) 8419542e6a6STrond Myklebust goto out; 8429542e6a6STrond Myklebust 84365294c1fSJeff Layton nfsd_file_slab = kmem_cache_create("nfsd_file", 84465294c1fSJeff Layton sizeof(struct nfsd_file), 0, 0, NULL); 84565294c1fSJeff Layton if (!nfsd_file_slab) { 84665294c1fSJeff Layton pr_err("nfsd: unable to create nfsd_file_slab\n"); 84765294c1fSJeff Layton goto out_err; 84865294c1fSJeff Layton } 84965294c1fSJeff Layton 85065294c1fSJeff Layton nfsd_file_mark_slab = kmem_cache_create("nfsd_file_mark", 85165294c1fSJeff Layton sizeof(struct nfsd_file_mark), 0, 0, NULL); 85265294c1fSJeff Layton if (!nfsd_file_mark_slab) { 85365294c1fSJeff Layton pr_err("nfsd: unable to create nfsd_file_mark_slab\n"); 85465294c1fSJeff Layton goto out_err; 85565294c1fSJeff Layton } 85665294c1fSJeff Layton 85765294c1fSJeff Layton 85865294c1fSJeff Layton ret = list_lru_init(&nfsd_file_lru); 85965294c1fSJeff Layton if (ret) { 86065294c1fSJeff Layton pr_err("nfsd: failed to init nfsd_file_lru: %d\n", ret); 86165294c1fSJeff Layton goto out_err; 86265294c1fSJeff Layton } 86365294c1fSJeff Layton 864e33c267aSRoman Gushchin ret = register_shrinker(&nfsd_file_shrinker, "nfsd-filecache"); 86565294c1fSJeff Layton if (ret) { 86665294c1fSJeff Layton pr_err("nfsd: failed to register nfsd_file_shrinker: %d\n", ret); 86765294c1fSJeff Layton goto out_lru; 86865294c1fSJeff Layton } 86965294c1fSJeff Layton 87065294c1fSJeff Layton ret = lease_register_notifier(&nfsd_file_lease_notifier); 87165294c1fSJeff Layton if (ret) { 87265294c1fSJeff Layton pr_err("nfsd: unable to register lease notifier: %d\n", ret); 87365294c1fSJeff Layton goto out_shrinker; 87465294c1fSJeff Layton } 87565294c1fSJeff Layton 876867a448dSAmir Goldstein nfsd_file_fsnotify_group = fsnotify_alloc_group(&nfsd_file_fsnotify_ops, 877b8962a9dSAmir Goldstein FSNOTIFY_GROUP_NOFS); 87865294c1fSJeff Layton if (IS_ERR(nfsd_file_fsnotify_group)) { 87965294c1fSJeff Layton pr_err("nfsd: unable to create fsnotify group: %ld\n", 88065294c1fSJeff Layton PTR_ERR(nfsd_file_fsnotify_group)); 881231307dfSHuang Guobin ret = PTR_ERR(nfsd_file_fsnotify_group); 88265294c1fSJeff Layton nfsd_file_fsnotify_group = NULL; 88365294c1fSJeff Layton goto out_notifier; 88465294c1fSJeff Layton } 88565294c1fSJeff Layton 8869542e6a6STrond Myklebust INIT_DELAYED_WORK(&nfsd_filecache_laundrette, nfsd_file_gc_worker); 88765294c1fSJeff Layton out: 88865294c1fSJeff Layton return ret; 88965294c1fSJeff Layton out_notifier: 89065294c1fSJeff Layton lease_unregister_notifier(&nfsd_file_lease_notifier); 89165294c1fSJeff Layton out_shrinker: 89265294c1fSJeff Layton unregister_shrinker(&nfsd_file_shrinker); 89365294c1fSJeff Layton out_lru: 89465294c1fSJeff Layton list_lru_destroy(&nfsd_file_lru); 89565294c1fSJeff Layton out_err: 89665294c1fSJeff Layton kmem_cache_destroy(nfsd_file_slab); 89765294c1fSJeff Layton nfsd_file_slab = NULL; 89865294c1fSJeff Layton kmem_cache_destroy(nfsd_file_mark_slab); 89965294c1fSJeff Layton nfsd_file_mark_slab = NULL; 9009542e6a6STrond Myklebust destroy_workqueue(nfsd_filecache_wq); 9019542e6a6STrond Myklebust nfsd_filecache_wq = NULL; 902fc22945eSChuck Lever rhashtable_destroy(&nfsd_file_rhash_tbl); 90365294c1fSJeff Layton goto out; 90465294c1fSJeff Layton } 90565294c1fSJeff Layton 906c7b824c3SChuck Lever static void 907c7b824c3SChuck Lever __nfsd_file_cache_purge(struct net *net) 90865294c1fSJeff Layton { 909ce502f81SChuck Lever struct rhashtable_iter iter; 91065294c1fSJeff Layton struct nfsd_file *nf; 91165294c1fSJeff Layton LIST_HEAD(dispose); 91265294c1fSJeff Layton 913ce502f81SChuck Lever rhashtable_walk_enter(&nfsd_file_rhash_tbl, &iter); 914ce502f81SChuck Lever do { 915ce502f81SChuck Lever rhashtable_walk_start(&iter); 91665294c1fSJeff Layton 917ce502f81SChuck Lever nf = rhashtable_walk_next(&iter); 918ce502f81SChuck Lever while (!IS_ERR_OR_NULL(nf)) { 919d3aefd2bSJeff Layton if (!net || nf->nf_net == net) 9208d0d254bSJeff Layton nfsd_file_unhash_and_dispose(nf, &dispose); 921ce502f81SChuck Lever nf = rhashtable_walk_next(&iter); 92265294c1fSJeff Layton } 923ce502f81SChuck Lever 924ce502f81SChuck Lever rhashtable_walk_stop(&iter); 925ce502f81SChuck Lever } while (nf == ERR_PTR(-EAGAIN)); 926ce502f81SChuck Lever rhashtable_walk_exit(&iter); 927ce502f81SChuck Lever 92865294c1fSJeff Layton nfsd_file_dispose_list(&dispose); 92965294c1fSJeff Layton } 93065294c1fSJeff Layton 9319542e6a6STrond Myklebust static struct nfsd_fcache_disposal * 9321463b38eSNeilBrown nfsd_alloc_fcache_disposal(void) 9339542e6a6STrond Myklebust { 9349542e6a6STrond Myklebust struct nfsd_fcache_disposal *l; 9359542e6a6STrond Myklebust 9369542e6a6STrond Myklebust l = kmalloc(sizeof(*l), GFP_KERNEL); 9379542e6a6STrond Myklebust if (!l) 9389542e6a6STrond Myklebust return NULL; 9399542e6a6STrond Myklebust INIT_WORK(&l->work, nfsd_file_delayed_close); 9409542e6a6STrond Myklebust spin_lock_init(&l->lock); 9419542e6a6STrond Myklebust INIT_LIST_HEAD(&l->freeme); 9429542e6a6STrond Myklebust return l; 9439542e6a6STrond Myklebust } 9449542e6a6STrond Myklebust 9459542e6a6STrond Myklebust static void 9469542e6a6STrond Myklebust nfsd_free_fcache_disposal(struct nfsd_fcache_disposal *l) 9479542e6a6STrond Myklebust { 9489542e6a6STrond Myklebust cancel_work_sync(&l->work); 9499542e6a6STrond Myklebust nfsd_file_dispose_list(&l->freeme); 9501463b38eSNeilBrown kfree(l); 9519542e6a6STrond Myklebust } 9529542e6a6STrond Myklebust 9539542e6a6STrond Myklebust static void 9549542e6a6STrond Myklebust nfsd_free_fcache_disposal_net(struct net *net) 9559542e6a6STrond Myklebust { 9561463b38eSNeilBrown struct nfsd_net *nn = net_generic(net, nfsd_net_id); 9571463b38eSNeilBrown struct nfsd_fcache_disposal *l = nn->fcache_disposal; 9589542e6a6STrond Myklebust 9599542e6a6STrond Myklebust nfsd_free_fcache_disposal(l); 9609542e6a6STrond Myklebust } 9619542e6a6STrond Myklebust 9629542e6a6STrond Myklebust int 9639542e6a6STrond Myklebust nfsd_file_cache_start_net(struct net *net) 9649542e6a6STrond Myklebust { 9651463b38eSNeilBrown struct nfsd_net *nn = net_generic(net, nfsd_net_id); 9661463b38eSNeilBrown 9671463b38eSNeilBrown nn->fcache_disposal = nfsd_alloc_fcache_disposal(); 9681463b38eSNeilBrown return nn->fcache_disposal ? 0 : -ENOMEM; 9699542e6a6STrond Myklebust } 9709542e6a6STrond Myklebust 971c7b824c3SChuck Lever /** 972c7b824c3SChuck Lever * nfsd_file_cache_purge - Remove all cache items associated with @net 973c7b824c3SChuck Lever * @net: target net namespace 974c7b824c3SChuck Lever * 975c7b824c3SChuck Lever */ 976c7b824c3SChuck Lever void 977c7b824c3SChuck Lever nfsd_file_cache_purge(struct net *net) 978c7b824c3SChuck Lever { 979c7b824c3SChuck Lever lockdep_assert_held(&nfsd_mutex); 980c7b824c3SChuck Lever if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1) 981c7b824c3SChuck Lever __nfsd_file_cache_purge(net); 982c7b824c3SChuck Lever } 983c7b824c3SChuck Lever 9849542e6a6STrond Myklebust void 9859542e6a6STrond Myklebust nfsd_file_cache_shutdown_net(struct net *net) 9869542e6a6STrond Myklebust { 9879542e6a6STrond Myklebust nfsd_file_cache_purge(net); 9889542e6a6STrond Myklebust nfsd_free_fcache_disposal_net(net); 9899542e6a6STrond Myklebust } 9909542e6a6STrond Myklebust 99165294c1fSJeff Layton void 99265294c1fSJeff Layton nfsd_file_cache_shutdown(void) 99365294c1fSJeff Layton { 9948b330f78SChuck Lever int i; 9958b330f78SChuck Lever 996c7b824c3SChuck Lever lockdep_assert_held(&nfsd_mutex); 997c7b824c3SChuck Lever if (test_and_clear_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 0) 998c7b824c3SChuck Lever return; 99965294c1fSJeff Layton 100065294c1fSJeff Layton lease_unregister_notifier(&nfsd_file_lease_notifier); 100165294c1fSJeff Layton unregister_shrinker(&nfsd_file_shrinker); 100265294c1fSJeff Layton /* 100365294c1fSJeff Layton * make sure all callers of nfsd_file_lru_cb are done before 100465294c1fSJeff Layton * calling nfsd_file_cache_purge 100565294c1fSJeff Layton */ 100665294c1fSJeff Layton cancel_delayed_work_sync(&nfsd_filecache_laundrette); 1007c7b824c3SChuck Lever __nfsd_file_cache_purge(NULL); 100865294c1fSJeff Layton list_lru_destroy(&nfsd_file_lru); 100965294c1fSJeff Layton rcu_barrier(); 101065294c1fSJeff Layton fsnotify_put_group(nfsd_file_fsnotify_group); 101165294c1fSJeff Layton nfsd_file_fsnotify_group = NULL; 101265294c1fSJeff Layton kmem_cache_destroy(nfsd_file_slab); 101365294c1fSJeff Layton nfsd_file_slab = NULL; 101465294c1fSJeff Layton fsnotify_wait_marks_destroyed(); 101565294c1fSJeff Layton kmem_cache_destroy(nfsd_file_mark_slab); 101665294c1fSJeff Layton nfsd_file_mark_slab = NULL; 10179542e6a6STrond Myklebust destroy_workqueue(nfsd_filecache_wq); 10189542e6a6STrond Myklebust nfsd_filecache_wq = NULL; 1019fc22945eSChuck Lever rhashtable_destroy(&nfsd_file_rhash_tbl); 102065294c1fSJeff Layton 10218b330f78SChuck Lever for_each_possible_cpu(i) { 10228b330f78SChuck Lever per_cpu(nfsd_file_cache_hits, i) = 0; 10238b330f78SChuck Lever per_cpu(nfsd_file_acquisitions, i) = 0; 10248b330f78SChuck Lever per_cpu(nfsd_file_releases, i) = 0; 10258b330f78SChuck Lever per_cpu(nfsd_file_total_age, i) = 0; 10268b330f78SChuck Lever per_cpu(nfsd_file_pages_flushed, i) = 0; 10278b330f78SChuck Lever per_cpu(nfsd_file_evictions, i) = 0; 102865294c1fSJeff Layton } 102965294c1fSJeff Layton } 103065294c1fSJeff Layton 103165294c1fSJeff Layton /** 1032ce502f81SChuck Lever * nfsd_file_is_cached - are there any cached open files for this inode? 1033ce502f81SChuck Lever * @inode: inode to check 103465294c1fSJeff Layton * 1035ce502f81SChuck Lever * The lookup matches inodes in all net namespaces and is atomic wrt 1036ce502f81SChuck Lever * nfsd_file_acquire(). 1037ce502f81SChuck Lever * 1038ce502f81SChuck Lever * Return values: 1039ce502f81SChuck Lever * %true: filecache contains at least one file matching this inode 1040ce502f81SChuck Lever * %false: filecache contains no files matching this inode 104165294c1fSJeff Layton */ 104265294c1fSJeff Layton bool 104365294c1fSJeff Layton nfsd_file_is_cached(struct inode *inode) 104465294c1fSJeff Layton { 1045ce502f81SChuck Lever struct nfsd_file_lookup_key key = { 1046ce502f81SChuck Lever .type = NFSD_FILE_KEY_INODE, 1047ce502f81SChuck Lever .inode = inode, 1048ce502f81SChuck Lever }; 104965294c1fSJeff Layton bool ret = false; 105065294c1fSJeff Layton 1051ce502f81SChuck Lever if (rhashtable_lookup_fast(&nfsd_file_rhash_tbl, &key, 1052ce502f81SChuck Lever nfsd_file_rhash_params) != NULL) 105365294c1fSJeff Layton ret = true; 105454f7df70SChuck Lever trace_nfsd_file_is_cached(inode, (int)ret); 105565294c1fSJeff Layton return ret; 105665294c1fSJeff Layton } 105765294c1fSJeff Layton 1058fb70bf12SChuck Lever static __be32 1059be023006SChuck Lever nfsd_file_do_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp, 10604d1ea845SChuck Lever unsigned int may_flags, struct nfsd_file **pnf, 10614d1ea845SChuck Lever bool open, bool want_gc) 106265294c1fSJeff Layton { 1063ce502f81SChuck Lever struct nfsd_file_lookup_key key = { 1064ce502f81SChuck Lever .type = NFSD_FILE_KEY_FULL, 1065ce502f81SChuck Lever .need = may_flags & NFSD_FILE_MAY_MASK, 1066ce502f81SChuck Lever .net = SVC_NET(rqstp), 10674d1ea845SChuck Lever .gc = want_gc, 1068ce502f81SChuck Lever }; 1069243a5263SJeff Layton bool open_retry = true; 1070243a5263SJeff Layton struct nfsd_file *nf; 1071ce502f81SChuck Lever __be32 status; 1072243a5263SJeff Layton int ret; 107365294c1fSJeff Layton 107465294c1fSJeff Layton status = fh_verify(rqstp, fhp, S_IFREG, 107565294c1fSJeff Layton may_flags|NFSD_MAY_OWNER_OVERRIDE); 107665294c1fSJeff Layton if (status != nfs_ok) 107765294c1fSJeff Layton return status; 1078ce502f81SChuck Lever key.inode = d_inode(fhp->fh_dentry); 1079ce502f81SChuck Lever key.cred = get_current_cred(); 108065294c1fSJeff Layton 108165294c1fSJeff Layton retry: 1082243a5263SJeff Layton rcu_read_lock(); 1083243a5263SJeff Layton nf = rhashtable_lookup(&nfsd_file_rhash_tbl, &key, 1084ce502f81SChuck Lever nfsd_file_rhash_params); 1085ce502f81SChuck Lever if (nf) 1086ce502f81SChuck Lever nf = nfsd_file_get(nf); 1087243a5263SJeff Layton rcu_read_unlock(); 108865294c1fSJeff Layton if (nf) 108965294c1fSJeff Layton goto wait_for_construction; 109065294c1fSJeff Layton 1091243a5263SJeff Layton nf = nfsd_file_alloc(&key, may_flags); 1092243a5263SJeff Layton if (!nf) { 109354f7df70SChuck Lever status = nfserr_jukebox; 109454f7df70SChuck Lever goto out_status; 109565294c1fSJeff Layton } 109665294c1fSJeff Layton 1097243a5263SJeff Layton ret = rhashtable_lookup_insert_key(&nfsd_file_rhash_tbl, 1098243a5263SJeff Layton &key, &nf->nf_rhash, 1099ce502f81SChuck Lever nfsd_file_rhash_params); 1100243a5263SJeff Layton if (likely(ret == 0)) 110165294c1fSJeff Layton goto open_file; 1102243a5263SJeff Layton 1103243a5263SJeff Layton nfsd_file_slab_free(&nf->nf_rcu); 1104bdd6b562SJeff Layton nf = NULL; 1105243a5263SJeff Layton if (ret == -EEXIST) 1106243a5263SJeff Layton goto retry; 1107243a5263SJeff Layton trace_nfsd_file_insert_err(rqstp, key.inode, may_flags, ret); 1108243a5263SJeff Layton status = nfserr_jukebox; 1109243a5263SJeff Layton goto out_status; 111065294c1fSJeff Layton 111165294c1fSJeff Layton wait_for_construction: 111265294c1fSJeff Layton wait_on_bit(&nf->nf_flags, NFSD_FILE_PENDING, TASK_UNINTERRUPTIBLE); 111365294c1fSJeff Layton 111465294c1fSJeff Layton /* Did construction of this file fail? */ 111565294c1fSJeff Layton if (!test_bit(NFSD_FILE_HASHED, &nf->nf_flags)) { 1116ce502f81SChuck Lever trace_nfsd_file_cons_err(rqstp, key.inode, may_flags, nf); 1117243a5263SJeff Layton if (!open_retry) { 111828c7d86bSTrond Myklebust status = nfserr_jukebox; 111928c7d86bSTrond Myklebust goto out; 112028c7d86bSTrond Myklebust } 1121243a5263SJeff Layton open_retry = false; 112265294c1fSJeff Layton nfsd_file_put_noref(nf); 112365294c1fSJeff Layton goto retry; 112465294c1fSJeff Layton } 112565294c1fSJeff Layton 11264a0e73e6SChuck Lever nfsd_file_lru_remove(nf); 112765294c1fSJeff Layton this_cpu_inc(nfsd_file_cache_hits); 112865294c1fSJeff Layton 112923ba98deSJeff Layton status = nfserrno(nfsd_open_break_lease(file_inode(nf->nf_file), may_flags)); 113065294c1fSJeff Layton out: 113165294c1fSJeff Layton if (status == nfs_ok) { 113229d4bdbbSChuck Lever if (open) 113329d4bdbbSChuck Lever this_cpu_inc(nfsd_file_acquisitions); 113465294c1fSJeff Layton *pnf = nf; 113565294c1fSJeff Layton } else { 113665294c1fSJeff Layton nfsd_file_put(nf); 113765294c1fSJeff Layton nf = NULL; 113865294c1fSJeff Layton } 113965294c1fSJeff Layton 114054f7df70SChuck Lever out_status: 1141ce502f81SChuck Lever put_cred(key.cred); 1142be023006SChuck Lever if (open) 1143ce502f81SChuck Lever trace_nfsd_file_acquire(rqstp, key.inode, may_flags, nf, status); 114465294c1fSJeff Layton return status; 114565294c1fSJeff Layton 114665294c1fSJeff Layton open_file: 1147b40a2839SChuck Lever trace_nfsd_file_alloc(nf); 1148427f5f83SChuck Lever nf->nf_mark = nfsd_file_mark_find_or_create(nf, key.inode); 1149fb70bf12SChuck Lever if (nf->nf_mark) { 11500122e882SChuck Lever if (open) { 1151f4d84c52SChuck Lever status = nfsd_open_verified(rqstp, fhp, may_flags, 1152f4d84c52SChuck Lever &nf->nf_file); 11530122e882SChuck Lever trace_nfsd_file_open(nf, status); 11540122e882SChuck Lever } else 1155fb70bf12SChuck Lever status = nfs_ok; 1156fb70bf12SChuck Lever } else 115765294c1fSJeff Layton status = nfserr_jukebox; 115865294c1fSJeff Layton /* 115965294c1fSJeff Layton * If construction failed, or we raced with a call to unlink() 116065294c1fSJeff Layton * then unhash. 116165294c1fSJeff Layton */ 1162ce502f81SChuck Lever if (status != nfs_ok || key.inode->i_nlink == 0) 11634d1ea845SChuck Lever nfsd_file_unhash_and_put(nf); 116465294c1fSJeff Layton clear_bit_unlock(NFSD_FILE_PENDING, &nf->nf_flags); 116565294c1fSJeff Layton smp_mb__after_atomic(); 116665294c1fSJeff Layton wake_up_bit(&nf->nf_flags, NFSD_FILE_PENDING); 116765294c1fSJeff Layton goto out; 116865294c1fSJeff Layton } 116965294c1fSJeff Layton 1170fb70bf12SChuck Lever /** 11714d1ea845SChuck Lever * nfsd_file_acquire_gc - Get a struct nfsd_file with an open file 11724d1ea845SChuck Lever * @rqstp: the RPC transaction being executed 11734d1ea845SChuck Lever * @fhp: the NFS filehandle of the file to be opened 11744d1ea845SChuck Lever * @may_flags: NFSD_MAY_ settings for the file 11754d1ea845SChuck Lever * @pnf: OUT: new or found "struct nfsd_file" object 11764d1ea845SChuck Lever * 11774d1ea845SChuck Lever * The nfsd_file object returned by this API is reference-counted 11784d1ea845SChuck Lever * and garbage-collected. The object is retained for a few 11794d1ea845SChuck Lever * seconds after the final nfsd_file_put() in case the caller 11804d1ea845SChuck Lever * wants to re-use it. 11814d1ea845SChuck Lever * 11824d1ea845SChuck Lever * Returns nfs_ok and sets @pnf on success; otherwise an nfsstat in 11834d1ea845SChuck Lever * network byte order is returned. 11844d1ea845SChuck Lever */ 11854d1ea845SChuck Lever __be32 11864d1ea845SChuck Lever nfsd_file_acquire_gc(struct svc_rqst *rqstp, struct svc_fh *fhp, 11874d1ea845SChuck Lever unsigned int may_flags, struct nfsd_file **pnf) 11884d1ea845SChuck Lever { 11894d1ea845SChuck Lever return nfsd_file_do_acquire(rqstp, fhp, may_flags, pnf, true, true); 11904d1ea845SChuck Lever } 11914d1ea845SChuck Lever 11924d1ea845SChuck Lever /** 1193fb70bf12SChuck Lever * nfsd_file_acquire - Get a struct nfsd_file with an open file 1194fb70bf12SChuck Lever * @rqstp: the RPC transaction being executed 1195fb70bf12SChuck Lever * @fhp: the NFS filehandle of the file to be opened 1196fb70bf12SChuck Lever * @may_flags: NFSD_MAY_ settings for the file 1197fb70bf12SChuck Lever * @pnf: OUT: new or found "struct nfsd_file" object 1198fb70bf12SChuck Lever * 11994d1ea845SChuck Lever * The nfsd_file_object returned by this API is reference-counted 12004d1ea845SChuck Lever * but not garbage-collected. The object is unhashed after the 12014d1ea845SChuck Lever * final nfsd_file_put(). 12024d1ea845SChuck Lever * 1203fb70bf12SChuck Lever * Returns nfs_ok and sets @pnf on success; otherwise an nfsstat in 1204fb70bf12SChuck Lever * network byte order is returned. 1205fb70bf12SChuck Lever */ 1206fb70bf12SChuck Lever __be32 1207fb70bf12SChuck Lever nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp, 1208fb70bf12SChuck Lever unsigned int may_flags, struct nfsd_file **pnf) 1209fb70bf12SChuck Lever { 12104d1ea845SChuck Lever return nfsd_file_do_acquire(rqstp, fhp, may_flags, pnf, true, false); 1211fb70bf12SChuck Lever } 1212fb70bf12SChuck Lever 1213fb70bf12SChuck Lever /** 1214fb70bf12SChuck Lever * nfsd_file_create - Get a struct nfsd_file, do not open 1215fb70bf12SChuck Lever * @rqstp: the RPC transaction being executed 1216fb70bf12SChuck Lever * @fhp: the NFS filehandle of the file just created 1217fb70bf12SChuck Lever * @may_flags: NFSD_MAY_ settings for the file 1218fb70bf12SChuck Lever * @pnf: OUT: new or found "struct nfsd_file" object 1219fb70bf12SChuck Lever * 12204d1ea845SChuck Lever * The nfsd_file_object returned by this API is reference-counted 12214d1ea845SChuck Lever * but not garbage-collected. The object is released immediately 12224d1ea845SChuck Lever * one RCU grace period after the final nfsd_file_put(). 12234d1ea845SChuck Lever * 1224fb70bf12SChuck Lever * Returns nfs_ok and sets @pnf on success; otherwise an nfsstat in 1225fb70bf12SChuck Lever * network byte order is returned. 1226fb70bf12SChuck Lever */ 1227fb70bf12SChuck Lever __be32 1228fb70bf12SChuck Lever nfsd_file_create(struct svc_rqst *rqstp, struct svc_fh *fhp, 1229fb70bf12SChuck Lever unsigned int may_flags, struct nfsd_file **pnf) 1230fb70bf12SChuck Lever { 12314d1ea845SChuck Lever return nfsd_file_do_acquire(rqstp, fhp, may_flags, pnf, false, false); 1232fb70bf12SChuck Lever } 1233fb70bf12SChuck Lever 123465294c1fSJeff Layton /* 123565294c1fSJeff Layton * Note that fields may be added, removed or reordered in the future. Programs 123665294c1fSJeff Layton * scraping this file for info should test the labels to ensure they're 123765294c1fSJeff Layton * getting the correct field. 123865294c1fSJeff Layton */ 12391342f9ddSChenXiaoSong int nfsd_file_cache_stats_show(struct seq_file *m, void *v) 124065294c1fSJeff Layton { 1241df2aff52SChuck Lever unsigned long releases = 0, pages_flushed = 0, evictions = 0; 1242df2aff52SChuck Lever unsigned long hits = 0, acquisitions = 0; 1243ce502f81SChuck Lever unsigned int i, count = 0, buckets = 0; 1244904940e9SChuck Lever unsigned long lru = 0, total_age = 0; 124565294c1fSJeff Layton 1246ce502f81SChuck Lever /* Serialize with server shutdown */ 124765294c1fSJeff Layton mutex_lock(&nfsd_mutex); 1248c7b824c3SChuck Lever if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1) { 1249ce502f81SChuck Lever struct bucket_table *tbl; 1250ce502f81SChuck Lever struct rhashtable *ht; 1251ce502f81SChuck Lever 12520fd244c1SChuck Lever lru = list_lru_count(&nfsd_file_lru); 1253ce502f81SChuck Lever 1254ce502f81SChuck Lever rcu_read_lock(); 1255ce502f81SChuck Lever ht = &nfsd_file_rhash_tbl; 1256ce502f81SChuck Lever count = atomic_read(&ht->nelems); 1257ce502f81SChuck Lever tbl = rht_dereference_rcu(ht->tbl, ht); 1258ce502f81SChuck Lever buckets = tbl->size; 1259ce502f81SChuck Lever rcu_read_unlock(); 126065294c1fSJeff Layton } 126165294c1fSJeff Layton mutex_unlock(&nfsd_mutex); 126265294c1fSJeff Layton 126329d4bdbbSChuck Lever for_each_possible_cpu(i) { 126465294c1fSJeff Layton hits += per_cpu(nfsd_file_cache_hits, i); 126529d4bdbbSChuck Lever acquisitions += per_cpu(nfsd_file_acquisitions, i); 1266d6329327SChuck Lever releases += per_cpu(nfsd_file_releases, i); 1267904940e9SChuck Lever total_age += per_cpu(nfsd_file_total_age, i); 126894660cc1SChuck Lever evictions += per_cpu(nfsd_file_evictions, i); 1269df2aff52SChuck Lever pages_flushed += per_cpu(nfsd_file_pages_flushed, i); 127029d4bdbbSChuck Lever } 127165294c1fSJeff Layton 127265294c1fSJeff Layton seq_printf(m, "total entries: %u\n", count); 1273ce502f81SChuck Lever seq_printf(m, "hash buckets: %u\n", buckets); 12740fd244c1SChuck Lever seq_printf(m, "lru entries: %lu\n", lru); 127565294c1fSJeff Layton seq_printf(m, "cache hits: %lu\n", hits); 127629d4bdbbSChuck Lever seq_printf(m, "acquisitions: %lu\n", acquisitions); 1277d6329327SChuck Lever seq_printf(m, "releases: %lu\n", releases); 127894660cc1SChuck Lever seq_printf(m, "evictions: %lu\n", evictions); 1279904940e9SChuck Lever if (releases) 1280904940e9SChuck Lever seq_printf(m, "mean age (ms): %ld\n", total_age / releases); 1281904940e9SChuck Lever else 1282904940e9SChuck Lever seq_printf(m, "mean age (ms): -\n"); 1283df2aff52SChuck Lever seq_printf(m, "pages flushed: %lu\n", pages_flushed); 128465294c1fSJeff Layton return 0; 128565294c1fSJeff Layton } 1286