linux/net/sunrpc/cache.c

// SPDX-License-Identifier: GPL-2.0-only
/*
 * net/sunrpc/cache.c
 *
 * Generic code for various authentication-related caches
 * used by sunrpc clients and servers.
 *
 * Copyright (C) 2002 Neil Brown <[email protected]>
 */

#include <linux/types.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/slab.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/kmod.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/ctype.h>
#include <linux/string_helpers.h>
#include <linux/uaccess.h>
#include <linux/poll.h>
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
#include <linux/net.h>
#include <linux/workqueue.h>
#include <linux/mutex.h>
#include <linux/pagemap.h>
#include <asm/ioctls.h>
#include <linux/sunrpc/types.h>
#include <linux/sunrpc/cache.h>
#include <linux/sunrpc/stats.h>
#include <linux/sunrpc/rpc_pipe_fs.h>
#include <trace/events/sunrpc.h>

#include "netns.h"
#include "fail.h"

#define RPCDBG_FACILITY

static bool cache_defer_req(struct cache_req *req, struct cache_head *item);
static void cache_revisit_request(struct cache_head *item);

static void cache_init(struct cache_head *h, struct cache_detail *detail)
{}

static void cache_fresh_unlocked(struct cache_head *head,
				struct cache_detail *detail);

static struct cache_head *sunrpc_cache_find_rcu(struct cache_detail *detail,
						struct cache_head *key,
						int hash)
{}

static void sunrpc_begin_cache_remove_entry(struct cache_head *ch,
					    struct cache_detail *cd)
{}

static void sunrpc_end_cache_remove_entry(struct cache_head *ch,
					  struct cache_detail *cd)
{}

static struct cache_head *sunrpc_cache_add_entry(struct cache_detail *detail,
						 struct cache_head *key,
						 int hash)
{}

struct cache_head *sunrpc_cache_lookup_rcu(struct cache_detail *detail,
					   struct cache_head *key, int hash)
{}
EXPORT_SYMBOL_GPL();

static void cache_dequeue(struct cache_detail *detail, struct cache_head *ch);

static void cache_fresh_locked(struct cache_head *head, time64_t expiry,
			       struct cache_detail *detail)
{}

static void cache_fresh_unlocked(struct cache_head *head,
				 struct cache_detail *detail)
{}

static void cache_make_negative(struct cache_detail *detail,
				struct cache_head *h)
{}

static void cache_entry_update(struct cache_detail *detail,
			       struct cache_head *h,
			       struct cache_head *new)
{}

struct cache_head *sunrpc_cache_update(struct cache_detail *detail,
				       struct cache_head *new, struct cache_head *old, int hash)
{}
EXPORT_SYMBOL_GPL();

static inline int cache_is_valid(struct cache_head *h)
{}

static int try_to_negate_entry(struct cache_detail *detail, struct cache_head *h)
{}

/*
 * This is the generic cache management routine for all
 * the authentication caches.
 * It checks the currency of a cache item and will (later)
 * initiate an upcall to fill it if needed.
 *
 *
 * Returns 0 if the cache_head can be used, or cache_puts it and returns
 * -EAGAIN if upcall is pending and request has been queued
 * -ETIMEDOUT if upcall failed or request could not be queue or
 *           upcall completed but item is still invalid (implying that
 *           the cache item has been replaced with a newer one).
 * -ENOENT if cache entry was negative
 */
int cache_check(struct cache_detail *detail,
		    struct cache_head *h, struct cache_req *rqstp)
{}
EXPORT_SYMBOL_GPL();

/*
 * caches need to be periodically cleaned.
 * For this we maintain a list of cache_detail and
 * a current pointer into that list and into the table
 * for that entry.
 *
 * Each time cache_clean is called it finds the next non-empty entry
 * in the current table and walks the list in that entry
 * looking for entries that can be removed.
 *
 * An entry gets removed if:
 * - The expiry is before current time
 * - The last_refresh time is before the flush_time for that cache
 *
 * later we might drop old entries with non-NEVER expiry if that table
 * is getting 'full' for some definition of 'full'
 *
 * The question of "how often to scan a table" is an interesting one
 * and is answered in part by the use of the "nextcheck" field in the
 * cache_detail.
 * When a scan of a table begins, the nextcheck field is set to a time
 * that is well into the future.
 * While scanning, if an expiry time is found that is earlier than the
 * current nextcheck time, nextcheck is set to that expiry time.
 * If the flush_time is ever set to a time earlier than the nextcheck
 * time, the nextcheck time is then set to that flush_time.
 *
 * A table is then only scanned if the current time is at least
 * the nextcheck time.
 *
 */

static LIST_HEAD(cache_list);
static DEFINE_SPINLOCK(cache_list_lock);
static struct cache_detail *current_detail;
static int current_index;

static void do_cache_clean(struct work_struct *work);
static struct delayed_work cache_cleaner;

void sunrpc_init_cache_detail(struct cache_detail *cd)
{}
EXPORT_SYMBOL_GPL();

void sunrpc_destroy_cache_detail(struct cache_detail *cd)
{}
EXPORT_SYMBOL_GPL();

/* clean cache tries to find something to clean
 * and cleans it.
 * It returns 1 if it cleaned something,
 *            0 if it didn't find anything this time
 *           -1 if it fell off the end of the list.
 */
static int cache_clean(void)
{}

/*
 * We want to regularly clean the cache, so we need to schedule some work ...
 */
static void do_cache_clean(struct work_struct *work)
{}


/*
 * Clean all caches promptly.  This just calls cache_clean
 * repeatedly until we are sure that every cache has had a chance to
 * be fully cleaned
 */
void cache_flush(void)
{}
EXPORT_SYMBOL_GPL();

void cache_purge(struct cache_detail *detail)
{}
EXPORT_SYMBOL_GPL();


/*
 * Deferral and Revisiting of Requests.
 *
 * If a cache lookup finds a pending entry, we
 * need to defer the request and revisit it later.
 * All deferred requests are stored in a hash table,
 * indexed by "struct cache_head *".
 * As it may be wasteful to store a whole request
 * structure, we allow the request to provide a
 * deferred form, which must contain a
 * 'struct cache_deferred_req'
 * This cache_deferred_req contains a method to allow
 * it to be revisited when cache info is available
 */

#define DFR_HASHSIZE
#define DFR_HASH(item)

#define DFR_MAX

static DEFINE_SPINLOCK(cache_defer_lock);
static LIST_HEAD(cache_defer_list);
static struct hlist_head cache_defer_hash[DFR_HASHSIZE];
static int cache_defer_cnt;

static void __unhash_deferred_req(struct cache_deferred_req *dreq)
{}

static void __hash_deferred_req(struct cache_deferred_req *dreq, struct cache_head *item)
{}

static void setup_deferral(struct cache_deferred_req *dreq,
			   struct cache_head *item,
			   int count_me)
{}

struct thread_deferred_req {};

static void cache_restart_thread(struct cache_deferred_req *dreq, int too_many)
{}

static void cache_wait_req(struct cache_req *req, struct cache_head *item)
{}

static void cache_limit_defers(void)
{}

#if IS_ENABLED(CONFIG_FAIL_SUNRPC)
static inline bool cache_defer_immediately(void)
{}
#else
static inline bool cache_defer_immediately(void)
{
	return false;
}
#endif

/* Return true if and only if a deferred request is queued. */
static bool cache_defer_req(struct cache_req *req, struct cache_head *item)
{}

static void cache_revisit_request(struct cache_head *item)
{}

void cache_clean_deferred(void *owner)
{}

/*
 * communicate with user-space
 *
 * We have a magic /proc file - /proc/net/rpc/<cachename>/channel.
 * On read, you get a full request, or block.
 * On write, an update request is processed.
 * Poll works if anything to read, and always allows write.
 *
 * Implemented by linked list of requests.  Each open file has
 * a ->private that also exists in this list.  New requests are added
 * to the end and may wakeup and preceding readers.
 * New readers are added to the head.  If, on read, an item is found with
 * CACHE_UPCALLING clear, we free it from the list.
 *
 */

static DEFINE_SPINLOCK(queue_lock);

struct cache_queue {};
struct cache_request {};
struct cache_reader {};

static int cache_request(struct cache_detail *detail,
			       struct cache_request *crq)
{}

static ssize_t cache_read(struct file *filp, char __user *buf, size_t count,
			  loff_t *ppos, struct cache_detail *cd)
{}

static ssize_t cache_do_downcall(char *kaddr, const char __user *buf,
				 size_t count, struct cache_detail *cd)
{}

static ssize_t cache_downcall(struct address_space *mapping,
			      const char __user *buf,
			      size_t count, struct cache_detail *cd)
{}

static ssize_t cache_write(struct file *filp, const char __user *buf,
			   size_t count, loff_t *ppos,
			   struct cache_detail *cd)
{}

static DECLARE_WAIT_QUEUE_HEAD(queue_wait);

static __poll_t cache_poll(struct file *filp, poll_table *wait,
			       struct cache_detail *cd)
{}

static int cache_ioctl(struct inode *ino, struct file *filp,
		       unsigned int cmd, unsigned long arg,
		       struct cache_detail *cd)
{}

static int cache_open(struct inode *inode, struct file *filp,
		      struct cache_detail *cd)
{}

static int cache_release(struct inode *inode, struct file *filp,
			 struct cache_detail *cd)
{}



static void cache_dequeue(struct cache_detail *detail, struct cache_head *ch)
{}

/*
 * Support routines for text-based upcalls.
 * Fields are separated by spaces.
 * Fields are either mangled to quote space tab newline slosh with slosh
 * or a hexified with a leading \x
 * Record is terminated with newline.
 *
 */

void qword_add(char **bpp, int *lp, char *str)
{}
EXPORT_SYMBOL_GPL();

void qword_addhex(char **bpp, int *lp, char *buf, int blen)
{}
EXPORT_SYMBOL_GPL();

static void warn_no_listener(struct cache_detail *detail)
{}

static bool cache_listeners_exist(struct cache_detail *detail)
{}

/*
 * register an upcall request to user-space and queue it up for read() by the
 * upcall daemon.
 *
 * Each request is at most one page long.
 */
static int cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h)
{}

int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h)
{}
EXPORT_SYMBOL_GPL();

int sunrpc_cache_pipe_upcall_timeout(struct cache_detail *detail,
				     struct cache_head *h)
{}
EXPORT_SYMBOL_GPL();

/*
 * parse a message from user-space and pass it
 * to an appropriate cache
 * Messages are, like requests, separated into fields by
 * spaces and dequotes as \xHEXSTRING or embedded \nnn octal
 *
 * Message is
 *   reply cachename expiry key ... content....
 *
 * key and content are both parsed by cache
 */

int qword_get(char **bpp, char *dest, int bufsize)
{}
EXPORT_SYMBOL_GPL();


/*
 * support /proc/net/rpc/$CACHENAME/content
 * as a seqfile.
 * We call ->cache_show passing NULL for the item to
 * get a header, then pass each real item in the cache
 */

static void *__cache_seq_start(struct seq_file *m, loff_t *pos)
{}

static void *cache_seq_next(struct seq_file *m, void *p, loff_t *pos)
{}

void *cache_seq_start_rcu(struct seq_file *m, loff_t *pos)
	__acquires(RCU)
{}
EXPORT_SYMBOL_GPL();

void *cache_seq_next_rcu(struct seq_file *file, void *p, loff_t *pos)
{}
EXPORT_SYMBOL_GPL();

void cache_seq_stop_rcu(struct seq_file *m, void *p)
	__releases(RCU)
{}
EXPORT_SYMBOL_GPL();

static int c_show(struct seq_file *m, void *p)
{}

static const struct seq_operations cache_content_op =;

static int content_open(struct inode *inode, struct file *file,
			struct cache_detail *cd)
{}

static int content_release(struct inode *inode, struct file *file,
		struct cache_detail *cd)
{}

static int open_flush(struct inode *inode, struct file *file,
			struct cache_detail *cd)
{}

static int release_flush(struct inode *inode, struct file *file,
			struct cache_detail *cd)
{}

static ssize_t read_flush(struct file *file, char __user *buf,
			  size_t count, loff_t *ppos,
			  struct cache_detail *cd)
{}

static ssize_t write_flush(struct file *file, const char __user *buf,
			   size_t count, loff_t *ppos,
			   struct cache_detail *cd)
{}

static ssize_t cache_read_procfs(struct file *filp, char __user *buf,
				 size_t count, loff_t *ppos)
{}

static ssize_t cache_write_procfs(struct file *filp, const char __user *buf,
				  size_t count, loff_t *ppos)
{}

static __poll_t cache_poll_procfs(struct file *filp, poll_table *wait)
{}

static long cache_ioctl_procfs(struct file *filp,
			       unsigned int cmd, unsigned long arg)
{}

static int cache_open_procfs(struct inode *inode, struct file *filp)
{}

static int cache_release_procfs(struct inode *inode, struct file *filp)
{}

static const struct proc_ops cache_channel_proc_ops =;

static int content_open_procfs(struct inode *inode, struct file *filp)
{}

static int content_release_procfs(struct inode *inode, struct file *filp)
{}

static const struct proc_ops content_proc_ops =;

static int open_flush_procfs(struct inode *inode, struct file *filp)
{}

static int release_flush_procfs(struct inode *inode, struct file *filp)
{}

static ssize_t read_flush_procfs(struct file *filp, char __user *buf,
			    size_t count, loff_t *ppos)
{}

static ssize_t write_flush_procfs(struct file *filp,
				  const char __user *buf,
				  size_t count, loff_t *ppos)
{}

static const struct proc_ops cache_flush_proc_ops =;

static void remove_cache_proc_entries(struct cache_detail *cd)
{}

#ifdef CONFIG_PROC_FS
static int create_cache_proc_entries(struct cache_detail *cd, struct net *net)
{}
#else /* CONFIG_PROC_FS */
static int create_cache_proc_entries(struct cache_detail *cd, struct net *net)
{
	return 0;
}
#endif

void __init cache_initialize(void)
{}

int cache_register_net(struct cache_detail *cd, struct net *net)
{}
EXPORT_SYMBOL_GPL();

void cache_unregister_net(struct cache_detail *cd, struct net *net)
{}
EXPORT_SYMBOL_GPL();

struct cache_detail *cache_create_net(const struct cache_detail *tmpl, struct net *net)
{}
EXPORT_SYMBOL_GPL();

void cache_destroy_net(struct cache_detail *cd, struct net *net)
{}
EXPORT_SYMBOL_GPL();

static ssize_t cache_read_pipefs(struct file *filp, char __user *buf,
				 size_t count, loff_t *ppos)
{}

static ssize_t cache_write_pipefs(struct file *filp, const char __user *buf,
				  size_t count, loff_t *ppos)
{}

static __poll_t cache_poll_pipefs(struct file *filp, poll_table *wait)
{}

static long cache_ioctl_pipefs(struct file *filp,
			      unsigned int cmd, unsigned long arg)
{}

static int cache_open_pipefs(struct inode *inode, struct file *filp)
{}

static int cache_release_pipefs(struct inode *inode, struct file *filp)
{}

const struct file_operations cache_file_operations_pipefs =;

static int content_open_pipefs(struct inode *inode, struct file *filp)
{}

static int content_release_pipefs(struct inode *inode, struct file *filp)
{}

const struct file_operations content_file_operations_pipefs =;

static int open_flush_pipefs(struct inode *inode, struct file *filp)
{}

static int release_flush_pipefs(struct inode *inode, struct file *filp)
{}

static ssize_t read_flush_pipefs(struct file *filp, char __user *buf,
			    size_t count, loff_t *ppos)
{}

static ssize_t write_flush_pipefs(struct file *filp,
				  const char __user *buf,
				  size_t count, loff_t *ppos)
{}

const struct file_operations cache_flush_operations_pipefs =;

int sunrpc_cache_register_pipefs(struct dentry *parent,
				 const char *name, umode_t umode,
				 struct cache_detail *cd)
{}
EXPORT_SYMBOL_GPL();

void sunrpc_cache_unregister_pipefs(struct cache_detail *cd)
{}
EXPORT_SYMBOL_GPL();

void sunrpc_cache_unhash(struct cache_detail *cd, struct cache_head *h)
{}
EXPORT_SYMBOL_GPL();