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