xref: /openbmc/linux/fs/fscache/cache.c (revision 7d7ae873b5e0f46d19e5dc818d1a7809e4b7cc81)
19549332dSDavid Howells // SPDX-License-Identifier: GPL-2.0-or-later
29549332dSDavid Howells /* FS-Cache cache handling
39549332dSDavid Howells  *
49549332dSDavid Howells  * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved.
59549332dSDavid Howells  * Written by David Howells (dhowells@redhat.com)
69549332dSDavid Howells  */
79549332dSDavid Howells 
89549332dSDavid Howells #define FSCACHE_DEBUG_LEVEL CACHE
99549332dSDavid Howells #include <linux/export.h>
109549332dSDavid Howells #include <linux/slab.h>
119549332dSDavid Howells #include "internal.h"
129549332dSDavid Howells 
139549332dSDavid Howells static LIST_HEAD(fscache_caches);
149549332dSDavid Howells DECLARE_RWSEM(fscache_addremove_sem);
159549332dSDavid Howells EXPORT_SYMBOL(fscache_addremove_sem);
16cdf262f2SDavid Howells DECLARE_WAIT_QUEUE_HEAD(fscache_clearance_waiters);
17cdf262f2SDavid Howells EXPORT_SYMBOL(fscache_clearance_waiters);
189549332dSDavid Howells 
199549332dSDavid Howells static atomic_t fscache_cache_debug_id;
209549332dSDavid Howells 
219549332dSDavid Howells /*
229549332dSDavid Howells  * Allocate a cache cookie.
239549332dSDavid Howells  */
fscache_alloc_cache(const char * name)249549332dSDavid Howells static struct fscache_cache *fscache_alloc_cache(const char *name)
259549332dSDavid Howells {
269549332dSDavid Howells 	struct fscache_cache *cache;
279549332dSDavid Howells 
289549332dSDavid Howells 	cache = kzalloc(sizeof(*cache), GFP_KERNEL);
299549332dSDavid Howells 	if (cache) {
309549332dSDavid Howells 		if (name) {
319549332dSDavid Howells 			cache->name = kstrdup(name, GFP_KERNEL);
329549332dSDavid Howells 			if (!cache->name) {
339549332dSDavid Howells 				kfree(cache);
349549332dSDavid Howells 				return NULL;
359549332dSDavid Howells 			}
369549332dSDavid Howells 		}
379549332dSDavid Howells 		refcount_set(&cache->ref, 1);
389549332dSDavid Howells 		INIT_LIST_HEAD(&cache->cache_link);
399549332dSDavid Howells 		cache->debug_id = atomic_inc_return(&fscache_cache_debug_id);
409549332dSDavid Howells 	}
419549332dSDavid Howells 	return cache;
429549332dSDavid Howells }
439549332dSDavid Howells 
fscache_get_cache_maybe(struct fscache_cache * cache,enum fscache_cache_trace where)449549332dSDavid Howells static bool fscache_get_cache_maybe(struct fscache_cache *cache,
459549332dSDavid Howells 				    enum fscache_cache_trace where)
469549332dSDavid Howells {
479549332dSDavid Howells 	bool success;
489549332dSDavid Howells 	int ref;
499549332dSDavid Howells 
509549332dSDavid Howells 	success = __refcount_inc_not_zero(&cache->ref, &ref);
519549332dSDavid Howells 	if (success)
529549332dSDavid Howells 		trace_fscache_cache(cache->debug_id, ref + 1, where);
539549332dSDavid Howells 	return success;
549549332dSDavid Howells }
559549332dSDavid Howells 
569549332dSDavid Howells /*
579549332dSDavid Howells  * Look up a cache cookie.
589549332dSDavid Howells  */
fscache_lookup_cache(const char * name,bool is_cache)599549332dSDavid Howells struct fscache_cache *fscache_lookup_cache(const char *name, bool is_cache)
609549332dSDavid Howells {
619549332dSDavid Howells 	struct fscache_cache *candidate, *cache, *unnamed = NULL;
629549332dSDavid Howells 
639549332dSDavid Howells 	/* firstly check for the existence of the cache under read lock */
649549332dSDavid Howells 	down_read(&fscache_addremove_sem);
659549332dSDavid Howells 
669549332dSDavid Howells 	list_for_each_entry(cache, &fscache_caches, cache_link) {
679549332dSDavid Howells 		if (cache->name && name && strcmp(cache->name, name) == 0 &&
689549332dSDavid Howells 		    fscache_get_cache_maybe(cache, fscache_cache_get_acquire))
699549332dSDavid Howells 			goto got_cache_r;
709549332dSDavid Howells 		if (!cache->name && !name &&
719549332dSDavid Howells 		    fscache_get_cache_maybe(cache, fscache_cache_get_acquire))
729549332dSDavid Howells 			goto got_cache_r;
739549332dSDavid Howells 	}
749549332dSDavid Howells 
759549332dSDavid Howells 	if (!name) {
769549332dSDavid Howells 		list_for_each_entry(cache, &fscache_caches, cache_link) {
779549332dSDavid Howells 			if (cache->name &&
789549332dSDavid Howells 			    fscache_get_cache_maybe(cache, fscache_cache_get_acquire))
799549332dSDavid Howells 				goto got_cache_r;
809549332dSDavid Howells 		}
819549332dSDavid Howells 	}
829549332dSDavid Howells 
839549332dSDavid Howells 	up_read(&fscache_addremove_sem);
849549332dSDavid Howells 
859549332dSDavid Howells 	/* the cache does not exist - create a candidate */
869549332dSDavid Howells 	candidate = fscache_alloc_cache(name);
879549332dSDavid Howells 	if (!candidate)
889549332dSDavid Howells 		return ERR_PTR(-ENOMEM);
899549332dSDavid Howells 
909549332dSDavid Howells 	/* write lock, search again and add if still not present */
919549332dSDavid Howells 	down_write(&fscache_addremove_sem);
929549332dSDavid Howells 
939549332dSDavid Howells 	list_for_each_entry(cache, &fscache_caches, cache_link) {
949549332dSDavid Howells 		if (cache->name && name && strcmp(cache->name, name) == 0 &&
959549332dSDavid Howells 		    fscache_get_cache_maybe(cache, fscache_cache_get_acquire))
969549332dSDavid Howells 			goto got_cache_w;
979549332dSDavid Howells 		if (!cache->name) {
989549332dSDavid Howells 			unnamed = cache;
999549332dSDavid Howells 			if (!name &&
1009549332dSDavid Howells 			    fscache_get_cache_maybe(cache, fscache_cache_get_acquire))
1019549332dSDavid Howells 				goto got_cache_w;
1029549332dSDavid Howells 		}
1039549332dSDavid Howells 	}
1049549332dSDavid Howells 
1059549332dSDavid Howells 	if (unnamed && is_cache &&
1069549332dSDavid Howells 	    fscache_get_cache_maybe(unnamed, fscache_cache_get_acquire))
1079549332dSDavid Howells 		goto use_unnamed_cache;
1089549332dSDavid Howells 
1099549332dSDavid Howells 	if (!name) {
1109549332dSDavid Howells 		list_for_each_entry(cache, &fscache_caches, cache_link) {
1119549332dSDavid Howells 			if (cache->name &&
1129549332dSDavid Howells 			    fscache_get_cache_maybe(cache, fscache_cache_get_acquire))
1139549332dSDavid Howells 				goto got_cache_w;
1149549332dSDavid Howells 		}
1159549332dSDavid Howells 	}
1169549332dSDavid Howells 
1179549332dSDavid Howells 	list_add_tail(&candidate->cache_link, &fscache_caches);
1189549332dSDavid Howells 	trace_fscache_cache(candidate->debug_id,
1199549332dSDavid Howells 			    refcount_read(&candidate->ref),
1209549332dSDavid Howells 			    fscache_cache_new_acquire);
1219549332dSDavid Howells 	up_write(&fscache_addremove_sem);
1229549332dSDavid Howells 	return candidate;
1239549332dSDavid Howells 
1249549332dSDavid Howells got_cache_r:
1259549332dSDavid Howells 	up_read(&fscache_addremove_sem);
1269549332dSDavid Howells 	return cache;
1279549332dSDavid Howells use_unnamed_cache:
1289549332dSDavid Howells 	cache = unnamed;
1299549332dSDavid Howells 	cache->name = candidate->name;
1309549332dSDavid Howells 	candidate->name = NULL;
1319549332dSDavid Howells got_cache_w:
1329549332dSDavid Howells 	up_write(&fscache_addremove_sem);
1339549332dSDavid Howells 	kfree(candidate->name);
1349549332dSDavid Howells 	kfree(candidate);
1359549332dSDavid Howells 	return cache;
1369549332dSDavid Howells }
1379549332dSDavid Howells 
1389549332dSDavid Howells /**
1399549332dSDavid Howells  * fscache_acquire_cache - Acquire a cache-level cookie.
1409549332dSDavid Howells  * @name: The name of the cache.
1419549332dSDavid Howells  *
1429549332dSDavid Howells  * Get a cookie to represent an actual cache.  If a name is given and there is
1439549332dSDavid Howells  * a nameless cache record available, this will acquire that and set its name,
1449549332dSDavid Howells  * directing all the volumes using it to this cache.
1459549332dSDavid Howells  *
1469549332dSDavid Howells  * The cache will be switched over to the preparing state if not currently in
1479549332dSDavid Howells  * use, otherwise -EBUSY will be returned.
1489549332dSDavid Howells  */
fscache_acquire_cache(const char * name)1499549332dSDavid Howells struct fscache_cache *fscache_acquire_cache(const char *name)
1509549332dSDavid Howells {
1519549332dSDavid Howells 	struct fscache_cache *cache;
1529549332dSDavid Howells 
1539549332dSDavid Howells 	ASSERT(name);
1549549332dSDavid Howells 	cache = fscache_lookup_cache(name, true);
1559549332dSDavid Howells 	if (IS_ERR(cache))
1569549332dSDavid Howells 		return cache;
1579549332dSDavid Howells 
1589549332dSDavid Howells 	if (!fscache_set_cache_state_maybe(cache,
1599549332dSDavid Howells 					   FSCACHE_CACHE_IS_NOT_PRESENT,
1609549332dSDavid Howells 					   FSCACHE_CACHE_IS_PREPARING)) {
1619549332dSDavid Howells 		pr_warn("Cache tag %s in use\n", name);
1629549332dSDavid Howells 		fscache_put_cache(cache, fscache_cache_put_cache);
1639549332dSDavid Howells 		return ERR_PTR(-EBUSY);
1649549332dSDavid Howells 	}
1659549332dSDavid Howells 
1669549332dSDavid Howells 	return cache;
1679549332dSDavid Howells }
1689549332dSDavid Howells EXPORT_SYMBOL(fscache_acquire_cache);
1699549332dSDavid Howells 
1709549332dSDavid Howells /**
1719549332dSDavid Howells  * fscache_put_cache - Release a cache-level cookie.
1729549332dSDavid Howells  * @cache: The cache cookie to be released
1739549332dSDavid Howells  * @where: An indication of where the release happened
1749549332dSDavid Howells  *
1759549332dSDavid Howells  * Release the caller's reference on a cache-level cookie.  The @where
1769549332dSDavid Howells  * indication should give information about the circumstances in which the call
1779549332dSDavid Howells  * occurs and will be logged through a tracepoint.
1789549332dSDavid Howells  */
fscache_put_cache(struct fscache_cache * cache,enum fscache_cache_trace where)1799549332dSDavid Howells void fscache_put_cache(struct fscache_cache *cache,
1809549332dSDavid Howells 		       enum fscache_cache_trace where)
1819549332dSDavid Howells {
182*1c45256eSDan Carpenter 	unsigned int debug_id;
1839549332dSDavid Howells 	bool zero;
1849549332dSDavid Howells 	int ref;
1859549332dSDavid Howells 
1869549332dSDavid Howells 	if (IS_ERR_OR_NULL(cache))
1879549332dSDavid Howells 		return;
1889549332dSDavid Howells 
189*1c45256eSDan Carpenter 	debug_id = cache->debug_id;
1909549332dSDavid Howells 	zero = __refcount_dec_and_test(&cache->ref, &ref);
1919549332dSDavid Howells 	trace_fscache_cache(debug_id, ref - 1, where);
1929549332dSDavid Howells 
1939549332dSDavid Howells 	if (zero) {
1949549332dSDavid Howells 		down_write(&fscache_addremove_sem);
1959549332dSDavid Howells 		list_del_init(&cache->cache_link);
1969549332dSDavid Howells 		up_write(&fscache_addremove_sem);
1979549332dSDavid Howells 		kfree(cache->name);
1989549332dSDavid Howells 		kfree(cache);
1999549332dSDavid Howells 	}
2009549332dSDavid Howells }
2019549332dSDavid Howells 
2029549332dSDavid Howells /**
2039549332dSDavid Howells  * fscache_relinquish_cache - Reset cache state and release cookie
2049549332dSDavid Howells  * @cache: The cache cookie to be released
2059549332dSDavid Howells  *
2069549332dSDavid Howells  * Reset the state of a cache and release the caller's reference on a cache
2079549332dSDavid Howells  * cookie.
2089549332dSDavid Howells  */
fscache_relinquish_cache(struct fscache_cache * cache)2099549332dSDavid Howells void fscache_relinquish_cache(struct fscache_cache *cache)
2109549332dSDavid Howells {
2119549332dSDavid Howells 	enum fscache_cache_trace where =
2129549332dSDavid Howells 		(cache->state == FSCACHE_CACHE_IS_PREPARING) ?
2139549332dSDavid Howells 		fscache_cache_put_prep_failed :
2149549332dSDavid Howells 		fscache_cache_put_relinquish;
2159549332dSDavid Howells 
2162e0c76aeSDavid Howells 	cache->ops = NULL;
2179549332dSDavid Howells 	cache->cache_priv = NULL;
218b3c958c2SYue Hu 	fscache_set_cache_state(cache, FSCACHE_CACHE_IS_NOT_PRESENT);
2199549332dSDavid Howells 	fscache_put_cache(cache, where);
2209549332dSDavid Howells }
2219549332dSDavid Howells EXPORT_SYMBOL(fscache_relinquish_cache);
2229549332dSDavid Howells 
22323e12e28SDavid Howells /**
2242e0c76aeSDavid Howells  * fscache_add_cache - Declare a cache as being open for business
2252e0c76aeSDavid Howells  * @cache: The cache-level cookie representing the cache
2262e0c76aeSDavid Howells  * @ops: Table of cache operations to use
2272e0c76aeSDavid Howells  * @cache_priv: Private data for the cache record
2282e0c76aeSDavid Howells  *
2292e0c76aeSDavid Howells  * Add a cache to the system, making it available for netfs's to use.
2302e0c76aeSDavid Howells  *
2312e0c76aeSDavid Howells  * See Documentation/filesystems/caching/backend-api.rst for a complete
2322e0c76aeSDavid Howells  * description.
2332e0c76aeSDavid Howells  */
fscache_add_cache(struct fscache_cache * cache,const struct fscache_cache_ops * ops,void * cache_priv)2342e0c76aeSDavid Howells int fscache_add_cache(struct fscache_cache *cache,
2352e0c76aeSDavid Howells 		      const struct fscache_cache_ops *ops,
2362e0c76aeSDavid Howells 		      void *cache_priv)
2372e0c76aeSDavid Howells {
2382e0c76aeSDavid Howells 	int n_accesses;
2392e0c76aeSDavid Howells 
2402e0c76aeSDavid Howells 	_enter("{%s,%s}", ops->name, cache->name);
2412e0c76aeSDavid Howells 
2422e0c76aeSDavid Howells 	BUG_ON(fscache_cache_state(cache) != FSCACHE_CACHE_IS_PREPARING);
2432e0c76aeSDavid Howells 
2442e0c76aeSDavid Howells 	/* Get a ref on the cache cookie and keep its n_accesses counter raised
2452e0c76aeSDavid Howells 	 * by 1 to prevent wakeups from transitioning it to 0 until we're
2462e0c76aeSDavid Howells 	 * withdrawing caching services from it.
2472e0c76aeSDavid Howells 	 */
2482e0c76aeSDavid Howells 	n_accesses = atomic_inc_return(&cache->n_accesses);
2492e0c76aeSDavid Howells 	trace_fscache_access_cache(cache->debug_id, refcount_read(&cache->ref),
2502e0c76aeSDavid Howells 				   n_accesses, fscache_access_cache_pin);
2512e0c76aeSDavid Howells 
2522e0c76aeSDavid Howells 	down_write(&fscache_addremove_sem);
2532e0c76aeSDavid Howells 
2542e0c76aeSDavid Howells 	cache->ops = ops;
2552e0c76aeSDavid Howells 	cache->cache_priv = cache_priv;
2562e0c76aeSDavid Howells 	fscache_set_cache_state(cache, FSCACHE_CACHE_IS_ACTIVE);
2572e0c76aeSDavid Howells 
2582e0c76aeSDavid Howells 	up_write(&fscache_addremove_sem);
2592e0c76aeSDavid Howells 	pr_notice("Cache \"%s\" added (type %s)\n", cache->name, ops->name);
2602e0c76aeSDavid Howells 	_leave(" = 0 [%s]", cache->name);
2612e0c76aeSDavid Howells 	return 0;
2622e0c76aeSDavid Howells }
2632e0c76aeSDavid Howells EXPORT_SYMBOL(fscache_add_cache);
2642e0c76aeSDavid Howells 
2652e0c76aeSDavid Howells /**
26623e12e28SDavid Howells  * fscache_begin_cache_access - Pin a cache so it can be accessed
26723e12e28SDavid Howells  * @cache: The cache-level cookie
26823e12e28SDavid Howells  * @why: An indication of the circumstances of the access for tracing
26923e12e28SDavid Howells  *
27023e12e28SDavid Howells  * Attempt to pin the cache to prevent it from going away whilst we're
27123e12e28SDavid Howells  * accessing it and returns true if successful.  This works as follows:
27223e12e28SDavid Howells  *
27323e12e28SDavid Howells  *  (1) If the cache tests as not live (state is not FSCACHE_CACHE_IS_ACTIVE),
27423e12e28SDavid Howells  *      then we return false to indicate access was not permitted.
27523e12e28SDavid Howells  *
27623e12e28SDavid Howells  *  (2) If the cache tests as live, then we increment the n_accesses count and
27723e12e28SDavid Howells  *      then recheck the liveness, ending the access if it ceased to be live.
27823e12e28SDavid Howells  *
27923e12e28SDavid Howells  *  (3) When we end the access, we decrement n_accesses and wake up the any
28023e12e28SDavid Howells  *      waiters if it reaches 0.
28123e12e28SDavid Howells  *
28223e12e28SDavid Howells  *  (4) Whilst the cache is caching, n_accesses is kept artificially
28323e12e28SDavid Howells  *      incremented to prevent wakeups from happening.
28423e12e28SDavid Howells  *
28523e12e28SDavid Howells  *  (5) When the cache is taken offline, the state is changed to prevent new
28623e12e28SDavid Howells  *      accesses, n_accesses is decremented and we wait for n_accesses to
28723e12e28SDavid Howells  *      become 0.
28823e12e28SDavid Howells  */
fscache_begin_cache_access(struct fscache_cache * cache,enum fscache_access_trace why)28923e12e28SDavid Howells bool fscache_begin_cache_access(struct fscache_cache *cache, enum fscache_access_trace why)
29023e12e28SDavid Howells {
29123e12e28SDavid Howells 	int n_accesses;
29223e12e28SDavid Howells 
29323e12e28SDavid Howells 	if (!fscache_cache_is_live(cache))
29423e12e28SDavid Howells 		return false;
29523e12e28SDavid Howells 
29623e12e28SDavid Howells 	n_accesses = atomic_inc_return(&cache->n_accesses);
29723e12e28SDavid Howells 	smp_mb__after_atomic(); /* Reread live flag after n_accesses */
29823e12e28SDavid Howells 	trace_fscache_access_cache(cache->debug_id, refcount_read(&cache->ref),
29923e12e28SDavid Howells 				   n_accesses, why);
30023e12e28SDavid Howells 	if (!fscache_cache_is_live(cache)) {
30123e12e28SDavid Howells 		fscache_end_cache_access(cache, fscache_access_unlive);
30223e12e28SDavid Howells 		return false;
30323e12e28SDavid Howells 	}
30423e12e28SDavid Howells 	return true;
30523e12e28SDavid Howells }
30623e12e28SDavid Howells 
30723e12e28SDavid Howells /**
30823e12e28SDavid Howells  * fscache_end_cache_access - Unpin a cache at the end of an access.
30923e12e28SDavid Howells  * @cache: The cache-level cookie
31023e12e28SDavid Howells  * @why: An indication of the circumstances of the access for tracing
31123e12e28SDavid Howells  *
31223e12e28SDavid Howells  * Unpin a cache after we've accessed it.  The @why indicator is merely
31323e12e28SDavid Howells  * provided for tracing purposes.
31423e12e28SDavid Howells  */
fscache_end_cache_access(struct fscache_cache * cache,enum fscache_access_trace why)31523e12e28SDavid Howells void fscache_end_cache_access(struct fscache_cache *cache, enum fscache_access_trace why)
31623e12e28SDavid Howells {
31723e12e28SDavid Howells 	int n_accesses;
31823e12e28SDavid Howells 
31923e12e28SDavid Howells 	smp_mb__before_atomic();
32023e12e28SDavid Howells 	n_accesses = atomic_dec_return(&cache->n_accesses);
32123e12e28SDavid Howells 	trace_fscache_access_cache(cache->debug_id, refcount_read(&cache->ref),
32223e12e28SDavid Howells 				   n_accesses, why);
32323e12e28SDavid Howells 	if (n_accesses == 0)
32423e12e28SDavid Howells 		wake_up_var(&cache->n_accesses);
32523e12e28SDavid Howells }
32623e12e28SDavid Howells 
3272e0c76aeSDavid Howells /**
32829f18e79SDavid Howells  * fscache_io_error - Note a cache I/O error
32929f18e79SDavid Howells  * @cache: The record describing the cache
33029f18e79SDavid Howells  *
33129f18e79SDavid Howells  * Note that an I/O error occurred in a cache and that it should no longer be
33229f18e79SDavid Howells  * used for anything.  This also reports the error into the kernel log.
33329f18e79SDavid Howells  *
33429f18e79SDavid Howells  * See Documentation/filesystems/caching/backend-api.rst for a complete
33529f18e79SDavid Howells  * description.
33629f18e79SDavid Howells  */
fscache_io_error(struct fscache_cache * cache)33729f18e79SDavid Howells void fscache_io_error(struct fscache_cache *cache)
33829f18e79SDavid Howells {
33929f18e79SDavid Howells 	if (fscache_set_cache_state_maybe(cache,
34029f18e79SDavid Howells 					  FSCACHE_CACHE_IS_ACTIVE,
34129f18e79SDavid Howells 					  FSCACHE_CACHE_GOT_IOERROR))
34229f18e79SDavid Howells 		pr_err("Cache '%s' stopped due to I/O error\n",
34329f18e79SDavid Howells 		       cache->name);
34429f18e79SDavid Howells }
34529f18e79SDavid Howells EXPORT_SYMBOL(fscache_io_error);
34629f18e79SDavid Howells 
34729f18e79SDavid Howells /**
3482e0c76aeSDavid Howells  * fscache_withdraw_cache - Withdraw a cache from the active service
3492e0c76aeSDavid Howells  * @cache: The cache cookie
3502e0c76aeSDavid Howells  *
3512e0c76aeSDavid Howells  * Begin the process of withdrawing a cache from service.  This stops new
3522e0c76aeSDavid Howells  * cache-level and volume-level accesses from taking place and waits for
3532e0c76aeSDavid Howells  * currently ongoing cache-level accesses to end.
3542e0c76aeSDavid Howells  */
fscache_withdraw_cache(struct fscache_cache * cache)3552e0c76aeSDavid Howells void fscache_withdraw_cache(struct fscache_cache *cache)
3562e0c76aeSDavid Howells {
3572e0c76aeSDavid Howells 	int n_accesses;
3582e0c76aeSDavid Howells 
3592e0c76aeSDavid Howells 	pr_notice("Withdrawing cache \"%s\" (%u objs)\n",
3602e0c76aeSDavid Howells 		  cache->name, atomic_read(&cache->object_count));
3612e0c76aeSDavid Howells 
3622e0c76aeSDavid Howells 	fscache_set_cache_state(cache, FSCACHE_CACHE_IS_WITHDRAWN);
3632e0c76aeSDavid Howells 
3642e0c76aeSDavid Howells 	/* Allow wakeups on dec-to-0 */
3652e0c76aeSDavid Howells 	n_accesses = atomic_dec_return(&cache->n_accesses);
3662e0c76aeSDavid Howells 	trace_fscache_access_cache(cache->debug_id, refcount_read(&cache->ref),
3672e0c76aeSDavid Howells 				   n_accesses, fscache_access_cache_unpin);
3682e0c76aeSDavid Howells 
3692e0c76aeSDavid Howells 	wait_var_event(&cache->n_accesses,
3702e0c76aeSDavid Howells 		       atomic_read(&cache->n_accesses) == 0);
3712e0c76aeSDavid Howells }
3722e0c76aeSDavid Howells EXPORT_SYMBOL(fscache_withdraw_cache);
3732e0c76aeSDavid Howells 
3749549332dSDavid Howells #ifdef CONFIG_PROC_FS
3759549332dSDavid Howells static const char fscache_cache_states[NR__FSCACHE_CACHE_STATE] = "-PAEW";
3769549332dSDavid Howells 
3779549332dSDavid Howells /*
3789549332dSDavid Howells  * Generate a list of caches in /proc/fs/fscache/caches
3799549332dSDavid Howells  */
fscache_caches_seq_show(struct seq_file * m,void * v)3809549332dSDavid Howells static int fscache_caches_seq_show(struct seq_file *m, void *v)
3819549332dSDavid Howells {
3829549332dSDavid Howells 	struct fscache_cache *cache;
3839549332dSDavid Howells 
3849549332dSDavid Howells 	if (v == &fscache_caches) {
3859549332dSDavid Howells 		seq_puts(m,
3869549332dSDavid Howells 			 "CACHE    REF   VOLS  OBJS  ACCES S NAME\n"
3879549332dSDavid Howells 			 "======== ===== ===== ===== ===== = ===============\n"
3889549332dSDavid Howells 			 );
3899549332dSDavid Howells 		return 0;
3909549332dSDavid Howells 	}
3919549332dSDavid Howells 
3929549332dSDavid Howells 	cache = list_entry(v, struct fscache_cache, cache_link);
3939549332dSDavid Howells 	seq_printf(m,
3949549332dSDavid Howells 		   "%08x %5d %5d %5d %5d %c %s\n",
3959549332dSDavid Howells 		   cache->debug_id,
3969549332dSDavid Howells 		   refcount_read(&cache->ref),
3979549332dSDavid Howells 		   atomic_read(&cache->n_volumes),
3989549332dSDavid Howells 		   atomic_read(&cache->object_count),
3999549332dSDavid Howells 		   atomic_read(&cache->n_accesses),
4009549332dSDavid Howells 		   fscache_cache_states[cache->state],
4019549332dSDavid Howells 		   cache->name ?: "-");
4029549332dSDavid Howells 	return 0;
4039549332dSDavid Howells }
4049549332dSDavid Howells 
fscache_caches_seq_start(struct seq_file * m,loff_t * _pos)4059549332dSDavid Howells static void *fscache_caches_seq_start(struct seq_file *m, loff_t *_pos)
4069549332dSDavid Howells 	__acquires(fscache_addremove_sem)
4079549332dSDavid Howells {
4089549332dSDavid Howells 	down_read(&fscache_addremove_sem);
4099549332dSDavid Howells 	return seq_list_start_head(&fscache_caches, *_pos);
4109549332dSDavid Howells }
4119549332dSDavid Howells 
fscache_caches_seq_next(struct seq_file * m,void * v,loff_t * _pos)4129549332dSDavid Howells static void *fscache_caches_seq_next(struct seq_file *m, void *v, loff_t *_pos)
4139549332dSDavid Howells {
4149549332dSDavid Howells 	return seq_list_next(v, &fscache_caches, _pos);
4159549332dSDavid Howells }
4169549332dSDavid Howells 
fscache_caches_seq_stop(struct seq_file * m,void * v)4179549332dSDavid Howells static void fscache_caches_seq_stop(struct seq_file *m, void *v)
4189549332dSDavid Howells 	__releases(fscache_addremove_sem)
4199549332dSDavid Howells {
4209549332dSDavid Howells 	up_read(&fscache_addremove_sem);
4219549332dSDavid Howells }
4229549332dSDavid Howells 
4239549332dSDavid Howells const struct seq_operations fscache_caches_seq_ops = {
4249549332dSDavid Howells 	.start  = fscache_caches_seq_start,
4259549332dSDavid Howells 	.next   = fscache_caches_seq_next,
4269549332dSDavid Howells 	.stop   = fscache_caches_seq_stop,
4279549332dSDavid Howells 	.show   = fscache_caches_seq_show,
4289549332dSDavid Howells };
4299549332dSDavid Howells #endif /* CONFIG_PROC_FS */
430