xref: /linux/net/sunrpc/cache.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
1ddc64d0aSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * net/sunrpc/cache.c
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 #include <linux/types.h>
121da177e4SLinus Torvalds #include <linux/fs.h>
131da177e4SLinus Torvalds #include <linux/file.h>
141da177e4SLinus Torvalds #include <linux/slab.h>
151da177e4SLinus Torvalds #include <linux/signal.h>
161da177e4SLinus Torvalds #include <linux/sched.h>
171da177e4SLinus Torvalds #include <linux/kmod.h>
181da177e4SLinus Torvalds #include <linux/list.h>
191da177e4SLinus Torvalds #include <linux/module.h>
201da177e4SLinus Torvalds #include <linux/ctype.h>
211b2e122dSAndy Shevchenko #include <linux/string_helpers.h>
227c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
231da177e4SLinus Torvalds #include <linux/poll.h>
241da177e4SLinus Torvalds #include <linux/seq_file.h>
251da177e4SLinus Torvalds #include <linux/proc_fs.h>
261da177e4SLinus Torvalds #include <linux/net.h>
271da177e4SLinus Torvalds #include <linux/workqueue.h>
284a3e2f71SArjan van de Ven #include <linux/mutex.h>
29da77005fSTrond Myklebust #include <linux/pagemap.h>
301da177e4SLinus Torvalds #include <asm/ioctls.h>
311da177e4SLinus Torvalds #include <linux/sunrpc/types.h>
321da177e4SLinus Torvalds #include <linux/sunrpc/cache.h>
331da177e4SLinus Torvalds #include <linux/sunrpc/stats.h>
348854e82dSTrond Myklebust #include <linux/sunrpc/rpc_pipe_fs.h>
3578a947f5STrond Myklebust #include <trace/events/sunrpc.h>
3637324e6bSChuck Lever 
374f42d0d5SPavel Emelyanov #include "netns.h"
3837324e6bSChuck Lever #include "fail.h"
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds #define	 RPCDBG_FACILITY RPCDBG_CACHE
411da177e4SLinus Torvalds 
42d76d1815SJ. Bruce Fields static bool cache_defer_req(struct cache_req *req, struct cache_head *item);
431da177e4SLinus Torvalds static void cache_revisit_request(struct cache_head *item);
441da177e4SLinus Torvalds 
cache_init(struct cache_head * h,struct cache_detail * detail)4577862036SNeil Brown static void cache_init(struct cache_head *h, struct cache_detail *detail)
461da177e4SLinus Torvalds {
47f559935eSArnd Bergmann 	time64_t now = seconds_since_boot();
48129e5824SKinglong Mee 	INIT_HLIST_NODE(&h->cache_list);
491da177e4SLinus Torvalds 	h->flags = 0;
50baab935fSNeilBrown 	kref_init(&h->ref);
511da177e4SLinus Torvalds 	h->expiry_time = now + CACHE_NEW_EXPIRY;
5277862036SNeil Brown 	if (now <= detail->flush_time)
5377862036SNeil Brown 		/* ensure it isn't already expired */
5477862036SNeil Brown 		now = detail->flush_time + 1;
551da177e4SLinus Torvalds 	h->last_refresh = now;
561da177e4SLinus Torvalds }
571da177e4SLinus Torvalds 
584ecd55eaSVasily Averin static void cache_fresh_unlocked(struct cache_head *head,
594ecd55eaSVasily Averin 				struct cache_detail *detail);
604ecd55eaSVasily Averin 
sunrpc_cache_find_rcu(struct cache_detail * detail,struct cache_head * key,int hash)61ae74136bSTrond Myklebust static struct cache_head *sunrpc_cache_find_rcu(struct cache_detail *detail,
62ae74136bSTrond Myklebust 						struct cache_head *key,
63ae74136bSTrond Myklebust 						int hash)
64ae74136bSTrond Myklebust {
65ae74136bSTrond Myklebust 	struct hlist_head *head = &detail->hash_table[hash];
66ae74136bSTrond Myklebust 	struct cache_head *tmp;
67ae74136bSTrond Myklebust 
68ae74136bSTrond Myklebust 	rcu_read_lock();
69ae74136bSTrond Myklebust 	hlist_for_each_entry_rcu(tmp, head, cache_list) {
70277f27e2STrond Myklebust 		if (!detail->match(tmp, key))
71277f27e2STrond Myklebust 			continue;
72277f27e2STrond Myklebust 		if (test_bit(CACHE_VALID, &tmp->flags) &&
73277f27e2STrond Myklebust 		    cache_is_expired(detail, tmp))
74ae74136bSTrond Myklebust 			continue;
75ae74136bSTrond Myklebust 		tmp = cache_get_rcu(tmp);
76ae74136bSTrond Myklebust 		rcu_read_unlock();
77ae74136bSTrond Myklebust 		return tmp;
78ae74136bSTrond Myklebust 	}
79ae74136bSTrond Myklebust 	rcu_read_unlock();
80ae74136bSTrond Myklebust 	return NULL;
81ae74136bSTrond Myklebust }
82ae74136bSTrond Myklebust 
sunrpc_begin_cache_remove_entry(struct cache_head * ch,struct cache_detail * cd)83809fe3c5STrond Myklebust static void sunrpc_begin_cache_remove_entry(struct cache_head *ch,
84809fe3c5STrond Myklebust 					    struct cache_detail *cd)
85809fe3c5STrond Myklebust {
86809fe3c5STrond Myklebust 	/* Must be called under cd->hash_lock */
87809fe3c5STrond Myklebust 	hlist_del_init_rcu(&ch->cache_list);
88809fe3c5STrond Myklebust 	set_bit(CACHE_CLEANED, &ch->flags);
89809fe3c5STrond Myklebust 	cd->entries --;
90809fe3c5STrond Myklebust }
91809fe3c5STrond Myklebust 
sunrpc_end_cache_remove_entry(struct cache_head * ch,struct cache_detail * cd)92809fe3c5STrond Myklebust static void sunrpc_end_cache_remove_entry(struct cache_head *ch,
93809fe3c5STrond Myklebust 					  struct cache_detail *cd)
94809fe3c5STrond Myklebust {
95809fe3c5STrond Myklebust 	cache_fresh_unlocked(ch, cd);
96809fe3c5STrond Myklebust 	cache_put(ch, cd);
97809fe3c5STrond Myklebust }
98809fe3c5STrond Myklebust 
sunrpc_cache_add_entry(struct cache_detail * detail,struct cache_head * key,int hash)99b92a8fabSTrond Myklebust static struct cache_head *sunrpc_cache_add_entry(struct cache_detail *detail,
100b92a8fabSTrond Myklebust 						 struct cache_head *key,
101b92a8fabSTrond Myklebust 						 int hash)
102b92a8fabSTrond Myklebust {
103b92a8fabSTrond Myklebust 	struct cache_head *new, *tmp, *freeme = NULL;
104b92a8fabSTrond Myklebust 	struct hlist_head *head = &detail->hash_table[hash];
10515a5f6bdSNeilBrown 
10615a5f6bdSNeilBrown 	new = detail->alloc();
10715a5f6bdSNeilBrown 	if (!new)
10815a5f6bdSNeilBrown 		return NULL;
1092f34931fSNeil Brown 	/* must fully initialise 'new', else
1102f34931fSNeil Brown 	 * we might get lose if we need to
1112f34931fSNeil Brown 	 * cache_put it soon.
1122f34931fSNeil Brown 	 */
11377862036SNeil Brown 	cache_init(new, detail);
1142f34931fSNeil Brown 	detail->init(new, key);
11515a5f6bdSNeilBrown 
1161863d77fSTrond Myklebust 	spin_lock(&detail->hash_lock);
11715a5f6bdSNeilBrown 
11815a5f6bdSNeilBrown 	/* check if entry appeared while we slept */
11951cae673SAmol Grover 	hlist_for_each_entry_rcu(tmp, head, cache_list,
12051cae673SAmol Grover 				 lockdep_is_held(&detail->hash_lock)) {
121277f27e2STrond Myklebust 		if (!detail->match(tmp, key))
122277f27e2STrond Myklebust 			continue;
123277f27e2STrond Myklebust 		if (test_bit(CACHE_VALID, &tmp->flags) &&
124277f27e2STrond Myklebust 		    cache_is_expired(detail, tmp)) {
125809fe3c5STrond Myklebust 			sunrpc_begin_cache_remove_entry(tmp, detail);
12678a947f5STrond Myklebust 			trace_cache_entry_expired(detail, tmp);
127d202cce8SNeilBrown 			freeme = tmp;
128d202cce8SNeilBrown 			break;
129d202cce8SNeilBrown 		}
13015a5f6bdSNeilBrown 		cache_get(tmp);
1311863d77fSTrond Myklebust 		spin_unlock(&detail->hash_lock);
132baab935fSNeilBrown 		cache_put(new, detail);
13315a5f6bdSNeilBrown 		return tmp;
13415a5f6bdSNeilBrown 	}
135129e5824SKinglong Mee 
136ae74136bSTrond Myklebust 	hlist_add_head_rcu(&new->cache_list, head);
13715a5f6bdSNeilBrown 	detail->entries++;
13815a5f6bdSNeilBrown 	cache_get(new);
1391863d77fSTrond Myklebust 	spin_unlock(&detail->hash_lock);
14015a5f6bdSNeilBrown 
141809fe3c5STrond Myklebust 	if (freeme)
142809fe3c5STrond Myklebust 		sunrpc_end_cache_remove_entry(freeme, detail);
14315a5f6bdSNeilBrown 	return new;
14415a5f6bdSNeilBrown }
14515a5f6bdSNeilBrown 
sunrpc_cache_lookup_rcu(struct cache_detail * detail,struct cache_head * key,int hash)146ae74136bSTrond Myklebust struct cache_head *sunrpc_cache_lookup_rcu(struct cache_detail *detail,
147ae74136bSTrond Myklebust 					   struct cache_head *key, int hash)
148ae74136bSTrond Myklebust {
149ae74136bSTrond Myklebust 	struct cache_head *ret;
150ae74136bSTrond Myklebust 
151ae74136bSTrond Myklebust 	ret = sunrpc_cache_find_rcu(detail, key, hash);
152ae74136bSTrond Myklebust 	if (ret)
153ae74136bSTrond Myklebust 		return ret;
154ae74136bSTrond Myklebust 	/* Didn't find anything, insert an empty entry */
155ae74136bSTrond Myklebust 	return sunrpc_cache_add_entry(detail, key, hash);
156ae74136bSTrond Myklebust }
157ae74136bSTrond Myklebust EXPORT_SYMBOL_GPL(sunrpc_cache_lookup_rcu);
158ae74136bSTrond Myklebust 
159f866a819SNeilBrown static void cache_dequeue(struct cache_detail *detail, struct cache_head *ch);
160ebd0cb1aSNeilBrown 
cache_fresh_locked(struct cache_head * head,time64_t expiry,struct cache_detail * detail)161f559935eSArnd Bergmann static void cache_fresh_locked(struct cache_head *head, time64_t expiry,
16277862036SNeil Brown 			       struct cache_detail *detail)
163ebd0cb1aSNeilBrown {
164f559935eSArnd Bergmann 	time64_t now = seconds_since_boot();
16577862036SNeil Brown 	if (now <= detail->flush_time)
16677862036SNeil Brown 		/* ensure it isn't immediately treated as expired */
16777862036SNeil Brown 		now = detail->flush_time + 1;
168ebd0cb1aSNeilBrown 	head->expiry_time = expiry;
16977862036SNeil Brown 	head->last_refresh = now;
170fdef7aa5SJ. Bruce Fields 	smp_wmb(); /* paired with smp_rmb() in cache_is_valid() */
171908329f2SNeilBrown 	set_bit(CACHE_VALID, &head->flags);
172ebd0cb1aSNeilBrown }
173ebd0cb1aSNeilBrown 
cache_fresh_unlocked(struct cache_head * head,struct cache_detail * detail)174ebd0cb1aSNeilBrown static void cache_fresh_unlocked(struct cache_head *head,
175908329f2SNeilBrown 				 struct cache_detail *detail)
176ebd0cb1aSNeilBrown {
177ebd0cb1aSNeilBrown 	if (test_and_clear_bit(CACHE_PENDING, &head->flags)) {
178ebd0cb1aSNeilBrown 		cache_revisit_request(head);
179f866a819SNeilBrown 		cache_dequeue(detail, head);
180ebd0cb1aSNeilBrown 	}
181ebd0cb1aSNeilBrown }
182ebd0cb1aSNeilBrown 
cache_make_negative(struct cache_detail * detail,struct cache_head * h)18378a947f5STrond Myklebust static void cache_make_negative(struct cache_detail *detail,
18478a947f5STrond Myklebust 				struct cache_head *h)
18578a947f5STrond Myklebust {
18678a947f5STrond Myklebust 	set_bit(CACHE_NEGATIVE, &h->flags);
18778a947f5STrond Myklebust 	trace_cache_entry_make_negative(detail, h);
18878a947f5STrond Myklebust }
18978a947f5STrond Myklebust 
cache_entry_update(struct cache_detail * detail,struct cache_head * h,struct cache_head * new)19078a947f5STrond Myklebust static void cache_entry_update(struct cache_detail *detail,
19178a947f5STrond Myklebust 			       struct cache_head *h,
19278a947f5STrond Myklebust 			       struct cache_head *new)
19378a947f5STrond Myklebust {
19478a947f5STrond Myklebust 	if (!test_bit(CACHE_NEGATIVE, &new->flags)) {
19578a947f5STrond Myklebust 		detail->update(h, new);
19678a947f5STrond Myklebust 		trace_cache_entry_update(detail, h);
19778a947f5STrond Myklebust 	} else {
19878a947f5STrond Myklebust 		cache_make_negative(detail, h);
19978a947f5STrond Myklebust 	}
20078a947f5STrond Myklebust }
20178a947f5STrond Myklebust 
sunrpc_cache_update(struct cache_detail * detail,struct cache_head * new,struct cache_head * old,int hash)20215a5f6bdSNeilBrown struct cache_head *sunrpc_cache_update(struct cache_detail *detail,
20315a5f6bdSNeilBrown 				       struct cache_head *new, struct cache_head *old, int hash)
20415a5f6bdSNeilBrown {
20515a5f6bdSNeilBrown 	/* The 'old' entry is to be replaced by 'new'.
20615a5f6bdSNeilBrown 	 * If 'old' is not VALID, we update it directly,
20715a5f6bdSNeilBrown 	 * otherwise we need to replace it
20815a5f6bdSNeilBrown 	 */
20915a5f6bdSNeilBrown 	struct cache_head *tmp;
21015a5f6bdSNeilBrown 
21115a5f6bdSNeilBrown 	if (!test_bit(CACHE_VALID, &old->flags)) {
2121863d77fSTrond Myklebust 		spin_lock(&detail->hash_lock);
21315a5f6bdSNeilBrown 		if (!test_bit(CACHE_VALID, &old->flags)) {
21478a947f5STrond Myklebust 			cache_entry_update(detail, old, new);
21577862036SNeil Brown 			cache_fresh_locked(old, new->expiry_time, detail);
2161863d77fSTrond Myklebust 			spin_unlock(&detail->hash_lock);
217908329f2SNeilBrown 			cache_fresh_unlocked(old, detail);
21815a5f6bdSNeilBrown 			return old;
21915a5f6bdSNeilBrown 		}
2201863d77fSTrond Myklebust 		spin_unlock(&detail->hash_lock);
22115a5f6bdSNeilBrown 	}
22215a5f6bdSNeilBrown 	/* We need to insert a new entry */
22315a5f6bdSNeilBrown 	tmp = detail->alloc();
22415a5f6bdSNeilBrown 	if (!tmp) {
225baab935fSNeilBrown 		cache_put(old, detail);
22615a5f6bdSNeilBrown 		return NULL;
22715a5f6bdSNeilBrown 	}
22877862036SNeil Brown 	cache_init(tmp, detail);
22915a5f6bdSNeilBrown 	detail->init(tmp, old);
23015a5f6bdSNeilBrown 
2311863d77fSTrond Myklebust 	spin_lock(&detail->hash_lock);
23278a947f5STrond Myklebust 	cache_entry_update(detail, tmp, new);
233129e5824SKinglong Mee 	hlist_add_head(&tmp->cache_list, &detail->hash_table[hash]);
234f2d39586SNeilBrown 	detail->entries++;
23515a5f6bdSNeilBrown 	cache_get(tmp);
23677862036SNeil Brown 	cache_fresh_locked(tmp, new->expiry_time, detail);
23777862036SNeil Brown 	cache_fresh_locked(old, 0, detail);
2381863d77fSTrond Myklebust 	spin_unlock(&detail->hash_lock);
239908329f2SNeilBrown 	cache_fresh_unlocked(tmp, detail);
240908329f2SNeilBrown 	cache_fresh_unlocked(old, detail);
241baab935fSNeilBrown 	cache_put(old, detail);
24215a5f6bdSNeilBrown 	return tmp;
24315a5f6bdSNeilBrown }
24424c3767eSTrond Myklebust EXPORT_SYMBOL_GPL(sunrpc_cache_update);
2451da177e4SLinus Torvalds 
cache_is_valid(struct cache_head * h)246b6040f97Schaoting fan static inline int cache_is_valid(struct cache_head *h)
247989a19b9SNeilBrown {
248d202cce8SNeilBrown 	if (!test_bit(CACHE_VALID, &h->flags))
249989a19b9SNeilBrown 		return -EAGAIN;
250989a19b9SNeilBrown 	else {
251989a19b9SNeilBrown 		/* entry is valid */
252989a19b9SNeilBrown 		if (test_bit(CACHE_NEGATIVE, &h->flags))
253989a19b9SNeilBrown 			return -ENOENT;
254fdef7aa5SJ. Bruce Fields 		else {
255fdef7aa5SJ. Bruce Fields 			/*
256fdef7aa5SJ. Bruce Fields 			 * In combination with write barrier in
257fdef7aa5SJ. Bruce Fields 			 * sunrpc_cache_update, ensures that anyone
258fdef7aa5SJ. Bruce Fields 			 * using the cache entry after this sees the
259fdef7aa5SJ. Bruce Fields 			 * updated contents:
260fdef7aa5SJ. Bruce Fields 			 */
261fdef7aa5SJ. Bruce Fields 			smp_rmb();
262989a19b9SNeilBrown 			return 0;
263989a19b9SNeilBrown 		}
264989a19b9SNeilBrown 	}
265fdef7aa5SJ. Bruce Fields }
266e9dc1221SJ. Bruce Fields 
try_to_negate_entry(struct cache_detail * detail,struct cache_head * h)2676bab93f8SJ. Bruce Fields static int try_to_negate_entry(struct cache_detail *detail, struct cache_head *h)
2686bab93f8SJ. Bruce Fields {
2696bab93f8SJ. Bruce Fields 	int rv;
2706bab93f8SJ. Bruce Fields 
2711863d77fSTrond Myklebust 	spin_lock(&detail->hash_lock);
272b6040f97Schaoting fan 	rv = cache_is_valid(h);
2732a1c7f53SNeilBrown 	if (rv == -EAGAIN) {
27478a947f5STrond Myklebust 		cache_make_negative(detail, h);
27577862036SNeil Brown 		cache_fresh_locked(h, seconds_since_boot()+CACHE_NEW_EXPIRY,
27677862036SNeil Brown 				   detail);
2772a1c7f53SNeilBrown 		rv = -ENOENT;
2782a1c7f53SNeilBrown 	}
2791863d77fSTrond Myklebust 	spin_unlock(&detail->hash_lock);
2806bab93f8SJ. Bruce Fields 	cache_fresh_unlocked(h, detail);
2812a1c7f53SNeilBrown 	return rv;
2826bab93f8SJ. Bruce Fields }
2836bab93f8SJ. Bruce Fields 
2841da177e4SLinus Torvalds /*
2851da177e4SLinus Torvalds  * This is the generic cache management routine for all
2861da177e4SLinus Torvalds  * the authentication caches.
2871da177e4SLinus Torvalds  * It checks the currency of a cache item and will (later)
2881da177e4SLinus Torvalds  * initiate an upcall to fill it if needed.
2891da177e4SLinus Torvalds  *
2901da177e4SLinus Torvalds  *
2911da177e4SLinus Torvalds  * Returns 0 if the cache_head can be used, or cache_puts it and returns
292989a19b9SNeilBrown  * -EAGAIN if upcall is pending and request has been queued
293989a19b9SNeilBrown  * -ETIMEDOUT if upcall failed or request could not be queue or
294989a19b9SNeilBrown  *           upcall completed but item is still invalid (implying that
295989a19b9SNeilBrown  *           the cache item has been replaced with a newer one).
2961da177e4SLinus Torvalds  * -ENOENT if cache entry was negative
2971da177e4SLinus Torvalds  */
cache_check(struct cache_detail * detail,struct cache_head * h,struct cache_req * rqstp)2981da177e4SLinus Torvalds int cache_check(struct cache_detail *detail,
2991da177e4SLinus Torvalds 		    struct cache_head *h, struct cache_req *rqstp)
3001da177e4SLinus Torvalds {
3011da177e4SLinus Torvalds 	int rv;
302f559935eSArnd Bergmann 	time64_t refresh_age, age;
3031da177e4SLinus Torvalds 
3041da177e4SLinus Torvalds 	/* First decide return status as best we can */
305b6040f97Schaoting fan 	rv = cache_is_valid(h);
3061da177e4SLinus Torvalds 
3071da177e4SLinus Torvalds 	/* now see if we want to start an upcall */
3081da177e4SLinus Torvalds 	refresh_age = (h->expiry_time - h->last_refresh);
309c5b29f88SNeilBrown 	age = seconds_since_boot() - h->last_refresh;
3101da177e4SLinus Torvalds 
3111da177e4SLinus Torvalds 	if (rqstp == NULL) {
3121da177e4SLinus Torvalds 		if (rv == -EAGAIN)
3131da177e4SLinus Torvalds 			rv = -ENOENT;
3140bebc633SNeilBrown 	} else if (rv == -EAGAIN ||
3150bebc633SNeilBrown 		   (h->expiry_time != 0 && age > refresh_age/2)) {
316f559935eSArnd Bergmann 		dprintk("RPC:       Want update, refage=%lld, age=%lld\n",
31746121cf7SChuck Lever 				refresh_age, age);
31865286b88STrond Myklebust 		switch (detail->cache_upcall(detail, h)) {
3191da177e4SLinus Torvalds 		case -EINVAL:
3206bab93f8SJ. Bruce Fields 			rv = try_to_negate_entry(detail, h);
3211da177e4SLinus Torvalds 			break;
3221da177e4SLinus Torvalds 		case -EAGAIN:
3232a1c7f53SNeilBrown 			cache_fresh_unlocked(h, detail);
3241da177e4SLinus Torvalds 			break;
3251da177e4SLinus Torvalds 		}
3261da177e4SLinus Torvalds 	}
3271da177e4SLinus Torvalds 
328989a19b9SNeilBrown 	if (rv == -EAGAIN) {
329d76d1815SJ. Bruce Fields 		if (!cache_defer_req(rqstp, h)) {
330d76d1815SJ. Bruce Fields 			/*
331d76d1815SJ. Bruce Fields 			 * Request was not deferred; handle it as best
332d76d1815SJ. Bruce Fields 			 * we can ourselves:
333d76d1815SJ. Bruce Fields 			 */
334b6040f97Schaoting fan 			rv = cache_is_valid(h);
3351da177e4SLinus Torvalds 			if (rv == -EAGAIN)
336e0bb89efSJ.Bruce Fields 				rv = -ETIMEDOUT;
337989a19b9SNeilBrown 		}
338989a19b9SNeilBrown 	}
3394013edeaSNeilBrown 	if (rv)
340baab935fSNeilBrown 		cache_put(h, detail);
3411da177e4SLinus Torvalds 	return rv;
3421da177e4SLinus Torvalds }
34324c3767eSTrond Myklebust EXPORT_SYMBOL_GPL(cache_check);
3441da177e4SLinus Torvalds 
3451da177e4SLinus Torvalds /*
3461da177e4SLinus Torvalds  * caches need to be periodically cleaned.
3471da177e4SLinus Torvalds  * For this we maintain a list of cache_detail and
3481da177e4SLinus Torvalds  * a current pointer into that list and into the table
3491da177e4SLinus Torvalds  * for that entry.
3501da177e4SLinus Torvalds  *
351013920ebSNeilBrown  * Each time cache_clean is called it finds the next non-empty entry
3521da177e4SLinus Torvalds  * in the current table and walks the list in that entry
3531da177e4SLinus Torvalds  * looking for entries that can be removed.
3541da177e4SLinus Torvalds  *
3551da177e4SLinus Torvalds  * An entry gets removed if:
3561da177e4SLinus Torvalds  * - The expiry is before current time
3571da177e4SLinus Torvalds  * - The last_refresh time is before the flush_time for that cache
3581da177e4SLinus Torvalds  *
3591da177e4SLinus Torvalds  * later we might drop old entries with non-NEVER expiry if that table
3601da177e4SLinus Torvalds  * is getting 'full' for some definition of 'full'
3611da177e4SLinus Torvalds  *
3621da177e4SLinus Torvalds  * The question of "how often to scan a table" is an interesting one
3631da177e4SLinus Torvalds  * and is answered in part by the use of the "nextcheck" field in the
3641da177e4SLinus Torvalds  * cache_detail.
3651da177e4SLinus Torvalds  * When a scan of a table begins, the nextcheck field is set to a time
3661da177e4SLinus Torvalds  * that is well into the future.
3671da177e4SLinus Torvalds  * While scanning, if an expiry time is found that is earlier than the
3681da177e4SLinus Torvalds  * current nextcheck time, nextcheck is set to that expiry time.
3691da177e4SLinus Torvalds  * If the flush_time is ever set to a time earlier than the nextcheck
3701da177e4SLinus Torvalds  * time, the nextcheck time is then set to that flush_time.
3711da177e4SLinus Torvalds  *
3721da177e4SLinus Torvalds  * A table is then only scanned if the current time is at least
3731da177e4SLinus Torvalds  * the nextcheck time.
3741da177e4SLinus Torvalds  *
3751da177e4SLinus Torvalds  */
3761da177e4SLinus Torvalds 
3771da177e4SLinus Torvalds static LIST_HEAD(cache_list);
3781da177e4SLinus Torvalds static DEFINE_SPINLOCK(cache_list_lock);
3791da177e4SLinus Torvalds static struct cache_detail *current_detail;
3801da177e4SLinus Torvalds static int current_index;
3811da177e4SLinus Torvalds 
38265f27f38SDavid Howells static void do_cache_clean(struct work_struct *work);
3838eab945cSArtem Bityutskiy static struct delayed_work cache_cleaner;
3841da177e4SLinus Torvalds 
sunrpc_init_cache_detail(struct cache_detail * cd)385820f9442SStanislav Kinsbursky void sunrpc_init_cache_detail(struct cache_detail *cd)
3861da177e4SLinus Torvalds {
3871863d77fSTrond Myklebust 	spin_lock_init(&cd->hash_lock);
3881da177e4SLinus Torvalds 	INIT_LIST_HEAD(&cd->queue);
3891da177e4SLinus Torvalds 	spin_lock(&cache_list_lock);
3901da177e4SLinus Torvalds 	cd->nextcheck = 0;
3911da177e4SLinus Torvalds 	cd->entries = 0;
39264a38e84SDave Wysochanski 	atomic_set(&cd->writers, 0);
3931da177e4SLinus Torvalds 	cd->last_close = 0;
3941da177e4SLinus Torvalds 	cd->last_warn = -1;
3951da177e4SLinus Torvalds 	list_add(&cd->others, &cache_list);
3961da177e4SLinus Torvalds 	spin_unlock(&cache_list_lock);
3971da177e4SLinus Torvalds 
3981da177e4SLinus Torvalds 	/* start the cleaning process */
39977b00bc0SKe Wang 	queue_delayed_work(system_power_efficient_wq, &cache_cleaner, 0);
4001da177e4SLinus Torvalds }
401820f9442SStanislav Kinsbursky EXPORT_SYMBOL_GPL(sunrpc_init_cache_detail);
4021da177e4SLinus Torvalds 
sunrpc_destroy_cache_detail(struct cache_detail * cd)403820f9442SStanislav Kinsbursky void sunrpc_destroy_cache_detail(struct cache_detail *cd)
4041da177e4SLinus Torvalds {
4051da177e4SLinus Torvalds 	cache_purge(cd);
4061da177e4SLinus Torvalds 	spin_lock(&cache_list_lock);
4071863d77fSTrond Myklebust 	spin_lock(&cd->hash_lock);
4081da177e4SLinus Torvalds 	if (current_detail == cd)
4091da177e4SLinus Torvalds 		current_detail = NULL;
4101da177e4SLinus Torvalds 	list_del_init(&cd->others);
4111863d77fSTrond Myklebust 	spin_unlock(&cd->hash_lock);
4121da177e4SLinus Torvalds 	spin_unlock(&cache_list_lock);
4131da177e4SLinus Torvalds 	if (list_empty(&cache_list)) {
4141da177e4SLinus Torvalds 		/* module must be being unloaded so its safe to kill the worker */
4154011cd97STrond Myklebust 		cancel_delayed_work_sync(&cache_cleaner);
4161da177e4SLinus Torvalds 	}
4171da177e4SLinus Torvalds }
418820f9442SStanislav Kinsbursky EXPORT_SYMBOL_GPL(sunrpc_destroy_cache_detail);
4191da177e4SLinus Torvalds 
4201da177e4SLinus Torvalds /* clean cache tries to find something to clean
4211da177e4SLinus Torvalds  * and cleans it.
4221da177e4SLinus Torvalds  * It returns 1 if it cleaned something,
4231da177e4SLinus Torvalds  *            0 if it didn't find anything this time
4241da177e4SLinus Torvalds  *           -1 if it fell off the end of the list.
4251da177e4SLinus Torvalds  */
cache_clean(void)4261da177e4SLinus Torvalds static int cache_clean(void)
4271da177e4SLinus Torvalds {
4281da177e4SLinus Torvalds 	int rv = 0;
4291da177e4SLinus Torvalds 	struct list_head *next;
4301da177e4SLinus Torvalds 
4311da177e4SLinus Torvalds 	spin_lock(&cache_list_lock);
4321da177e4SLinus Torvalds 
4331da177e4SLinus Torvalds 	/* find a suitable table if we don't already have one */
4341da177e4SLinus Torvalds 	while (current_detail == NULL ||
4351da177e4SLinus Torvalds 	    current_index >= current_detail->hash_size) {
4361da177e4SLinus Torvalds 		if (current_detail)
4371da177e4SLinus Torvalds 			next = current_detail->others.next;
4381da177e4SLinus Torvalds 		else
4391da177e4SLinus Torvalds 			next = cache_list.next;
4401da177e4SLinus Torvalds 		if (next == &cache_list) {
4411da177e4SLinus Torvalds 			current_detail = NULL;
4421da177e4SLinus Torvalds 			spin_unlock(&cache_list_lock);
4431da177e4SLinus Torvalds 			return -1;
4441da177e4SLinus Torvalds 		}
4451da177e4SLinus Torvalds 		current_detail = list_entry(next, struct cache_detail, others);
446c5b29f88SNeilBrown 		if (current_detail->nextcheck > seconds_since_boot())
4471da177e4SLinus Torvalds 			current_index = current_detail->hash_size;
4481da177e4SLinus Torvalds 		else {
4491da177e4SLinus Torvalds 			current_index = 0;
450c5b29f88SNeilBrown 			current_detail->nextcheck = seconds_since_boot()+30*60;
4511da177e4SLinus Torvalds 		}
4521da177e4SLinus Torvalds 	}
4531da177e4SLinus Torvalds 
4541da177e4SLinus Torvalds 	/* find a non-empty bucket in the table */
4551da177e4SLinus Torvalds 	while (current_detail &&
4561da177e4SLinus Torvalds 	       current_index < current_detail->hash_size &&
457129e5824SKinglong Mee 	       hlist_empty(&current_detail->hash_table[current_index]))
4581da177e4SLinus Torvalds 		current_index++;
4591da177e4SLinus Torvalds 
4601da177e4SLinus Torvalds 	/* find a cleanable entry in the bucket and clean it, or set to next bucket */
4611da177e4SLinus Torvalds 
4621da177e4SLinus Torvalds 	if (current_detail && current_index < current_detail->hash_size) {
463129e5824SKinglong Mee 		struct cache_head *ch = NULL;
4641da177e4SLinus Torvalds 		struct cache_detail *d;
465129e5824SKinglong Mee 		struct hlist_head *head;
466129e5824SKinglong Mee 		struct hlist_node *tmp;
4671da177e4SLinus Torvalds 
4681863d77fSTrond Myklebust 		spin_lock(&current_detail->hash_lock);
4691da177e4SLinus Torvalds 
4701da177e4SLinus Torvalds 		/* Ok, now to clean this strand */
4711da177e4SLinus Torvalds 
472129e5824SKinglong Mee 		head = &current_detail->hash_table[current_index];
473129e5824SKinglong Mee 		hlist_for_each_entry_safe(ch, tmp, head, cache_list) {
4741da177e4SLinus Torvalds 			if (current_detail->nextcheck > ch->expiry_time)
4751da177e4SLinus Torvalds 				current_detail->nextcheck = ch->expiry_time+1;
4762f50d8b6SNeilBrown 			if (!cache_is_expired(current_detail, ch))
4771da177e4SLinus Torvalds 				continue;
4781da177e4SLinus Torvalds 
479809fe3c5STrond Myklebust 			sunrpc_begin_cache_remove_entry(ch, current_detail);
48078a947f5STrond Myklebust 			trace_cache_entry_expired(current_detail, ch);
4811da177e4SLinus Torvalds 			rv = 1;
4823af4974eSNeilBrown 			break;
4831da177e4SLinus Torvalds 		}
4843af4974eSNeilBrown 
4851863d77fSTrond Myklebust 		spin_unlock(&current_detail->hash_lock);
4861da177e4SLinus Torvalds 		d = current_detail;
4871da177e4SLinus Torvalds 		if (!ch)
4881da177e4SLinus Torvalds 			current_index ++;
4891da177e4SLinus Torvalds 		spin_unlock(&cache_list_lock);
490809fe3c5STrond Myklebust 		if (ch)
491809fe3c5STrond Myklebust 			sunrpc_end_cache_remove_entry(ch, d);
4921da177e4SLinus Torvalds 	} else
4931da177e4SLinus Torvalds 		spin_unlock(&cache_list_lock);
4941da177e4SLinus Torvalds 
4951da177e4SLinus Torvalds 	return rv;
4961da177e4SLinus Torvalds }
4971da177e4SLinus Torvalds 
4981da177e4SLinus Torvalds /*
4991da177e4SLinus Torvalds  * We want to regularly clean the cache, so we need to schedule some work ...
5001da177e4SLinus Torvalds  */
do_cache_clean(struct work_struct * work)50165f27f38SDavid Howells static void do_cache_clean(struct work_struct *work)
5021da177e4SLinus Torvalds {
5030aa99c4dSJ. Bruce Fields 	int delay;
5041da177e4SLinus Torvalds 
5051da177e4SLinus Torvalds 	if (list_empty(&cache_list))
5060aa99c4dSJ. Bruce Fields 		return;
5071da177e4SLinus Torvalds 
5080aa99c4dSJ. Bruce Fields 	if (cache_clean() == -1)
5090aa99c4dSJ. Bruce Fields 		delay = round_jiffies_relative(30*HZ);
5100aa99c4dSJ. Bruce Fields 	else
5110aa99c4dSJ. Bruce Fields 		delay = 5;
5120aa99c4dSJ. Bruce Fields 
5130aa99c4dSJ. Bruce Fields 	queue_delayed_work(system_power_efficient_wq, &cache_cleaner, delay);
5141da177e4SLinus Torvalds }
5151da177e4SLinus Torvalds 
5161da177e4SLinus Torvalds 
5171da177e4SLinus Torvalds /*
5181da177e4SLinus Torvalds  * Clean all caches promptly.  This just calls cache_clean
5191da177e4SLinus Torvalds  * repeatedly until we are sure that every cache has had a chance to
5201da177e4SLinus Torvalds  * be fully cleaned
5211da177e4SLinus Torvalds  */
cache_flush(void)5221da177e4SLinus Torvalds void cache_flush(void)
5231da177e4SLinus Torvalds {
5241da177e4SLinus Torvalds 	while (cache_clean() != -1)
5251da177e4SLinus Torvalds 		cond_resched();
5261da177e4SLinus Torvalds 	while (cache_clean() != -1)
5271da177e4SLinus Torvalds 		cond_resched();
5281da177e4SLinus Torvalds }
52924c3767eSTrond Myklebust EXPORT_SYMBOL_GPL(cache_flush);
5301da177e4SLinus Torvalds 
cache_purge(struct cache_detail * detail)5311da177e4SLinus Torvalds void cache_purge(struct cache_detail *detail)
5321da177e4SLinus Torvalds {
533471a930aSKinglong Mee 	struct cache_head *ch = NULL;
534471a930aSKinglong Mee 	struct hlist_head *head = NULL;
535471a930aSKinglong Mee 	int i = 0;
536471a930aSKinglong Mee 
5371863d77fSTrond Myklebust 	spin_lock(&detail->hash_lock);
538471a930aSKinglong Mee 	if (!detail->entries) {
5391863d77fSTrond Myklebust 		spin_unlock(&detail->hash_lock);
540471a930aSKinglong Mee 		return;
541471a930aSKinglong Mee 	}
542471a930aSKinglong Mee 
543471a930aSKinglong Mee 	dprintk("RPC: %d entries in %s cache\n", detail->entries, detail->name);
544471a930aSKinglong Mee 	for (i = 0; i < detail->hash_size; i++) {
545471a930aSKinglong Mee 		head = &detail->hash_table[i];
54643e33924SYihao Wu 		while (!hlist_empty(head)) {
54743e33924SYihao Wu 			ch = hlist_entry(head->first, struct cache_head,
54843e33924SYihao Wu 					 cache_list);
549809fe3c5STrond Myklebust 			sunrpc_begin_cache_remove_entry(ch, detail);
5501863d77fSTrond Myklebust 			spin_unlock(&detail->hash_lock);
551809fe3c5STrond Myklebust 			sunrpc_end_cache_remove_entry(ch, detail);
5521863d77fSTrond Myklebust 			spin_lock(&detail->hash_lock);
553471a930aSKinglong Mee 		}
554471a930aSKinglong Mee 	}
5551863d77fSTrond Myklebust 	spin_unlock(&detail->hash_lock);
5561da177e4SLinus Torvalds }
55724c3767eSTrond Myklebust EXPORT_SYMBOL_GPL(cache_purge);
5581da177e4SLinus Torvalds 
5591da177e4SLinus Torvalds 
5601da177e4SLinus Torvalds /*
5611da177e4SLinus Torvalds  * Deferral and Revisiting of Requests.
5621da177e4SLinus Torvalds  *
5631da177e4SLinus Torvalds  * If a cache lookup finds a pending entry, we
5641da177e4SLinus Torvalds  * need to defer the request and revisit it later.
5651da177e4SLinus Torvalds  * All deferred requests are stored in a hash table,
5661da177e4SLinus Torvalds  * indexed by "struct cache_head *".
5671da177e4SLinus Torvalds  * As it may be wasteful to store a whole request
5681da177e4SLinus Torvalds  * structure, we allow the request to provide a
5691da177e4SLinus Torvalds  * deferred form, which must contain a
5701da177e4SLinus Torvalds  * 'struct cache_deferred_req'
5711da177e4SLinus Torvalds  * This cache_deferred_req contains a method to allow
5721da177e4SLinus Torvalds  * it to be revisited when cache info is available
5731da177e4SLinus Torvalds  */
5741da177e4SLinus Torvalds 
5751da177e4SLinus Torvalds #define	DFR_HASHSIZE	(PAGE_SIZE/sizeof(struct list_head))
5761da177e4SLinus Torvalds #define	DFR_HASH(item)	((((long)item)>>4 ^ (((long)item)>>13)) % DFR_HASHSIZE)
5771da177e4SLinus Torvalds 
5781da177e4SLinus Torvalds #define	DFR_MAX	300	/* ??? */
5791da177e4SLinus Torvalds 
5801da177e4SLinus Torvalds static DEFINE_SPINLOCK(cache_defer_lock);
5811da177e4SLinus Torvalds static LIST_HEAD(cache_defer_list);
58211174492SNeilBrown static struct hlist_head cache_defer_hash[DFR_HASHSIZE];
5831da177e4SLinus Torvalds static int cache_defer_cnt;
5841da177e4SLinus Torvalds 
__unhash_deferred_req(struct cache_deferred_req * dreq)5856610f720SJ. Bruce Fields static void __unhash_deferred_req(struct cache_deferred_req *dreq)
5861da177e4SLinus Torvalds {
58711174492SNeilBrown 	hlist_del_init(&dreq->hash);
588e33534d5SNeilBrown 	if (!list_empty(&dreq->recent)) {
589e33534d5SNeilBrown 		list_del_init(&dreq->recent);
5906610f720SJ. Bruce Fields 		cache_defer_cnt--;
5916610f720SJ. Bruce Fields 	}
592e33534d5SNeilBrown }
5936610f720SJ. Bruce Fields 
__hash_deferred_req(struct cache_deferred_req * dreq,struct cache_head * item)5946610f720SJ. Bruce Fields static void __hash_deferred_req(struct cache_deferred_req *dreq, struct cache_head *item)
5956610f720SJ. Bruce Fields {
5961da177e4SLinus Torvalds 	int hash = DFR_HASH(item);
5971da177e4SLinus Torvalds 
598e33534d5SNeilBrown 	INIT_LIST_HEAD(&dreq->recent);
59911174492SNeilBrown 	hlist_add_head(&dreq->hash, &cache_defer_hash[hash]);
60001f3bd1fSJ.Bruce Fields }
6016610f720SJ. Bruce Fields 
setup_deferral(struct cache_deferred_req * dreq,struct cache_head * item,int count_me)602e33534d5SNeilBrown static void setup_deferral(struct cache_deferred_req *dreq,
603e33534d5SNeilBrown 			   struct cache_head *item,
604e33534d5SNeilBrown 			   int count_me)
6051da177e4SLinus Torvalds {
6061da177e4SLinus Torvalds 
6071da177e4SLinus Torvalds 	dreq->item = item;
6081da177e4SLinus Torvalds 
6091da177e4SLinus Torvalds 	spin_lock(&cache_defer_lock);
6101da177e4SLinus Torvalds 
6116610f720SJ. Bruce Fields 	__hash_deferred_req(dreq, item);
6121da177e4SLinus Torvalds 
613e33534d5SNeilBrown 	if (count_me) {
614e33534d5SNeilBrown 		cache_defer_cnt++;
6151da177e4SLinus Torvalds 		list_add(&dreq->recent, &cache_defer_list);
6161da177e4SLinus Torvalds 	}
617e33534d5SNeilBrown 
6181da177e4SLinus Torvalds 	spin_unlock(&cache_defer_lock);
6191da177e4SLinus Torvalds 
6201da177e4SLinus Torvalds }
621f16b6e8dSNeilBrown 
6223211af11SJ. Bruce Fields struct thread_deferred_req {
6233211af11SJ. Bruce Fields 	struct cache_deferred_req handle;
6243211af11SJ. Bruce Fields 	struct completion completion;
6253211af11SJ. Bruce Fields };
6263211af11SJ. Bruce Fields 
cache_restart_thread(struct cache_deferred_req * dreq,int too_many)6273211af11SJ. Bruce Fields static void cache_restart_thread(struct cache_deferred_req *dreq, int too_many)
6283211af11SJ. Bruce Fields {
6293211af11SJ. Bruce Fields 	struct thread_deferred_req *dr =
6303211af11SJ. Bruce Fields 		container_of(dreq, struct thread_deferred_req, handle);
6313211af11SJ. Bruce Fields 	complete(&dr->completion);
6323211af11SJ. Bruce Fields }
6333211af11SJ. Bruce Fields 
cache_wait_req(struct cache_req * req,struct cache_head * item)634d29068c4SNeilBrown static void cache_wait_req(struct cache_req *req, struct cache_head *item)
6353211af11SJ. Bruce Fields {
6363211af11SJ. Bruce Fields 	struct thread_deferred_req sleeper;
6373211af11SJ. Bruce Fields 	struct cache_deferred_req *dreq = &sleeper.handle;
6383211af11SJ. Bruce Fields 
6393211af11SJ. Bruce Fields 	sleeper.completion = COMPLETION_INITIALIZER_ONSTACK(sleeper.completion);
6403211af11SJ. Bruce Fields 	dreq->revisit = cache_restart_thread;
6413211af11SJ. Bruce Fields 
642e33534d5SNeilBrown 	setup_deferral(dreq, item, 0);
6433211af11SJ. Bruce Fields 
644d29068c4SNeilBrown 	if (!test_bit(CACHE_PENDING, &item->flags) ||
645277f68dbSNeilBrown 	    wait_for_completion_interruptible_timeout(
646f16b6e8dSNeilBrown 		    &sleeper.completion, req->thread_wait) <= 0) {
647f16b6e8dSNeilBrown 		/* The completion wasn't completed, so we need
648f16b6e8dSNeilBrown 		 * to clean up
649f16b6e8dSNeilBrown 		 */
650f16b6e8dSNeilBrown 		spin_lock(&cache_defer_lock);
65111174492SNeilBrown 		if (!hlist_unhashed(&sleeper.handle.hash)) {
6526610f720SJ. Bruce Fields 			__unhash_deferred_req(&sleeper.handle);
653f16b6e8dSNeilBrown 			spin_unlock(&cache_defer_lock);
654f16b6e8dSNeilBrown 		} else {
655f16b6e8dSNeilBrown 			/* cache_revisit_request already removed
656f16b6e8dSNeilBrown 			 * this from the hash table, but hasn't
657f16b6e8dSNeilBrown 			 * called ->revisit yet.  It will very soon
658f16b6e8dSNeilBrown 			 * and we need to wait for it.
659f16b6e8dSNeilBrown 			 */
660f16b6e8dSNeilBrown 			spin_unlock(&cache_defer_lock);
661f16b6e8dSNeilBrown 			wait_for_completion(&sleeper.completion);
662f16b6e8dSNeilBrown 		}
663f16b6e8dSNeilBrown 	}
664f16b6e8dSNeilBrown }
6653211af11SJ. Bruce Fields 
cache_limit_defers(void)666e33534d5SNeilBrown static void cache_limit_defers(void)
667e33534d5SNeilBrown {
668e33534d5SNeilBrown 	/* Make sure we haven't exceed the limit of allowed deferred
669e33534d5SNeilBrown 	 * requests.
670e33534d5SNeilBrown 	 */
671e33534d5SNeilBrown 	struct cache_deferred_req *discard = NULL;
672e33534d5SNeilBrown 
673e33534d5SNeilBrown 	if (cache_defer_cnt <= DFR_MAX)
674e33534d5SNeilBrown 		return;
675e33534d5SNeilBrown 
676e33534d5SNeilBrown 	spin_lock(&cache_defer_lock);
677e33534d5SNeilBrown 
678e33534d5SNeilBrown 	/* Consider removing either the first or the last */
679e33534d5SNeilBrown 	if (cache_defer_cnt > DFR_MAX) {
6808032bf12SJason A. Donenfeld 		if (get_random_u32_below(2))
681e33534d5SNeilBrown 			discard = list_entry(cache_defer_list.next,
682e33534d5SNeilBrown 					     struct cache_deferred_req, recent);
683e33534d5SNeilBrown 		else
684e33534d5SNeilBrown 			discard = list_entry(cache_defer_list.prev,
685e33534d5SNeilBrown 					     struct cache_deferred_req, recent);
686e33534d5SNeilBrown 		__unhash_deferred_req(discard);
687e33534d5SNeilBrown 	}
688e33534d5SNeilBrown 	spin_unlock(&cache_defer_lock);
689e33534d5SNeilBrown 	if (discard)
690e33534d5SNeilBrown 		discard->revisit(discard, 1);
691e33534d5SNeilBrown }
692e33534d5SNeilBrown 
69337324e6bSChuck Lever #if IS_ENABLED(CONFIG_FAIL_SUNRPC)
cache_defer_immediately(void)69437324e6bSChuck Lever static inline bool cache_defer_immediately(void)
69537324e6bSChuck Lever {
69637324e6bSChuck Lever 	return !fail_sunrpc.ignore_cache_wait &&
69737324e6bSChuck Lever 		should_fail(&fail_sunrpc.attr, 1);
69837324e6bSChuck Lever }
69937324e6bSChuck Lever #else
cache_defer_immediately(void)70037324e6bSChuck Lever static inline bool cache_defer_immediately(void)
70137324e6bSChuck Lever {
70237324e6bSChuck Lever 	return false;
70337324e6bSChuck Lever }
70437324e6bSChuck Lever #endif
70537324e6bSChuck Lever 
706d76d1815SJ. Bruce Fields /* Return true if and only if a deferred request is queued. */
cache_defer_req(struct cache_req * req,struct cache_head * item)707d76d1815SJ. Bruce Fields static bool cache_defer_req(struct cache_req *req, struct cache_head *item)
7083211af11SJ. Bruce Fields {
7093211af11SJ. Bruce Fields 	struct cache_deferred_req *dreq;
7103211af11SJ. Bruce Fields 
71137324e6bSChuck Lever 	if (!cache_defer_immediately()) {
712d29068c4SNeilBrown 		cache_wait_req(req, item);
713d29068c4SNeilBrown 		if (!test_bit(CACHE_PENDING, &item->flags))
714d76d1815SJ. Bruce Fields 			return false;
7153211af11SJ. Bruce Fields 	}
71637324e6bSChuck Lever 
7173211af11SJ. Bruce Fields 	dreq = req->defer(req);
7183211af11SJ. Bruce Fields 	if (dreq == NULL)
719d76d1815SJ. Bruce Fields 		return false;
720e33534d5SNeilBrown 	setup_deferral(dreq, item, 1);
721d29068c4SNeilBrown 	if (!test_bit(CACHE_PENDING, &item->flags))
722d29068c4SNeilBrown 		/* Bit could have been cleared before we managed to
723d29068c4SNeilBrown 		 * set up the deferral, so need to revisit just in case
724d29068c4SNeilBrown 		 */
725d29068c4SNeilBrown 		cache_revisit_request(item);
726e33534d5SNeilBrown 
727e33534d5SNeilBrown 	cache_limit_defers();
728d76d1815SJ. Bruce Fields 	return true;
729989a19b9SNeilBrown }
7301da177e4SLinus Torvalds 
cache_revisit_request(struct cache_head * item)7311da177e4SLinus Torvalds static void cache_revisit_request(struct cache_head *item)
7321da177e4SLinus Torvalds {
7331da177e4SLinus Torvalds 	struct cache_deferred_req *dreq;
734b67bfe0dSSasha Levin 	struct hlist_node *tmp;
7351da177e4SLinus Torvalds 	int hash = DFR_HASH(item);
736*64a3ab99SHongbo Li 	LIST_HEAD(pending);
7371da177e4SLinus Torvalds 
7381da177e4SLinus Torvalds 	spin_lock(&cache_defer_lock);
7391da177e4SLinus Torvalds 
740b67bfe0dSSasha Levin 	hlist_for_each_entry_safe(dreq, tmp, &cache_defer_hash[hash], hash)
7411da177e4SLinus Torvalds 		if (dreq->item == item) {
7426610f720SJ. Bruce Fields 			__unhash_deferred_req(dreq);
7436610f720SJ. Bruce Fields 			list_add(&dreq->recent, &pending);
7441da177e4SLinus Torvalds 		}
74511174492SNeilBrown 
7461da177e4SLinus Torvalds 	spin_unlock(&cache_defer_lock);
7471da177e4SLinus Torvalds 
7481da177e4SLinus Torvalds 	while (!list_empty(&pending)) {
7491da177e4SLinus Torvalds 		dreq = list_entry(pending.next, struct cache_deferred_req, recent);
7501da177e4SLinus Torvalds 		list_del_init(&dreq->recent);
7511da177e4SLinus Torvalds 		dreq->revisit(dreq, 0);
7521da177e4SLinus Torvalds 	}
7531da177e4SLinus Torvalds }
7541da177e4SLinus Torvalds 
cache_clean_deferred(void * owner)7551da177e4SLinus Torvalds void cache_clean_deferred(void *owner)
7561da177e4SLinus Torvalds {
7571da177e4SLinus Torvalds 	struct cache_deferred_req *dreq, *tmp;
758*64a3ab99SHongbo Li 	LIST_HEAD(pending);
7591da177e4SLinus Torvalds 
7601da177e4SLinus Torvalds 	spin_lock(&cache_defer_lock);
7611da177e4SLinus Torvalds 
7621da177e4SLinus Torvalds 	list_for_each_entry_safe(dreq, tmp, &cache_defer_list, recent) {
7631da177e4SLinus Torvalds 		if (dreq->owner == owner) {
7646610f720SJ. Bruce Fields 			__unhash_deferred_req(dreq);
765e95dffa4SNeilBrown 			list_add(&dreq->recent, &pending);
7661da177e4SLinus Torvalds 		}
7671da177e4SLinus Torvalds 	}
7681da177e4SLinus Torvalds 	spin_unlock(&cache_defer_lock);
7691da177e4SLinus Torvalds 
7701da177e4SLinus Torvalds 	while (!list_empty(&pending)) {
7711da177e4SLinus Torvalds 		dreq = list_entry(pending.next, struct cache_deferred_req, recent);
7721da177e4SLinus Torvalds 		list_del_init(&dreq->recent);
7731da177e4SLinus Torvalds 		dreq->revisit(dreq, 1);
7741da177e4SLinus Torvalds 	}
7751da177e4SLinus Torvalds }
7761da177e4SLinus Torvalds 
7771da177e4SLinus Torvalds /*
7781da177e4SLinus Torvalds  * communicate with user-space
7791da177e4SLinus Torvalds  *
7806489a8f4SKinglong Mee  * We have a magic /proc file - /proc/net/rpc/<cachename>/channel.
781a490c681SJ. Bruce Fields  * On read, you get a full request, or block.
782a490c681SJ. Bruce Fields  * On write, an update request is processed.
783a490c681SJ. Bruce Fields  * Poll works if anything to read, and always allows write.
7841da177e4SLinus Torvalds  *
7851da177e4SLinus Torvalds  * Implemented by linked list of requests.  Each open file has
786a490c681SJ. Bruce Fields  * a ->private that also exists in this list.  New requests are added
7871da177e4SLinus Torvalds  * to the end and may wakeup and preceding readers.
7881da177e4SLinus Torvalds  * New readers are added to the head.  If, on read, an item is found with
7891da177e4SLinus Torvalds  * CACHE_UPCALLING clear, we free it from the list.
7901da177e4SLinus Torvalds  *
7911da177e4SLinus Torvalds  */
7921da177e4SLinus Torvalds 
7931da177e4SLinus Torvalds static DEFINE_SPINLOCK(queue_lock);
7941da177e4SLinus Torvalds 
7951da177e4SLinus Torvalds struct cache_queue {
7961da177e4SLinus Torvalds 	struct list_head	list;
7971da177e4SLinus Torvalds 	int			reader;	/* if 0, then request */
7981da177e4SLinus Torvalds };
7991da177e4SLinus Torvalds struct cache_request {
8001da177e4SLinus Torvalds 	struct cache_queue	q;
8011da177e4SLinus Torvalds 	struct cache_head	*item;
8021da177e4SLinus Torvalds 	char			* buf;
8031da177e4SLinus Torvalds 	int			len;
8041da177e4SLinus Torvalds 	int			readers;
8051da177e4SLinus Torvalds };
8061da177e4SLinus Torvalds struct cache_reader {
8071da177e4SLinus Torvalds 	struct cache_queue	q;
8081da177e4SLinus Torvalds 	int			offset;	/* if non-0, we have a refcnt on next request */
8091da177e4SLinus Torvalds };
8101da177e4SLinus Torvalds 
cache_request(struct cache_detail * detail,struct cache_request * crq)811d94af6deSStanislav Kinsbursky static int cache_request(struct cache_detail *detail,
812d94af6deSStanislav Kinsbursky 			       struct cache_request *crq)
813d94af6deSStanislav Kinsbursky {
814d94af6deSStanislav Kinsbursky 	char *bp = crq->buf;
815d94af6deSStanislav Kinsbursky 	int len = PAGE_SIZE;
816d94af6deSStanislav Kinsbursky 
817d94af6deSStanislav Kinsbursky 	detail->cache_request(detail, crq->item, &bp, &len);
818d94af6deSStanislav Kinsbursky 	if (len < 0)
8190c217d50SNeilBrown 		return -E2BIG;
820d94af6deSStanislav Kinsbursky 	return PAGE_SIZE - len;
821d94af6deSStanislav Kinsbursky }
822d94af6deSStanislav Kinsbursky 
cache_read(struct file * filp,char __user * buf,size_t count,loff_t * ppos,struct cache_detail * cd)823173912a6STrond Myklebust static ssize_t cache_read(struct file *filp, char __user *buf, size_t count,
824173912a6STrond Myklebust 			  loff_t *ppos, struct cache_detail *cd)
8251da177e4SLinus Torvalds {
8261da177e4SLinus Torvalds 	struct cache_reader *rp = filp->private_data;
8271da177e4SLinus Torvalds 	struct cache_request *rq;
828496ad9aaSAl Viro 	struct inode *inode = file_inode(filp);
8291da177e4SLinus Torvalds 	int err;
8301da177e4SLinus Torvalds 
8311da177e4SLinus Torvalds 	if (count == 0)
8321da177e4SLinus Torvalds 		return 0;
8331da177e4SLinus Torvalds 
8345955102cSAl Viro 	inode_lock(inode); /* protect against multiple concurrent
8351da177e4SLinus Torvalds 			      * readers on this file */
8361da177e4SLinus Torvalds  again:
8371da177e4SLinus Torvalds 	spin_lock(&queue_lock);
8381da177e4SLinus Torvalds 	/* need to find next request */
8391da177e4SLinus Torvalds 	while (rp->q.list.next != &cd->queue &&
8401da177e4SLinus Torvalds 	       list_entry(rp->q.list.next, struct cache_queue, list)
8411da177e4SLinus Torvalds 	       ->reader) {
8421da177e4SLinus Torvalds 		struct list_head *next = rp->q.list.next;
8431da177e4SLinus Torvalds 		list_move(&rp->q.list, next);
8441da177e4SLinus Torvalds 	}
8451da177e4SLinus Torvalds 	if (rp->q.list.next == &cd->queue) {
8461da177e4SLinus Torvalds 		spin_unlock(&queue_lock);
8475955102cSAl Viro 		inode_unlock(inode);
8480db74d9aSWeston Andros Adamson 		WARN_ON_ONCE(rp->offset);
8491da177e4SLinus Torvalds 		return 0;
8501da177e4SLinus Torvalds 	}
8511da177e4SLinus Torvalds 	rq = container_of(rp->q.list.next, struct cache_request, q.list);
8520db74d9aSWeston Andros Adamson 	WARN_ON_ONCE(rq->q.reader);
8531da177e4SLinus Torvalds 	if (rp->offset == 0)
8541da177e4SLinus Torvalds 		rq->readers++;
8551da177e4SLinus Torvalds 	spin_unlock(&queue_lock);
8561da177e4SLinus Torvalds 
857d94af6deSStanislav Kinsbursky 	if (rq->len == 0) {
858d94af6deSStanislav Kinsbursky 		err = cache_request(cd, rq);
859d94af6deSStanislav Kinsbursky 		if (err < 0)
860d94af6deSStanislav Kinsbursky 			goto out;
861d94af6deSStanislav Kinsbursky 		rq->len = err;
862d94af6deSStanislav Kinsbursky 	}
863d94af6deSStanislav Kinsbursky 
8641da177e4SLinus Torvalds 	if (rp->offset == 0 && !test_bit(CACHE_PENDING, &rq->item->flags)) {
8651da177e4SLinus Torvalds 		err = -EAGAIN;
8661da177e4SLinus Torvalds 		spin_lock(&queue_lock);
8671da177e4SLinus Torvalds 		list_move(&rp->q.list, &rq->q.list);
8681da177e4SLinus Torvalds 		spin_unlock(&queue_lock);
8691da177e4SLinus Torvalds 	} else {
8701da177e4SLinus Torvalds 		if (rp->offset + count > rq->len)
8711da177e4SLinus Torvalds 			count = rq->len - rp->offset;
8721da177e4SLinus Torvalds 		err = -EFAULT;
8731da177e4SLinus Torvalds 		if (copy_to_user(buf, rq->buf + rp->offset, count))
8741da177e4SLinus Torvalds 			goto out;
8751da177e4SLinus Torvalds 		rp->offset += count;
8761da177e4SLinus Torvalds 		if (rp->offset >= rq->len) {
8771da177e4SLinus Torvalds 			rp->offset = 0;
8781da177e4SLinus Torvalds 			spin_lock(&queue_lock);
8791da177e4SLinus Torvalds 			list_move(&rp->q.list, &rq->q.list);
8801da177e4SLinus Torvalds 			spin_unlock(&queue_lock);
8811da177e4SLinus Torvalds 		}
8821da177e4SLinus Torvalds 		err = 0;
8831da177e4SLinus Torvalds 	}
8841da177e4SLinus Torvalds  out:
8851da177e4SLinus Torvalds 	if (rp->offset == 0) {
8861da177e4SLinus Torvalds 		/* need to release rq */
8871da177e4SLinus Torvalds 		spin_lock(&queue_lock);
8881da177e4SLinus Torvalds 		rq->readers--;
8891da177e4SLinus Torvalds 		if (rq->readers == 0 &&
8901da177e4SLinus Torvalds 		    !test_bit(CACHE_PENDING, &rq->item->flags)) {
8911da177e4SLinus Torvalds 			list_del(&rq->q.list);
8921da177e4SLinus Torvalds 			spin_unlock(&queue_lock);
893baab935fSNeilBrown 			cache_put(rq->item, cd);
8941da177e4SLinus Torvalds 			kfree(rq->buf);
8951da177e4SLinus Torvalds 			kfree(rq);
8961da177e4SLinus Torvalds 		} else
8971da177e4SLinus Torvalds 			spin_unlock(&queue_lock);
8981da177e4SLinus Torvalds 	}
8991da177e4SLinus Torvalds 	if (err == -EAGAIN)
9001da177e4SLinus Torvalds 		goto again;
9015955102cSAl Viro 	inode_unlock(inode);
9021da177e4SLinus Torvalds 	return err ? err :  count;
9031da177e4SLinus Torvalds }
9041da177e4SLinus Torvalds 
cache_do_downcall(char * kaddr,const char __user * buf,size_t count,struct cache_detail * cd)905da77005fSTrond Myklebust static ssize_t cache_do_downcall(char *kaddr, const char __user *buf,
906da77005fSTrond Myklebust 				 size_t count, struct cache_detail *cd)
9071da177e4SLinus Torvalds {
908da77005fSTrond Myklebust 	ssize_t ret;
9091da177e4SLinus Torvalds 
9106d8d1749SDan Carpenter 	if (count == 0)
9116d8d1749SDan Carpenter 		return -EINVAL;
912da77005fSTrond Myklebust 	if (copy_from_user(kaddr, buf, count))
9131da177e4SLinus Torvalds 		return -EFAULT;
914da77005fSTrond Myklebust 	kaddr[count] = '\0';
915da77005fSTrond Myklebust 	ret = cd->cache_parse(cd, kaddr, count);
916da77005fSTrond Myklebust 	if (!ret)
917da77005fSTrond Myklebust 		ret = count;
918da77005fSTrond Myklebust 	return ret;
9191da177e4SLinus Torvalds }
9201da177e4SLinus Torvalds 
cache_downcall(struct address_space * mapping,const char __user * buf,size_t count,struct cache_detail * cd)921da77005fSTrond Myklebust static ssize_t cache_downcall(struct address_space *mapping,
922da77005fSTrond Myklebust 			      const char __user *buf,
923da77005fSTrond Myklebust 			      size_t count, struct cache_detail *cd)
924da77005fSTrond Myklebust {
9254b5cff7eSRoberto Bergantinos Corpas 	char *write_buf;
926da77005fSTrond Myklebust 	ssize_t ret = -ENOMEM;
927da77005fSTrond Myklebust 
9284b5cff7eSRoberto Bergantinos Corpas 	if (count >= 32768) { /* 32k is max userland buffer, lets check anyway */
9294b5cff7eSRoberto Bergantinos Corpas 		ret = -EINVAL;
9304b5cff7eSRoberto Bergantinos Corpas 		goto out;
9314b5cff7eSRoberto Bergantinos Corpas 	}
932da77005fSTrond Myklebust 
9334b5cff7eSRoberto Bergantinos Corpas 	write_buf = kvmalloc(count + 1, GFP_KERNEL);
9344b5cff7eSRoberto Bergantinos Corpas 	if (!write_buf)
9354b5cff7eSRoberto Bergantinos Corpas 		goto out;
936da77005fSTrond Myklebust 
9374b5cff7eSRoberto Bergantinos Corpas 	ret = cache_do_downcall(write_buf, buf, count, cd);
9384b5cff7eSRoberto Bergantinos Corpas 	kvfree(write_buf);
9394b5cff7eSRoberto Bergantinos Corpas out:
940da77005fSTrond Myklebust 	return ret;
941da77005fSTrond Myklebust }
9421da177e4SLinus Torvalds 
cache_write(struct file * filp,const char __user * buf,size_t count,loff_t * ppos,struct cache_detail * cd)943173912a6STrond Myklebust static ssize_t cache_write(struct file *filp, const char __user *buf,
944173912a6STrond Myklebust 			   size_t count, loff_t *ppos,
945173912a6STrond Myklebust 			   struct cache_detail *cd)
9461da177e4SLinus Torvalds {
947da77005fSTrond Myklebust 	struct address_space *mapping = filp->f_mapping;
948496ad9aaSAl Viro 	struct inode *inode = file_inode(filp);
949da77005fSTrond Myklebust 	ssize_t ret = -EINVAL;
9501da177e4SLinus Torvalds 
951da77005fSTrond Myklebust 	if (!cd->cache_parse)
952da77005fSTrond Myklebust 		goto out;
9531da177e4SLinus Torvalds 
9545955102cSAl Viro 	inode_lock(inode);
955da77005fSTrond Myklebust 	ret = cache_downcall(mapping, buf, count, cd);
9565955102cSAl Viro 	inode_unlock(inode);
957da77005fSTrond Myklebust out:
958da77005fSTrond Myklebust 	return ret;
9591da177e4SLinus Torvalds }
9601da177e4SLinus Torvalds 
9611da177e4SLinus Torvalds static DECLARE_WAIT_QUEUE_HEAD(queue_wait);
9621da177e4SLinus Torvalds 
cache_poll(struct file * filp,poll_table * wait,struct cache_detail * cd)963ade994f4SAl Viro static __poll_t cache_poll(struct file *filp, poll_table *wait,
964173912a6STrond Myklebust 			       struct cache_detail *cd)
9651da177e4SLinus Torvalds {
966ade994f4SAl Viro 	__poll_t mask;
9671da177e4SLinus Torvalds 	struct cache_reader *rp = filp->private_data;
9681da177e4SLinus Torvalds 	struct cache_queue *cq;
9691da177e4SLinus Torvalds 
9701da177e4SLinus Torvalds 	poll_wait(filp, &queue_wait, wait);
9711da177e4SLinus Torvalds 
9721da177e4SLinus Torvalds 	/* alway allow write */
973a9a08845SLinus Torvalds 	mask = EPOLLOUT | EPOLLWRNORM;
9741da177e4SLinus Torvalds 
9751da177e4SLinus Torvalds 	if (!rp)
9761da177e4SLinus Torvalds 		return mask;
9771da177e4SLinus Torvalds 
9781da177e4SLinus Torvalds 	spin_lock(&queue_lock);
9791da177e4SLinus Torvalds 
9801da177e4SLinus Torvalds 	for (cq= &rp->q; &cq->list != &cd->queue;
9811da177e4SLinus Torvalds 	     cq = list_entry(cq->list.next, struct cache_queue, list))
9821da177e4SLinus Torvalds 		if (!cq->reader) {
983a9a08845SLinus Torvalds 			mask |= EPOLLIN | EPOLLRDNORM;
9841da177e4SLinus Torvalds 			break;
9851da177e4SLinus Torvalds 		}
9861da177e4SLinus Torvalds 	spin_unlock(&queue_lock);
9871da177e4SLinus Torvalds 	return mask;
9881da177e4SLinus Torvalds }
9891da177e4SLinus Torvalds 
cache_ioctl(struct inode * ino,struct file * filp,unsigned int cmd,unsigned long arg,struct cache_detail * cd)990173912a6STrond Myklebust static int cache_ioctl(struct inode *ino, struct file *filp,
991173912a6STrond Myklebust 		       unsigned int cmd, unsigned long arg,
992173912a6STrond Myklebust 		       struct cache_detail *cd)
9931da177e4SLinus Torvalds {
9941da177e4SLinus Torvalds 	int len = 0;
9951da177e4SLinus Torvalds 	struct cache_reader *rp = filp->private_data;
9961da177e4SLinus Torvalds 	struct cache_queue *cq;
9971da177e4SLinus Torvalds 
9981da177e4SLinus Torvalds 	if (cmd != FIONREAD || !rp)
9991da177e4SLinus Torvalds 		return -EINVAL;
10001da177e4SLinus Torvalds 
10011da177e4SLinus Torvalds 	spin_lock(&queue_lock);
10021da177e4SLinus Torvalds 
10031da177e4SLinus Torvalds 	/* only find the length remaining in current request,
10041da177e4SLinus Torvalds 	 * or the length of the next request
10051da177e4SLinus Torvalds 	 */
10061da177e4SLinus Torvalds 	for (cq= &rp->q; &cq->list != &cd->queue;
10071da177e4SLinus Torvalds 	     cq = list_entry(cq->list.next, struct cache_queue, list))
10081da177e4SLinus Torvalds 		if (!cq->reader) {
10091da177e4SLinus Torvalds 			struct cache_request *cr =
10101da177e4SLinus Torvalds 				container_of(cq, struct cache_request, q);
10111da177e4SLinus Torvalds 			len = cr->len - rp->offset;
10121da177e4SLinus Torvalds 			break;
10131da177e4SLinus Torvalds 		}
10141da177e4SLinus Torvalds 	spin_unlock(&queue_lock);
10151da177e4SLinus Torvalds 
10161da177e4SLinus Torvalds 	return put_user(len, (int __user *)arg);
10171da177e4SLinus Torvalds }
10181da177e4SLinus Torvalds 
cache_open(struct inode * inode,struct file * filp,struct cache_detail * cd)1019173912a6STrond Myklebust static int cache_open(struct inode *inode, struct file *filp,
1020173912a6STrond Myklebust 		      struct cache_detail *cd)
10211da177e4SLinus Torvalds {
10221da177e4SLinus Torvalds 	struct cache_reader *rp = NULL;
10231da177e4SLinus Torvalds 
1024f7e86ab9STrond Myklebust 	if (!cd || !try_module_get(cd->owner))
1025f7e86ab9STrond Myklebust 		return -EACCES;
10261da177e4SLinus Torvalds 	nonseekable_open(inode, filp);
10271da177e4SLinus Torvalds 	if (filp->f_mode & FMODE_READ) {
10281da177e4SLinus Torvalds 		rp = kmalloc(sizeof(*rp), GFP_KERNEL);
1029a7823c79SAlexey Khoroshilov 		if (!rp) {
1030a7823c79SAlexey Khoroshilov 			module_put(cd->owner);
10311da177e4SLinus Torvalds 			return -ENOMEM;
1032a7823c79SAlexey Khoroshilov 		}
10331da177e4SLinus Torvalds 		rp->offset = 0;
10341da177e4SLinus Torvalds 		rp->q.reader = 1;
103564a38e84SDave Wysochanski 
10361da177e4SLinus Torvalds 		spin_lock(&queue_lock);
10371da177e4SLinus Torvalds 		list_add(&rp->q.list, &cd->queue);
10381da177e4SLinus Torvalds 		spin_unlock(&queue_lock);
10391da177e4SLinus Torvalds 	}
104064a38e84SDave Wysochanski 	if (filp->f_mode & FMODE_WRITE)
104164a38e84SDave Wysochanski 		atomic_inc(&cd->writers);
10421da177e4SLinus Torvalds 	filp->private_data = rp;
10431da177e4SLinus Torvalds 	return 0;
10441da177e4SLinus Torvalds }
10451da177e4SLinus Torvalds 
cache_release(struct inode * inode,struct file * filp,struct cache_detail * cd)1046173912a6STrond Myklebust static int cache_release(struct inode *inode, struct file *filp,
1047173912a6STrond Myklebust 			 struct cache_detail *cd)
10481da177e4SLinus Torvalds {
10491da177e4SLinus Torvalds 	struct cache_reader *rp = filp->private_data;
10501da177e4SLinus Torvalds 
10511da177e4SLinus Torvalds 	if (rp) {
10521da177e4SLinus Torvalds 		spin_lock(&queue_lock);
10531da177e4SLinus Torvalds 		if (rp->offset) {
10541da177e4SLinus Torvalds 			struct cache_queue *cq;
10551da177e4SLinus Torvalds 			for (cq= &rp->q; &cq->list != &cd->queue;
10561da177e4SLinus Torvalds 			     cq = list_entry(cq->list.next, struct cache_queue, list))
10571da177e4SLinus Torvalds 				if (!cq->reader) {
10581da177e4SLinus Torvalds 					container_of(cq, struct cache_request, q)
10591da177e4SLinus Torvalds 						->readers--;
10601da177e4SLinus Torvalds 					break;
10611da177e4SLinus Torvalds 				}
10621da177e4SLinus Torvalds 			rp->offset = 0;
10631da177e4SLinus Torvalds 		}
10641da177e4SLinus Torvalds 		list_del(&rp->q.list);
10651da177e4SLinus Torvalds 		spin_unlock(&queue_lock);
10661da177e4SLinus Torvalds 
10671da177e4SLinus Torvalds 		filp->private_data = NULL;
10681da177e4SLinus Torvalds 		kfree(rp);
10691da177e4SLinus Torvalds 
107064a38e84SDave Wysochanski 	}
107164a38e84SDave Wysochanski 	if (filp->f_mode & FMODE_WRITE) {
107264a38e84SDave Wysochanski 		atomic_dec(&cd->writers);
1073c5b29f88SNeilBrown 		cd->last_close = seconds_since_boot();
10741da177e4SLinus Torvalds 	}
1075f7e86ab9STrond Myklebust 	module_put(cd->owner);
10761da177e4SLinus Torvalds 	return 0;
10771da177e4SLinus Torvalds }
10781da177e4SLinus Torvalds 
10791da177e4SLinus Torvalds 
10801da177e4SLinus Torvalds 
cache_dequeue(struct cache_detail * detail,struct cache_head * ch)1081f866a819SNeilBrown static void cache_dequeue(struct cache_detail *detail, struct cache_head *ch)
10821da177e4SLinus Torvalds {
1083f9e1aedcSNeilBrown 	struct cache_queue *cq, *tmp;
1084f9e1aedcSNeilBrown 	struct cache_request *cr;
1085*64a3ab99SHongbo Li 	LIST_HEAD(dequeued);
1086f9e1aedcSNeilBrown 
10871da177e4SLinus Torvalds 	spin_lock(&queue_lock);
1088f9e1aedcSNeilBrown 	list_for_each_entry_safe(cq, tmp, &detail->queue, list)
10891da177e4SLinus Torvalds 		if (!cq->reader) {
1090f9e1aedcSNeilBrown 			cr = container_of(cq, struct cache_request, q);
10911da177e4SLinus Torvalds 			if (cr->item != ch)
10921da177e4SLinus Torvalds 				continue;
1093f9e1aedcSNeilBrown 			if (test_bit(CACHE_PENDING, &ch->flags))
1094f9e1aedcSNeilBrown 				/* Lost a race and it is pending again */
1095f9e1aedcSNeilBrown 				break;
10961da177e4SLinus Torvalds 			if (cr->readers != 0)
10974013edeaSNeilBrown 				continue;
1098f9e1aedcSNeilBrown 			list_move(&cr->q.list, &dequeued);
1099f9e1aedcSNeilBrown 		}
11001da177e4SLinus Torvalds 	spin_unlock(&queue_lock);
1101f9e1aedcSNeilBrown 	while (!list_empty(&dequeued)) {
1102f9e1aedcSNeilBrown 		cr = list_entry(dequeued.next, struct cache_request, q.list);
1103f9e1aedcSNeilBrown 		list_del(&cr->q.list);
1104baab935fSNeilBrown 		cache_put(cr->item, detail);
11051da177e4SLinus Torvalds 		kfree(cr->buf);
11061da177e4SLinus Torvalds 		kfree(cr);
11071da177e4SLinus Torvalds 	}
11081da177e4SLinus Torvalds }
11091da177e4SLinus Torvalds 
11101da177e4SLinus Torvalds /*
11111da177e4SLinus Torvalds  * Support routines for text-based upcalls.
11121da177e4SLinus Torvalds  * Fields are separated by spaces.
11131da177e4SLinus Torvalds  * Fields are either mangled to quote space tab newline slosh with slosh
11141da177e4SLinus Torvalds  * or a hexified with a leading \x
11151da177e4SLinus Torvalds  * Record is terminated with newline.
11161da177e4SLinus Torvalds  *
11171da177e4SLinus Torvalds  */
11181da177e4SLinus Torvalds 
qword_add(char ** bpp,int * lp,char * str)11191da177e4SLinus Torvalds void qword_add(char **bpp, int *lp, char *str)
11201da177e4SLinus Torvalds {
11211da177e4SLinus Torvalds 	char *bp = *bpp;
11221da177e4SLinus Torvalds 	int len = *lp;
11231b2e122dSAndy Shevchenko 	int ret;
11241da177e4SLinus Torvalds 
11251da177e4SLinus Torvalds 	if (len < 0) return;
11261da177e4SLinus Torvalds 
112741416f23SRasmus Villemoes 	ret = string_escape_str(str, bp, len, ESCAPE_OCTAL, "\\ \n\t");
112841416f23SRasmus Villemoes 	if (ret >= len) {
112941416f23SRasmus Villemoes 		bp += len;
11301b2e122dSAndy Shevchenko 		len = -1;
113141416f23SRasmus Villemoes 	} else {
113241416f23SRasmus Villemoes 		bp += ret;
11331b2e122dSAndy Shevchenko 		len -= ret;
11341da177e4SLinus Torvalds 		*bp++ = ' ';
11351da177e4SLinus Torvalds 		len--;
11361da177e4SLinus Torvalds 	}
11371da177e4SLinus Torvalds 	*bpp = bp;
11381da177e4SLinus Torvalds 	*lp = len;
11391da177e4SLinus Torvalds }
114024c3767eSTrond Myklebust EXPORT_SYMBOL_GPL(qword_add);
11411da177e4SLinus Torvalds 
qword_addhex(char ** bpp,int * lp,char * buf,int blen)11421da177e4SLinus Torvalds void qword_addhex(char **bpp, int *lp, char *buf, int blen)
11431da177e4SLinus Torvalds {
11441da177e4SLinus Torvalds 	char *bp = *bpp;
11451da177e4SLinus Torvalds 	int len = *lp;
11461da177e4SLinus Torvalds 
11471da177e4SLinus Torvalds 	if (len < 0) return;
11481da177e4SLinus Torvalds 
11491da177e4SLinus Torvalds 	if (len > 2) {
11501da177e4SLinus Torvalds 		*bp++ = '\\';
11511da177e4SLinus Torvalds 		*bp++ = 'x';
11521da177e4SLinus Torvalds 		len -= 2;
11531da177e4SLinus Torvalds 		while (blen && len >= 2) {
1154056785eaSAndy Shevchenko 			bp = hex_byte_pack(bp, *buf++);
11551da177e4SLinus Torvalds 			len -= 2;
11561da177e4SLinus Torvalds 			blen--;
11571da177e4SLinus Torvalds 		}
11581da177e4SLinus Torvalds 	}
11591da177e4SLinus Torvalds 	if (blen || len<1) len = -1;
11601da177e4SLinus Torvalds 	else {
11611da177e4SLinus Torvalds 		*bp++ = ' ';
11621da177e4SLinus Torvalds 		len--;
11631da177e4SLinus Torvalds 	}
11641da177e4SLinus Torvalds 	*bpp = bp;
11651da177e4SLinus Torvalds 	*lp = len;
11661da177e4SLinus Torvalds }
116724c3767eSTrond Myklebust EXPORT_SYMBOL_GPL(qword_addhex);
11681da177e4SLinus Torvalds 
warn_no_listener(struct cache_detail * detail)11691da177e4SLinus Torvalds static void warn_no_listener(struct cache_detail *detail)
11701da177e4SLinus Torvalds {
11711da177e4SLinus Torvalds 	if (detail->last_warn != detail->last_close) {
11721da177e4SLinus Torvalds 		detail->last_warn = detail->last_close;
11731da177e4SLinus Torvalds 		if (detail->warn_no_listener)
11742da8ca26STrond Myklebust 			detail->warn_no_listener(detail, detail->last_close != 0);
11751da177e4SLinus Torvalds 	}
11761da177e4SLinus Torvalds }
11771da177e4SLinus Torvalds 
cache_listeners_exist(struct cache_detail * detail)117806497524SJ. Bruce Fields static bool cache_listeners_exist(struct cache_detail *detail)
117906497524SJ. Bruce Fields {
118064a38e84SDave Wysochanski 	if (atomic_read(&detail->writers))
118106497524SJ. Bruce Fields 		return true;
118206497524SJ. Bruce Fields 	if (detail->last_close == 0)
118306497524SJ. Bruce Fields 		/* This cache was never opened */
118406497524SJ. Bruce Fields 		return false;
118506497524SJ. Bruce Fields 	if (detail->last_close < seconds_since_boot() - 30)
118606497524SJ. Bruce Fields 		/*
118706497524SJ. Bruce Fields 		 * We allow for the possibility that someone might
118806497524SJ. Bruce Fields 		 * restart a userspace daemon without restarting the
118906497524SJ. Bruce Fields 		 * server; but after 30 seconds, we give up.
119006497524SJ. Bruce Fields 		 */
119106497524SJ. Bruce Fields 		 return false;
119206497524SJ. Bruce Fields 	return true;
119306497524SJ. Bruce Fields }
119406497524SJ. Bruce Fields 
11951da177e4SLinus Torvalds /*
1196bc74b4f5STrond Myklebust  * register an upcall request to user-space and queue it up for read() by the
1197bc74b4f5STrond Myklebust  * upcall daemon.
1198bc74b4f5STrond Myklebust  *
11991da177e4SLinus Torvalds  * Each request is at most one page long.
12001da177e4SLinus Torvalds  */
cache_pipe_upcall(struct cache_detail * detail,struct cache_head * h)120165286b88STrond Myklebust static int cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h)
12021da177e4SLinus Torvalds {
12031da177e4SLinus Torvalds 	char *buf;
12041da177e4SLinus Torvalds 	struct cache_request *crq;
1205f9e1aedcSNeilBrown 	int ret = 0;
12061da177e4SLinus Torvalds 
1207013920ebSNeilBrown 	if (test_bit(CACHE_CLEANED, &h->flags))
1208013920ebSNeilBrown 		/* Too late to make an upcall */
1209013920ebSNeilBrown 		return -EAGAIN;
12101da177e4SLinus Torvalds 
12111da177e4SLinus Torvalds 	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
12121da177e4SLinus Torvalds 	if (!buf)
12131da177e4SLinus Torvalds 		return -EAGAIN;
12141da177e4SLinus Torvalds 
12151da177e4SLinus Torvalds 	crq = kmalloc(sizeof (*crq), GFP_KERNEL);
12161da177e4SLinus Torvalds 	if (!crq) {
12171da177e4SLinus Torvalds 		kfree(buf);
12181da177e4SLinus Torvalds 		return -EAGAIN;
12191da177e4SLinus Torvalds 	}
12201da177e4SLinus Torvalds 
12211da177e4SLinus Torvalds 	crq->q.reader = 0;
12221da177e4SLinus Torvalds 	crq->buf = buf;
1223d94af6deSStanislav Kinsbursky 	crq->len = 0;
12241da177e4SLinus Torvalds 	crq->readers = 0;
12251da177e4SLinus Torvalds 	spin_lock(&queue_lock);
1226a6ab1e81SNeilBrown 	if (test_bit(CACHE_PENDING, &h->flags)) {
1227a6ab1e81SNeilBrown 		crq->item = cache_get(h);
12281da177e4SLinus Torvalds 		list_add_tail(&crq->q.list, &detail->queue);
122978a947f5STrond Myklebust 		trace_cache_entry_upcall(detail, h);
1230a6ab1e81SNeilBrown 	} else
1231f9e1aedcSNeilBrown 		/* Lost a race, no longer PENDING, so don't enqueue */
1232f9e1aedcSNeilBrown 		ret = -EAGAIN;
12331da177e4SLinus Torvalds 	spin_unlock(&queue_lock);
12341da177e4SLinus Torvalds 	wake_up(&queue_wait);
1235f9e1aedcSNeilBrown 	if (ret == -EAGAIN) {
1236f9e1aedcSNeilBrown 		kfree(buf);
1237f9e1aedcSNeilBrown 		kfree(crq);
1238f9e1aedcSNeilBrown 	}
1239f9e1aedcSNeilBrown 	return ret;
12401da177e4SLinus Torvalds }
124165286b88STrond Myklebust 
sunrpc_cache_pipe_upcall(struct cache_detail * detail,struct cache_head * h)124265286b88STrond Myklebust int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h)
124365286b88STrond Myklebust {
124465286b88STrond Myklebust 	if (test_and_set_bit(CACHE_PENDING, &h->flags))
124565286b88STrond Myklebust 		return 0;
124665286b88STrond Myklebust 	return cache_pipe_upcall(detail, h);
124765286b88STrond Myklebust }
1248bc74b4f5STrond Myklebust EXPORT_SYMBOL_GPL(sunrpc_cache_pipe_upcall);
12491da177e4SLinus Torvalds 
sunrpc_cache_pipe_upcall_timeout(struct cache_detail * detail,struct cache_head * h)125065286b88STrond Myklebust int sunrpc_cache_pipe_upcall_timeout(struct cache_detail *detail,
125165286b88STrond Myklebust 				     struct cache_head *h)
125265286b88STrond Myklebust {
125365286b88STrond Myklebust 	if (!cache_listeners_exist(detail)) {
125465286b88STrond Myklebust 		warn_no_listener(detail);
125578a947f5STrond Myklebust 		trace_cache_entry_no_listener(detail, h);
125665286b88STrond Myklebust 		return -EINVAL;
125765286b88STrond Myklebust 	}
125865286b88STrond Myklebust 	return sunrpc_cache_pipe_upcall(detail, h);
125965286b88STrond Myklebust }
126065286b88STrond Myklebust EXPORT_SYMBOL_GPL(sunrpc_cache_pipe_upcall_timeout);
126165286b88STrond Myklebust 
12621da177e4SLinus Torvalds /*
12631da177e4SLinus Torvalds  * parse a message from user-space and pass it
12641da177e4SLinus Torvalds  * to an appropriate cache
12651da177e4SLinus Torvalds  * Messages are, like requests, separated into fields by
12661da177e4SLinus Torvalds  * spaces and dequotes as \xHEXSTRING or embedded \nnn octal
12671da177e4SLinus Torvalds  *
12681da177e4SLinus Torvalds  * Message is
12691da177e4SLinus Torvalds  *   reply cachename expiry key ... content....
12701da177e4SLinus Torvalds  *
12711da177e4SLinus Torvalds  * key and content are both parsed by cache
12721da177e4SLinus Torvalds  */
12731da177e4SLinus Torvalds 
qword_get(char ** bpp,char * dest,int bufsize)12741da177e4SLinus Torvalds int qword_get(char **bpp, char *dest, int bufsize)
12751da177e4SLinus Torvalds {
12761da177e4SLinus Torvalds 	/* return bytes copied, or -1 on error */
12771da177e4SLinus Torvalds 	char *bp = *bpp;
12781da177e4SLinus Torvalds 	int len = 0;
12791da177e4SLinus Torvalds 
12801da177e4SLinus Torvalds 	while (*bp == ' ') bp++;
12811da177e4SLinus Torvalds 
12821da177e4SLinus Torvalds 	if (bp[0] == '\\' && bp[1] == 'x') {
12831da177e4SLinus Torvalds 		/* HEX STRING */
12841da177e4SLinus Torvalds 		bp += 2;
1285b7052cd7SStefan Hajnoczi 		while (len < bufsize - 1) {
1286e7f483eaSAndy Shevchenko 			int h, l;
1287e7f483eaSAndy Shevchenko 
1288e7f483eaSAndy Shevchenko 			h = hex_to_bin(bp[0]);
1289e7f483eaSAndy Shevchenko 			if (h < 0)
1290e7f483eaSAndy Shevchenko 				break;
1291e7f483eaSAndy Shevchenko 
1292e7f483eaSAndy Shevchenko 			l = hex_to_bin(bp[1]);
1293e7f483eaSAndy Shevchenko 			if (l < 0)
1294e7f483eaSAndy Shevchenko 				break;
1295e7f483eaSAndy Shevchenko 
1296e7f483eaSAndy Shevchenko 			*dest++ = (h << 4) | l;
1297e7f483eaSAndy Shevchenko 			bp += 2;
12981da177e4SLinus Torvalds 			len++;
12991da177e4SLinus Torvalds 		}
13001da177e4SLinus Torvalds 	} else {
13011da177e4SLinus Torvalds 		/* text with \nnn octal quoting */
13021da177e4SLinus Torvalds 		while (*bp != ' ' && *bp != '\n' && *bp && len < bufsize-1) {
13031da177e4SLinus Torvalds 			if (*bp == '\\' &&
13041da177e4SLinus Torvalds 			    isodigit(bp[1]) && (bp[1] <= '3') &&
13051da177e4SLinus Torvalds 			    isodigit(bp[2]) &&
13061da177e4SLinus Torvalds 			    isodigit(bp[3])) {
13071da177e4SLinus Torvalds 				int byte = (*++bp -'0');
13081da177e4SLinus Torvalds 				bp++;
13091da177e4SLinus Torvalds 				byte = (byte << 3) | (*bp++ - '0');
13101da177e4SLinus Torvalds 				byte = (byte << 3) | (*bp++ - '0');
13111da177e4SLinus Torvalds 				*dest++ = byte;
13121da177e4SLinus Torvalds 				len++;
13131da177e4SLinus Torvalds 			} else {
13141da177e4SLinus Torvalds 				*dest++ = *bp++;
13151da177e4SLinus Torvalds 				len++;
13161da177e4SLinus Torvalds 			}
13171da177e4SLinus Torvalds 		}
13181da177e4SLinus Torvalds 	}
13191da177e4SLinus Torvalds 
13201da177e4SLinus Torvalds 	if (*bp != ' ' && *bp != '\n' && *bp != '\0')
13211da177e4SLinus Torvalds 		return -1;
13221da177e4SLinus Torvalds 	while (*bp == ' ') bp++;
13231da177e4SLinus Torvalds 	*bpp = bp;
13241da177e4SLinus Torvalds 	*dest = '\0';
13251da177e4SLinus Torvalds 	return len;
13261da177e4SLinus Torvalds }
132724c3767eSTrond Myklebust EXPORT_SYMBOL_GPL(qword_get);
13281da177e4SLinus Torvalds 
13291da177e4SLinus Torvalds 
13301da177e4SLinus Torvalds /*
13316489a8f4SKinglong Mee  * support /proc/net/rpc/$CACHENAME/content
13321da177e4SLinus Torvalds  * as a seqfile.
13331da177e4SLinus Torvalds  * We call ->cache_show passing NULL for the item to
13341da177e4SLinus Torvalds  * get a header, then pass each real item in the cache
13351da177e4SLinus Torvalds  */
13361da177e4SLinus Torvalds 
__cache_seq_start(struct seq_file * m,loff_t * pos)1337ae74136bSTrond Myklebust static void *__cache_seq_start(struct seq_file *m, loff_t *pos)
13381da177e4SLinus Torvalds {
13391da177e4SLinus Torvalds 	loff_t n = *pos;
134095c96174SEric Dumazet 	unsigned int hash, entry;
13411da177e4SLinus Torvalds 	struct cache_head *ch;
13429936f2aeSKinglong Mee 	struct cache_detail *cd = m->private;
13431da177e4SLinus Torvalds 
13441da177e4SLinus Torvalds 	if (!n--)
13451da177e4SLinus Torvalds 		return SEQ_START_TOKEN;
13461da177e4SLinus Torvalds 	hash = n >> 32;
13471da177e4SLinus Torvalds 	entry = n & ((1LL<<32) - 1);
13481da177e4SLinus Torvalds 
1349ae74136bSTrond Myklebust 	hlist_for_each_entry_rcu(ch, &cd->hash_table[hash], cache_list)
13501da177e4SLinus Torvalds 		if (!entry--)
13511da177e4SLinus Torvalds 			return ch;
13521da177e4SLinus Torvalds 	n &= ~((1LL<<32) - 1);
13531da177e4SLinus Torvalds 	do {
13541da177e4SLinus Torvalds 		hash++;
13551da177e4SLinus Torvalds 		n += 1LL<<32;
13561da177e4SLinus Torvalds 	} while(hash < cd->hash_size &&
1357129e5824SKinglong Mee 		hlist_empty(&cd->hash_table[hash]));
13581da177e4SLinus Torvalds 	if (hash >= cd->hash_size)
13591da177e4SLinus Torvalds 		return NULL;
13601da177e4SLinus Torvalds 	*pos = n+1;
1361ae74136bSTrond Myklebust 	return hlist_entry_safe(rcu_dereference_raw(
1362ae74136bSTrond Myklebust 				hlist_first_rcu(&cd->hash_table[hash])),
1363129e5824SKinglong Mee 				struct cache_head, cache_list);
13641da177e4SLinus Torvalds }
1365ae74136bSTrond Myklebust 
cache_seq_next(struct seq_file * m,void * p,loff_t * pos)1366d48cf356STrond Myklebust static void *cache_seq_next(struct seq_file *m, void *p, loff_t *pos)
13671da177e4SLinus Torvalds {
13681da177e4SLinus Torvalds 	struct cache_head *ch = p;
13691da177e4SLinus Torvalds 	int hash = (*pos >> 32);
13709936f2aeSKinglong Mee 	struct cache_detail *cd = m->private;
13711da177e4SLinus Torvalds 
13721da177e4SLinus Torvalds 	if (p == SEQ_START_TOKEN)
13731da177e4SLinus Torvalds 		hash = 0;
1374129e5824SKinglong Mee 	else if (ch->cache_list.next == NULL) {
13751da177e4SLinus Torvalds 		hash++;
13761da177e4SLinus Torvalds 		*pos += 1LL<<32;
13771da177e4SLinus Torvalds 	} else {
13781da177e4SLinus Torvalds 		++*pos;
1379ae74136bSTrond Myklebust 		return hlist_entry_safe(rcu_dereference_raw(
1380ae74136bSTrond Myklebust 					hlist_next_rcu(&ch->cache_list)),
1381129e5824SKinglong Mee 					struct cache_head, cache_list);
13821da177e4SLinus Torvalds 	}
13831da177e4SLinus Torvalds 	*pos &= ~((1LL<<32) - 1);
13841da177e4SLinus Torvalds 	while (hash < cd->hash_size &&
1385129e5824SKinglong Mee 	       hlist_empty(&cd->hash_table[hash])) {
13861da177e4SLinus Torvalds 		hash++;
13871da177e4SLinus Torvalds 		*pos += 1LL<<32;
13881da177e4SLinus Torvalds 	}
13891da177e4SLinus Torvalds 	if (hash >= cd->hash_size)
13901da177e4SLinus Torvalds 		return NULL;
13911da177e4SLinus Torvalds 	++*pos;
1392ae74136bSTrond Myklebust 	return hlist_entry_safe(rcu_dereference_raw(
1393ae74136bSTrond Myklebust 				hlist_first_rcu(&cd->hash_table[hash])),
1394129e5824SKinglong Mee 				struct cache_head, cache_list);
13951da177e4SLinus Torvalds }
13961da177e4SLinus Torvalds 
cache_seq_start_rcu(struct seq_file * m,loff_t * pos)1397ae74136bSTrond Myklebust void *cache_seq_start_rcu(struct seq_file *m, loff_t *pos)
1398ae74136bSTrond Myklebust 	__acquires(RCU)
1399ae74136bSTrond Myklebust {
1400ae74136bSTrond Myklebust 	rcu_read_lock();
1401ae74136bSTrond Myklebust 	return __cache_seq_start(m, pos);
1402ae74136bSTrond Myklebust }
1403ae74136bSTrond Myklebust EXPORT_SYMBOL_GPL(cache_seq_start_rcu);
1404ae74136bSTrond Myklebust 
cache_seq_next_rcu(struct seq_file * file,void * p,loff_t * pos)1405ae74136bSTrond Myklebust void *cache_seq_next_rcu(struct seq_file *file, void *p, loff_t *pos)
1406ae74136bSTrond Myklebust {
1407ae74136bSTrond Myklebust 	return cache_seq_next(file, p, pos);
1408ae74136bSTrond Myklebust }
1409ae74136bSTrond Myklebust EXPORT_SYMBOL_GPL(cache_seq_next_rcu);
1410ae74136bSTrond Myklebust 
cache_seq_stop_rcu(struct seq_file * m,void * p)1411ae74136bSTrond Myklebust void cache_seq_stop_rcu(struct seq_file *m, void *p)
1412ae74136bSTrond Myklebust 	__releases(RCU)
1413ae74136bSTrond Myklebust {
1414ae74136bSTrond Myklebust 	rcu_read_unlock();
1415ae74136bSTrond Myklebust }
1416ae74136bSTrond Myklebust EXPORT_SYMBOL_GPL(cache_seq_stop_rcu);
1417ae74136bSTrond Myklebust 
c_show(struct seq_file * m,void * p)14181da177e4SLinus Torvalds static int c_show(struct seq_file *m, void *p)
14191da177e4SLinus Torvalds {
14201da177e4SLinus Torvalds 	struct cache_head *cp = p;
14219936f2aeSKinglong Mee 	struct cache_detail *cd = m->private;
14221da177e4SLinus Torvalds 
14231da177e4SLinus Torvalds 	if (p == SEQ_START_TOKEN)
14241da177e4SLinus Torvalds 		return cd->cache_show(m, cd, NULL);
14251da177e4SLinus Torvalds 
14261da177e4SLinus Torvalds 	ifdebug(CACHE)
1427f559935eSArnd Bergmann 		seq_printf(m, "# expiry=%lld refcnt=%d flags=%lx\n",
1428c5b29f88SNeilBrown 			   convert_to_wallclock(cp->expiry_time),
14292c935bc5SPeter Zijlstra 			   kref_read(&cp->ref), cp->flags);
14301da177e4SLinus Torvalds 	cache_get(cp);
14311da177e4SLinus Torvalds 	if (cache_check(cd, cp, NULL))
14321da177e4SLinus Torvalds 		/* cache_check does a cache_put on failure */
14339dbc1f45SXu Wang 		seq_puts(m, "# ");
1434200724a7SNeilBrown 	else {
1435200724a7SNeilBrown 		if (cache_is_expired(cd, cp))
14369dbc1f45SXu Wang 			seq_puts(m, "# ");
14371da177e4SLinus Torvalds 		cache_put(cp, cd);
1438200724a7SNeilBrown 	}
14391da177e4SLinus Torvalds 
14401da177e4SLinus Torvalds 	return cd->cache_show(m, cd, cp);
14411da177e4SLinus Torvalds }
14421da177e4SLinus Torvalds 
144356b3d975SPhilippe De Muyter static const struct seq_operations cache_content_op = {
1444d48cf356STrond Myklebust 	.start	= cache_seq_start_rcu,
1445d48cf356STrond Myklebust 	.next	= cache_seq_next_rcu,
1446d48cf356STrond Myklebust 	.stop	= cache_seq_stop_rcu,
14471da177e4SLinus Torvalds 	.show	= c_show,
14481da177e4SLinus Torvalds };
14491da177e4SLinus Torvalds 
content_open(struct inode * inode,struct file * file,struct cache_detail * cd)1450173912a6STrond Myklebust static int content_open(struct inode *inode, struct file *file,
1451173912a6STrond Myklebust 			struct cache_detail *cd)
14521da177e4SLinus Torvalds {
14539936f2aeSKinglong Mee 	struct seq_file *seq;
14549936f2aeSKinglong Mee 	int err;
14551da177e4SLinus Torvalds 
1456f7e86ab9STrond Myklebust 	if (!cd || !try_module_get(cd->owner))
1457f7e86ab9STrond Myklebust 		return -EACCES;
14589936f2aeSKinglong Mee 
14599936f2aeSKinglong Mee 	err = seq_open(file, &cache_content_op);
14609936f2aeSKinglong Mee 	if (err) {
1461a5990ea1SLi Zefan 		module_put(cd->owner);
14629936f2aeSKinglong Mee 		return err;
1463a5990ea1SLi Zefan 	}
14641da177e4SLinus Torvalds 
14659936f2aeSKinglong Mee 	seq = file->private_data;
14669936f2aeSKinglong Mee 	seq->private = cd;
1467ec931035SPavel Emelyanov 	return 0;
14681da177e4SLinus Torvalds }
14691da177e4SLinus Torvalds 
content_release(struct inode * inode,struct file * file,struct cache_detail * cd)1470f7e86ab9STrond Myklebust static int content_release(struct inode *inode, struct file *file,
1471f7e86ab9STrond Myklebust 		struct cache_detail *cd)
1472f7e86ab9STrond Myklebust {
14739936f2aeSKinglong Mee 	int ret = seq_release(inode, file);
1474f7e86ab9STrond Myklebust 	module_put(cd->owner);
1475f7e86ab9STrond Myklebust 	return ret;
1476f7e86ab9STrond Myklebust }
1477f7e86ab9STrond Myklebust 
open_flush(struct inode * inode,struct file * file,struct cache_detail * cd)1478f7e86ab9STrond Myklebust static int open_flush(struct inode *inode, struct file *file,
1479f7e86ab9STrond Myklebust 			struct cache_detail *cd)
1480f7e86ab9STrond Myklebust {
1481f7e86ab9STrond Myklebust 	if (!cd || !try_module_get(cd->owner))
1482f7e86ab9STrond Myklebust 		return -EACCES;
1483f7e86ab9STrond Myklebust 	return nonseekable_open(inode, file);
1484f7e86ab9STrond Myklebust }
1485f7e86ab9STrond Myklebust 
release_flush(struct inode * inode,struct file * file,struct cache_detail * cd)1486f7e86ab9STrond Myklebust static int release_flush(struct inode *inode, struct file *file,
1487f7e86ab9STrond Myklebust 			struct cache_detail *cd)
1488f7e86ab9STrond Myklebust {
1489f7e86ab9STrond Myklebust 	module_put(cd->owner);
1490f7e86ab9STrond Myklebust 	return 0;
1491f7e86ab9STrond Myklebust }
14921da177e4SLinus Torvalds 
read_flush(struct file * file,char __user * buf,size_t count,loff_t * ppos,struct cache_detail * cd)14931da177e4SLinus Torvalds static ssize_t read_flush(struct file *file, char __user *buf,
1494173912a6STrond Myklebust 			  size_t count, loff_t *ppos,
1495173912a6STrond Myklebust 			  struct cache_detail *cd)
14961da177e4SLinus Torvalds {
1497212ba906SSasha Levin 	char tbuf[22];
149801b2969aSChuck Lever 	size_t len;
14991da177e4SLinus Torvalds 
1500f559935eSArnd Bergmann 	len = snprintf(tbuf, sizeof(tbuf), "%llu\n",
15018ccc8691SKinglong Mee 			convert_to_wallclock(cd->flush_time));
15028ccc8691SKinglong Mee 	return simple_read_from_buffer(buf, count, ppos, tbuf, len);
15031da177e4SLinus Torvalds }
15041da177e4SLinus Torvalds 
write_flush(struct file * file,const char __user * buf,size_t count,loff_t * ppos,struct cache_detail * cd)15051da177e4SLinus Torvalds static ssize_t write_flush(struct file *file, const char __user *buf,
1506173912a6STrond Myklebust 			   size_t count, loff_t *ppos,
1507173912a6STrond Myklebust 			   struct cache_detail *cd)
15081da177e4SLinus Torvalds {
15091da177e4SLinus Torvalds 	char tbuf[20];
15103b68e6eeSNeilBrown 	char *ep;
1511f559935eSArnd Bergmann 	time64_t now;
1512c5b29f88SNeilBrown 
15131da177e4SLinus Torvalds 	if (*ppos || count > sizeof(tbuf)-1)
15141da177e4SLinus Torvalds 		return -EINVAL;
15151da177e4SLinus Torvalds 	if (copy_from_user(tbuf, buf, count))
15161da177e4SLinus Torvalds 		return -EFAULT;
15171da177e4SLinus Torvalds 	tbuf[count] = 0;
1518c5b29f88SNeilBrown 	simple_strtoul(tbuf, &ep, 0);
15191da177e4SLinus Torvalds 	if (*ep && *ep != '\n')
15201da177e4SLinus Torvalds 		return -EINVAL;
15213b68e6eeSNeilBrown 	/* Note that while we check that 'buf' holds a valid number,
15223b68e6eeSNeilBrown 	 * we always ignore the value and just flush everything.
15233b68e6eeSNeilBrown 	 * Making use of the number leads to races.
152477862036SNeil Brown 	 */
15253b68e6eeSNeilBrown 
15263b68e6eeSNeilBrown 	now = seconds_since_boot();
15273b68e6eeSNeilBrown 	/* Always flush everything, so behave like cache_purge()
15283b68e6eeSNeilBrown 	 * Do this by advancing flush_time to the current time,
15293b68e6eeSNeilBrown 	 * or by one second if it has already reached the current time.
15303b68e6eeSNeilBrown 	 * Newly added cache entries will always have ->last_refresh greater
15313b68e6eeSNeilBrown 	 * that ->flush_time, so they don't get flushed prematurely.
15323b68e6eeSNeilBrown 	 */
15333b68e6eeSNeilBrown 
153477862036SNeil Brown 	if (cd->flush_time >= now)
153577862036SNeil Brown 		now = cd->flush_time + 1;
153677862036SNeil Brown 
15373b68e6eeSNeilBrown 	cd->flush_time = now;
15383b68e6eeSNeilBrown 	cd->nextcheck = now;
15391da177e4SLinus Torvalds 	cache_flush();
15401da177e4SLinus Torvalds 
1541f69d6d8eSJeff Layton 	if (cd->flush)
1542f69d6d8eSJeff Layton 		cd->flush();
1543f69d6d8eSJeff Layton 
15441da177e4SLinus Torvalds 	*ppos += count;
15451da177e4SLinus Torvalds 	return count;
15461da177e4SLinus Torvalds }
15471da177e4SLinus Torvalds 
cache_read_procfs(struct file * filp,char __user * buf,size_t count,loff_t * ppos)1548173912a6STrond Myklebust static ssize_t cache_read_procfs(struct file *filp, char __user *buf,
1549173912a6STrond Myklebust 				 size_t count, loff_t *ppos)
1550173912a6STrond Myklebust {
1551359745d7SMuchun Song 	struct cache_detail *cd = pde_data(file_inode(filp));
1552173912a6STrond Myklebust 
1553173912a6STrond Myklebust 	return cache_read(filp, buf, count, ppos, cd);
1554173912a6STrond Myklebust }
1555173912a6STrond Myklebust 
cache_write_procfs(struct file * filp,const char __user * buf,size_t count,loff_t * ppos)1556173912a6STrond Myklebust static ssize_t cache_write_procfs(struct file *filp, const char __user *buf,
1557173912a6STrond Myklebust 				  size_t count, loff_t *ppos)
1558173912a6STrond Myklebust {
1559359745d7SMuchun Song 	struct cache_detail *cd = pde_data(file_inode(filp));
1560173912a6STrond Myklebust 
1561173912a6STrond Myklebust 	return cache_write(filp, buf, count, ppos, cd);
1562173912a6STrond Myklebust }
1563173912a6STrond Myklebust 
cache_poll_procfs(struct file * filp,poll_table * wait)1564ade994f4SAl Viro static __poll_t cache_poll_procfs(struct file *filp, poll_table *wait)
1565173912a6STrond Myklebust {
1566359745d7SMuchun Song 	struct cache_detail *cd = pde_data(file_inode(filp));
1567173912a6STrond Myklebust 
1568173912a6STrond Myklebust 	return cache_poll(filp, wait, cd);
1569173912a6STrond Myklebust }
1570173912a6STrond Myklebust 
cache_ioctl_procfs(struct file * filp,unsigned int cmd,unsigned long arg)1571d79b6f4dSFrederic Weisbecker static long cache_ioctl_procfs(struct file *filp,
1572173912a6STrond Myklebust 			       unsigned int cmd, unsigned long arg)
1573173912a6STrond Myklebust {
1574496ad9aaSAl Viro 	struct inode *inode = file_inode(filp);
1575359745d7SMuchun Song 	struct cache_detail *cd = pde_data(inode);
1576173912a6STrond Myklebust 
1577a6f8dbc6SArnd Bergmann 	return cache_ioctl(inode, filp, cmd, arg, cd);
1578173912a6STrond Myklebust }
1579173912a6STrond Myklebust 
cache_open_procfs(struct inode * inode,struct file * filp)1580173912a6STrond Myklebust static int cache_open_procfs(struct inode *inode, struct file *filp)
1581173912a6STrond Myklebust {
1582359745d7SMuchun Song 	struct cache_detail *cd = pde_data(inode);
1583173912a6STrond Myklebust 
1584173912a6STrond Myklebust 	return cache_open(inode, filp, cd);
1585173912a6STrond Myklebust }
1586173912a6STrond Myklebust 
cache_release_procfs(struct inode * inode,struct file * filp)1587173912a6STrond Myklebust static int cache_release_procfs(struct inode *inode, struct file *filp)
1588173912a6STrond Myklebust {
1589359745d7SMuchun Song 	struct cache_detail *cd = pde_data(inode);
1590173912a6STrond Myklebust 
1591173912a6STrond Myklebust 	return cache_release(inode, filp, cd);
1592173912a6STrond Myklebust }
1593173912a6STrond Myklebust 
159497a32539SAlexey Dobriyan static const struct proc_ops cache_channel_proc_ops = {
159597a32539SAlexey Dobriyan 	.proc_read	= cache_read_procfs,
159697a32539SAlexey Dobriyan 	.proc_write	= cache_write_procfs,
159797a32539SAlexey Dobriyan 	.proc_poll	= cache_poll_procfs,
159897a32539SAlexey Dobriyan 	.proc_ioctl	= cache_ioctl_procfs, /* for FIONREAD */
159997a32539SAlexey Dobriyan 	.proc_open	= cache_open_procfs,
160097a32539SAlexey Dobriyan 	.proc_release	= cache_release_procfs,
160197a32539SAlexey Dobriyan };
16021da177e4SLinus Torvalds 
content_open_procfs(struct inode * inode,struct file * filp)1603173912a6STrond Myklebust static int content_open_procfs(struct inode *inode, struct file *filp)
1604173912a6STrond Myklebust {
1605173912a6STrond Myklebust 	struct cache_detail *cd = pde_data(inode);
1606359745d7SMuchun Song 
1607173912a6STrond Myklebust 	return content_open(inode, filp, cd);
1608173912a6STrond Myklebust }
1609173912a6STrond Myklebust 
content_release_procfs(struct inode * inode,struct file * filp)1610173912a6STrond Myklebust static int content_release_procfs(struct inode *inode, struct file *filp)
1611f7e86ab9STrond Myklebust {
1612f7e86ab9STrond Myklebust 	struct cache_detail *cd = pde_data(inode);
1613359745d7SMuchun Song 
1614f7e86ab9STrond Myklebust 	return content_release(inode, filp, cd);
1615f7e86ab9STrond Myklebust }
1616f7e86ab9STrond Myklebust 
1617f7e86ab9STrond Myklebust static const struct proc_ops content_proc_ops = {
161897a32539SAlexey Dobriyan 	.proc_open	= content_open_procfs,
161997a32539SAlexey Dobriyan 	.proc_read	= seq_read,
162097a32539SAlexey Dobriyan 	.proc_lseek	= seq_lseek,
162197a32539SAlexey Dobriyan 	.proc_release	= content_release_procfs,
162297a32539SAlexey Dobriyan };
1623173912a6STrond Myklebust 
open_flush_procfs(struct inode * inode,struct file * filp)1624173912a6STrond Myklebust static int open_flush_procfs(struct inode *inode, struct file *filp)
1625f7e86ab9STrond Myklebust {
1626f7e86ab9STrond Myklebust 	struct cache_detail *cd = pde_data(inode);
1627359745d7SMuchun Song 
1628f7e86ab9STrond Myklebust 	return open_flush(inode, filp, cd);
1629f7e86ab9STrond Myklebust }
1630f7e86ab9STrond Myklebust 
release_flush_procfs(struct inode * inode,struct file * filp)1631f7e86ab9STrond Myklebust static int release_flush_procfs(struct inode *inode, struct file *filp)
1632f7e86ab9STrond Myklebust {
1633f7e86ab9STrond Myklebust 	struct cache_detail *cd = pde_data(inode);
1634359745d7SMuchun Song 
1635f7e86ab9STrond Myklebust 	return release_flush(inode, filp, cd);
1636f7e86ab9STrond Myklebust }
1637f7e86ab9STrond Myklebust 
read_flush_procfs(struct file * filp,char __user * buf,size_t count,loff_t * ppos)1638f7e86ab9STrond Myklebust static ssize_t read_flush_procfs(struct file *filp, char __user *buf,
1639173912a6STrond Myklebust 			    size_t count, loff_t *ppos)
1640173912a6STrond Myklebust {
1641173912a6STrond Myklebust 	struct cache_detail *cd = pde_data(file_inode(filp));
1642359745d7SMuchun Song 
1643173912a6STrond Myklebust 	return read_flush(filp, buf, count, ppos, cd);
1644173912a6STrond Myklebust }
1645173912a6STrond Myklebust 
write_flush_procfs(struct file * filp,const char __user * buf,size_t count,loff_t * ppos)1646173912a6STrond Myklebust static ssize_t write_flush_procfs(struct file *filp,
1647173912a6STrond Myklebust 				  const char __user *buf,
1648173912a6STrond Myklebust 				  size_t count, loff_t *ppos)
1649173912a6STrond Myklebust {
1650173912a6STrond Myklebust 	struct cache_detail *cd = pde_data(file_inode(filp));
1651359745d7SMuchun Song 
1652173912a6STrond Myklebust 	return write_flush(filp, buf, count, ppos, cd);
1653173912a6STrond Myklebust }
1654173912a6STrond Myklebust 
1655173912a6STrond Myklebust static const struct proc_ops cache_flush_proc_ops = {
165697a32539SAlexey Dobriyan 	.proc_open	= open_flush_procfs,
165797a32539SAlexey Dobriyan 	.proc_read	= read_flush_procfs,
165897a32539SAlexey Dobriyan 	.proc_write	= write_flush_procfs,
165997a32539SAlexey Dobriyan 	.proc_release	= release_flush_procfs,
166097a32539SAlexey Dobriyan };
166197a32539SAlexey Dobriyan 
remove_cache_proc_entries(struct cache_detail * cd)1662173912a6STrond Myklebust static void remove_cache_proc_entries(struct cache_detail *cd)
1663173912a6STrond Myklebust {
1664863d7d9cSKinglong Mee 	if (cd->procfs) {
1665173912a6STrond Myklebust 		proc_remove(cd->procfs);
1666863d7d9cSKinglong Mee 		cd->procfs = NULL;
1667863d7d9cSKinglong Mee 	}
1668863d7d9cSKinglong Mee }
1669863d7d9cSKinglong Mee 
1670173912a6STrond Myklebust #ifdef CONFIG_PROC_FS
create_cache_proc_entries(struct cache_detail * cd,struct net * net)1671173912a6STrond Myklebust static int create_cache_proc_entries(struct cache_detail *cd, struct net *net)
1672173912a6STrond Myklebust {
1673593ce16bSPavel Emelyanov 	struct proc_dir_entry *p;
1674173912a6STrond Myklebust 	struct sunrpc_net *sn;
1675173912a6STrond Myklebust 
16764f42d0d5SPavel Emelyanov 	sn = net_generic(net, sunrpc_net_id);
1677173912a6STrond Myklebust 	cd->procfs = proc_mkdir(cd->name, sn->proc_net_rpc);
16784f42d0d5SPavel Emelyanov 	if (cd->procfs == NULL)
1679863d7d9cSKinglong Mee 		goto out_nomem;
1680863d7d9cSKinglong Mee 
1681173912a6STrond Myklebust 	p = proc_create_data("flush", S_IFREG | 0600,
1682173912a6STrond Myklebust 			     cd->procfs, &cache_flush_proc_ops, cd);
1683d6444062SJoe Perches 	if (p == NULL)
168497a32539SAlexey Dobriyan 		goto out_nomem;
1685173912a6STrond Myklebust 
1686173912a6STrond Myklebust 	if (cd->cache_request || cd->cache_parse) {
1687173912a6STrond Myklebust 		p = proc_create_data("channel", S_IFREG | 0600, cd->procfs,
16882d438338SStanislav Kinsbursky 				     &cache_channel_proc_ops, cd);
1689d6444062SJoe Perches 		if (p == NULL)
169097a32539SAlexey Dobriyan 			goto out_nomem;
1691173912a6STrond Myklebust 	}
1692173912a6STrond Myklebust 	if (cd->cache_show) {
1693173912a6STrond Myklebust 		p = proc_create_data("content", S_IFREG | 0400, cd->procfs,
1694173912a6STrond Myklebust 				     &content_proc_ops, cd);
1695d6444062SJoe Perches 		if (p == NULL)
169697a32539SAlexey Dobriyan 			goto out_nomem;
1697173912a6STrond Myklebust 	}
1698173912a6STrond Myklebust 	return 0;
1699173912a6STrond Myklebust out_nomem:
1700173912a6STrond Myklebust 	remove_cache_proc_entries(cd);
1701173912a6STrond Myklebust 	return -ENOMEM;
1702863d7d9cSKinglong Mee }
1703173912a6STrond Myklebust #else /* CONFIG_PROC_FS */
create_cache_proc_entries(struct cache_detail * cd,struct net * net)1704173912a6STrond Myklebust static int create_cache_proc_entries(struct cache_detail *cd, struct net *net)
1705173912a6STrond Myklebust {
1706593ce16bSPavel Emelyanov 	return 0;
1707173912a6STrond Myklebust }
1708173912a6STrond Myklebust #endif
1709173912a6STrond Myklebust 
cache_initialize(void)1710173912a6STrond Myklebust void __init cache_initialize(void)
1711173912a6STrond Myklebust {
17128eab945cSArtem Bityutskiy 	INIT_DEFERRABLE_WORK(&cache_cleaner, do_cache_clean);
17138eab945cSArtem Bityutskiy }
1714203b42f7STejun Heo 
cache_register_net(struct cache_detail * cd,struct net * net)17158eab945cSArtem Bityutskiy int cache_register_net(struct cache_detail *cd, struct net *net)
17168eab945cSArtem Bityutskiy {
1717593ce16bSPavel Emelyanov 	int ret;
1718173912a6STrond Myklebust 
1719173912a6STrond Myklebust 	sunrpc_init_cache_detail(cd);
1720173912a6STrond Myklebust 	ret = create_cache_proc_entries(cd, net);
1721173912a6STrond Myklebust 	if (ret)
1722593ce16bSPavel Emelyanov 		sunrpc_destroy_cache_detail(cd);
1723173912a6STrond Myklebust 	return ret;
1724173912a6STrond Myklebust }
1725173912a6STrond Myklebust EXPORT_SYMBOL_GPL(cache_register_net);
1726173912a6STrond Myklebust 
cache_unregister_net(struct cache_detail * cd,struct net * net)1727f5c8593bSStanislav Kinsbursky void cache_unregister_net(struct cache_detail *cd, struct net *net)
1728593ce16bSPavel Emelyanov {
1729593ce16bSPavel Emelyanov 	remove_cache_proc_entries(cd);
1730593ce16bSPavel Emelyanov 	sunrpc_destroy_cache_detail(cd);
1731863d7d9cSKinglong Mee }
1732593ce16bSPavel Emelyanov EXPORT_SYMBOL_GPL(cache_unregister_net);
1733593ce16bSPavel Emelyanov 
cache_create_net(const struct cache_detail * tmpl,struct net * net)1734f5c8593bSStanislav Kinsbursky struct cache_detail *cache_create_net(const struct cache_detail *tmpl, struct net *net)
1735593ce16bSPavel Emelyanov {
1736d34971a6SBhumika Goyal 	struct cache_detail *cd;
1737173912a6STrond Myklebust 	int i;
17380a402d5aSStanislav Kinsbursky 
1739129e5824SKinglong Mee 	cd = kmemdup(tmpl, sizeof(struct cache_detail), GFP_KERNEL);
17400a402d5aSStanislav Kinsbursky 	if (cd == NULL)
17410a402d5aSStanislav Kinsbursky 		return ERR_PTR(-ENOMEM);
17420a402d5aSStanislav Kinsbursky 
17430a402d5aSStanislav Kinsbursky 	cd->hash_table = kcalloc(cd->hash_size, sizeof(struct hlist_head),
17440a402d5aSStanislav Kinsbursky 				 GFP_KERNEL);
17456396bb22SKees Cook 	if (cd->hash_table == NULL) {
17460a402d5aSStanislav Kinsbursky 		kfree(cd);
17470a402d5aSStanislav Kinsbursky 		return ERR_PTR(-ENOMEM);
17480a402d5aSStanislav Kinsbursky 	}
17490a402d5aSStanislav Kinsbursky 
1750173912a6STrond Myklebust 	for (i = 0; i < cd->hash_size; i++)
1751129e5824SKinglong Mee 		INIT_HLIST_HEAD(&cd->hash_table[i]);
1752129e5824SKinglong Mee 	cd->net = net;
1753129e5824SKinglong Mee 	return cd;
17540a402d5aSStanislav Kinsbursky }
17550a402d5aSStanislav Kinsbursky EXPORT_SYMBOL_GPL(cache_create_net);
17560a402d5aSStanislav Kinsbursky 
cache_destroy_net(struct cache_detail * cd,struct net * net)17570a402d5aSStanislav Kinsbursky void cache_destroy_net(struct cache_detail *cd, struct net *net)
17580a402d5aSStanislav Kinsbursky {
17590a402d5aSStanislav Kinsbursky 	kfree(cd->hash_table);
17600a402d5aSStanislav Kinsbursky 	kfree(cd);
17610a402d5aSStanislav Kinsbursky }
17620a402d5aSStanislav Kinsbursky EXPORT_SYMBOL_GPL(cache_destroy_net);
17630a402d5aSStanislav Kinsbursky 
cache_read_pipefs(struct file * filp,char __user * buf,size_t count,loff_t * ppos)17640a402d5aSStanislav Kinsbursky static ssize_t cache_read_pipefs(struct file *filp, char __user *buf,
17658854e82dSTrond Myklebust 				 size_t count, loff_t *ppos)
17668854e82dSTrond Myklebust {
17678854e82dSTrond Myklebust 	struct cache_detail *cd = RPC_I(file_inode(filp))->private;
17688854e82dSTrond Myklebust 
1769496ad9aaSAl Viro 	return cache_read(filp, buf, count, ppos, cd);
17708854e82dSTrond Myklebust }
17718854e82dSTrond Myklebust 
cache_write_pipefs(struct file * filp,const char __user * buf,size_t count,loff_t * ppos)17728854e82dSTrond Myklebust static ssize_t cache_write_pipefs(struct file *filp, const char __user *buf,
17738854e82dSTrond Myklebust 				  size_t count, loff_t *ppos)
17748854e82dSTrond Myklebust {
17758854e82dSTrond Myklebust 	struct cache_detail *cd = RPC_I(file_inode(filp))->private;
17768854e82dSTrond Myklebust 
1777496ad9aaSAl Viro 	return cache_write(filp, buf, count, ppos, cd);
17788854e82dSTrond Myklebust }
17798854e82dSTrond Myklebust 
cache_poll_pipefs(struct file * filp,poll_table * wait)17808854e82dSTrond Myklebust static __poll_t cache_poll_pipefs(struct file *filp, poll_table *wait)
17818854e82dSTrond Myklebust {
1782ade994f4SAl Viro 	struct cache_detail *cd = RPC_I(file_inode(filp))->private;
17838854e82dSTrond Myklebust 
1784496ad9aaSAl Viro 	return cache_poll(filp, wait, cd);
17858854e82dSTrond Myklebust }
17868854e82dSTrond Myklebust 
cache_ioctl_pipefs(struct file * filp,unsigned int cmd,unsigned long arg)17878854e82dSTrond Myklebust static long cache_ioctl_pipefs(struct file *filp,
17888854e82dSTrond Myklebust 			      unsigned int cmd, unsigned long arg)
17899918ff26SFrederic Weisbecker {
17908854e82dSTrond Myklebust 	struct inode *inode = file_inode(filp);
17918854e82dSTrond Myklebust 	struct cache_detail *cd = RPC_I(inode)->private;
1792496ad9aaSAl Viro 
17938854e82dSTrond Myklebust 	return cache_ioctl(inode, filp, cmd, arg, cd);
17948854e82dSTrond Myklebust }
1795a6f8dbc6SArnd Bergmann 
cache_open_pipefs(struct inode * inode,struct file * filp)17968854e82dSTrond Myklebust static int cache_open_pipefs(struct inode *inode, struct file *filp)
17978854e82dSTrond Myklebust {
17988854e82dSTrond Myklebust 	struct cache_detail *cd = RPC_I(inode)->private;
17998854e82dSTrond Myklebust 
18008854e82dSTrond Myklebust 	return cache_open(inode, filp, cd);
18018854e82dSTrond Myklebust }
18028854e82dSTrond Myklebust 
cache_release_pipefs(struct inode * inode,struct file * filp)18038854e82dSTrond Myklebust static int cache_release_pipefs(struct inode *inode, struct file *filp)
18048854e82dSTrond Myklebust {
18058854e82dSTrond Myklebust 	struct cache_detail *cd = RPC_I(inode)->private;
18068854e82dSTrond Myklebust 
18078854e82dSTrond Myklebust 	return cache_release(inode, filp, cd);
18088854e82dSTrond Myklebust }
18098854e82dSTrond Myklebust 
18108854e82dSTrond Myklebust const struct file_operations cache_file_operations_pipefs = {
18118854e82dSTrond Myklebust 	.owner		= THIS_MODULE,
18128854e82dSTrond Myklebust 	.read		= cache_read_pipefs,
18138854e82dSTrond Myklebust 	.write		= cache_write_pipefs,
18148854e82dSTrond Myklebust 	.poll		= cache_poll_pipefs,
18158854e82dSTrond Myklebust 	.unlocked_ioctl	= cache_ioctl_pipefs, /* for FIONREAD */
18168854e82dSTrond Myklebust 	.open		= cache_open_pipefs,
18178854e82dSTrond Myklebust 	.release	= cache_release_pipefs,
18189918ff26SFrederic Weisbecker };
18198854e82dSTrond Myklebust 
content_open_pipefs(struct inode * inode,struct file * filp)18208854e82dSTrond Myklebust static int content_open_pipefs(struct inode *inode, struct file *filp)
18218854e82dSTrond Myklebust {
18228854e82dSTrond Myklebust 	struct cache_detail *cd = RPC_I(inode)->private;
18238854e82dSTrond Myklebust 
18248854e82dSTrond Myklebust 	return content_open(inode, filp, cd);
18258854e82dSTrond Myklebust }
18268854e82dSTrond Myklebust 
content_release_pipefs(struct inode * inode,struct file * filp)18278854e82dSTrond Myklebust static int content_release_pipefs(struct inode *inode, struct file *filp)
18288854e82dSTrond Myklebust {
18298854e82dSTrond Myklebust 	struct cache_detail *cd = RPC_I(inode)->private;
1830f7e86ab9STrond Myklebust 
1831f7e86ab9STrond Myklebust 	return content_release(inode, filp, cd);
1832f7e86ab9STrond Myklebust }
1833f7e86ab9STrond Myklebust 
1834f7e86ab9STrond Myklebust const struct file_operations content_file_operations_pipefs = {
1835f7e86ab9STrond Myklebust 	.open		= content_open_pipefs,
1836f7e86ab9STrond Myklebust 	.read		= seq_read,
18378854e82dSTrond Myklebust 	.llseek		= seq_lseek,
18388854e82dSTrond Myklebust 	.release	= content_release_pipefs,
18398854e82dSTrond Myklebust };
18408854e82dSTrond Myklebust 
open_flush_pipefs(struct inode * inode,struct file * filp)1841f7e86ab9STrond Myklebust static int open_flush_pipefs(struct inode *inode, struct file *filp)
18428854e82dSTrond Myklebust {
18438854e82dSTrond Myklebust 	struct cache_detail *cd = RPC_I(inode)->private;
1844f7e86ab9STrond Myklebust 
1845f7e86ab9STrond Myklebust 	return open_flush(inode, filp, cd);
1846f7e86ab9STrond Myklebust }
1847f7e86ab9STrond Myklebust 
release_flush_pipefs(struct inode * inode,struct file * filp)1848f7e86ab9STrond Myklebust static int release_flush_pipefs(struct inode *inode, struct file *filp)
1849f7e86ab9STrond Myklebust {
1850f7e86ab9STrond Myklebust 	struct cache_detail *cd = RPC_I(inode)->private;
1851f7e86ab9STrond Myklebust 
1852f7e86ab9STrond Myklebust 	return release_flush(inode, filp, cd);
1853f7e86ab9STrond Myklebust }
1854f7e86ab9STrond Myklebust 
read_flush_pipefs(struct file * filp,char __user * buf,size_t count,loff_t * ppos)1855f7e86ab9STrond Myklebust static ssize_t read_flush_pipefs(struct file *filp, char __user *buf,
1856f7e86ab9STrond Myklebust 			    size_t count, loff_t *ppos)
1857f7e86ab9STrond Myklebust {
18588854e82dSTrond Myklebust 	struct cache_detail *cd = RPC_I(file_inode(filp))->private;
18598854e82dSTrond Myklebust 
18608854e82dSTrond Myklebust 	return read_flush(filp, buf, count, ppos, cd);
1861496ad9aaSAl Viro }
18628854e82dSTrond Myklebust 
write_flush_pipefs(struct file * filp,const char __user * buf,size_t count,loff_t * ppos)18638854e82dSTrond Myklebust static ssize_t write_flush_pipefs(struct file *filp,
18648854e82dSTrond Myklebust 				  const char __user *buf,
18658854e82dSTrond Myklebust 				  size_t count, loff_t *ppos)
18668854e82dSTrond Myklebust {
18678854e82dSTrond Myklebust 	struct cache_detail *cd = RPC_I(file_inode(filp))->private;
18688854e82dSTrond Myklebust 
18698854e82dSTrond Myklebust 	return write_flush(filp, buf, count, ppos, cd);
1870496ad9aaSAl Viro }
18718854e82dSTrond Myklebust 
18728854e82dSTrond Myklebust const struct file_operations cache_flush_operations_pipefs = {
18738854e82dSTrond Myklebust 	.open		= open_flush_pipefs,
18748854e82dSTrond Myklebust 	.read		= read_flush_pipefs,
18758854e82dSTrond Myklebust 	.write		= write_flush_pipefs,
1876f7e86ab9STrond Myklebust 	.release	= release_flush_pipefs,
18778854e82dSTrond Myklebust };
18788854e82dSTrond Myklebust 
sunrpc_cache_register_pipefs(struct dentry * parent,const char * name,umode_t umode,struct cache_detail * cd)1879f7e86ab9STrond Myklebust int sunrpc_cache_register_pipefs(struct dentry *parent,
18806038f373SArnd Bergmann 				 const char *name, umode_t umode,
18818854e82dSTrond Myklebust 				 struct cache_detail *cd)
18828854e82dSTrond Myklebust {
18838854e82dSTrond Myklebust 	struct dentry *dir = rpc_create_cache_dir(parent, name, umode, cd);
188464f1426fSAl Viro 	if (IS_ERR(dir))
18858854e82dSTrond Myklebust 		return PTR_ERR(dir);
18868854e82dSTrond Myklebust 	cd->pipefs = dir;
1887a95e691fSAl Viro 	return 0;
1888a95e691fSAl Viro }
1889a95e691fSAl Viro EXPORT_SYMBOL_GPL(sunrpc_cache_register_pipefs);
1890863d7d9cSKinglong Mee 
sunrpc_cache_unregister_pipefs(struct cache_detail * cd)1891a95e691fSAl Viro void sunrpc_cache_unregister_pipefs(struct cache_detail *cd)
18928854e82dSTrond Myklebust {
18938854e82dSTrond Myklebust 	if (cd->pipefs) {
18948854e82dSTrond Myklebust 		rpc_remove_cache_dir(cd->pipefs);
18958854e82dSTrond Myklebust 		cd->pipefs = NULL;
18968854e82dSTrond Myklebust 	}
1897863d7d9cSKinglong Mee }
1898863d7d9cSKinglong Mee EXPORT_SYMBOL_GPL(sunrpc_cache_unregister_pipefs);
1899863d7d9cSKinglong Mee 
sunrpc_cache_unhash(struct cache_detail * cd,struct cache_head * h)1900863d7d9cSKinglong Mee void sunrpc_cache_unhash(struct cache_detail *cd, struct cache_head *h)
19018854e82dSTrond Myklebust {
19028854e82dSTrond Myklebust 	spin_lock(&cd->hash_lock);
19038854e82dSTrond Myklebust 	if (!hlist_unhashed(&h->cache_list)){
19042b477c00SNeil Brown 		sunrpc_begin_cache_remove_entry(h, cd);
19052b477c00SNeil Brown 		spin_unlock(&cd->hash_lock);
19061863d77fSTrond Myklebust 		sunrpc_end_cache_remove_entry(h, cd);
19072b477c00SNeil Brown 	} else
1908809fe3c5STrond Myklebust 		spin_unlock(&cd->hash_lock);
19091863d77fSTrond Myklebust }
1910809fe3c5STrond Myklebust EXPORT_SYMBOL_GPL(sunrpc_cache_unhash);
19112b477c00SNeil Brown