162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* FS-Cache cache handling 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. 562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define FSCACHE_DEBUG_LEVEL CACHE 962306a36Sopenharmony_ci#include <linux/export.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include "internal.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_cistatic LIST_HEAD(fscache_caches); 1462306a36Sopenharmony_ciDECLARE_RWSEM(fscache_addremove_sem); 1562306a36Sopenharmony_ciEXPORT_SYMBOL(fscache_addremove_sem); 1662306a36Sopenharmony_ciDECLARE_WAIT_QUEUE_HEAD(fscache_clearance_waiters); 1762306a36Sopenharmony_ciEXPORT_SYMBOL(fscache_clearance_waiters); 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic atomic_t fscache_cache_debug_id; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* 2262306a36Sopenharmony_ci * Allocate a cache cookie. 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_cistatic struct fscache_cache *fscache_alloc_cache(const char *name) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci struct fscache_cache *cache; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci cache = kzalloc(sizeof(*cache), GFP_KERNEL); 2962306a36Sopenharmony_ci if (cache) { 3062306a36Sopenharmony_ci if (name) { 3162306a36Sopenharmony_ci cache->name = kstrdup(name, GFP_KERNEL); 3262306a36Sopenharmony_ci if (!cache->name) { 3362306a36Sopenharmony_ci kfree(cache); 3462306a36Sopenharmony_ci return NULL; 3562306a36Sopenharmony_ci } 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci refcount_set(&cache->ref, 1); 3862306a36Sopenharmony_ci INIT_LIST_HEAD(&cache->cache_link); 3962306a36Sopenharmony_ci cache->debug_id = atomic_inc_return(&fscache_cache_debug_id); 4062306a36Sopenharmony_ci } 4162306a36Sopenharmony_ci return cache; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic bool fscache_get_cache_maybe(struct fscache_cache *cache, 4562306a36Sopenharmony_ci enum fscache_cache_trace where) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci bool success; 4862306a36Sopenharmony_ci int ref; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci success = __refcount_inc_not_zero(&cache->ref, &ref); 5162306a36Sopenharmony_ci if (success) 5262306a36Sopenharmony_ci trace_fscache_cache(cache->debug_id, ref + 1, where); 5362306a36Sopenharmony_ci return success; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* 5762306a36Sopenharmony_ci * Look up a cache cookie. 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_cistruct fscache_cache *fscache_lookup_cache(const char *name, bool is_cache) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci struct fscache_cache *candidate, *cache, *unnamed = NULL; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci /* firstly check for the existence of the cache under read lock */ 6462306a36Sopenharmony_ci down_read(&fscache_addremove_sem); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci list_for_each_entry(cache, &fscache_caches, cache_link) { 6762306a36Sopenharmony_ci if (cache->name && name && strcmp(cache->name, name) == 0 && 6862306a36Sopenharmony_ci fscache_get_cache_maybe(cache, fscache_cache_get_acquire)) 6962306a36Sopenharmony_ci goto got_cache_r; 7062306a36Sopenharmony_ci if (!cache->name && !name && 7162306a36Sopenharmony_ci fscache_get_cache_maybe(cache, fscache_cache_get_acquire)) 7262306a36Sopenharmony_ci goto got_cache_r; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (!name) { 7662306a36Sopenharmony_ci list_for_each_entry(cache, &fscache_caches, cache_link) { 7762306a36Sopenharmony_ci if (cache->name && 7862306a36Sopenharmony_ci fscache_get_cache_maybe(cache, fscache_cache_get_acquire)) 7962306a36Sopenharmony_ci goto got_cache_r; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci up_read(&fscache_addremove_sem); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* the cache does not exist - create a candidate */ 8662306a36Sopenharmony_ci candidate = fscache_alloc_cache(name); 8762306a36Sopenharmony_ci if (!candidate) 8862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* write lock, search again and add if still not present */ 9162306a36Sopenharmony_ci down_write(&fscache_addremove_sem); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci list_for_each_entry(cache, &fscache_caches, cache_link) { 9462306a36Sopenharmony_ci if (cache->name && name && strcmp(cache->name, name) == 0 && 9562306a36Sopenharmony_ci fscache_get_cache_maybe(cache, fscache_cache_get_acquire)) 9662306a36Sopenharmony_ci goto got_cache_w; 9762306a36Sopenharmony_ci if (!cache->name) { 9862306a36Sopenharmony_ci unnamed = cache; 9962306a36Sopenharmony_ci if (!name && 10062306a36Sopenharmony_ci fscache_get_cache_maybe(cache, fscache_cache_get_acquire)) 10162306a36Sopenharmony_ci goto got_cache_w; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (unnamed && is_cache && 10662306a36Sopenharmony_ci fscache_get_cache_maybe(unnamed, fscache_cache_get_acquire)) 10762306a36Sopenharmony_ci goto use_unnamed_cache; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (!name) { 11062306a36Sopenharmony_ci list_for_each_entry(cache, &fscache_caches, cache_link) { 11162306a36Sopenharmony_ci if (cache->name && 11262306a36Sopenharmony_ci fscache_get_cache_maybe(cache, fscache_cache_get_acquire)) 11362306a36Sopenharmony_ci goto got_cache_w; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci list_add_tail(&candidate->cache_link, &fscache_caches); 11862306a36Sopenharmony_ci trace_fscache_cache(candidate->debug_id, 11962306a36Sopenharmony_ci refcount_read(&candidate->ref), 12062306a36Sopenharmony_ci fscache_cache_new_acquire); 12162306a36Sopenharmony_ci up_write(&fscache_addremove_sem); 12262306a36Sopenharmony_ci return candidate; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cigot_cache_r: 12562306a36Sopenharmony_ci up_read(&fscache_addremove_sem); 12662306a36Sopenharmony_ci return cache; 12762306a36Sopenharmony_ciuse_unnamed_cache: 12862306a36Sopenharmony_ci cache = unnamed; 12962306a36Sopenharmony_ci cache->name = candidate->name; 13062306a36Sopenharmony_ci candidate->name = NULL; 13162306a36Sopenharmony_cigot_cache_w: 13262306a36Sopenharmony_ci up_write(&fscache_addremove_sem); 13362306a36Sopenharmony_ci kfree(candidate->name); 13462306a36Sopenharmony_ci kfree(candidate); 13562306a36Sopenharmony_ci return cache; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci/** 13962306a36Sopenharmony_ci * fscache_acquire_cache - Acquire a cache-level cookie. 14062306a36Sopenharmony_ci * @name: The name of the cache. 14162306a36Sopenharmony_ci * 14262306a36Sopenharmony_ci * Get a cookie to represent an actual cache. If a name is given and there is 14362306a36Sopenharmony_ci * a nameless cache record available, this will acquire that and set its name, 14462306a36Sopenharmony_ci * directing all the volumes using it to this cache. 14562306a36Sopenharmony_ci * 14662306a36Sopenharmony_ci * The cache will be switched over to the preparing state if not currently in 14762306a36Sopenharmony_ci * use, otherwise -EBUSY will be returned. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_cistruct fscache_cache *fscache_acquire_cache(const char *name) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci struct fscache_cache *cache; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci ASSERT(name); 15462306a36Sopenharmony_ci cache = fscache_lookup_cache(name, true); 15562306a36Sopenharmony_ci if (IS_ERR(cache)) 15662306a36Sopenharmony_ci return cache; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (!fscache_set_cache_state_maybe(cache, 15962306a36Sopenharmony_ci FSCACHE_CACHE_IS_NOT_PRESENT, 16062306a36Sopenharmony_ci FSCACHE_CACHE_IS_PREPARING)) { 16162306a36Sopenharmony_ci pr_warn("Cache tag %s in use\n", name); 16262306a36Sopenharmony_ci fscache_put_cache(cache, fscache_cache_put_cache); 16362306a36Sopenharmony_ci return ERR_PTR(-EBUSY); 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return cache; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ciEXPORT_SYMBOL(fscache_acquire_cache); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci/** 17162306a36Sopenharmony_ci * fscache_put_cache - Release a cache-level cookie. 17262306a36Sopenharmony_ci * @cache: The cache cookie to be released 17362306a36Sopenharmony_ci * @where: An indication of where the release happened 17462306a36Sopenharmony_ci * 17562306a36Sopenharmony_ci * Release the caller's reference on a cache-level cookie. The @where 17662306a36Sopenharmony_ci * indication should give information about the circumstances in which the call 17762306a36Sopenharmony_ci * occurs and will be logged through a tracepoint. 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_civoid fscache_put_cache(struct fscache_cache *cache, 18062306a36Sopenharmony_ci enum fscache_cache_trace where) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci unsigned int debug_id; 18362306a36Sopenharmony_ci bool zero; 18462306a36Sopenharmony_ci int ref; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (IS_ERR_OR_NULL(cache)) 18762306a36Sopenharmony_ci return; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci debug_id = cache->debug_id; 19062306a36Sopenharmony_ci zero = __refcount_dec_and_test(&cache->ref, &ref); 19162306a36Sopenharmony_ci trace_fscache_cache(debug_id, ref - 1, where); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (zero) { 19462306a36Sopenharmony_ci down_write(&fscache_addremove_sem); 19562306a36Sopenharmony_ci list_del_init(&cache->cache_link); 19662306a36Sopenharmony_ci up_write(&fscache_addremove_sem); 19762306a36Sopenharmony_ci kfree(cache->name); 19862306a36Sopenharmony_ci kfree(cache); 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci/** 20362306a36Sopenharmony_ci * fscache_relinquish_cache - Reset cache state and release cookie 20462306a36Sopenharmony_ci * @cache: The cache cookie to be released 20562306a36Sopenharmony_ci * 20662306a36Sopenharmony_ci * Reset the state of a cache and release the caller's reference on a cache 20762306a36Sopenharmony_ci * cookie. 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_civoid fscache_relinquish_cache(struct fscache_cache *cache) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci enum fscache_cache_trace where = 21262306a36Sopenharmony_ci (cache->state == FSCACHE_CACHE_IS_PREPARING) ? 21362306a36Sopenharmony_ci fscache_cache_put_prep_failed : 21462306a36Sopenharmony_ci fscache_cache_put_relinquish; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci cache->ops = NULL; 21762306a36Sopenharmony_ci cache->cache_priv = NULL; 21862306a36Sopenharmony_ci fscache_set_cache_state(cache, FSCACHE_CACHE_IS_NOT_PRESENT); 21962306a36Sopenharmony_ci fscache_put_cache(cache, where); 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ciEXPORT_SYMBOL(fscache_relinquish_cache); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci/** 22462306a36Sopenharmony_ci * fscache_add_cache - Declare a cache as being open for business 22562306a36Sopenharmony_ci * @cache: The cache-level cookie representing the cache 22662306a36Sopenharmony_ci * @ops: Table of cache operations to use 22762306a36Sopenharmony_ci * @cache_priv: Private data for the cache record 22862306a36Sopenharmony_ci * 22962306a36Sopenharmony_ci * Add a cache to the system, making it available for netfs's to use. 23062306a36Sopenharmony_ci * 23162306a36Sopenharmony_ci * See Documentation/filesystems/caching/backend-api.rst for a complete 23262306a36Sopenharmony_ci * description. 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_ciint fscache_add_cache(struct fscache_cache *cache, 23562306a36Sopenharmony_ci const struct fscache_cache_ops *ops, 23662306a36Sopenharmony_ci void *cache_priv) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci int n_accesses; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci _enter("{%s,%s}", ops->name, cache->name); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci BUG_ON(fscache_cache_state(cache) != FSCACHE_CACHE_IS_PREPARING); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* Get a ref on the cache cookie and keep its n_accesses counter raised 24562306a36Sopenharmony_ci * by 1 to prevent wakeups from transitioning it to 0 until we're 24662306a36Sopenharmony_ci * withdrawing caching services from it. 24762306a36Sopenharmony_ci */ 24862306a36Sopenharmony_ci n_accesses = atomic_inc_return(&cache->n_accesses); 24962306a36Sopenharmony_ci trace_fscache_access_cache(cache->debug_id, refcount_read(&cache->ref), 25062306a36Sopenharmony_ci n_accesses, fscache_access_cache_pin); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci down_write(&fscache_addremove_sem); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci cache->ops = ops; 25562306a36Sopenharmony_ci cache->cache_priv = cache_priv; 25662306a36Sopenharmony_ci fscache_set_cache_state(cache, FSCACHE_CACHE_IS_ACTIVE); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci up_write(&fscache_addremove_sem); 25962306a36Sopenharmony_ci pr_notice("Cache \"%s\" added (type %s)\n", cache->name, ops->name); 26062306a36Sopenharmony_ci _leave(" = 0 [%s]", cache->name); 26162306a36Sopenharmony_ci return 0; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ciEXPORT_SYMBOL(fscache_add_cache); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci/** 26662306a36Sopenharmony_ci * fscache_begin_cache_access - Pin a cache so it can be accessed 26762306a36Sopenharmony_ci * @cache: The cache-level cookie 26862306a36Sopenharmony_ci * @why: An indication of the circumstances of the access for tracing 26962306a36Sopenharmony_ci * 27062306a36Sopenharmony_ci * Attempt to pin the cache to prevent it from going away whilst we're 27162306a36Sopenharmony_ci * accessing it and returns true if successful. This works as follows: 27262306a36Sopenharmony_ci * 27362306a36Sopenharmony_ci * (1) If the cache tests as not live (state is not FSCACHE_CACHE_IS_ACTIVE), 27462306a36Sopenharmony_ci * then we return false to indicate access was not permitted. 27562306a36Sopenharmony_ci * 27662306a36Sopenharmony_ci * (2) If the cache tests as live, then we increment the n_accesses count and 27762306a36Sopenharmony_ci * then recheck the liveness, ending the access if it ceased to be live. 27862306a36Sopenharmony_ci * 27962306a36Sopenharmony_ci * (3) When we end the access, we decrement n_accesses and wake up the any 28062306a36Sopenharmony_ci * waiters if it reaches 0. 28162306a36Sopenharmony_ci * 28262306a36Sopenharmony_ci * (4) Whilst the cache is caching, n_accesses is kept artificially 28362306a36Sopenharmony_ci * incremented to prevent wakeups from happening. 28462306a36Sopenharmony_ci * 28562306a36Sopenharmony_ci * (5) When the cache is taken offline, the state is changed to prevent new 28662306a36Sopenharmony_ci * accesses, n_accesses is decremented and we wait for n_accesses to 28762306a36Sopenharmony_ci * become 0. 28862306a36Sopenharmony_ci */ 28962306a36Sopenharmony_cibool fscache_begin_cache_access(struct fscache_cache *cache, enum fscache_access_trace why) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci int n_accesses; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (!fscache_cache_is_live(cache)) 29462306a36Sopenharmony_ci return false; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci n_accesses = atomic_inc_return(&cache->n_accesses); 29762306a36Sopenharmony_ci smp_mb__after_atomic(); /* Reread live flag after n_accesses */ 29862306a36Sopenharmony_ci trace_fscache_access_cache(cache->debug_id, refcount_read(&cache->ref), 29962306a36Sopenharmony_ci n_accesses, why); 30062306a36Sopenharmony_ci if (!fscache_cache_is_live(cache)) { 30162306a36Sopenharmony_ci fscache_end_cache_access(cache, fscache_access_unlive); 30262306a36Sopenharmony_ci return false; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci return true; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci/** 30862306a36Sopenharmony_ci * fscache_end_cache_access - Unpin a cache at the end of an access. 30962306a36Sopenharmony_ci * @cache: The cache-level cookie 31062306a36Sopenharmony_ci * @why: An indication of the circumstances of the access for tracing 31162306a36Sopenharmony_ci * 31262306a36Sopenharmony_ci * Unpin a cache after we've accessed it. The @why indicator is merely 31362306a36Sopenharmony_ci * provided for tracing purposes. 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_civoid fscache_end_cache_access(struct fscache_cache *cache, enum fscache_access_trace why) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci int n_accesses; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci smp_mb__before_atomic(); 32062306a36Sopenharmony_ci n_accesses = atomic_dec_return(&cache->n_accesses); 32162306a36Sopenharmony_ci trace_fscache_access_cache(cache->debug_id, refcount_read(&cache->ref), 32262306a36Sopenharmony_ci n_accesses, why); 32362306a36Sopenharmony_ci if (n_accesses == 0) 32462306a36Sopenharmony_ci wake_up_var(&cache->n_accesses); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci/** 32862306a36Sopenharmony_ci * fscache_io_error - Note a cache I/O error 32962306a36Sopenharmony_ci * @cache: The record describing the cache 33062306a36Sopenharmony_ci * 33162306a36Sopenharmony_ci * Note that an I/O error occurred in a cache and that it should no longer be 33262306a36Sopenharmony_ci * used for anything. This also reports the error into the kernel log. 33362306a36Sopenharmony_ci * 33462306a36Sopenharmony_ci * See Documentation/filesystems/caching/backend-api.rst for a complete 33562306a36Sopenharmony_ci * description. 33662306a36Sopenharmony_ci */ 33762306a36Sopenharmony_civoid fscache_io_error(struct fscache_cache *cache) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci if (fscache_set_cache_state_maybe(cache, 34062306a36Sopenharmony_ci FSCACHE_CACHE_IS_ACTIVE, 34162306a36Sopenharmony_ci FSCACHE_CACHE_GOT_IOERROR)) 34262306a36Sopenharmony_ci pr_err("Cache '%s' stopped due to I/O error\n", 34362306a36Sopenharmony_ci cache->name); 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ciEXPORT_SYMBOL(fscache_io_error); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci/** 34862306a36Sopenharmony_ci * fscache_withdraw_cache - Withdraw a cache from the active service 34962306a36Sopenharmony_ci * @cache: The cache cookie 35062306a36Sopenharmony_ci * 35162306a36Sopenharmony_ci * Begin the process of withdrawing a cache from service. This stops new 35262306a36Sopenharmony_ci * cache-level and volume-level accesses from taking place and waits for 35362306a36Sopenharmony_ci * currently ongoing cache-level accesses to end. 35462306a36Sopenharmony_ci */ 35562306a36Sopenharmony_civoid fscache_withdraw_cache(struct fscache_cache *cache) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci int n_accesses; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci pr_notice("Withdrawing cache \"%s\" (%u objs)\n", 36062306a36Sopenharmony_ci cache->name, atomic_read(&cache->object_count)); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci fscache_set_cache_state(cache, FSCACHE_CACHE_IS_WITHDRAWN); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* Allow wakeups on dec-to-0 */ 36562306a36Sopenharmony_ci n_accesses = atomic_dec_return(&cache->n_accesses); 36662306a36Sopenharmony_ci trace_fscache_access_cache(cache->debug_id, refcount_read(&cache->ref), 36762306a36Sopenharmony_ci n_accesses, fscache_access_cache_unpin); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci wait_var_event(&cache->n_accesses, 37062306a36Sopenharmony_ci atomic_read(&cache->n_accesses) == 0); 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ciEXPORT_SYMBOL(fscache_withdraw_cache); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 37562306a36Sopenharmony_cistatic const char fscache_cache_states[NR__FSCACHE_CACHE_STATE] = "-PAEW"; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci/* 37862306a36Sopenharmony_ci * Generate a list of caches in /proc/fs/fscache/caches 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_cistatic int fscache_caches_seq_show(struct seq_file *m, void *v) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci struct fscache_cache *cache; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (v == &fscache_caches) { 38562306a36Sopenharmony_ci seq_puts(m, 38662306a36Sopenharmony_ci "CACHE REF VOLS OBJS ACCES S NAME\n" 38762306a36Sopenharmony_ci "======== ===== ===== ===== ===== = ===============\n" 38862306a36Sopenharmony_ci ); 38962306a36Sopenharmony_ci return 0; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci cache = list_entry(v, struct fscache_cache, cache_link); 39362306a36Sopenharmony_ci seq_printf(m, 39462306a36Sopenharmony_ci "%08x %5d %5d %5d %5d %c %s\n", 39562306a36Sopenharmony_ci cache->debug_id, 39662306a36Sopenharmony_ci refcount_read(&cache->ref), 39762306a36Sopenharmony_ci atomic_read(&cache->n_volumes), 39862306a36Sopenharmony_ci atomic_read(&cache->object_count), 39962306a36Sopenharmony_ci atomic_read(&cache->n_accesses), 40062306a36Sopenharmony_ci fscache_cache_states[cache->state], 40162306a36Sopenharmony_ci cache->name ?: "-"); 40262306a36Sopenharmony_ci return 0; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic void *fscache_caches_seq_start(struct seq_file *m, loff_t *_pos) 40662306a36Sopenharmony_ci __acquires(fscache_addremove_sem) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci down_read(&fscache_addremove_sem); 40962306a36Sopenharmony_ci return seq_list_start_head(&fscache_caches, *_pos); 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic void *fscache_caches_seq_next(struct seq_file *m, void *v, loff_t *_pos) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci return seq_list_next(v, &fscache_caches, _pos); 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic void fscache_caches_seq_stop(struct seq_file *m, void *v) 41862306a36Sopenharmony_ci __releases(fscache_addremove_sem) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci up_read(&fscache_addremove_sem); 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ciconst struct seq_operations fscache_caches_seq_ops = { 42462306a36Sopenharmony_ci .start = fscache_caches_seq_start, 42562306a36Sopenharmony_ci .next = fscache_caches_seq_next, 42662306a36Sopenharmony_ci .stop = fscache_caches_seq_stop, 42762306a36Sopenharmony_ci .show = fscache_caches_seq_show, 42862306a36Sopenharmony_ci}; 42962306a36Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 430