162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * net/sunrpc/cache.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Generic code for various authentication-related caches 662306a36Sopenharmony_ci * used by sunrpc clients and servers. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (C) 2002 Neil Brown <neilb@cse.unsw.edu.au> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/types.h> 1262306a36Sopenharmony_ci#include <linux/fs.h> 1362306a36Sopenharmony_ci#include <linux/file.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/signal.h> 1662306a36Sopenharmony_ci#include <linux/sched.h> 1762306a36Sopenharmony_ci#include <linux/kmod.h> 1862306a36Sopenharmony_ci#include <linux/list.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/ctype.h> 2162306a36Sopenharmony_ci#include <linux/string_helpers.h> 2262306a36Sopenharmony_ci#include <linux/uaccess.h> 2362306a36Sopenharmony_ci#include <linux/poll.h> 2462306a36Sopenharmony_ci#include <linux/seq_file.h> 2562306a36Sopenharmony_ci#include <linux/proc_fs.h> 2662306a36Sopenharmony_ci#include <linux/net.h> 2762306a36Sopenharmony_ci#include <linux/workqueue.h> 2862306a36Sopenharmony_ci#include <linux/mutex.h> 2962306a36Sopenharmony_ci#include <linux/pagemap.h> 3062306a36Sopenharmony_ci#include <asm/ioctls.h> 3162306a36Sopenharmony_ci#include <linux/sunrpc/types.h> 3262306a36Sopenharmony_ci#include <linux/sunrpc/cache.h> 3362306a36Sopenharmony_ci#include <linux/sunrpc/stats.h> 3462306a36Sopenharmony_ci#include <linux/sunrpc/rpc_pipe_fs.h> 3562306a36Sopenharmony_ci#include <trace/events/sunrpc.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include "netns.h" 3862306a36Sopenharmony_ci#include "fail.h" 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define RPCDBG_FACILITY RPCDBG_CACHE 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic bool cache_defer_req(struct cache_req *req, struct cache_head *item); 4362306a36Sopenharmony_cistatic void cache_revisit_request(struct cache_head *item); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic void cache_init(struct cache_head *h, struct cache_detail *detail) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci time64_t now = seconds_since_boot(); 4862306a36Sopenharmony_ci INIT_HLIST_NODE(&h->cache_list); 4962306a36Sopenharmony_ci h->flags = 0; 5062306a36Sopenharmony_ci kref_init(&h->ref); 5162306a36Sopenharmony_ci h->expiry_time = now + CACHE_NEW_EXPIRY; 5262306a36Sopenharmony_ci if (now <= detail->flush_time) 5362306a36Sopenharmony_ci /* ensure it isn't already expired */ 5462306a36Sopenharmony_ci now = detail->flush_time + 1; 5562306a36Sopenharmony_ci h->last_refresh = now; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic void cache_fresh_unlocked(struct cache_head *head, 5962306a36Sopenharmony_ci struct cache_detail *detail); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic struct cache_head *sunrpc_cache_find_rcu(struct cache_detail *detail, 6262306a36Sopenharmony_ci struct cache_head *key, 6362306a36Sopenharmony_ci int hash) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct hlist_head *head = &detail->hash_table[hash]; 6662306a36Sopenharmony_ci struct cache_head *tmp; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci rcu_read_lock(); 6962306a36Sopenharmony_ci hlist_for_each_entry_rcu(tmp, head, cache_list) { 7062306a36Sopenharmony_ci if (!detail->match(tmp, key)) 7162306a36Sopenharmony_ci continue; 7262306a36Sopenharmony_ci if (test_bit(CACHE_VALID, &tmp->flags) && 7362306a36Sopenharmony_ci cache_is_expired(detail, tmp)) 7462306a36Sopenharmony_ci continue; 7562306a36Sopenharmony_ci tmp = cache_get_rcu(tmp); 7662306a36Sopenharmony_ci rcu_read_unlock(); 7762306a36Sopenharmony_ci return tmp; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci rcu_read_unlock(); 8062306a36Sopenharmony_ci return NULL; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic void sunrpc_begin_cache_remove_entry(struct cache_head *ch, 8462306a36Sopenharmony_ci struct cache_detail *cd) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci /* Must be called under cd->hash_lock */ 8762306a36Sopenharmony_ci hlist_del_init_rcu(&ch->cache_list); 8862306a36Sopenharmony_ci set_bit(CACHE_CLEANED, &ch->flags); 8962306a36Sopenharmony_ci cd->entries --; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic void sunrpc_end_cache_remove_entry(struct cache_head *ch, 9362306a36Sopenharmony_ci struct cache_detail *cd) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci cache_fresh_unlocked(ch, cd); 9662306a36Sopenharmony_ci cache_put(ch, cd); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic struct cache_head *sunrpc_cache_add_entry(struct cache_detail *detail, 10062306a36Sopenharmony_ci struct cache_head *key, 10162306a36Sopenharmony_ci int hash) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct cache_head *new, *tmp, *freeme = NULL; 10462306a36Sopenharmony_ci struct hlist_head *head = &detail->hash_table[hash]; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci new = detail->alloc(); 10762306a36Sopenharmony_ci if (!new) 10862306a36Sopenharmony_ci return NULL; 10962306a36Sopenharmony_ci /* must fully initialise 'new', else 11062306a36Sopenharmony_ci * we might get lose if we need to 11162306a36Sopenharmony_ci * cache_put it soon. 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_ci cache_init(new, detail); 11462306a36Sopenharmony_ci detail->init(new, key); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci spin_lock(&detail->hash_lock); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* check if entry appeared while we slept */ 11962306a36Sopenharmony_ci hlist_for_each_entry_rcu(tmp, head, cache_list, 12062306a36Sopenharmony_ci lockdep_is_held(&detail->hash_lock)) { 12162306a36Sopenharmony_ci if (!detail->match(tmp, key)) 12262306a36Sopenharmony_ci continue; 12362306a36Sopenharmony_ci if (test_bit(CACHE_VALID, &tmp->flags) && 12462306a36Sopenharmony_ci cache_is_expired(detail, tmp)) { 12562306a36Sopenharmony_ci sunrpc_begin_cache_remove_entry(tmp, detail); 12662306a36Sopenharmony_ci trace_cache_entry_expired(detail, tmp); 12762306a36Sopenharmony_ci freeme = tmp; 12862306a36Sopenharmony_ci break; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci cache_get(tmp); 13162306a36Sopenharmony_ci spin_unlock(&detail->hash_lock); 13262306a36Sopenharmony_ci cache_put(new, detail); 13362306a36Sopenharmony_ci return tmp; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci hlist_add_head_rcu(&new->cache_list, head); 13762306a36Sopenharmony_ci detail->entries++; 13862306a36Sopenharmony_ci cache_get(new); 13962306a36Sopenharmony_ci spin_unlock(&detail->hash_lock); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (freeme) 14262306a36Sopenharmony_ci sunrpc_end_cache_remove_entry(freeme, detail); 14362306a36Sopenharmony_ci return new; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistruct cache_head *sunrpc_cache_lookup_rcu(struct cache_detail *detail, 14762306a36Sopenharmony_ci struct cache_head *key, int hash) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct cache_head *ret; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci ret = sunrpc_cache_find_rcu(detail, key, hash); 15262306a36Sopenharmony_ci if (ret) 15362306a36Sopenharmony_ci return ret; 15462306a36Sopenharmony_ci /* Didn't find anything, insert an empty entry */ 15562306a36Sopenharmony_ci return sunrpc_cache_add_entry(detail, key, hash); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sunrpc_cache_lookup_rcu); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic void cache_dequeue(struct cache_detail *detail, struct cache_head *ch); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic void cache_fresh_locked(struct cache_head *head, time64_t expiry, 16262306a36Sopenharmony_ci struct cache_detail *detail) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci time64_t now = seconds_since_boot(); 16562306a36Sopenharmony_ci if (now <= detail->flush_time) 16662306a36Sopenharmony_ci /* ensure it isn't immediately treated as expired */ 16762306a36Sopenharmony_ci now = detail->flush_time + 1; 16862306a36Sopenharmony_ci head->expiry_time = expiry; 16962306a36Sopenharmony_ci head->last_refresh = now; 17062306a36Sopenharmony_ci smp_wmb(); /* paired with smp_rmb() in cache_is_valid() */ 17162306a36Sopenharmony_ci set_bit(CACHE_VALID, &head->flags); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic void cache_fresh_unlocked(struct cache_head *head, 17562306a36Sopenharmony_ci struct cache_detail *detail) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci if (test_and_clear_bit(CACHE_PENDING, &head->flags)) { 17862306a36Sopenharmony_ci cache_revisit_request(head); 17962306a36Sopenharmony_ci cache_dequeue(detail, head); 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic void cache_make_negative(struct cache_detail *detail, 18462306a36Sopenharmony_ci struct cache_head *h) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci set_bit(CACHE_NEGATIVE, &h->flags); 18762306a36Sopenharmony_ci trace_cache_entry_make_negative(detail, h); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic void cache_entry_update(struct cache_detail *detail, 19162306a36Sopenharmony_ci struct cache_head *h, 19262306a36Sopenharmony_ci struct cache_head *new) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci if (!test_bit(CACHE_NEGATIVE, &new->flags)) { 19562306a36Sopenharmony_ci detail->update(h, new); 19662306a36Sopenharmony_ci trace_cache_entry_update(detail, h); 19762306a36Sopenharmony_ci } else { 19862306a36Sopenharmony_ci cache_make_negative(detail, h); 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistruct cache_head *sunrpc_cache_update(struct cache_detail *detail, 20362306a36Sopenharmony_ci struct cache_head *new, struct cache_head *old, int hash) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci /* The 'old' entry is to be replaced by 'new'. 20662306a36Sopenharmony_ci * If 'old' is not VALID, we update it directly, 20762306a36Sopenharmony_ci * otherwise we need to replace it 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci struct cache_head *tmp; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (!test_bit(CACHE_VALID, &old->flags)) { 21262306a36Sopenharmony_ci spin_lock(&detail->hash_lock); 21362306a36Sopenharmony_ci if (!test_bit(CACHE_VALID, &old->flags)) { 21462306a36Sopenharmony_ci cache_entry_update(detail, old, new); 21562306a36Sopenharmony_ci cache_fresh_locked(old, new->expiry_time, detail); 21662306a36Sopenharmony_ci spin_unlock(&detail->hash_lock); 21762306a36Sopenharmony_ci cache_fresh_unlocked(old, detail); 21862306a36Sopenharmony_ci return old; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci spin_unlock(&detail->hash_lock); 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci /* We need to insert a new entry */ 22362306a36Sopenharmony_ci tmp = detail->alloc(); 22462306a36Sopenharmony_ci if (!tmp) { 22562306a36Sopenharmony_ci cache_put(old, detail); 22662306a36Sopenharmony_ci return NULL; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci cache_init(tmp, detail); 22962306a36Sopenharmony_ci detail->init(tmp, old); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci spin_lock(&detail->hash_lock); 23262306a36Sopenharmony_ci cache_entry_update(detail, tmp, new); 23362306a36Sopenharmony_ci hlist_add_head(&tmp->cache_list, &detail->hash_table[hash]); 23462306a36Sopenharmony_ci detail->entries++; 23562306a36Sopenharmony_ci cache_get(tmp); 23662306a36Sopenharmony_ci cache_fresh_locked(tmp, new->expiry_time, detail); 23762306a36Sopenharmony_ci cache_fresh_locked(old, 0, detail); 23862306a36Sopenharmony_ci spin_unlock(&detail->hash_lock); 23962306a36Sopenharmony_ci cache_fresh_unlocked(tmp, detail); 24062306a36Sopenharmony_ci cache_fresh_unlocked(old, detail); 24162306a36Sopenharmony_ci cache_put(old, detail); 24262306a36Sopenharmony_ci return tmp; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sunrpc_cache_update); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic inline int cache_is_valid(struct cache_head *h) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci if (!test_bit(CACHE_VALID, &h->flags)) 24962306a36Sopenharmony_ci return -EAGAIN; 25062306a36Sopenharmony_ci else { 25162306a36Sopenharmony_ci /* entry is valid */ 25262306a36Sopenharmony_ci if (test_bit(CACHE_NEGATIVE, &h->flags)) 25362306a36Sopenharmony_ci return -ENOENT; 25462306a36Sopenharmony_ci else { 25562306a36Sopenharmony_ci /* 25662306a36Sopenharmony_ci * In combination with write barrier in 25762306a36Sopenharmony_ci * sunrpc_cache_update, ensures that anyone 25862306a36Sopenharmony_ci * using the cache entry after this sees the 25962306a36Sopenharmony_ci * updated contents: 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_ci smp_rmb(); 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic int try_to_negate_entry(struct cache_detail *detail, struct cache_head *h) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci int rv; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci spin_lock(&detail->hash_lock); 27262306a36Sopenharmony_ci rv = cache_is_valid(h); 27362306a36Sopenharmony_ci if (rv == -EAGAIN) { 27462306a36Sopenharmony_ci cache_make_negative(detail, h); 27562306a36Sopenharmony_ci cache_fresh_locked(h, seconds_since_boot()+CACHE_NEW_EXPIRY, 27662306a36Sopenharmony_ci detail); 27762306a36Sopenharmony_ci rv = -ENOENT; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci spin_unlock(&detail->hash_lock); 28062306a36Sopenharmony_ci cache_fresh_unlocked(h, detail); 28162306a36Sopenharmony_ci return rv; 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci/* 28562306a36Sopenharmony_ci * This is the generic cache management routine for all 28662306a36Sopenharmony_ci * the authentication caches. 28762306a36Sopenharmony_ci * It checks the currency of a cache item and will (later) 28862306a36Sopenharmony_ci * initiate an upcall to fill it if needed. 28962306a36Sopenharmony_ci * 29062306a36Sopenharmony_ci * 29162306a36Sopenharmony_ci * Returns 0 if the cache_head can be used, or cache_puts it and returns 29262306a36Sopenharmony_ci * -EAGAIN if upcall is pending and request has been queued 29362306a36Sopenharmony_ci * -ETIMEDOUT if upcall failed or request could not be queue or 29462306a36Sopenharmony_ci * upcall completed but item is still invalid (implying that 29562306a36Sopenharmony_ci * the cache item has been replaced with a newer one). 29662306a36Sopenharmony_ci * -ENOENT if cache entry was negative 29762306a36Sopenharmony_ci */ 29862306a36Sopenharmony_ciint cache_check(struct cache_detail *detail, 29962306a36Sopenharmony_ci struct cache_head *h, struct cache_req *rqstp) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci int rv; 30262306a36Sopenharmony_ci time64_t refresh_age, age; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci /* First decide return status as best we can */ 30562306a36Sopenharmony_ci rv = cache_is_valid(h); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* now see if we want to start an upcall */ 30862306a36Sopenharmony_ci refresh_age = (h->expiry_time - h->last_refresh); 30962306a36Sopenharmony_ci age = seconds_since_boot() - h->last_refresh; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (rqstp == NULL) { 31262306a36Sopenharmony_ci if (rv == -EAGAIN) 31362306a36Sopenharmony_ci rv = -ENOENT; 31462306a36Sopenharmony_ci } else if (rv == -EAGAIN || 31562306a36Sopenharmony_ci (h->expiry_time != 0 && age > refresh_age/2)) { 31662306a36Sopenharmony_ci dprintk("RPC: Want update, refage=%lld, age=%lld\n", 31762306a36Sopenharmony_ci refresh_age, age); 31862306a36Sopenharmony_ci switch (detail->cache_upcall(detail, h)) { 31962306a36Sopenharmony_ci case -EINVAL: 32062306a36Sopenharmony_ci rv = try_to_negate_entry(detail, h); 32162306a36Sopenharmony_ci break; 32262306a36Sopenharmony_ci case -EAGAIN: 32362306a36Sopenharmony_ci cache_fresh_unlocked(h, detail); 32462306a36Sopenharmony_ci break; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (rv == -EAGAIN) { 32962306a36Sopenharmony_ci if (!cache_defer_req(rqstp, h)) { 33062306a36Sopenharmony_ci /* 33162306a36Sopenharmony_ci * Request was not deferred; handle it as best 33262306a36Sopenharmony_ci * we can ourselves: 33362306a36Sopenharmony_ci */ 33462306a36Sopenharmony_ci rv = cache_is_valid(h); 33562306a36Sopenharmony_ci if (rv == -EAGAIN) 33662306a36Sopenharmony_ci rv = -ETIMEDOUT; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci if (rv) 34062306a36Sopenharmony_ci cache_put(h, detail); 34162306a36Sopenharmony_ci return rv; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cache_check); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci/* 34662306a36Sopenharmony_ci * caches need to be periodically cleaned. 34762306a36Sopenharmony_ci * For this we maintain a list of cache_detail and 34862306a36Sopenharmony_ci * a current pointer into that list and into the table 34962306a36Sopenharmony_ci * for that entry. 35062306a36Sopenharmony_ci * 35162306a36Sopenharmony_ci * Each time cache_clean is called it finds the next non-empty entry 35262306a36Sopenharmony_ci * in the current table and walks the list in that entry 35362306a36Sopenharmony_ci * looking for entries that can be removed. 35462306a36Sopenharmony_ci * 35562306a36Sopenharmony_ci * An entry gets removed if: 35662306a36Sopenharmony_ci * - The expiry is before current time 35762306a36Sopenharmony_ci * - The last_refresh time is before the flush_time for that cache 35862306a36Sopenharmony_ci * 35962306a36Sopenharmony_ci * later we might drop old entries with non-NEVER expiry if that table 36062306a36Sopenharmony_ci * is getting 'full' for some definition of 'full' 36162306a36Sopenharmony_ci * 36262306a36Sopenharmony_ci * The question of "how often to scan a table" is an interesting one 36362306a36Sopenharmony_ci * and is answered in part by the use of the "nextcheck" field in the 36462306a36Sopenharmony_ci * cache_detail. 36562306a36Sopenharmony_ci * When a scan of a table begins, the nextcheck field is set to a time 36662306a36Sopenharmony_ci * that is well into the future. 36762306a36Sopenharmony_ci * While scanning, if an expiry time is found that is earlier than the 36862306a36Sopenharmony_ci * current nextcheck time, nextcheck is set to that expiry time. 36962306a36Sopenharmony_ci * If the flush_time is ever set to a time earlier than the nextcheck 37062306a36Sopenharmony_ci * time, the nextcheck time is then set to that flush_time. 37162306a36Sopenharmony_ci * 37262306a36Sopenharmony_ci * A table is then only scanned if the current time is at least 37362306a36Sopenharmony_ci * the nextcheck time. 37462306a36Sopenharmony_ci * 37562306a36Sopenharmony_ci */ 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic LIST_HEAD(cache_list); 37862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(cache_list_lock); 37962306a36Sopenharmony_cistatic struct cache_detail *current_detail; 38062306a36Sopenharmony_cistatic int current_index; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic void do_cache_clean(struct work_struct *work); 38362306a36Sopenharmony_cistatic struct delayed_work cache_cleaner; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_civoid sunrpc_init_cache_detail(struct cache_detail *cd) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci spin_lock_init(&cd->hash_lock); 38862306a36Sopenharmony_ci INIT_LIST_HEAD(&cd->queue); 38962306a36Sopenharmony_ci spin_lock(&cache_list_lock); 39062306a36Sopenharmony_ci cd->nextcheck = 0; 39162306a36Sopenharmony_ci cd->entries = 0; 39262306a36Sopenharmony_ci atomic_set(&cd->writers, 0); 39362306a36Sopenharmony_ci cd->last_close = 0; 39462306a36Sopenharmony_ci cd->last_warn = -1; 39562306a36Sopenharmony_ci list_add(&cd->others, &cache_list); 39662306a36Sopenharmony_ci spin_unlock(&cache_list_lock); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* start the cleaning process */ 39962306a36Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, &cache_cleaner, 0); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sunrpc_init_cache_detail); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_civoid sunrpc_destroy_cache_detail(struct cache_detail *cd) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci cache_purge(cd); 40662306a36Sopenharmony_ci spin_lock(&cache_list_lock); 40762306a36Sopenharmony_ci spin_lock(&cd->hash_lock); 40862306a36Sopenharmony_ci if (current_detail == cd) 40962306a36Sopenharmony_ci current_detail = NULL; 41062306a36Sopenharmony_ci list_del_init(&cd->others); 41162306a36Sopenharmony_ci spin_unlock(&cd->hash_lock); 41262306a36Sopenharmony_ci spin_unlock(&cache_list_lock); 41362306a36Sopenharmony_ci if (list_empty(&cache_list)) { 41462306a36Sopenharmony_ci /* module must be being unloaded so its safe to kill the worker */ 41562306a36Sopenharmony_ci cancel_delayed_work_sync(&cache_cleaner); 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sunrpc_destroy_cache_detail); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci/* clean cache tries to find something to clean 42162306a36Sopenharmony_ci * and cleans it. 42262306a36Sopenharmony_ci * It returns 1 if it cleaned something, 42362306a36Sopenharmony_ci * 0 if it didn't find anything this time 42462306a36Sopenharmony_ci * -1 if it fell off the end of the list. 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_cistatic int cache_clean(void) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci int rv = 0; 42962306a36Sopenharmony_ci struct list_head *next; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci spin_lock(&cache_list_lock); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* find a suitable table if we don't already have one */ 43462306a36Sopenharmony_ci while (current_detail == NULL || 43562306a36Sopenharmony_ci current_index >= current_detail->hash_size) { 43662306a36Sopenharmony_ci if (current_detail) 43762306a36Sopenharmony_ci next = current_detail->others.next; 43862306a36Sopenharmony_ci else 43962306a36Sopenharmony_ci next = cache_list.next; 44062306a36Sopenharmony_ci if (next == &cache_list) { 44162306a36Sopenharmony_ci current_detail = NULL; 44262306a36Sopenharmony_ci spin_unlock(&cache_list_lock); 44362306a36Sopenharmony_ci return -1; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci current_detail = list_entry(next, struct cache_detail, others); 44662306a36Sopenharmony_ci if (current_detail->nextcheck > seconds_since_boot()) 44762306a36Sopenharmony_ci current_index = current_detail->hash_size; 44862306a36Sopenharmony_ci else { 44962306a36Sopenharmony_ci current_index = 0; 45062306a36Sopenharmony_ci current_detail->nextcheck = seconds_since_boot()+30*60; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci /* find a non-empty bucket in the table */ 45562306a36Sopenharmony_ci while (current_detail && 45662306a36Sopenharmony_ci current_index < current_detail->hash_size && 45762306a36Sopenharmony_ci hlist_empty(¤t_detail->hash_table[current_index])) 45862306a36Sopenharmony_ci current_index++; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* find a cleanable entry in the bucket and clean it, or set to next bucket */ 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci if (current_detail && current_index < current_detail->hash_size) { 46362306a36Sopenharmony_ci struct cache_head *ch = NULL; 46462306a36Sopenharmony_ci struct cache_detail *d; 46562306a36Sopenharmony_ci struct hlist_head *head; 46662306a36Sopenharmony_ci struct hlist_node *tmp; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci spin_lock(¤t_detail->hash_lock); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci /* Ok, now to clean this strand */ 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci head = ¤t_detail->hash_table[current_index]; 47362306a36Sopenharmony_ci hlist_for_each_entry_safe(ch, tmp, head, cache_list) { 47462306a36Sopenharmony_ci if (current_detail->nextcheck > ch->expiry_time) 47562306a36Sopenharmony_ci current_detail->nextcheck = ch->expiry_time+1; 47662306a36Sopenharmony_ci if (!cache_is_expired(current_detail, ch)) 47762306a36Sopenharmony_ci continue; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci sunrpc_begin_cache_remove_entry(ch, current_detail); 48062306a36Sopenharmony_ci trace_cache_entry_expired(current_detail, ch); 48162306a36Sopenharmony_ci rv = 1; 48262306a36Sopenharmony_ci break; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci spin_unlock(¤t_detail->hash_lock); 48662306a36Sopenharmony_ci d = current_detail; 48762306a36Sopenharmony_ci if (!ch) 48862306a36Sopenharmony_ci current_index ++; 48962306a36Sopenharmony_ci spin_unlock(&cache_list_lock); 49062306a36Sopenharmony_ci if (ch) 49162306a36Sopenharmony_ci sunrpc_end_cache_remove_entry(ch, d); 49262306a36Sopenharmony_ci } else 49362306a36Sopenharmony_ci spin_unlock(&cache_list_lock); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci return rv; 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci/* 49962306a36Sopenharmony_ci * We want to regularly clean the cache, so we need to schedule some work ... 50062306a36Sopenharmony_ci */ 50162306a36Sopenharmony_cistatic void do_cache_clean(struct work_struct *work) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci int delay; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (list_empty(&cache_list)) 50662306a36Sopenharmony_ci return; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (cache_clean() == -1) 50962306a36Sopenharmony_ci delay = round_jiffies_relative(30*HZ); 51062306a36Sopenharmony_ci else 51162306a36Sopenharmony_ci delay = 5; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, &cache_cleaner, delay); 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci/* 51862306a36Sopenharmony_ci * Clean all caches promptly. This just calls cache_clean 51962306a36Sopenharmony_ci * repeatedly until we are sure that every cache has had a chance to 52062306a36Sopenharmony_ci * be fully cleaned 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_civoid cache_flush(void) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci while (cache_clean() != -1) 52562306a36Sopenharmony_ci cond_resched(); 52662306a36Sopenharmony_ci while (cache_clean() != -1) 52762306a36Sopenharmony_ci cond_resched(); 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cache_flush); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_civoid cache_purge(struct cache_detail *detail) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci struct cache_head *ch = NULL; 53462306a36Sopenharmony_ci struct hlist_head *head = NULL; 53562306a36Sopenharmony_ci int i = 0; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci spin_lock(&detail->hash_lock); 53862306a36Sopenharmony_ci if (!detail->entries) { 53962306a36Sopenharmony_ci spin_unlock(&detail->hash_lock); 54062306a36Sopenharmony_ci return; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci dprintk("RPC: %d entries in %s cache\n", detail->entries, detail->name); 54462306a36Sopenharmony_ci for (i = 0; i < detail->hash_size; i++) { 54562306a36Sopenharmony_ci head = &detail->hash_table[i]; 54662306a36Sopenharmony_ci while (!hlist_empty(head)) { 54762306a36Sopenharmony_ci ch = hlist_entry(head->first, struct cache_head, 54862306a36Sopenharmony_ci cache_list); 54962306a36Sopenharmony_ci sunrpc_begin_cache_remove_entry(ch, detail); 55062306a36Sopenharmony_ci spin_unlock(&detail->hash_lock); 55162306a36Sopenharmony_ci sunrpc_end_cache_remove_entry(ch, detail); 55262306a36Sopenharmony_ci spin_lock(&detail->hash_lock); 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci spin_unlock(&detail->hash_lock); 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cache_purge); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci/* 56162306a36Sopenharmony_ci * Deferral and Revisiting of Requests. 56262306a36Sopenharmony_ci * 56362306a36Sopenharmony_ci * If a cache lookup finds a pending entry, we 56462306a36Sopenharmony_ci * need to defer the request and revisit it later. 56562306a36Sopenharmony_ci * All deferred requests are stored in a hash table, 56662306a36Sopenharmony_ci * indexed by "struct cache_head *". 56762306a36Sopenharmony_ci * As it may be wasteful to store a whole request 56862306a36Sopenharmony_ci * structure, we allow the request to provide a 56962306a36Sopenharmony_ci * deferred form, which must contain a 57062306a36Sopenharmony_ci * 'struct cache_deferred_req' 57162306a36Sopenharmony_ci * This cache_deferred_req contains a method to allow 57262306a36Sopenharmony_ci * it to be revisited when cache info is available 57362306a36Sopenharmony_ci */ 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci#define DFR_HASHSIZE (PAGE_SIZE/sizeof(struct list_head)) 57662306a36Sopenharmony_ci#define DFR_HASH(item) ((((long)item)>>4 ^ (((long)item)>>13)) % DFR_HASHSIZE) 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci#define DFR_MAX 300 /* ??? */ 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_cistatic DEFINE_SPINLOCK(cache_defer_lock); 58162306a36Sopenharmony_cistatic LIST_HEAD(cache_defer_list); 58262306a36Sopenharmony_cistatic struct hlist_head cache_defer_hash[DFR_HASHSIZE]; 58362306a36Sopenharmony_cistatic int cache_defer_cnt; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic void __unhash_deferred_req(struct cache_deferred_req *dreq) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci hlist_del_init(&dreq->hash); 58862306a36Sopenharmony_ci if (!list_empty(&dreq->recent)) { 58962306a36Sopenharmony_ci list_del_init(&dreq->recent); 59062306a36Sopenharmony_ci cache_defer_cnt--; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_cistatic void __hash_deferred_req(struct cache_deferred_req *dreq, struct cache_head *item) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci int hash = DFR_HASH(item); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci INIT_LIST_HEAD(&dreq->recent); 59962306a36Sopenharmony_ci hlist_add_head(&dreq->hash, &cache_defer_hash[hash]); 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic void setup_deferral(struct cache_deferred_req *dreq, 60362306a36Sopenharmony_ci struct cache_head *item, 60462306a36Sopenharmony_ci int count_me) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci dreq->item = item; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci spin_lock(&cache_defer_lock); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci __hash_deferred_req(dreq, item); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (count_me) { 61462306a36Sopenharmony_ci cache_defer_cnt++; 61562306a36Sopenharmony_ci list_add(&dreq->recent, &cache_defer_list); 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci spin_unlock(&cache_defer_lock); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistruct thread_deferred_req { 62362306a36Sopenharmony_ci struct cache_deferred_req handle; 62462306a36Sopenharmony_ci struct completion completion; 62562306a36Sopenharmony_ci}; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_cistatic void cache_restart_thread(struct cache_deferred_req *dreq, int too_many) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci struct thread_deferred_req *dr = 63062306a36Sopenharmony_ci container_of(dreq, struct thread_deferred_req, handle); 63162306a36Sopenharmony_ci complete(&dr->completion); 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_cistatic void cache_wait_req(struct cache_req *req, struct cache_head *item) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci struct thread_deferred_req sleeper; 63762306a36Sopenharmony_ci struct cache_deferred_req *dreq = &sleeper.handle; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci sleeper.completion = COMPLETION_INITIALIZER_ONSTACK(sleeper.completion); 64062306a36Sopenharmony_ci dreq->revisit = cache_restart_thread; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci setup_deferral(dreq, item, 0); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (!test_bit(CACHE_PENDING, &item->flags) || 64562306a36Sopenharmony_ci wait_for_completion_interruptible_timeout( 64662306a36Sopenharmony_ci &sleeper.completion, req->thread_wait) <= 0) { 64762306a36Sopenharmony_ci /* The completion wasn't completed, so we need 64862306a36Sopenharmony_ci * to clean up 64962306a36Sopenharmony_ci */ 65062306a36Sopenharmony_ci spin_lock(&cache_defer_lock); 65162306a36Sopenharmony_ci if (!hlist_unhashed(&sleeper.handle.hash)) { 65262306a36Sopenharmony_ci __unhash_deferred_req(&sleeper.handle); 65362306a36Sopenharmony_ci spin_unlock(&cache_defer_lock); 65462306a36Sopenharmony_ci } else { 65562306a36Sopenharmony_ci /* cache_revisit_request already removed 65662306a36Sopenharmony_ci * this from the hash table, but hasn't 65762306a36Sopenharmony_ci * called ->revisit yet. It will very soon 65862306a36Sopenharmony_ci * and we need to wait for it. 65962306a36Sopenharmony_ci */ 66062306a36Sopenharmony_ci spin_unlock(&cache_defer_lock); 66162306a36Sopenharmony_ci wait_for_completion(&sleeper.completion); 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cistatic void cache_limit_defers(void) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci /* Make sure we haven't exceed the limit of allowed deferred 66962306a36Sopenharmony_ci * requests. 67062306a36Sopenharmony_ci */ 67162306a36Sopenharmony_ci struct cache_deferred_req *discard = NULL; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci if (cache_defer_cnt <= DFR_MAX) 67462306a36Sopenharmony_ci return; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci spin_lock(&cache_defer_lock); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci /* Consider removing either the first or the last */ 67962306a36Sopenharmony_ci if (cache_defer_cnt > DFR_MAX) { 68062306a36Sopenharmony_ci if (get_random_u32_below(2)) 68162306a36Sopenharmony_ci discard = list_entry(cache_defer_list.next, 68262306a36Sopenharmony_ci struct cache_deferred_req, recent); 68362306a36Sopenharmony_ci else 68462306a36Sopenharmony_ci discard = list_entry(cache_defer_list.prev, 68562306a36Sopenharmony_ci struct cache_deferred_req, recent); 68662306a36Sopenharmony_ci __unhash_deferred_req(discard); 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci spin_unlock(&cache_defer_lock); 68962306a36Sopenharmony_ci if (discard) 69062306a36Sopenharmony_ci discard->revisit(discard, 1); 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_FAIL_SUNRPC) 69462306a36Sopenharmony_cistatic inline bool cache_defer_immediately(void) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci return !fail_sunrpc.ignore_cache_wait && 69762306a36Sopenharmony_ci should_fail(&fail_sunrpc.attr, 1); 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ci#else 70062306a36Sopenharmony_cistatic inline bool cache_defer_immediately(void) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci return false; 70362306a36Sopenharmony_ci} 70462306a36Sopenharmony_ci#endif 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci/* Return true if and only if a deferred request is queued. */ 70762306a36Sopenharmony_cistatic bool cache_defer_req(struct cache_req *req, struct cache_head *item) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci struct cache_deferred_req *dreq; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci if (!cache_defer_immediately()) { 71262306a36Sopenharmony_ci cache_wait_req(req, item); 71362306a36Sopenharmony_ci if (!test_bit(CACHE_PENDING, &item->flags)) 71462306a36Sopenharmony_ci return false; 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci dreq = req->defer(req); 71862306a36Sopenharmony_ci if (dreq == NULL) 71962306a36Sopenharmony_ci return false; 72062306a36Sopenharmony_ci setup_deferral(dreq, item, 1); 72162306a36Sopenharmony_ci if (!test_bit(CACHE_PENDING, &item->flags)) 72262306a36Sopenharmony_ci /* Bit could have been cleared before we managed to 72362306a36Sopenharmony_ci * set up the deferral, so need to revisit just in case 72462306a36Sopenharmony_ci */ 72562306a36Sopenharmony_ci cache_revisit_request(item); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci cache_limit_defers(); 72862306a36Sopenharmony_ci return true; 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_cistatic void cache_revisit_request(struct cache_head *item) 73262306a36Sopenharmony_ci{ 73362306a36Sopenharmony_ci struct cache_deferred_req *dreq; 73462306a36Sopenharmony_ci struct list_head pending; 73562306a36Sopenharmony_ci struct hlist_node *tmp; 73662306a36Sopenharmony_ci int hash = DFR_HASH(item); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci INIT_LIST_HEAD(&pending); 73962306a36Sopenharmony_ci spin_lock(&cache_defer_lock); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci hlist_for_each_entry_safe(dreq, tmp, &cache_defer_hash[hash], hash) 74262306a36Sopenharmony_ci if (dreq->item == item) { 74362306a36Sopenharmony_ci __unhash_deferred_req(dreq); 74462306a36Sopenharmony_ci list_add(&dreq->recent, &pending); 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci spin_unlock(&cache_defer_lock); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci while (!list_empty(&pending)) { 75062306a36Sopenharmony_ci dreq = list_entry(pending.next, struct cache_deferred_req, recent); 75162306a36Sopenharmony_ci list_del_init(&dreq->recent); 75262306a36Sopenharmony_ci dreq->revisit(dreq, 0); 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_civoid cache_clean_deferred(void *owner) 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci struct cache_deferred_req *dreq, *tmp; 75962306a36Sopenharmony_ci struct list_head pending; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci INIT_LIST_HEAD(&pending); 76362306a36Sopenharmony_ci spin_lock(&cache_defer_lock); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci list_for_each_entry_safe(dreq, tmp, &cache_defer_list, recent) { 76662306a36Sopenharmony_ci if (dreq->owner == owner) { 76762306a36Sopenharmony_ci __unhash_deferred_req(dreq); 76862306a36Sopenharmony_ci list_add(&dreq->recent, &pending); 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci spin_unlock(&cache_defer_lock); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci while (!list_empty(&pending)) { 77462306a36Sopenharmony_ci dreq = list_entry(pending.next, struct cache_deferred_req, recent); 77562306a36Sopenharmony_ci list_del_init(&dreq->recent); 77662306a36Sopenharmony_ci dreq->revisit(dreq, 1); 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci} 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci/* 78162306a36Sopenharmony_ci * communicate with user-space 78262306a36Sopenharmony_ci * 78362306a36Sopenharmony_ci * We have a magic /proc file - /proc/net/rpc/<cachename>/channel. 78462306a36Sopenharmony_ci * On read, you get a full request, or block. 78562306a36Sopenharmony_ci * On write, an update request is processed. 78662306a36Sopenharmony_ci * Poll works if anything to read, and always allows write. 78762306a36Sopenharmony_ci * 78862306a36Sopenharmony_ci * Implemented by linked list of requests. Each open file has 78962306a36Sopenharmony_ci * a ->private that also exists in this list. New requests are added 79062306a36Sopenharmony_ci * to the end and may wakeup and preceding readers. 79162306a36Sopenharmony_ci * New readers are added to the head. If, on read, an item is found with 79262306a36Sopenharmony_ci * CACHE_UPCALLING clear, we free it from the list. 79362306a36Sopenharmony_ci * 79462306a36Sopenharmony_ci */ 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_cistatic DEFINE_SPINLOCK(queue_lock); 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_cistruct cache_queue { 79962306a36Sopenharmony_ci struct list_head list; 80062306a36Sopenharmony_ci int reader; /* if 0, then request */ 80162306a36Sopenharmony_ci}; 80262306a36Sopenharmony_cistruct cache_request { 80362306a36Sopenharmony_ci struct cache_queue q; 80462306a36Sopenharmony_ci struct cache_head *item; 80562306a36Sopenharmony_ci char * buf; 80662306a36Sopenharmony_ci int len; 80762306a36Sopenharmony_ci int readers; 80862306a36Sopenharmony_ci}; 80962306a36Sopenharmony_cistruct cache_reader { 81062306a36Sopenharmony_ci struct cache_queue q; 81162306a36Sopenharmony_ci int offset; /* if non-0, we have a refcnt on next request */ 81262306a36Sopenharmony_ci}; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_cistatic int cache_request(struct cache_detail *detail, 81562306a36Sopenharmony_ci struct cache_request *crq) 81662306a36Sopenharmony_ci{ 81762306a36Sopenharmony_ci char *bp = crq->buf; 81862306a36Sopenharmony_ci int len = PAGE_SIZE; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci detail->cache_request(detail, crq->item, &bp, &len); 82162306a36Sopenharmony_ci if (len < 0) 82262306a36Sopenharmony_ci return -E2BIG; 82362306a36Sopenharmony_ci return PAGE_SIZE - len; 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_cistatic ssize_t cache_read(struct file *filp, char __user *buf, size_t count, 82762306a36Sopenharmony_ci loff_t *ppos, struct cache_detail *cd) 82862306a36Sopenharmony_ci{ 82962306a36Sopenharmony_ci struct cache_reader *rp = filp->private_data; 83062306a36Sopenharmony_ci struct cache_request *rq; 83162306a36Sopenharmony_ci struct inode *inode = file_inode(filp); 83262306a36Sopenharmony_ci int err; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci if (count == 0) 83562306a36Sopenharmony_ci return 0; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci inode_lock(inode); /* protect against multiple concurrent 83862306a36Sopenharmony_ci * readers on this file */ 83962306a36Sopenharmony_ci again: 84062306a36Sopenharmony_ci spin_lock(&queue_lock); 84162306a36Sopenharmony_ci /* need to find next request */ 84262306a36Sopenharmony_ci while (rp->q.list.next != &cd->queue && 84362306a36Sopenharmony_ci list_entry(rp->q.list.next, struct cache_queue, list) 84462306a36Sopenharmony_ci ->reader) { 84562306a36Sopenharmony_ci struct list_head *next = rp->q.list.next; 84662306a36Sopenharmony_ci list_move(&rp->q.list, next); 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci if (rp->q.list.next == &cd->queue) { 84962306a36Sopenharmony_ci spin_unlock(&queue_lock); 85062306a36Sopenharmony_ci inode_unlock(inode); 85162306a36Sopenharmony_ci WARN_ON_ONCE(rp->offset); 85262306a36Sopenharmony_ci return 0; 85362306a36Sopenharmony_ci } 85462306a36Sopenharmony_ci rq = container_of(rp->q.list.next, struct cache_request, q.list); 85562306a36Sopenharmony_ci WARN_ON_ONCE(rq->q.reader); 85662306a36Sopenharmony_ci if (rp->offset == 0) 85762306a36Sopenharmony_ci rq->readers++; 85862306a36Sopenharmony_ci spin_unlock(&queue_lock); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci if (rq->len == 0) { 86162306a36Sopenharmony_ci err = cache_request(cd, rq); 86262306a36Sopenharmony_ci if (err < 0) 86362306a36Sopenharmony_ci goto out; 86462306a36Sopenharmony_ci rq->len = err; 86562306a36Sopenharmony_ci } 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci if (rp->offset == 0 && !test_bit(CACHE_PENDING, &rq->item->flags)) { 86862306a36Sopenharmony_ci err = -EAGAIN; 86962306a36Sopenharmony_ci spin_lock(&queue_lock); 87062306a36Sopenharmony_ci list_move(&rp->q.list, &rq->q.list); 87162306a36Sopenharmony_ci spin_unlock(&queue_lock); 87262306a36Sopenharmony_ci } else { 87362306a36Sopenharmony_ci if (rp->offset + count > rq->len) 87462306a36Sopenharmony_ci count = rq->len - rp->offset; 87562306a36Sopenharmony_ci err = -EFAULT; 87662306a36Sopenharmony_ci if (copy_to_user(buf, rq->buf + rp->offset, count)) 87762306a36Sopenharmony_ci goto out; 87862306a36Sopenharmony_ci rp->offset += count; 87962306a36Sopenharmony_ci if (rp->offset >= rq->len) { 88062306a36Sopenharmony_ci rp->offset = 0; 88162306a36Sopenharmony_ci spin_lock(&queue_lock); 88262306a36Sopenharmony_ci list_move(&rp->q.list, &rq->q.list); 88362306a36Sopenharmony_ci spin_unlock(&queue_lock); 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci err = 0; 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci out: 88862306a36Sopenharmony_ci if (rp->offset == 0) { 88962306a36Sopenharmony_ci /* need to release rq */ 89062306a36Sopenharmony_ci spin_lock(&queue_lock); 89162306a36Sopenharmony_ci rq->readers--; 89262306a36Sopenharmony_ci if (rq->readers == 0 && 89362306a36Sopenharmony_ci !test_bit(CACHE_PENDING, &rq->item->flags)) { 89462306a36Sopenharmony_ci list_del(&rq->q.list); 89562306a36Sopenharmony_ci spin_unlock(&queue_lock); 89662306a36Sopenharmony_ci cache_put(rq->item, cd); 89762306a36Sopenharmony_ci kfree(rq->buf); 89862306a36Sopenharmony_ci kfree(rq); 89962306a36Sopenharmony_ci } else 90062306a36Sopenharmony_ci spin_unlock(&queue_lock); 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci if (err == -EAGAIN) 90362306a36Sopenharmony_ci goto again; 90462306a36Sopenharmony_ci inode_unlock(inode); 90562306a36Sopenharmony_ci return err ? err : count; 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_cistatic ssize_t cache_do_downcall(char *kaddr, const char __user *buf, 90962306a36Sopenharmony_ci size_t count, struct cache_detail *cd) 91062306a36Sopenharmony_ci{ 91162306a36Sopenharmony_ci ssize_t ret; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci if (count == 0) 91462306a36Sopenharmony_ci return -EINVAL; 91562306a36Sopenharmony_ci if (copy_from_user(kaddr, buf, count)) 91662306a36Sopenharmony_ci return -EFAULT; 91762306a36Sopenharmony_ci kaddr[count] = '\0'; 91862306a36Sopenharmony_ci ret = cd->cache_parse(cd, kaddr, count); 91962306a36Sopenharmony_ci if (!ret) 92062306a36Sopenharmony_ci ret = count; 92162306a36Sopenharmony_ci return ret; 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_cistatic ssize_t cache_downcall(struct address_space *mapping, 92562306a36Sopenharmony_ci const char __user *buf, 92662306a36Sopenharmony_ci size_t count, struct cache_detail *cd) 92762306a36Sopenharmony_ci{ 92862306a36Sopenharmony_ci char *write_buf; 92962306a36Sopenharmony_ci ssize_t ret = -ENOMEM; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci if (count >= 32768) { /* 32k is max userland buffer, lets check anyway */ 93262306a36Sopenharmony_ci ret = -EINVAL; 93362306a36Sopenharmony_ci goto out; 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci write_buf = kvmalloc(count + 1, GFP_KERNEL); 93762306a36Sopenharmony_ci if (!write_buf) 93862306a36Sopenharmony_ci goto out; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci ret = cache_do_downcall(write_buf, buf, count, cd); 94162306a36Sopenharmony_ci kvfree(write_buf); 94262306a36Sopenharmony_ciout: 94362306a36Sopenharmony_ci return ret; 94462306a36Sopenharmony_ci} 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_cistatic ssize_t cache_write(struct file *filp, const char __user *buf, 94762306a36Sopenharmony_ci size_t count, loff_t *ppos, 94862306a36Sopenharmony_ci struct cache_detail *cd) 94962306a36Sopenharmony_ci{ 95062306a36Sopenharmony_ci struct address_space *mapping = filp->f_mapping; 95162306a36Sopenharmony_ci struct inode *inode = file_inode(filp); 95262306a36Sopenharmony_ci ssize_t ret = -EINVAL; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci if (!cd->cache_parse) 95562306a36Sopenharmony_ci goto out; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci inode_lock(inode); 95862306a36Sopenharmony_ci ret = cache_downcall(mapping, buf, count, cd); 95962306a36Sopenharmony_ci inode_unlock(inode); 96062306a36Sopenharmony_ciout: 96162306a36Sopenharmony_ci return ret; 96262306a36Sopenharmony_ci} 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(queue_wait); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_cistatic __poll_t cache_poll(struct file *filp, poll_table *wait, 96762306a36Sopenharmony_ci struct cache_detail *cd) 96862306a36Sopenharmony_ci{ 96962306a36Sopenharmony_ci __poll_t mask; 97062306a36Sopenharmony_ci struct cache_reader *rp = filp->private_data; 97162306a36Sopenharmony_ci struct cache_queue *cq; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci poll_wait(filp, &queue_wait, wait); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci /* alway allow write */ 97662306a36Sopenharmony_ci mask = EPOLLOUT | EPOLLWRNORM; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci if (!rp) 97962306a36Sopenharmony_ci return mask; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci spin_lock(&queue_lock); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci for (cq= &rp->q; &cq->list != &cd->queue; 98462306a36Sopenharmony_ci cq = list_entry(cq->list.next, struct cache_queue, list)) 98562306a36Sopenharmony_ci if (!cq->reader) { 98662306a36Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM; 98762306a36Sopenharmony_ci break; 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci spin_unlock(&queue_lock); 99062306a36Sopenharmony_ci return mask; 99162306a36Sopenharmony_ci} 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_cistatic int cache_ioctl(struct inode *ino, struct file *filp, 99462306a36Sopenharmony_ci unsigned int cmd, unsigned long arg, 99562306a36Sopenharmony_ci struct cache_detail *cd) 99662306a36Sopenharmony_ci{ 99762306a36Sopenharmony_ci int len = 0; 99862306a36Sopenharmony_ci struct cache_reader *rp = filp->private_data; 99962306a36Sopenharmony_ci struct cache_queue *cq; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci if (cmd != FIONREAD || !rp) 100262306a36Sopenharmony_ci return -EINVAL; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci spin_lock(&queue_lock); 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci /* only find the length remaining in current request, 100762306a36Sopenharmony_ci * or the length of the next request 100862306a36Sopenharmony_ci */ 100962306a36Sopenharmony_ci for (cq= &rp->q; &cq->list != &cd->queue; 101062306a36Sopenharmony_ci cq = list_entry(cq->list.next, struct cache_queue, list)) 101162306a36Sopenharmony_ci if (!cq->reader) { 101262306a36Sopenharmony_ci struct cache_request *cr = 101362306a36Sopenharmony_ci container_of(cq, struct cache_request, q); 101462306a36Sopenharmony_ci len = cr->len - rp->offset; 101562306a36Sopenharmony_ci break; 101662306a36Sopenharmony_ci } 101762306a36Sopenharmony_ci spin_unlock(&queue_lock); 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci return put_user(len, (int __user *)arg); 102062306a36Sopenharmony_ci} 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_cistatic int cache_open(struct inode *inode, struct file *filp, 102362306a36Sopenharmony_ci struct cache_detail *cd) 102462306a36Sopenharmony_ci{ 102562306a36Sopenharmony_ci struct cache_reader *rp = NULL; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci if (!cd || !try_module_get(cd->owner)) 102862306a36Sopenharmony_ci return -EACCES; 102962306a36Sopenharmony_ci nonseekable_open(inode, filp); 103062306a36Sopenharmony_ci if (filp->f_mode & FMODE_READ) { 103162306a36Sopenharmony_ci rp = kmalloc(sizeof(*rp), GFP_KERNEL); 103262306a36Sopenharmony_ci if (!rp) { 103362306a36Sopenharmony_ci module_put(cd->owner); 103462306a36Sopenharmony_ci return -ENOMEM; 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci rp->offset = 0; 103762306a36Sopenharmony_ci rp->q.reader = 1; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci spin_lock(&queue_lock); 104062306a36Sopenharmony_ci list_add(&rp->q.list, &cd->queue); 104162306a36Sopenharmony_ci spin_unlock(&queue_lock); 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci if (filp->f_mode & FMODE_WRITE) 104462306a36Sopenharmony_ci atomic_inc(&cd->writers); 104562306a36Sopenharmony_ci filp->private_data = rp; 104662306a36Sopenharmony_ci return 0; 104762306a36Sopenharmony_ci} 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_cistatic int cache_release(struct inode *inode, struct file *filp, 105062306a36Sopenharmony_ci struct cache_detail *cd) 105162306a36Sopenharmony_ci{ 105262306a36Sopenharmony_ci struct cache_reader *rp = filp->private_data; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci if (rp) { 105562306a36Sopenharmony_ci spin_lock(&queue_lock); 105662306a36Sopenharmony_ci if (rp->offset) { 105762306a36Sopenharmony_ci struct cache_queue *cq; 105862306a36Sopenharmony_ci for (cq= &rp->q; &cq->list != &cd->queue; 105962306a36Sopenharmony_ci cq = list_entry(cq->list.next, struct cache_queue, list)) 106062306a36Sopenharmony_ci if (!cq->reader) { 106162306a36Sopenharmony_ci container_of(cq, struct cache_request, q) 106262306a36Sopenharmony_ci ->readers--; 106362306a36Sopenharmony_ci break; 106462306a36Sopenharmony_ci } 106562306a36Sopenharmony_ci rp->offset = 0; 106662306a36Sopenharmony_ci } 106762306a36Sopenharmony_ci list_del(&rp->q.list); 106862306a36Sopenharmony_ci spin_unlock(&queue_lock); 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci filp->private_data = NULL; 107162306a36Sopenharmony_ci kfree(rp); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci if (filp->f_mode & FMODE_WRITE) { 107562306a36Sopenharmony_ci atomic_dec(&cd->writers); 107662306a36Sopenharmony_ci cd->last_close = seconds_since_boot(); 107762306a36Sopenharmony_ci } 107862306a36Sopenharmony_ci module_put(cd->owner); 107962306a36Sopenharmony_ci return 0; 108062306a36Sopenharmony_ci} 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_cistatic void cache_dequeue(struct cache_detail *detail, struct cache_head *ch) 108562306a36Sopenharmony_ci{ 108662306a36Sopenharmony_ci struct cache_queue *cq, *tmp; 108762306a36Sopenharmony_ci struct cache_request *cr; 108862306a36Sopenharmony_ci struct list_head dequeued; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci INIT_LIST_HEAD(&dequeued); 109162306a36Sopenharmony_ci spin_lock(&queue_lock); 109262306a36Sopenharmony_ci list_for_each_entry_safe(cq, tmp, &detail->queue, list) 109362306a36Sopenharmony_ci if (!cq->reader) { 109462306a36Sopenharmony_ci cr = container_of(cq, struct cache_request, q); 109562306a36Sopenharmony_ci if (cr->item != ch) 109662306a36Sopenharmony_ci continue; 109762306a36Sopenharmony_ci if (test_bit(CACHE_PENDING, &ch->flags)) 109862306a36Sopenharmony_ci /* Lost a race and it is pending again */ 109962306a36Sopenharmony_ci break; 110062306a36Sopenharmony_ci if (cr->readers != 0) 110162306a36Sopenharmony_ci continue; 110262306a36Sopenharmony_ci list_move(&cr->q.list, &dequeued); 110362306a36Sopenharmony_ci } 110462306a36Sopenharmony_ci spin_unlock(&queue_lock); 110562306a36Sopenharmony_ci while (!list_empty(&dequeued)) { 110662306a36Sopenharmony_ci cr = list_entry(dequeued.next, struct cache_request, q.list); 110762306a36Sopenharmony_ci list_del(&cr->q.list); 110862306a36Sopenharmony_ci cache_put(cr->item, detail); 110962306a36Sopenharmony_ci kfree(cr->buf); 111062306a36Sopenharmony_ci kfree(cr); 111162306a36Sopenharmony_ci } 111262306a36Sopenharmony_ci} 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci/* 111562306a36Sopenharmony_ci * Support routines for text-based upcalls. 111662306a36Sopenharmony_ci * Fields are separated by spaces. 111762306a36Sopenharmony_ci * Fields are either mangled to quote space tab newline slosh with slosh 111862306a36Sopenharmony_ci * or a hexified with a leading \x 111962306a36Sopenharmony_ci * Record is terminated with newline. 112062306a36Sopenharmony_ci * 112162306a36Sopenharmony_ci */ 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_civoid qword_add(char **bpp, int *lp, char *str) 112462306a36Sopenharmony_ci{ 112562306a36Sopenharmony_ci char *bp = *bpp; 112662306a36Sopenharmony_ci int len = *lp; 112762306a36Sopenharmony_ci int ret; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci if (len < 0) return; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci ret = string_escape_str(str, bp, len, ESCAPE_OCTAL, "\\ \n\t"); 113262306a36Sopenharmony_ci if (ret >= len) { 113362306a36Sopenharmony_ci bp += len; 113462306a36Sopenharmony_ci len = -1; 113562306a36Sopenharmony_ci } else { 113662306a36Sopenharmony_ci bp += ret; 113762306a36Sopenharmony_ci len -= ret; 113862306a36Sopenharmony_ci *bp++ = ' '; 113962306a36Sopenharmony_ci len--; 114062306a36Sopenharmony_ci } 114162306a36Sopenharmony_ci *bpp = bp; 114262306a36Sopenharmony_ci *lp = len; 114362306a36Sopenharmony_ci} 114462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qword_add); 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_civoid qword_addhex(char **bpp, int *lp, char *buf, int blen) 114762306a36Sopenharmony_ci{ 114862306a36Sopenharmony_ci char *bp = *bpp; 114962306a36Sopenharmony_ci int len = *lp; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci if (len < 0) return; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci if (len > 2) { 115462306a36Sopenharmony_ci *bp++ = '\\'; 115562306a36Sopenharmony_ci *bp++ = 'x'; 115662306a36Sopenharmony_ci len -= 2; 115762306a36Sopenharmony_ci while (blen && len >= 2) { 115862306a36Sopenharmony_ci bp = hex_byte_pack(bp, *buf++); 115962306a36Sopenharmony_ci len -= 2; 116062306a36Sopenharmony_ci blen--; 116162306a36Sopenharmony_ci } 116262306a36Sopenharmony_ci } 116362306a36Sopenharmony_ci if (blen || len<1) len = -1; 116462306a36Sopenharmony_ci else { 116562306a36Sopenharmony_ci *bp++ = ' '; 116662306a36Sopenharmony_ci len--; 116762306a36Sopenharmony_ci } 116862306a36Sopenharmony_ci *bpp = bp; 116962306a36Sopenharmony_ci *lp = len; 117062306a36Sopenharmony_ci} 117162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qword_addhex); 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_cistatic void warn_no_listener(struct cache_detail *detail) 117462306a36Sopenharmony_ci{ 117562306a36Sopenharmony_ci if (detail->last_warn != detail->last_close) { 117662306a36Sopenharmony_ci detail->last_warn = detail->last_close; 117762306a36Sopenharmony_ci if (detail->warn_no_listener) 117862306a36Sopenharmony_ci detail->warn_no_listener(detail, detail->last_close != 0); 117962306a36Sopenharmony_ci } 118062306a36Sopenharmony_ci} 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_cistatic bool cache_listeners_exist(struct cache_detail *detail) 118362306a36Sopenharmony_ci{ 118462306a36Sopenharmony_ci if (atomic_read(&detail->writers)) 118562306a36Sopenharmony_ci return true; 118662306a36Sopenharmony_ci if (detail->last_close == 0) 118762306a36Sopenharmony_ci /* This cache was never opened */ 118862306a36Sopenharmony_ci return false; 118962306a36Sopenharmony_ci if (detail->last_close < seconds_since_boot() - 30) 119062306a36Sopenharmony_ci /* 119162306a36Sopenharmony_ci * We allow for the possibility that someone might 119262306a36Sopenharmony_ci * restart a userspace daemon without restarting the 119362306a36Sopenharmony_ci * server; but after 30 seconds, we give up. 119462306a36Sopenharmony_ci */ 119562306a36Sopenharmony_ci return false; 119662306a36Sopenharmony_ci return true; 119762306a36Sopenharmony_ci} 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci/* 120062306a36Sopenharmony_ci * register an upcall request to user-space and queue it up for read() by the 120162306a36Sopenharmony_ci * upcall daemon. 120262306a36Sopenharmony_ci * 120362306a36Sopenharmony_ci * Each request is at most one page long. 120462306a36Sopenharmony_ci */ 120562306a36Sopenharmony_cistatic int cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h) 120662306a36Sopenharmony_ci{ 120762306a36Sopenharmony_ci char *buf; 120862306a36Sopenharmony_ci struct cache_request *crq; 120962306a36Sopenharmony_ci int ret = 0; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci if (test_bit(CACHE_CLEANED, &h->flags)) 121262306a36Sopenharmony_ci /* Too late to make an upcall */ 121362306a36Sopenharmony_ci return -EAGAIN; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci buf = kmalloc(PAGE_SIZE, GFP_KERNEL); 121662306a36Sopenharmony_ci if (!buf) 121762306a36Sopenharmony_ci return -EAGAIN; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci crq = kmalloc(sizeof (*crq), GFP_KERNEL); 122062306a36Sopenharmony_ci if (!crq) { 122162306a36Sopenharmony_ci kfree(buf); 122262306a36Sopenharmony_ci return -EAGAIN; 122362306a36Sopenharmony_ci } 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci crq->q.reader = 0; 122662306a36Sopenharmony_ci crq->buf = buf; 122762306a36Sopenharmony_ci crq->len = 0; 122862306a36Sopenharmony_ci crq->readers = 0; 122962306a36Sopenharmony_ci spin_lock(&queue_lock); 123062306a36Sopenharmony_ci if (test_bit(CACHE_PENDING, &h->flags)) { 123162306a36Sopenharmony_ci crq->item = cache_get(h); 123262306a36Sopenharmony_ci list_add_tail(&crq->q.list, &detail->queue); 123362306a36Sopenharmony_ci trace_cache_entry_upcall(detail, h); 123462306a36Sopenharmony_ci } else 123562306a36Sopenharmony_ci /* Lost a race, no longer PENDING, so don't enqueue */ 123662306a36Sopenharmony_ci ret = -EAGAIN; 123762306a36Sopenharmony_ci spin_unlock(&queue_lock); 123862306a36Sopenharmony_ci wake_up(&queue_wait); 123962306a36Sopenharmony_ci if (ret == -EAGAIN) { 124062306a36Sopenharmony_ci kfree(buf); 124162306a36Sopenharmony_ci kfree(crq); 124262306a36Sopenharmony_ci } 124362306a36Sopenharmony_ci return ret; 124462306a36Sopenharmony_ci} 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ciint sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h) 124762306a36Sopenharmony_ci{ 124862306a36Sopenharmony_ci if (test_and_set_bit(CACHE_PENDING, &h->flags)) 124962306a36Sopenharmony_ci return 0; 125062306a36Sopenharmony_ci return cache_pipe_upcall(detail, h); 125162306a36Sopenharmony_ci} 125262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sunrpc_cache_pipe_upcall); 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ciint sunrpc_cache_pipe_upcall_timeout(struct cache_detail *detail, 125562306a36Sopenharmony_ci struct cache_head *h) 125662306a36Sopenharmony_ci{ 125762306a36Sopenharmony_ci if (!cache_listeners_exist(detail)) { 125862306a36Sopenharmony_ci warn_no_listener(detail); 125962306a36Sopenharmony_ci trace_cache_entry_no_listener(detail, h); 126062306a36Sopenharmony_ci return -EINVAL; 126162306a36Sopenharmony_ci } 126262306a36Sopenharmony_ci return sunrpc_cache_pipe_upcall(detail, h); 126362306a36Sopenharmony_ci} 126462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sunrpc_cache_pipe_upcall_timeout); 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci/* 126762306a36Sopenharmony_ci * parse a message from user-space and pass it 126862306a36Sopenharmony_ci * to an appropriate cache 126962306a36Sopenharmony_ci * Messages are, like requests, separated into fields by 127062306a36Sopenharmony_ci * spaces and dequotes as \xHEXSTRING or embedded \nnn octal 127162306a36Sopenharmony_ci * 127262306a36Sopenharmony_ci * Message is 127362306a36Sopenharmony_ci * reply cachename expiry key ... content.... 127462306a36Sopenharmony_ci * 127562306a36Sopenharmony_ci * key and content are both parsed by cache 127662306a36Sopenharmony_ci */ 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ciint qword_get(char **bpp, char *dest, int bufsize) 127962306a36Sopenharmony_ci{ 128062306a36Sopenharmony_ci /* return bytes copied, or -1 on error */ 128162306a36Sopenharmony_ci char *bp = *bpp; 128262306a36Sopenharmony_ci int len = 0; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci while (*bp == ' ') bp++; 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci if (bp[0] == '\\' && bp[1] == 'x') { 128762306a36Sopenharmony_ci /* HEX STRING */ 128862306a36Sopenharmony_ci bp += 2; 128962306a36Sopenharmony_ci while (len < bufsize - 1) { 129062306a36Sopenharmony_ci int h, l; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci h = hex_to_bin(bp[0]); 129362306a36Sopenharmony_ci if (h < 0) 129462306a36Sopenharmony_ci break; 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci l = hex_to_bin(bp[1]); 129762306a36Sopenharmony_ci if (l < 0) 129862306a36Sopenharmony_ci break; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci *dest++ = (h << 4) | l; 130162306a36Sopenharmony_ci bp += 2; 130262306a36Sopenharmony_ci len++; 130362306a36Sopenharmony_ci } 130462306a36Sopenharmony_ci } else { 130562306a36Sopenharmony_ci /* text with \nnn octal quoting */ 130662306a36Sopenharmony_ci while (*bp != ' ' && *bp != '\n' && *bp && len < bufsize-1) { 130762306a36Sopenharmony_ci if (*bp == '\\' && 130862306a36Sopenharmony_ci isodigit(bp[1]) && (bp[1] <= '3') && 130962306a36Sopenharmony_ci isodigit(bp[2]) && 131062306a36Sopenharmony_ci isodigit(bp[3])) { 131162306a36Sopenharmony_ci int byte = (*++bp -'0'); 131262306a36Sopenharmony_ci bp++; 131362306a36Sopenharmony_ci byte = (byte << 3) | (*bp++ - '0'); 131462306a36Sopenharmony_ci byte = (byte << 3) | (*bp++ - '0'); 131562306a36Sopenharmony_ci *dest++ = byte; 131662306a36Sopenharmony_ci len++; 131762306a36Sopenharmony_ci } else { 131862306a36Sopenharmony_ci *dest++ = *bp++; 131962306a36Sopenharmony_ci len++; 132062306a36Sopenharmony_ci } 132162306a36Sopenharmony_ci } 132262306a36Sopenharmony_ci } 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci if (*bp != ' ' && *bp != '\n' && *bp != '\0') 132562306a36Sopenharmony_ci return -1; 132662306a36Sopenharmony_ci while (*bp == ' ') bp++; 132762306a36Sopenharmony_ci *bpp = bp; 132862306a36Sopenharmony_ci *dest = '\0'; 132962306a36Sopenharmony_ci return len; 133062306a36Sopenharmony_ci} 133162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qword_get); 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci/* 133562306a36Sopenharmony_ci * support /proc/net/rpc/$CACHENAME/content 133662306a36Sopenharmony_ci * as a seqfile. 133762306a36Sopenharmony_ci * We call ->cache_show passing NULL for the item to 133862306a36Sopenharmony_ci * get a header, then pass each real item in the cache 133962306a36Sopenharmony_ci */ 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_cistatic void *__cache_seq_start(struct seq_file *m, loff_t *pos) 134262306a36Sopenharmony_ci{ 134362306a36Sopenharmony_ci loff_t n = *pos; 134462306a36Sopenharmony_ci unsigned int hash, entry; 134562306a36Sopenharmony_ci struct cache_head *ch; 134662306a36Sopenharmony_ci struct cache_detail *cd = m->private; 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci if (!n--) 134962306a36Sopenharmony_ci return SEQ_START_TOKEN; 135062306a36Sopenharmony_ci hash = n >> 32; 135162306a36Sopenharmony_ci entry = n & ((1LL<<32) - 1); 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci hlist_for_each_entry_rcu(ch, &cd->hash_table[hash], cache_list) 135462306a36Sopenharmony_ci if (!entry--) 135562306a36Sopenharmony_ci return ch; 135662306a36Sopenharmony_ci n &= ~((1LL<<32) - 1); 135762306a36Sopenharmony_ci do { 135862306a36Sopenharmony_ci hash++; 135962306a36Sopenharmony_ci n += 1LL<<32; 136062306a36Sopenharmony_ci } while(hash < cd->hash_size && 136162306a36Sopenharmony_ci hlist_empty(&cd->hash_table[hash])); 136262306a36Sopenharmony_ci if (hash >= cd->hash_size) 136362306a36Sopenharmony_ci return NULL; 136462306a36Sopenharmony_ci *pos = n+1; 136562306a36Sopenharmony_ci return hlist_entry_safe(rcu_dereference_raw( 136662306a36Sopenharmony_ci hlist_first_rcu(&cd->hash_table[hash])), 136762306a36Sopenharmony_ci struct cache_head, cache_list); 136862306a36Sopenharmony_ci} 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_cistatic void *cache_seq_next(struct seq_file *m, void *p, loff_t *pos) 137162306a36Sopenharmony_ci{ 137262306a36Sopenharmony_ci struct cache_head *ch = p; 137362306a36Sopenharmony_ci int hash = (*pos >> 32); 137462306a36Sopenharmony_ci struct cache_detail *cd = m->private; 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci if (p == SEQ_START_TOKEN) 137762306a36Sopenharmony_ci hash = 0; 137862306a36Sopenharmony_ci else if (ch->cache_list.next == NULL) { 137962306a36Sopenharmony_ci hash++; 138062306a36Sopenharmony_ci *pos += 1LL<<32; 138162306a36Sopenharmony_ci } else { 138262306a36Sopenharmony_ci ++*pos; 138362306a36Sopenharmony_ci return hlist_entry_safe(rcu_dereference_raw( 138462306a36Sopenharmony_ci hlist_next_rcu(&ch->cache_list)), 138562306a36Sopenharmony_ci struct cache_head, cache_list); 138662306a36Sopenharmony_ci } 138762306a36Sopenharmony_ci *pos &= ~((1LL<<32) - 1); 138862306a36Sopenharmony_ci while (hash < cd->hash_size && 138962306a36Sopenharmony_ci hlist_empty(&cd->hash_table[hash])) { 139062306a36Sopenharmony_ci hash++; 139162306a36Sopenharmony_ci *pos += 1LL<<32; 139262306a36Sopenharmony_ci } 139362306a36Sopenharmony_ci if (hash >= cd->hash_size) 139462306a36Sopenharmony_ci return NULL; 139562306a36Sopenharmony_ci ++*pos; 139662306a36Sopenharmony_ci return hlist_entry_safe(rcu_dereference_raw( 139762306a36Sopenharmony_ci hlist_first_rcu(&cd->hash_table[hash])), 139862306a36Sopenharmony_ci struct cache_head, cache_list); 139962306a36Sopenharmony_ci} 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_civoid *cache_seq_start_rcu(struct seq_file *m, loff_t *pos) 140262306a36Sopenharmony_ci __acquires(RCU) 140362306a36Sopenharmony_ci{ 140462306a36Sopenharmony_ci rcu_read_lock(); 140562306a36Sopenharmony_ci return __cache_seq_start(m, pos); 140662306a36Sopenharmony_ci} 140762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cache_seq_start_rcu); 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_civoid *cache_seq_next_rcu(struct seq_file *file, void *p, loff_t *pos) 141062306a36Sopenharmony_ci{ 141162306a36Sopenharmony_ci return cache_seq_next(file, p, pos); 141262306a36Sopenharmony_ci} 141362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cache_seq_next_rcu); 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_civoid cache_seq_stop_rcu(struct seq_file *m, void *p) 141662306a36Sopenharmony_ci __releases(RCU) 141762306a36Sopenharmony_ci{ 141862306a36Sopenharmony_ci rcu_read_unlock(); 141962306a36Sopenharmony_ci} 142062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cache_seq_stop_rcu); 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_cistatic int c_show(struct seq_file *m, void *p) 142362306a36Sopenharmony_ci{ 142462306a36Sopenharmony_ci struct cache_head *cp = p; 142562306a36Sopenharmony_ci struct cache_detail *cd = m->private; 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci if (p == SEQ_START_TOKEN) 142862306a36Sopenharmony_ci return cd->cache_show(m, cd, NULL); 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci ifdebug(CACHE) 143162306a36Sopenharmony_ci seq_printf(m, "# expiry=%lld refcnt=%d flags=%lx\n", 143262306a36Sopenharmony_ci convert_to_wallclock(cp->expiry_time), 143362306a36Sopenharmony_ci kref_read(&cp->ref), cp->flags); 143462306a36Sopenharmony_ci cache_get(cp); 143562306a36Sopenharmony_ci if (cache_check(cd, cp, NULL)) 143662306a36Sopenharmony_ci /* cache_check does a cache_put on failure */ 143762306a36Sopenharmony_ci seq_puts(m, "# "); 143862306a36Sopenharmony_ci else { 143962306a36Sopenharmony_ci if (cache_is_expired(cd, cp)) 144062306a36Sopenharmony_ci seq_puts(m, "# "); 144162306a36Sopenharmony_ci cache_put(cp, cd); 144262306a36Sopenharmony_ci } 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci return cd->cache_show(m, cd, cp); 144562306a36Sopenharmony_ci} 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_cistatic const struct seq_operations cache_content_op = { 144862306a36Sopenharmony_ci .start = cache_seq_start_rcu, 144962306a36Sopenharmony_ci .next = cache_seq_next_rcu, 145062306a36Sopenharmony_ci .stop = cache_seq_stop_rcu, 145162306a36Sopenharmony_ci .show = c_show, 145262306a36Sopenharmony_ci}; 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_cistatic int content_open(struct inode *inode, struct file *file, 145562306a36Sopenharmony_ci struct cache_detail *cd) 145662306a36Sopenharmony_ci{ 145762306a36Sopenharmony_ci struct seq_file *seq; 145862306a36Sopenharmony_ci int err; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci if (!cd || !try_module_get(cd->owner)) 146162306a36Sopenharmony_ci return -EACCES; 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci err = seq_open(file, &cache_content_op); 146462306a36Sopenharmony_ci if (err) { 146562306a36Sopenharmony_ci module_put(cd->owner); 146662306a36Sopenharmony_ci return err; 146762306a36Sopenharmony_ci } 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci seq = file->private_data; 147062306a36Sopenharmony_ci seq->private = cd; 147162306a36Sopenharmony_ci return 0; 147262306a36Sopenharmony_ci} 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_cistatic int content_release(struct inode *inode, struct file *file, 147562306a36Sopenharmony_ci struct cache_detail *cd) 147662306a36Sopenharmony_ci{ 147762306a36Sopenharmony_ci int ret = seq_release(inode, file); 147862306a36Sopenharmony_ci module_put(cd->owner); 147962306a36Sopenharmony_ci return ret; 148062306a36Sopenharmony_ci} 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_cistatic int open_flush(struct inode *inode, struct file *file, 148362306a36Sopenharmony_ci struct cache_detail *cd) 148462306a36Sopenharmony_ci{ 148562306a36Sopenharmony_ci if (!cd || !try_module_get(cd->owner)) 148662306a36Sopenharmony_ci return -EACCES; 148762306a36Sopenharmony_ci return nonseekable_open(inode, file); 148862306a36Sopenharmony_ci} 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_cistatic int release_flush(struct inode *inode, struct file *file, 149162306a36Sopenharmony_ci struct cache_detail *cd) 149262306a36Sopenharmony_ci{ 149362306a36Sopenharmony_ci module_put(cd->owner); 149462306a36Sopenharmony_ci return 0; 149562306a36Sopenharmony_ci} 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_cistatic ssize_t read_flush(struct file *file, char __user *buf, 149862306a36Sopenharmony_ci size_t count, loff_t *ppos, 149962306a36Sopenharmony_ci struct cache_detail *cd) 150062306a36Sopenharmony_ci{ 150162306a36Sopenharmony_ci char tbuf[22]; 150262306a36Sopenharmony_ci size_t len; 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci len = snprintf(tbuf, sizeof(tbuf), "%llu\n", 150562306a36Sopenharmony_ci convert_to_wallclock(cd->flush_time)); 150662306a36Sopenharmony_ci return simple_read_from_buffer(buf, count, ppos, tbuf, len); 150762306a36Sopenharmony_ci} 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_cistatic ssize_t write_flush(struct file *file, const char __user *buf, 151062306a36Sopenharmony_ci size_t count, loff_t *ppos, 151162306a36Sopenharmony_ci struct cache_detail *cd) 151262306a36Sopenharmony_ci{ 151362306a36Sopenharmony_ci char tbuf[20]; 151462306a36Sopenharmony_ci char *ep; 151562306a36Sopenharmony_ci time64_t now; 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci if (*ppos || count > sizeof(tbuf)-1) 151862306a36Sopenharmony_ci return -EINVAL; 151962306a36Sopenharmony_ci if (copy_from_user(tbuf, buf, count)) 152062306a36Sopenharmony_ci return -EFAULT; 152162306a36Sopenharmony_ci tbuf[count] = 0; 152262306a36Sopenharmony_ci simple_strtoul(tbuf, &ep, 0); 152362306a36Sopenharmony_ci if (*ep && *ep != '\n') 152462306a36Sopenharmony_ci return -EINVAL; 152562306a36Sopenharmony_ci /* Note that while we check that 'buf' holds a valid number, 152662306a36Sopenharmony_ci * we always ignore the value and just flush everything. 152762306a36Sopenharmony_ci * Making use of the number leads to races. 152862306a36Sopenharmony_ci */ 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci now = seconds_since_boot(); 153162306a36Sopenharmony_ci /* Always flush everything, so behave like cache_purge() 153262306a36Sopenharmony_ci * Do this by advancing flush_time to the current time, 153362306a36Sopenharmony_ci * or by one second if it has already reached the current time. 153462306a36Sopenharmony_ci * Newly added cache entries will always have ->last_refresh greater 153562306a36Sopenharmony_ci * that ->flush_time, so they don't get flushed prematurely. 153662306a36Sopenharmony_ci */ 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci if (cd->flush_time >= now) 153962306a36Sopenharmony_ci now = cd->flush_time + 1; 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci cd->flush_time = now; 154262306a36Sopenharmony_ci cd->nextcheck = now; 154362306a36Sopenharmony_ci cache_flush(); 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci if (cd->flush) 154662306a36Sopenharmony_ci cd->flush(); 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci *ppos += count; 154962306a36Sopenharmony_ci return count; 155062306a36Sopenharmony_ci} 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_cistatic ssize_t cache_read_procfs(struct file *filp, char __user *buf, 155362306a36Sopenharmony_ci size_t count, loff_t *ppos) 155462306a36Sopenharmony_ci{ 155562306a36Sopenharmony_ci struct cache_detail *cd = pde_data(file_inode(filp)); 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci return cache_read(filp, buf, count, ppos, cd); 155862306a36Sopenharmony_ci} 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_cistatic ssize_t cache_write_procfs(struct file *filp, const char __user *buf, 156162306a36Sopenharmony_ci size_t count, loff_t *ppos) 156262306a36Sopenharmony_ci{ 156362306a36Sopenharmony_ci struct cache_detail *cd = pde_data(file_inode(filp)); 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci return cache_write(filp, buf, count, ppos, cd); 156662306a36Sopenharmony_ci} 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_cistatic __poll_t cache_poll_procfs(struct file *filp, poll_table *wait) 156962306a36Sopenharmony_ci{ 157062306a36Sopenharmony_ci struct cache_detail *cd = pde_data(file_inode(filp)); 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci return cache_poll(filp, wait, cd); 157362306a36Sopenharmony_ci} 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_cistatic long cache_ioctl_procfs(struct file *filp, 157662306a36Sopenharmony_ci unsigned int cmd, unsigned long arg) 157762306a36Sopenharmony_ci{ 157862306a36Sopenharmony_ci struct inode *inode = file_inode(filp); 157962306a36Sopenharmony_ci struct cache_detail *cd = pde_data(inode); 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci return cache_ioctl(inode, filp, cmd, arg, cd); 158262306a36Sopenharmony_ci} 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_cistatic int cache_open_procfs(struct inode *inode, struct file *filp) 158562306a36Sopenharmony_ci{ 158662306a36Sopenharmony_ci struct cache_detail *cd = pde_data(inode); 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci return cache_open(inode, filp, cd); 158962306a36Sopenharmony_ci} 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_cistatic int cache_release_procfs(struct inode *inode, struct file *filp) 159262306a36Sopenharmony_ci{ 159362306a36Sopenharmony_ci struct cache_detail *cd = pde_data(inode); 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci return cache_release(inode, filp, cd); 159662306a36Sopenharmony_ci} 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_cistatic const struct proc_ops cache_channel_proc_ops = { 159962306a36Sopenharmony_ci .proc_lseek = no_llseek, 160062306a36Sopenharmony_ci .proc_read = cache_read_procfs, 160162306a36Sopenharmony_ci .proc_write = cache_write_procfs, 160262306a36Sopenharmony_ci .proc_poll = cache_poll_procfs, 160362306a36Sopenharmony_ci .proc_ioctl = cache_ioctl_procfs, /* for FIONREAD */ 160462306a36Sopenharmony_ci .proc_open = cache_open_procfs, 160562306a36Sopenharmony_ci .proc_release = cache_release_procfs, 160662306a36Sopenharmony_ci}; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_cistatic int content_open_procfs(struct inode *inode, struct file *filp) 160962306a36Sopenharmony_ci{ 161062306a36Sopenharmony_ci struct cache_detail *cd = pde_data(inode); 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci return content_open(inode, filp, cd); 161362306a36Sopenharmony_ci} 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_cistatic int content_release_procfs(struct inode *inode, struct file *filp) 161662306a36Sopenharmony_ci{ 161762306a36Sopenharmony_ci struct cache_detail *cd = pde_data(inode); 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci return content_release(inode, filp, cd); 162062306a36Sopenharmony_ci} 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_cistatic const struct proc_ops content_proc_ops = { 162362306a36Sopenharmony_ci .proc_open = content_open_procfs, 162462306a36Sopenharmony_ci .proc_read = seq_read, 162562306a36Sopenharmony_ci .proc_lseek = seq_lseek, 162662306a36Sopenharmony_ci .proc_release = content_release_procfs, 162762306a36Sopenharmony_ci}; 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_cistatic int open_flush_procfs(struct inode *inode, struct file *filp) 163062306a36Sopenharmony_ci{ 163162306a36Sopenharmony_ci struct cache_detail *cd = pde_data(inode); 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci return open_flush(inode, filp, cd); 163462306a36Sopenharmony_ci} 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_cistatic int release_flush_procfs(struct inode *inode, struct file *filp) 163762306a36Sopenharmony_ci{ 163862306a36Sopenharmony_ci struct cache_detail *cd = pde_data(inode); 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci return release_flush(inode, filp, cd); 164162306a36Sopenharmony_ci} 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_cistatic ssize_t read_flush_procfs(struct file *filp, char __user *buf, 164462306a36Sopenharmony_ci size_t count, loff_t *ppos) 164562306a36Sopenharmony_ci{ 164662306a36Sopenharmony_ci struct cache_detail *cd = pde_data(file_inode(filp)); 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci return read_flush(filp, buf, count, ppos, cd); 164962306a36Sopenharmony_ci} 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_cistatic ssize_t write_flush_procfs(struct file *filp, 165262306a36Sopenharmony_ci const char __user *buf, 165362306a36Sopenharmony_ci size_t count, loff_t *ppos) 165462306a36Sopenharmony_ci{ 165562306a36Sopenharmony_ci struct cache_detail *cd = pde_data(file_inode(filp)); 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci return write_flush(filp, buf, count, ppos, cd); 165862306a36Sopenharmony_ci} 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_cistatic const struct proc_ops cache_flush_proc_ops = { 166162306a36Sopenharmony_ci .proc_open = open_flush_procfs, 166262306a36Sopenharmony_ci .proc_read = read_flush_procfs, 166362306a36Sopenharmony_ci .proc_write = write_flush_procfs, 166462306a36Sopenharmony_ci .proc_release = release_flush_procfs, 166562306a36Sopenharmony_ci .proc_lseek = no_llseek, 166662306a36Sopenharmony_ci}; 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_cistatic void remove_cache_proc_entries(struct cache_detail *cd) 166962306a36Sopenharmony_ci{ 167062306a36Sopenharmony_ci if (cd->procfs) { 167162306a36Sopenharmony_ci proc_remove(cd->procfs); 167262306a36Sopenharmony_ci cd->procfs = NULL; 167362306a36Sopenharmony_ci } 167462306a36Sopenharmony_ci} 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 167762306a36Sopenharmony_cistatic int create_cache_proc_entries(struct cache_detail *cd, struct net *net) 167862306a36Sopenharmony_ci{ 167962306a36Sopenharmony_ci struct proc_dir_entry *p; 168062306a36Sopenharmony_ci struct sunrpc_net *sn; 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci sn = net_generic(net, sunrpc_net_id); 168362306a36Sopenharmony_ci cd->procfs = proc_mkdir(cd->name, sn->proc_net_rpc); 168462306a36Sopenharmony_ci if (cd->procfs == NULL) 168562306a36Sopenharmony_ci goto out_nomem; 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci p = proc_create_data("flush", S_IFREG | 0600, 168862306a36Sopenharmony_ci cd->procfs, &cache_flush_proc_ops, cd); 168962306a36Sopenharmony_ci if (p == NULL) 169062306a36Sopenharmony_ci goto out_nomem; 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci if (cd->cache_request || cd->cache_parse) { 169362306a36Sopenharmony_ci p = proc_create_data("channel", S_IFREG | 0600, cd->procfs, 169462306a36Sopenharmony_ci &cache_channel_proc_ops, cd); 169562306a36Sopenharmony_ci if (p == NULL) 169662306a36Sopenharmony_ci goto out_nomem; 169762306a36Sopenharmony_ci } 169862306a36Sopenharmony_ci if (cd->cache_show) { 169962306a36Sopenharmony_ci p = proc_create_data("content", S_IFREG | 0400, cd->procfs, 170062306a36Sopenharmony_ci &content_proc_ops, cd); 170162306a36Sopenharmony_ci if (p == NULL) 170262306a36Sopenharmony_ci goto out_nomem; 170362306a36Sopenharmony_ci } 170462306a36Sopenharmony_ci return 0; 170562306a36Sopenharmony_ciout_nomem: 170662306a36Sopenharmony_ci remove_cache_proc_entries(cd); 170762306a36Sopenharmony_ci return -ENOMEM; 170862306a36Sopenharmony_ci} 170962306a36Sopenharmony_ci#else /* CONFIG_PROC_FS */ 171062306a36Sopenharmony_cistatic int create_cache_proc_entries(struct cache_detail *cd, struct net *net) 171162306a36Sopenharmony_ci{ 171262306a36Sopenharmony_ci return 0; 171362306a36Sopenharmony_ci} 171462306a36Sopenharmony_ci#endif 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_civoid __init cache_initialize(void) 171762306a36Sopenharmony_ci{ 171862306a36Sopenharmony_ci INIT_DEFERRABLE_WORK(&cache_cleaner, do_cache_clean); 171962306a36Sopenharmony_ci} 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ciint cache_register_net(struct cache_detail *cd, struct net *net) 172262306a36Sopenharmony_ci{ 172362306a36Sopenharmony_ci int ret; 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci sunrpc_init_cache_detail(cd); 172662306a36Sopenharmony_ci ret = create_cache_proc_entries(cd, net); 172762306a36Sopenharmony_ci if (ret) 172862306a36Sopenharmony_ci sunrpc_destroy_cache_detail(cd); 172962306a36Sopenharmony_ci return ret; 173062306a36Sopenharmony_ci} 173162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cache_register_net); 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_civoid cache_unregister_net(struct cache_detail *cd, struct net *net) 173462306a36Sopenharmony_ci{ 173562306a36Sopenharmony_ci remove_cache_proc_entries(cd); 173662306a36Sopenharmony_ci sunrpc_destroy_cache_detail(cd); 173762306a36Sopenharmony_ci} 173862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cache_unregister_net); 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_cistruct cache_detail *cache_create_net(const struct cache_detail *tmpl, struct net *net) 174162306a36Sopenharmony_ci{ 174262306a36Sopenharmony_ci struct cache_detail *cd; 174362306a36Sopenharmony_ci int i; 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ci cd = kmemdup(tmpl, sizeof(struct cache_detail), GFP_KERNEL); 174662306a36Sopenharmony_ci if (cd == NULL) 174762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci cd->hash_table = kcalloc(cd->hash_size, sizeof(struct hlist_head), 175062306a36Sopenharmony_ci GFP_KERNEL); 175162306a36Sopenharmony_ci if (cd->hash_table == NULL) { 175262306a36Sopenharmony_ci kfree(cd); 175362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 175462306a36Sopenharmony_ci } 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci for (i = 0; i < cd->hash_size; i++) 175762306a36Sopenharmony_ci INIT_HLIST_HEAD(&cd->hash_table[i]); 175862306a36Sopenharmony_ci cd->net = net; 175962306a36Sopenharmony_ci return cd; 176062306a36Sopenharmony_ci} 176162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cache_create_net); 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_civoid cache_destroy_net(struct cache_detail *cd, struct net *net) 176462306a36Sopenharmony_ci{ 176562306a36Sopenharmony_ci kfree(cd->hash_table); 176662306a36Sopenharmony_ci kfree(cd); 176762306a36Sopenharmony_ci} 176862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cache_destroy_net); 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_cistatic ssize_t cache_read_pipefs(struct file *filp, char __user *buf, 177162306a36Sopenharmony_ci size_t count, loff_t *ppos) 177262306a36Sopenharmony_ci{ 177362306a36Sopenharmony_ci struct cache_detail *cd = RPC_I(file_inode(filp))->private; 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci return cache_read(filp, buf, count, ppos, cd); 177662306a36Sopenharmony_ci} 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_cistatic ssize_t cache_write_pipefs(struct file *filp, const char __user *buf, 177962306a36Sopenharmony_ci size_t count, loff_t *ppos) 178062306a36Sopenharmony_ci{ 178162306a36Sopenharmony_ci struct cache_detail *cd = RPC_I(file_inode(filp))->private; 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci return cache_write(filp, buf, count, ppos, cd); 178462306a36Sopenharmony_ci} 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_cistatic __poll_t cache_poll_pipefs(struct file *filp, poll_table *wait) 178762306a36Sopenharmony_ci{ 178862306a36Sopenharmony_ci struct cache_detail *cd = RPC_I(file_inode(filp))->private; 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci return cache_poll(filp, wait, cd); 179162306a36Sopenharmony_ci} 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_cistatic long cache_ioctl_pipefs(struct file *filp, 179462306a36Sopenharmony_ci unsigned int cmd, unsigned long arg) 179562306a36Sopenharmony_ci{ 179662306a36Sopenharmony_ci struct inode *inode = file_inode(filp); 179762306a36Sopenharmony_ci struct cache_detail *cd = RPC_I(inode)->private; 179862306a36Sopenharmony_ci 179962306a36Sopenharmony_ci return cache_ioctl(inode, filp, cmd, arg, cd); 180062306a36Sopenharmony_ci} 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_cistatic int cache_open_pipefs(struct inode *inode, struct file *filp) 180362306a36Sopenharmony_ci{ 180462306a36Sopenharmony_ci struct cache_detail *cd = RPC_I(inode)->private; 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci return cache_open(inode, filp, cd); 180762306a36Sopenharmony_ci} 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_cistatic int cache_release_pipefs(struct inode *inode, struct file *filp) 181062306a36Sopenharmony_ci{ 181162306a36Sopenharmony_ci struct cache_detail *cd = RPC_I(inode)->private; 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci return cache_release(inode, filp, cd); 181462306a36Sopenharmony_ci} 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ciconst struct file_operations cache_file_operations_pipefs = { 181762306a36Sopenharmony_ci .owner = THIS_MODULE, 181862306a36Sopenharmony_ci .llseek = no_llseek, 181962306a36Sopenharmony_ci .read = cache_read_pipefs, 182062306a36Sopenharmony_ci .write = cache_write_pipefs, 182162306a36Sopenharmony_ci .poll = cache_poll_pipefs, 182262306a36Sopenharmony_ci .unlocked_ioctl = cache_ioctl_pipefs, /* for FIONREAD */ 182362306a36Sopenharmony_ci .open = cache_open_pipefs, 182462306a36Sopenharmony_ci .release = cache_release_pipefs, 182562306a36Sopenharmony_ci}; 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_cistatic int content_open_pipefs(struct inode *inode, struct file *filp) 182862306a36Sopenharmony_ci{ 182962306a36Sopenharmony_ci struct cache_detail *cd = RPC_I(inode)->private; 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci return content_open(inode, filp, cd); 183262306a36Sopenharmony_ci} 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_cistatic int content_release_pipefs(struct inode *inode, struct file *filp) 183562306a36Sopenharmony_ci{ 183662306a36Sopenharmony_ci struct cache_detail *cd = RPC_I(inode)->private; 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci return content_release(inode, filp, cd); 183962306a36Sopenharmony_ci} 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ciconst struct file_operations content_file_operations_pipefs = { 184262306a36Sopenharmony_ci .open = content_open_pipefs, 184362306a36Sopenharmony_ci .read = seq_read, 184462306a36Sopenharmony_ci .llseek = seq_lseek, 184562306a36Sopenharmony_ci .release = content_release_pipefs, 184662306a36Sopenharmony_ci}; 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_cistatic int open_flush_pipefs(struct inode *inode, struct file *filp) 184962306a36Sopenharmony_ci{ 185062306a36Sopenharmony_ci struct cache_detail *cd = RPC_I(inode)->private; 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci return open_flush(inode, filp, cd); 185362306a36Sopenharmony_ci} 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_cistatic int release_flush_pipefs(struct inode *inode, struct file *filp) 185662306a36Sopenharmony_ci{ 185762306a36Sopenharmony_ci struct cache_detail *cd = RPC_I(inode)->private; 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci return release_flush(inode, filp, cd); 186062306a36Sopenharmony_ci} 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_cistatic ssize_t read_flush_pipefs(struct file *filp, char __user *buf, 186362306a36Sopenharmony_ci size_t count, loff_t *ppos) 186462306a36Sopenharmony_ci{ 186562306a36Sopenharmony_ci struct cache_detail *cd = RPC_I(file_inode(filp))->private; 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci return read_flush(filp, buf, count, ppos, cd); 186862306a36Sopenharmony_ci} 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_cistatic ssize_t write_flush_pipefs(struct file *filp, 187162306a36Sopenharmony_ci const char __user *buf, 187262306a36Sopenharmony_ci size_t count, loff_t *ppos) 187362306a36Sopenharmony_ci{ 187462306a36Sopenharmony_ci struct cache_detail *cd = RPC_I(file_inode(filp))->private; 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci return write_flush(filp, buf, count, ppos, cd); 187762306a36Sopenharmony_ci} 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_ciconst struct file_operations cache_flush_operations_pipefs = { 188062306a36Sopenharmony_ci .open = open_flush_pipefs, 188162306a36Sopenharmony_ci .read = read_flush_pipefs, 188262306a36Sopenharmony_ci .write = write_flush_pipefs, 188362306a36Sopenharmony_ci .release = release_flush_pipefs, 188462306a36Sopenharmony_ci .llseek = no_llseek, 188562306a36Sopenharmony_ci}; 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_ciint sunrpc_cache_register_pipefs(struct dentry *parent, 188862306a36Sopenharmony_ci const char *name, umode_t umode, 188962306a36Sopenharmony_ci struct cache_detail *cd) 189062306a36Sopenharmony_ci{ 189162306a36Sopenharmony_ci struct dentry *dir = rpc_create_cache_dir(parent, name, umode, cd); 189262306a36Sopenharmony_ci if (IS_ERR(dir)) 189362306a36Sopenharmony_ci return PTR_ERR(dir); 189462306a36Sopenharmony_ci cd->pipefs = dir; 189562306a36Sopenharmony_ci return 0; 189662306a36Sopenharmony_ci} 189762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sunrpc_cache_register_pipefs); 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_civoid sunrpc_cache_unregister_pipefs(struct cache_detail *cd) 190062306a36Sopenharmony_ci{ 190162306a36Sopenharmony_ci if (cd->pipefs) { 190262306a36Sopenharmony_ci rpc_remove_cache_dir(cd->pipefs); 190362306a36Sopenharmony_ci cd->pipefs = NULL; 190462306a36Sopenharmony_ci } 190562306a36Sopenharmony_ci} 190662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sunrpc_cache_unregister_pipefs); 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_civoid sunrpc_cache_unhash(struct cache_detail *cd, struct cache_head *h) 190962306a36Sopenharmony_ci{ 191062306a36Sopenharmony_ci spin_lock(&cd->hash_lock); 191162306a36Sopenharmony_ci if (!hlist_unhashed(&h->cache_list)){ 191262306a36Sopenharmony_ci sunrpc_begin_cache_remove_entry(h, cd); 191362306a36Sopenharmony_ci spin_unlock(&cd->hash_lock); 191462306a36Sopenharmony_ci sunrpc_end_cache_remove_entry(h, cd); 191562306a36Sopenharmony_ci } else 191662306a36Sopenharmony_ci spin_unlock(&cd->hash_lock); 191762306a36Sopenharmony_ci} 191862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sunrpc_cache_unhash); 1919