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