xref: /openbmc/linux/fs/nfs/nfs42xattr.c (revision c2508730d6bb62196783b3de28a09782c97d4365)
195ad37f9SFrank van der Linden // SPDX-License-Identifier: GPL-2.0
295ad37f9SFrank van der Linden 
395ad37f9SFrank van der Linden /*
495ad37f9SFrank van der Linden  * Copyright 2019, 2020 Amazon.com, Inc. or its affiliates. All rights reserved.
595ad37f9SFrank van der Linden  *
695ad37f9SFrank van der Linden  * User extended attribute client side cache functions.
795ad37f9SFrank van der Linden  *
895ad37f9SFrank van der Linden  * Author: Frank van der Linden <fllinden@amazon.com>
995ad37f9SFrank van der Linden  */
1095ad37f9SFrank van der Linden #include <linux/errno.h>
1195ad37f9SFrank van der Linden #include <linux/nfs_fs.h>
1295ad37f9SFrank van der Linden #include <linux/hashtable.h>
1395ad37f9SFrank van der Linden #include <linux/refcount.h>
1495ad37f9SFrank van der Linden #include <uapi/linux/xattr.h>
1595ad37f9SFrank van der Linden 
1695ad37f9SFrank van der Linden #include "nfs4_fs.h"
1795ad37f9SFrank van der Linden #include "internal.h"
1895ad37f9SFrank van der Linden 
1995ad37f9SFrank van der Linden /*
2095ad37f9SFrank van der Linden  * User extended attributes client side caching is implemented by having
2195ad37f9SFrank van der Linden  * a cache structure attached to NFS inodes. This structure is allocated
2295ad37f9SFrank van der Linden  * when needed, and freed when the cache is zapped.
2395ad37f9SFrank van der Linden  *
2495ad37f9SFrank van der Linden  * The cache structure contains as hash table of entries, and a pointer
2595ad37f9SFrank van der Linden  * to a special-cased entry for the listxattr cache.
2695ad37f9SFrank van der Linden  *
2795ad37f9SFrank van der Linden  * Accessing and allocating / freeing the caches is done via reference
2895ad37f9SFrank van der Linden  * counting. The cache entries use a similar refcounting scheme.
2995ad37f9SFrank van der Linden  *
3095ad37f9SFrank van der Linden  * This makes freeing a cache, both from the shrinker and from the
3195ad37f9SFrank van der Linden  * zap cache path, easy. It also means that, in current use cases,
3295ad37f9SFrank van der Linden  * the large majority of inodes will not waste any memory, as they
3395ad37f9SFrank van der Linden  * will never have any user extended attributes assigned to them.
3495ad37f9SFrank van der Linden  *
3595ad37f9SFrank van der Linden  * Attribute entries are hashed in to a simple hash table. They are
3695ad37f9SFrank van der Linden  * also part of an LRU.
3795ad37f9SFrank van der Linden  *
3895ad37f9SFrank van der Linden  * There are three shrinkers.
3995ad37f9SFrank van der Linden  *
4095ad37f9SFrank van der Linden  * Two shrinkers deal with the cache entries themselves: one for
4195ad37f9SFrank van der Linden  * large entries (> PAGE_SIZE), and one for smaller entries. The
4295ad37f9SFrank van der Linden  * shrinker for the larger entries works more aggressively than
4395ad37f9SFrank van der Linden  * those for the smaller entries.
4495ad37f9SFrank van der Linden  *
4595ad37f9SFrank van der Linden  * The other shrinker frees the cache structures themselves.
4695ad37f9SFrank van der Linden  */
4795ad37f9SFrank van der Linden 
4895ad37f9SFrank van der Linden /*
4995ad37f9SFrank van der Linden  * 64 buckets is a good default. There is likely no reasonable
5095ad37f9SFrank van der Linden  * workload that uses more than even 64 user extended attributes.
5195ad37f9SFrank van der Linden  * You can certainly add a lot more - but you get what you ask for
5295ad37f9SFrank van der Linden  * in those circumstances.
5395ad37f9SFrank van der Linden  */
5495ad37f9SFrank van der Linden #define NFS4_XATTR_HASH_SIZE	64
5595ad37f9SFrank van der Linden 
5695ad37f9SFrank van der Linden #define NFSDBG_FACILITY	NFSDBG_XATTRCACHE
5795ad37f9SFrank van der Linden 
5895ad37f9SFrank van der Linden struct nfs4_xattr_cache;
5995ad37f9SFrank van der Linden struct nfs4_xattr_entry;
6095ad37f9SFrank van der Linden 
6195ad37f9SFrank van der Linden struct nfs4_xattr_bucket {
6295ad37f9SFrank van der Linden 	spinlock_t lock;
6395ad37f9SFrank van der Linden 	struct hlist_head hlist;
6495ad37f9SFrank van der Linden 	struct nfs4_xattr_cache *cache;
6595ad37f9SFrank van der Linden 	bool draining;
6695ad37f9SFrank van der Linden };
6795ad37f9SFrank van der Linden 
6895ad37f9SFrank van der Linden struct nfs4_xattr_cache {
6995ad37f9SFrank van der Linden 	struct kref ref;
7095ad37f9SFrank van der Linden 	struct nfs4_xattr_bucket buckets[NFS4_XATTR_HASH_SIZE];
7195ad37f9SFrank van der Linden 	struct list_head lru;
7295ad37f9SFrank van der Linden 	struct list_head dispose;
7395ad37f9SFrank van der Linden 	atomic_long_t nent;
7495ad37f9SFrank van der Linden 	spinlock_t listxattr_lock;
7595ad37f9SFrank van der Linden 	struct inode *inode;
7695ad37f9SFrank van der Linden 	struct nfs4_xattr_entry *listxattr;
7795ad37f9SFrank van der Linden };
7895ad37f9SFrank van der Linden 
7995ad37f9SFrank van der Linden struct nfs4_xattr_entry {
8095ad37f9SFrank van der Linden 	struct kref ref;
8195ad37f9SFrank van der Linden 	struct hlist_node hnode;
8295ad37f9SFrank van der Linden 	struct list_head lru;
8395ad37f9SFrank van der Linden 	struct list_head dispose;
8495ad37f9SFrank van der Linden 	char *xattr_name;
8595ad37f9SFrank van der Linden 	void *xattr_value;
8695ad37f9SFrank van der Linden 	size_t xattr_size;
8795ad37f9SFrank van der Linden 	struct nfs4_xattr_bucket *bucket;
8895ad37f9SFrank van der Linden 	uint32_t flags;
8995ad37f9SFrank van der Linden };
9095ad37f9SFrank van der Linden 
9195ad37f9SFrank van der Linden #define	NFS4_XATTR_ENTRY_EXTVAL	0x0001
9295ad37f9SFrank van der Linden 
9395ad37f9SFrank van der Linden /*
9495ad37f9SFrank van der Linden  * LRU list of NFS inodes that have xattr caches.
9595ad37f9SFrank van der Linden  */
9695ad37f9SFrank van der Linden static struct list_lru nfs4_xattr_cache_lru;
9795ad37f9SFrank van der Linden static struct list_lru nfs4_xattr_entry_lru;
9895ad37f9SFrank van der Linden static struct list_lru nfs4_xattr_large_entry_lru;
9995ad37f9SFrank van der Linden 
10095ad37f9SFrank van der Linden static struct kmem_cache *nfs4_xattr_cache_cachep;
10195ad37f9SFrank van der Linden 
10295ad37f9SFrank van der Linden /*
10395ad37f9SFrank van der Linden  * Hashing helper functions.
10495ad37f9SFrank van der Linden  */
10595ad37f9SFrank van der Linden static void
10695ad37f9SFrank van der Linden nfs4_xattr_hash_init(struct nfs4_xattr_cache *cache)
10795ad37f9SFrank van der Linden {
10895ad37f9SFrank van der Linden 	unsigned int i;
10995ad37f9SFrank van der Linden 
11095ad37f9SFrank van der Linden 	for (i = 0; i < NFS4_XATTR_HASH_SIZE; i++) {
11195ad37f9SFrank van der Linden 		INIT_HLIST_HEAD(&cache->buckets[i].hlist);
11295ad37f9SFrank van der Linden 		spin_lock_init(&cache->buckets[i].lock);
11395ad37f9SFrank van der Linden 		cache->buckets[i].cache = cache;
11495ad37f9SFrank van der Linden 		cache->buckets[i].draining = false;
11595ad37f9SFrank van der Linden 	}
11695ad37f9SFrank van der Linden }
11795ad37f9SFrank van der Linden 
11895ad37f9SFrank van der Linden /*
11995ad37f9SFrank van der Linden  * Locking order:
12095ad37f9SFrank van der Linden  * 1. inode i_lock or bucket lock
12195ad37f9SFrank van der Linden  * 2. list_lru lock (taken by list_lru_* functions)
12295ad37f9SFrank van der Linden  */
12395ad37f9SFrank van der Linden 
12495ad37f9SFrank van der Linden /*
12595ad37f9SFrank van der Linden  * Wrapper functions to add a cache entry to the right LRU.
12695ad37f9SFrank van der Linden  */
12795ad37f9SFrank van der Linden static bool
12895ad37f9SFrank van der Linden nfs4_xattr_entry_lru_add(struct nfs4_xattr_entry *entry)
12995ad37f9SFrank van der Linden {
13095ad37f9SFrank van der Linden 	struct list_lru *lru;
13195ad37f9SFrank van der Linden 
13295ad37f9SFrank van der Linden 	lru = (entry->flags & NFS4_XATTR_ENTRY_EXTVAL) ?
13395ad37f9SFrank van der Linden 	    &nfs4_xattr_large_entry_lru : &nfs4_xattr_entry_lru;
13495ad37f9SFrank van der Linden 
13595ad37f9SFrank van der Linden 	return list_lru_add(lru, &entry->lru);
13695ad37f9SFrank van der Linden }
13795ad37f9SFrank van der Linden 
13895ad37f9SFrank van der Linden static bool
13995ad37f9SFrank van der Linden nfs4_xattr_entry_lru_del(struct nfs4_xattr_entry *entry)
14095ad37f9SFrank van der Linden {
14195ad37f9SFrank van der Linden 	struct list_lru *lru;
14295ad37f9SFrank van der Linden 
14395ad37f9SFrank van der Linden 	lru = (entry->flags & NFS4_XATTR_ENTRY_EXTVAL) ?
14495ad37f9SFrank van der Linden 	    &nfs4_xattr_large_entry_lru : &nfs4_xattr_entry_lru;
14595ad37f9SFrank van der Linden 
14695ad37f9SFrank van der Linden 	return list_lru_del(lru, &entry->lru);
14795ad37f9SFrank van der Linden }
14895ad37f9SFrank van der Linden 
14995ad37f9SFrank van der Linden /*
15095ad37f9SFrank van der Linden  * This function allocates cache entries. They are the normal
15195ad37f9SFrank van der Linden  * extended attribute name/value pairs, but may also be a listxattr
15295ad37f9SFrank van der Linden  * cache. Those allocations use the same entry so that they can be
15395ad37f9SFrank van der Linden  * treated as one by the memory shrinker.
15495ad37f9SFrank van der Linden  *
15595ad37f9SFrank van der Linden  * xattr cache entries are allocated together with names. If the
15695ad37f9SFrank van der Linden  * value fits in to one page with the entry structure and the name,
15795ad37f9SFrank van der Linden  * it will also be part of the same allocation (kmalloc). This is
15895ad37f9SFrank van der Linden  * expected to be the vast majority of cases. Larger allocations
15995ad37f9SFrank van der Linden  * have a value pointer that is allocated separately by kvmalloc.
16095ad37f9SFrank van der Linden  *
16195ad37f9SFrank van der Linden  * Parameters:
16295ad37f9SFrank van der Linden  *
16395ad37f9SFrank van der Linden  * @name:  Name of the extended attribute. NULL for listxattr cache
16495ad37f9SFrank van der Linden  *         entry.
16595ad37f9SFrank van der Linden  * @value: Value of attribute, or listxattr cache. NULL if the
16695ad37f9SFrank van der Linden  *         value is to be copied from pages instead.
16795ad37f9SFrank van der Linden  * @pages: Pages to copy the value from, if not NULL. Passed in to
16895ad37f9SFrank van der Linden  *	   make it easier to copy the value after an RPC, even if
16995ad37f9SFrank van der Linden  *	   the value will not be passed up to application (e.g.
17095ad37f9SFrank van der Linden  *	   for a 'query' getxattr with NULL buffer).
171*c2508730SBhaskar Chowdhury  * @len:   Length of the value. Can be 0 for zero-length attributes.
17295ad37f9SFrank van der Linden  *         @value and @pages will be NULL if @len is 0.
17395ad37f9SFrank van der Linden  */
17495ad37f9SFrank van der Linden static struct nfs4_xattr_entry *
17595ad37f9SFrank van der Linden nfs4_xattr_alloc_entry(const char *name, const void *value,
17695ad37f9SFrank van der Linden 		       struct page **pages, size_t len)
17795ad37f9SFrank van der Linden {
17895ad37f9SFrank van der Linden 	struct nfs4_xattr_entry *entry;
17995ad37f9SFrank van der Linden 	void *valp;
18095ad37f9SFrank van der Linden 	char *namep;
18195ad37f9SFrank van der Linden 	size_t alloclen, slen;
18295ad37f9SFrank van der Linden 	char *buf;
18395ad37f9SFrank van der Linden 	uint32_t flags;
18495ad37f9SFrank van der Linden 
18595ad37f9SFrank van der Linden 	BUILD_BUG_ON(sizeof(struct nfs4_xattr_entry) +
18695ad37f9SFrank van der Linden 	    XATTR_NAME_MAX + 1 > PAGE_SIZE);
18795ad37f9SFrank van der Linden 
18895ad37f9SFrank van der Linden 	alloclen = sizeof(struct nfs4_xattr_entry);
18995ad37f9SFrank van der Linden 	if (name != NULL) {
19095ad37f9SFrank van der Linden 		slen = strlen(name) + 1;
19195ad37f9SFrank van der Linden 		alloclen += slen;
19295ad37f9SFrank van der Linden 	} else
19395ad37f9SFrank van der Linden 		slen = 0;
19495ad37f9SFrank van der Linden 
19595ad37f9SFrank van der Linden 	if (alloclen + len <= PAGE_SIZE) {
19695ad37f9SFrank van der Linden 		alloclen += len;
19795ad37f9SFrank van der Linden 		flags = 0;
19895ad37f9SFrank van der Linden 	} else {
19995ad37f9SFrank van der Linden 		flags = NFS4_XATTR_ENTRY_EXTVAL;
20095ad37f9SFrank van der Linden 	}
20195ad37f9SFrank van der Linden 
20295ad37f9SFrank van der Linden 	buf = kmalloc(alloclen, GFP_KERNEL_ACCOUNT | GFP_NOFS);
20395ad37f9SFrank van der Linden 	if (buf == NULL)
20495ad37f9SFrank van der Linden 		return NULL;
20595ad37f9SFrank van der Linden 	entry = (struct nfs4_xattr_entry *)buf;
20695ad37f9SFrank van der Linden 
20795ad37f9SFrank van der Linden 	if (name != NULL) {
20895ad37f9SFrank van der Linden 		namep = buf + sizeof(struct nfs4_xattr_entry);
20995ad37f9SFrank van der Linden 		memcpy(namep, name, slen);
21095ad37f9SFrank van der Linden 	} else {
21195ad37f9SFrank van der Linden 		namep = NULL;
21295ad37f9SFrank van der Linden 	}
21395ad37f9SFrank van der Linden 
21495ad37f9SFrank van der Linden 
21595ad37f9SFrank van der Linden 	if (flags & NFS4_XATTR_ENTRY_EXTVAL) {
21695ad37f9SFrank van der Linden 		valp = kvmalloc(len, GFP_KERNEL_ACCOUNT | GFP_NOFS);
21795ad37f9SFrank van der Linden 		if (valp == NULL) {
21895ad37f9SFrank van der Linden 			kfree(buf);
21995ad37f9SFrank van der Linden 			return NULL;
22095ad37f9SFrank van der Linden 		}
22195ad37f9SFrank van der Linden 	} else if (len != 0) {
22295ad37f9SFrank van der Linden 		valp = buf + sizeof(struct nfs4_xattr_entry) + slen;
22395ad37f9SFrank van der Linden 	} else
22495ad37f9SFrank van der Linden 		valp = NULL;
22595ad37f9SFrank van der Linden 
22695ad37f9SFrank van der Linden 	if (valp != NULL) {
22795ad37f9SFrank van der Linden 		if (value != NULL)
22895ad37f9SFrank van der Linden 			memcpy(valp, value, len);
22995ad37f9SFrank van der Linden 		else
23095ad37f9SFrank van der Linden 			_copy_from_pages(valp, pages, 0, len);
23195ad37f9SFrank van der Linden 	}
23295ad37f9SFrank van der Linden 
23395ad37f9SFrank van der Linden 	entry->flags = flags;
23495ad37f9SFrank van der Linden 	entry->xattr_value = valp;
23595ad37f9SFrank van der Linden 	kref_init(&entry->ref);
23695ad37f9SFrank van der Linden 	entry->xattr_name = namep;
23795ad37f9SFrank van der Linden 	entry->xattr_size = len;
23895ad37f9SFrank van der Linden 	entry->bucket = NULL;
23995ad37f9SFrank van der Linden 	INIT_LIST_HEAD(&entry->lru);
24095ad37f9SFrank van der Linden 	INIT_LIST_HEAD(&entry->dispose);
24195ad37f9SFrank van der Linden 	INIT_HLIST_NODE(&entry->hnode);
24295ad37f9SFrank van der Linden 
24395ad37f9SFrank van der Linden 	return entry;
24495ad37f9SFrank van der Linden }
24595ad37f9SFrank van der Linden 
24695ad37f9SFrank van der Linden static void
24795ad37f9SFrank van der Linden nfs4_xattr_free_entry(struct nfs4_xattr_entry *entry)
24895ad37f9SFrank van der Linden {
24995ad37f9SFrank van der Linden 	if (entry->flags & NFS4_XATTR_ENTRY_EXTVAL)
25095ad37f9SFrank van der Linden 		kvfree(entry->xattr_value);
25195ad37f9SFrank van der Linden 	kfree(entry);
25295ad37f9SFrank van der Linden }
25395ad37f9SFrank van der Linden 
25495ad37f9SFrank van der Linden static void
25595ad37f9SFrank van der Linden nfs4_xattr_free_entry_cb(struct kref *kref)
25695ad37f9SFrank van der Linden {
25795ad37f9SFrank van der Linden 	struct nfs4_xattr_entry *entry;
25895ad37f9SFrank van der Linden 
25995ad37f9SFrank van der Linden 	entry = container_of(kref, struct nfs4_xattr_entry, ref);
26095ad37f9SFrank van der Linden 
26195ad37f9SFrank van der Linden 	if (WARN_ON(!list_empty(&entry->lru)))
26295ad37f9SFrank van der Linden 		return;
26395ad37f9SFrank van der Linden 
26495ad37f9SFrank van der Linden 	nfs4_xattr_free_entry(entry);
26595ad37f9SFrank van der Linden }
26695ad37f9SFrank van der Linden 
26795ad37f9SFrank van der Linden static void
26895ad37f9SFrank van der Linden nfs4_xattr_free_cache_cb(struct kref *kref)
26995ad37f9SFrank van der Linden {
27095ad37f9SFrank van der Linden 	struct nfs4_xattr_cache *cache;
27195ad37f9SFrank van der Linden 	int i;
27295ad37f9SFrank van der Linden 
27395ad37f9SFrank van der Linden 	cache = container_of(kref, struct nfs4_xattr_cache, ref);
27495ad37f9SFrank van der Linden 
27595ad37f9SFrank van der Linden 	for (i = 0; i < NFS4_XATTR_HASH_SIZE; i++) {
27695ad37f9SFrank van der Linden 		if (WARN_ON(!hlist_empty(&cache->buckets[i].hlist)))
27795ad37f9SFrank van der Linden 			return;
27895ad37f9SFrank van der Linden 		cache->buckets[i].draining = false;
27995ad37f9SFrank van der Linden 	}
28095ad37f9SFrank van der Linden 
28195ad37f9SFrank van der Linden 	cache->listxattr = NULL;
28295ad37f9SFrank van der Linden 
28395ad37f9SFrank van der Linden 	kmem_cache_free(nfs4_xattr_cache_cachep, cache);
28495ad37f9SFrank van der Linden 
28595ad37f9SFrank van der Linden }
28695ad37f9SFrank van der Linden 
28795ad37f9SFrank van der Linden static struct nfs4_xattr_cache *
28895ad37f9SFrank van der Linden nfs4_xattr_alloc_cache(void)
28995ad37f9SFrank van der Linden {
29095ad37f9SFrank van der Linden 	struct nfs4_xattr_cache *cache;
29195ad37f9SFrank van der Linden 
29295ad37f9SFrank van der Linden 	cache = kmem_cache_alloc(nfs4_xattr_cache_cachep,
29395ad37f9SFrank van der Linden 	    GFP_KERNEL_ACCOUNT | GFP_NOFS);
29495ad37f9SFrank van der Linden 	if (cache == NULL)
29595ad37f9SFrank van der Linden 		return NULL;
29695ad37f9SFrank van der Linden 
29795ad37f9SFrank van der Linden 	kref_init(&cache->ref);
29895ad37f9SFrank van der Linden 	atomic_long_set(&cache->nent, 0);
29995ad37f9SFrank van der Linden 
30095ad37f9SFrank van der Linden 	return cache;
30195ad37f9SFrank van der Linden }
30295ad37f9SFrank van der Linden 
30395ad37f9SFrank van der Linden /*
30495ad37f9SFrank van der Linden  * Set the listxattr cache, which is a special-cased cache entry.
30595ad37f9SFrank van der Linden  * The special value ERR_PTR(-ESTALE) is used to indicate that
30695ad37f9SFrank van der Linden  * the cache is being drained - this prevents a new listxattr
30795ad37f9SFrank van der Linden  * cache from being added to what is now a stale cache.
30895ad37f9SFrank van der Linden  */
30995ad37f9SFrank van der Linden static int
31095ad37f9SFrank van der Linden nfs4_xattr_set_listcache(struct nfs4_xattr_cache *cache,
31195ad37f9SFrank van der Linden 			 struct nfs4_xattr_entry *new)
31295ad37f9SFrank van der Linden {
31395ad37f9SFrank van der Linden 	struct nfs4_xattr_entry *old;
31495ad37f9SFrank van der Linden 	int ret = 1;
31595ad37f9SFrank van der Linden 
31695ad37f9SFrank van der Linden 	spin_lock(&cache->listxattr_lock);
31795ad37f9SFrank van der Linden 
31895ad37f9SFrank van der Linden 	old = cache->listxattr;
31995ad37f9SFrank van der Linden 
32095ad37f9SFrank van der Linden 	if (old == ERR_PTR(-ESTALE)) {
32195ad37f9SFrank van der Linden 		ret = 0;
32295ad37f9SFrank van der Linden 		goto out;
32395ad37f9SFrank van der Linden 	}
32495ad37f9SFrank van der Linden 
32595ad37f9SFrank van der Linden 	cache->listxattr = new;
32695ad37f9SFrank van der Linden 	if (new != NULL && new != ERR_PTR(-ESTALE))
32795ad37f9SFrank van der Linden 		nfs4_xattr_entry_lru_add(new);
32895ad37f9SFrank van der Linden 
32995ad37f9SFrank van der Linden 	if (old != NULL) {
33095ad37f9SFrank van der Linden 		nfs4_xattr_entry_lru_del(old);
33195ad37f9SFrank van der Linden 		kref_put(&old->ref, nfs4_xattr_free_entry_cb);
33295ad37f9SFrank van der Linden 	}
33395ad37f9SFrank van der Linden out:
33495ad37f9SFrank van der Linden 	spin_unlock(&cache->listxattr_lock);
33595ad37f9SFrank van der Linden 
33695ad37f9SFrank van der Linden 	return ret;
33795ad37f9SFrank van der Linden }
33895ad37f9SFrank van der Linden 
33995ad37f9SFrank van der Linden /*
34095ad37f9SFrank van der Linden  * Unlink a cache from its parent inode, clearing out an invalid
34195ad37f9SFrank van der Linden  * cache. Must be called with i_lock held.
34295ad37f9SFrank van der Linden  */
34395ad37f9SFrank van der Linden static struct nfs4_xattr_cache *
34495ad37f9SFrank van der Linden nfs4_xattr_cache_unlink(struct inode *inode)
34595ad37f9SFrank van der Linden {
34695ad37f9SFrank van der Linden 	struct nfs_inode *nfsi;
34795ad37f9SFrank van der Linden 	struct nfs4_xattr_cache *oldcache;
34895ad37f9SFrank van der Linden 
34995ad37f9SFrank van der Linden 	nfsi = NFS_I(inode);
35095ad37f9SFrank van der Linden 
35195ad37f9SFrank van der Linden 	oldcache = nfsi->xattr_cache;
35295ad37f9SFrank van der Linden 	if (oldcache != NULL) {
35395ad37f9SFrank van der Linden 		list_lru_del(&nfs4_xattr_cache_lru, &oldcache->lru);
35495ad37f9SFrank van der Linden 		oldcache->inode = NULL;
35595ad37f9SFrank van der Linden 	}
35695ad37f9SFrank van der Linden 	nfsi->xattr_cache = NULL;
35795ad37f9SFrank van der Linden 	nfsi->cache_validity &= ~NFS_INO_INVALID_XATTR;
35895ad37f9SFrank van der Linden 
35995ad37f9SFrank van der Linden 	return oldcache;
36095ad37f9SFrank van der Linden 
36195ad37f9SFrank van der Linden }
36295ad37f9SFrank van der Linden 
36395ad37f9SFrank van der Linden /*
364048c397aSFrank van der Linden  * Discard a cache. Called by get_cache() if there was an old,
365048c397aSFrank van der Linden  * invalid cache. Can also be called from a shrinker callback.
36695ad37f9SFrank van der Linden  *
36795ad37f9SFrank van der Linden  * The cache is dead, it has already been unlinked from its inode,
36895ad37f9SFrank van der Linden  * and no longer appears on the cache LRU list.
36995ad37f9SFrank van der Linden  *
37095ad37f9SFrank van der Linden  * Mark all buckets as draining, so that no new entries are added. This
37195ad37f9SFrank van der Linden  * could still happen in the unlikely, but possible case that another
37295ad37f9SFrank van der Linden  * thread had grabbed a reference before it was unlinked from the inode,
37395ad37f9SFrank van der Linden  * and is still holding it for an add operation.
37495ad37f9SFrank van der Linden  *
37595ad37f9SFrank van der Linden  * Remove all entries from the LRU lists, so that there is no longer
37695ad37f9SFrank van der Linden  * any way to 'find' this cache. Then, remove the entries from the hash
37795ad37f9SFrank van der Linden  * table.
37895ad37f9SFrank van der Linden  *
37995ad37f9SFrank van der Linden  * At that point, the cache will remain empty and can be freed when the final
38095ad37f9SFrank van der Linden  * reference drops, which is very likely the kref_put at the end of
38195ad37f9SFrank van der Linden  * this function, or the one called immediately afterwards in the
38295ad37f9SFrank van der Linden  * shrinker callback.
38395ad37f9SFrank van der Linden  */
38495ad37f9SFrank van der Linden static void
38595ad37f9SFrank van der Linden nfs4_xattr_discard_cache(struct nfs4_xattr_cache *cache)
38695ad37f9SFrank van der Linden {
38795ad37f9SFrank van der Linden 	unsigned int i;
38895ad37f9SFrank van der Linden 	struct nfs4_xattr_entry *entry;
38995ad37f9SFrank van der Linden 	struct nfs4_xattr_bucket *bucket;
39095ad37f9SFrank van der Linden 	struct hlist_node *n;
39195ad37f9SFrank van der Linden 
39295ad37f9SFrank van der Linden 	nfs4_xattr_set_listcache(cache, ERR_PTR(-ESTALE));
39395ad37f9SFrank van der Linden 
39495ad37f9SFrank van der Linden 	for (i = 0; i < NFS4_XATTR_HASH_SIZE; i++) {
39595ad37f9SFrank van der Linden 		bucket = &cache->buckets[i];
39695ad37f9SFrank van der Linden 
39795ad37f9SFrank van der Linden 		spin_lock(&bucket->lock);
39895ad37f9SFrank van der Linden 		bucket->draining = true;
39995ad37f9SFrank van der Linden 		hlist_for_each_entry_safe(entry, n, &bucket->hlist, hnode) {
40095ad37f9SFrank van der Linden 			nfs4_xattr_entry_lru_del(entry);
40195ad37f9SFrank van der Linden 			hlist_del_init(&entry->hnode);
40295ad37f9SFrank van der Linden 			kref_put(&entry->ref, nfs4_xattr_free_entry_cb);
40395ad37f9SFrank van der Linden 		}
40495ad37f9SFrank van der Linden 		spin_unlock(&bucket->lock);
40595ad37f9SFrank van der Linden 	}
40695ad37f9SFrank van der Linden 
40795ad37f9SFrank van der Linden 	atomic_long_set(&cache->nent, 0);
40895ad37f9SFrank van der Linden 
40995ad37f9SFrank van der Linden 	kref_put(&cache->ref, nfs4_xattr_free_cache_cb);
41095ad37f9SFrank van der Linden }
41195ad37f9SFrank van der Linden 
41295ad37f9SFrank van der Linden /*
41395ad37f9SFrank van der Linden  * Get a referenced copy of the cache structure. Avoid doing allocs
41495ad37f9SFrank van der Linden  * while holding i_lock. Which means that we do some optimistic allocation,
41595ad37f9SFrank van der Linden  * and might have to free the result in rare cases.
41695ad37f9SFrank van der Linden  *
41795ad37f9SFrank van der Linden  * This function only checks the NFS_INO_INVALID_XATTR cache validity bit
41895ad37f9SFrank van der Linden  * and acts accordingly, replacing the cache when needed. For the read case
41995ad37f9SFrank van der Linden  * (!add), this means that the caller must make sure that the cache
42095ad37f9SFrank van der Linden  * is valid before caling this function. getxattr and listxattr call
42195ad37f9SFrank van der Linden  * revalidate_inode to do this. The attribute cache timeout (for the
42295ad37f9SFrank van der Linden  * non-delegated case) is expected to be dealt with in the revalidate
42395ad37f9SFrank van der Linden  * call.
42495ad37f9SFrank van der Linden  */
42595ad37f9SFrank van der Linden 
42695ad37f9SFrank van der Linden static struct nfs4_xattr_cache *
42795ad37f9SFrank van der Linden nfs4_xattr_get_cache(struct inode *inode, int add)
42895ad37f9SFrank van der Linden {
42995ad37f9SFrank van der Linden 	struct nfs_inode *nfsi;
43095ad37f9SFrank van der Linden 	struct nfs4_xattr_cache *cache, *oldcache, *newcache;
43195ad37f9SFrank van der Linden 
43295ad37f9SFrank van der Linden 	nfsi = NFS_I(inode);
43395ad37f9SFrank van der Linden 
43495ad37f9SFrank van der Linden 	cache = oldcache = NULL;
43595ad37f9SFrank van der Linden 
43695ad37f9SFrank van der Linden 	spin_lock(&inode->i_lock);
43795ad37f9SFrank van der Linden 
43895ad37f9SFrank van der Linden 	if (nfsi->cache_validity & NFS_INO_INVALID_XATTR)
43995ad37f9SFrank van der Linden 		oldcache = nfs4_xattr_cache_unlink(inode);
44095ad37f9SFrank van der Linden 	else
44195ad37f9SFrank van der Linden 		cache = nfsi->xattr_cache;
44295ad37f9SFrank van der Linden 
44395ad37f9SFrank van der Linden 	if (cache != NULL)
44495ad37f9SFrank van der Linden 		kref_get(&cache->ref);
44595ad37f9SFrank van der Linden 
44695ad37f9SFrank van der Linden 	spin_unlock(&inode->i_lock);
44795ad37f9SFrank van der Linden 
44895ad37f9SFrank van der Linden 	if (add && cache == NULL) {
44995ad37f9SFrank van der Linden 		newcache = NULL;
45095ad37f9SFrank van der Linden 
45195ad37f9SFrank van der Linden 		cache = nfs4_xattr_alloc_cache();
45295ad37f9SFrank van der Linden 		if (cache == NULL)
45395ad37f9SFrank van der Linden 			goto out;
45495ad37f9SFrank van der Linden 
45595ad37f9SFrank van der Linden 		spin_lock(&inode->i_lock);
45695ad37f9SFrank van der Linden 		if (nfsi->cache_validity & NFS_INO_INVALID_XATTR) {
45795ad37f9SFrank van der Linden 			/*
45895ad37f9SFrank van der Linden 			 * The cache was invalidated again. Give up,
45995ad37f9SFrank van der Linden 			 * since what we want to enter is now likely
46095ad37f9SFrank van der Linden 			 * outdated anyway.
46195ad37f9SFrank van der Linden 			 */
46295ad37f9SFrank van der Linden 			spin_unlock(&inode->i_lock);
46395ad37f9SFrank van der Linden 			kref_put(&cache->ref, nfs4_xattr_free_cache_cb);
46495ad37f9SFrank van der Linden 			cache = NULL;
46595ad37f9SFrank van der Linden 			goto out;
46695ad37f9SFrank van der Linden 		}
46795ad37f9SFrank van der Linden 
46895ad37f9SFrank van der Linden 		/*
46995ad37f9SFrank van der Linden 		 * Check if someone beat us to it.
47095ad37f9SFrank van der Linden 		 */
47195ad37f9SFrank van der Linden 		if (nfsi->xattr_cache != NULL) {
47295ad37f9SFrank van der Linden 			newcache = nfsi->xattr_cache;
47395ad37f9SFrank van der Linden 			kref_get(&newcache->ref);
47495ad37f9SFrank van der Linden 		} else {
47595ad37f9SFrank van der Linden 			kref_get(&cache->ref);
47695ad37f9SFrank van der Linden 			nfsi->xattr_cache = cache;
47795ad37f9SFrank van der Linden 			cache->inode = inode;
47895ad37f9SFrank van der Linden 			list_lru_add(&nfs4_xattr_cache_lru, &cache->lru);
47995ad37f9SFrank van der Linden 		}
48095ad37f9SFrank van der Linden 
48195ad37f9SFrank van der Linden 		spin_unlock(&inode->i_lock);
48295ad37f9SFrank van der Linden 
48395ad37f9SFrank van der Linden 		/*
48495ad37f9SFrank van der Linden 		 * If there was a race, throw away the cache we just
48595ad37f9SFrank van der Linden 		 * allocated, and use the new one allocated by someone
48695ad37f9SFrank van der Linden 		 * else.
48795ad37f9SFrank van der Linden 		 */
48895ad37f9SFrank van der Linden 		if (newcache != NULL) {
48995ad37f9SFrank van der Linden 			kref_put(&cache->ref, nfs4_xattr_free_cache_cb);
49095ad37f9SFrank van der Linden 			cache = newcache;
49195ad37f9SFrank van der Linden 		}
49295ad37f9SFrank van der Linden 	}
49395ad37f9SFrank van der Linden 
49495ad37f9SFrank van der Linden out:
49595ad37f9SFrank van der Linden 	/*
496048c397aSFrank van der Linden 	 * Discard the now orphaned old cache.
49795ad37f9SFrank van der Linden 	 */
49895ad37f9SFrank van der Linden 	if (oldcache != NULL)
499048c397aSFrank van der Linden 		nfs4_xattr_discard_cache(oldcache);
50095ad37f9SFrank van der Linden 
50195ad37f9SFrank van der Linden 	return cache;
50295ad37f9SFrank van der Linden }
50395ad37f9SFrank van der Linden 
50495ad37f9SFrank van der Linden static inline struct nfs4_xattr_bucket *
50595ad37f9SFrank van der Linden nfs4_xattr_hash_bucket(struct nfs4_xattr_cache *cache, const char *name)
50695ad37f9SFrank van der Linden {
50795ad37f9SFrank van der Linden 	return &cache->buckets[jhash(name, strlen(name), 0) &
50895ad37f9SFrank van der Linden 	    (ARRAY_SIZE(cache->buckets) - 1)];
50995ad37f9SFrank van der Linden }
51095ad37f9SFrank van der Linden 
51195ad37f9SFrank van der Linden static struct nfs4_xattr_entry *
51295ad37f9SFrank van der Linden nfs4_xattr_get_entry(struct nfs4_xattr_bucket *bucket, const char *name)
51395ad37f9SFrank van der Linden {
51495ad37f9SFrank van der Linden 	struct nfs4_xattr_entry *entry;
51595ad37f9SFrank van der Linden 
51695ad37f9SFrank van der Linden 	entry = NULL;
51795ad37f9SFrank van der Linden 
51895ad37f9SFrank van der Linden 	hlist_for_each_entry(entry, &bucket->hlist, hnode) {
51995ad37f9SFrank van der Linden 		if (!strcmp(entry->xattr_name, name))
52095ad37f9SFrank van der Linden 			break;
52195ad37f9SFrank van der Linden 	}
52295ad37f9SFrank van der Linden 
52395ad37f9SFrank van der Linden 	return entry;
52495ad37f9SFrank van der Linden }
52595ad37f9SFrank van der Linden 
52695ad37f9SFrank van der Linden static int
52795ad37f9SFrank van der Linden nfs4_xattr_hash_add(struct nfs4_xattr_cache *cache,
52895ad37f9SFrank van der Linden 		    struct nfs4_xattr_entry *entry)
52995ad37f9SFrank van der Linden {
53095ad37f9SFrank van der Linden 	struct nfs4_xattr_bucket *bucket;
53195ad37f9SFrank van der Linden 	struct nfs4_xattr_entry *oldentry = NULL;
53295ad37f9SFrank van der Linden 	int ret = 1;
53395ad37f9SFrank van der Linden 
53495ad37f9SFrank van der Linden 	bucket = nfs4_xattr_hash_bucket(cache, entry->xattr_name);
53595ad37f9SFrank van der Linden 	entry->bucket = bucket;
53695ad37f9SFrank van der Linden 
53795ad37f9SFrank van der Linden 	spin_lock(&bucket->lock);
53895ad37f9SFrank van der Linden 
53995ad37f9SFrank van der Linden 	if (bucket->draining) {
54095ad37f9SFrank van der Linden 		ret = 0;
54195ad37f9SFrank van der Linden 		goto out;
54295ad37f9SFrank van der Linden 	}
54395ad37f9SFrank van der Linden 
54495ad37f9SFrank van der Linden 	oldentry = nfs4_xattr_get_entry(bucket, entry->xattr_name);
54595ad37f9SFrank van der Linden 	if (oldentry != NULL) {
54695ad37f9SFrank van der Linden 		hlist_del_init(&oldentry->hnode);
54795ad37f9SFrank van der Linden 		nfs4_xattr_entry_lru_del(oldentry);
54895ad37f9SFrank van der Linden 	} else {
54995ad37f9SFrank van der Linden 		atomic_long_inc(&cache->nent);
55095ad37f9SFrank van der Linden 	}
55195ad37f9SFrank van der Linden 
55295ad37f9SFrank van der Linden 	hlist_add_head(&entry->hnode, &bucket->hlist);
55395ad37f9SFrank van der Linden 	nfs4_xattr_entry_lru_add(entry);
55495ad37f9SFrank van der Linden 
55595ad37f9SFrank van der Linden out:
55695ad37f9SFrank van der Linden 	spin_unlock(&bucket->lock);
55795ad37f9SFrank van der Linden 
55895ad37f9SFrank van der Linden 	if (oldentry != NULL)
55995ad37f9SFrank van der Linden 		kref_put(&oldentry->ref, nfs4_xattr_free_entry_cb);
56095ad37f9SFrank van der Linden 
56195ad37f9SFrank van der Linden 	return ret;
56295ad37f9SFrank van der Linden }
56395ad37f9SFrank van der Linden 
56495ad37f9SFrank van der Linden static void
56595ad37f9SFrank van der Linden nfs4_xattr_hash_remove(struct nfs4_xattr_cache *cache, const char *name)
56695ad37f9SFrank van der Linden {
56795ad37f9SFrank van der Linden 	struct nfs4_xattr_bucket *bucket;
56895ad37f9SFrank van der Linden 	struct nfs4_xattr_entry *entry;
56995ad37f9SFrank van der Linden 
57095ad37f9SFrank van der Linden 	bucket = nfs4_xattr_hash_bucket(cache, name);
57195ad37f9SFrank van der Linden 
57295ad37f9SFrank van der Linden 	spin_lock(&bucket->lock);
57395ad37f9SFrank van der Linden 
57495ad37f9SFrank van der Linden 	entry = nfs4_xattr_get_entry(bucket, name);
57595ad37f9SFrank van der Linden 	if (entry != NULL) {
57695ad37f9SFrank van der Linden 		hlist_del_init(&entry->hnode);
57795ad37f9SFrank van der Linden 		nfs4_xattr_entry_lru_del(entry);
57895ad37f9SFrank van der Linden 		atomic_long_dec(&cache->nent);
57995ad37f9SFrank van der Linden 	}
58095ad37f9SFrank van der Linden 
58195ad37f9SFrank van der Linden 	spin_unlock(&bucket->lock);
58295ad37f9SFrank van der Linden 
58395ad37f9SFrank van der Linden 	if (entry != NULL)
58495ad37f9SFrank van der Linden 		kref_put(&entry->ref, nfs4_xattr_free_entry_cb);
58595ad37f9SFrank van der Linden }
58695ad37f9SFrank van der Linden 
58795ad37f9SFrank van der Linden static struct nfs4_xattr_entry *
58895ad37f9SFrank van der Linden nfs4_xattr_hash_find(struct nfs4_xattr_cache *cache, const char *name)
58995ad37f9SFrank van der Linden {
59095ad37f9SFrank van der Linden 	struct nfs4_xattr_bucket *bucket;
59195ad37f9SFrank van der Linden 	struct nfs4_xattr_entry *entry;
59295ad37f9SFrank van der Linden 
59395ad37f9SFrank van der Linden 	bucket = nfs4_xattr_hash_bucket(cache, name);
59495ad37f9SFrank van der Linden 
59595ad37f9SFrank van der Linden 	spin_lock(&bucket->lock);
59695ad37f9SFrank van der Linden 
59795ad37f9SFrank van der Linden 	entry = nfs4_xattr_get_entry(bucket, name);
59895ad37f9SFrank van der Linden 	if (entry != NULL)
59995ad37f9SFrank van der Linden 		kref_get(&entry->ref);
60095ad37f9SFrank van der Linden 
60195ad37f9SFrank van der Linden 	spin_unlock(&bucket->lock);
60295ad37f9SFrank van der Linden 
60395ad37f9SFrank van der Linden 	return entry;
60495ad37f9SFrank van der Linden }
60595ad37f9SFrank van der Linden 
60695ad37f9SFrank van der Linden /*
60795ad37f9SFrank van der Linden  * Entry point to retrieve an entry from the cache.
60895ad37f9SFrank van der Linden  */
60995ad37f9SFrank van der Linden ssize_t nfs4_xattr_cache_get(struct inode *inode, const char *name, char *buf,
61095ad37f9SFrank van der Linden 			 ssize_t buflen)
61195ad37f9SFrank van der Linden {
61295ad37f9SFrank van der Linden 	struct nfs4_xattr_cache *cache;
61395ad37f9SFrank van der Linden 	struct nfs4_xattr_entry *entry;
61495ad37f9SFrank van der Linden 	ssize_t ret;
61595ad37f9SFrank van der Linden 
61695ad37f9SFrank van der Linden 	cache = nfs4_xattr_get_cache(inode, 0);
61795ad37f9SFrank van der Linden 	if (cache == NULL)
61895ad37f9SFrank van der Linden 		return -ENOENT;
61995ad37f9SFrank van der Linden 
62095ad37f9SFrank van der Linden 	ret = 0;
62195ad37f9SFrank van der Linden 	entry = nfs4_xattr_hash_find(cache, name);
62295ad37f9SFrank van der Linden 
62395ad37f9SFrank van der Linden 	if (entry != NULL) {
62495ad37f9SFrank van der Linden 		dprintk("%s: cache hit '%s', len %lu\n", __func__,
62595ad37f9SFrank van der Linden 		    entry->xattr_name, (unsigned long)entry->xattr_size);
62695ad37f9SFrank van der Linden 		if (buflen == 0) {
62795ad37f9SFrank van der Linden 			/* Length probe only */
62895ad37f9SFrank van der Linden 			ret = entry->xattr_size;
62995ad37f9SFrank van der Linden 		} else if (buflen < entry->xattr_size)
63095ad37f9SFrank van der Linden 			ret = -ERANGE;
63195ad37f9SFrank van der Linden 		else {
63295ad37f9SFrank van der Linden 			memcpy(buf, entry->xattr_value, entry->xattr_size);
63395ad37f9SFrank van der Linden 			ret = entry->xattr_size;
63495ad37f9SFrank van der Linden 		}
63595ad37f9SFrank van der Linden 		kref_put(&entry->ref, nfs4_xattr_free_entry_cb);
63695ad37f9SFrank van der Linden 	} else {
63795ad37f9SFrank van der Linden 		dprintk("%s: cache miss '%s'\n", __func__, name);
63895ad37f9SFrank van der Linden 		ret = -ENOENT;
63995ad37f9SFrank van der Linden 	}
64095ad37f9SFrank van der Linden 
64195ad37f9SFrank van der Linden 	kref_put(&cache->ref, nfs4_xattr_free_cache_cb);
64295ad37f9SFrank van der Linden 
64395ad37f9SFrank van der Linden 	return ret;
64495ad37f9SFrank van der Linden }
64595ad37f9SFrank van der Linden 
64695ad37f9SFrank van der Linden /*
64795ad37f9SFrank van der Linden  * Retrieve a cached list of xattrs from the cache.
64895ad37f9SFrank van der Linden  */
64995ad37f9SFrank van der Linden ssize_t nfs4_xattr_cache_list(struct inode *inode, char *buf, ssize_t buflen)
65095ad37f9SFrank van der Linden {
65195ad37f9SFrank van der Linden 	struct nfs4_xattr_cache *cache;
65295ad37f9SFrank van der Linden 	struct nfs4_xattr_entry *entry;
65395ad37f9SFrank van der Linden 	ssize_t ret;
65495ad37f9SFrank van der Linden 
65595ad37f9SFrank van der Linden 	cache = nfs4_xattr_get_cache(inode, 0);
65695ad37f9SFrank van der Linden 	if (cache == NULL)
65795ad37f9SFrank van der Linden 		return -ENOENT;
65895ad37f9SFrank van der Linden 
65995ad37f9SFrank van der Linden 	spin_lock(&cache->listxattr_lock);
66095ad37f9SFrank van der Linden 
66195ad37f9SFrank van der Linden 	entry = cache->listxattr;
66295ad37f9SFrank van der Linden 
66395ad37f9SFrank van der Linden 	if (entry != NULL && entry != ERR_PTR(-ESTALE)) {
66495ad37f9SFrank van der Linden 		if (buflen == 0) {
66595ad37f9SFrank van der Linden 			/* Length probe only */
66695ad37f9SFrank van der Linden 			ret = entry->xattr_size;
66795ad37f9SFrank van der Linden 		} else if (entry->xattr_size > buflen)
66895ad37f9SFrank van der Linden 			ret = -ERANGE;
66995ad37f9SFrank van der Linden 		else {
67095ad37f9SFrank van der Linden 			memcpy(buf, entry->xattr_value, entry->xattr_size);
67195ad37f9SFrank van der Linden 			ret = entry->xattr_size;
67295ad37f9SFrank van der Linden 		}
67395ad37f9SFrank van der Linden 	} else {
67495ad37f9SFrank van der Linden 		ret = -ENOENT;
67595ad37f9SFrank van der Linden 	}
67695ad37f9SFrank van der Linden 
67795ad37f9SFrank van der Linden 	spin_unlock(&cache->listxattr_lock);
67895ad37f9SFrank van der Linden 
67995ad37f9SFrank van der Linden 	kref_put(&cache->ref, nfs4_xattr_free_cache_cb);
68095ad37f9SFrank van der Linden 
68195ad37f9SFrank van der Linden 	return ret;
68295ad37f9SFrank van der Linden }
68395ad37f9SFrank van der Linden 
68495ad37f9SFrank van der Linden /*
68595ad37f9SFrank van der Linden  * Add an xattr to the cache.
68695ad37f9SFrank van der Linden  *
68795ad37f9SFrank van der Linden  * This also invalidates the xattr list cache.
68895ad37f9SFrank van der Linden  */
68995ad37f9SFrank van der Linden void nfs4_xattr_cache_add(struct inode *inode, const char *name,
69095ad37f9SFrank van der Linden 			  const char *buf, struct page **pages, ssize_t buflen)
69195ad37f9SFrank van der Linden {
69295ad37f9SFrank van der Linden 	struct nfs4_xattr_cache *cache;
69395ad37f9SFrank van der Linden 	struct nfs4_xattr_entry *entry;
69495ad37f9SFrank van der Linden 
69595ad37f9SFrank van der Linden 	dprintk("%s: add '%s' len %lu\n", __func__,
69695ad37f9SFrank van der Linden 	    name, (unsigned long)buflen);
69795ad37f9SFrank van der Linden 
69895ad37f9SFrank van der Linden 	cache = nfs4_xattr_get_cache(inode, 1);
69995ad37f9SFrank van der Linden 	if (cache == NULL)
70095ad37f9SFrank van der Linden 		return;
70195ad37f9SFrank van der Linden 
70295ad37f9SFrank van der Linden 	entry = nfs4_xattr_alloc_entry(name, buf, pages, buflen);
70395ad37f9SFrank van der Linden 	if (entry == NULL)
70495ad37f9SFrank van der Linden 		goto out;
70595ad37f9SFrank van der Linden 
70695ad37f9SFrank van der Linden 	(void)nfs4_xattr_set_listcache(cache, NULL);
70795ad37f9SFrank van der Linden 
70895ad37f9SFrank van der Linden 	if (!nfs4_xattr_hash_add(cache, entry))
70995ad37f9SFrank van der Linden 		kref_put(&entry->ref, nfs4_xattr_free_entry_cb);
71095ad37f9SFrank van der Linden 
71195ad37f9SFrank van der Linden out:
71295ad37f9SFrank van der Linden 	kref_put(&cache->ref, nfs4_xattr_free_cache_cb);
71395ad37f9SFrank van der Linden }
71495ad37f9SFrank van der Linden 
71595ad37f9SFrank van der Linden 
71695ad37f9SFrank van der Linden /*
71795ad37f9SFrank van der Linden  * Remove an xattr from the cache.
71895ad37f9SFrank van der Linden  *
71995ad37f9SFrank van der Linden  * This also invalidates the xattr list cache.
72095ad37f9SFrank van der Linden  */
72195ad37f9SFrank van der Linden void nfs4_xattr_cache_remove(struct inode *inode, const char *name)
72295ad37f9SFrank van der Linden {
72395ad37f9SFrank van der Linden 	struct nfs4_xattr_cache *cache;
72495ad37f9SFrank van der Linden 
72595ad37f9SFrank van der Linden 	dprintk("%s: remove '%s'\n", __func__, name);
72695ad37f9SFrank van der Linden 
72795ad37f9SFrank van der Linden 	cache = nfs4_xattr_get_cache(inode, 0);
72895ad37f9SFrank van der Linden 	if (cache == NULL)
72995ad37f9SFrank van der Linden 		return;
73095ad37f9SFrank van der Linden 
73195ad37f9SFrank van der Linden 	(void)nfs4_xattr_set_listcache(cache, NULL);
73295ad37f9SFrank van der Linden 	nfs4_xattr_hash_remove(cache, name);
73395ad37f9SFrank van der Linden 
73495ad37f9SFrank van der Linden 	kref_put(&cache->ref, nfs4_xattr_free_cache_cb);
73595ad37f9SFrank van der Linden }
73695ad37f9SFrank van der Linden 
73795ad37f9SFrank van der Linden /*
73895ad37f9SFrank van der Linden  * Cache listxattr output, replacing any possible old one.
73995ad37f9SFrank van der Linden  */
74095ad37f9SFrank van der Linden void nfs4_xattr_cache_set_list(struct inode *inode, const char *buf,
74195ad37f9SFrank van der Linden 			       ssize_t buflen)
74295ad37f9SFrank van der Linden {
74395ad37f9SFrank van der Linden 	struct nfs4_xattr_cache *cache;
74495ad37f9SFrank van der Linden 	struct nfs4_xattr_entry *entry;
74595ad37f9SFrank van der Linden 
74695ad37f9SFrank van der Linden 	cache = nfs4_xattr_get_cache(inode, 1);
74795ad37f9SFrank van der Linden 	if (cache == NULL)
74895ad37f9SFrank van der Linden 		return;
74995ad37f9SFrank van der Linden 
75095ad37f9SFrank van der Linden 	entry = nfs4_xattr_alloc_entry(NULL, buf, NULL, buflen);
75195ad37f9SFrank van der Linden 	if (entry == NULL)
75295ad37f9SFrank van der Linden 		goto out;
75395ad37f9SFrank van der Linden 
75495ad37f9SFrank van der Linden 	/*
75595ad37f9SFrank van der Linden 	 * This is just there to be able to get to bucket->cache,
75695ad37f9SFrank van der Linden 	 * which is obviously the same for all buckets, so just
75795ad37f9SFrank van der Linden 	 * use bucket 0.
75895ad37f9SFrank van der Linden 	 */
75995ad37f9SFrank van der Linden 	entry->bucket = &cache->buckets[0];
76095ad37f9SFrank van der Linden 
76195ad37f9SFrank van der Linden 	if (!nfs4_xattr_set_listcache(cache, entry))
76295ad37f9SFrank van der Linden 		kref_put(&entry->ref, nfs4_xattr_free_entry_cb);
76395ad37f9SFrank van der Linden 
76495ad37f9SFrank van der Linden out:
76595ad37f9SFrank van der Linden 	kref_put(&cache->ref, nfs4_xattr_free_cache_cb);
76695ad37f9SFrank van der Linden }
76795ad37f9SFrank van der Linden 
76895ad37f9SFrank van der Linden /*
76995ad37f9SFrank van der Linden  * Zap the entire cache. Called when an inode is evicted.
77095ad37f9SFrank van der Linden  */
77195ad37f9SFrank van der Linden void nfs4_xattr_cache_zap(struct inode *inode)
77295ad37f9SFrank van der Linden {
77395ad37f9SFrank van der Linden 	struct nfs4_xattr_cache *oldcache;
77495ad37f9SFrank van der Linden 
77595ad37f9SFrank van der Linden 	spin_lock(&inode->i_lock);
77695ad37f9SFrank van der Linden 	oldcache = nfs4_xattr_cache_unlink(inode);
77795ad37f9SFrank van der Linden 	spin_unlock(&inode->i_lock);
77895ad37f9SFrank van der Linden 
77995ad37f9SFrank van der Linden 	if (oldcache)
78095ad37f9SFrank van der Linden 		nfs4_xattr_discard_cache(oldcache);
78195ad37f9SFrank van der Linden }
78295ad37f9SFrank van der Linden 
78395ad37f9SFrank van der Linden /*
78495ad37f9SFrank van der Linden  * The entry LRU is shrunk more aggressively than the cache LRU,
78595ad37f9SFrank van der Linden  * by settings @seeks to 1.
78695ad37f9SFrank van der Linden  *
78795ad37f9SFrank van der Linden  * Cache structures are freed only when they've become empty, after
78895ad37f9SFrank van der Linden  * pruning all but one entry.
78995ad37f9SFrank van der Linden  */
79095ad37f9SFrank van der Linden 
79195ad37f9SFrank van der Linden static unsigned long nfs4_xattr_cache_count(struct shrinker *shrink,
79295ad37f9SFrank van der Linden 					    struct shrink_control *sc);
79395ad37f9SFrank van der Linden static unsigned long nfs4_xattr_entry_count(struct shrinker *shrink,
79495ad37f9SFrank van der Linden 					    struct shrink_control *sc);
79595ad37f9SFrank van der Linden static unsigned long nfs4_xattr_cache_scan(struct shrinker *shrink,
79695ad37f9SFrank van der Linden 					   struct shrink_control *sc);
79795ad37f9SFrank van der Linden static unsigned long nfs4_xattr_entry_scan(struct shrinker *shrink,
79895ad37f9SFrank van der Linden 					   struct shrink_control *sc);
79995ad37f9SFrank van der Linden 
80095ad37f9SFrank van der Linden static struct shrinker nfs4_xattr_cache_shrinker = {
80195ad37f9SFrank van der Linden 	.count_objects	= nfs4_xattr_cache_count,
80295ad37f9SFrank van der Linden 	.scan_objects	= nfs4_xattr_cache_scan,
80395ad37f9SFrank van der Linden 	.seeks		= DEFAULT_SEEKS,
80495ad37f9SFrank van der Linden 	.flags		= SHRINKER_MEMCG_AWARE,
80595ad37f9SFrank van der Linden };
80695ad37f9SFrank van der Linden 
80795ad37f9SFrank van der Linden static struct shrinker nfs4_xattr_entry_shrinker = {
80895ad37f9SFrank van der Linden 	.count_objects	= nfs4_xattr_entry_count,
80995ad37f9SFrank van der Linden 	.scan_objects	= nfs4_xattr_entry_scan,
81095ad37f9SFrank van der Linden 	.seeks		= DEFAULT_SEEKS,
81195ad37f9SFrank van der Linden 	.batch		= 512,
81295ad37f9SFrank van der Linden 	.flags		= SHRINKER_MEMCG_AWARE,
81395ad37f9SFrank van der Linden };
81495ad37f9SFrank van der Linden 
81595ad37f9SFrank van der Linden static struct shrinker nfs4_xattr_large_entry_shrinker = {
81695ad37f9SFrank van der Linden 	.count_objects	= nfs4_xattr_entry_count,
81795ad37f9SFrank van der Linden 	.scan_objects	= nfs4_xattr_entry_scan,
81895ad37f9SFrank van der Linden 	.seeks		= 1,
81995ad37f9SFrank van der Linden 	.batch		= 512,
82095ad37f9SFrank van der Linden 	.flags		= SHRINKER_MEMCG_AWARE,
82195ad37f9SFrank van der Linden };
82295ad37f9SFrank van der Linden 
82395ad37f9SFrank van der Linden static enum lru_status
82495ad37f9SFrank van der Linden cache_lru_isolate(struct list_head *item,
82595ad37f9SFrank van der Linden 	struct list_lru_one *lru, spinlock_t *lru_lock, void *arg)
82695ad37f9SFrank van der Linden {
82795ad37f9SFrank van der Linden 	struct list_head *dispose = arg;
82895ad37f9SFrank van der Linden 	struct inode *inode;
82995ad37f9SFrank van der Linden 	struct nfs4_xattr_cache *cache = container_of(item,
83095ad37f9SFrank van der Linden 	    struct nfs4_xattr_cache, lru);
83195ad37f9SFrank van der Linden 
83295ad37f9SFrank van der Linden 	if (atomic_long_read(&cache->nent) > 1)
83395ad37f9SFrank van der Linden 		return LRU_SKIP;
83495ad37f9SFrank van der Linden 
83595ad37f9SFrank van der Linden 	/*
83695ad37f9SFrank van der Linden 	 * If a cache structure is on the LRU list, we know that
83795ad37f9SFrank van der Linden 	 * its inode is valid. Try to lock it to break the link.
83895ad37f9SFrank van der Linden 	 * Since we're inverting the lock order here, only try.
83995ad37f9SFrank van der Linden 	 */
84095ad37f9SFrank van der Linden 	inode = cache->inode;
84195ad37f9SFrank van der Linden 
84295ad37f9SFrank van der Linden 	if (!spin_trylock(&inode->i_lock))
84395ad37f9SFrank van der Linden 		return LRU_SKIP;
84495ad37f9SFrank van der Linden 
84595ad37f9SFrank van der Linden 	kref_get(&cache->ref);
84695ad37f9SFrank van der Linden 
84795ad37f9SFrank van der Linden 	cache->inode = NULL;
84895ad37f9SFrank van der Linden 	NFS_I(inode)->xattr_cache = NULL;
84995ad37f9SFrank van der Linden 	NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_XATTR;
85095ad37f9SFrank van der Linden 	list_lru_isolate(lru, &cache->lru);
85195ad37f9SFrank van der Linden 
85295ad37f9SFrank van der Linden 	spin_unlock(&inode->i_lock);
85395ad37f9SFrank van der Linden 
85495ad37f9SFrank van der Linden 	list_add_tail(&cache->dispose, dispose);
85595ad37f9SFrank van der Linden 	return LRU_REMOVED;
85695ad37f9SFrank van der Linden }
85795ad37f9SFrank van der Linden 
85895ad37f9SFrank van der Linden static unsigned long
85995ad37f9SFrank van der Linden nfs4_xattr_cache_scan(struct shrinker *shrink, struct shrink_control *sc)
86095ad37f9SFrank van der Linden {
86195ad37f9SFrank van der Linden 	LIST_HEAD(dispose);
86295ad37f9SFrank van der Linden 	unsigned long freed;
86395ad37f9SFrank van der Linden 	struct nfs4_xattr_cache *cache;
86495ad37f9SFrank van der Linden 
86595ad37f9SFrank van der Linden 	freed = list_lru_shrink_walk(&nfs4_xattr_cache_lru, sc,
86695ad37f9SFrank van der Linden 	    cache_lru_isolate, &dispose);
86795ad37f9SFrank van der Linden 	while (!list_empty(&dispose)) {
86895ad37f9SFrank van der Linden 		cache = list_first_entry(&dispose, struct nfs4_xattr_cache,
86995ad37f9SFrank van der Linden 		    dispose);
87095ad37f9SFrank van der Linden 		list_del_init(&cache->dispose);
87195ad37f9SFrank van der Linden 		nfs4_xattr_discard_cache(cache);
87295ad37f9SFrank van der Linden 		kref_put(&cache->ref, nfs4_xattr_free_cache_cb);
87395ad37f9SFrank van der Linden 	}
87495ad37f9SFrank van der Linden 
87595ad37f9SFrank van der Linden 	return freed;
87695ad37f9SFrank van der Linden }
87795ad37f9SFrank van der Linden 
87895ad37f9SFrank van der Linden 
87995ad37f9SFrank van der Linden static unsigned long
88095ad37f9SFrank van der Linden nfs4_xattr_cache_count(struct shrinker *shrink, struct shrink_control *sc)
88195ad37f9SFrank van der Linden {
88295ad37f9SFrank van der Linden 	unsigned long count;
88395ad37f9SFrank van der Linden 
8845904c16dSYang Shi 	count = list_lru_shrink_count(&nfs4_xattr_cache_lru, sc);
88595ad37f9SFrank van der Linden 	return vfs_pressure_ratio(count);
88695ad37f9SFrank van der Linden }
88795ad37f9SFrank van der Linden 
88895ad37f9SFrank van der Linden static enum lru_status
88995ad37f9SFrank van der Linden entry_lru_isolate(struct list_head *item,
89095ad37f9SFrank van der Linden 	struct list_lru_one *lru, spinlock_t *lru_lock, void *arg)
89195ad37f9SFrank van der Linden {
89295ad37f9SFrank van der Linden 	struct list_head *dispose = arg;
89395ad37f9SFrank van der Linden 	struct nfs4_xattr_bucket *bucket;
89495ad37f9SFrank van der Linden 	struct nfs4_xattr_cache *cache;
89595ad37f9SFrank van der Linden 	struct nfs4_xattr_entry *entry = container_of(item,
89695ad37f9SFrank van der Linden 	    struct nfs4_xattr_entry, lru);
89795ad37f9SFrank van der Linden 
89895ad37f9SFrank van der Linden 	bucket = entry->bucket;
89995ad37f9SFrank van der Linden 	cache = bucket->cache;
90095ad37f9SFrank van der Linden 
90195ad37f9SFrank van der Linden 	/*
90295ad37f9SFrank van der Linden 	 * Unhook the entry from its parent (either a cache bucket
90395ad37f9SFrank van der Linden 	 * or a cache structure if it's a listxattr buf), so that
90495ad37f9SFrank van der Linden 	 * it's no longer found. Then add it to the isolate list,
90595ad37f9SFrank van der Linden 	 * to be freed later.
90695ad37f9SFrank van der Linden 	 *
90795ad37f9SFrank van der Linden 	 * In both cases, we're reverting lock order, so use
90895ad37f9SFrank van der Linden 	 * trylock and skip the entry if we can't get the lock.
90995ad37f9SFrank van der Linden 	 */
91095ad37f9SFrank van der Linden 	if (entry->xattr_name != NULL) {
91195ad37f9SFrank van der Linden 		/* Regular cache entry */
91295ad37f9SFrank van der Linden 		if (!spin_trylock(&bucket->lock))
91395ad37f9SFrank van der Linden 			return LRU_SKIP;
91495ad37f9SFrank van der Linden 
91595ad37f9SFrank van der Linden 		kref_get(&entry->ref);
91695ad37f9SFrank van der Linden 
91795ad37f9SFrank van der Linden 		hlist_del_init(&entry->hnode);
91895ad37f9SFrank van der Linden 		atomic_long_dec(&cache->nent);
91995ad37f9SFrank van der Linden 		list_lru_isolate(lru, &entry->lru);
92095ad37f9SFrank van der Linden 
92195ad37f9SFrank van der Linden 		spin_unlock(&bucket->lock);
92295ad37f9SFrank van der Linden 	} else {
92395ad37f9SFrank van der Linden 		/* Listxattr cache entry */
92495ad37f9SFrank van der Linden 		if (!spin_trylock(&cache->listxattr_lock))
92595ad37f9SFrank van der Linden 			return LRU_SKIP;
92695ad37f9SFrank van der Linden 
92795ad37f9SFrank van der Linden 		kref_get(&entry->ref);
92895ad37f9SFrank van der Linden 
92995ad37f9SFrank van der Linden 		cache->listxattr = NULL;
93095ad37f9SFrank van der Linden 		list_lru_isolate(lru, &entry->lru);
93195ad37f9SFrank van der Linden 
93295ad37f9SFrank van der Linden 		spin_unlock(&cache->listxattr_lock);
93395ad37f9SFrank van der Linden 	}
93495ad37f9SFrank van der Linden 
93595ad37f9SFrank van der Linden 	list_add_tail(&entry->dispose, dispose);
93695ad37f9SFrank van der Linden 	return LRU_REMOVED;
93795ad37f9SFrank van der Linden }
93895ad37f9SFrank van der Linden 
93995ad37f9SFrank van der Linden static unsigned long
94095ad37f9SFrank van der Linden nfs4_xattr_entry_scan(struct shrinker *shrink, struct shrink_control *sc)
94195ad37f9SFrank van der Linden {
94295ad37f9SFrank van der Linden 	LIST_HEAD(dispose);
94395ad37f9SFrank van der Linden 	unsigned long freed;
94495ad37f9SFrank van der Linden 	struct nfs4_xattr_entry *entry;
94595ad37f9SFrank van der Linden 	struct list_lru *lru;
94695ad37f9SFrank van der Linden 
94795ad37f9SFrank van der Linden 	lru = (shrink == &nfs4_xattr_large_entry_shrinker) ?
94895ad37f9SFrank van der Linden 	    &nfs4_xattr_large_entry_lru : &nfs4_xattr_entry_lru;
94995ad37f9SFrank van der Linden 
95095ad37f9SFrank van der Linden 	freed = list_lru_shrink_walk(lru, sc, entry_lru_isolate, &dispose);
95195ad37f9SFrank van der Linden 
95295ad37f9SFrank van der Linden 	while (!list_empty(&dispose)) {
95395ad37f9SFrank van der Linden 		entry = list_first_entry(&dispose, struct nfs4_xattr_entry,
95495ad37f9SFrank van der Linden 		    dispose);
95595ad37f9SFrank van der Linden 		list_del_init(&entry->dispose);
95695ad37f9SFrank van der Linden 
95795ad37f9SFrank van der Linden 		/*
95895ad37f9SFrank van der Linden 		 * Drop two references: the one that we just grabbed
95995ad37f9SFrank van der Linden 		 * in entry_lru_isolate, and the one that was set
96095ad37f9SFrank van der Linden 		 * when the entry was first allocated.
96195ad37f9SFrank van der Linden 		 */
96295ad37f9SFrank van der Linden 		kref_put(&entry->ref, nfs4_xattr_free_entry_cb);
96395ad37f9SFrank van der Linden 		kref_put(&entry->ref, nfs4_xattr_free_entry_cb);
96495ad37f9SFrank van der Linden 	}
96595ad37f9SFrank van der Linden 
96695ad37f9SFrank van der Linden 	return freed;
96795ad37f9SFrank van der Linden }
96895ad37f9SFrank van der Linden 
96995ad37f9SFrank van der Linden static unsigned long
97095ad37f9SFrank van der Linden nfs4_xattr_entry_count(struct shrinker *shrink, struct shrink_control *sc)
97195ad37f9SFrank van der Linden {
97295ad37f9SFrank van der Linden 	unsigned long count;
97395ad37f9SFrank van der Linden 	struct list_lru *lru;
97495ad37f9SFrank van der Linden 
97595ad37f9SFrank van der Linden 	lru = (shrink == &nfs4_xattr_large_entry_shrinker) ?
97695ad37f9SFrank van der Linden 	    &nfs4_xattr_large_entry_lru : &nfs4_xattr_entry_lru;
97795ad37f9SFrank van der Linden 
9785904c16dSYang Shi 	count = list_lru_shrink_count(lru, sc);
97995ad37f9SFrank van der Linden 	return vfs_pressure_ratio(count);
98095ad37f9SFrank van der Linden }
98195ad37f9SFrank van der Linden 
98295ad37f9SFrank van der Linden 
98395ad37f9SFrank van der Linden static void nfs4_xattr_cache_init_once(void *p)
98495ad37f9SFrank van der Linden {
98595ad37f9SFrank van der Linden 	struct nfs4_xattr_cache *cache = (struct nfs4_xattr_cache *)p;
98695ad37f9SFrank van der Linden 
98795ad37f9SFrank van der Linden 	spin_lock_init(&cache->listxattr_lock);
98895ad37f9SFrank van der Linden 	atomic_long_set(&cache->nent, 0);
98995ad37f9SFrank van der Linden 	nfs4_xattr_hash_init(cache);
99095ad37f9SFrank van der Linden 	cache->listxattr = NULL;
99195ad37f9SFrank van der Linden 	INIT_LIST_HEAD(&cache->lru);
99295ad37f9SFrank van der Linden 	INIT_LIST_HEAD(&cache->dispose);
99395ad37f9SFrank van der Linden }
99495ad37f9SFrank van der Linden 
99595ad37f9SFrank van der Linden int __init nfs4_xattr_cache_init(void)
99695ad37f9SFrank van der Linden {
99795ad37f9SFrank van der Linden 	int ret = 0;
99895ad37f9SFrank van der Linden 
99995ad37f9SFrank van der Linden 	nfs4_xattr_cache_cachep = kmem_cache_create("nfs4_xattr_cache_cache",
100095ad37f9SFrank van der Linden 	    sizeof(struct nfs4_xattr_cache), 0,
100195ad37f9SFrank van der Linden 	    (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD|SLAB_ACCOUNT),
100295ad37f9SFrank van der Linden 	    nfs4_xattr_cache_init_once);
100395ad37f9SFrank van der Linden 	if (nfs4_xattr_cache_cachep == NULL)
100495ad37f9SFrank van der Linden 		return -ENOMEM;
100595ad37f9SFrank van der Linden 
100695ad37f9SFrank van der Linden 	ret = list_lru_init_memcg(&nfs4_xattr_large_entry_lru,
100795ad37f9SFrank van der Linden 	    &nfs4_xattr_large_entry_shrinker);
100895ad37f9SFrank van der Linden 	if (ret)
100995ad37f9SFrank van der Linden 		goto out4;
101095ad37f9SFrank van der Linden 
101195ad37f9SFrank van der Linden 	ret = list_lru_init_memcg(&nfs4_xattr_entry_lru,
101295ad37f9SFrank van der Linden 	    &nfs4_xattr_entry_shrinker);
101395ad37f9SFrank van der Linden 	if (ret)
101495ad37f9SFrank van der Linden 		goto out3;
101595ad37f9SFrank van der Linden 
101695ad37f9SFrank van der Linden 	ret = list_lru_init_memcg(&nfs4_xattr_cache_lru,
101795ad37f9SFrank van der Linden 	    &nfs4_xattr_cache_shrinker);
101895ad37f9SFrank van der Linden 	if (ret)
101995ad37f9SFrank van der Linden 		goto out2;
102095ad37f9SFrank van der Linden 
102195ad37f9SFrank van der Linden 	ret = register_shrinker(&nfs4_xattr_cache_shrinker);
102295ad37f9SFrank van der Linden 	if (ret)
1023048c397aSFrank van der Linden 		goto out1;
102495ad37f9SFrank van der Linden 
102595ad37f9SFrank van der Linden 	ret = register_shrinker(&nfs4_xattr_entry_shrinker);
102695ad37f9SFrank van der Linden 	if (ret)
102795ad37f9SFrank van der Linden 		goto out;
102895ad37f9SFrank van der Linden 
102995ad37f9SFrank van der Linden 	ret = register_shrinker(&nfs4_xattr_large_entry_shrinker);
103095ad37f9SFrank van der Linden 	if (!ret)
103195ad37f9SFrank van der Linden 		return 0;
103295ad37f9SFrank van der Linden 
103395ad37f9SFrank van der Linden 	unregister_shrinker(&nfs4_xattr_entry_shrinker);
103495ad37f9SFrank van der Linden out:
103595ad37f9SFrank van der Linden 	unregister_shrinker(&nfs4_xattr_cache_shrinker);
103695ad37f9SFrank van der Linden out1:
103795ad37f9SFrank van der Linden 	list_lru_destroy(&nfs4_xattr_cache_lru);
103895ad37f9SFrank van der Linden out2:
103995ad37f9SFrank van der Linden 	list_lru_destroy(&nfs4_xattr_entry_lru);
104095ad37f9SFrank van der Linden out3:
104195ad37f9SFrank van der Linden 	list_lru_destroy(&nfs4_xattr_large_entry_lru);
104295ad37f9SFrank van der Linden out4:
104395ad37f9SFrank van der Linden 	kmem_cache_destroy(nfs4_xattr_cache_cachep);
104495ad37f9SFrank van der Linden 
104595ad37f9SFrank van der Linden 	return ret;
104695ad37f9SFrank van der Linden }
104795ad37f9SFrank van der Linden 
104895ad37f9SFrank van der Linden void nfs4_xattr_cache_exit(void)
104995ad37f9SFrank van der Linden {
105070438afbSJ. Bruce Fields 	unregister_shrinker(&nfs4_xattr_large_entry_shrinker);
105195ad37f9SFrank van der Linden 	unregister_shrinker(&nfs4_xattr_entry_shrinker);
105295ad37f9SFrank van der Linden 	unregister_shrinker(&nfs4_xattr_cache_shrinker);
105370438afbSJ. Bruce Fields 	list_lru_destroy(&nfs4_xattr_large_entry_lru);
105495ad37f9SFrank van der Linden 	list_lru_destroy(&nfs4_xattr_entry_lru);
105595ad37f9SFrank van der Linden 	list_lru_destroy(&nfs4_xattr_cache_lru);
105695ad37f9SFrank van der Linden 	kmem_cache_destroy(nfs4_xattr_cache_cachep);
105795ad37f9SFrank van der Linden }
1058