162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci/* 462306a36Sopenharmony_ci * Copyright 2019, 2020 Amazon.com, Inc. or its affiliates. All rights reserved. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * User extended attribute client side cache functions. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Author: Frank van der Linden <fllinden@amazon.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#include <linux/errno.h> 1162306a36Sopenharmony_ci#include <linux/nfs_fs.h> 1262306a36Sopenharmony_ci#include <linux/hashtable.h> 1362306a36Sopenharmony_ci#include <linux/refcount.h> 1462306a36Sopenharmony_ci#include <uapi/linux/xattr.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "nfs4_fs.h" 1762306a36Sopenharmony_ci#include "internal.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * User extended attributes client side caching is implemented by having 2162306a36Sopenharmony_ci * a cache structure attached to NFS inodes. This structure is allocated 2262306a36Sopenharmony_ci * when needed, and freed when the cache is zapped. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * The cache structure contains as hash table of entries, and a pointer 2562306a36Sopenharmony_ci * to a special-cased entry for the listxattr cache. 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * Accessing and allocating / freeing the caches is done via reference 2862306a36Sopenharmony_ci * counting. The cache entries use a similar refcounting scheme. 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * This makes freeing a cache, both from the shrinker and from the 3162306a36Sopenharmony_ci * zap cache path, easy. It also means that, in current use cases, 3262306a36Sopenharmony_ci * the large majority of inodes will not waste any memory, as they 3362306a36Sopenharmony_ci * will never have any user extended attributes assigned to them. 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * Attribute entries are hashed in to a simple hash table. They are 3662306a36Sopenharmony_ci * also part of an LRU. 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * There are three shrinkers. 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * Two shrinkers deal with the cache entries themselves: one for 4162306a36Sopenharmony_ci * large entries (> PAGE_SIZE), and one for smaller entries. The 4262306a36Sopenharmony_ci * shrinker for the larger entries works more aggressively than 4362306a36Sopenharmony_ci * those for the smaller entries. 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * The other shrinker frees the cache structures themselves. 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* 4962306a36Sopenharmony_ci * 64 buckets is a good default. There is likely no reasonable 5062306a36Sopenharmony_ci * workload that uses more than even 64 user extended attributes. 5162306a36Sopenharmony_ci * You can certainly add a lot more - but you get what you ask for 5262306a36Sopenharmony_ci * in those circumstances. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci#define NFS4_XATTR_HASH_SIZE 64 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define NFSDBG_FACILITY NFSDBG_XATTRCACHE 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistruct nfs4_xattr_cache; 5962306a36Sopenharmony_cistruct nfs4_xattr_entry; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistruct nfs4_xattr_bucket { 6262306a36Sopenharmony_ci spinlock_t lock; 6362306a36Sopenharmony_ci struct hlist_head hlist; 6462306a36Sopenharmony_ci struct nfs4_xattr_cache *cache; 6562306a36Sopenharmony_ci bool draining; 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistruct nfs4_xattr_cache { 6962306a36Sopenharmony_ci struct kref ref; 7062306a36Sopenharmony_ci struct nfs4_xattr_bucket buckets[NFS4_XATTR_HASH_SIZE]; 7162306a36Sopenharmony_ci struct list_head lru; 7262306a36Sopenharmony_ci struct list_head dispose; 7362306a36Sopenharmony_ci atomic_long_t nent; 7462306a36Sopenharmony_ci spinlock_t listxattr_lock; 7562306a36Sopenharmony_ci struct inode *inode; 7662306a36Sopenharmony_ci struct nfs4_xattr_entry *listxattr; 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistruct nfs4_xattr_entry { 8062306a36Sopenharmony_ci struct kref ref; 8162306a36Sopenharmony_ci struct hlist_node hnode; 8262306a36Sopenharmony_ci struct list_head lru; 8362306a36Sopenharmony_ci struct list_head dispose; 8462306a36Sopenharmony_ci char *xattr_name; 8562306a36Sopenharmony_ci void *xattr_value; 8662306a36Sopenharmony_ci size_t xattr_size; 8762306a36Sopenharmony_ci struct nfs4_xattr_bucket *bucket; 8862306a36Sopenharmony_ci uint32_t flags; 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#define NFS4_XATTR_ENTRY_EXTVAL 0x0001 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* 9462306a36Sopenharmony_ci * LRU list of NFS inodes that have xattr caches. 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_cistatic struct list_lru nfs4_xattr_cache_lru; 9762306a36Sopenharmony_cistatic struct list_lru nfs4_xattr_entry_lru; 9862306a36Sopenharmony_cistatic struct list_lru nfs4_xattr_large_entry_lru; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic struct kmem_cache *nfs4_xattr_cache_cachep; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* 10362306a36Sopenharmony_ci * Hashing helper functions. 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_cistatic void 10662306a36Sopenharmony_cinfs4_xattr_hash_init(struct nfs4_xattr_cache *cache) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci unsigned int i; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci for (i = 0; i < NFS4_XATTR_HASH_SIZE; i++) { 11162306a36Sopenharmony_ci INIT_HLIST_HEAD(&cache->buckets[i].hlist); 11262306a36Sopenharmony_ci spin_lock_init(&cache->buckets[i].lock); 11362306a36Sopenharmony_ci cache->buckets[i].cache = cache; 11462306a36Sopenharmony_ci cache->buckets[i].draining = false; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* 11962306a36Sopenharmony_ci * Locking order: 12062306a36Sopenharmony_ci * 1. inode i_lock or bucket lock 12162306a36Sopenharmony_ci * 2. list_lru lock (taken by list_lru_* functions) 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* 12562306a36Sopenharmony_ci * Wrapper functions to add a cache entry to the right LRU. 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_cistatic bool 12862306a36Sopenharmony_cinfs4_xattr_entry_lru_add(struct nfs4_xattr_entry *entry) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct list_lru *lru; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci lru = (entry->flags & NFS4_XATTR_ENTRY_EXTVAL) ? 13362306a36Sopenharmony_ci &nfs4_xattr_large_entry_lru : &nfs4_xattr_entry_lru; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return list_lru_add(lru, &entry->lru); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic bool 13962306a36Sopenharmony_cinfs4_xattr_entry_lru_del(struct nfs4_xattr_entry *entry) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct list_lru *lru; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci lru = (entry->flags & NFS4_XATTR_ENTRY_EXTVAL) ? 14462306a36Sopenharmony_ci &nfs4_xattr_large_entry_lru : &nfs4_xattr_entry_lru; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci return list_lru_del(lru, &entry->lru); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* 15062306a36Sopenharmony_ci * This function allocates cache entries. They are the normal 15162306a36Sopenharmony_ci * extended attribute name/value pairs, but may also be a listxattr 15262306a36Sopenharmony_ci * cache. Those allocations use the same entry so that they can be 15362306a36Sopenharmony_ci * treated as one by the memory shrinker. 15462306a36Sopenharmony_ci * 15562306a36Sopenharmony_ci * xattr cache entries are allocated together with names. If the 15662306a36Sopenharmony_ci * value fits in to one page with the entry structure and the name, 15762306a36Sopenharmony_ci * it will also be part of the same allocation (kmalloc). This is 15862306a36Sopenharmony_ci * expected to be the vast majority of cases. Larger allocations 15962306a36Sopenharmony_ci * have a value pointer that is allocated separately by kvmalloc. 16062306a36Sopenharmony_ci * 16162306a36Sopenharmony_ci * Parameters: 16262306a36Sopenharmony_ci * 16362306a36Sopenharmony_ci * @name: Name of the extended attribute. NULL for listxattr cache 16462306a36Sopenharmony_ci * entry. 16562306a36Sopenharmony_ci * @value: Value of attribute, or listxattr cache. NULL if the 16662306a36Sopenharmony_ci * value is to be copied from pages instead. 16762306a36Sopenharmony_ci * @pages: Pages to copy the value from, if not NULL. Passed in to 16862306a36Sopenharmony_ci * make it easier to copy the value after an RPC, even if 16962306a36Sopenharmony_ci * the value will not be passed up to application (e.g. 17062306a36Sopenharmony_ci * for a 'query' getxattr with NULL buffer). 17162306a36Sopenharmony_ci * @len: Length of the value. Can be 0 for zero-length attributes. 17262306a36Sopenharmony_ci * @value and @pages will be NULL if @len is 0. 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_cistatic struct nfs4_xattr_entry * 17562306a36Sopenharmony_cinfs4_xattr_alloc_entry(const char *name, const void *value, 17662306a36Sopenharmony_ci struct page **pages, size_t len) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci struct nfs4_xattr_entry *entry; 17962306a36Sopenharmony_ci void *valp; 18062306a36Sopenharmony_ci char *namep; 18162306a36Sopenharmony_ci size_t alloclen, slen; 18262306a36Sopenharmony_ci char *buf; 18362306a36Sopenharmony_ci uint32_t flags; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct nfs4_xattr_entry) + 18662306a36Sopenharmony_ci XATTR_NAME_MAX + 1 > PAGE_SIZE); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci alloclen = sizeof(struct nfs4_xattr_entry); 18962306a36Sopenharmony_ci if (name != NULL) { 19062306a36Sopenharmony_ci slen = strlen(name) + 1; 19162306a36Sopenharmony_ci alloclen += slen; 19262306a36Sopenharmony_ci } else 19362306a36Sopenharmony_ci slen = 0; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (alloclen + len <= PAGE_SIZE) { 19662306a36Sopenharmony_ci alloclen += len; 19762306a36Sopenharmony_ci flags = 0; 19862306a36Sopenharmony_ci } else { 19962306a36Sopenharmony_ci flags = NFS4_XATTR_ENTRY_EXTVAL; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci buf = kmalloc(alloclen, GFP_KERNEL); 20362306a36Sopenharmony_ci if (buf == NULL) 20462306a36Sopenharmony_ci return NULL; 20562306a36Sopenharmony_ci entry = (struct nfs4_xattr_entry *)buf; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (name != NULL) { 20862306a36Sopenharmony_ci namep = buf + sizeof(struct nfs4_xattr_entry); 20962306a36Sopenharmony_ci memcpy(namep, name, slen); 21062306a36Sopenharmony_ci } else { 21162306a36Sopenharmony_ci namep = NULL; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (flags & NFS4_XATTR_ENTRY_EXTVAL) { 21662306a36Sopenharmony_ci valp = kvmalloc(len, GFP_KERNEL); 21762306a36Sopenharmony_ci if (valp == NULL) { 21862306a36Sopenharmony_ci kfree(buf); 21962306a36Sopenharmony_ci return NULL; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci } else if (len != 0) { 22262306a36Sopenharmony_ci valp = buf + sizeof(struct nfs4_xattr_entry) + slen; 22362306a36Sopenharmony_ci } else 22462306a36Sopenharmony_ci valp = NULL; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (valp != NULL) { 22762306a36Sopenharmony_ci if (value != NULL) 22862306a36Sopenharmony_ci memcpy(valp, value, len); 22962306a36Sopenharmony_ci else 23062306a36Sopenharmony_ci _copy_from_pages(valp, pages, 0, len); 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci entry->flags = flags; 23462306a36Sopenharmony_ci entry->xattr_value = valp; 23562306a36Sopenharmony_ci kref_init(&entry->ref); 23662306a36Sopenharmony_ci entry->xattr_name = namep; 23762306a36Sopenharmony_ci entry->xattr_size = len; 23862306a36Sopenharmony_ci entry->bucket = NULL; 23962306a36Sopenharmony_ci INIT_LIST_HEAD(&entry->lru); 24062306a36Sopenharmony_ci INIT_LIST_HEAD(&entry->dispose); 24162306a36Sopenharmony_ci INIT_HLIST_NODE(&entry->hnode); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci return entry; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic void 24762306a36Sopenharmony_cinfs4_xattr_free_entry(struct nfs4_xattr_entry *entry) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci if (entry->flags & NFS4_XATTR_ENTRY_EXTVAL) 25062306a36Sopenharmony_ci kvfree(entry->xattr_value); 25162306a36Sopenharmony_ci kfree(entry); 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic void 25562306a36Sopenharmony_cinfs4_xattr_free_entry_cb(struct kref *kref) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci struct nfs4_xattr_entry *entry; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci entry = container_of(kref, struct nfs4_xattr_entry, ref); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (WARN_ON(!list_empty(&entry->lru))) 26262306a36Sopenharmony_ci return; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci nfs4_xattr_free_entry(entry); 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic void 26862306a36Sopenharmony_cinfs4_xattr_free_cache_cb(struct kref *kref) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci struct nfs4_xattr_cache *cache; 27162306a36Sopenharmony_ci int i; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci cache = container_of(kref, struct nfs4_xattr_cache, ref); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci for (i = 0; i < NFS4_XATTR_HASH_SIZE; i++) { 27662306a36Sopenharmony_ci if (WARN_ON(!hlist_empty(&cache->buckets[i].hlist))) 27762306a36Sopenharmony_ci return; 27862306a36Sopenharmony_ci cache->buckets[i].draining = false; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci cache->listxattr = NULL; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci kmem_cache_free(nfs4_xattr_cache_cachep, cache); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic struct nfs4_xattr_cache * 28862306a36Sopenharmony_cinfs4_xattr_alloc_cache(void) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct nfs4_xattr_cache *cache; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci cache = kmem_cache_alloc(nfs4_xattr_cache_cachep, GFP_KERNEL); 29362306a36Sopenharmony_ci if (cache == NULL) 29462306a36Sopenharmony_ci return NULL; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci kref_init(&cache->ref); 29762306a36Sopenharmony_ci atomic_long_set(&cache->nent, 0); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci return cache; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci/* 30362306a36Sopenharmony_ci * Set the listxattr cache, which is a special-cased cache entry. 30462306a36Sopenharmony_ci * The special value ERR_PTR(-ESTALE) is used to indicate that 30562306a36Sopenharmony_ci * the cache is being drained - this prevents a new listxattr 30662306a36Sopenharmony_ci * cache from being added to what is now a stale cache. 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_cistatic int 30962306a36Sopenharmony_cinfs4_xattr_set_listcache(struct nfs4_xattr_cache *cache, 31062306a36Sopenharmony_ci struct nfs4_xattr_entry *new) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci struct nfs4_xattr_entry *old; 31362306a36Sopenharmony_ci int ret = 1; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci spin_lock(&cache->listxattr_lock); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci old = cache->listxattr; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (old == ERR_PTR(-ESTALE)) { 32062306a36Sopenharmony_ci ret = 0; 32162306a36Sopenharmony_ci goto out; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci cache->listxattr = new; 32562306a36Sopenharmony_ci if (new != NULL && new != ERR_PTR(-ESTALE)) 32662306a36Sopenharmony_ci nfs4_xattr_entry_lru_add(new); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (old != NULL) { 32962306a36Sopenharmony_ci nfs4_xattr_entry_lru_del(old); 33062306a36Sopenharmony_ci kref_put(&old->ref, nfs4_xattr_free_entry_cb); 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ciout: 33362306a36Sopenharmony_ci spin_unlock(&cache->listxattr_lock); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci return ret; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci/* 33962306a36Sopenharmony_ci * Unlink a cache from its parent inode, clearing out an invalid 34062306a36Sopenharmony_ci * cache. Must be called with i_lock held. 34162306a36Sopenharmony_ci */ 34262306a36Sopenharmony_cistatic struct nfs4_xattr_cache * 34362306a36Sopenharmony_cinfs4_xattr_cache_unlink(struct inode *inode) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci struct nfs_inode *nfsi; 34662306a36Sopenharmony_ci struct nfs4_xattr_cache *oldcache; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci nfsi = NFS_I(inode); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci oldcache = nfsi->xattr_cache; 35162306a36Sopenharmony_ci if (oldcache != NULL) { 35262306a36Sopenharmony_ci list_lru_del(&nfs4_xattr_cache_lru, &oldcache->lru); 35362306a36Sopenharmony_ci oldcache->inode = NULL; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci nfsi->xattr_cache = NULL; 35662306a36Sopenharmony_ci nfsi->cache_validity &= ~NFS_INO_INVALID_XATTR; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci return oldcache; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci/* 36362306a36Sopenharmony_ci * Discard a cache. Called by get_cache() if there was an old, 36462306a36Sopenharmony_ci * invalid cache. Can also be called from a shrinker callback. 36562306a36Sopenharmony_ci * 36662306a36Sopenharmony_ci * The cache is dead, it has already been unlinked from its inode, 36762306a36Sopenharmony_ci * and no longer appears on the cache LRU list. 36862306a36Sopenharmony_ci * 36962306a36Sopenharmony_ci * Mark all buckets as draining, so that no new entries are added. This 37062306a36Sopenharmony_ci * could still happen in the unlikely, but possible case that another 37162306a36Sopenharmony_ci * thread had grabbed a reference before it was unlinked from the inode, 37262306a36Sopenharmony_ci * and is still holding it for an add operation. 37362306a36Sopenharmony_ci * 37462306a36Sopenharmony_ci * Remove all entries from the LRU lists, so that there is no longer 37562306a36Sopenharmony_ci * any way to 'find' this cache. Then, remove the entries from the hash 37662306a36Sopenharmony_ci * table. 37762306a36Sopenharmony_ci * 37862306a36Sopenharmony_ci * At that point, the cache will remain empty and can be freed when the final 37962306a36Sopenharmony_ci * reference drops, which is very likely the kref_put at the end of 38062306a36Sopenharmony_ci * this function, or the one called immediately afterwards in the 38162306a36Sopenharmony_ci * shrinker callback. 38262306a36Sopenharmony_ci */ 38362306a36Sopenharmony_cistatic void 38462306a36Sopenharmony_cinfs4_xattr_discard_cache(struct nfs4_xattr_cache *cache) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci unsigned int i; 38762306a36Sopenharmony_ci struct nfs4_xattr_entry *entry; 38862306a36Sopenharmony_ci struct nfs4_xattr_bucket *bucket; 38962306a36Sopenharmony_ci struct hlist_node *n; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci nfs4_xattr_set_listcache(cache, ERR_PTR(-ESTALE)); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci for (i = 0; i < NFS4_XATTR_HASH_SIZE; i++) { 39462306a36Sopenharmony_ci bucket = &cache->buckets[i]; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci spin_lock(&bucket->lock); 39762306a36Sopenharmony_ci bucket->draining = true; 39862306a36Sopenharmony_ci hlist_for_each_entry_safe(entry, n, &bucket->hlist, hnode) { 39962306a36Sopenharmony_ci nfs4_xattr_entry_lru_del(entry); 40062306a36Sopenharmony_ci hlist_del_init(&entry->hnode); 40162306a36Sopenharmony_ci kref_put(&entry->ref, nfs4_xattr_free_entry_cb); 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci spin_unlock(&bucket->lock); 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci atomic_long_set(&cache->nent, 0); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci kref_put(&cache->ref, nfs4_xattr_free_cache_cb); 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci/* 41262306a36Sopenharmony_ci * Get a referenced copy of the cache structure. Avoid doing allocs 41362306a36Sopenharmony_ci * while holding i_lock. Which means that we do some optimistic allocation, 41462306a36Sopenharmony_ci * and might have to free the result in rare cases. 41562306a36Sopenharmony_ci * 41662306a36Sopenharmony_ci * This function only checks the NFS_INO_INVALID_XATTR cache validity bit 41762306a36Sopenharmony_ci * and acts accordingly, replacing the cache when needed. For the read case 41862306a36Sopenharmony_ci * (!add), this means that the caller must make sure that the cache 41962306a36Sopenharmony_ci * is valid before caling this function. getxattr and listxattr call 42062306a36Sopenharmony_ci * revalidate_inode to do this. The attribute cache timeout (for the 42162306a36Sopenharmony_ci * non-delegated case) is expected to be dealt with in the revalidate 42262306a36Sopenharmony_ci * call. 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic struct nfs4_xattr_cache * 42662306a36Sopenharmony_cinfs4_xattr_get_cache(struct inode *inode, int add) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct nfs_inode *nfsi; 42962306a36Sopenharmony_ci struct nfs4_xattr_cache *cache, *oldcache, *newcache; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci nfsi = NFS_I(inode); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci cache = oldcache = NULL; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci spin_lock(&inode->i_lock); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (nfsi->cache_validity & NFS_INO_INVALID_XATTR) 43862306a36Sopenharmony_ci oldcache = nfs4_xattr_cache_unlink(inode); 43962306a36Sopenharmony_ci else 44062306a36Sopenharmony_ci cache = nfsi->xattr_cache; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (cache != NULL) 44362306a36Sopenharmony_ci kref_get(&cache->ref); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci spin_unlock(&inode->i_lock); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (add && cache == NULL) { 44862306a36Sopenharmony_ci newcache = NULL; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci cache = nfs4_xattr_alloc_cache(); 45162306a36Sopenharmony_ci if (cache == NULL) 45262306a36Sopenharmony_ci goto out; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci spin_lock(&inode->i_lock); 45562306a36Sopenharmony_ci if (nfsi->cache_validity & NFS_INO_INVALID_XATTR) { 45662306a36Sopenharmony_ci /* 45762306a36Sopenharmony_ci * The cache was invalidated again. Give up, 45862306a36Sopenharmony_ci * since what we want to enter is now likely 45962306a36Sopenharmony_ci * outdated anyway. 46062306a36Sopenharmony_ci */ 46162306a36Sopenharmony_ci spin_unlock(&inode->i_lock); 46262306a36Sopenharmony_ci kref_put(&cache->ref, nfs4_xattr_free_cache_cb); 46362306a36Sopenharmony_ci cache = NULL; 46462306a36Sopenharmony_ci goto out; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* 46862306a36Sopenharmony_ci * Check if someone beat us to it. 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_ci if (nfsi->xattr_cache != NULL) { 47162306a36Sopenharmony_ci newcache = nfsi->xattr_cache; 47262306a36Sopenharmony_ci kref_get(&newcache->ref); 47362306a36Sopenharmony_ci } else { 47462306a36Sopenharmony_ci kref_get(&cache->ref); 47562306a36Sopenharmony_ci nfsi->xattr_cache = cache; 47662306a36Sopenharmony_ci cache->inode = inode; 47762306a36Sopenharmony_ci list_lru_add(&nfs4_xattr_cache_lru, &cache->lru); 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci spin_unlock(&inode->i_lock); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* 48362306a36Sopenharmony_ci * If there was a race, throw away the cache we just 48462306a36Sopenharmony_ci * allocated, and use the new one allocated by someone 48562306a36Sopenharmony_ci * else. 48662306a36Sopenharmony_ci */ 48762306a36Sopenharmony_ci if (newcache != NULL) { 48862306a36Sopenharmony_ci kref_put(&cache->ref, nfs4_xattr_free_cache_cb); 48962306a36Sopenharmony_ci cache = newcache; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ciout: 49462306a36Sopenharmony_ci /* 49562306a36Sopenharmony_ci * Discard the now orphaned old cache. 49662306a36Sopenharmony_ci */ 49762306a36Sopenharmony_ci if (oldcache != NULL) 49862306a36Sopenharmony_ci nfs4_xattr_discard_cache(oldcache); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci return cache; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic inline struct nfs4_xattr_bucket * 50462306a36Sopenharmony_cinfs4_xattr_hash_bucket(struct nfs4_xattr_cache *cache, const char *name) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci return &cache->buckets[jhash(name, strlen(name), 0) & 50762306a36Sopenharmony_ci (ARRAY_SIZE(cache->buckets) - 1)]; 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic struct nfs4_xattr_entry * 51162306a36Sopenharmony_cinfs4_xattr_get_entry(struct nfs4_xattr_bucket *bucket, const char *name) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct nfs4_xattr_entry *entry; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci entry = NULL; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci hlist_for_each_entry(entry, &bucket->hlist, hnode) { 51862306a36Sopenharmony_ci if (!strcmp(entry->xattr_name, name)) 51962306a36Sopenharmony_ci break; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci return entry; 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic int 52662306a36Sopenharmony_cinfs4_xattr_hash_add(struct nfs4_xattr_cache *cache, 52762306a36Sopenharmony_ci struct nfs4_xattr_entry *entry) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci struct nfs4_xattr_bucket *bucket; 53062306a36Sopenharmony_ci struct nfs4_xattr_entry *oldentry = NULL; 53162306a36Sopenharmony_ci int ret = 1; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci bucket = nfs4_xattr_hash_bucket(cache, entry->xattr_name); 53462306a36Sopenharmony_ci entry->bucket = bucket; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci spin_lock(&bucket->lock); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (bucket->draining) { 53962306a36Sopenharmony_ci ret = 0; 54062306a36Sopenharmony_ci goto out; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci oldentry = nfs4_xattr_get_entry(bucket, entry->xattr_name); 54462306a36Sopenharmony_ci if (oldentry != NULL) { 54562306a36Sopenharmony_ci hlist_del_init(&oldentry->hnode); 54662306a36Sopenharmony_ci nfs4_xattr_entry_lru_del(oldentry); 54762306a36Sopenharmony_ci } else { 54862306a36Sopenharmony_ci atomic_long_inc(&cache->nent); 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci hlist_add_head(&entry->hnode, &bucket->hlist); 55262306a36Sopenharmony_ci nfs4_xattr_entry_lru_add(entry); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ciout: 55562306a36Sopenharmony_ci spin_unlock(&bucket->lock); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci if (oldentry != NULL) 55862306a36Sopenharmony_ci kref_put(&oldentry->ref, nfs4_xattr_free_entry_cb); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci return ret; 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistatic void 56462306a36Sopenharmony_cinfs4_xattr_hash_remove(struct nfs4_xattr_cache *cache, const char *name) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci struct nfs4_xattr_bucket *bucket; 56762306a36Sopenharmony_ci struct nfs4_xattr_entry *entry; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci bucket = nfs4_xattr_hash_bucket(cache, name); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci spin_lock(&bucket->lock); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci entry = nfs4_xattr_get_entry(bucket, name); 57462306a36Sopenharmony_ci if (entry != NULL) { 57562306a36Sopenharmony_ci hlist_del_init(&entry->hnode); 57662306a36Sopenharmony_ci nfs4_xattr_entry_lru_del(entry); 57762306a36Sopenharmony_ci atomic_long_dec(&cache->nent); 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci spin_unlock(&bucket->lock); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if (entry != NULL) 58362306a36Sopenharmony_ci kref_put(&entry->ref, nfs4_xattr_free_entry_cb); 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_cistatic struct nfs4_xattr_entry * 58762306a36Sopenharmony_cinfs4_xattr_hash_find(struct nfs4_xattr_cache *cache, const char *name) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci struct nfs4_xattr_bucket *bucket; 59062306a36Sopenharmony_ci struct nfs4_xattr_entry *entry; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci bucket = nfs4_xattr_hash_bucket(cache, name); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci spin_lock(&bucket->lock); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci entry = nfs4_xattr_get_entry(bucket, name); 59762306a36Sopenharmony_ci if (entry != NULL) 59862306a36Sopenharmony_ci kref_get(&entry->ref); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci spin_unlock(&bucket->lock); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci return entry; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci/* 60662306a36Sopenharmony_ci * Entry point to retrieve an entry from the cache. 60762306a36Sopenharmony_ci */ 60862306a36Sopenharmony_cissize_t nfs4_xattr_cache_get(struct inode *inode, const char *name, char *buf, 60962306a36Sopenharmony_ci ssize_t buflen) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci struct nfs4_xattr_cache *cache; 61262306a36Sopenharmony_ci struct nfs4_xattr_entry *entry; 61362306a36Sopenharmony_ci ssize_t ret; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci cache = nfs4_xattr_get_cache(inode, 0); 61662306a36Sopenharmony_ci if (cache == NULL) 61762306a36Sopenharmony_ci return -ENOENT; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci ret = 0; 62062306a36Sopenharmony_ci entry = nfs4_xattr_hash_find(cache, name); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (entry != NULL) { 62362306a36Sopenharmony_ci dprintk("%s: cache hit '%s', len %lu\n", __func__, 62462306a36Sopenharmony_ci entry->xattr_name, (unsigned long)entry->xattr_size); 62562306a36Sopenharmony_ci if (buflen == 0) { 62662306a36Sopenharmony_ci /* Length probe only */ 62762306a36Sopenharmony_ci ret = entry->xattr_size; 62862306a36Sopenharmony_ci } else if (buflen < entry->xattr_size) 62962306a36Sopenharmony_ci ret = -ERANGE; 63062306a36Sopenharmony_ci else { 63162306a36Sopenharmony_ci memcpy(buf, entry->xattr_value, entry->xattr_size); 63262306a36Sopenharmony_ci ret = entry->xattr_size; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci kref_put(&entry->ref, nfs4_xattr_free_entry_cb); 63562306a36Sopenharmony_ci } else { 63662306a36Sopenharmony_ci dprintk("%s: cache miss '%s'\n", __func__, name); 63762306a36Sopenharmony_ci ret = -ENOENT; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci kref_put(&cache->ref, nfs4_xattr_free_cache_cb); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci return ret; 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci/* 64662306a36Sopenharmony_ci * Retrieve a cached list of xattrs from the cache. 64762306a36Sopenharmony_ci */ 64862306a36Sopenharmony_cissize_t nfs4_xattr_cache_list(struct inode *inode, char *buf, ssize_t buflen) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci struct nfs4_xattr_cache *cache; 65162306a36Sopenharmony_ci struct nfs4_xattr_entry *entry; 65262306a36Sopenharmony_ci ssize_t ret; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci cache = nfs4_xattr_get_cache(inode, 0); 65562306a36Sopenharmony_ci if (cache == NULL) 65662306a36Sopenharmony_ci return -ENOENT; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci spin_lock(&cache->listxattr_lock); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci entry = cache->listxattr; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (entry != NULL && entry != ERR_PTR(-ESTALE)) { 66362306a36Sopenharmony_ci if (buflen == 0) { 66462306a36Sopenharmony_ci /* Length probe only */ 66562306a36Sopenharmony_ci ret = entry->xattr_size; 66662306a36Sopenharmony_ci } else if (entry->xattr_size > buflen) 66762306a36Sopenharmony_ci ret = -ERANGE; 66862306a36Sopenharmony_ci else { 66962306a36Sopenharmony_ci memcpy(buf, entry->xattr_value, entry->xattr_size); 67062306a36Sopenharmony_ci ret = entry->xattr_size; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci } else { 67362306a36Sopenharmony_ci ret = -ENOENT; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci spin_unlock(&cache->listxattr_lock); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci kref_put(&cache->ref, nfs4_xattr_free_cache_cb); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci return ret; 68162306a36Sopenharmony_ci} 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci/* 68462306a36Sopenharmony_ci * Add an xattr to the cache. 68562306a36Sopenharmony_ci * 68662306a36Sopenharmony_ci * This also invalidates the xattr list cache. 68762306a36Sopenharmony_ci */ 68862306a36Sopenharmony_civoid nfs4_xattr_cache_add(struct inode *inode, const char *name, 68962306a36Sopenharmony_ci const char *buf, struct page **pages, ssize_t buflen) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci struct nfs4_xattr_cache *cache; 69262306a36Sopenharmony_ci struct nfs4_xattr_entry *entry; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci dprintk("%s: add '%s' len %lu\n", __func__, 69562306a36Sopenharmony_ci name, (unsigned long)buflen); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci cache = nfs4_xattr_get_cache(inode, 1); 69862306a36Sopenharmony_ci if (cache == NULL) 69962306a36Sopenharmony_ci return; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci entry = nfs4_xattr_alloc_entry(name, buf, pages, buflen); 70262306a36Sopenharmony_ci if (entry == NULL) 70362306a36Sopenharmony_ci goto out; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci (void)nfs4_xattr_set_listcache(cache, NULL); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci if (!nfs4_xattr_hash_add(cache, entry)) 70862306a36Sopenharmony_ci kref_put(&entry->ref, nfs4_xattr_free_entry_cb); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ciout: 71162306a36Sopenharmony_ci kref_put(&cache->ref, nfs4_xattr_free_cache_cb); 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci/* 71662306a36Sopenharmony_ci * Remove an xattr from the cache. 71762306a36Sopenharmony_ci * 71862306a36Sopenharmony_ci * This also invalidates the xattr list cache. 71962306a36Sopenharmony_ci */ 72062306a36Sopenharmony_civoid nfs4_xattr_cache_remove(struct inode *inode, const char *name) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci struct nfs4_xattr_cache *cache; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci dprintk("%s: remove '%s'\n", __func__, name); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci cache = nfs4_xattr_get_cache(inode, 0); 72762306a36Sopenharmony_ci if (cache == NULL) 72862306a36Sopenharmony_ci return; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci (void)nfs4_xattr_set_listcache(cache, NULL); 73162306a36Sopenharmony_ci nfs4_xattr_hash_remove(cache, name); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci kref_put(&cache->ref, nfs4_xattr_free_cache_cb); 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci/* 73762306a36Sopenharmony_ci * Cache listxattr output, replacing any possible old one. 73862306a36Sopenharmony_ci */ 73962306a36Sopenharmony_civoid nfs4_xattr_cache_set_list(struct inode *inode, const char *buf, 74062306a36Sopenharmony_ci ssize_t buflen) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci struct nfs4_xattr_cache *cache; 74362306a36Sopenharmony_ci struct nfs4_xattr_entry *entry; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci cache = nfs4_xattr_get_cache(inode, 1); 74662306a36Sopenharmony_ci if (cache == NULL) 74762306a36Sopenharmony_ci return; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci entry = nfs4_xattr_alloc_entry(NULL, buf, NULL, buflen); 75062306a36Sopenharmony_ci if (entry == NULL) 75162306a36Sopenharmony_ci goto out; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci /* 75462306a36Sopenharmony_ci * This is just there to be able to get to bucket->cache, 75562306a36Sopenharmony_ci * which is obviously the same for all buckets, so just 75662306a36Sopenharmony_ci * use bucket 0. 75762306a36Sopenharmony_ci */ 75862306a36Sopenharmony_ci entry->bucket = &cache->buckets[0]; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci if (!nfs4_xattr_set_listcache(cache, entry)) 76162306a36Sopenharmony_ci kref_put(&entry->ref, nfs4_xattr_free_entry_cb); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ciout: 76462306a36Sopenharmony_ci kref_put(&cache->ref, nfs4_xattr_free_cache_cb); 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci/* 76862306a36Sopenharmony_ci * Zap the entire cache. Called when an inode is evicted. 76962306a36Sopenharmony_ci */ 77062306a36Sopenharmony_civoid nfs4_xattr_cache_zap(struct inode *inode) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci struct nfs4_xattr_cache *oldcache; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci spin_lock(&inode->i_lock); 77562306a36Sopenharmony_ci oldcache = nfs4_xattr_cache_unlink(inode); 77662306a36Sopenharmony_ci spin_unlock(&inode->i_lock); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci if (oldcache) 77962306a36Sopenharmony_ci nfs4_xattr_discard_cache(oldcache); 78062306a36Sopenharmony_ci} 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci/* 78362306a36Sopenharmony_ci * The entry LRU is shrunk more aggressively than the cache LRU, 78462306a36Sopenharmony_ci * by settings @seeks to 1. 78562306a36Sopenharmony_ci * 78662306a36Sopenharmony_ci * Cache structures are freed only when they've become empty, after 78762306a36Sopenharmony_ci * pruning all but one entry. 78862306a36Sopenharmony_ci */ 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_cistatic unsigned long nfs4_xattr_cache_count(struct shrinker *shrink, 79162306a36Sopenharmony_ci struct shrink_control *sc); 79262306a36Sopenharmony_cistatic unsigned long nfs4_xattr_entry_count(struct shrinker *shrink, 79362306a36Sopenharmony_ci struct shrink_control *sc); 79462306a36Sopenharmony_cistatic unsigned long nfs4_xattr_cache_scan(struct shrinker *shrink, 79562306a36Sopenharmony_ci struct shrink_control *sc); 79662306a36Sopenharmony_cistatic unsigned long nfs4_xattr_entry_scan(struct shrinker *shrink, 79762306a36Sopenharmony_ci struct shrink_control *sc); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic struct shrinker nfs4_xattr_cache_shrinker = { 80062306a36Sopenharmony_ci .count_objects = nfs4_xattr_cache_count, 80162306a36Sopenharmony_ci .scan_objects = nfs4_xattr_cache_scan, 80262306a36Sopenharmony_ci .seeks = DEFAULT_SEEKS, 80362306a36Sopenharmony_ci .flags = SHRINKER_MEMCG_AWARE, 80462306a36Sopenharmony_ci}; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cistatic struct shrinker nfs4_xattr_entry_shrinker = { 80762306a36Sopenharmony_ci .count_objects = nfs4_xattr_entry_count, 80862306a36Sopenharmony_ci .scan_objects = nfs4_xattr_entry_scan, 80962306a36Sopenharmony_ci .seeks = DEFAULT_SEEKS, 81062306a36Sopenharmony_ci .batch = 512, 81162306a36Sopenharmony_ci .flags = SHRINKER_MEMCG_AWARE, 81262306a36Sopenharmony_ci}; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_cistatic struct shrinker nfs4_xattr_large_entry_shrinker = { 81562306a36Sopenharmony_ci .count_objects = nfs4_xattr_entry_count, 81662306a36Sopenharmony_ci .scan_objects = nfs4_xattr_entry_scan, 81762306a36Sopenharmony_ci .seeks = 1, 81862306a36Sopenharmony_ci .batch = 512, 81962306a36Sopenharmony_ci .flags = SHRINKER_MEMCG_AWARE, 82062306a36Sopenharmony_ci}; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_cistatic enum lru_status 82362306a36Sopenharmony_cicache_lru_isolate(struct list_head *item, 82462306a36Sopenharmony_ci struct list_lru_one *lru, spinlock_t *lru_lock, void *arg) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci struct list_head *dispose = arg; 82762306a36Sopenharmony_ci struct inode *inode; 82862306a36Sopenharmony_ci struct nfs4_xattr_cache *cache = container_of(item, 82962306a36Sopenharmony_ci struct nfs4_xattr_cache, lru); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci if (atomic_long_read(&cache->nent) > 1) 83262306a36Sopenharmony_ci return LRU_SKIP; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci /* 83562306a36Sopenharmony_ci * If a cache structure is on the LRU list, we know that 83662306a36Sopenharmony_ci * its inode is valid. Try to lock it to break the link. 83762306a36Sopenharmony_ci * Since we're inverting the lock order here, only try. 83862306a36Sopenharmony_ci */ 83962306a36Sopenharmony_ci inode = cache->inode; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci if (!spin_trylock(&inode->i_lock)) 84262306a36Sopenharmony_ci return LRU_SKIP; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci kref_get(&cache->ref); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci cache->inode = NULL; 84762306a36Sopenharmony_ci NFS_I(inode)->xattr_cache = NULL; 84862306a36Sopenharmony_ci NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_XATTR; 84962306a36Sopenharmony_ci list_lru_isolate(lru, &cache->lru); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci spin_unlock(&inode->i_lock); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci list_add_tail(&cache->dispose, dispose); 85462306a36Sopenharmony_ci return LRU_REMOVED; 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_cistatic unsigned long 85862306a36Sopenharmony_cinfs4_xattr_cache_scan(struct shrinker *shrink, struct shrink_control *sc) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci LIST_HEAD(dispose); 86162306a36Sopenharmony_ci unsigned long freed; 86262306a36Sopenharmony_ci struct nfs4_xattr_cache *cache; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci freed = list_lru_shrink_walk(&nfs4_xattr_cache_lru, sc, 86562306a36Sopenharmony_ci cache_lru_isolate, &dispose); 86662306a36Sopenharmony_ci while (!list_empty(&dispose)) { 86762306a36Sopenharmony_ci cache = list_first_entry(&dispose, struct nfs4_xattr_cache, 86862306a36Sopenharmony_ci dispose); 86962306a36Sopenharmony_ci list_del_init(&cache->dispose); 87062306a36Sopenharmony_ci nfs4_xattr_discard_cache(cache); 87162306a36Sopenharmony_ci kref_put(&cache->ref, nfs4_xattr_free_cache_cb); 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci return freed; 87562306a36Sopenharmony_ci} 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_cistatic unsigned long 87962306a36Sopenharmony_cinfs4_xattr_cache_count(struct shrinker *shrink, struct shrink_control *sc) 88062306a36Sopenharmony_ci{ 88162306a36Sopenharmony_ci unsigned long count; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci count = list_lru_shrink_count(&nfs4_xattr_cache_lru, sc); 88462306a36Sopenharmony_ci return vfs_pressure_ratio(count); 88562306a36Sopenharmony_ci} 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_cistatic enum lru_status 88862306a36Sopenharmony_cientry_lru_isolate(struct list_head *item, 88962306a36Sopenharmony_ci struct list_lru_one *lru, spinlock_t *lru_lock, void *arg) 89062306a36Sopenharmony_ci{ 89162306a36Sopenharmony_ci struct list_head *dispose = arg; 89262306a36Sopenharmony_ci struct nfs4_xattr_bucket *bucket; 89362306a36Sopenharmony_ci struct nfs4_xattr_cache *cache; 89462306a36Sopenharmony_ci struct nfs4_xattr_entry *entry = container_of(item, 89562306a36Sopenharmony_ci struct nfs4_xattr_entry, lru); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci bucket = entry->bucket; 89862306a36Sopenharmony_ci cache = bucket->cache; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci /* 90162306a36Sopenharmony_ci * Unhook the entry from its parent (either a cache bucket 90262306a36Sopenharmony_ci * or a cache structure if it's a listxattr buf), so that 90362306a36Sopenharmony_ci * it's no longer found. Then add it to the isolate list, 90462306a36Sopenharmony_ci * to be freed later. 90562306a36Sopenharmony_ci * 90662306a36Sopenharmony_ci * In both cases, we're reverting lock order, so use 90762306a36Sopenharmony_ci * trylock and skip the entry if we can't get the lock. 90862306a36Sopenharmony_ci */ 90962306a36Sopenharmony_ci if (entry->xattr_name != NULL) { 91062306a36Sopenharmony_ci /* Regular cache entry */ 91162306a36Sopenharmony_ci if (!spin_trylock(&bucket->lock)) 91262306a36Sopenharmony_ci return LRU_SKIP; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci kref_get(&entry->ref); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci hlist_del_init(&entry->hnode); 91762306a36Sopenharmony_ci atomic_long_dec(&cache->nent); 91862306a36Sopenharmony_ci list_lru_isolate(lru, &entry->lru); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci spin_unlock(&bucket->lock); 92162306a36Sopenharmony_ci } else { 92262306a36Sopenharmony_ci /* Listxattr cache entry */ 92362306a36Sopenharmony_ci if (!spin_trylock(&cache->listxattr_lock)) 92462306a36Sopenharmony_ci return LRU_SKIP; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci kref_get(&entry->ref); 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci cache->listxattr = NULL; 92962306a36Sopenharmony_ci list_lru_isolate(lru, &entry->lru); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci spin_unlock(&cache->listxattr_lock); 93262306a36Sopenharmony_ci } 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci list_add_tail(&entry->dispose, dispose); 93562306a36Sopenharmony_ci return LRU_REMOVED; 93662306a36Sopenharmony_ci} 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_cistatic unsigned long 93962306a36Sopenharmony_cinfs4_xattr_entry_scan(struct shrinker *shrink, struct shrink_control *sc) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci LIST_HEAD(dispose); 94262306a36Sopenharmony_ci unsigned long freed; 94362306a36Sopenharmony_ci struct nfs4_xattr_entry *entry; 94462306a36Sopenharmony_ci struct list_lru *lru; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci lru = (shrink == &nfs4_xattr_large_entry_shrinker) ? 94762306a36Sopenharmony_ci &nfs4_xattr_large_entry_lru : &nfs4_xattr_entry_lru; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci freed = list_lru_shrink_walk(lru, sc, entry_lru_isolate, &dispose); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci while (!list_empty(&dispose)) { 95262306a36Sopenharmony_ci entry = list_first_entry(&dispose, struct nfs4_xattr_entry, 95362306a36Sopenharmony_ci dispose); 95462306a36Sopenharmony_ci list_del_init(&entry->dispose); 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci /* 95762306a36Sopenharmony_ci * Drop two references: the one that we just grabbed 95862306a36Sopenharmony_ci * in entry_lru_isolate, and the one that was set 95962306a36Sopenharmony_ci * when the entry was first allocated. 96062306a36Sopenharmony_ci */ 96162306a36Sopenharmony_ci kref_put(&entry->ref, nfs4_xattr_free_entry_cb); 96262306a36Sopenharmony_ci kref_put(&entry->ref, nfs4_xattr_free_entry_cb); 96362306a36Sopenharmony_ci } 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci return freed; 96662306a36Sopenharmony_ci} 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_cistatic unsigned long 96962306a36Sopenharmony_cinfs4_xattr_entry_count(struct shrinker *shrink, struct shrink_control *sc) 97062306a36Sopenharmony_ci{ 97162306a36Sopenharmony_ci unsigned long count; 97262306a36Sopenharmony_ci struct list_lru *lru; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci lru = (shrink == &nfs4_xattr_large_entry_shrinker) ? 97562306a36Sopenharmony_ci &nfs4_xattr_large_entry_lru : &nfs4_xattr_entry_lru; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci count = list_lru_shrink_count(lru, sc); 97862306a36Sopenharmony_ci return vfs_pressure_ratio(count); 97962306a36Sopenharmony_ci} 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_cistatic void nfs4_xattr_cache_init_once(void *p) 98362306a36Sopenharmony_ci{ 98462306a36Sopenharmony_ci struct nfs4_xattr_cache *cache = p; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci spin_lock_init(&cache->listxattr_lock); 98762306a36Sopenharmony_ci atomic_long_set(&cache->nent, 0); 98862306a36Sopenharmony_ci nfs4_xattr_hash_init(cache); 98962306a36Sopenharmony_ci cache->listxattr = NULL; 99062306a36Sopenharmony_ci INIT_LIST_HEAD(&cache->lru); 99162306a36Sopenharmony_ci INIT_LIST_HEAD(&cache->dispose); 99262306a36Sopenharmony_ci} 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_cistatic int nfs4_xattr_shrinker_init(struct shrinker *shrinker, 99562306a36Sopenharmony_ci struct list_lru *lru, const char *name) 99662306a36Sopenharmony_ci{ 99762306a36Sopenharmony_ci int ret = 0; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci ret = register_shrinker(shrinker, name); 100062306a36Sopenharmony_ci if (ret) 100162306a36Sopenharmony_ci return ret; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci ret = list_lru_init_memcg(lru, shrinker); 100462306a36Sopenharmony_ci if (ret) 100562306a36Sopenharmony_ci unregister_shrinker(shrinker); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci return ret; 100862306a36Sopenharmony_ci} 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_cistatic void nfs4_xattr_shrinker_destroy(struct shrinker *shrinker, 101162306a36Sopenharmony_ci struct list_lru *lru) 101262306a36Sopenharmony_ci{ 101362306a36Sopenharmony_ci unregister_shrinker(shrinker); 101462306a36Sopenharmony_ci list_lru_destroy(lru); 101562306a36Sopenharmony_ci} 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ciint __init nfs4_xattr_cache_init(void) 101862306a36Sopenharmony_ci{ 101962306a36Sopenharmony_ci int ret = 0; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci nfs4_xattr_cache_cachep = kmem_cache_create("nfs4_xattr_cache_cache", 102262306a36Sopenharmony_ci sizeof(struct nfs4_xattr_cache), 0, 102362306a36Sopenharmony_ci (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD), 102462306a36Sopenharmony_ci nfs4_xattr_cache_init_once); 102562306a36Sopenharmony_ci if (nfs4_xattr_cache_cachep == NULL) 102662306a36Sopenharmony_ci return -ENOMEM; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci ret = nfs4_xattr_shrinker_init(&nfs4_xattr_cache_shrinker, 102962306a36Sopenharmony_ci &nfs4_xattr_cache_lru, 103062306a36Sopenharmony_ci "nfs-xattr_cache"); 103162306a36Sopenharmony_ci if (ret) 103262306a36Sopenharmony_ci goto out1; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci ret = nfs4_xattr_shrinker_init(&nfs4_xattr_entry_shrinker, 103562306a36Sopenharmony_ci &nfs4_xattr_entry_lru, 103662306a36Sopenharmony_ci "nfs-xattr_entry"); 103762306a36Sopenharmony_ci if (ret) 103862306a36Sopenharmony_ci goto out2; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci ret = nfs4_xattr_shrinker_init(&nfs4_xattr_large_entry_shrinker, 104162306a36Sopenharmony_ci &nfs4_xattr_large_entry_lru, 104262306a36Sopenharmony_ci "nfs-xattr_large_entry"); 104362306a36Sopenharmony_ci if (!ret) 104462306a36Sopenharmony_ci return 0; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci nfs4_xattr_shrinker_destroy(&nfs4_xattr_entry_shrinker, 104762306a36Sopenharmony_ci &nfs4_xattr_entry_lru); 104862306a36Sopenharmony_ciout2: 104962306a36Sopenharmony_ci nfs4_xattr_shrinker_destroy(&nfs4_xattr_cache_shrinker, 105062306a36Sopenharmony_ci &nfs4_xattr_cache_lru); 105162306a36Sopenharmony_ciout1: 105262306a36Sopenharmony_ci kmem_cache_destroy(nfs4_xattr_cache_cachep); 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci return ret; 105562306a36Sopenharmony_ci} 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_civoid nfs4_xattr_cache_exit(void) 105862306a36Sopenharmony_ci{ 105962306a36Sopenharmony_ci nfs4_xattr_shrinker_destroy(&nfs4_xattr_large_entry_shrinker, 106062306a36Sopenharmony_ci &nfs4_xattr_large_entry_lru); 106162306a36Sopenharmony_ci nfs4_xattr_shrinker_destroy(&nfs4_xattr_entry_shrinker, 106262306a36Sopenharmony_ci &nfs4_xattr_entry_lru); 106362306a36Sopenharmony_ci nfs4_xattr_shrinker_destroy(&nfs4_xattr_cache_shrinker, 106462306a36Sopenharmony_ci &nfs4_xattr_cache_lru); 106562306a36Sopenharmony_ci kmem_cache_destroy(nfs4_xattr_cache_cachep); 106662306a36Sopenharmony_ci} 1067