18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* netfs cookie management
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2004-2007 Red Hat, Inc. All Rights Reserved.
58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * See Documentation/filesystems/caching/netfs-api.rst for more information on
88c2ecf20Sopenharmony_ci * the netfs API.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#define FSCACHE_DEBUG_LEVEL COOKIE
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include "internal.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistruct kmem_cache *fscache_cookie_jar;
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic atomic_t fscache_object_debug_id = ATOMIC_INIT(0);
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define fscache_cookie_hash_shift 15
218c2ecf20Sopenharmony_cistatic struct hlist_bl_head fscache_cookie_hash[1 << fscache_cookie_hash_shift];
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic int fscache_acquire_non_index_cookie(struct fscache_cookie *cookie,
248c2ecf20Sopenharmony_ci					    loff_t object_size);
258c2ecf20Sopenharmony_cistatic int fscache_alloc_object(struct fscache_cache *cache,
268c2ecf20Sopenharmony_ci				struct fscache_cookie *cookie);
278c2ecf20Sopenharmony_cistatic int fscache_attach_object(struct fscache_cookie *cookie,
288c2ecf20Sopenharmony_ci				 struct fscache_object *object);
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic void fscache_print_cookie(struct fscache_cookie *cookie, char prefix)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	struct hlist_node *object;
338c2ecf20Sopenharmony_ci	const u8 *k;
348c2ecf20Sopenharmony_ci	unsigned loop;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	pr_err("%c-cookie c=%p [p=%p fl=%lx nc=%u na=%u]\n",
378c2ecf20Sopenharmony_ci	       prefix, cookie, cookie->parent, cookie->flags,
388c2ecf20Sopenharmony_ci	       atomic_read(&cookie->n_children),
398c2ecf20Sopenharmony_ci	       atomic_read(&cookie->n_active));
408c2ecf20Sopenharmony_ci	pr_err("%c-cookie d=%p n=%p\n",
418c2ecf20Sopenharmony_ci	       prefix, cookie->def, cookie->netfs_data);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	object = READ_ONCE(cookie->backing_objects.first);
448c2ecf20Sopenharmony_ci	if (object)
458c2ecf20Sopenharmony_ci		pr_err("%c-cookie o=%p\n",
468c2ecf20Sopenharmony_ci		       prefix, hlist_entry(object, struct fscache_object, cookie_link));
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	pr_err("%c-key=[%u] '", prefix, cookie->key_len);
498c2ecf20Sopenharmony_ci	k = (cookie->key_len <= sizeof(cookie->inline_key)) ?
508c2ecf20Sopenharmony_ci		cookie->inline_key : cookie->key;
518c2ecf20Sopenharmony_ci	for (loop = 0; loop < cookie->key_len; loop++)
528c2ecf20Sopenharmony_ci		pr_cont("%02x", k[loop]);
538c2ecf20Sopenharmony_ci	pr_cont("'\n");
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_civoid fscache_free_cookie(struct fscache_cookie *cookie)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	if (cookie) {
598c2ecf20Sopenharmony_ci		BUG_ON(!hlist_empty(&cookie->backing_objects));
608c2ecf20Sopenharmony_ci		if (cookie->aux_len > sizeof(cookie->inline_aux))
618c2ecf20Sopenharmony_ci			kfree(cookie->aux);
628c2ecf20Sopenharmony_ci		if (cookie->key_len > sizeof(cookie->inline_key))
638c2ecf20Sopenharmony_ci			kfree(cookie->key);
648c2ecf20Sopenharmony_ci		kmem_cache_free(fscache_cookie_jar, cookie);
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/*
698c2ecf20Sopenharmony_ci * Set the index key in a cookie.  The cookie struct has space for a 16-byte
708c2ecf20Sopenharmony_ci * key plus length and hash, but if that's not big enough, it's instead a
718c2ecf20Sopenharmony_ci * pointer to a buffer containing 3 bytes of hash, 1 byte of length and then
728c2ecf20Sopenharmony_ci * the key data.
738c2ecf20Sopenharmony_ci */
748c2ecf20Sopenharmony_cistatic int fscache_set_key(struct fscache_cookie *cookie,
758c2ecf20Sopenharmony_ci			   const void *index_key, size_t index_key_len)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	u32 *buf;
788c2ecf20Sopenharmony_ci	int bufs;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	bufs = DIV_ROUND_UP(index_key_len, sizeof(*buf));
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	if (index_key_len > sizeof(cookie->inline_key)) {
838c2ecf20Sopenharmony_ci		buf = kcalloc(bufs, sizeof(*buf), GFP_KERNEL);
848c2ecf20Sopenharmony_ci		if (!buf)
858c2ecf20Sopenharmony_ci			return -ENOMEM;
868c2ecf20Sopenharmony_ci		cookie->key = buf;
878c2ecf20Sopenharmony_ci	} else {
888c2ecf20Sopenharmony_ci		buf = (u32 *)cookie->inline_key;
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	memcpy(buf, index_key, index_key_len);
928c2ecf20Sopenharmony_ci	cookie->key_hash = fscache_hash(0, buf, bufs);
938c2ecf20Sopenharmony_ci	return 0;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic long fscache_compare_cookie(const struct fscache_cookie *a,
978c2ecf20Sopenharmony_ci				   const struct fscache_cookie *b)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	const void *ka, *kb;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (a->key_hash != b->key_hash)
1028c2ecf20Sopenharmony_ci		return (long)a->key_hash - (long)b->key_hash;
1038c2ecf20Sopenharmony_ci	if (a->parent != b->parent)
1048c2ecf20Sopenharmony_ci		return (long)a->parent - (long)b->parent;
1058c2ecf20Sopenharmony_ci	if (a->key_len != b->key_len)
1068c2ecf20Sopenharmony_ci		return (long)a->key_len - (long)b->key_len;
1078c2ecf20Sopenharmony_ci	if (a->type != b->type)
1088c2ecf20Sopenharmony_ci		return (long)a->type - (long)b->type;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if (a->key_len <= sizeof(a->inline_key)) {
1118c2ecf20Sopenharmony_ci		ka = &a->inline_key;
1128c2ecf20Sopenharmony_ci		kb = &b->inline_key;
1138c2ecf20Sopenharmony_ci	} else {
1148c2ecf20Sopenharmony_ci		ka = a->key;
1158c2ecf20Sopenharmony_ci		kb = b->key;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci	return memcmp(ka, kb, a->key_len);
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci/*
1218c2ecf20Sopenharmony_ci * Allocate a cookie.
1228c2ecf20Sopenharmony_ci */
1238c2ecf20Sopenharmony_cistruct fscache_cookie *fscache_alloc_cookie(
1248c2ecf20Sopenharmony_ci	struct fscache_cookie *parent,
1258c2ecf20Sopenharmony_ci	const struct fscache_cookie_def *def,
1268c2ecf20Sopenharmony_ci	const void *index_key, size_t index_key_len,
1278c2ecf20Sopenharmony_ci	const void *aux_data, size_t aux_data_len,
1288c2ecf20Sopenharmony_ci	void *netfs_data,
1298c2ecf20Sopenharmony_ci	loff_t object_size)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	struct fscache_cookie *cookie;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	/* allocate and initialise a cookie */
1348c2ecf20Sopenharmony_ci	cookie = kmem_cache_zalloc(fscache_cookie_jar, GFP_KERNEL);
1358c2ecf20Sopenharmony_ci	if (!cookie)
1368c2ecf20Sopenharmony_ci		return NULL;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	cookie->key_len = index_key_len;
1398c2ecf20Sopenharmony_ci	cookie->aux_len = aux_data_len;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	if (fscache_set_key(cookie, index_key, index_key_len) < 0)
1428c2ecf20Sopenharmony_ci		goto nomem;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (cookie->aux_len <= sizeof(cookie->inline_aux)) {
1458c2ecf20Sopenharmony_ci		memcpy(cookie->inline_aux, aux_data, cookie->aux_len);
1468c2ecf20Sopenharmony_ci	} else {
1478c2ecf20Sopenharmony_ci		cookie->aux = kmemdup(aux_data, cookie->aux_len, GFP_KERNEL);
1488c2ecf20Sopenharmony_ci		if (!cookie->aux)
1498c2ecf20Sopenharmony_ci			goto nomem;
1508c2ecf20Sopenharmony_ci	}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	atomic_set(&cookie->usage, 1);
1538c2ecf20Sopenharmony_ci	atomic_set(&cookie->n_children, 0);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	/* We keep the active count elevated until relinquishment to prevent an
1568c2ecf20Sopenharmony_ci	 * attempt to wake up every time the object operations queue quiesces.
1578c2ecf20Sopenharmony_ci	 */
1588c2ecf20Sopenharmony_ci	atomic_set(&cookie->n_active, 1);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	cookie->def		= def;
1618c2ecf20Sopenharmony_ci	cookie->parent		= parent;
1628c2ecf20Sopenharmony_ci	cookie->netfs_data	= netfs_data;
1638c2ecf20Sopenharmony_ci	cookie->flags		= (1 << FSCACHE_COOKIE_NO_DATA_YET);
1648c2ecf20Sopenharmony_ci	cookie->type		= def->type;
1658c2ecf20Sopenharmony_ci	spin_lock_init(&cookie->lock);
1668c2ecf20Sopenharmony_ci	spin_lock_init(&cookie->stores_lock);
1678c2ecf20Sopenharmony_ci	INIT_HLIST_HEAD(&cookie->backing_objects);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	/* radix tree insertion won't use the preallocation pool unless it's
1708c2ecf20Sopenharmony_ci	 * told it may not wait */
1718c2ecf20Sopenharmony_ci	INIT_RADIX_TREE(&cookie->stores, GFP_NOFS & ~__GFP_DIRECT_RECLAIM);
1728c2ecf20Sopenharmony_ci	return cookie;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cinomem:
1758c2ecf20Sopenharmony_ci	fscache_free_cookie(cookie);
1768c2ecf20Sopenharmony_ci	return NULL;
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci/*
1808c2ecf20Sopenharmony_ci * Attempt to insert the new cookie into the hash.  If there's a collision, we
1818c2ecf20Sopenharmony_ci * return the old cookie if it's not in use and an error otherwise.
1828c2ecf20Sopenharmony_ci */
1838c2ecf20Sopenharmony_cistruct fscache_cookie *fscache_hash_cookie(struct fscache_cookie *candidate)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	struct fscache_cookie *cursor;
1868c2ecf20Sopenharmony_ci	struct hlist_bl_head *h;
1878c2ecf20Sopenharmony_ci	struct hlist_bl_node *p;
1888c2ecf20Sopenharmony_ci	unsigned int bucket;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	bucket = candidate->key_hash & (ARRAY_SIZE(fscache_cookie_hash) - 1);
1918c2ecf20Sopenharmony_ci	h = &fscache_cookie_hash[bucket];
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	hlist_bl_lock(h);
1948c2ecf20Sopenharmony_ci	hlist_bl_for_each_entry(cursor, p, h, hash_link) {
1958c2ecf20Sopenharmony_ci		if (fscache_compare_cookie(candidate, cursor) == 0)
1968c2ecf20Sopenharmony_ci			goto collision;
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	__set_bit(FSCACHE_COOKIE_ACQUIRED, &candidate->flags);
2008c2ecf20Sopenharmony_ci	fscache_cookie_get(candidate->parent, fscache_cookie_get_acquire_parent);
2018c2ecf20Sopenharmony_ci	atomic_inc(&candidate->parent->n_children);
2028c2ecf20Sopenharmony_ci	hlist_bl_add_head(&candidate->hash_link, h);
2038c2ecf20Sopenharmony_ci	hlist_bl_unlock(h);
2048c2ecf20Sopenharmony_ci	return candidate;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cicollision:
2078c2ecf20Sopenharmony_ci	if (test_and_set_bit(FSCACHE_COOKIE_ACQUIRED, &cursor->flags)) {
2088c2ecf20Sopenharmony_ci		trace_fscache_cookie(cursor, fscache_cookie_collision,
2098c2ecf20Sopenharmony_ci				     atomic_read(&cursor->usage));
2108c2ecf20Sopenharmony_ci		pr_err("Duplicate cookie detected\n");
2118c2ecf20Sopenharmony_ci		fscache_print_cookie(cursor, 'O');
2128c2ecf20Sopenharmony_ci		fscache_print_cookie(candidate, 'N');
2138c2ecf20Sopenharmony_ci		hlist_bl_unlock(h);
2148c2ecf20Sopenharmony_ci		return NULL;
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	fscache_cookie_get(cursor, fscache_cookie_get_reacquire);
2188c2ecf20Sopenharmony_ci	hlist_bl_unlock(h);
2198c2ecf20Sopenharmony_ci	return cursor;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci/*
2238c2ecf20Sopenharmony_ci * request a cookie to represent an object (index, datafile, xattr, etc)
2248c2ecf20Sopenharmony_ci * - parent specifies the parent object
2258c2ecf20Sopenharmony_ci *   - the top level index cookie for each netfs is stored in the fscache_netfs
2268c2ecf20Sopenharmony_ci *     struct upon registration
2278c2ecf20Sopenharmony_ci * - def points to the definition
2288c2ecf20Sopenharmony_ci * - the netfs_data will be passed to the functions pointed to in *def
2298c2ecf20Sopenharmony_ci * - all attached caches will be searched to see if they contain this object
2308c2ecf20Sopenharmony_ci * - index objects aren't stored on disk until there's a dependent file that
2318c2ecf20Sopenharmony_ci *   needs storing
2328c2ecf20Sopenharmony_ci * - other objects are stored in a selected cache immediately, and all the
2338c2ecf20Sopenharmony_ci *   indices forming the path to it are instantiated if necessary
2348c2ecf20Sopenharmony_ci * - we never let on to the netfs about errors
2358c2ecf20Sopenharmony_ci *   - we may set a negative cookie pointer, but that's okay
2368c2ecf20Sopenharmony_ci */
2378c2ecf20Sopenharmony_cistruct fscache_cookie *__fscache_acquire_cookie(
2388c2ecf20Sopenharmony_ci	struct fscache_cookie *parent,
2398c2ecf20Sopenharmony_ci	const struct fscache_cookie_def *def,
2408c2ecf20Sopenharmony_ci	const void *index_key, size_t index_key_len,
2418c2ecf20Sopenharmony_ci	const void *aux_data, size_t aux_data_len,
2428c2ecf20Sopenharmony_ci	void *netfs_data,
2438c2ecf20Sopenharmony_ci	loff_t object_size,
2448c2ecf20Sopenharmony_ci	bool enable)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	struct fscache_cookie *candidate, *cookie;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	BUG_ON(!def);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	_enter("{%s},{%s},%p,%u",
2518c2ecf20Sopenharmony_ci	       parent ? (char *) parent->def->name : "<no-parent>",
2528c2ecf20Sopenharmony_ci	       def->name, netfs_data, enable);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	if (!index_key || !index_key_len || index_key_len > 255 || aux_data_len > 255)
2558c2ecf20Sopenharmony_ci		return NULL;
2568c2ecf20Sopenharmony_ci	if (!aux_data || !aux_data_len) {
2578c2ecf20Sopenharmony_ci		aux_data = NULL;
2588c2ecf20Sopenharmony_ci		aux_data_len = 0;
2598c2ecf20Sopenharmony_ci	}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	fscache_stat(&fscache_n_acquires);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	/* if there's no parent cookie, then we don't create one here either */
2648c2ecf20Sopenharmony_ci	if (!parent) {
2658c2ecf20Sopenharmony_ci		fscache_stat(&fscache_n_acquires_null);
2668c2ecf20Sopenharmony_ci		_leave(" [no parent]");
2678c2ecf20Sopenharmony_ci		return NULL;
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	/* validate the definition */
2718c2ecf20Sopenharmony_ci	BUG_ON(!def->name[0]);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	BUG_ON(def->type == FSCACHE_COOKIE_TYPE_INDEX &&
2748c2ecf20Sopenharmony_ci	       parent->type != FSCACHE_COOKIE_TYPE_INDEX);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	candidate = fscache_alloc_cookie(parent, def,
2778c2ecf20Sopenharmony_ci					 index_key, index_key_len,
2788c2ecf20Sopenharmony_ci					 aux_data, aux_data_len,
2798c2ecf20Sopenharmony_ci					 netfs_data, object_size);
2808c2ecf20Sopenharmony_ci	if (!candidate) {
2818c2ecf20Sopenharmony_ci		fscache_stat(&fscache_n_acquires_oom);
2828c2ecf20Sopenharmony_ci		_leave(" [ENOMEM]");
2838c2ecf20Sopenharmony_ci		return NULL;
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	cookie = fscache_hash_cookie(candidate);
2878c2ecf20Sopenharmony_ci	if (!cookie) {
2888c2ecf20Sopenharmony_ci		trace_fscache_cookie(candidate, fscache_cookie_discard, 1);
2898c2ecf20Sopenharmony_ci		goto out;
2908c2ecf20Sopenharmony_ci	}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	if (cookie == candidate)
2938c2ecf20Sopenharmony_ci		candidate = NULL;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	switch (cookie->type) {
2968c2ecf20Sopenharmony_ci	case FSCACHE_COOKIE_TYPE_INDEX:
2978c2ecf20Sopenharmony_ci		fscache_stat(&fscache_n_cookie_index);
2988c2ecf20Sopenharmony_ci		break;
2998c2ecf20Sopenharmony_ci	case FSCACHE_COOKIE_TYPE_DATAFILE:
3008c2ecf20Sopenharmony_ci		fscache_stat(&fscache_n_cookie_data);
3018c2ecf20Sopenharmony_ci		break;
3028c2ecf20Sopenharmony_ci	default:
3038c2ecf20Sopenharmony_ci		fscache_stat(&fscache_n_cookie_special);
3048c2ecf20Sopenharmony_ci		break;
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	trace_fscache_acquire(cookie);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	if (enable) {
3108c2ecf20Sopenharmony_ci		/* if the object is an index then we need do nothing more here
3118c2ecf20Sopenharmony_ci		 * - we create indices on disk when we need them as an index
3128c2ecf20Sopenharmony_ci		 * may exist in multiple caches */
3138c2ecf20Sopenharmony_ci		if (cookie->type != FSCACHE_COOKIE_TYPE_INDEX) {
3148c2ecf20Sopenharmony_ci			if (fscache_acquire_non_index_cookie(cookie, object_size) == 0) {
3158c2ecf20Sopenharmony_ci				set_bit(FSCACHE_COOKIE_ENABLED, &cookie->flags);
3168c2ecf20Sopenharmony_ci			} else {
3178c2ecf20Sopenharmony_ci				atomic_dec(&parent->n_children);
3188c2ecf20Sopenharmony_ci				fscache_cookie_put(cookie,
3198c2ecf20Sopenharmony_ci						   fscache_cookie_put_acquire_nobufs);
3208c2ecf20Sopenharmony_ci				fscache_stat(&fscache_n_acquires_nobufs);
3218c2ecf20Sopenharmony_ci				_leave(" = NULL");
3228c2ecf20Sopenharmony_ci				return NULL;
3238c2ecf20Sopenharmony_ci			}
3248c2ecf20Sopenharmony_ci		} else {
3258c2ecf20Sopenharmony_ci			set_bit(FSCACHE_COOKIE_ENABLED, &cookie->flags);
3268c2ecf20Sopenharmony_ci		}
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	fscache_stat(&fscache_n_acquires_ok);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ciout:
3328c2ecf20Sopenharmony_ci	fscache_free_cookie(candidate);
3338c2ecf20Sopenharmony_ci	return cookie;
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__fscache_acquire_cookie);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci/*
3388c2ecf20Sopenharmony_ci * Enable a cookie to permit it to accept new operations.
3398c2ecf20Sopenharmony_ci */
3408c2ecf20Sopenharmony_civoid __fscache_enable_cookie(struct fscache_cookie *cookie,
3418c2ecf20Sopenharmony_ci			     const void *aux_data,
3428c2ecf20Sopenharmony_ci			     loff_t object_size,
3438c2ecf20Sopenharmony_ci			     bool (*can_enable)(void *data),
3448c2ecf20Sopenharmony_ci			     void *data)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	_enter("%p", cookie);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	trace_fscache_enable(cookie);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	wait_on_bit_lock(&cookie->flags, FSCACHE_COOKIE_ENABLEMENT_LOCK,
3518c2ecf20Sopenharmony_ci			 TASK_UNINTERRUPTIBLE);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	fscache_update_aux(cookie, aux_data);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	if (test_bit(FSCACHE_COOKIE_ENABLED, &cookie->flags))
3568c2ecf20Sopenharmony_ci		goto out_unlock;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	if (can_enable && !can_enable(data)) {
3598c2ecf20Sopenharmony_ci		/* The netfs decided it didn't want to enable after all */
3608c2ecf20Sopenharmony_ci	} else if (cookie->type != FSCACHE_COOKIE_TYPE_INDEX) {
3618c2ecf20Sopenharmony_ci		/* Wait for outstanding disablement to complete */
3628c2ecf20Sopenharmony_ci		__fscache_wait_on_invalidate(cookie);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci		if (fscache_acquire_non_index_cookie(cookie, object_size) == 0)
3658c2ecf20Sopenharmony_ci			set_bit(FSCACHE_COOKIE_ENABLED, &cookie->flags);
3668c2ecf20Sopenharmony_ci	} else {
3678c2ecf20Sopenharmony_ci		set_bit(FSCACHE_COOKIE_ENABLED, &cookie->flags);
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ciout_unlock:
3718c2ecf20Sopenharmony_ci	clear_bit_unlock(FSCACHE_COOKIE_ENABLEMENT_LOCK, &cookie->flags);
3728c2ecf20Sopenharmony_ci	wake_up_bit(&cookie->flags, FSCACHE_COOKIE_ENABLEMENT_LOCK);
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__fscache_enable_cookie);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci/*
3778c2ecf20Sopenharmony_ci * acquire a non-index cookie
3788c2ecf20Sopenharmony_ci * - this must make sure the index chain is instantiated and instantiate the
3798c2ecf20Sopenharmony_ci *   object representation too
3808c2ecf20Sopenharmony_ci */
3818c2ecf20Sopenharmony_cistatic int fscache_acquire_non_index_cookie(struct fscache_cookie *cookie,
3828c2ecf20Sopenharmony_ci					    loff_t object_size)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	struct fscache_object *object;
3858c2ecf20Sopenharmony_ci	struct fscache_cache *cache;
3868c2ecf20Sopenharmony_ci	int ret;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	_enter("");
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	set_bit(FSCACHE_COOKIE_UNAVAILABLE, &cookie->flags);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	/* now we need to see whether the backing objects for this cookie yet
3938c2ecf20Sopenharmony_ci	 * exist, if not there'll be nothing to search */
3948c2ecf20Sopenharmony_ci	down_read(&fscache_addremove_sem);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	if (list_empty(&fscache_cache_list)) {
3978c2ecf20Sopenharmony_ci		up_read(&fscache_addremove_sem);
3988c2ecf20Sopenharmony_ci		_leave(" = 0 [no caches]");
3998c2ecf20Sopenharmony_ci		return 0;
4008c2ecf20Sopenharmony_ci	}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	/* select a cache in which to store the object */
4038c2ecf20Sopenharmony_ci	cache = fscache_select_cache_for_object(cookie->parent);
4048c2ecf20Sopenharmony_ci	if (!cache) {
4058c2ecf20Sopenharmony_ci		up_read(&fscache_addremove_sem);
4068c2ecf20Sopenharmony_ci		fscache_stat(&fscache_n_acquires_no_cache);
4078c2ecf20Sopenharmony_ci		_leave(" = -ENOMEDIUM [no cache]");
4088c2ecf20Sopenharmony_ci		return -ENOMEDIUM;
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	_debug("cache %s", cache->tag->name);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	set_bit(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	/* ask the cache to allocate objects for this cookie and its parent
4168c2ecf20Sopenharmony_ci	 * chain */
4178c2ecf20Sopenharmony_ci	ret = fscache_alloc_object(cache, cookie);
4188c2ecf20Sopenharmony_ci	if (ret < 0) {
4198c2ecf20Sopenharmony_ci		up_read(&fscache_addremove_sem);
4208c2ecf20Sopenharmony_ci		_leave(" = %d", ret);
4218c2ecf20Sopenharmony_ci		return ret;
4228c2ecf20Sopenharmony_ci	}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	spin_lock(&cookie->lock);
4258c2ecf20Sopenharmony_ci	if (hlist_empty(&cookie->backing_objects)) {
4268c2ecf20Sopenharmony_ci		spin_unlock(&cookie->lock);
4278c2ecf20Sopenharmony_ci		goto unavailable;
4288c2ecf20Sopenharmony_ci	}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	object = hlist_entry(cookie->backing_objects.first,
4318c2ecf20Sopenharmony_ci			     struct fscache_object, cookie_link);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	fscache_set_store_limit(object, object_size);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	/* initiate the process of looking up all the objects in the chain
4368c2ecf20Sopenharmony_ci	 * (done by fscache_initialise_object()) */
4378c2ecf20Sopenharmony_ci	fscache_raise_event(object, FSCACHE_OBJECT_EV_NEW_CHILD);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	spin_unlock(&cookie->lock);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	/* we may be required to wait for lookup to complete at this point */
4428c2ecf20Sopenharmony_ci	if (!fscache_defer_lookup) {
4438c2ecf20Sopenharmony_ci		_debug("non-deferred lookup %p", &cookie->flags);
4448c2ecf20Sopenharmony_ci		wait_on_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP,
4458c2ecf20Sopenharmony_ci			    TASK_UNINTERRUPTIBLE);
4468c2ecf20Sopenharmony_ci		_debug("complete");
4478c2ecf20Sopenharmony_ci		if (test_bit(FSCACHE_COOKIE_UNAVAILABLE, &cookie->flags))
4488c2ecf20Sopenharmony_ci			goto unavailable;
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	up_read(&fscache_addremove_sem);
4528c2ecf20Sopenharmony_ci	_leave(" = 0 [deferred]");
4538c2ecf20Sopenharmony_ci	return 0;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ciunavailable:
4568c2ecf20Sopenharmony_ci	up_read(&fscache_addremove_sem);
4578c2ecf20Sopenharmony_ci	_leave(" = -ENOBUFS");
4588c2ecf20Sopenharmony_ci	return -ENOBUFS;
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci/*
4628c2ecf20Sopenharmony_ci * recursively allocate cache object records for a cookie/cache combination
4638c2ecf20Sopenharmony_ci * - caller must be holding the addremove sem
4648c2ecf20Sopenharmony_ci */
4658c2ecf20Sopenharmony_cistatic int fscache_alloc_object(struct fscache_cache *cache,
4668c2ecf20Sopenharmony_ci				struct fscache_cookie *cookie)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	struct fscache_object *object;
4698c2ecf20Sopenharmony_ci	int ret;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	_enter("%p,%p{%s}", cache, cookie, cookie->def->name);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	spin_lock(&cookie->lock);
4748c2ecf20Sopenharmony_ci	hlist_for_each_entry(object, &cookie->backing_objects,
4758c2ecf20Sopenharmony_ci			     cookie_link) {
4768c2ecf20Sopenharmony_ci		if (object->cache == cache)
4778c2ecf20Sopenharmony_ci			goto object_already_extant;
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci	spin_unlock(&cookie->lock);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	/* ask the cache to allocate an object (we may end up with duplicate
4828c2ecf20Sopenharmony_ci	 * objects at this stage, but we sort that out later) */
4838c2ecf20Sopenharmony_ci	fscache_stat(&fscache_n_cop_alloc_object);
4848c2ecf20Sopenharmony_ci	object = cache->ops->alloc_object(cache, cookie);
4858c2ecf20Sopenharmony_ci	fscache_stat_d(&fscache_n_cop_alloc_object);
4868c2ecf20Sopenharmony_ci	if (IS_ERR(object)) {
4878c2ecf20Sopenharmony_ci		fscache_stat(&fscache_n_object_no_alloc);
4888c2ecf20Sopenharmony_ci		ret = PTR_ERR(object);
4898c2ecf20Sopenharmony_ci		goto error;
4908c2ecf20Sopenharmony_ci	}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	ASSERTCMP(object->cookie, ==, cookie);
4938c2ecf20Sopenharmony_ci	fscache_stat(&fscache_n_object_alloc);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	object->debug_id = atomic_inc_return(&fscache_object_debug_id);
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	_debug("ALLOC OBJ%x: %s {%lx}",
4988c2ecf20Sopenharmony_ci	       object->debug_id, cookie->def->name, object->events);
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	ret = fscache_alloc_object(cache, cookie->parent);
5018c2ecf20Sopenharmony_ci	if (ret < 0)
5028c2ecf20Sopenharmony_ci		goto error_put;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	/* only attach if we managed to allocate all we needed, otherwise
5058c2ecf20Sopenharmony_ci	 * discard the object we just allocated and instead use the one
5068c2ecf20Sopenharmony_ci	 * attached to the cookie */
5078c2ecf20Sopenharmony_ci	if (fscache_attach_object(cookie, object) < 0) {
5088c2ecf20Sopenharmony_ci		fscache_stat(&fscache_n_cop_put_object);
5098c2ecf20Sopenharmony_ci		cache->ops->put_object(object, fscache_obj_put_attach_fail);
5108c2ecf20Sopenharmony_ci		fscache_stat_d(&fscache_n_cop_put_object);
5118c2ecf20Sopenharmony_ci	}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	_leave(" = 0");
5148c2ecf20Sopenharmony_ci	return 0;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ciobject_already_extant:
5178c2ecf20Sopenharmony_ci	ret = -ENOBUFS;
5188c2ecf20Sopenharmony_ci	if (fscache_object_is_dying(object) ||
5198c2ecf20Sopenharmony_ci	    fscache_cache_is_broken(object)) {
5208c2ecf20Sopenharmony_ci		spin_unlock(&cookie->lock);
5218c2ecf20Sopenharmony_ci		goto error;
5228c2ecf20Sopenharmony_ci	}
5238c2ecf20Sopenharmony_ci	spin_unlock(&cookie->lock);
5248c2ecf20Sopenharmony_ci	_leave(" = 0 [found]");
5258c2ecf20Sopenharmony_ci	return 0;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_cierror_put:
5288c2ecf20Sopenharmony_ci	fscache_stat(&fscache_n_cop_put_object);
5298c2ecf20Sopenharmony_ci	cache->ops->put_object(object, fscache_obj_put_alloc_fail);
5308c2ecf20Sopenharmony_ci	fscache_stat_d(&fscache_n_cop_put_object);
5318c2ecf20Sopenharmony_cierror:
5328c2ecf20Sopenharmony_ci	_leave(" = %d", ret);
5338c2ecf20Sopenharmony_ci	return ret;
5348c2ecf20Sopenharmony_ci}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci/*
5378c2ecf20Sopenharmony_ci * attach a cache object to a cookie
5388c2ecf20Sopenharmony_ci */
5398c2ecf20Sopenharmony_cistatic int fscache_attach_object(struct fscache_cookie *cookie,
5408c2ecf20Sopenharmony_ci				 struct fscache_object *object)
5418c2ecf20Sopenharmony_ci{
5428c2ecf20Sopenharmony_ci	struct fscache_object *p;
5438c2ecf20Sopenharmony_ci	struct fscache_cache *cache = object->cache;
5448c2ecf20Sopenharmony_ci	int ret;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	_enter("{%s},{OBJ%x}", cookie->def->name, object->debug_id);
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	ASSERTCMP(object->cookie, ==, cookie);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	spin_lock(&cookie->lock);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	/* there may be multiple initial creations of this object, but we only
5538c2ecf20Sopenharmony_ci	 * want one */
5548c2ecf20Sopenharmony_ci	ret = -EEXIST;
5558c2ecf20Sopenharmony_ci	hlist_for_each_entry(p, &cookie->backing_objects, cookie_link) {
5568c2ecf20Sopenharmony_ci		if (p->cache == object->cache) {
5578c2ecf20Sopenharmony_ci			if (fscache_object_is_dying(p))
5588c2ecf20Sopenharmony_ci				ret = -ENOBUFS;
5598c2ecf20Sopenharmony_ci			goto cant_attach_object;
5608c2ecf20Sopenharmony_ci		}
5618c2ecf20Sopenharmony_ci	}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	/* pin the parent object */
5648c2ecf20Sopenharmony_ci	spin_lock_nested(&cookie->parent->lock, 1);
5658c2ecf20Sopenharmony_ci	hlist_for_each_entry(p, &cookie->parent->backing_objects,
5668c2ecf20Sopenharmony_ci			     cookie_link) {
5678c2ecf20Sopenharmony_ci		if (p->cache == object->cache) {
5688c2ecf20Sopenharmony_ci			if (fscache_object_is_dying(p)) {
5698c2ecf20Sopenharmony_ci				ret = -ENOBUFS;
5708c2ecf20Sopenharmony_ci				spin_unlock(&cookie->parent->lock);
5718c2ecf20Sopenharmony_ci				goto cant_attach_object;
5728c2ecf20Sopenharmony_ci			}
5738c2ecf20Sopenharmony_ci			object->parent = p;
5748c2ecf20Sopenharmony_ci			spin_lock(&p->lock);
5758c2ecf20Sopenharmony_ci			p->n_children++;
5768c2ecf20Sopenharmony_ci			spin_unlock(&p->lock);
5778c2ecf20Sopenharmony_ci			break;
5788c2ecf20Sopenharmony_ci		}
5798c2ecf20Sopenharmony_ci	}
5808c2ecf20Sopenharmony_ci	spin_unlock(&cookie->parent->lock);
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	/* attach to the cache's object list */
5838c2ecf20Sopenharmony_ci	if (list_empty(&object->cache_link)) {
5848c2ecf20Sopenharmony_ci		spin_lock(&cache->object_list_lock);
5858c2ecf20Sopenharmony_ci		list_add(&object->cache_link, &cache->object_list);
5868c2ecf20Sopenharmony_ci		spin_unlock(&cache->object_list_lock);
5878c2ecf20Sopenharmony_ci	}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	/* Attach to the cookie.  The object already has a ref on it. */
5908c2ecf20Sopenharmony_ci	hlist_add_head(&object->cookie_link, &cookie->backing_objects);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	fscache_objlist_add(object);
5938c2ecf20Sopenharmony_ci	ret = 0;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_cicant_attach_object:
5968c2ecf20Sopenharmony_ci	spin_unlock(&cookie->lock);
5978c2ecf20Sopenharmony_ci	_leave(" = %d", ret);
5988c2ecf20Sopenharmony_ci	return ret;
5998c2ecf20Sopenharmony_ci}
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci/*
6028c2ecf20Sopenharmony_ci * Invalidate an object.  Callable with spinlocks held.
6038c2ecf20Sopenharmony_ci */
6048c2ecf20Sopenharmony_civoid __fscache_invalidate(struct fscache_cookie *cookie)
6058c2ecf20Sopenharmony_ci{
6068c2ecf20Sopenharmony_ci	struct fscache_object *object;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	_enter("{%s}", cookie->def->name);
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	fscache_stat(&fscache_n_invalidates);
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	/* Only permit invalidation of data files.  Invalidating an index will
6138c2ecf20Sopenharmony_ci	 * require the caller to release all its attachments to the tree rooted
6148c2ecf20Sopenharmony_ci	 * there, and if it's doing that, it may as well just retire the
6158c2ecf20Sopenharmony_ci	 * cookie.
6168c2ecf20Sopenharmony_ci	 */
6178c2ecf20Sopenharmony_ci	ASSERTCMP(cookie->type, ==, FSCACHE_COOKIE_TYPE_DATAFILE);
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	/* If there's an object, we tell the object state machine to handle the
6208c2ecf20Sopenharmony_ci	 * invalidation on our behalf, otherwise there's nothing to do.
6218c2ecf20Sopenharmony_ci	 */
6228c2ecf20Sopenharmony_ci	if (!hlist_empty(&cookie->backing_objects)) {
6238c2ecf20Sopenharmony_ci		spin_lock(&cookie->lock);
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci		if (fscache_cookie_enabled(cookie) &&
6268c2ecf20Sopenharmony_ci		    !hlist_empty(&cookie->backing_objects) &&
6278c2ecf20Sopenharmony_ci		    !test_and_set_bit(FSCACHE_COOKIE_INVALIDATING,
6288c2ecf20Sopenharmony_ci				      &cookie->flags)) {
6298c2ecf20Sopenharmony_ci			object = hlist_entry(cookie->backing_objects.first,
6308c2ecf20Sopenharmony_ci					     struct fscache_object,
6318c2ecf20Sopenharmony_ci					     cookie_link);
6328c2ecf20Sopenharmony_ci			if (fscache_object_is_live(object))
6338c2ecf20Sopenharmony_ci				fscache_raise_event(
6348c2ecf20Sopenharmony_ci					object, FSCACHE_OBJECT_EV_INVALIDATE);
6358c2ecf20Sopenharmony_ci		}
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci		spin_unlock(&cookie->lock);
6388c2ecf20Sopenharmony_ci	}
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	_leave("");
6418c2ecf20Sopenharmony_ci}
6428c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__fscache_invalidate);
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci/*
6458c2ecf20Sopenharmony_ci * Wait for object invalidation to complete.
6468c2ecf20Sopenharmony_ci */
6478c2ecf20Sopenharmony_civoid __fscache_wait_on_invalidate(struct fscache_cookie *cookie)
6488c2ecf20Sopenharmony_ci{
6498c2ecf20Sopenharmony_ci	_enter("%p", cookie);
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	wait_on_bit(&cookie->flags, FSCACHE_COOKIE_INVALIDATING,
6528c2ecf20Sopenharmony_ci		    TASK_UNINTERRUPTIBLE);
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	_leave("");
6558c2ecf20Sopenharmony_ci}
6568c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__fscache_wait_on_invalidate);
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci/*
6598c2ecf20Sopenharmony_ci * update the index entries backing a cookie
6608c2ecf20Sopenharmony_ci */
6618c2ecf20Sopenharmony_civoid __fscache_update_cookie(struct fscache_cookie *cookie, const void *aux_data)
6628c2ecf20Sopenharmony_ci{
6638c2ecf20Sopenharmony_ci	struct fscache_object *object;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	fscache_stat(&fscache_n_updates);
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	if (!cookie) {
6688c2ecf20Sopenharmony_ci		fscache_stat(&fscache_n_updates_null);
6698c2ecf20Sopenharmony_ci		_leave(" [no cookie]");
6708c2ecf20Sopenharmony_ci		return;
6718c2ecf20Sopenharmony_ci	}
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	_enter("{%s}", cookie->def->name);
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	spin_lock(&cookie->lock);
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	fscache_update_aux(cookie, aux_data);
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	if (fscache_cookie_enabled(cookie)) {
6808c2ecf20Sopenharmony_ci		/* update the index entry on disk in each cache backing this
6818c2ecf20Sopenharmony_ci		 * cookie.
6828c2ecf20Sopenharmony_ci		 */
6838c2ecf20Sopenharmony_ci		hlist_for_each_entry(object,
6848c2ecf20Sopenharmony_ci				     &cookie->backing_objects, cookie_link) {
6858c2ecf20Sopenharmony_ci			fscache_raise_event(object, FSCACHE_OBJECT_EV_UPDATE);
6868c2ecf20Sopenharmony_ci		}
6878c2ecf20Sopenharmony_ci	}
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	spin_unlock(&cookie->lock);
6908c2ecf20Sopenharmony_ci	_leave("");
6918c2ecf20Sopenharmony_ci}
6928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__fscache_update_cookie);
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci/*
6958c2ecf20Sopenharmony_ci * Disable a cookie to stop it from accepting new requests from the netfs.
6968c2ecf20Sopenharmony_ci */
6978c2ecf20Sopenharmony_civoid __fscache_disable_cookie(struct fscache_cookie *cookie,
6988c2ecf20Sopenharmony_ci			      const void *aux_data,
6998c2ecf20Sopenharmony_ci			      bool invalidate)
7008c2ecf20Sopenharmony_ci{
7018c2ecf20Sopenharmony_ci	struct fscache_object *object;
7028c2ecf20Sopenharmony_ci	bool awaken = false;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	_enter("%p,%u", cookie, invalidate);
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	trace_fscache_disable(cookie);
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	ASSERTCMP(atomic_read(&cookie->n_active), >, 0);
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	if (atomic_read(&cookie->n_children) != 0) {
7118c2ecf20Sopenharmony_ci		pr_err("Cookie '%s' still has children\n",
7128c2ecf20Sopenharmony_ci		       cookie->def->name);
7138c2ecf20Sopenharmony_ci		BUG();
7148c2ecf20Sopenharmony_ci	}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	wait_on_bit_lock(&cookie->flags, FSCACHE_COOKIE_ENABLEMENT_LOCK,
7178c2ecf20Sopenharmony_ci			 TASK_UNINTERRUPTIBLE);
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	fscache_update_aux(cookie, aux_data);
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	if (!test_and_clear_bit(FSCACHE_COOKIE_ENABLED, &cookie->flags))
7228c2ecf20Sopenharmony_ci		goto out_unlock_enable;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	/* If the cookie is being invalidated, wait for that to complete first
7258c2ecf20Sopenharmony_ci	 * so that we can reuse the flag.
7268c2ecf20Sopenharmony_ci	 */
7278c2ecf20Sopenharmony_ci	__fscache_wait_on_invalidate(cookie);
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	/* Dispose of the backing objects */
7308c2ecf20Sopenharmony_ci	set_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags);
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	spin_lock(&cookie->lock);
7338c2ecf20Sopenharmony_ci	if (!hlist_empty(&cookie->backing_objects)) {
7348c2ecf20Sopenharmony_ci		hlist_for_each_entry(object, &cookie->backing_objects, cookie_link) {
7358c2ecf20Sopenharmony_ci			if (invalidate)
7368c2ecf20Sopenharmony_ci				set_bit(FSCACHE_OBJECT_RETIRED, &object->flags);
7378c2ecf20Sopenharmony_ci			clear_bit(FSCACHE_OBJECT_PENDING_WRITE, &object->flags);
7388c2ecf20Sopenharmony_ci			fscache_raise_event(object, FSCACHE_OBJECT_EV_KILL);
7398c2ecf20Sopenharmony_ci		}
7408c2ecf20Sopenharmony_ci	} else {
7418c2ecf20Sopenharmony_ci		if (test_and_clear_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags))
7428c2ecf20Sopenharmony_ci			awaken = true;
7438c2ecf20Sopenharmony_ci	}
7448c2ecf20Sopenharmony_ci	spin_unlock(&cookie->lock);
7458c2ecf20Sopenharmony_ci	if (awaken)
7468c2ecf20Sopenharmony_ci		wake_up_bit(&cookie->flags, FSCACHE_COOKIE_INVALIDATING);
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	/* Wait for cessation of activity requiring access to the netfs (when
7498c2ecf20Sopenharmony_ci	 * n_active reaches 0).  This makes sure outstanding reads and writes
7508c2ecf20Sopenharmony_ci	 * have completed.
7518c2ecf20Sopenharmony_ci	 */
7528c2ecf20Sopenharmony_ci	if (!atomic_dec_and_test(&cookie->n_active)) {
7538c2ecf20Sopenharmony_ci		wait_var_event(&cookie->n_active,
7548c2ecf20Sopenharmony_ci			       !atomic_read(&cookie->n_active));
7558c2ecf20Sopenharmony_ci	}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	/* Make sure any pending writes are cancelled. */
7588c2ecf20Sopenharmony_ci	if (cookie->type != FSCACHE_COOKIE_TYPE_INDEX)
7598c2ecf20Sopenharmony_ci		fscache_invalidate_writes(cookie);
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	/* Reset the cookie state if it wasn't relinquished */
7628c2ecf20Sopenharmony_ci	if (!test_bit(FSCACHE_COOKIE_RELINQUISHED, &cookie->flags)) {
7638c2ecf20Sopenharmony_ci		atomic_inc(&cookie->n_active);
7648c2ecf20Sopenharmony_ci		set_bit(FSCACHE_COOKIE_NO_DATA_YET, &cookie->flags);
7658c2ecf20Sopenharmony_ci	}
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ciout_unlock_enable:
7688c2ecf20Sopenharmony_ci	clear_bit_unlock(FSCACHE_COOKIE_ENABLEMENT_LOCK, &cookie->flags);
7698c2ecf20Sopenharmony_ci	wake_up_bit(&cookie->flags, FSCACHE_COOKIE_ENABLEMENT_LOCK);
7708c2ecf20Sopenharmony_ci	_leave("");
7718c2ecf20Sopenharmony_ci}
7728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__fscache_disable_cookie);
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci/*
7758c2ecf20Sopenharmony_ci * release a cookie back to the cache
7768c2ecf20Sopenharmony_ci * - the object will be marked as recyclable on disk if retire is true
7778c2ecf20Sopenharmony_ci * - all dependents of this cookie must have already been unregistered
7788c2ecf20Sopenharmony_ci *   (indices/files/pages)
7798c2ecf20Sopenharmony_ci */
7808c2ecf20Sopenharmony_civoid __fscache_relinquish_cookie(struct fscache_cookie *cookie,
7818c2ecf20Sopenharmony_ci				 const void *aux_data,
7828c2ecf20Sopenharmony_ci				 bool retire)
7838c2ecf20Sopenharmony_ci{
7848c2ecf20Sopenharmony_ci	fscache_stat(&fscache_n_relinquishes);
7858c2ecf20Sopenharmony_ci	if (retire)
7868c2ecf20Sopenharmony_ci		fscache_stat(&fscache_n_relinquishes_retire);
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	if (!cookie) {
7898c2ecf20Sopenharmony_ci		fscache_stat(&fscache_n_relinquishes_null);
7908c2ecf20Sopenharmony_ci		_leave(" [no cookie]");
7918c2ecf20Sopenharmony_ci		return;
7928c2ecf20Sopenharmony_ci	}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	_enter("%p{%s,%p,%d},%d",
7958c2ecf20Sopenharmony_ci	       cookie, cookie->def->name, cookie->netfs_data,
7968c2ecf20Sopenharmony_ci	       atomic_read(&cookie->n_active), retire);
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	trace_fscache_relinquish(cookie, retire);
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	/* No further netfs-accessing operations on this cookie permitted */
8018c2ecf20Sopenharmony_ci	if (test_and_set_bit(FSCACHE_COOKIE_RELINQUISHED, &cookie->flags))
8028c2ecf20Sopenharmony_ci		BUG();
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	__fscache_disable_cookie(cookie, aux_data, retire);
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	/* Clear pointers back to the netfs */
8078c2ecf20Sopenharmony_ci	cookie->netfs_data	= NULL;
8088c2ecf20Sopenharmony_ci	cookie->def		= NULL;
8098c2ecf20Sopenharmony_ci	BUG_ON(!radix_tree_empty(&cookie->stores));
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	if (cookie->parent) {
8128c2ecf20Sopenharmony_ci		ASSERTCMP(atomic_read(&cookie->parent->usage), >, 0);
8138c2ecf20Sopenharmony_ci		ASSERTCMP(atomic_read(&cookie->parent->n_children), >, 0);
8148c2ecf20Sopenharmony_ci		atomic_dec(&cookie->parent->n_children);
8158c2ecf20Sopenharmony_ci	}
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	/* Dispose of the netfs's link to the cookie */
8188c2ecf20Sopenharmony_ci	ASSERTCMP(atomic_read(&cookie->usage), >, 0);
8198c2ecf20Sopenharmony_ci	fscache_cookie_put(cookie, fscache_cookie_put_relinquish);
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	_leave("");
8228c2ecf20Sopenharmony_ci}
8238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__fscache_relinquish_cookie);
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci/*
8268c2ecf20Sopenharmony_ci * Remove a cookie from the hash table.
8278c2ecf20Sopenharmony_ci */
8288c2ecf20Sopenharmony_cistatic void fscache_unhash_cookie(struct fscache_cookie *cookie)
8298c2ecf20Sopenharmony_ci{
8308c2ecf20Sopenharmony_ci	struct hlist_bl_head *h;
8318c2ecf20Sopenharmony_ci	unsigned int bucket;
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	bucket = cookie->key_hash & (ARRAY_SIZE(fscache_cookie_hash) - 1);
8348c2ecf20Sopenharmony_ci	h = &fscache_cookie_hash[bucket];
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	hlist_bl_lock(h);
8378c2ecf20Sopenharmony_ci	hlist_bl_del(&cookie->hash_link);
8388c2ecf20Sopenharmony_ci	hlist_bl_unlock(h);
8398c2ecf20Sopenharmony_ci}
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci/*
8428c2ecf20Sopenharmony_ci * Drop a reference to a cookie.
8438c2ecf20Sopenharmony_ci */
8448c2ecf20Sopenharmony_civoid fscache_cookie_put(struct fscache_cookie *cookie,
8458c2ecf20Sopenharmony_ci			enum fscache_cookie_trace where)
8468c2ecf20Sopenharmony_ci{
8478c2ecf20Sopenharmony_ci	struct fscache_cookie *parent;
8488c2ecf20Sopenharmony_ci	int usage;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	_enter("%p", cookie);
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	do {
8538c2ecf20Sopenharmony_ci		usage = atomic_dec_return(&cookie->usage);
8548c2ecf20Sopenharmony_ci		trace_fscache_cookie(cookie, where, usage);
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci		if (usage > 0)
8578c2ecf20Sopenharmony_ci			return;
8588c2ecf20Sopenharmony_ci		BUG_ON(usage < 0);
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci		parent = cookie->parent;
8618c2ecf20Sopenharmony_ci		fscache_unhash_cookie(cookie);
8628c2ecf20Sopenharmony_ci		fscache_free_cookie(cookie);
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci		cookie = parent;
8658c2ecf20Sopenharmony_ci		where = fscache_cookie_put_parent;
8668c2ecf20Sopenharmony_ci	} while (cookie);
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	_leave("");
8698c2ecf20Sopenharmony_ci}
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci/*
8728c2ecf20Sopenharmony_ci * check the consistency between the netfs inode and the backing cache
8738c2ecf20Sopenharmony_ci *
8748c2ecf20Sopenharmony_ci * NOTE: it only serves no-index type
8758c2ecf20Sopenharmony_ci */
8768c2ecf20Sopenharmony_ciint __fscache_check_consistency(struct fscache_cookie *cookie,
8778c2ecf20Sopenharmony_ci				const void *aux_data)
8788c2ecf20Sopenharmony_ci{
8798c2ecf20Sopenharmony_ci	struct fscache_operation *op;
8808c2ecf20Sopenharmony_ci	struct fscache_object *object;
8818c2ecf20Sopenharmony_ci	bool wake_cookie = false;
8828c2ecf20Sopenharmony_ci	int ret;
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	_enter("%p,", cookie);
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	ASSERTCMP(cookie->type, ==, FSCACHE_COOKIE_TYPE_DATAFILE);
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	if (fscache_wait_for_deferred_lookup(cookie) < 0)
8898c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	if (hlist_empty(&cookie->backing_objects))
8928c2ecf20Sopenharmony_ci		return 0;
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	op = kzalloc(sizeof(*op), GFP_NOIO | __GFP_NOMEMALLOC | __GFP_NORETRY);
8958c2ecf20Sopenharmony_ci	if (!op)
8968c2ecf20Sopenharmony_ci		return -ENOMEM;
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	fscache_operation_init(cookie, op, NULL, NULL, NULL);
8998c2ecf20Sopenharmony_ci	op->flags = FSCACHE_OP_MYTHREAD |
9008c2ecf20Sopenharmony_ci		(1 << FSCACHE_OP_WAITING) |
9018c2ecf20Sopenharmony_ci		(1 << FSCACHE_OP_UNUSE_COOKIE);
9028c2ecf20Sopenharmony_ci	trace_fscache_page_op(cookie, NULL, op, fscache_page_op_check_consistency);
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	spin_lock(&cookie->lock);
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	fscache_update_aux(cookie, aux_data);
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	if (!fscache_cookie_enabled(cookie) ||
9098c2ecf20Sopenharmony_ci	    hlist_empty(&cookie->backing_objects))
9108c2ecf20Sopenharmony_ci		goto inconsistent;
9118c2ecf20Sopenharmony_ci	object = hlist_entry(cookie->backing_objects.first,
9128c2ecf20Sopenharmony_ci			     struct fscache_object, cookie_link);
9138c2ecf20Sopenharmony_ci	if (test_bit(FSCACHE_IOERROR, &object->cache->flags))
9148c2ecf20Sopenharmony_ci		goto inconsistent;
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	op->debug_id = atomic_inc_return(&fscache_op_debug_id);
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci	__fscache_use_cookie(cookie);
9198c2ecf20Sopenharmony_ci	if (fscache_submit_op(object, op) < 0)
9208c2ecf20Sopenharmony_ci		goto submit_failed;
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	/* the work queue now carries its own ref on the object */
9238c2ecf20Sopenharmony_ci	spin_unlock(&cookie->lock);
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	ret = fscache_wait_for_operation_activation(object, op, NULL, NULL);
9268c2ecf20Sopenharmony_ci	if (ret == 0) {
9278c2ecf20Sopenharmony_ci		/* ask the cache to honour the operation */
9288c2ecf20Sopenharmony_ci		ret = object->cache->ops->check_consistency(op);
9298c2ecf20Sopenharmony_ci		fscache_op_complete(op, false);
9308c2ecf20Sopenharmony_ci	} else if (ret == -ENOBUFS) {
9318c2ecf20Sopenharmony_ci		ret = 0;
9328c2ecf20Sopenharmony_ci	}
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	fscache_put_operation(op);
9358c2ecf20Sopenharmony_ci	_leave(" = %d", ret);
9368c2ecf20Sopenharmony_ci	return ret;
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_cisubmit_failed:
9398c2ecf20Sopenharmony_ci	wake_cookie = __fscache_unuse_cookie(cookie);
9408c2ecf20Sopenharmony_ciinconsistent:
9418c2ecf20Sopenharmony_ci	spin_unlock(&cookie->lock);
9428c2ecf20Sopenharmony_ci	if (wake_cookie)
9438c2ecf20Sopenharmony_ci		__fscache_wake_unused_cookie(cookie);
9448c2ecf20Sopenharmony_ci	kfree(op);
9458c2ecf20Sopenharmony_ci	_leave(" = -ESTALE");
9468c2ecf20Sopenharmony_ci	return -ESTALE;
9478c2ecf20Sopenharmony_ci}
9488c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__fscache_check_consistency);
949