195ad37f9SFrank van der Linden // SPDX-License-Identifier: GPL-2.0 295ad37f9SFrank van der Linden 395ad37f9SFrank van der Linden /* 495ad37f9SFrank van der Linden * Copyright 2019, 2020 Amazon.com, Inc. or its affiliates. All rights reserved. 595ad37f9SFrank van der Linden * 695ad37f9SFrank van der Linden * User extended attribute client side cache functions. 795ad37f9SFrank van der Linden * 895ad37f9SFrank van der Linden * Author: Frank van der Linden <fllinden@amazon.com> 995ad37f9SFrank van der Linden */ 1095ad37f9SFrank van der Linden #include <linux/errno.h> 1195ad37f9SFrank van der Linden #include <linux/nfs_fs.h> 1295ad37f9SFrank van der Linden #include <linux/hashtable.h> 1395ad37f9SFrank van der Linden #include <linux/refcount.h> 1495ad37f9SFrank van der Linden #include <uapi/linux/xattr.h> 1595ad37f9SFrank van der Linden 1695ad37f9SFrank van der Linden #include "nfs4_fs.h" 1795ad37f9SFrank van der Linden #include "internal.h" 1895ad37f9SFrank van der Linden 1995ad37f9SFrank van der Linden /* 2095ad37f9SFrank van der Linden * User extended attributes client side caching is implemented by having 2195ad37f9SFrank van der Linden * a cache structure attached to NFS inodes. This structure is allocated 2295ad37f9SFrank van der Linden * when needed, and freed when the cache is zapped. 2395ad37f9SFrank van der Linden * 2495ad37f9SFrank van der Linden * The cache structure contains as hash table of entries, and a pointer 2595ad37f9SFrank van der Linden * to a special-cased entry for the listxattr cache. 2695ad37f9SFrank van der Linden * 2795ad37f9SFrank van der Linden * Accessing and allocating / freeing the caches is done via reference 2895ad37f9SFrank van der Linden * counting. The cache entries use a similar refcounting scheme. 2995ad37f9SFrank van der Linden * 3095ad37f9SFrank van der Linden * This makes freeing a cache, both from the shrinker and from the 3195ad37f9SFrank van der Linden * zap cache path, easy. It also means that, in current use cases, 3295ad37f9SFrank van der Linden * the large majority of inodes will not waste any memory, as they 3395ad37f9SFrank van der Linden * will never have any user extended attributes assigned to them. 3495ad37f9SFrank van der Linden * 3595ad37f9SFrank van der Linden * Attribute entries are hashed in to a simple hash table. They are 3695ad37f9SFrank van der Linden * also part of an LRU. 3795ad37f9SFrank van der Linden * 3895ad37f9SFrank van der Linden * There are three shrinkers. 3995ad37f9SFrank van der Linden * 4095ad37f9SFrank van der Linden * Two shrinkers deal with the cache entries themselves: one for 4195ad37f9SFrank van der Linden * large entries (> PAGE_SIZE), and one for smaller entries. The 4295ad37f9SFrank van der Linden * shrinker for the larger entries works more aggressively than 4395ad37f9SFrank van der Linden * those for the smaller entries. 4495ad37f9SFrank van der Linden * 4595ad37f9SFrank van der Linden * The other shrinker frees the cache structures themselves. 4695ad37f9SFrank van der Linden */ 4795ad37f9SFrank van der Linden 4895ad37f9SFrank van der Linden /* 4995ad37f9SFrank van der Linden * 64 buckets is a good default. There is likely no reasonable 5095ad37f9SFrank van der Linden * workload that uses more than even 64 user extended attributes. 5195ad37f9SFrank van der Linden * You can certainly add a lot more - but you get what you ask for 5295ad37f9SFrank van der Linden * in those circumstances. 5395ad37f9SFrank van der Linden */ 5495ad37f9SFrank van der Linden #define NFS4_XATTR_HASH_SIZE 64 5595ad37f9SFrank van der Linden 5695ad37f9SFrank van der Linden #define NFSDBG_FACILITY NFSDBG_XATTRCACHE 5795ad37f9SFrank van der Linden 5895ad37f9SFrank van der Linden struct nfs4_xattr_cache; 5995ad37f9SFrank van der Linden struct nfs4_xattr_entry; 6095ad37f9SFrank van der Linden 6195ad37f9SFrank van der Linden struct nfs4_xattr_bucket { 6295ad37f9SFrank van der Linden spinlock_t lock; 6395ad37f9SFrank van der Linden struct hlist_head hlist; 6495ad37f9SFrank van der Linden struct nfs4_xattr_cache *cache; 6595ad37f9SFrank van der Linden bool draining; 6695ad37f9SFrank van der Linden }; 6795ad37f9SFrank van der Linden 6895ad37f9SFrank van der Linden struct nfs4_xattr_cache { 6995ad37f9SFrank van der Linden struct kref ref; 7095ad37f9SFrank van der Linden struct nfs4_xattr_bucket buckets[NFS4_XATTR_HASH_SIZE]; 7195ad37f9SFrank van der Linden struct list_head lru; 7295ad37f9SFrank van der Linden struct list_head dispose; 7395ad37f9SFrank van der Linden atomic_long_t nent; 7495ad37f9SFrank van der Linden spinlock_t listxattr_lock; 7595ad37f9SFrank van der Linden struct inode *inode; 7695ad37f9SFrank van der Linden struct nfs4_xattr_entry *listxattr; 7795ad37f9SFrank van der Linden }; 7895ad37f9SFrank van der Linden 7995ad37f9SFrank van der Linden struct nfs4_xattr_entry { 8095ad37f9SFrank van der Linden struct kref ref; 8195ad37f9SFrank van der Linden struct hlist_node hnode; 8295ad37f9SFrank van der Linden struct list_head lru; 8395ad37f9SFrank van der Linden struct list_head dispose; 8495ad37f9SFrank van der Linden char *xattr_name; 8595ad37f9SFrank van der Linden void *xattr_value; 8695ad37f9SFrank van der Linden size_t xattr_size; 8795ad37f9SFrank van der Linden struct nfs4_xattr_bucket *bucket; 8895ad37f9SFrank van der Linden uint32_t flags; 8995ad37f9SFrank van der Linden }; 9095ad37f9SFrank van der Linden 9195ad37f9SFrank van der Linden #define NFS4_XATTR_ENTRY_EXTVAL 0x0001 9295ad37f9SFrank van der Linden 9395ad37f9SFrank van der Linden /* 9495ad37f9SFrank van der Linden * LRU list of NFS inodes that have xattr caches. 9595ad37f9SFrank van der Linden */ 9695ad37f9SFrank van der Linden static struct list_lru nfs4_xattr_cache_lru; 9795ad37f9SFrank van der Linden static struct list_lru nfs4_xattr_entry_lru; 9895ad37f9SFrank van der Linden static struct list_lru nfs4_xattr_large_entry_lru; 9995ad37f9SFrank van der Linden 10095ad37f9SFrank van der Linden static struct kmem_cache *nfs4_xattr_cache_cachep; 10195ad37f9SFrank van der Linden 10295ad37f9SFrank van der Linden /* 10395ad37f9SFrank van der Linden * Hashing helper functions. 10495ad37f9SFrank van der Linden */ 10595ad37f9SFrank van der Linden static void 10695ad37f9SFrank van der Linden nfs4_xattr_hash_init(struct nfs4_xattr_cache *cache) 10795ad37f9SFrank van der Linden { 10895ad37f9SFrank van der Linden unsigned int i; 10995ad37f9SFrank van der Linden 11095ad37f9SFrank van der Linden for (i = 0; i < NFS4_XATTR_HASH_SIZE; i++) { 11195ad37f9SFrank van der Linden INIT_HLIST_HEAD(&cache->buckets[i].hlist); 11295ad37f9SFrank van der Linden spin_lock_init(&cache->buckets[i].lock); 11395ad37f9SFrank van der Linden cache->buckets[i].cache = cache; 11495ad37f9SFrank van der Linden cache->buckets[i].draining = false; 11595ad37f9SFrank van der Linden } 11695ad37f9SFrank van der Linden } 11795ad37f9SFrank van der Linden 11895ad37f9SFrank van der Linden /* 11995ad37f9SFrank van der Linden * Locking order: 12095ad37f9SFrank van der Linden * 1. inode i_lock or bucket lock 12195ad37f9SFrank van der Linden * 2. list_lru lock (taken by list_lru_* functions) 12295ad37f9SFrank van der Linden */ 12395ad37f9SFrank van der Linden 12495ad37f9SFrank van der Linden /* 12595ad37f9SFrank van der Linden * Wrapper functions to add a cache entry to the right LRU. 12695ad37f9SFrank van der Linden */ 12795ad37f9SFrank van der Linden static bool 12895ad37f9SFrank van der Linden nfs4_xattr_entry_lru_add(struct nfs4_xattr_entry *entry) 12995ad37f9SFrank van der Linden { 13095ad37f9SFrank van der Linden struct list_lru *lru; 13195ad37f9SFrank van der Linden 13295ad37f9SFrank van der Linden lru = (entry->flags & NFS4_XATTR_ENTRY_EXTVAL) ? 13395ad37f9SFrank van der Linden &nfs4_xattr_large_entry_lru : &nfs4_xattr_entry_lru; 13495ad37f9SFrank van der Linden 13595ad37f9SFrank van der Linden return list_lru_add(lru, &entry->lru); 13695ad37f9SFrank van der Linden } 13795ad37f9SFrank van der Linden 13895ad37f9SFrank van der Linden static bool 13995ad37f9SFrank van der Linden nfs4_xattr_entry_lru_del(struct nfs4_xattr_entry *entry) 14095ad37f9SFrank van der Linden { 14195ad37f9SFrank van der Linden struct list_lru *lru; 14295ad37f9SFrank van der Linden 14395ad37f9SFrank van der Linden lru = (entry->flags & NFS4_XATTR_ENTRY_EXTVAL) ? 14495ad37f9SFrank van der Linden &nfs4_xattr_large_entry_lru : &nfs4_xattr_entry_lru; 14595ad37f9SFrank van der Linden 14695ad37f9SFrank van der Linden return list_lru_del(lru, &entry->lru); 14795ad37f9SFrank van der Linden } 14895ad37f9SFrank van der Linden 14995ad37f9SFrank van der Linden /* 15095ad37f9SFrank van der Linden * This function allocates cache entries. They are the normal 15195ad37f9SFrank van der Linden * extended attribute name/value pairs, but may also be a listxattr 15295ad37f9SFrank van der Linden * cache. Those allocations use the same entry so that they can be 15395ad37f9SFrank van der Linden * treated as one by the memory shrinker. 15495ad37f9SFrank van der Linden * 15595ad37f9SFrank van der Linden * xattr cache entries are allocated together with names. If the 15695ad37f9SFrank van der Linden * value fits in to one page with the entry structure and the name, 15795ad37f9SFrank van der Linden * it will also be part of the same allocation (kmalloc). This is 15895ad37f9SFrank van der Linden * expected to be the vast majority of cases. Larger allocations 15995ad37f9SFrank van der Linden * have a value pointer that is allocated separately by kvmalloc. 16095ad37f9SFrank van der Linden * 16195ad37f9SFrank van der Linden * Parameters: 16295ad37f9SFrank van der Linden * 16395ad37f9SFrank van der Linden * @name: Name of the extended attribute. NULL for listxattr cache 16495ad37f9SFrank van der Linden * entry. 16595ad37f9SFrank van der Linden * @value: Value of attribute, or listxattr cache. NULL if the 16695ad37f9SFrank van der Linden * value is to be copied from pages instead. 16795ad37f9SFrank van der Linden * @pages: Pages to copy the value from, if not NULL. Passed in to 16895ad37f9SFrank van der Linden * make it easier to copy the value after an RPC, even if 16995ad37f9SFrank van der Linden * the value will not be passed up to application (e.g. 17095ad37f9SFrank van der Linden * for a 'query' getxattr with NULL buffer). 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 * 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 202*5c60e89eSTrond 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) { 216*5c60e89eSTrond 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 24795ad37f9SFrank van der Linden nfs4_xattr_free_entry(struct nfs4_xattr_entry *entry) 24895ad37f9SFrank van der Linden { 24995ad37f9SFrank van der Linden if (entry->flags & NFS4_XATTR_ENTRY_EXTVAL) 25095ad37f9SFrank van der Linden kvfree(entry->xattr_value); 25195ad37f9SFrank van der Linden kfree(entry); 25295ad37f9SFrank van der Linden } 25395ad37f9SFrank van der Linden 25495ad37f9SFrank van der Linden static void 25595ad37f9SFrank van der Linden nfs4_xattr_free_entry_cb(struct kref *kref) 25695ad37f9SFrank van der Linden { 25795ad37f9SFrank van der Linden struct nfs4_xattr_entry *entry; 25895ad37f9SFrank van der Linden 25995ad37f9SFrank van der Linden entry = container_of(kref, struct nfs4_xattr_entry, ref); 26095ad37f9SFrank van der Linden 26195ad37f9SFrank van der Linden if (WARN_ON(!list_empty(&entry->lru))) 26295ad37f9SFrank van der Linden return; 26395ad37f9SFrank van der Linden 26495ad37f9SFrank van der Linden nfs4_xattr_free_entry(entry); 26595ad37f9SFrank van der Linden } 26695ad37f9SFrank van der Linden 26795ad37f9SFrank van der Linden static void 26895ad37f9SFrank van der Linden nfs4_xattr_free_cache_cb(struct kref *kref) 26995ad37f9SFrank van der Linden { 27095ad37f9SFrank van der Linden struct nfs4_xattr_cache *cache; 27195ad37f9SFrank van der Linden int i; 27295ad37f9SFrank van der Linden 27395ad37f9SFrank van der Linden cache = container_of(kref, struct nfs4_xattr_cache, ref); 27495ad37f9SFrank van der Linden 27595ad37f9SFrank van der Linden for (i = 0; i < NFS4_XATTR_HASH_SIZE; i++) { 27695ad37f9SFrank van der Linden if (WARN_ON(!hlist_empty(&cache->buckets[i].hlist))) 27795ad37f9SFrank van der Linden return; 27895ad37f9SFrank van der Linden cache->buckets[i].draining = false; 27995ad37f9SFrank van der Linden } 28095ad37f9SFrank van der Linden 28195ad37f9SFrank van der Linden cache->listxattr = NULL; 28295ad37f9SFrank van der Linden 28395ad37f9SFrank van der Linden kmem_cache_free(nfs4_xattr_cache_cachep, cache); 28495ad37f9SFrank van der Linden 28595ad37f9SFrank van der Linden } 28695ad37f9SFrank van der Linden 28795ad37f9SFrank van der Linden static struct nfs4_xattr_cache * 28895ad37f9SFrank van der Linden nfs4_xattr_alloc_cache(void) 28995ad37f9SFrank van der Linden { 29095ad37f9SFrank van der Linden struct nfs4_xattr_cache *cache; 29195ad37f9SFrank van der Linden 292*5c60e89eSTrond 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 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 * 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 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 * 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 * 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 * 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 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 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 * 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 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 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 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 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 98295ad37f9SFrank van der Linden static void nfs4_xattr_cache_init_once(void *p) 98395ad37f9SFrank van der Linden { 98495ad37f9SFrank van der Linden struct nfs4_xattr_cache *cache = (struct nfs4_xattr_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 99495ad37f9SFrank van der Linden int __init nfs4_xattr_cache_init(void) 99595ad37f9SFrank van der Linden { 99695ad37f9SFrank van der Linden int ret = 0; 99795ad37f9SFrank van der Linden 99895ad37f9SFrank van der Linden nfs4_xattr_cache_cachep = kmem_cache_create("nfs4_xattr_cache_cache", 99995ad37f9SFrank van der Linden sizeof(struct nfs4_xattr_cache), 0, 100095ad37f9SFrank van der Linden (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD|SLAB_ACCOUNT), 100195ad37f9SFrank van der Linden nfs4_xattr_cache_init_once); 100295ad37f9SFrank van der Linden if (nfs4_xattr_cache_cachep == NULL) 100395ad37f9SFrank van der Linden return -ENOMEM; 100495ad37f9SFrank van der Linden 100595ad37f9SFrank van der Linden ret = list_lru_init_memcg(&nfs4_xattr_large_entry_lru, 100695ad37f9SFrank van der Linden &nfs4_xattr_large_entry_shrinker); 100795ad37f9SFrank van der Linden if (ret) 100895ad37f9SFrank van der Linden goto out4; 100995ad37f9SFrank van der Linden 101095ad37f9SFrank van der Linden ret = list_lru_init_memcg(&nfs4_xattr_entry_lru, 101195ad37f9SFrank van der Linden &nfs4_xattr_entry_shrinker); 101295ad37f9SFrank van der Linden if (ret) 101395ad37f9SFrank van der Linden goto out3; 101495ad37f9SFrank van der Linden 101595ad37f9SFrank van der Linden ret = list_lru_init_memcg(&nfs4_xattr_cache_lru, 101695ad37f9SFrank van der Linden &nfs4_xattr_cache_shrinker); 101795ad37f9SFrank van der Linden if (ret) 101895ad37f9SFrank van der Linden goto out2; 101995ad37f9SFrank van der Linden 102095ad37f9SFrank van der Linden ret = register_shrinker(&nfs4_xattr_cache_shrinker); 102195ad37f9SFrank van der Linden if (ret) 1022048c397aSFrank van der Linden goto out1; 102395ad37f9SFrank van der Linden 102495ad37f9SFrank van der Linden ret = register_shrinker(&nfs4_xattr_entry_shrinker); 102595ad37f9SFrank van der Linden if (ret) 102695ad37f9SFrank van der Linden goto out; 102795ad37f9SFrank van der Linden 102895ad37f9SFrank van der Linden ret = register_shrinker(&nfs4_xattr_large_entry_shrinker); 102995ad37f9SFrank van der Linden if (!ret) 103095ad37f9SFrank van der Linden return 0; 103195ad37f9SFrank van der Linden 103295ad37f9SFrank van der Linden unregister_shrinker(&nfs4_xattr_entry_shrinker); 103395ad37f9SFrank van der Linden out: 103495ad37f9SFrank van der Linden unregister_shrinker(&nfs4_xattr_cache_shrinker); 103595ad37f9SFrank van der Linden out1: 103695ad37f9SFrank van der Linden list_lru_destroy(&nfs4_xattr_cache_lru); 103795ad37f9SFrank van der Linden out2: 103895ad37f9SFrank van der Linden list_lru_destroy(&nfs4_xattr_entry_lru); 103995ad37f9SFrank van der Linden out3: 104095ad37f9SFrank van der Linden list_lru_destroy(&nfs4_xattr_large_entry_lru); 104195ad37f9SFrank van der Linden out4: 104295ad37f9SFrank van der Linden kmem_cache_destroy(nfs4_xattr_cache_cachep); 104395ad37f9SFrank van der Linden 104495ad37f9SFrank van der Linden return ret; 104595ad37f9SFrank van der Linden } 104695ad37f9SFrank van der Linden 104795ad37f9SFrank van der Linden void nfs4_xattr_cache_exit(void) 104895ad37f9SFrank van der Linden { 104970438afbSJ. Bruce Fields unregister_shrinker(&nfs4_xattr_large_entry_shrinker); 105095ad37f9SFrank van der Linden unregister_shrinker(&nfs4_xattr_entry_shrinker); 105195ad37f9SFrank van der Linden unregister_shrinker(&nfs4_xattr_cache_shrinker); 105270438afbSJ. Bruce Fields list_lru_destroy(&nfs4_xattr_large_entry_lru); 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