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(¤t_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(¤t_detail->hash_lock);
4691da177e4SLinus Torvalds
4701da177e4SLinus Torvalds /* Ok, now to clean this strand */
4711da177e4SLinus Torvalds
472129e5824SKinglong Mee head = ¤t_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(¤t_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