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