162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* netfs cookie management
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 * See Documentation/filesystems/caching/netfs-api.rst for more information on
862306a36Sopenharmony_ci * the netfs API.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define FSCACHE_DEBUG_LEVEL COOKIE
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include "internal.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistruct kmem_cache *fscache_cookie_jar;
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic void fscache_cookie_lru_timed_out(struct timer_list *timer);
1962306a36Sopenharmony_cistatic void fscache_cookie_lru_worker(struct work_struct *work);
2062306a36Sopenharmony_cistatic void fscache_cookie_worker(struct work_struct *work);
2162306a36Sopenharmony_cistatic void fscache_unhash_cookie(struct fscache_cookie *cookie);
2262306a36Sopenharmony_cistatic void fscache_perform_invalidation(struct fscache_cookie *cookie);
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define fscache_cookie_hash_shift 15
2562306a36Sopenharmony_cistatic struct hlist_bl_head fscache_cookie_hash[1 << fscache_cookie_hash_shift];
2662306a36Sopenharmony_cistatic LIST_HEAD(fscache_cookies);
2762306a36Sopenharmony_cistatic DEFINE_RWLOCK(fscache_cookies_lock);
2862306a36Sopenharmony_cistatic LIST_HEAD(fscache_cookie_lru);
2962306a36Sopenharmony_cistatic DEFINE_SPINLOCK(fscache_cookie_lru_lock);
3062306a36Sopenharmony_ciDEFINE_TIMER(fscache_cookie_lru_timer, fscache_cookie_lru_timed_out);
3162306a36Sopenharmony_cistatic DECLARE_WORK(fscache_cookie_lru_work, fscache_cookie_lru_worker);
3262306a36Sopenharmony_cistatic const char fscache_cookie_states[FSCACHE_COOKIE_STATE__NR] = "-LCAIFUWRD";
3362306a36Sopenharmony_cistatic unsigned int fscache_lru_cookie_timeout = 10 * HZ;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_civoid fscache_print_cookie(struct fscache_cookie *cookie, char prefix)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	const u8 *k;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	pr_err("%c-cookie c=%08x [fl=%lx na=%u nA=%u s=%c]\n",
4062306a36Sopenharmony_ci	       prefix,
4162306a36Sopenharmony_ci	       cookie->debug_id,
4262306a36Sopenharmony_ci	       cookie->flags,
4362306a36Sopenharmony_ci	       atomic_read(&cookie->n_active),
4462306a36Sopenharmony_ci	       atomic_read(&cookie->n_accesses),
4562306a36Sopenharmony_ci	       fscache_cookie_states[cookie->state]);
4662306a36Sopenharmony_ci	pr_err("%c-cookie V=%08x [%s]\n",
4762306a36Sopenharmony_ci	       prefix,
4862306a36Sopenharmony_ci	       cookie->volume->debug_id,
4962306a36Sopenharmony_ci	       cookie->volume->key);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	k = (cookie->key_len <= sizeof(cookie->inline_key)) ?
5262306a36Sopenharmony_ci		cookie->inline_key : cookie->key;
5362306a36Sopenharmony_ci	pr_err("%c-key=[%u] '%*phN'\n", prefix, cookie->key_len, cookie->key_len, k);
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic void fscache_free_cookie(struct fscache_cookie *cookie)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	if (WARN_ON_ONCE(!list_empty(&cookie->commit_link))) {
5962306a36Sopenharmony_ci		spin_lock(&fscache_cookie_lru_lock);
6062306a36Sopenharmony_ci		list_del_init(&cookie->commit_link);
6162306a36Sopenharmony_ci		spin_unlock(&fscache_cookie_lru_lock);
6262306a36Sopenharmony_ci		fscache_stat_d(&fscache_n_cookies_lru);
6362306a36Sopenharmony_ci		fscache_stat(&fscache_n_cookies_lru_removed);
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	if (WARN_ON_ONCE(test_bit(FSCACHE_COOKIE_IS_HASHED, &cookie->flags))) {
6762306a36Sopenharmony_ci		fscache_print_cookie(cookie, 'F');
6862306a36Sopenharmony_ci		return;
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	write_lock(&fscache_cookies_lock);
7262306a36Sopenharmony_ci	list_del(&cookie->proc_link);
7362306a36Sopenharmony_ci	write_unlock(&fscache_cookies_lock);
7462306a36Sopenharmony_ci	if (cookie->aux_len > sizeof(cookie->inline_aux))
7562306a36Sopenharmony_ci		kfree(cookie->aux);
7662306a36Sopenharmony_ci	if (cookie->key_len > sizeof(cookie->inline_key))
7762306a36Sopenharmony_ci		kfree(cookie->key);
7862306a36Sopenharmony_ci	fscache_stat_d(&fscache_n_cookies);
7962306a36Sopenharmony_ci	kmem_cache_free(fscache_cookie_jar, cookie);
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic void __fscache_queue_cookie(struct fscache_cookie *cookie)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	if (!queue_work(fscache_wq, &cookie->work))
8562306a36Sopenharmony_ci		fscache_put_cookie(cookie, fscache_cookie_put_over_queued);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic void fscache_queue_cookie(struct fscache_cookie *cookie,
8962306a36Sopenharmony_ci				 enum fscache_cookie_trace where)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	fscache_get_cookie(cookie, where);
9262306a36Sopenharmony_ci	__fscache_queue_cookie(cookie);
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci/*
9662306a36Sopenharmony_ci * Initialise the access gate on a cookie by setting a flag to prevent the
9762306a36Sopenharmony_ci * state machine from being queued when the access counter transitions to 0.
9862306a36Sopenharmony_ci * We're only interested in this when we withdraw caching services from the
9962306a36Sopenharmony_ci * cookie.
10062306a36Sopenharmony_ci */
10162306a36Sopenharmony_cistatic void fscache_init_access_gate(struct fscache_cookie *cookie)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	int n_accesses;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	n_accesses = atomic_read(&cookie->n_accesses);
10662306a36Sopenharmony_ci	trace_fscache_access(cookie->debug_id, refcount_read(&cookie->ref),
10762306a36Sopenharmony_ci			     n_accesses, fscache_access_cache_pin);
10862306a36Sopenharmony_ci	set_bit(FSCACHE_COOKIE_NO_ACCESS_WAKE, &cookie->flags);
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/**
11262306a36Sopenharmony_ci * fscache_end_cookie_access - Unpin a cache at the end of an access.
11362306a36Sopenharmony_ci * @cookie: A data file cookie
11462306a36Sopenharmony_ci * @why: An indication of the circumstances of the access for tracing
11562306a36Sopenharmony_ci *
11662306a36Sopenharmony_ci * Unpin a cache cookie after we've accessed it and bring a deferred
11762306a36Sopenharmony_ci * relinquishment or withdrawal state into effect.
11862306a36Sopenharmony_ci *
11962306a36Sopenharmony_ci * The @why indicator is provided for tracing purposes.
12062306a36Sopenharmony_ci */
12162306a36Sopenharmony_civoid fscache_end_cookie_access(struct fscache_cookie *cookie,
12262306a36Sopenharmony_ci			       enum fscache_access_trace why)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	int n_accesses;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	smp_mb__before_atomic();
12762306a36Sopenharmony_ci	n_accesses = atomic_dec_return(&cookie->n_accesses);
12862306a36Sopenharmony_ci	trace_fscache_access(cookie->debug_id, refcount_read(&cookie->ref),
12962306a36Sopenharmony_ci			     n_accesses, why);
13062306a36Sopenharmony_ci	if (n_accesses == 0 &&
13162306a36Sopenharmony_ci	    !test_bit(FSCACHE_COOKIE_NO_ACCESS_WAKE, &cookie->flags))
13262306a36Sopenharmony_ci		fscache_queue_cookie(cookie, fscache_cookie_get_end_access);
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ciEXPORT_SYMBOL(fscache_end_cookie_access);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci/*
13762306a36Sopenharmony_ci * Pin the cache behind a cookie so that we can access it.
13862306a36Sopenharmony_ci */
13962306a36Sopenharmony_cistatic void __fscache_begin_cookie_access(struct fscache_cookie *cookie,
14062306a36Sopenharmony_ci					  enum fscache_access_trace why)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	int n_accesses;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	n_accesses = atomic_inc_return(&cookie->n_accesses);
14562306a36Sopenharmony_ci	smp_mb__after_atomic(); /* (Future) read state after is-caching.
14662306a36Sopenharmony_ci				 * Reread n_accesses after is-caching
14762306a36Sopenharmony_ci				 */
14862306a36Sopenharmony_ci	trace_fscache_access(cookie->debug_id, refcount_read(&cookie->ref),
14962306a36Sopenharmony_ci			     n_accesses, why);
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci/**
15362306a36Sopenharmony_ci * fscache_begin_cookie_access - Pin a cache so data can be accessed
15462306a36Sopenharmony_ci * @cookie: A data file cookie
15562306a36Sopenharmony_ci * @why: An indication of the circumstances of the access for tracing
15662306a36Sopenharmony_ci *
15762306a36Sopenharmony_ci * Attempt to pin the cache to prevent it from going away whilst we're
15862306a36Sopenharmony_ci * accessing data and returns true if successful.  This works as follows:
15962306a36Sopenharmony_ci *
16062306a36Sopenharmony_ci *  (1) If the cookie is not being cached (ie. FSCACHE_COOKIE_IS_CACHING is not
16162306a36Sopenharmony_ci *      set), we return false to indicate access was not permitted.
16262306a36Sopenharmony_ci *
16362306a36Sopenharmony_ci *  (2) If the cookie is being cached, we increment its n_accesses count and
16462306a36Sopenharmony_ci *      then recheck the IS_CACHING flag, ending the access if it got cleared.
16562306a36Sopenharmony_ci *
16662306a36Sopenharmony_ci *  (3) When we end the access, we decrement the cookie's n_accesses and wake
16762306a36Sopenharmony_ci *      up the any waiters if it reaches 0.
16862306a36Sopenharmony_ci *
16962306a36Sopenharmony_ci *  (4) Whilst the cookie is actively being cached, its n_accesses is kept
17062306a36Sopenharmony_ci *      artificially incremented to prevent wakeups from happening.
17162306a36Sopenharmony_ci *
17262306a36Sopenharmony_ci *  (5) When the cache is taken offline or if the cookie is culled, the flag is
17362306a36Sopenharmony_ci *      cleared to prevent new accesses, the cookie's n_accesses is decremented
17462306a36Sopenharmony_ci *      and we wait for it to become 0.
17562306a36Sopenharmony_ci *
17662306a36Sopenharmony_ci * The @why indicator are merely provided for tracing purposes.
17762306a36Sopenharmony_ci */
17862306a36Sopenharmony_cibool fscache_begin_cookie_access(struct fscache_cookie *cookie,
17962306a36Sopenharmony_ci				 enum fscache_access_trace why)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	if (!test_bit(FSCACHE_COOKIE_IS_CACHING, &cookie->flags))
18262306a36Sopenharmony_ci		return false;
18362306a36Sopenharmony_ci	__fscache_begin_cookie_access(cookie, why);
18462306a36Sopenharmony_ci	if (!test_bit(FSCACHE_COOKIE_IS_CACHING, &cookie->flags) ||
18562306a36Sopenharmony_ci	    !fscache_cache_is_live(cookie->volume->cache)) {
18662306a36Sopenharmony_ci		fscache_end_cookie_access(cookie, fscache_access_unlive);
18762306a36Sopenharmony_ci		return false;
18862306a36Sopenharmony_ci	}
18962306a36Sopenharmony_ci	return true;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic inline void wake_up_cookie_state(struct fscache_cookie *cookie)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	/* Use a barrier to ensure that waiters see the state variable
19562306a36Sopenharmony_ci	 * change, as spin_unlock doesn't guarantee a barrier.
19662306a36Sopenharmony_ci	 *
19762306a36Sopenharmony_ci	 * See comments over wake_up_bit() and waitqueue_active().
19862306a36Sopenharmony_ci	 */
19962306a36Sopenharmony_ci	smp_mb();
20062306a36Sopenharmony_ci	wake_up_var(&cookie->state);
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci/*
20462306a36Sopenharmony_ci * Change the state a cookie is at and wake up anyone waiting for that.  Impose
20562306a36Sopenharmony_ci * an ordering between the stuff stored in the cookie and the state member.
20662306a36Sopenharmony_ci * Paired with fscache_cookie_state().
20762306a36Sopenharmony_ci */
20862306a36Sopenharmony_cistatic void __fscache_set_cookie_state(struct fscache_cookie *cookie,
20962306a36Sopenharmony_ci				       enum fscache_cookie_state state)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	smp_store_release(&cookie->state, state);
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic void fscache_set_cookie_state(struct fscache_cookie *cookie,
21562306a36Sopenharmony_ci				     enum fscache_cookie_state state)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	spin_lock(&cookie->lock);
21862306a36Sopenharmony_ci	__fscache_set_cookie_state(cookie, state);
21962306a36Sopenharmony_ci	spin_unlock(&cookie->lock);
22062306a36Sopenharmony_ci	wake_up_cookie_state(cookie);
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci/**
22462306a36Sopenharmony_ci * fscache_cookie_lookup_negative - Note negative lookup
22562306a36Sopenharmony_ci * @cookie: The cookie that was being looked up
22662306a36Sopenharmony_ci *
22762306a36Sopenharmony_ci * Note that some part of the metadata path in the cache doesn't exist and so
22862306a36Sopenharmony_ci * we can release any waiting readers in the certain knowledge that there's
22962306a36Sopenharmony_ci * nothing for them to actually read.
23062306a36Sopenharmony_ci *
23162306a36Sopenharmony_ci * This function uses no locking and must only be called from the state machine.
23262306a36Sopenharmony_ci */
23362306a36Sopenharmony_civoid fscache_cookie_lookup_negative(struct fscache_cookie *cookie)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	set_bit(FSCACHE_COOKIE_NO_DATA_TO_READ, &cookie->flags);
23662306a36Sopenharmony_ci	fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_CREATING);
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ciEXPORT_SYMBOL(fscache_cookie_lookup_negative);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci/**
24162306a36Sopenharmony_ci * fscache_resume_after_invalidation - Allow I/O to resume after invalidation
24262306a36Sopenharmony_ci * @cookie: The cookie that was invalidated
24362306a36Sopenharmony_ci *
24462306a36Sopenharmony_ci * Tell fscache that invalidation is sufficiently complete that I/O can be
24562306a36Sopenharmony_ci * allowed again.
24662306a36Sopenharmony_ci */
24762306a36Sopenharmony_civoid fscache_resume_after_invalidation(struct fscache_cookie *cookie)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_ACTIVE);
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ciEXPORT_SYMBOL(fscache_resume_after_invalidation);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci/**
25462306a36Sopenharmony_ci * fscache_caching_failed - Report that a failure stopped caching on a cookie
25562306a36Sopenharmony_ci * @cookie: The cookie that was affected
25662306a36Sopenharmony_ci *
25762306a36Sopenharmony_ci * Tell fscache that caching on a cookie needs to be stopped due to some sort
25862306a36Sopenharmony_ci * of failure.
25962306a36Sopenharmony_ci *
26062306a36Sopenharmony_ci * This function uses no locking and must only be called from the state machine.
26162306a36Sopenharmony_ci */
26262306a36Sopenharmony_civoid fscache_caching_failed(struct fscache_cookie *cookie)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	clear_bit(FSCACHE_COOKIE_IS_CACHING, &cookie->flags);
26562306a36Sopenharmony_ci	fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_FAILED);
26662306a36Sopenharmony_ci	trace_fscache_cookie(cookie->debug_id, refcount_read(&cookie->ref),
26762306a36Sopenharmony_ci				fscache_cookie_failed);
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ciEXPORT_SYMBOL(fscache_caching_failed);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci/*
27262306a36Sopenharmony_ci * Set the index key in a cookie.  The cookie struct has space for a 16-byte
27362306a36Sopenharmony_ci * key plus length and hash, but if that's not big enough, it's instead a
27462306a36Sopenharmony_ci * pointer to a buffer containing 3 bytes of hash, 1 byte of length and then
27562306a36Sopenharmony_ci * the key data.
27662306a36Sopenharmony_ci */
27762306a36Sopenharmony_cistatic int fscache_set_key(struct fscache_cookie *cookie,
27862306a36Sopenharmony_ci			   const void *index_key, size_t index_key_len)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	void *buf;
28162306a36Sopenharmony_ci	size_t buf_size;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	buf_size = round_up(index_key_len, sizeof(__le32));
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	if (index_key_len > sizeof(cookie->inline_key)) {
28662306a36Sopenharmony_ci		buf = kzalloc(buf_size, GFP_KERNEL);
28762306a36Sopenharmony_ci		if (!buf)
28862306a36Sopenharmony_ci			return -ENOMEM;
28962306a36Sopenharmony_ci		cookie->key = buf;
29062306a36Sopenharmony_ci	} else {
29162306a36Sopenharmony_ci		buf = cookie->inline_key;
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	memcpy(buf, index_key, index_key_len);
29562306a36Sopenharmony_ci	cookie->key_hash = fscache_hash(cookie->volume->key_hash,
29662306a36Sopenharmony_ci					buf, buf_size);
29762306a36Sopenharmony_ci	return 0;
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic bool fscache_cookie_same(const struct fscache_cookie *a,
30162306a36Sopenharmony_ci				const struct fscache_cookie *b)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	const void *ka, *kb;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	if (a->key_hash	!= b->key_hash ||
30662306a36Sopenharmony_ci	    a->volume	!= b->volume ||
30762306a36Sopenharmony_ci	    a->key_len	!= b->key_len)
30862306a36Sopenharmony_ci		return false;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	if (a->key_len <= sizeof(a->inline_key)) {
31162306a36Sopenharmony_ci		ka = &a->inline_key;
31262306a36Sopenharmony_ci		kb = &b->inline_key;
31362306a36Sopenharmony_ci	} else {
31462306a36Sopenharmony_ci		ka = a->key;
31562306a36Sopenharmony_ci		kb = b->key;
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci	return memcmp(ka, kb, a->key_len) == 0;
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic atomic_t fscache_cookie_debug_id = ATOMIC_INIT(1);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci/*
32362306a36Sopenharmony_ci * Allocate a cookie.
32462306a36Sopenharmony_ci */
32562306a36Sopenharmony_cistatic struct fscache_cookie *fscache_alloc_cookie(
32662306a36Sopenharmony_ci	struct fscache_volume *volume,
32762306a36Sopenharmony_ci	u8 advice,
32862306a36Sopenharmony_ci	const void *index_key, size_t index_key_len,
32962306a36Sopenharmony_ci	const void *aux_data, size_t aux_data_len,
33062306a36Sopenharmony_ci	loff_t object_size)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	struct fscache_cookie *cookie;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	/* allocate and initialise a cookie */
33562306a36Sopenharmony_ci	cookie = kmem_cache_zalloc(fscache_cookie_jar, GFP_KERNEL);
33662306a36Sopenharmony_ci	if (!cookie)
33762306a36Sopenharmony_ci		return NULL;
33862306a36Sopenharmony_ci	fscache_stat(&fscache_n_cookies);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	cookie->volume		= volume;
34162306a36Sopenharmony_ci	cookie->advice		= advice;
34262306a36Sopenharmony_ci	cookie->key_len		= index_key_len;
34362306a36Sopenharmony_ci	cookie->aux_len		= aux_data_len;
34462306a36Sopenharmony_ci	cookie->object_size	= object_size;
34562306a36Sopenharmony_ci	if (object_size == 0)
34662306a36Sopenharmony_ci		__set_bit(FSCACHE_COOKIE_NO_DATA_TO_READ, &cookie->flags);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	if (fscache_set_key(cookie, index_key, index_key_len) < 0)
34962306a36Sopenharmony_ci		goto nomem;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	if (cookie->aux_len <= sizeof(cookie->inline_aux)) {
35262306a36Sopenharmony_ci		memcpy(cookie->inline_aux, aux_data, cookie->aux_len);
35362306a36Sopenharmony_ci	} else {
35462306a36Sopenharmony_ci		cookie->aux = kmemdup(aux_data, cookie->aux_len, GFP_KERNEL);
35562306a36Sopenharmony_ci		if (!cookie->aux)
35662306a36Sopenharmony_ci			goto nomem;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	refcount_set(&cookie->ref, 1);
36062306a36Sopenharmony_ci	cookie->debug_id = atomic_inc_return(&fscache_cookie_debug_id);
36162306a36Sopenharmony_ci	spin_lock_init(&cookie->lock);
36262306a36Sopenharmony_ci	INIT_LIST_HEAD(&cookie->commit_link);
36362306a36Sopenharmony_ci	INIT_WORK(&cookie->work, fscache_cookie_worker);
36462306a36Sopenharmony_ci	__fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_QUIESCENT);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	write_lock(&fscache_cookies_lock);
36762306a36Sopenharmony_ci	list_add_tail(&cookie->proc_link, &fscache_cookies);
36862306a36Sopenharmony_ci	write_unlock(&fscache_cookies_lock);
36962306a36Sopenharmony_ci	fscache_see_cookie(cookie, fscache_cookie_new_acquire);
37062306a36Sopenharmony_ci	return cookie;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cinomem:
37362306a36Sopenharmony_ci	fscache_free_cookie(cookie);
37462306a36Sopenharmony_ci	return NULL;
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic inline bool fscache_cookie_is_dropped(struct fscache_cookie *cookie)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	return READ_ONCE(cookie->state) == FSCACHE_COOKIE_STATE_DROPPED;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic void fscache_wait_on_collision(struct fscache_cookie *candidate,
38362306a36Sopenharmony_ci				      struct fscache_cookie *wait_for)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	enum fscache_cookie_state *statep = &wait_for->state;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	wait_var_event_timeout(statep, fscache_cookie_is_dropped(wait_for),
38862306a36Sopenharmony_ci			       20 * HZ);
38962306a36Sopenharmony_ci	if (!fscache_cookie_is_dropped(wait_for)) {
39062306a36Sopenharmony_ci		pr_notice("Potential collision c=%08x old: c=%08x",
39162306a36Sopenharmony_ci			  candidate->debug_id, wait_for->debug_id);
39262306a36Sopenharmony_ci		wait_var_event(statep, fscache_cookie_is_dropped(wait_for));
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci/*
39762306a36Sopenharmony_ci * Attempt to insert the new cookie into the hash.  If there's a collision, we
39862306a36Sopenharmony_ci * wait for the old cookie to complete if it's being relinquished and an error
39962306a36Sopenharmony_ci * otherwise.
40062306a36Sopenharmony_ci */
40162306a36Sopenharmony_cistatic bool fscache_hash_cookie(struct fscache_cookie *candidate)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	struct fscache_cookie *cursor, *wait_for = NULL;
40462306a36Sopenharmony_ci	struct hlist_bl_head *h;
40562306a36Sopenharmony_ci	struct hlist_bl_node *p;
40662306a36Sopenharmony_ci	unsigned int bucket;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	bucket = candidate->key_hash & (ARRAY_SIZE(fscache_cookie_hash) - 1);
40962306a36Sopenharmony_ci	h = &fscache_cookie_hash[bucket];
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	hlist_bl_lock(h);
41262306a36Sopenharmony_ci	hlist_bl_for_each_entry(cursor, p, h, hash_link) {
41362306a36Sopenharmony_ci		if (fscache_cookie_same(candidate, cursor)) {
41462306a36Sopenharmony_ci			if (!test_bit(FSCACHE_COOKIE_RELINQUISHED, &cursor->flags))
41562306a36Sopenharmony_ci				goto collision;
41662306a36Sopenharmony_ci			wait_for = fscache_get_cookie(cursor,
41762306a36Sopenharmony_ci						      fscache_cookie_get_hash_collision);
41862306a36Sopenharmony_ci			break;
41962306a36Sopenharmony_ci		}
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	fscache_get_volume(candidate->volume, fscache_volume_get_cookie);
42362306a36Sopenharmony_ci	atomic_inc(&candidate->volume->n_cookies);
42462306a36Sopenharmony_ci	hlist_bl_add_head(&candidate->hash_link, h);
42562306a36Sopenharmony_ci	set_bit(FSCACHE_COOKIE_IS_HASHED, &candidate->flags);
42662306a36Sopenharmony_ci	hlist_bl_unlock(h);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	if (wait_for) {
42962306a36Sopenharmony_ci		fscache_wait_on_collision(candidate, wait_for);
43062306a36Sopenharmony_ci		fscache_put_cookie(wait_for, fscache_cookie_put_hash_collision);
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci	return true;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cicollision:
43562306a36Sopenharmony_ci	trace_fscache_cookie(cursor->debug_id, refcount_read(&cursor->ref),
43662306a36Sopenharmony_ci			     fscache_cookie_collision);
43762306a36Sopenharmony_ci	pr_err("Duplicate cookie detected\n");
43862306a36Sopenharmony_ci	fscache_print_cookie(cursor, 'O');
43962306a36Sopenharmony_ci	fscache_print_cookie(candidate, 'N');
44062306a36Sopenharmony_ci	hlist_bl_unlock(h);
44162306a36Sopenharmony_ci	return false;
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci/*
44562306a36Sopenharmony_ci * Request a cookie to represent a data storage object within a volume.
44662306a36Sopenharmony_ci *
44762306a36Sopenharmony_ci * We never let on to the netfs about errors.  We may set a negative cookie
44862306a36Sopenharmony_ci * pointer, but that's okay
44962306a36Sopenharmony_ci */
45062306a36Sopenharmony_cistruct fscache_cookie *__fscache_acquire_cookie(
45162306a36Sopenharmony_ci	struct fscache_volume *volume,
45262306a36Sopenharmony_ci	u8 advice,
45362306a36Sopenharmony_ci	const void *index_key, size_t index_key_len,
45462306a36Sopenharmony_ci	const void *aux_data, size_t aux_data_len,
45562306a36Sopenharmony_ci	loff_t object_size)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	struct fscache_cookie *cookie;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	_enter("V=%x", volume->debug_id);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	if (!index_key || !index_key_len || index_key_len > 255 || aux_data_len > 255)
46262306a36Sopenharmony_ci		return NULL;
46362306a36Sopenharmony_ci	if (!aux_data || !aux_data_len) {
46462306a36Sopenharmony_ci		aux_data = NULL;
46562306a36Sopenharmony_ci		aux_data_len = 0;
46662306a36Sopenharmony_ci	}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	fscache_stat(&fscache_n_acquires);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	cookie = fscache_alloc_cookie(volume, advice,
47162306a36Sopenharmony_ci				      index_key, index_key_len,
47262306a36Sopenharmony_ci				      aux_data, aux_data_len,
47362306a36Sopenharmony_ci				      object_size);
47462306a36Sopenharmony_ci	if (!cookie) {
47562306a36Sopenharmony_ci		fscache_stat(&fscache_n_acquires_oom);
47662306a36Sopenharmony_ci		return NULL;
47762306a36Sopenharmony_ci	}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	if (!fscache_hash_cookie(cookie)) {
48062306a36Sopenharmony_ci		fscache_see_cookie(cookie, fscache_cookie_discard);
48162306a36Sopenharmony_ci		fscache_free_cookie(cookie);
48262306a36Sopenharmony_ci		return NULL;
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	trace_fscache_acquire(cookie);
48662306a36Sopenharmony_ci	fscache_stat(&fscache_n_acquires_ok);
48762306a36Sopenharmony_ci	_leave(" = c=%08x", cookie->debug_id);
48862306a36Sopenharmony_ci	return cookie;
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ciEXPORT_SYMBOL(__fscache_acquire_cookie);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci/*
49362306a36Sopenharmony_ci * Prepare a cache object to be written to.
49462306a36Sopenharmony_ci */
49562306a36Sopenharmony_cistatic void fscache_prepare_to_write(struct fscache_cookie *cookie)
49662306a36Sopenharmony_ci{
49762306a36Sopenharmony_ci	cookie->volume->cache->ops->prepare_to_write(cookie);
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci/*
50162306a36Sopenharmony_ci * Look up a cookie in the cache.
50262306a36Sopenharmony_ci */
50362306a36Sopenharmony_cistatic void fscache_perform_lookup(struct fscache_cookie *cookie)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci	enum fscache_access_trace trace = fscache_access_lookup_cookie_end_failed;
50662306a36Sopenharmony_ci	bool need_withdraw = false;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	_enter("");
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	if (!cookie->volume->cache_priv) {
51162306a36Sopenharmony_ci		fscache_create_volume(cookie->volume, true);
51262306a36Sopenharmony_ci		if (!cookie->volume->cache_priv) {
51362306a36Sopenharmony_ci			fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_QUIESCENT);
51462306a36Sopenharmony_ci			goto out;
51562306a36Sopenharmony_ci		}
51662306a36Sopenharmony_ci	}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	if (!cookie->volume->cache->ops->lookup_cookie(cookie)) {
51962306a36Sopenharmony_ci		if (cookie->state != FSCACHE_COOKIE_STATE_FAILED)
52062306a36Sopenharmony_ci			fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_QUIESCENT);
52162306a36Sopenharmony_ci		need_withdraw = true;
52262306a36Sopenharmony_ci		_leave(" [fail]");
52362306a36Sopenharmony_ci		goto out;
52462306a36Sopenharmony_ci	}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	fscache_see_cookie(cookie, fscache_cookie_see_active);
52762306a36Sopenharmony_ci	spin_lock(&cookie->lock);
52862306a36Sopenharmony_ci	if (test_and_clear_bit(FSCACHE_COOKIE_DO_INVALIDATE, &cookie->flags))
52962306a36Sopenharmony_ci		__fscache_set_cookie_state(cookie,
53062306a36Sopenharmony_ci					   FSCACHE_COOKIE_STATE_INVALIDATING);
53162306a36Sopenharmony_ci	else
53262306a36Sopenharmony_ci		__fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_ACTIVE);
53362306a36Sopenharmony_ci	spin_unlock(&cookie->lock);
53462306a36Sopenharmony_ci	wake_up_cookie_state(cookie);
53562306a36Sopenharmony_ci	trace = fscache_access_lookup_cookie_end;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ciout:
53862306a36Sopenharmony_ci	fscache_end_cookie_access(cookie, trace);
53962306a36Sopenharmony_ci	if (need_withdraw)
54062306a36Sopenharmony_ci		fscache_withdraw_cookie(cookie);
54162306a36Sopenharmony_ci	fscache_end_volume_access(cookie->volume, cookie, trace);
54262306a36Sopenharmony_ci}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci/*
54562306a36Sopenharmony_ci * Begin the process of looking up a cookie.  We offload the actual process to
54662306a36Sopenharmony_ci * a worker thread.
54762306a36Sopenharmony_ci */
54862306a36Sopenharmony_cistatic bool fscache_begin_lookup(struct fscache_cookie *cookie, bool will_modify)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci	if (will_modify) {
55162306a36Sopenharmony_ci		set_bit(FSCACHE_COOKIE_LOCAL_WRITE, &cookie->flags);
55262306a36Sopenharmony_ci		set_bit(FSCACHE_COOKIE_DO_PREP_TO_WRITE, &cookie->flags);
55362306a36Sopenharmony_ci	}
55462306a36Sopenharmony_ci	if (!fscache_begin_volume_access(cookie->volume, cookie,
55562306a36Sopenharmony_ci					 fscache_access_lookup_cookie))
55662306a36Sopenharmony_ci		return false;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	__fscache_begin_cookie_access(cookie, fscache_access_lookup_cookie);
55962306a36Sopenharmony_ci	__fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_LOOKING_UP);
56062306a36Sopenharmony_ci	set_bit(FSCACHE_COOKIE_IS_CACHING, &cookie->flags);
56162306a36Sopenharmony_ci	set_bit(FSCACHE_COOKIE_HAS_BEEN_CACHED, &cookie->flags);
56262306a36Sopenharmony_ci	return true;
56362306a36Sopenharmony_ci}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci/*
56662306a36Sopenharmony_ci * Start using the cookie for I/O.  This prevents the backing object from being
56762306a36Sopenharmony_ci * reaped by VM pressure.
56862306a36Sopenharmony_ci */
56962306a36Sopenharmony_civoid __fscache_use_cookie(struct fscache_cookie *cookie, bool will_modify)
57062306a36Sopenharmony_ci{
57162306a36Sopenharmony_ci	enum fscache_cookie_state state;
57262306a36Sopenharmony_ci	bool queue = false;
57362306a36Sopenharmony_ci	int n_active;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	_enter("c=%08x", cookie->debug_id);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	if (WARN(test_bit(FSCACHE_COOKIE_RELINQUISHED, &cookie->flags),
57862306a36Sopenharmony_ci		 "Trying to use relinquished cookie\n"))
57962306a36Sopenharmony_ci		return;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	spin_lock(&cookie->lock);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	n_active = atomic_inc_return(&cookie->n_active);
58462306a36Sopenharmony_ci	trace_fscache_active(cookie->debug_id, refcount_read(&cookie->ref),
58562306a36Sopenharmony_ci			     n_active, atomic_read(&cookie->n_accesses),
58662306a36Sopenharmony_ci			     will_modify ?
58762306a36Sopenharmony_ci			     fscache_active_use_modify : fscache_active_use);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ciagain:
59062306a36Sopenharmony_ci	state = fscache_cookie_state(cookie);
59162306a36Sopenharmony_ci	switch (state) {
59262306a36Sopenharmony_ci	case FSCACHE_COOKIE_STATE_QUIESCENT:
59362306a36Sopenharmony_ci		queue = fscache_begin_lookup(cookie, will_modify);
59462306a36Sopenharmony_ci		break;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	case FSCACHE_COOKIE_STATE_LOOKING_UP:
59762306a36Sopenharmony_ci	case FSCACHE_COOKIE_STATE_CREATING:
59862306a36Sopenharmony_ci		if (will_modify)
59962306a36Sopenharmony_ci			set_bit(FSCACHE_COOKIE_LOCAL_WRITE, &cookie->flags);
60062306a36Sopenharmony_ci		break;
60162306a36Sopenharmony_ci	case FSCACHE_COOKIE_STATE_ACTIVE:
60262306a36Sopenharmony_ci	case FSCACHE_COOKIE_STATE_INVALIDATING:
60362306a36Sopenharmony_ci		if (will_modify &&
60462306a36Sopenharmony_ci		    !test_and_set_bit(FSCACHE_COOKIE_LOCAL_WRITE, &cookie->flags)) {
60562306a36Sopenharmony_ci			set_bit(FSCACHE_COOKIE_DO_PREP_TO_WRITE, &cookie->flags);
60662306a36Sopenharmony_ci			queue = true;
60762306a36Sopenharmony_ci		}
60862306a36Sopenharmony_ci		/*
60962306a36Sopenharmony_ci		 * We could race with cookie_lru which may set LRU_DISCARD bit
61062306a36Sopenharmony_ci		 * but has yet to run the cookie state machine.  If this happens
61162306a36Sopenharmony_ci		 * and another thread tries to use the cookie, clear LRU_DISCARD
61262306a36Sopenharmony_ci		 * so we don't end up withdrawing the cookie while in use.
61362306a36Sopenharmony_ci		 */
61462306a36Sopenharmony_ci		if (test_and_clear_bit(FSCACHE_COOKIE_DO_LRU_DISCARD, &cookie->flags))
61562306a36Sopenharmony_ci			fscache_see_cookie(cookie, fscache_cookie_see_lru_discard_clear);
61662306a36Sopenharmony_ci		break;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	case FSCACHE_COOKIE_STATE_FAILED:
61962306a36Sopenharmony_ci	case FSCACHE_COOKIE_STATE_WITHDRAWING:
62062306a36Sopenharmony_ci		break;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	case FSCACHE_COOKIE_STATE_LRU_DISCARDING:
62362306a36Sopenharmony_ci		spin_unlock(&cookie->lock);
62462306a36Sopenharmony_ci		wait_var_event(&cookie->state,
62562306a36Sopenharmony_ci			       fscache_cookie_state(cookie) !=
62662306a36Sopenharmony_ci			       FSCACHE_COOKIE_STATE_LRU_DISCARDING);
62762306a36Sopenharmony_ci		spin_lock(&cookie->lock);
62862306a36Sopenharmony_ci		goto again;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	case FSCACHE_COOKIE_STATE_DROPPED:
63162306a36Sopenharmony_ci	case FSCACHE_COOKIE_STATE_RELINQUISHING:
63262306a36Sopenharmony_ci		WARN(1, "Can't use cookie in state %u\n", state);
63362306a36Sopenharmony_ci		break;
63462306a36Sopenharmony_ci	}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	spin_unlock(&cookie->lock);
63762306a36Sopenharmony_ci	if (queue)
63862306a36Sopenharmony_ci		fscache_queue_cookie(cookie, fscache_cookie_get_use_work);
63962306a36Sopenharmony_ci	_leave("");
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ciEXPORT_SYMBOL(__fscache_use_cookie);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_cistatic void fscache_unuse_cookie_locked(struct fscache_cookie *cookie)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	clear_bit(FSCACHE_COOKIE_DISABLED, &cookie->flags);
64662306a36Sopenharmony_ci	if (!test_bit(FSCACHE_COOKIE_IS_CACHING, &cookie->flags))
64762306a36Sopenharmony_ci		return;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	cookie->unused_at = jiffies;
65062306a36Sopenharmony_ci	spin_lock(&fscache_cookie_lru_lock);
65162306a36Sopenharmony_ci	if (list_empty(&cookie->commit_link)) {
65262306a36Sopenharmony_ci		fscache_get_cookie(cookie, fscache_cookie_get_lru);
65362306a36Sopenharmony_ci		fscache_stat(&fscache_n_cookies_lru);
65462306a36Sopenharmony_ci	}
65562306a36Sopenharmony_ci	list_move_tail(&cookie->commit_link, &fscache_cookie_lru);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	spin_unlock(&fscache_cookie_lru_lock);
65862306a36Sopenharmony_ci	timer_reduce(&fscache_cookie_lru_timer,
65962306a36Sopenharmony_ci		     jiffies + fscache_lru_cookie_timeout);
66062306a36Sopenharmony_ci}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci/*
66362306a36Sopenharmony_ci * Stop using the cookie for I/O.
66462306a36Sopenharmony_ci */
66562306a36Sopenharmony_civoid __fscache_unuse_cookie(struct fscache_cookie *cookie,
66662306a36Sopenharmony_ci			    const void *aux_data, const loff_t *object_size)
66762306a36Sopenharmony_ci{
66862306a36Sopenharmony_ci	unsigned int debug_id = cookie->debug_id;
66962306a36Sopenharmony_ci	unsigned int r = refcount_read(&cookie->ref);
67062306a36Sopenharmony_ci	unsigned int a = atomic_read(&cookie->n_accesses);
67162306a36Sopenharmony_ci	unsigned int c;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	if (aux_data || object_size)
67462306a36Sopenharmony_ci		__fscache_update_cookie(cookie, aux_data, object_size);
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	/* Subtract 1 from counter unless that drops it to 0 (ie. it was 1) */
67762306a36Sopenharmony_ci	c = atomic_fetch_add_unless(&cookie->n_active, -1, 1);
67862306a36Sopenharmony_ci	if (c != 1) {
67962306a36Sopenharmony_ci		trace_fscache_active(debug_id, r, c - 1, a, fscache_active_unuse);
68062306a36Sopenharmony_ci		return;
68162306a36Sopenharmony_ci	}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	spin_lock(&cookie->lock);
68462306a36Sopenharmony_ci	r = refcount_read(&cookie->ref);
68562306a36Sopenharmony_ci	a = atomic_read(&cookie->n_accesses);
68662306a36Sopenharmony_ci	c = atomic_dec_return(&cookie->n_active);
68762306a36Sopenharmony_ci	trace_fscache_active(debug_id, r, c, a, fscache_active_unuse);
68862306a36Sopenharmony_ci	if (c == 0)
68962306a36Sopenharmony_ci		fscache_unuse_cookie_locked(cookie);
69062306a36Sopenharmony_ci	spin_unlock(&cookie->lock);
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ciEXPORT_SYMBOL(__fscache_unuse_cookie);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci/*
69562306a36Sopenharmony_ci * Perform work upon the cookie, such as committing its cache state,
69662306a36Sopenharmony_ci * relinquishing it or withdrawing the backing cache.  We're protected from the
69762306a36Sopenharmony_ci * cache going away under us as object withdrawal must come through this
69862306a36Sopenharmony_ci * non-reentrant work item.
69962306a36Sopenharmony_ci */
70062306a36Sopenharmony_cistatic void fscache_cookie_state_machine(struct fscache_cookie *cookie)
70162306a36Sopenharmony_ci{
70262306a36Sopenharmony_ci	enum fscache_cookie_state state;
70362306a36Sopenharmony_ci	bool wake = false;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	_enter("c=%x", cookie->debug_id);
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ciagain:
70862306a36Sopenharmony_ci	spin_lock(&cookie->lock);
70962306a36Sopenharmony_ciagain_locked:
71062306a36Sopenharmony_ci	state = cookie->state;
71162306a36Sopenharmony_ci	switch (state) {
71262306a36Sopenharmony_ci	case FSCACHE_COOKIE_STATE_QUIESCENT:
71362306a36Sopenharmony_ci		/* The QUIESCENT state is jumped to the LOOKING_UP state by
71462306a36Sopenharmony_ci		 * fscache_use_cookie().
71562306a36Sopenharmony_ci		 */
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci		if (atomic_read(&cookie->n_accesses) == 0 &&
71862306a36Sopenharmony_ci		    test_bit(FSCACHE_COOKIE_DO_RELINQUISH, &cookie->flags)) {
71962306a36Sopenharmony_ci			__fscache_set_cookie_state(cookie,
72062306a36Sopenharmony_ci						   FSCACHE_COOKIE_STATE_RELINQUISHING);
72162306a36Sopenharmony_ci			wake = true;
72262306a36Sopenharmony_ci			goto again_locked;
72362306a36Sopenharmony_ci		}
72462306a36Sopenharmony_ci		break;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	case FSCACHE_COOKIE_STATE_LOOKING_UP:
72762306a36Sopenharmony_ci		spin_unlock(&cookie->lock);
72862306a36Sopenharmony_ci		fscache_init_access_gate(cookie);
72962306a36Sopenharmony_ci		fscache_perform_lookup(cookie);
73062306a36Sopenharmony_ci		goto again;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	case FSCACHE_COOKIE_STATE_INVALIDATING:
73362306a36Sopenharmony_ci		spin_unlock(&cookie->lock);
73462306a36Sopenharmony_ci		fscache_perform_invalidation(cookie);
73562306a36Sopenharmony_ci		goto again;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	case FSCACHE_COOKIE_STATE_ACTIVE:
73862306a36Sopenharmony_ci		if (test_and_clear_bit(FSCACHE_COOKIE_DO_PREP_TO_WRITE, &cookie->flags)) {
73962306a36Sopenharmony_ci			spin_unlock(&cookie->lock);
74062306a36Sopenharmony_ci			fscache_prepare_to_write(cookie);
74162306a36Sopenharmony_ci			spin_lock(&cookie->lock);
74262306a36Sopenharmony_ci		}
74362306a36Sopenharmony_ci		if (test_bit(FSCACHE_COOKIE_DO_LRU_DISCARD, &cookie->flags)) {
74462306a36Sopenharmony_ci			__fscache_set_cookie_state(cookie,
74562306a36Sopenharmony_ci						   FSCACHE_COOKIE_STATE_LRU_DISCARDING);
74662306a36Sopenharmony_ci			wake = true;
74762306a36Sopenharmony_ci			goto again_locked;
74862306a36Sopenharmony_ci		}
74962306a36Sopenharmony_ci		fallthrough;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	case FSCACHE_COOKIE_STATE_FAILED:
75262306a36Sopenharmony_ci		if (test_and_clear_bit(FSCACHE_COOKIE_DO_INVALIDATE, &cookie->flags))
75362306a36Sopenharmony_ci			fscache_end_cookie_access(cookie, fscache_access_invalidate_cookie_end);
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci		if (atomic_read(&cookie->n_accesses) != 0)
75662306a36Sopenharmony_ci			break;
75762306a36Sopenharmony_ci		if (test_bit(FSCACHE_COOKIE_DO_RELINQUISH, &cookie->flags)) {
75862306a36Sopenharmony_ci			__fscache_set_cookie_state(cookie,
75962306a36Sopenharmony_ci						   FSCACHE_COOKIE_STATE_RELINQUISHING);
76062306a36Sopenharmony_ci			wake = true;
76162306a36Sopenharmony_ci			goto again_locked;
76262306a36Sopenharmony_ci		}
76362306a36Sopenharmony_ci		if (test_bit(FSCACHE_COOKIE_DO_WITHDRAW, &cookie->flags)) {
76462306a36Sopenharmony_ci			__fscache_set_cookie_state(cookie,
76562306a36Sopenharmony_ci						   FSCACHE_COOKIE_STATE_WITHDRAWING);
76662306a36Sopenharmony_ci			wake = true;
76762306a36Sopenharmony_ci			goto again_locked;
76862306a36Sopenharmony_ci		}
76962306a36Sopenharmony_ci		break;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	case FSCACHE_COOKIE_STATE_LRU_DISCARDING:
77262306a36Sopenharmony_ci	case FSCACHE_COOKIE_STATE_RELINQUISHING:
77362306a36Sopenharmony_ci	case FSCACHE_COOKIE_STATE_WITHDRAWING:
77462306a36Sopenharmony_ci		if (cookie->cache_priv) {
77562306a36Sopenharmony_ci			spin_unlock(&cookie->lock);
77662306a36Sopenharmony_ci			cookie->volume->cache->ops->withdraw_cookie(cookie);
77762306a36Sopenharmony_ci			spin_lock(&cookie->lock);
77862306a36Sopenharmony_ci		}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci		if (test_and_clear_bit(FSCACHE_COOKIE_DO_INVALIDATE, &cookie->flags))
78162306a36Sopenharmony_ci			fscache_end_cookie_access(cookie, fscache_access_invalidate_cookie_end);
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci		switch (state) {
78462306a36Sopenharmony_ci		case FSCACHE_COOKIE_STATE_RELINQUISHING:
78562306a36Sopenharmony_ci			fscache_see_cookie(cookie, fscache_cookie_see_relinquish);
78662306a36Sopenharmony_ci			fscache_unhash_cookie(cookie);
78762306a36Sopenharmony_ci			__fscache_set_cookie_state(cookie,
78862306a36Sopenharmony_ci						   FSCACHE_COOKIE_STATE_DROPPED);
78962306a36Sopenharmony_ci			wake = true;
79062306a36Sopenharmony_ci			goto out;
79162306a36Sopenharmony_ci		case FSCACHE_COOKIE_STATE_LRU_DISCARDING:
79262306a36Sopenharmony_ci			fscache_see_cookie(cookie, fscache_cookie_see_lru_discard);
79362306a36Sopenharmony_ci			break;
79462306a36Sopenharmony_ci		case FSCACHE_COOKIE_STATE_WITHDRAWING:
79562306a36Sopenharmony_ci			fscache_see_cookie(cookie, fscache_cookie_see_withdraw);
79662306a36Sopenharmony_ci			break;
79762306a36Sopenharmony_ci		default:
79862306a36Sopenharmony_ci			BUG();
79962306a36Sopenharmony_ci		}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci		clear_bit(FSCACHE_COOKIE_NEEDS_UPDATE, &cookie->flags);
80262306a36Sopenharmony_ci		clear_bit(FSCACHE_COOKIE_DO_WITHDRAW, &cookie->flags);
80362306a36Sopenharmony_ci		clear_bit(FSCACHE_COOKIE_DO_LRU_DISCARD, &cookie->flags);
80462306a36Sopenharmony_ci		clear_bit(FSCACHE_COOKIE_DO_PREP_TO_WRITE, &cookie->flags);
80562306a36Sopenharmony_ci		set_bit(FSCACHE_COOKIE_NO_DATA_TO_READ, &cookie->flags);
80662306a36Sopenharmony_ci		__fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_QUIESCENT);
80762306a36Sopenharmony_ci		wake = true;
80862306a36Sopenharmony_ci		goto again_locked;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	case FSCACHE_COOKIE_STATE_DROPPED:
81162306a36Sopenharmony_ci		break;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	default:
81462306a36Sopenharmony_ci		WARN_ONCE(1, "Cookie %x in unexpected state %u\n",
81562306a36Sopenharmony_ci			  cookie->debug_id, state);
81662306a36Sopenharmony_ci		break;
81762306a36Sopenharmony_ci	}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ciout:
82062306a36Sopenharmony_ci	spin_unlock(&cookie->lock);
82162306a36Sopenharmony_ci	if (wake)
82262306a36Sopenharmony_ci		wake_up_cookie_state(cookie);
82362306a36Sopenharmony_ci	_leave("");
82462306a36Sopenharmony_ci}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_cistatic void fscache_cookie_worker(struct work_struct *work)
82762306a36Sopenharmony_ci{
82862306a36Sopenharmony_ci	struct fscache_cookie *cookie = container_of(work, struct fscache_cookie, work);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	fscache_see_cookie(cookie, fscache_cookie_see_work);
83162306a36Sopenharmony_ci	fscache_cookie_state_machine(cookie);
83262306a36Sopenharmony_ci	fscache_put_cookie(cookie, fscache_cookie_put_work);
83362306a36Sopenharmony_ci}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci/*
83662306a36Sopenharmony_ci * Wait for the object to become inactive.  The cookie's work item will be
83762306a36Sopenharmony_ci * scheduled when someone transitions n_accesses to 0 - but if someone's
83862306a36Sopenharmony_ci * already done that, schedule it anyway.
83962306a36Sopenharmony_ci */
84062306a36Sopenharmony_cistatic void __fscache_withdraw_cookie(struct fscache_cookie *cookie)
84162306a36Sopenharmony_ci{
84262306a36Sopenharmony_ci	int n_accesses;
84362306a36Sopenharmony_ci	bool unpinned;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	unpinned = test_and_clear_bit(FSCACHE_COOKIE_NO_ACCESS_WAKE, &cookie->flags);
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	/* Need to read the access count after unpinning */
84862306a36Sopenharmony_ci	n_accesses = atomic_read(&cookie->n_accesses);
84962306a36Sopenharmony_ci	if (unpinned)
85062306a36Sopenharmony_ci		trace_fscache_access(cookie->debug_id, refcount_read(&cookie->ref),
85162306a36Sopenharmony_ci				     n_accesses, fscache_access_cache_unpin);
85262306a36Sopenharmony_ci	if (n_accesses == 0)
85362306a36Sopenharmony_ci		fscache_queue_cookie(cookie, fscache_cookie_get_end_access);
85462306a36Sopenharmony_ci}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_cistatic void fscache_cookie_lru_do_one(struct fscache_cookie *cookie)
85762306a36Sopenharmony_ci{
85862306a36Sopenharmony_ci	fscache_see_cookie(cookie, fscache_cookie_see_lru_do_one);
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	spin_lock(&cookie->lock);
86162306a36Sopenharmony_ci	if (cookie->state != FSCACHE_COOKIE_STATE_ACTIVE ||
86262306a36Sopenharmony_ci	    time_before(jiffies, cookie->unused_at + fscache_lru_cookie_timeout) ||
86362306a36Sopenharmony_ci	    atomic_read(&cookie->n_active) > 0) {
86462306a36Sopenharmony_ci		spin_unlock(&cookie->lock);
86562306a36Sopenharmony_ci		fscache_stat(&fscache_n_cookies_lru_removed);
86662306a36Sopenharmony_ci	} else {
86762306a36Sopenharmony_ci		set_bit(FSCACHE_COOKIE_DO_LRU_DISCARD, &cookie->flags);
86862306a36Sopenharmony_ci		spin_unlock(&cookie->lock);
86962306a36Sopenharmony_ci		fscache_stat(&fscache_n_cookies_lru_expired);
87062306a36Sopenharmony_ci		_debug("lru c=%x", cookie->debug_id);
87162306a36Sopenharmony_ci		__fscache_withdraw_cookie(cookie);
87262306a36Sopenharmony_ci	}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	fscache_put_cookie(cookie, fscache_cookie_put_lru);
87562306a36Sopenharmony_ci}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_cistatic void fscache_cookie_lru_worker(struct work_struct *work)
87862306a36Sopenharmony_ci{
87962306a36Sopenharmony_ci	struct fscache_cookie *cookie;
88062306a36Sopenharmony_ci	unsigned long unused_at;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	spin_lock(&fscache_cookie_lru_lock);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	while (!list_empty(&fscache_cookie_lru)) {
88562306a36Sopenharmony_ci		cookie = list_first_entry(&fscache_cookie_lru,
88662306a36Sopenharmony_ci					  struct fscache_cookie, commit_link);
88762306a36Sopenharmony_ci		unused_at = cookie->unused_at + fscache_lru_cookie_timeout;
88862306a36Sopenharmony_ci		if (time_before(jiffies, unused_at)) {
88962306a36Sopenharmony_ci			timer_reduce(&fscache_cookie_lru_timer, unused_at);
89062306a36Sopenharmony_ci			break;
89162306a36Sopenharmony_ci		}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci		list_del_init(&cookie->commit_link);
89462306a36Sopenharmony_ci		fscache_stat_d(&fscache_n_cookies_lru);
89562306a36Sopenharmony_ci		spin_unlock(&fscache_cookie_lru_lock);
89662306a36Sopenharmony_ci		fscache_cookie_lru_do_one(cookie);
89762306a36Sopenharmony_ci		spin_lock(&fscache_cookie_lru_lock);
89862306a36Sopenharmony_ci	}
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	spin_unlock(&fscache_cookie_lru_lock);
90162306a36Sopenharmony_ci}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_cistatic void fscache_cookie_lru_timed_out(struct timer_list *timer)
90462306a36Sopenharmony_ci{
90562306a36Sopenharmony_ci	queue_work(fscache_wq, &fscache_cookie_lru_work);
90662306a36Sopenharmony_ci}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_cistatic void fscache_cookie_drop_from_lru(struct fscache_cookie *cookie)
90962306a36Sopenharmony_ci{
91062306a36Sopenharmony_ci	bool need_put = false;
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	if (!list_empty(&cookie->commit_link)) {
91362306a36Sopenharmony_ci		spin_lock(&fscache_cookie_lru_lock);
91462306a36Sopenharmony_ci		if (!list_empty(&cookie->commit_link)) {
91562306a36Sopenharmony_ci			list_del_init(&cookie->commit_link);
91662306a36Sopenharmony_ci			fscache_stat_d(&fscache_n_cookies_lru);
91762306a36Sopenharmony_ci			fscache_stat(&fscache_n_cookies_lru_dropped);
91862306a36Sopenharmony_ci			need_put = true;
91962306a36Sopenharmony_ci		}
92062306a36Sopenharmony_ci		spin_unlock(&fscache_cookie_lru_lock);
92162306a36Sopenharmony_ci		if (need_put)
92262306a36Sopenharmony_ci			fscache_put_cookie(cookie, fscache_cookie_put_lru);
92362306a36Sopenharmony_ci	}
92462306a36Sopenharmony_ci}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci/*
92762306a36Sopenharmony_ci * Remove a cookie from the hash table.
92862306a36Sopenharmony_ci */
92962306a36Sopenharmony_cistatic void fscache_unhash_cookie(struct fscache_cookie *cookie)
93062306a36Sopenharmony_ci{
93162306a36Sopenharmony_ci	struct hlist_bl_head *h;
93262306a36Sopenharmony_ci	unsigned int bucket;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	bucket = cookie->key_hash & (ARRAY_SIZE(fscache_cookie_hash) - 1);
93562306a36Sopenharmony_ci	h = &fscache_cookie_hash[bucket];
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	hlist_bl_lock(h);
93862306a36Sopenharmony_ci	hlist_bl_del(&cookie->hash_link);
93962306a36Sopenharmony_ci	clear_bit(FSCACHE_COOKIE_IS_HASHED, &cookie->flags);
94062306a36Sopenharmony_ci	hlist_bl_unlock(h);
94162306a36Sopenharmony_ci	fscache_stat(&fscache_n_relinquishes_dropped);
94262306a36Sopenharmony_ci}
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_cistatic void fscache_drop_withdraw_cookie(struct fscache_cookie *cookie)
94562306a36Sopenharmony_ci{
94662306a36Sopenharmony_ci	fscache_cookie_drop_from_lru(cookie);
94762306a36Sopenharmony_ci	__fscache_withdraw_cookie(cookie);
94862306a36Sopenharmony_ci}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci/**
95162306a36Sopenharmony_ci * fscache_withdraw_cookie - Mark a cookie for withdrawal
95262306a36Sopenharmony_ci * @cookie: The cookie to be withdrawn.
95362306a36Sopenharmony_ci *
95462306a36Sopenharmony_ci * Allow the cache backend to withdraw the backing for a cookie for its own
95562306a36Sopenharmony_ci * reasons, even if that cookie is in active use.
95662306a36Sopenharmony_ci */
95762306a36Sopenharmony_civoid fscache_withdraw_cookie(struct fscache_cookie *cookie)
95862306a36Sopenharmony_ci{
95962306a36Sopenharmony_ci	set_bit(FSCACHE_COOKIE_DO_WITHDRAW, &cookie->flags);
96062306a36Sopenharmony_ci	fscache_drop_withdraw_cookie(cookie);
96162306a36Sopenharmony_ci}
96262306a36Sopenharmony_ciEXPORT_SYMBOL(fscache_withdraw_cookie);
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci/*
96562306a36Sopenharmony_ci * Allow the netfs to release a cookie back to the cache.
96662306a36Sopenharmony_ci * - the object will be marked as recyclable on disk if retire is true
96762306a36Sopenharmony_ci */
96862306a36Sopenharmony_civoid __fscache_relinquish_cookie(struct fscache_cookie *cookie, bool retire)
96962306a36Sopenharmony_ci{
97062306a36Sopenharmony_ci	fscache_stat(&fscache_n_relinquishes);
97162306a36Sopenharmony_ci	if (retire)
97262306a36Sopenharmony_ci		fscache_stat(&fscache_n_relinquishes_retire);
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	_enter("c=%08x{%d},%d",
97562306a36Sopenharmony_ci	       cookie->debug_id, atomic_read(&cookie->n_active), retire);
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	if (WARN(test_and_set_bit(FSCACHE_COOKIE_RELINQUISHED, &cookie->flags),
97862306a36Sopenharmony_ci		 "Cookie c=%x already relinquished\n", cookie->debug_id))
97962306a36Sopenharmony_ci		return;
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	if (retire)
98262306a36Sopenharmony_ci		set_bit(FSCACHE_COOKIE_RETIRED, &cookie->flags);
98362306a36Sopenharmony_ci	trace_fscache_relinquish(cookie, retire);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	ASSERTCMP(atomic_read(&cookie->n_active), ==, 0);
98662306a36Sopenharmony_ci	ASSERTCMP(atomic_read(&cookie->volume->n_cookies), >, 0);
98762306a36Sopenharmony_ci	atomic_dec(&cookie->volume->n_cookies);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	if (test_bit(FSCACHE_COOKIE_HAS_BEEN_CACHED, &cookie->flags)) {
99062306a36Sopenharmony_ci		set_bit(FSCACHE_COOKIE_DO_RELINQUISH, &cookie->flags);
99162306a36Sopenharmony_ci		fscache_drop_withdraw_cookie(cookie);
99262306a36Sopenharmony_ci	} else {
99362306a36Sopenharmony_ci		fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_DROPPED);
99462306a36Sopenharmony_ci		fscache_unhash_cookie(cookie);
99562306a36Sopenharmony_ci	}
99662306a36Sopenharmony_ci	fscache_put_cookie(cookie, fscache_cookie_put_relinquish);
99762306a36Sopenharmony_ci}
99862306a36Sopenharmony_ciEXPORT_SYMBOL(__fscache_relinquish_cookie);
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci/*
100162306a36Sopenharmony_ci * Drop a reference to a cookie.
100262306a36Sopenharmony_ci */
100362306a36Sopenharmony_civoid fscache_put_cookie(struct fscache_cookie *cookie,
100462306a36Sopenharmony_ci			enum fscache_cookie_trace where)
100562306a36Sopenharmony_ci{
100662306a36Sopenharmony_ci	struct fscache_volume *volume = cookie->volume;
100762306a36Sopenharmony_ci	unsigned int cookie_debug_id = cookie->debug_id;
100862306a36Sopenharmony_ci	bool zero;
100962306a36Sopenharmony_ci	int ref;
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	zero = __refcount_dec_and_test(&cookie->ref, &ref);
101262306a36Sopenharmony_ci	trace_fscache_cookie(cookie_debug_id, ref - 1, where);
101362306a36Sopenharmony_ci	if (zero) {
101462306a36Sopenharmony_ci		fscache_free_cookie(cookie);
101562306a36Sopenharmony_ci		fscache_put_volume(volume, fscache_volume_put_cookie);
101662306a36Sopenharmony_ci	}
101762306a36Sopenharmony_ci}
101862306a36Sopenharmony_ciEXPORT_SYMBOL(fscache_put_cookie);
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci/*
102162306a36Sopenharmony_ci * Get a reference to a cookie.
102262306a36Sopenharmony_ci */
102362306a36Sopenharmony_cistruct fscache_cookie *fscache_get_cookie(struct fscache_cookie *cookie,
102462306a36Sopenharmony_ci					  enum fscache_cookie_trace where)
102562306a36Sopenharmony_ci{
102662306a36Sopenharmony_ci	int ref;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	__refcount_inc(&cookie->ref, &ref);
102962306a36Sopenharmony_ci	trace_fscache_cookie(cookie->debug_id, ref + 1, where);
103062306a36Sopenharmony_ci	return cookie;
103162306a36Sopenharmony_ci}
103262306a36Sopenharmony_ciEXPORT_SYMBOL(fscache_get_cookie);
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci/*
103562306a36Sopenharmony_ci * Ask the cache to effect invalidation of a cookie.
103662306a36Sopenharmony_ci */
103762306a36Sopenharmony_cistatic void fscache_perform_invalidation(struct fscache_cookie *cookie)
103862306a36Sopenharmony_ci{
103962306a36Sopenharmony_ci	if (!cookie->volume->cache->ops->invalidate_cookie(cookie))
104062306a36Sopenharmony_ci		fscache_caching_failed(cookie);
104162306a36Sopenharmony_ci	fscache_end_cookie_access(cookie, fscache_access_invalidate_cookie_end);
104262306a36Sopenharmony_ci}
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci/*
104562306a36Sopenharmony_ci * Invalidate an object.
104662306a36Sopenharmony_ci */
104762306a36Sopenharmony_civoid __fscache_invalidate(struct fscache_cookie *cookie,
104862306a36Sopenharmony_ci			  const void *aux_data, loff_t new_size,
104962306a36Sopenharmony_ci			  unsigned int flags)
105062306a36Sopenharmony_ci{
105162306a36Sopenharmony_ci	bool is_caching;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	_enter("c=%x", cookie->debug_id);
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	fscache_stat(&fscache_n_invalidates);
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	if (WARN(test_bit(FSCACHE_COOKIE_RELINQUISHED, &cookie->flags),
105862306a36Sopenharmony_ci		 "Trying to invalidate relinquished cookie\n"))
105962306a36Sopenharmony_ci		return;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	if ((flags & FSCACHE_INVAL_DIO_WRITE) &&
106262306a36Sopenharmony_ci	    test_and_set_bit(FSCACHE_COOKIE_DISABLED, &cookie->flags))
106362306a36Sopenharmony_ci		return;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	spin_lock(&cookie->lock);
106662306a36Sopenharmony_ci	set_bit(FSCACHE_COOKIE_NO_DATA_TO_READ, &cookie->flags);
106762306a36Sopenharmony_ci	fscache_update_aux(cookie, aux_data, &new_size);
106862306a36Sopenharmony_ci	cookie->inval_counter++;
106962306a36Sopenharmony_ci	trace_fscache_invalidate(cookie, new_size);
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	switch (cookie->state) {
107262306a36Sopenharmony_ci	case FSCACHE_COOKIE_STATE_INVALIDATING: /* is_still_valid will catch it */
107362306a36Sopenharmony_ci	default:
107462306a36Sopenharmony_ci		spin_unlock(&cookie->lock);
107562306a36Sopenharmony_ci		_leave(" [no %u]", cookie->state);
107662306a36Sopenharmony_ci		return;
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	case FSCACHE_COOKIE_STATE_LOOKING_UP:
107962306a36Sopenharmony_ci		if (!test_and_set_bit(FSCACHE_COOKIE_DO_INVALIDATE, &cookie->flags))
108062306a36Sopenharmony_ci			__fscache_begin_cookie_access(cookie, fscache_access_invalidate_cookie);
108162306a36Sopenharmony_ci		fallthrough;
108262306a36Sopenharmony_ci	case FSCACHE_COOKIE_STATE_CREATING:
108362306a36Sopenharmony_ci		spin_unlock(&cookie->lock);
108462306a36Sopenharmony_ci		_leave(" [look %x]", cookie->inval_counter);
108562306a36Sopenharmony_ci		return;
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	case FSCACHE_COOKIE_STATE_ACTIVE:
108862306a36Sopenharmony_ci		is_caching = fscache_begin_cookie_access(
108962306a36Sopenharmony_ci			cookie, fscache_access_invalidate_cookie);
109062306a36Sopenharmony_ci		if (is_caching)
109162306a36Sopenharmony_ci			__fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_INVALIDATING);
109262306a36Sopenharmony_ci		spin_unlock(&cookie->lock);
109362306a36Sopenharmony_ci		wake_up_cookie_state(cookie);
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci		if (is_caching)
109662306a36Sopenharmony_ci			fscache_queue_cookie(cookie, fscache_cookie_get_inval_work);
109762306a36Sopenharmony_ci		_leave(" [inv]");
109862306a36Sopenharmony_ci		return;
109962306a36Sopenharmony_ci	}
110062306a36Sopenharmony_ci}
110162306a36Sopenharmony_ciEXPORT_SYMBOL(__fscache_invalidate);
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS
110462306a36Sopenharmony_ci/*
110562306a36Sopenharmony_ci * Generate a list of extant cookies in /proc/fs/fscache/cookies
110662306a36Sopenharmony_ci */
110762306a36Sopenharmony_cistatic int fscache_cookies_seq_show(struct seq_file *m, void *v)
110862306a36Sopenharmony_ci{
110962306a36Sopenharmony_ci	struct fscache_cookie *cookie;
111062306a36Sopenharmony_ci	unsigned int keylen = 0, auxlen = 0;
111162306a36Sopenharmony_ci	u8 *p;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	if (v == &fscache_cookies) {
111462306a36Sopenharmony_ci		seq_puts(m,
111562306a36Sopenharmony_ci			 "COOKIE   VOLUME   REF ACT ACC S FL DEF             \n"
111662306a36Sopenharmony_ci			 "======== ======== === === === = == ================\n"
111762306a36Sopenharmony_ci			 );
111862306a36Sopenharmony_ci		return 0;
111962306a36Sopenharmony_ci	}
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	cookie = list_entry(v, struct fscache_cookie, proc_link);
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	seq_printf(m,
112462306a36Sopenharmony_ci		   "%08x %08x %3d %3d %3d %c %02lx",
112562306a36Sopenharmony_ci		   cookie->debug_id,
112662306a36Sopenharmony_ci		   cookie->volume->debug_id,
112762306a36Sopenharmony_ci		   refcount_read(&cookie->ref),
112862306a36Sopenharmony_ci		   atomic_read(&cookie->n_active),
112962306a36Sopenharmony_ci		   atomic_read(&cookie->n_accesses),
113062306a36Sopenharmony_ci		   fscache_cookie_states[cookie->state],
113162306a36Sopenharmony_ci		   cookie->flags);
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	keylen = cookie->key_len;
113462306a36Sopenharmony_ci	auxlen = cookie->aux_len;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	if (keylen > 0 || auxlen > 0) {
113762306a36Sopenharmony_ci		seq_puts(m, " ");
113862306a36Sopenharmony_ci		p = keylen <= sizeof(cookie->inline_key) ?
113962306a36Sopenharmony_ci			cookie->inline_key : cookie->key;
114062306a36Sopenharmony_ci		for (; keylen > 0; keylen--)
114162306a36Sopenharmony_ci			seq_printf(m, "%02x", *p++);
114262306a36Sopenharmony_ci		if (auxlen > 0) {
114362306a36Sopenharmony_ci			seq_puts(m, ", ");
114462306a36Sopenharmony_ci			p = auxlen <= sizeof(cookie->inline_aux) ?
114562306a36Sopenharmony_ci				cookie->inline_aux : cookie->aux;
114662306a36Sopenharmony_ci			for (; auxlen > 0; auxlen--)
114762306a36Sopenharmony_ci				seq_printf(m, "%02x", *p++);
114862306a36Sopenharmony_ci		}
114962306a36Sopenharmony_ci	}
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	seq_puts(m, "\n");
115262306a36Sopenharmony_ci	return 0;
115362306a36Sopenharmony_ci}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_cistatic void *fscache_cookies_seq_start(struct seq_file *m, loff_t *_pos)
115662306a36Sopenharmony_ci	__acquires(fscache_cookies_lock)
115762306a36Sopenharmony_ci{
115862306a36Sopenharmony_ci	read_lock(&fscache_cookies_lock);
115962306a36Sopenharmony_ci	return seq_list_start_head(&fscache_cookies, *_pos);
116062306a36Sopenharmony_ci}
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_cistatic void *fscache_cookies_seq_next(struct seq_file *m, void *v, loff_t *_pos)
116362306a36Sopenharmony_ci{
116462306a36Sopenharmony_ci	return seq_list_next(v, &fscache_cookies, _pos);
116562306a36Sopenharmony_ci}
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_cistatic void fscache_cookies_seq_stop(struct seq_file *m, void *v)
116862306a36Sopenharmony_ci	__releases(rcu)
116962306a36Sopenharmony_ci{
117062306a36Sopenharmony_ci	read_unlock(&fscache_cookies_lock);
117162306a36Sopenharmony_ci}
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ciconst struct seq_operations fscache_cookies_seq_ops = {
117562306a36Sopenharmony_ci	.start  = fscache_cookies_seq_start,
117662306a36Sopenharmony_ci	.next   = fscache_cookies_seq_next,
117762306a36Sopenharmony_ci	.stop   = fscache_cookies_seq_stop,
117862306a36Sopenharmony_ci	.show   = fscache_cookies_seq_show,
117962306a36Sopenharmony_ci};
118062306a36Sopenharmony_ci#endif
1181