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
nfs4_xattr_hash_init(struct nfs4_xattr_cache * cache)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
nfs4_xattr_entry_lru_add(struct nfs4_xattr_entry * entry)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
nfs4_xattr_entry_lru_del(struct nfs4_xattr_entry * entry)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).
171c2508730SBhaskar 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 *
nfs4_xattr_alloc_entry(const char * name,const void * value,struct page ** pages,size_t len)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
2025c60e89eSTrond Myklebust buf = kmalloc(alloclen, GFP_KERNEL);
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) {
2165c60e89eSTrond Myklebust valp = kvmalloc(len, GFP_KERNEL);
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
nfs4_xattr_free_entry(struct nfs4_xattr_entry * entry)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
nfs4_xattr_free_entry_cb(struct kref * kref)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
nfs4_xattr_free_cache_cb(struct kref * kref)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 *
nfs4_xattr_alloc_cache(void)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
2925c60e89eSTrond Myklebust cache = kmem_cache_alloc(nfs4_xattr_cache_cachep, GFP_KERNEL);
29395ad37f9SFrank van der Linden if (cache == NULL)
29495ad37f9SFrank van der Linden return NULL;
29595ad37f9SFrank van der Linden
29695ad37f9SFrank van der Linden kref_init(&cache->ref);
29795ad37f9SFrank van der Linden atomic_long_set(&cache->nent, 0);
29895ad37f9SFrank van der Linden
29995ad37f9SFrank van der Linden return cache;
30095ad37f9SFrank van der Linden }
30195ad37f9SFrank van der Linden
30295ad37f9SFrank van der Linden /*
30395ad37f9SFrank van der Linden * Set the listxattr cache, which is a special-cased cache entry.
30495ad37f9SFrank van der Linden * The special value ERR_PTR(-ESTALE) is used to indicate that
30595ad37f9SFrank van der Linden * the cache is being drained - this prevents a new listxattr
30695ad37f9SFrank van der Linden * cache from being added to what is now a stale cache.
30795ad37f9SFrank van der Linden */
30895ad37f9SFrank van der Linden static int
nfs4_xattr_set_listcache(struct nfs4_xattr_cache * cache,struct nfs4_xattr_entry * new)30995ad37f9SFrank van der Linden nfs4_xattr_set_listcache(struct nfs4_xattr_cache *cache,
31095ad37f9SFrank van der Linden struct nfs4_xattr_entry *new)
31195ad37f9SFrank van der Linden {
31295ad37f9SFrank van der Linden struct nfs4_xattr_entry *old;
31395ad37f9SFrank van der Linden int ret = 1;
31495ad37f9SFrank van der Linden
31595ad37f9SFrank van der Linden spin_lock(&cache->listxattr_lock);
31695ad37f9SFrank van der Linden
31795ad37f9SFrank van der Linden old = cache->listxattr;
31895ad37f9SFrank van der Linden
31995ad37f9SFrank van der Linden if (old == ERR_PTR(-ESTALE)) {
32095ad37f9SFrank van der Linden ret = 0;
32195ad37f9SFrank van der Linden goto out;
32295ad37f9SFrank van der Linden }
32395ad37f9SFrank van der Linden
32495ad37f9SFrank van der Linden cache->listxattr = new;
32595ad37f9SFrank van der Linden if (new != NULL && new != ERR_PTR(-ESTALE))
32695ad37f9SFrank van der Linden nfs4_xattr_entry_lru_add(new);
32795ad37f9SFrank van der Linden
32895ad37f9SFrank van der Linden if (old != NULL) {
32995ad37f9SFrank van der Linden nfs4_xattr_entry_lru_del(old);
33095ad37f9SFrank van der Linden kref_put(&old->ref, nfs4_xattr_free_entry_cb);
33195ad37f9SFrank van der Linden }
33295ad37f9SFrank van der Linden out:
33395ad37f9SFrank van der Linden spin_unlock(&cache->listxattr_lock);
33495ad37f9SFrank van der Linden
33595ad37f9SFrank van der Linden return ret;
33695ad37f9SFrank van der Linden }
33795ad37f9SFrank van der Linden
33895ad37f9SFrank van der Linden /*
33995ad37f9SFrank van der Linden * Unlink a cache from its parent inode, clearing out an invalid
34095ad37f9SFrank van der Linden * cache. Must be called with i_lock held.
34195ad37f9SFrank van der Linden */
34295ad37f9SFrank van der Linden static struct nfs4_xattr_cache *
nfs4_xattr_cache_unlink(struct inode * inode)34395ad37f9SFrank van der Linden nfs4_xattr_cache_unlink(struct inode *inode)
34495ad37f9SFrank van der Linden {
34595ad37f9SFrank van der Linden struct nfs_inode *nfsi;
34695ad37f9SFrank van der Linden struct nfs4_xattr_cache *oldcache;
34795ad37f9SFrank van der Linden
34895ad37f9SFrank van der Linden nfsi = NFS_I(inode);
34995ad37f9SFrank van der Linden
35095ad37f9SFrank van der Linden oldcache = nfsi->xattr_cache;
35195ad37f9SFrank van der Linden if (oldcache != NULL) {
35295ad37f9SFrank van der Linden list_lru_del(&nfs4_xattr_cache_lru, &oldcache->lru);
35395ad37f9SFrank van der Linden oldcache->inode = NULL;
35495ad37f9SFrank van der Linden }
35595ad37f9SFrank van der Linden nfsi->xattr_cache = NULL;
35695ad37f9SFrank van der Linden nfsi->cache_validity &= ~NFS_INO_INVALID_XATTR;
35795ad37f9SFrank van der Linden
35895ad37f9SFrank van der Linden return oldcache;
35995ad37f9SFrank van der Linden
36095ad37f9SFrank van der Linden }
36195ad37f9SFrank van der Linden
36295ad37f9SFrank van der Linden /*
363048c397aSFrank van der Linden * Discard a cache. Called by get_cache() if there was an old,
364048c397aSFrank van der Linden * invalid cache. Can also be called from a shrinker callback.
36595ad37f9SFrank van der Linden *
36695ad37f9SFrank van der Linden * The cache is dead, it has already been unlinked from its inode,
36795ad37f9SFrank van der Linden * and no longer appears on the cache LRU list.
36895ad37f9SFrank van der Linden *
36995ad37f9SFrank van der Linden * Mark all buckets as draining, so that no new entries are added. This
37095ad37f9SFrank van der Linden * could still happen in the unlikely, but possible case that another
37195ad37f9SFrank van der Linden * thread had grabbed a reference before it was unlinked from the inode,
37295ad37f9SFrank van der Linden * and is still holding it for an add operation.
37395ad37f9SFrank van der Linden *
37495ad37f9SFrank van der Linden * Remove all entries from the LRU lists, so that there is no longer
37595ad37f9SFrank van der Linden * any way to 'find' this cache. Then, remove the entries from the hash
37695ad37f9SFrank van der Linden * table.
37795ad37f9SFrank van der Linden *
37895ad37f9SFrank van der Linden * At that point, the cache will remain empty and can be freed when the final
37995ad37f9SFrank van der Linden * reference drops, which is very likely the kref_put at the end of
38095ad37f9SFrank van der Linden * this function, or the one called immediately afterwards in the
38195ad37f9SFrank van der Linden * shrinker callback.
38295ad37f9SFrank van der Linden */
38395ad37f9SFrank van der Linden static void
nfs4_xattr_discard_cache(struct nfs4_xattr_cache * cache)38495ad37f9SFrank van der Linden nfs4_xattr_discard_cache(struct nfs4_xattr_cache *cache)
38595ad37f9SFrank van der Linden {
38695ad37f9SFrank van der Linden unsigned int i;
38795ad37f9SFrank van der Linden struct nfs4_xattr_entry *entry;
38895ad37f9SFrank van der Linden struct nfs4_xattr_bucket *bucket;
38995ad37f9SFrank van der Linden struct hlist_node *n;
39095ad37f9SFrank van der Linden
39195ad37f9SFrank van der Linden nfs4_xattr_set_listcache(cache, ERR_PTR(-ESTALE));
39295ad37f9SFrank van der Linden
39395ad37f9SFrank van der Linden for (i = 0; i < NFS4_XATTR_HASH_SIZE; i++) {
39495ad37f9SFrank van der Linden bucket = &cache->buckets[i];
39595ad37f9SFrank van der Linden
39695ad37f9SFrank van der Linden spin_lock(&bucket->lock);
39795ad37f9SFrank van der Linden bucket->draining = true;
39895ad37f9SFrank van der Linden hlist_for_each_entry_safe(entry, n, &bucket->hlist, hnode) {
39995ad37f9SFrank van der Linden nfs4_xattr_entry_lru_del(entry);
40095ad37f9SFrank van der Linden hlist_del_init(&entry->hnode);
40195ad37f9SFrank van der Linden kref_put(&entry->ref, nfs4_xattr_free_entry_cb);
40295ad37f9SFrank van der Linden }
40395ad37f9SFrank van der Linden spin_unlock(&bucket->lock);
40495ad37f9SFrank van der Linden }
40595ad37f9SFrank van der Linden
40695ad37f9SFrank van der Linden atomic_long_set(&cache->nent, 0);
40795ad37f9SFrank van der Linden
40895ad37f9SFrank van der Linden kref_put(&cache->ref, nfs4_xattr_free_cache_cb);
40995ad37f9SFrank van der Linden }
41095ad37f9SFrank van der Linden
41195ad37f9SFrank van der Linden /*
41295ad37f9SFrank van der Linden * Get a referenced copy of the cache structure. Avoid doing allocs
41395ad37f9SFrank van der Linden * while holding i_lock. Which means that we do some optimistic allocation,
41495ad37f9SFrank van der Linden * and might have to free the result in rare cases.
41595ad37f9SFrank van der Linden *
41695ad37f9SFrank van der Linden * This function only checks the NFS_INO_INVALID_XATTR cache validity bit
41795ad37f9SFrank van der Linden * and acts accordingly, replacing the cache when needed. For the read case
41895ad37f9SFrank van der Linden * (!add), this means that the caller must make sure that the cache
41995ad37f9SFrank van der Linden * is valid before caling this function. getxattr and listxattr call
42095ad37f9SFrank van der Linden * revalidate_inode to do this. The attribute cache timeout (for the
42195ad37f9SFrank van der Linden * non-delegated case) is expected to be dealt with in the revalidate
42295ad37f9SFrank van der Linden * call.
42395ad37f9SFrank van der Linden */
42495ad37f9SFrank van der Linden
42595ad37f9SFrank van der Linden static struct nfs4_xattr_cache *
nfs4_xattr_get_cache(struct inode * inode,int add)42695ad37f9SFrank van der Linden nfs4_xattr_get_cache(struct inode *inode, int add)
42795ad37f9SFrank van der Linden {
42895ad37f9SFrank van der Linden struct nfs_inode *nfsi;
42995ad37f9SFrank van der Linden struct nfs4_xattr_cache *cache, *oldcache, *newcache;
43095ad37f9SFrank van der Linden
43195ad37f9SFrank van der Linden nfsi = NFS_I(inode);
43295ad37f9SFrank van der Linden
43395ad37f9SFrank van der Linden cache = oldcache = NULL;
43495ad37f9SFrank van der Linden
43595ad37f9SFrank van der Linden spin_lock(&inode->i_lock);
43695ad37f9SFrank van der Linden
43795ad37f9SFrank van der Linden if (nfsi->cache_validity & NFS_INO_INVALID_XATTR)
43895ad37f9SFrank van der Linden oldcache = nfs4_xattr_cache_unlink(inode);
43995ad37f9SFrank van der Linden else
44095ad37f9SFrank van der Linden cache = nfsi->xattr_cache;
44195ad37f9SFrank van der Linden
44295ad37f9SFrank van der Linden if (cache != NULL)
44395ad37f9SFrank van der Linden kref_get(&cache->ref);
44495ad37f9SFrank van der Linden
44595ad37f9SFrank van der Linden spin_unlock(&inode->i_lock);
44695ad37f9SFrank van der Linden
44795ad37f9SFrank van der Linden if (add && cache == NULL) {
44895ad37f9SFrank van der Linden newcache = NULL;
44995ad37f9SFrank van der Linden
45095ad37f9SFrank van der Linden cache = nfs4_xattr_alloc_cache();
45195ad37f9SFrank van der Linden if (cache == NULL)
45295ad37f9SFrank van der Linden goto out;
45395ad37f9SFrank van der Linden
45495ad37f9SFrank van der Linden spin_lock(&inode->i_lock);
45595ad37f9SFrank van der Linden if (nfsi->cache_validity & NFS_INO_INVALID_XATTR) {
45695ad37f9SFrank van der Linden /*
45795ad37f9SFrank van der Linden * The cache was invalidated again. Give up,
45895ad37f9SFrank van der Linden * since what we want to enter is now likely
45995ad37f9SFrank van der Linden * outdated anyway.
46095ad37f9SFrank van der Linden */
46195ad37f9SFrank van der Linden spin_unlock(&inode->i_lock);
46295ad37f9SFrank van der Linden kref_put(&cache->ref, nfs4_xattr_free_cache_cb);
46395ad37f9SFrank van der Linden cache = NULL;
46495ad37f9SFrank van der Linden goto out;
46595ad37f9SFrank van der Linden }
46695ad37f9SFrank van der Linden
46795ad37f9SFrank van der Linden /*
46895ad37f9SFrank van der Linden * Check if someone beat us to it.
46995ad37f9SFrank van der Linden */
47095ad37f9SFrank van der Linden if (nfsi->xattr_cache != NULL) {
47195ad37f9SFrank van der Linden newcache = nfsi->xattr_cache;
47295ad37f9SFrank van der Linden kref_get(&newcache->ref);
47395ad37f9SFrank van der Linden } else {
47495ad37f9SFrank van der Linden kref_get(&cache->ref);
47595ad37f9SFrank van der Linden nfsi->xattr_cache = cache;
47695ad37f9SFrank van der Linden cache->inode = inode;
47795ad37f9SFrank van der Linden list_lru_add(&nfs4_xattr_cache_lru, &cache->lru);
47895ad37f9SFrank van der Linden }
47995ad37f9SFrank van der Linden
48095ad37f9SFrank van der Linden spin_unlock(&inode->i_lock);
48195ad37f9SFrank van der Linden
48295ad37f9SFrank van der Linden /*
48395ad37f9SFrank van der Linden * If there was a race, throw away the cache we just
48495ad37f9SFrank van der Linden * allocated, and use the new one allocated by someone
48595ad37f9SFrank van der Linden * else.
48695ad37f9SFrank van der Linden */
48795ad37f9SFrank van der Linden if (newcache != NULL) {
48895ad37f9SFrank van der Linden kref_put(&cache->ref, nfs4_xattr_free_cache_cb);
48995ad37f9SFrank van der Linden cache = newcache;
49095ad37f9SFrank van der Linden }
49195ad37f9SFrank van der Linden }
49295ad37f9SFrank van der Linden
49395ad37f9SFrank van der Linden out:
49495ad37f9SFrank van der Linden /*
495048c397aSFrank van der Linden * Discard the now orphaned old cache.
49695ad37f9SFrank van der Linden */
49795ad37f9SFrank van der Linden if (oldcache != NULL)
498048c397aSFrank van der Linden nfs4_xattr_discard_cache(oldcache);
49995ad37f9SFrank van der Linden
50095ad37f9SFrank van der Linden return cache;
50195ad37f9SFrank van der Linden }
50295ad37f9SFrank van der Linden
50395ad37f9SFrank van der Linden static inline struct nfs4_xattr_bucket *
nfs4_xattr_hash_bucket(struct nfs4_xattr_cache * cache,const char * name)50495ad37f9SFrank van der Linden nfs4_xattr_hash_bucket(struct nfs4_xattr_cache *cache, const char *name)
50595ad37f9SFrank van der Linden {
50695ad37f9SFrank van der Linden return &cache->buckets[jhash(name, strlen(name), 0) &
50795ad37f9SFrank van der Linden (ARRAY_SIZE(cache->buckets) - 1)];
50895ad37f9SFrank van der Linden }
50995ad37f9SFrank van der Linden
51095ad37f9SFrank van der Linden static struct nfs4_xattr_entry *
nfs4_xattr_get_entry(struct nfs4_xattr_bucket * bucket,const char * name)51195ad37f9SFrank van der Linden nfs4_xattr_get_entry(struct nfs4_xattr_bucket *bucket, const char *name)
51295ad37f9SFrank van der Linden {
51395ad37f9SFrank van der Linden struct nfs4_xattr_entry *entry;
51495ad37f9SFrank van der Linden
51595ad37f9SFrank van der Linden entry = NULL;
51695ad37f9SFrank van der Linden
51795ad37f9SFrank van der Linden hlist_for_each_entry(entry, &bucket->hlist, hnode) {
51895ad37f9SFrank van der Linden if (!strcmp(entry->xattr_name, name))
51995ad37f9SFrank van der Linden break;
52095ad37f9SFrank van der Linden }
52195ad37f9SFrank van der Linden
52295ad37f9SFrank van der Linden return entry;
52395ad37f9SFrank van der Linden }
52495ad37f9SFrank van der Linden
52595ad37f9SFrank van der Linden static int
nfs4_xattr_hash_add(struct nfs4_xattr_cache * cache,struct nfs4_xattr_entry * entry)52695ad37f9SFrank van der Linden nfs4_xattr_hash_add(struct nfs4_xattr_cache *cache,
52795ad37f9SFrank van der Linden struct nfs4_xattr_entry *entry)
52895ad37f9SFrank van der Linden {
52995ad37f9SFrank van der Linden struct nfs4_xattr_bucket *bucket;
53095ad37f9SFrank van der Linden struct nfs4_xattr_entry *oldentry = NULL;
53195ad37f9SFrank van der Linden int ret = 1;
53295ad37f9SFrank van der Linden
53395ad37f9SFrank van der Linden bucket = nfs4_xattr_hash_bucket(cache, entry->xattr_name);
53495ad37f9SFrank van der Linden entry->bucket = bucket;
53595ad37f9SFrank van der Linden
53695ad37f9SFrank van der Linden spin_lock(&bucket->lock);
53795ad37f9SFrank van der Linden
53895ad37f9SFrank van der Linden if (bucket->draining) {
53995ad37f9SFrank van der Linden ret = 0;
54095ad37f9SFrank van der Linden goto out;
54195ad37f9SFrank van der Linden }
54295ad37f9SFrank van der Linden
54395ad37f9SFrank van der Linden oldentry = nfs4_xattr_get_entry(bucket, entry->xattr_name);
54495ad37f9SFrank van der Linden if (oldentry != NULL) {
54595ad37f9SFrank van der Linden hlist_del_init(&oldentry->hnode);
54695ad37f9SFrank van der Linden nfs4_xattr_entry_lru_del(oldentry);
54795ad37f9SFrank van der Linden } else {
54895ad37f9SFrank van der Linden atomic_long_inc(&cache->nent);
54995ad37f9SFrank van der Linden }
55095ad37f9SFrank van der Linden
55195ad37f9SFrank van der Linden hlist_add_head(&entry->hnode, &bucket->hlist);
55295ad37f9SFrank van der Linden nfs4_xattr_entry_lru_add(entry);
55395ad37f9SFrank van der Linden
55495ad37f9SFrank van der Linden out:
55595ad37f9SFrank van der Linden spin_unlock(&bucket->lock);
55695ad37f9SFrank van der Linden
55795ad37f9SFrank van der Linden if (oldentry != NULL)
55895ad37f9SFrank van der Linden kref_put(&oldentry->ref, nfs4_xattr_free_entry_cb);
55995ad37f9SFrank van der Linden
56095ad37f9SFrank van der Linden return ret;
56195ad37f9SFrank van der Linden }
56295ad37f9SFrank van der Linden
56395ad37f9SFrank van der Linden static void
nfs4_xattr_hash_remove(struct nfs4_xattr_cache * cache,const char * name)56495ad37f9SFrank van der Linden nfs4_xattr_hash_remove(struct nfs4_xattr_cache *cache, const char *name)
56595ad37f9SFrank van der Linden {
56695ad37f9SFrank van der Linden struct nfs4_xattr_bucket *bucket;
56795ad37f9SFrank van der Linden struct nfs4_xattr_entry *entry;
56895ad37f9SFrank van der Linden
56995ad37f9SFrank van der Linden bucket = nfs4_xattr_hash_bucket(cache, name);
57095ad37f9SFrank van der Linden
57195ad37f9SFrank van der Linden spin_lock(&bucket->lock);
57295ad37f9SFrank van der Linden
57395ad37f9SFrank van der Linden entry = nfs4_xattr_get_entry(bucket, name);
57495ad37f9SFrank van der Linden if (entry != NULL) {
57595ad37f9SFrank van der Linden hlist_del_init(&entry->hnode);
57695ad37f9SFrank van der Linden nfs4_xattr_entry_lru_del(entry);
57795ad37f9SFrank van der Linden atomic_long_dec(&cache->nent);
57895ad37f9SFrank van der Linden }
57995ad37f9SFrank van der Linden
58095ad37f9SFrank van der Linden spin_unlock(&bucket->lock);
58195ad37f9SFrank van der Linden
58295ad37f9SFrank van der Linden if (entry != NULL)
58395ad37f9SFrank van der Linden kref_put(&entry->ref, nfs4_xattr_free_entry_cb);
58495ad37f9SFrank van der Linden }
58595ad37f9SFrank van der Linden
58695ad37f9SFrank van der Linden static struct nfs4_xattr_entry *
nfs4_xattr_hash_find(struct nfs4_xattr_cache * cache,const char * name)58795ad37f9SFrank van der Linden nfs4_xattr_hash_find(struct nfs4_xattr_cache *cache, const char *name)
58895ad37f9SFrank van der Linden {
58995ad37f9SFrank van der Linden struct nfs4_xattr_bucket *bucket;
59095ad37f9SFrank van der Linden struct nfs4_xattr_entry *entry;
59195ad37f9SFrank van der Linden
59295ad37f9SFrank van der Linden bucket = nfs4_xattr_hash_bucket(cache, name);
59395ad37f9SFrank van der Linden
59495ad37f9SFrank van der Linden spin_lock(&bucket->lock);
59595ad37f9SFrank van der Linden
59695ad37f9SFrank van der Linden entry = nfs4_xattr_get_entry(bucket, name);
59795ad37f9SFrank van der Linden if (entry != NULL)
59895ad37f9SFrank van der Linden kref_get(&entry->ref);
59995ad37f9SFrank van der Linden
60095ad37f9SFrank van der Linden spin_unlock(&bucket->lock);
60195ad37f9SFrank van der Linden
60295ad37f9SFrank van der Linden return entry;
60395ad37f9SFrank van der Linden }
60495ad37f9SFrank van der Linden
60595ad37f9SFrank van der Linden /*
60695ad37f9SFrank van der Linden * Entry point to retrieve an entry from the cache.
60795ad37f9SFrank van der Linden */
nfs4_xattr_cache_get(struct inode * inode,const char * name,char * buf,ssize_t buflen)60895ad37f9SFrank van der Linden ssize_t nfs4_xattr_cache_get(struct inode *inode, const char *name, char *buf,
60995ad37f9SFrank van der Linden ssize_t buflen)
61095ad37f9SFrank van der Linden {
61195ad37f9SFrank van der Linden struct nfs4_xattr_cache *cache;
61295ad37f9SFrank van der Linden struct nfs4_xattr_entry *entry;
61395ad37f9SFrank van der Linden ssize_t ret;
61495ad37f9SFrank van der Linden
61595ad37f9SFrank van der Linden cache = nfs4_xattr_get_cache(inode, 0);
61695ad37f9SFrank van der Linden if (cache == NULL)
61795ad37f9SFrank van der Linden return -ENOENT;
61895ad37f9SFrank van der Linden
61995ad37f9SFrank van der Linden ret = 0;
62095ad37f9SFrank van der Linden entry = nfs4_xattr_hash_find(cache, name);
62195ad37f9SFrank van der Linden
62295ad37f9SFrank van der Linden if (entry != NULL) {
62395ad37f9SFrank van der Linden dprintk("%s: cache hit '%s', len %lu\n", __func__,
62495ad37f9SFrank van der Linden entry->xattr_name, (unsigned long)entry->xattr_size);
62595ad37f9SFrank van der Linden if (buflen == 0) {
62695ad37f9SFrank van der Linden /* Length probe only */
62795ad37f9SFrank van der Linden ret = entry->xattr_size;
62895ad37f9SFrank van der Linden } else if (buflen < entry->xattr_size)
62995ad37f9SFrank van der Linden ret = -ERANGE;
63095ad37f9SFrank van der Linden else {
63195ad37f9SFrank van der Linden memcpy(buf, entry->xattr_value, entry->xattr_size);
63295ad37f9SFrank van der Linden ret = entry->xattr_size;
63395ad37f9SFrank van der Linden }
63495ad37f9SFrank van der Linden kref_put(&entry->ref, nfs4_xattr_free_entry_cb);
63595ad37f9SFrank van der Linden } else {
63695ad37f9SFrank van der Linden dprintk("%s: cache miss '%s'\n", __func__, name);
63795ad37f9SFrank van der Linden ret = -ENOENT;
63895ad37f9SFrank van der Linden }
63995ad37f9SFrank van der Linden
64095ad37f9SFrank van der Linden kref_put(&cache->ref, nfs4_xattr_free_cache_cb);
64195ad37f9SFrank van der Linden
64295ad37f9SFrank van der Linden return ret;
64395ad37f9SFrank van der Linden }
64495ad37f9SFrank van der Linden
64595ad37f9SFrank van der Linden /*
64695ad37f9SFrank van der Linden * Retrieve a cached list of xattrs from the cache.
64795ad37f9SFrank van der Linden */
nfs4_xattr_cache_list(struct inode * inode,char * buf,ssize_t buflen)64895ad37f9SFrank van der Linden ssize_t nfs4_xattr_cache_list(struct inode *inode, char *buf, ssize_t buflen)
64995ad37f9SFrank van der Linden {
65095ad37f9SFrank van der Linden struct nfs4_xattr_cache *cache;
65195ad37f9SFrank van der Linden struct nfs4_xattr_entry *entry;
65295ad37f9SFrank van der Linden ssize_t ret;
65395ad37f9SFrank van der Linden
65495ad37f9SFrank van der Linden cache = nfs4_xattr_get_cache(inode, 0);
65595ad37f9SFrank van der Linden if (cache == NULL)
65695ad37f9SFrank van der Linden return -ENOENT;
65795ad37f9SFrank van der Linden
65895ad37f9SFrank van der Linden spin_lock(&cache->listxattr_lock);
65995ad37f9SFrank van der Linden
66095ad37f9SFrank van der Linden entry = cache->listxattr;
66195ad37f9SFrank van der Linden
66295ad37f9SFrank van der Linden if (entry != NULL && entry != ERR_PTR(-ESTALE)) {
66395ad37f9SFrank van der Linden if (buflen == 0) {
66495ad37f9SFrank van der Linden /* Length probe only */
66595ad37f9SFrank van der Linden ret = entry->xattr_size;
66695ad37f9SFrank van der Linden } else if (entry->xattr_size > buflen)
66795ad37f9SFrank van der Linden ret = -ERANGE;
66895ad37f9SFrank van der Linden else {
66995ad37f9SFrank van der Linden memcpy(buf, entry->xattr_value, entry->xattr_size);
67095ad37f9SFrank van der Linden ret = entry->xattr_size;
67195ad37f9SFrank van der Linden }
67295ad37f9SFrank van der Linden } else {
67395ad37f9SFrank van der Linden ret = -ENOENT;
67495ad37f9SFrank van der Linden }
67595ad37f9SFrank van der Linden
67695ad37f9SFrank van der Linden spin_unlock(&cache->listxattr_lock);
67795ad37f9SFrank van der Linden
67895ad37f9SFrank van der Linden kref_put(&cache->ref, nfs4_xattr_free_cache_cb);
67995ad37f9SFrank van der Linden
68095ad37f9SFrank van der Linden return ret;
68195ad37f9SFrank van der Linden }
68295ad37f9SFrank van der Linden
68395ad37f9SFrank van der Linden /*
68495ad37f9SFrank van der Linden * Add an xattr to the cache.
68595ad37f9SFrank van der Linden *
68695ad37f9SFrank van der Linden * This also invalidates the xattr list cache.
68795ad37f9SFrank van der Linden */
nfs4_xattr_cache_add(struct inode * inode,const char * name,const char * buf,struct page ** pages,ssize_t buflen)68895ad37f9SFrank van der Linden void nfs4_xattr_cache_add(struct inode *inode, const char *name,
68995ad37f9SFrank van der Linden const char *buf, struct page **pages, ssize_t buflen)
69095ad37f9SFrank van der Linden {
69195ad37f9SFrank van der Linden struct nfs4_xattr_cache *cache;
69295ad37f9SFrank van der Linden struct nfs4_xattr_entry *entry;
69395ad37f9SFrank van der Linden
69495ad37f9SFrank van der Linden dprintk("%s: add '%s' len %lu\n", __func__,
69595ad37f9SFrank van der Linden name, (unsigned long)buflen);
69695ad37f9SFrank van der Linden
69795ad37f9SFrank van der Linden cache = nfs4_xattr_get_cache(inode, 1);
69895ad37f9SFrank van der Linden if (cache == NULL)
69995ad37f9SFrank van der Linden return;
70095ad37f9SFrank van der Linden
70195ad37f9SFrank van der Linden entry = nfs4_xattr_alloc_entry(name, buf, pages, buflen);
70295ad37f9SFrank van der Linden if (entry == NULL)
70395ad37f9SFrank van der Linden goto out;
70495ad37f9SFrank van der Linden
70595ad37f9SFrank van der Linden (void)nfs4_xattr_set_listcache(cache, NULL);
70695ad37f9SFrank van der Linden
70795ad37f9SFrank van der Linden if (!nfs4_xattr_hash_add(cache, entry))
70895ad37f9SFrank van der Linden kref_put(&entry->ref, nfs4_xattr_free_entry_cb);
70995ad37f9SFrank van der Linden
71095ad37f9SFrank van der Linden out:
71195ad37f9SFrank van der Linden kref_put(&cache->ref, nfs4_xattr_free_cache_cb);
71295ad37f9SFrank van der Linden }
71395ad37f9SFrank van der Linden
71495ad37f9SFrank van der Linden
71595ad37f9SFrank van der Linden /*
71695ad37f9SFrank van der Linden * Remove an xattr from the cache.
71795ad37f9SFrank van der Linden *
71895ad37f9SFrank van der Linden * This also invalidates the xattr list cache.
71995ad37f9SFrank van der Linden */
nfs4_xattr_cache_remove(struct inode * inode,const char * name)72095ad37f9SFrank van der Linden void nfs4_xattr_cache_remove(struct inode *inode, const char *name)
72195ad37f9SFrank van der Linden {
72295ad37f9SFrank van der Linden struct nfs4_xattr_cache *cache;
72395ad37f9SFrank van der Linden
72495ad37f9SFrank van der Linden dprintk("%s: remove '%s'\n", __func__, name);
72595ad37f9SFrank van der Linden
72695ad37f9SFrank van der Linden cache = nfs4_xattr_get_cache(inode, 0);
72795ad37f9SFrank van der Linden if (cache == NULL)
72895ad37f9SFrank van der Linden return;
72995ad37f9SFrank van der Linden
73095ad37f9SFrank van der Linden (void)nfs4_xattr_set_listcache(cache, NULL);
73195ad37f9SFrank van der Linden nfs4_xattr_hash_remove(cache, name);
73295ad37f9SFrank van der Linden
73395ad37f9SFrank van der Linden kref_put(&cache->ref, nfs4_xattr_free_cache_cb);
73495ad37f9SFrank van der Linden }
73595ad37f9SFrank van der Linden
73695ad37f9SFrank van der Linden /*
73795ad37f9SFrank van der Linden * Cache listxattr output, replacing any possible old one.
73895ad37f9SFrank van der Linden */
nfs4_xattr_cache_set_list(struct inode * inode,const char * buf,ssize_t buflen)73995ad37f9SFrank van der Linden void nfs4_xattr_cache_set_list(struct inode *inode, const char *buf,
74095ad37f9SFrank van der Linden ssize_t buflen)
74195ad37f9SFrank van der Linden {
74295ad37f9SFrank van der Linden struct nfs4_xattr_cache *cache;
74395ad37f9SFrank van der Linden struct nfs4_xattr_entry *entry;
74495ad37f9SFrank van der Linden
74595ad37f9SFrank van der Linden cache = nfs4_xattr_get_cache(inode, 1);
74695ad37f9SFrank van der Linden if (cache == NULL)
74795ad37f9SFrank van der Linden return;
74895ad37f9SFrank van der Linden
74995ad37f9SFrank van der Linden entry = nfs4_xattr_alloc_entry(NULL, buf, NULL, buflen);
75095ad37f9SFrank van der Linden if (entry == NULL)
75195ad37f9SFrank van der Linden goto out;
75295ad37f9SFrank van der Linden
75395ad37f9SFrank van der Linden /*
75495ad37f9SFrank van der Linden * This is just there to be able to get to bucket->cache,
75595ad37f9SFrank van der Linden * which is obviously the same for all buckets, so just
75695ad37f9SFrank van der Linden * use bucket 0.
75795ad37f9SFrank van der Linden */
75895ad37f9SFrank van der Linden entry->bucket = &cache->buckets[0];
75995ad37f9SFrank van der Linden
76095ad37f9SFrank van der Linden if (!nfs4_xattr_set_listcache(cache, entry))
76195ad37f9SFrank van der Linden kref_put(&entry->ref, nfs4_xattr_free_entry_cb);
76295ad37f9SFrank van der Linden
76395ad37f9SFrank van der Linden out:
76495ad37f9SFrank van der Linden kref_put(&cache->ref, nfs4_xattr_free_cache_cb);
76595ad37f9SFrank van der Linden }
76695ad37f9SFrank van der Linden
76795ad37f9SFrank van der Linden /*
76895ad37f9SFrank van der Linden * Zap the entire cache. Called when an inode is evicted.
76995ad37f9SFrank van der Linden */
nfs4_xattr_cache_zap(struct inode * inode)77095ad37f9SFrank van der Linden void nfs4_xattr_cache_zap(struct inode *inode)
77195ad37f9SFrank van der Linden {
77295ad37f9SFrank van der Linden struct nfs4_xattr_cache *oldcache;
77395ad37f9SFrank van der Linden
77495ad37f9SFrank van der Linden spin_lock(&inode->i_lock);
77595ad37f9SFrank van der Linden oldcache = nfs4_xattr_cache_unlink(inode);
77695ad37f9SFrank van der Linden spin_unlock(&inode->i_lock);
77795ad37f9SFrank van der Linden
77895ad37f9SFrank van der Linden if (oldcache)
77995ad37f9SFrank van der Linden nfs4_xattr_discard_cache(oldcache);
78095ad37f9SFrank van der Linden }
78195ad37f9SFrank van der Linden
78295ad37f9SFrank van der Linden /*
78395ad37f9SFrank van der Linden * The entry LRU is shrunk more aggressively than the cache LRU,
78495ad37f9SFrank van der Linden * by settings @seeks to 1.
78595ad37f9SFrank van der Linden *
78695ad37f9SFrank van der Linden * Cache structures are freed only when they've become empty, after
78795ad37f9SFrank van der Linden * pruning all but one entry.
78895ad37f9SFrank van der Linden */
78995ad37f9SFrank van der Linden
79095ad37f9SFrank van der Linden static unsigned long nfs4_xattr_cache_count(struct shrinker *shrink,
79195ad37f9SFrank van der Linden struct shrink_control *sc);
79295ad37f9SFrank van der Linden static unsigned long nfs4_xattr_entry_count(struct shrinker *shrink,
79395ad37f9SFrank van der Linden struct shrink_control *sc);
79495ad37f9SFrank van der Linden static unsigned long nfs4_xattr_cache_scan(struct shrinker *shrink,
79595ad37f9SFrank van der Linden struct shrink_control *sc);
79695ad37f9SFrank van der Linden static unsigned long nfs4_xattr_entry_scan(struct shrinker *shrink,
79795ad37f9SFrank van der Linden struct shrink_control *sc);
79895ad37f9SFrank van der Linden
79995ad37f9SFrank van der Linden static struct shrinker nfs4_xattr_cache_shrinker = {
80095ad37f9SFrank van der Linden .count_objects = nfs4_xattr_cache_count,
80195ad37f9SFrank van der Linden .scan_objects = nfs4_xattr_cache_scan,
80295ad37f9SFrank van der Linden .seeks = DEFAULT_SEEKS,
80395ad37f9SFrank van der Linden .flags = SHRINKER_MEMCG_AWARE,
80495ad37f9SFrank van der Linden };
80595ad37f9SFrank van der Linden
80695ad37f9SFrank van der Linden static struct shrinker nfs4_xattr_entry_shrinker = {
80795ad37f9SFrank van der Linden .count_objects = nfs4_xattr_entry_count,
80895ad37f9SFrank van der Linden .scan_objects = nfs4_xattr_entry_scan,
80995ad37f9SFrank van der Linden .seeks = DEFAULT_SEEKS,
81095ad37f9SFrank van der Linden .batch = 512,
81195ad37f9SFrank van der Linden .flags = SHRINKER_MEMCG_AWARE,
81295ad37f9SFrank van der Linden };
81395ad37f9SFrank van der Linden
81495ad37f9SFrank van der Linden static struct shrinker nfs4_xattr_large_entry_shrinker = {
81595ad37f9SFrank van der Linden .count_objects = nfs4_xattr_entry_count,
81695ad37f9SFrank van der Linden .scan_objects = nfs4_xattr_entry_scan,
81795ad37f9SFrank van der Linden .seeks = 1,
81895ad37f9SFrank van der Linden .batch = 512,
81995ad37f9SFrank van der Linden .flags = SHRINKER_MEMCG_AWARE,
82095ad37f9SFrank van der Linden };
82195ad37f9SFrank van der Linden
82295ad37f9SFrank van der Linden static enum lru_status
cache_lru_isolate(struct list_head * item,struct list_lru_one * lru,spinlock_t * lru_lock,void * arg)82395ad37f9SFrank van der Linden cache_lru_isolate(struct list_head *item,
82495ad37f9SFrank van der Linden struct list_lru_one *lru, spinlock_t *lru_lock, void *arg)
82595ad37f9SFrank van der Linden {
82695ad37f9SFrank van der Linden struct list_head *dispose = arg;
82795ad37f9SFrank van der Linden struct inode *inode;
82895ad37f9SFrank van der Linden struct nfs4_xattr_cache *cache = container_of(item,
82995ad37f9SFrank van der Linden struct nfs4_xattr_cache, lru);
83095ad37f9SFrank van der Linden
83195ad37f9SFrank van der Linden if (atomic_long_read(&cache->nent) > 1)
83295ad37f9SFrank van der Linden return LRU_SKIP;
83395ad37f9SFrank van der Linden
83495ad37f9SFrank van der Linden /*
83595ad37f9SFrank van der Linden * If a cache structure is on the LRU list, we know that
83695ad37f9SFrank van der Linden * its inode is valid. Try to lock it to break the link.
83795ad37f9SFrank van der Linden * Since we're inverting the lock order here, only try.
83895ad37f9SFrank van der Linden */
83995ad37f9SFrank van der Linden inode = cache->inode;
84095ad37f9SFrank van der Linden
84195ad37f9SFrank van der Linden if (!spin_trylock(&inode->i_lock))
84295ad37f9SFrank van der Linden return LRU_SKIP;
84395ad37f9SFrank van der Linden
84495ad37f9SFrank van der Linden kref_get(&cache->ref);
84595ad37f9SFrank van der Linden
84695ad37f9SFrank van der Linden cache->inode = NULL;
84795ad37f9SFrank van der Linden NFS_I(inode)->xattr_cache = NULL;
84895ad37f9SFrank van der Linden NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_XATTR;
84995ad37f9SFrank van der Linden list_lru_isolate(lru, &cache->lru);
85095ad37f9SFrank van der Linden
85195ad37f9SFrank van der Linden spin_unlock(&inode->i_lock);
85295ad37f9SFrank van der Linden
85395ad37f9SFrank van der Linden list_add_tail(&cache->dispose, dispose);
85495ad37f9SFrank van der Linden return LRU_REMOVED;
85595ad37f9SFrank van der Linden }
85695ad37f9SFrank van der Linden
85795ad37f9SFrank van der Linden static unsigned long
nfs4_xattr_cache_scan(struct shrinker * shrink,struct shrink_control * sc)85895ad37f9SFrank van der Linden nfs4_xattr_cache_scan(struct shrinker *shrink, struct shrink_control *sc)
85995ad37f9SFrank van der Linden {
86095ad37f9SFrank van der Linden LIST_HEAD(dispose);
86195ad37f9SFrank van der Linden unsigned long freed;
86295ad37f9SFrank van der Linden struct nfs4_xattr_cache *cache;
86395ad37f9SFrank van der Linden
86495ad37f9SFrank van der Linden freed = list_lru_shrink_walk(&nfs4_xattr_cache_lru, sc,
86595ad37f9SFrank van der Linden cache_lru_isolate, &dispose);
86695ad37f9SFrank van der Linden while (!list_empty(&dispose)) {
86795ad37f9SFrank van der Linden cache = list_first_entry(&dispose, struct nfs4_xattr_cache,
86895ad37f9SFrank van der Linden dispose);
86995ad37f9SFrank van der Linden list_del_init(&cache->dispose);
87095ad37f9SFrank van der Linden nfs4_xattr_discard_cache(cache);
87195ad37f9SFrank van der Linden kref_put(&cache->ref, nfs4_xattr_free_cache_cb);
87295ad37f9SFrank van der Linden }
87395ad37f9SFrank van der Linden
87495ad37f9SFrank van der Linden return freed;
87595ad37f9SFrank van der Linden }
87695ad37f9SFrank van der Linden
87795ad37f9SFrank van der Linden
87895ad37f9SFrank van der Linden static unsigned long
nfs4_xattr_cache_count(struct shrinker * shrink,struct shrink_control * sc)87995ad37f9SFrank van der Linden nfs4_xattr_cache_count(struct shrinker *shrink, struct shrink_control *sc)
88095ad37f9SFrank van der Linden {
88195ad37f9SFrank van der Linden unsigned long count;
88295ad37f9SFrank van der Linden
8835904c16dSYang Shi count = list_lru_shrink_count(&nfs4_xattr_cache_lru, sc);
88495ad37f9SFrank van der Linden return vfs_pressure_ratio(count);
88595ad37f9SFrank van der Linden }
88695ad37f9SFrank van der Linden
88795ad37f9SFrank van der Linden static enum lru_status
entry_lru_isolate(struct list_head * item,struct list_lru_one * lru,spinlock_t * lru_lock,void * arg)88895ad37f9SFrank van der Linden entry_lru_isolate(struct list_head *item,
88995ad37f9SFrank van der Linden struct list_lru_one *lru, spinlock_t *lru_lock, void *arg)
89095ad37f9SFrank van der Linden {
89195ad37f9SFrank van der Linden struct list_head *dispose = arg;
89295ad37f9SFrank van der Linden struct nfs4_xattr_bucket *bucket;
89395ad37f9SFrank van der Linden struct nfs4_xattr_cache *cache;
89495ad37f9SFrank van der Linden struct nfs4_xattr_entry *entry = container_of(item,
89595ad37f9SFrank van der Linden struct nfs4_xattr_entry, lru);
89695ad37f9SFrank van der Linden
89795ad37f9SFrank van der Linden bucket = entry->bucket;
89895ad37f9SFrank van der Linden cache = bucket->cache;
89995ad37f9SFrank van der Linden
90095ad37f9SFrank van der Linden /*
90195ad37f9SFrank van der Linden * Unhook the entry from its parent (either a cache bucket
90295ad37f9SFrank van der Linden * or a cache structure if it's a listxattr buf), so that
90395ad37f9SFrank van der Linden * it's no longer found. Then add it to the isolate list,
90495ad37f9SFrank van der Linden * to be freed later.
90595ad37f9SFrank van der Linden *
90695ad37f9SFrank van der Linden * In both cases, we're reverting lock order, so use
90795ad37f9SFrank van der Linden * trylock and skip the entry if we can't get the lock.
90895ad37f9SFrank van der Linden */
90995ad37f9SFrank van der Linden if (entry->xattr_name != NULL) {
91095ad37f9SFrank van der Linden /* Regular cache entry */
91195ad37f9SFrank van der Linden if (!spin_trylock(&bucket->lock))
91295ad37f9SFrank van der Linden return LRU_SKIP;
91395ad37f9SFrank van der Linden
91495ad37f9SFrank van der Linden kref_get(&entry->ref);
91595ad37f9SFrank van der Linden
91695ad37f9SFrank van der Linden hlist_del_init(&entry->hnode);
91795ad37f9SFrank van der Linden atomic_long_dec(&cache->nent);
91895ad37f9SFrank van der Linden list_lru_isolate(lru, &entry->lru);
91995ad37f9SFrank van der Linden
92095ad37f9SFrank van der Linden spin_unlock(&bucket->lock);
92195ad37f9SFrank van der Linden } else {
92295ad37f9SFrank van der Linden /* Listxattr cache entry */
92395ad37f9SFrank van der Linden if (!spin_trylock(&cache->listxattr_lock))
92495ad37f9SFrank van der Linden return LRU_SKIP;
92595ad37f9SFrank van der Linden
92695ad37f9SFrank van der Linden kref_get(&entry->ref);
92795ad37f9SFrank van der Linden
92895ad37f9SFrank van der Linden cache->listxattr = NULL;
92995ad37f9SFrank van der Linden list_lru_isolate(lru, &entry->lru);
93095ad37f9SFrank van der Linden
93195ad37f9SFrank van der Linden spin_unlock(&cache->listxattr_lock);
93295ad37f9SFrank van der Linden }
93395ad37f9SFrank van der Linden
93495ad37f9SFrank van der Linden list_add_tail(&entry->dispose, dispose);
93595ad37f9SFrank van der Linden return LRU_REMOVED;
93695ad37f9SFrank van der Linden }
93795ad37f9SFrank van der Linden
93895ad37f9SFrank van der Linden static unsigned long
nfs4_xattr_entry_scan(struct shrinker * shrink,struct shrink_control * sc)93995ad37f9SFrank van der Linden nfs4_xattr_entry_scan(struct shrinker *shrink, struct shrink_control *sc)
94095ad37f9SFrank van der Linden {
94195ad37f9SFrank van der Linden LIST_HEAD(dispose);
94295ad37f9SFrank van der Linden unsigned long freed;
94395ad37f9SFrank van der Linden struct nfs4_xattr_entry *entry;
94495ad37f9SFrank van der Linden struct list_lru *lru;
94595ad37f9SFrank van der Linden
94695ad37f9SFrank van der Linden lru = (shrink == &nfs4_xattr_large_entry_shrinker) ?
94795ad37f9SFrank van der Linden &nfs4_xattr_large_entry_lru : &nfs4_xattr_entry_lru;
94895ad37f9SFrank van der Linden
94995ad37f9SFrank van der Linden freed = list_lru_shrink_walk(lru, sc, entry_lru_isolate, &dispose);
95095ad37f9SFrank van der Linden
95195ad37f9SFrank van der Linden while (!list_empty(&dispose)) {
95295ad37f9SFrank van der Linden entry = list_first_entry(&dispose, struct nfs4_xattr_entry,
95395ad37f9SFrank van der Linden dispose);
95495ad37f9SFrank van der Linden list_del_init(&entry->dispose);
95595ad37f9SFrank van der Linden
95695ad37f9SFrank van der Linden /*
95795ad37f9SFrank van der Linden * Drop two references: the one that we just grabbed
95895ad37f9SFrank van der Linden * in entry_lru_isolate, and the one that was set
95995ad37f9SFrank van der Linden * when the entry was first allocated.
96095ad37f9SFrank van der Linden */
96195ad37f9SFrank van der Linden kref_put(&entry->ref, nfs4_xattr_free_entry_cb);
96295ad37f9SFrank van der Linden kref_put(&entry->ref, nfs4_xattr_free_entry_cb);
96395ad37f9SFrank van der Linden }
96495ad37f9SFrank van der Linden
96595ad37f9SFrank van der Linden return freed;
96695ad37f9SFrank van der Linden }
96795ad37f9SFrank van der Linden
96895ad37f9SFrank van der Linden static unsigned long
nfs4_xattr_entry_count(struct shrinker * shrink,struct shrink_control * sc)96995ad37f9SFrank van der Linden nfs4_xattr_entry_count(struct shrinker *shrink, struct shrink_control *sc)
97095ad37f9SFrank van der Linden {
97195ad37f9SFrank van der Linden unsigned long count;
97295ad37f9SFrank van der Linden struct list_lru *lru;
97395ad37f9SFrank van der Linden
97495ad37f9SFrank van der Linden lru = (shrink == &nfs4_xattr_large_entry_shrinker) ?
97595ad37f9SFrank van der Linden &nfs4_xattr_large_entry_lru : &nfs4_xattr_entry_lru;
97695ad37f9SFrank van der Linden
9775904c16dSYang Shi count = list_lru_shrink_count(lru, sc);
97895ad37f9SFrank van der Linden return vfs_pressure_ratio(count);
97995ad37f9SFrank van der Linden }
98095ad37f9SFrank van der Linden
98195ad37f9SFrank van der Linden
nfs4_xattr_cache_init_once(void * p)98295ad37f9SFrank van der Linden static void nfs4_xattr_cache_init_once(void *p)
98395ad37f9SFrank van der Linden {
9847e7ce2ccSyuzhe struct nfs4_xattr_cache *cache = p;
98595ad37f9SFrank van der Linden
98695ad37f9SFrank van der Linden spin_lock_init(&cache->listxattr_lock);
98795ad37f9SFrank van der Linden atomic_long_set(&cache->nent, 0);
98895ad37f9SFrank van der Linden nfs4_xattr_hash_init(cache);
98995ad37f9SFrank van der Linden cache->listxattr = NULL;
99095ad37f9SFrank van der Linden INIT_LIST_HEAD(&cache->lru);
99195ad37f9SFrank van der Linden INIT_LIST_HEAD(&cache->dispose);
99295ad37f9SFrank van der Linden }
99395ad37f9SFrank van der Linden
nfs4_xattr_shrinker_init(struct shrinker * shrinker,struct list_lru * lru,const char * name)994*7f7ab336SQi Zheng static int nfs4_xattr_shrinker_init(struct shrinker *shrinker,
995*7f7ab336SQi Zheng struct list_lru *lru, const char *name)
996*7f7ab336SQi Zheng {
997*7f7ab336SQi Zheng int ret = 0;
998*7f7ab336SQi Zheng
999*7f7ab336SQi Zheng ret = register_shrinker(shrinker, name);
1000*7f7ab336SQi Zheng if (ret)
1001*7f7ab336SQi Zheng return ret;
1002*7f7ab336SQi Zheng
1003*7f7ab336SQi Zheng ret = list_lru_init_memcg(lru, shrinker);
1004*7f7ab336SQi Zheng if (ret)
1005*7f7ab336SQi Zheng unregister_shrinker(shrinker);
1006*7f7ab336SQi Zheng
1007*7f7ab336SQi Zheng return ret;
1008*7f7ab336SQi Zheng }
1009*7f7ab336SQi Zheng
nfs4_xattr_shrinker_destroy(struct shrinker * shrinker,struct list_lru * lru)1010*7f7ab336SQi Zheng static void nfs4_xattr_shrinker_destroy(struct shrinker *shrinker,
1011*7f7ab336SQi Zheng struct list_lru *lru)
1012*7f7ab336SQi Zheng {
1013*7f7ab336SQi Zheng unregister_shrinker(shrinker);
1014*7f7ab336SQi Zheng list_lru_destroy(lru);
1015*7f7ab336SQi Zheng }
1016*7f7ab336SQi Zheng
nfs4_xattr_cache_init(void)101795ad37f9SFrank van der Linden int __init nfs4_xattr_cache_init(void)
101895ad37f9SFrank van der Linden {
101995ad37f9SFrank van der Linden int ret = 0;
102095ad37f9SFrank van der Linden
102195ad37f9SFrank van der Linden nfs4_xattr_cache_cachep = kmem_cache_create("nfs4_xattr_cache_cache",
102295ad37f9SFrank van der Linden sizeof(struct nfs4_xattr_cache), 0,
1023dcc7977cSMuchun Song (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD),
102495ad37f9SFrank van der Linden nfs4_xattr_cache_init_once);
102595ad37f9SFrank van der Linden if (nfs4_xattr_cache_cachep == NULL)
102695ad37f9SFrank van der Linden return -ENOMEM;
102795ad37f9SFrank van der Linden
1028*7f7ab336SQi Zheng ret = nfs4_xattr_shrinker_init(&nfs4_xattr_cache_shrinker,
1029*7f7ab336SQi Zheng &nfs4_xattr_cache_lru,
1030*7f7ab336SQi Zheng "nfs-xattr_cache");
103195ad37f9SFrank van der Linden if (ret)
1032048c397aSFrank van der Linden goto out1;
103395ad37f9SFrank van der Linden
1034*7f7ab336SQi Zheng ret = nfs4_xattr_shrinker_init(&nfs4_xattr_entry_shrinker,
1035*7f7ab336SQi Zheng &nfs4_xattr_entry_lru,
1036*7f7ab336SQi Zheng "nfs-xattr_entry");
103795ad37f9SFrank van der Linden if (ret)
1038*7f7ab336SQi Zheng goto out2;
103995ad37f9SFrank van der Linden
1040*7f7ab336SQi Zheng ret = nfs4_xattr_shrinker_init(&nfs4_xattr_large_entry_shrinker,
1041*7f7ab336SQi Zheng &nfs4_xattr_large_entry_lru,
1042e33c267aSRoman Gushchin "nfs-xattr_large_entry");
104395ad37f9SFrank van der Linden if (!ret)
104495ad37f9SFrank van der Linden return 0;
104595ad37f9SFrank van der Linden
1046*7f7ab336SQi Zheng nfs4_xattr_shrinker_destroy(&nfs4_xattr_entry_shrinker,
1047*7f7ab336SQi Zheng &nfs4_xattr_entry_lru);
104895ad37f9SFrank van der Linden out2:
1049*7f7ab336SQi Zheng nfs4_xattr_shrinker_destroy(&nfs4_xattr_cache_shrinker,
1050*7f7ab336SQi Zheng &nfs4_xattr_cache_lru);
1051*7f7ab336SQi Zheng out1:
105295ad37f9SFrank van der Linden kmem_cache_destroy(nfs4_xattr_cache_cachep);
105395ad37f9SFrank van der Linden
105495ad37f9SFrank van der Linden return ret;
105595ad37f9SFrank van der Linden }
105695ad37f9SFrank van der Linden
nfs4_xattr_cache_exit(void)105795ad37f9SFrank van der Linden void nfs4_xattr_cache_exit(void)
105895ad37f9SFrank van der Linden {
1059*7f7ab336SQi Zheng nfs4_xattr_shrinker_destroy(&nfs4_xattr_large_entry_shrinker,
1060*7f7ab336SQi Zheng &nfs4_xattr_large_entry_lru);
1061*7f7ab336SQi Zheng nfs4_xattr_shrinker_destroy(&nfs4_xattr_entry_shrinker,
1062*7f7ab336SQi Zheng &nfs4_xattr_entry_lru);
1063*7f7ab336SQi Zheng nfs4_xattr_shrinker_destroy(&nfs4_xattr_cache_shrinker,
1064*7f7ab336SQi Zheng &nfs4_xattr_cache_lru);
106595ad37f9SFrank van der Linden kmem_cache_destroy(nfs4_xattr_cache_cachep);
106695ad37f9SFrank van der Linden }
1067