xref: /openbmc/linux/include/linux/sunrpc/cache.h (revision ba4bba6c)
1ddc64d0aSThomas Gleixner /* SPDX-License-Identifier: GPL-2.0-only */
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * include/linux/sunrpc/cache.h
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Generic code for various authentication-related caches
61da177e4SLinus Torvalds  * used by sunrpc clients and servers.
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  * Copyright (C) 2002 Neil Brown <neilb@cse.unsw.edu.au>
91da177e4SLinus Torvalds  */
101da177e4SLinus Torvalds 
111da177e4SLinus Torvalds #ifndef _LINUX_SUNRPC_CACHE_H_
121da177e4SLinus Torvalds #define _LINUX_SUNRPC_CACHE_H_
131da177e4SLinus Torvalds 
1457cc7215SAlexey Dobriyan #include <linux/kref.h>
151da177e4SLinus Torvalds #include <linux/slab.h>
1660063497SArun Sharma #include <linux/atomic.h>
174c527293SAndy Shevchenko #include <linux/kstrtox.h>
181da177e4SLinus Torvalds #include <linux/proc_fs.h>
191da177e4SLinus Torvalds 
201da177e4SLinus Torvalds /*
211da177e4SLinus Torvalds  * Each cache requires:
221da177e4SLinus Torvalds  *  - A 'struct cache_detail' which contains information specific to the cache
231da177e4SLinus Torvalds  *    for common code to use.
241da177e4SLinus Torvalds  *  - An item structure that must contain a "struct cache_head"
251da177e4SLinus Torvalds  *  - A lookup function defined using DefineCacheLookup
261da177e4SLinus Torvalds  *  - A 'put' function that can release a cache item. It will only
271da177e4SLinus Torvalds  *    be called after cache_put has succeed, so there are guarantee
281da177e4SLinus Torvalds  *    to be no references.
291da177e4SLinus Torvalds  *  - A function to calculate a hash of an item's key.
301da177e4SLinus Torvalds  *
311da177e4SLinus Torvalds  * as well as assorted code fragments (e.g. compare keys) and numbers
321da177e4SLinus Torvalds  * (e.g. hash size, goal_age, etc).
331da177e4SLinus Torvalds  *
341da177e4SLinus Torvalds  * Each cache must be registered so that it can be cleaned regularly.
351da177e4SLinus Torvalds  * When the cache is unregistered, it is flushed completely.
361da177e4SLinus Torvalds  *
3725985edcSLucas De Marchi  * Entries have a ref count and a 'hashed' flag which counts the existence
381da177e4SLinus Torvalds  * in the hash table.
391da177e4SLinus Torvalds  * We only expire entries when refcount is zero.
4025985edcSLucas De Marchi  * Existence in the cache is counted  the refcount.
411da177e4SLinus Torvalds  */
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds /* Every cache item has a common header that is used
441da177e4SLinus Torvalds  * for expiring and refreshing entries.
451da177e4SLinus Torvalds  *
461da177e4SLinus Torvalds  */
471da177e4SLinus Torvalds struct cache_head {
48129e5824SKinglong Mee 	struct hlist_node	cache_list;
491138ce1cSRandy Dunlap 	time64_t	expiry_time;	/* After time expiry_time, don't use
501138ce1cSRandy Dunlap 					 * the data */
51f559935eSArnd Bergmann 	time64_t	last_refresh;   /* If CACHE_PENDING, this is when upcall was
5277862036SNeil Brown 					 * sent, else this is when update was
5377862036SNeil Brown 					 * received, though it is alway set to
5477862036SNeil Brown 					 * be *after* ->flush_time.
551da177e4SLinus Torvalds 					 */
56baab935fSNeilBrown 	struct kref	ref;
571da177e4SLinus Torvalds 	unsigned long	flags;
581da177e4SLinus Torvalds };
59*ba4bba6cSNeilBrown 
60*ba4bba6cSNeilBrown /* cache_head.flags */
61*ba4bba6cSNeilBrown enum {
62*ba4bba6cSNeilBrown 	CACHE_VALID,		/* Entry contains valid data */
63*ba4bba6cSNeilBrown 	CACHE_NEGATIVE,		/* Negative entry - there is no match for the key */
64*ba4bba6cSNeilBrown 	CACHE_PENDING,		/* An upcall has been sent but no reply received yet*/
65*ba4bba6cSNeilBrown 	CACHE_CLEANED,		/* Entry has been cleaned from cache */
66*ba4bba6cSNeilBrown };
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds #define	CACHE_NEW_EXPIRY 120	/* keep new things pending confirmation for 120 seconds */
691da177e4SLinus Torvalds 
701da177e4SLinus Torvalds struct cache_detail {
71f35279d3SBruce Allan 	struct module *		owner;
721da177e4SLinus Torvalds 	int			hash_size;
73129e5824SKinglong Mee 	struct hlist_head *	hash_table;
741863d77fSTrond Myklebust 	spinlock_t		hash_lock;
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds 	char			*name;
77baab935fSNeilBrown 	void			(*cache_put)(struct kref *);
781da177e4SLinus Torvalds 
79bc74b4f5STrond Myklebust 	int			(*cache_upcall)(struct cache_detail *,
80bc74b4f5STrond Myklebust 						struct cache_head *);
81bc74b4f5STrond Myklebust 
8273fb847aSStanislav Kinsbursky 	void			(*cache_request)(struct cache_detail *cd,
8373fb847aSStanislav Kinsbursky 						 struct cache_head *ch,
8473fb847aSStanislav Kinsbursky 						 char **bpp, int *blen);
8573fb847aSStanislav Kinsbursky 
861da177e4SLinus Torvalds 	int			(*cache_parse)(struct cache_detail *,
871da177e4SLinus Torvalds 					       char *buf, int len);
881da177e4SLinus Torvalds 
891da177e4SLinus Torvalds 	int			(*cache_show)(struct seq_file *m,
901da177e4SLinus Torvalds 					      struct cache_detail *cd,
911da177e4SLinus Torvalds 					      struct cache_head *h);
922da8ca26STrond Myklebust 	void			(*warn_no_listener)(struct cache_detail *cd,
932da8ca26STrond Myklebust 					      int has_died);
941da177e4SLinus Torvalds 
9515a5f6bdSNeilBrown 	struct cache_head *	(*alloc)(void);
96f69d6d8eSJeff Layton 	void			(*flush)(void);
9715a5f6bdSNeilBrown 	int			(*match)(struct cache_head *orig, struct cache_head *new);
9815a5f6bdSNeilBrown 	void			(*init)(struct cache_head *orig, struct cache_head *new);
9915a5f6bdSNeilBrown 	void			(*update)(struct cache_head *orig, struct cache_head *new);
10015a5f6bdSNeilBrown 
1011da177e4SLinus Torvalds 	/* fields below this comment are for internal use
1021da177e4SLinus Torvalds 	 * and should not be touched by cache owners
1031da177e4SLinus Torvalds 	 */
104f559935eSArnd Bergmann 	time64_t		flush_time;		/* flush all cache items with
10577862036SNeil Brown 							 * last_refresh at or earlier
10677862036SNeil Brown 							 * than this.  last_refresh
10777862036SNeil Brown 							 * is never set at or earlier
10877862036SNeil Brown 							 * than this.
10977862036SNeil Brown 							 */
1101da177e4SLinus Torvalds 	struct list_head	others;
111f559935eSArnd Bergmann 	time64_t		nextcheck;
1121da177e4SLinus Torvalds 	int			entries;
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds 	/* fields for communication over channel */
1151da177e4SLinus Torvalds 	struct list_head	queue;
1161da177e4SLinus Torvalds 
11764a38e84SDave Wysochanski 	atomic_t		writers;		/* how many time is /channel open */
118f559935eSArnd Bergmann 	time64_t		last_close;		/* if no writers, when did last close */
119f559935eSArnd Bergmann 	time64_t		last_warn;		/* when we last warned about no writers */
120173912a6STrond Myklebust 
121173912a6STrond Myklebust 	union {
122863d7d9cSKinglong Mee 		struct proc_dir_entry	*procfs;
123863d7d9cSKinglong Mee 		struct dentry		*pipefs;
124863d7d9cSKinglong Mee 	};
1250a402d5aSStanislav Kinsbursky 	struct net		*net;
1261da177e4SLinus Torvalds };
1271da177e4SLinus Torvalds 
1281da177e4SLinus Torvalds /* this must be embedded in any request structure that
1291da177e4SLinus Torvalds  * identifies an object that will want a callback on
1301da177e4SLinus Torvalds  * a cache fill
1311da177e4SLinus Torvalds  */
1321da177e4SLinus Torvalds struct cache_req {
1331da177e4SLinus Torvalds 	struct cache_deferred_req *(*defer)(struct cache_req *req);
1340b6c14bdSChuck Lever 	unsigned long	thread_wait;	/* How long (jiffies) we can block the
135f16b6e8dSNeilBrown 					 * current thread to wait for updates.
136f16b6e8dSNeilBrown 					 */
1371da177e4SLinus Torvalds };
1380b6c14bdSChuck Lever 
1391da177e4SLinus Torvalds /* this must be embedded in a deferred_request that is being
1401da177e4SLinus Torvalds  * delayed awaiting cache-fill
1411da177e4SLinus Torvalds  */
1421da177e4SLinus Torvalds struct cache_deferred_req {
14311174492SNeilBrown 	struct hlist_node	hash;	/* on hash chain */
1441da177e4SLinus Torvalds 	struct list_head	recent; /* on fifo */
1451da177e4SLinus Torvalds 	struct cache_head	*item;  /* cache item we wait on */
1461da177e4SLinus Torvalds 	void			*owner; /* we might need to discard all defered requests
1471da177e4SLinus Torvalds 					 * owned by someone */
1481da177e4SLinus Torvalds 	void			(*revisit)(struct cache_deferred_req *req,
1491da177e4SLinus Torvalds 					   int too_many);
1501da177e4SLinus Torvalds };
1511da177e4SLinus Torvalds 
1527715cde8SNeilBrown /*
1537715cde8SNeilBrown  * timestamps kept in the cache are expressed in seconds
1547715cde8SNeilBrown  * since boot.  This is the best for measuring differences in
1557715cde8SNeilBrown  * real time.
156f559935eSArnd Bergmann  * This reimplemnts ktime_get_boottime_seconds() in a slightly
157f559935eSArnd Bergmann  * faster but less accurate way. When we end up converting
158f559935eSArnd Bergmann  * back to wallclock (CLOCK_REALTIME), that error often
159f559935eSArnd Bergmann  * cancels out during the reverse operation.
1607715cde8SNeilBrown  */
seconds_since_boot(void)161f559935eSArnd Bergmann static inline time64_t seconds_since_boot(void)
1627715cde8SNeilBrown {
163f559935eSArnd Bergmann 	struct timespec64 boot;
164f559935eSArnd Bergmann 	getboottime64(&boot);
165f559935eSArnd Bergmann 	return ktime_get_real_seconds() - boot.tv_sec;
1667715cde8SNeilBrown }
1677715cde8SNeilBrown 
convert_to_wallclock(time64_t sinceboot)168f559935eSArnd Bergmann static inline time64_t convert_to_wallclock(time64_t sinceboot)
1697715cde8SNeilBrown {
170f559935eSArnd Bergmann 	struct timespec64 boot;
171f559935eSArnd Bergmann 	getboottime64(&boot);
1727715cde8SNeilBrown 	return boot.tv_sec + sinceboot;
1737715cde8SNeilBrown }
1747d317f2cSNeilBrown 
1758854e82dSTrond Myklebust extern const struct file_operations cache_file_operations_pipefs;
1768854e82dSTrond Myklebust extern const struct file_operations content_file_operations_pipefs;
1778854e82dSTrond Myklebust extern const struct file_operations cache_flush_operations_pipefs;
1788854e82dSTrond Myklebust 
17915a5f6bdSNeilBrown extern struct cache_head *
180ae74136bSTrond Myklebust sunrpc_cache_lookup_rcu(struct cache_detail *detail,
181ae74136bSTrond Myklebust 			struct cache_head *key, int hash);
182ae74136bSTrond Myklebust extern struct cache_head *
18315a5f6bdSNeilBrown sunrpc_cache_update(struct cache_detail *detail,
18415a5f6bdSNeilBrown 		    struct cache_head *new, struct cache_head *old, int hash);
18515a5f6bdSNeilBrown 
186bc74b4f5STrond Myklebust extern int
18721cd1254SStanislav Kinsbursky sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h);
18865286b88STrond Myklebust extern int
18965286b88STrond Myklebust sunrpc_cache_pipe_upcall_timeout(struct cache_detail *detail,
19065286b88STrond Myklebust 				 struct cache_head *h);
191bc74b4f5STrond Myklebust 
1921da177e4SLinus Torvalds 
1931da177e4SLinus Torvalds extern void cache_clean_deferred(void *owner);
1941da177e4SLinus Torvalds 
cache_get(struct cache_head * h)1951da177e4SLinus Torvalds static inline struct cache_head  *cache_get(struct cache_head *h)
1961da177e4SLinus Torvalds {
197baab935fSNeilBrown 	kref_get(&h->ref);
1981da177e4SLinus Torvalds 	return h;
1991da177e4SLinus Torvalds }
2001da177e4SLinus Torvalds 
cache_get_rcu(struct cache_head * h)201ae74136bSTrond Myklebust static inline struct cache_head  *cache_get_rcu(struct cache_head *h)
202ae74136bSTrond Myklebust {
203ae74136bSTrond Myklebust 	if (kref_get_unless_zero(&h->ref))
204ae74136bSTrond Myklebust 		return h;
205ae74136bSTrond Myklebust 	return NULL;
206ae74136bSTrond Myklebust }
2071da177e4SLinus Torvalds 
cache_put(struct cache_head * h,struct cache_detail * cd)208baab935fSNeilBrown static inline void cache_put(struct cache_head *h, struct cache_detail *cd)
2091da177e4SLinus Torvalds {
2102c935bc5SPeter Zijlstra 	if (kref_read(&h->ref) <= 2 &&
2111da177e4SLinus Torvalds 	    h->expiry_time < cd->nextcheck)
2121da177e4SLinus Torvalds 		cd->nextcheck = h->expiry_time;
213baab935fSNeilBrown 	kref_put(&h->ref, cd->cache_put);
2141da177e4SLinus Torvalds }
2151da177e4SLinus Torvalds 
cache_is_expired(struct cache_detail * detail,struct cache_head * h)216d6fc8821SKinglong Mee static inline bool cache_is_expired(struct cache_detail *detail, struct cache_head *h)
2177b2b1feeSGreg Banks {
2189a81ef42SJ. Bruce Fields 	if (h->expiry_time < seconds_since_boot())
2199a81ef42SJ. Bruce Fields 		return true;
2209a81ef42SJ. Bruce Fields 	if (!test_bit(CACHE_VALID, &h->flags))
2219a81ef42SJ. Bruce Fields 		return false;
2229a81ef42SJ. Bruce Fields 	return detail->flush_time >= h->last_refresh;
2237b2b1feeSGreg Banks }
2247b2b1feeSGreg Banks 
2251da177e4SLinus Torvalds extern int cache_check(struct cache_detail *detail,
2261da177e4SLinus Torvalds 		       struct cache_head *h, struct cache_req *rqstp);
2271da177e4SLinus Torvalds extern void cache_flush(void);
2281da177e4SLinus Torvalds extern void cache_purge(struct cache_detail *detail);
2291da177e4SLinus Torvalds #define NEVER (0x7FFFFFFF)
2308eab945cSArtem Bityutskiy extern void __init cache_initialize(void);
231593ce16bSPavel Emelyanov extern int cache_register_net(struct cache_detail *cd, struct net *net);
232593ce16bSPavel Emelyanov extern void cache_unregister_net(struct cache_detail *cd, struct net *net);
2331da177e4SLinus Torvalds 
234d34971a6SBhumika Goyal extern struct cache_detail *cache_create_net(const struct cache_detail *tmpl, struct net *net);
2350a402d5aSStanislav Kinsbursky extern void cache_destroy_net(struct cache_detail *cd, struct net *net);
2360a402d5aSStanislav Kinsbursky 
237820f9442SStanislav Kinsbursky extern void sunrpc_init_cache_detail(struct cache_detail *cd);
238820f9442SStanislav Kinsbursky extern void sunrpc_destroy_cache_detail(struct cache_detail *cd);
2398854e82dSTrond Myklebust extern int sunrpc_cache_register_pipefs(struct dentry *parent, const char *,
24064f1426fSAl Viro 					umode_t, struct cache_detail *);
2418854e82dSTrond Myklebust extern void sunrpc_cache_unregister_pipefs(struct cache_detail *);
2422b477c00SNeil Brown extern void sunrpc_cache_unhash(struct cache_detail *, struct cache_head *);
2438854e82dSTrond Myklebust 
244c8c081b7SKinglong Mee /* Must store cache_detail in seq_file->private if using next three functions */
245ae74136bSTrond Myklebust extern void *cache_seq_start_rcu(struct seq_file *file, loff_t *pos);
246ae74136bSTrond Myklebust extern void *cache_seq_next_rcu(struct seq_file *file, void *p, loff_t *pos);
247ae74136bSTrond Myklebust extern void cache_seq_stop_rcu(struct seq_file *file, void *p);
248c8c081b7SKinglong Mee 
2491da177e4SLinus Torvalds extern void qword_add(char **bpp, int *lp, char *str);
2501da177e4SLinus Torvalds extern void qword_addhex(char **bpp, int *lp, char *buf, int blen);
2511da177e4SLinus Torvalds extern int qword_get(char **bpp, char *dest, int bufsize);
2521da177e4SLinus Torvalds 
get_int(char ** bpp,int * anint)2531da177e4SLinus Torvalds static inline int get_int(char **bpp, int *anint)
2541da177e4SLinus Torvalds {
2551da177e4SLinus Torvalds 	char buf[50];
256621eb19cSJ. Bruce Fields 	char *ep;
257621eb19cSJ. Bruce Fields 	int rv;
258d9c2ede6SEldad Zack 	int len = qword_get(bpp, buf, sizeof(buf));
259d9c2ede6SEldad Zack 
260d9c2ede6SEldad Zack 	if (len < 0)
261d9c2ede6SEldad Zack 		return -EINVAL;
262d9c2ede6SEldad Zack 	if (len == 0)
263d9c2ede6SEldad Zack 		return -ENOENT;
264d9c2ede6SEldad Zack 
265621eb19cSJ. Bruce Fields 	rv = simple_strtol(buf, &ep, 0);
266621eb19cSJ. Bruce Fields 	if (*ep)
267d9c2ede6SEldad Zack 		return -EINVAL;
268d9c2ede6SEldad Zack 
269621eb19cSJ. Bruce Fields 	*anint = rv;
2701da177e4SLinus Torvalds 	return 0;
2711da177e4SLinus Torvalds }
2721da177e4SLinus Torvalds 
get_uint(char ** bpp,unsigned int * anint)273a007c4c3SJ. Bruce Fields static inline int get_uint(char **bpp, unsigned int *anint)
274a007c4c3SJ. Bruce Fields {
275a007c4c3SJ. Bruce Fields 	char buf[50];
276a007c4c3SJ. Bruce Fields 	int len = qword_get(bpp, buf, sizeof(buf));
277a007c4c3SJ. Bruce Fields 
278a007c4c3SJ. Bruce Fields 	if (len < 0)
279a007c4c3SJ. Bruce Fields 		return -EINVAL;
280a007c4c3SJ. Bruce Fields 	if (len == 0)
281a007c4c3SJ. Bruce Fields 		return -ENOENT;
282a007c4c3SJ. Bruce Fields 
283a007c4c3SJ. Bruce Fields 	if (kstrtouint(buf, 0, anint))
284a007c4c3SJ. Bruce Fields 		return -EINVAL;
285a007c4c3SJ. Bruce Fields 
286a007c4c3SJ. Bruce Fields 	return 0;
287a007c4c3SJ. Bruce Fields }
288a007c4c3SJ. Bruce Fields 
get_time(char ** bpp,time64_t * time)289f559935eSArnd Bergmann static inline int get_time(char **bpp, time64_t *time)
2902f74f972SHarshula Jayasuriya {
2912f74f972SHarshula Jayasuriya 	char buf[50];
2922f74f972SHarshula Jayasuriya 	long long ll;
2932f74f972SHarshula Jayasuriya 	int len = qword_get(bpp, buf, sizeof(buf));
2942f74f972SHarshula Jayasuriya 
2952f74f972SHarshula Jayasuriya 	if (len < 0)
2962f74f972SHarshula Jayasuriya 		return -EINVAL;
2972f74f972SHarshula Jayasuriya 	if (len == 0)
2982f74f972SHarshula Jayasuriya 		return -ENOENT;
2992f74f972SHarshula Jayasuriya 
3002f74f972SHarshula Jayasuriya 	if (kstrtoll(buf, 0, &ll))
3012f74f972SHarshula Jayasuriya 		return -EINVAL;
3022f74f972SHarshula Jayasuriya 
303f559935eSArnd Bergmann 	*time = ll;
3042f74f972SHarshula Jayasuriya 	return 0;
3052f74f972SHarshula Jayasuriya }
3062f74f972SHarshula Jayasuriya 
get_expiry(char ** bpp,time64_t * rvp)307cf64b9bcSNeilBrown static inline int get_expiry(char **bpp, time64_t *rvp)
3081da177e4SLinus Torvalds {
309cf64b9bcSNeilBrown 	int error;
310f559935eSArnd Bergmann 	struct timespec64 boot;
311c5b29f88SNeilBrown 
312cf64b9bcSNeilBrown 	error = get_time(bpp, rvp);
313cf64b9bcSNeilBrown 	if (error)
314cf64b9bcSNeilBrown 		return error;
315cf64b9bcSNeilBrown 
316f559935eSArnd Bergmann 	getboottime64(&boot);
317cf64b9bcSNeilBrown 	(*rvp) -= boot.tv_sec;
318cf64b9bcSNeilBrown 	return 0;
3191da177e4SLinus Torvalds }
3201da177e4SLinus Torvalds 
3211da177e4SLinus Torvalds #endif /*  _LINUX_SUNRPC_CACHE_H_ */
322