// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2019, 2020 Amazon.com, Inc. or its affiliates. All rights reserved.
*
* User extended attribute client side cache functions.
*
* Author: Frank van der Linden <fllinden@amazon.com>
*/
#include <linux/errno.h>
#include <linux/nfs_fs.h>
#include <linux/hashtable.h>
#include <linux/refcount.h>
#include <uapi/linux/xattr.h>
#include "nfs4_fs.h"
#include "internal.h"
/*
* User extended attributes client side caching is implemented by having
* a cache structure attached to NFS inodes. This structure is allocated
* when needed, and freed when the cache is zapped.
*
* The cache structure contains as hash table of entries, and a pointer
* to a special-cased entry for the listxattr cache.
*
* Accessing and allocating / freeing the caches is done via reference
* counting. The cache entries use a similar refcounting scheme.
*
* This makes freeing a cache, both from the shrinker and from the
* zap cache path, easy. It also means that, in current use cases,
* the large majority of inodes will not waste any memory, as they
* will never have any user extended attributes assigned to them.
*
* Attribute entries are hashed in to a simple hash table. They are
* also part of an LRU.
*
* There are three shrinkers.
*
* Two shrinkers deal with the cache entries themselves: one for
* large entries (> PAGE_SIZE), and one for smaller entries. The
* shrinker for the larger entries works more aggressively than
* those for the smaller entries.
*
* The other shrinker frees the cache structures themselves.
*/
/*
* 64 buckets is a good default. There is likely no reasonable
* workload that uses more than even 64 user extended attributes.
* You can certainly add a lot more - but you get what you ask for
* in those circumstances.
*/
#define NFS4_XATTR_HASH_SIZE 64
#define NFSDBG_FACILITY NFSDBG_XATTRCACHE
struct nfs4_xattr_cache;
struct nfs4_xattr_entry;
struct nfs4_xattr_bucket {
spinlock_t lock;
struct hlist_head hlist;
struct nfs4_xattr_cache *cache;
bool draining;
};
struct nfs4_xattr_cache {
struct kref ref;
struct nfs4_xattr_bucket buckets[NFS4_XATTR_HASH_SIZE];
struct list_head lru;
struct list_head dispose;
atomic_long_t nent;
spinlock_t listxattr_lock;
struct inode *inode;
struct nfs4_xattr_entry *listxattr;
};
struct nfs4_xattr_entry {
struct kref ref;
struct hlist_node hnode;
struct list_head lru;
struct list_head dispose;
char *xattr_name;
void *xattr_value;
size_t xattr_size;
struct nfs4_xattr_bucket *bucket;
uint32_t flags;
};
#define NFS4_XATTR_ENTRY_EXTVAL 0x0001
/*
* LRU list of NFS inodes that have xattr caches.
*/
static struct list_lru nfs4_xattr_cache_lru;
static struct list_lru nfs4_xattr_entry_lru;
static struct list_lru nfs4_xattr_large_entry_lru;
static struct kmem_cache *nfs4_xattr_cache_cachep;
/*
* Hashing helper functions.
*/
static void
nfs4_xattr_hash_init(struct nfs4_xattr_cache *cache)
{
unsigned int i;
for (i = 0; i < NFS4_XATTR_HASH_SIZE; i++) {
INIT_HLIST_HEAD(&cache->buckets[i].hlist);
spin_lock_init(&cache->buckets[i].lock);
cache->buckets[i].cache = cache;
cache->buckets[i].draining = false;
}
}
/*
* Locking order:
* 1. inode i_lock or bucket lock
* 2. list_lru lock (taken by list_lru_* functions)
*/
/*
* Wrapper functions to add a cache entry to the right LRU.
*/
static bool
nfs4_xattr_entry_lru_add(struct nfs4_xattr_entry *entry)
{
struct list_lru *lru;
lru = (entry->flags & NFS4_XATTR_ENTRY_EXTVAL) ?
&nfs4_xattr_large_entry_lru : &nfs4_xattr_entry_lru;
return list_lru_add_obj(lru, &entry->lru);
}
static bool
nfs4_xattr_entry_lru_del(struct nfs4_xattr_entry *entry)
{
struct list_lru *lru;
lru = (entry->flags & NFS4_XATTR_ENTRY_EXTVAL) ?
&nfs4_xattr_large_entry_lru : &nfs4_xattr_entry_lru;
return list_lru_del_obj(lru, &entry->lru);
}
/*
* This function allocates cache entries. They are the normal
* extended attribute name/value pairs, but may also be a listxattr
* cache. Those allocations use the same entry so that they can be
* treated as one by the memory shrinker.
*
* xattr cache entries are allocated together with names. If the
* value fits in to one page with the entry structure and the name,
* it will also be part of the same allocation (kmalloc). This is
* expected to be the vast majority of cases. Larger allocations
* have a value pointer that is allocated separately by kvmalloc.
*
* Parameters:
*
* @name: Name of the extended attribute. NULL for listxattr cache
* entry.
* @value: Value of attribute, or listxattr cache. NULL if the
* value is to be copied from pages instead.
* @pages: Pages to copy the value from, if not NULL. Passed in to
* make it easier to copy the value after an RPC, even if
* the value will not be passed up to application (e.g.
* for a 'query' getxattr with NULL buffer).
* @len: Length of the value. Can be 0 for zero-length attributes.
* @value and @pages will be NULL if @len is 0.
*/
static struct nfs4_xattr_entry *
nfs4_xattr_alloc_entry(const char *name, const void *value,
struct page **pages, size_t len)
{
struct nfs4_xattr_entry *entry;
void *valp;
char *namep;
size_t alloclen, slen;
char *buf;
uint32_t flags;
BUILD_BUG_ON(sizeof(struct nfs4_xattr_entry) +
XATTR_NA