cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cache.c (12422B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/* FS-Cache cache handling
      3 *
      4 * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved.
      5 * Written by David Howells (dhowells@redhat.com)
      6 */
      7
      8#define FSCACHE_DEBUG_LEVEL CACHE
      9#include <linux/export.h>
     10#include <linux/slab.h>
     11#include "internal.h"
     12
     13static LIST_HEAD(fscache_caches);
     14DECLARE_RWSEM(fscache_addremove_sem);
     15EXPORT_SYMBOL(fscache_addremove_sem);
     16DECLARE_WAIT_QUEUE_HEAD(fscache_clearance_waiters);
     17EXPORT_SYMBOL(fscache_clearance_waiters);
     18
     19static atomic_t fscache_cache_debug_id;
     20
     21/*
     22 * Allocate a cache cookie.
     23 */
     24static struct fscache_cache *fscache_alloc_cache(const char *name)
     25{
     26	struct fscache_cache *cache;
     27
     28	cache = kzalloc(sizeof(*cache), GFP_KERNEL);
     29	if (cache) {
     30		if (name) {
     31			cache->name = kstrdup(name, GFP_KERNEL);
     32			if (!cache->name) {
     33				kfree(cache);
     34				return NULL;
     35			}
     36		}
     37		refcount_set(&cache->ref, 1);
     38		INIT_LIST_HEAD(&cache->cache_link);
     39		cache->debug_id = atomic_inc_return(&fscache_cache_debug_id);
     40	}
     41	return cache;
     42}
     43
     44static bool fscache_get_cache_maybe(struct fscache_cache *cache,
     45				    enum fscache_cache_trace where)
     46{
     47	bool success;
     48	int ref;
     49
     50	success = __refcount_inc_not_zero(&cache->ref, &ref);
     51	if (success)
     52		trace_fscache_cache(cache->debug_id, ref + 1, where);
     53	return success;
     54}
     55
     56/*
     57 * Look up a cache cookie.
     58 */
     59struct fscache_cache *fscache_lookup_cache(const char *name, bool is_cache)
     60{
     61	struct fscache_cache *candidate, *cache, *unnamed = NULL;
     62
     63	/* firstly check for the existence of the cache under read lock */
     64	down_read(&fscache_addremove_sem);
     65
     66	list_for_each_entry(cache, &fscache_caches, cache_link) {
     67		if (cache->name && name && strcmp(cache->name, name) == 0 &&
     68		    fscache_get_cache_maybe(cache, fscache_cache_get_acquire))
     69			goto got_cache_r;
     70		if (!cache->name && !name &&
     71		    fscache_get_cache_maybe(cache, fscache_cache_get_acquire))
     72			goto got_cache_r;
     73	}
     74
     75	if (!name) {
     76		list_for_each_entry(cache, &fscache_caches, cache_link) {
     77			if (cache->name &&
     78			    fscache_get_cache_maybe(cache, fscache_cache_get_acquire))
     79				goto got_cache_r;
     80		}
     81	}
     82
     83	up_read(&fscache_addremove_sem);
     84
     85	/* the cache does not exist - create a candidate */
     86	candidate = fscache_alloc_cache(name);
     87	if (!candidate)
     88		return ERR_PTR(-ENOMEM);
     89
     90	/* write lock, search again and add if still not present */
     91	down_write(&fscache_addremove_sem);
     92
     93	list_for_each_entry(cache, &fscache_caches, cache_link) {
     94		if (cache->name && name && strcmp(cache->name, name) == 0 &&
     95		    fscache_get_cache_maybe(cache, fscache_cache_get_acquire))
     96			goto got_cache_w;
     97		if (!cache->name) {
     98			unnamed = cache;
     99			if (!name &&
    100			    fscache_get_cache_maybe(cache, fscache_cache_get_acquire))
    101				goto got_cache_w;
    102		}
    103	}
    104
    105	if (unnamed && is_cache &&
    106	    fscache_get_cache_maybe(unnamed, fscache_cache_get_acquire))
    107		goto use_unnamed_cache;
    108
    109	if (!name) {
    110		list_for_each_entry(cache, &fscache_caches, cache_link) {
    111			if (cache->name &&
    112			    fscache_get_cache_maybe(cache, fscache_cache_get_acquire))
    113				goto got_cache_w;
    114		}
    115	}
    116
    117	list_add_tail(&candidate->cache_link, &fscache_caches);
    118	trace_fscache_cache(candidate->debug_id,
    119			    refcount_read(&candidate->ref),
    120			    fscache_cache_new_acquire);
    121	up_write(&fscache_addremove_sem);
    122	return candidate;
    123
    124got_cache_r:
    125	up_read(&fscache_addremove_sem);
    126	return cache;
    127use_unnamed_cache:
    128	cache = unnamed;
    129	cache->name = candidate->name;
    130	candidate->name = NULL;
    131got_cache_w:
    132	up_write(&fscache_addremove_sem);
    133	kfree(candidate->name);
    134	kfree(candidate);
    135	return cache;
    136}
    137
    138/**
    139 * fscache_acquire_cache - Acquire a cache-level cookie.
    140 * @name: The name of the cache.
    141 *
    142 * Get a cookie to represent an actual cache.  If a name is given and there is
    143 * a nameless cache record available, this will acquire that and set its name,
    144 * directing all the volumes using it to this cache.
    145 *
    146 * The cache will be switched over to the preparing state if not currently in
    147 * use, otherwise -EBUSY will be returned.
    148 */
    149struct fscache_cache *fscache_acquire_cache(const char *name)
    150{
    151	struct fscache_cache *cache;
    152
    153	ASSERT(name);
    154	cache = fscache_lookup_cache(name, true);
    155	if (IS_ERR(cache))
    156		return cache;
    157
    158	if (!fscache_set_cache_state_maybe(cache,
    159					   FSCACHE_CACHE_IS_NOT_PRESENT,
    160					   FSCACHE_CACHE_IS_PREPARING)) {
    161		pr_warn("Cache tag %s in use\n", name);
    162		fscache_put_cache(cache, fscache_cache_put_cache);
    163		return ERR_PTR(-EBUSY);
    164	}
    165
    166	return cache;
    167}
    168EXPORT_SYMBOL(fscache_acquire_cache);
    169
    170/**
    171 * fscache_put_cache - Release a cache-level cookie.
    172 * @cache: The cache cookie to be released
    173 * @where: An indication of where the release happened
    174 *
    175 * Release the caller's reference on a cache-level cookie.  The @where
    176 * indication should give information about the circumstances in which the call
    177 * occurs and will be logged through a tracepoint.
    178 */
    179void fscache_put_cache(struct fscache_cache *cache,
    180		       enum fscache_cache_trace where)
    181{
    182	unsigned int debug_id = cache->debug_id;
    183	bool zero;
    184	int ref;
    185
    186	if (IS_ERR_OR_NULL(cache))
    187		return;
    188
    189	zero = __refcount_dec_and_test(&cache->ref, &ref);
    190	trace_fscache_cache(debug_id, ref - 1, where);
    191
    192	if (zero) {
    193		down_write(&fscache_addremove_sem);
    194		list_del_init(&cache->cache_link);
    195		up_write(&fscache_addremove_sem);
    196		kfree(cache->name);
    197		kfree(cache);
    198	}
    199}
    200
    201/**
    202 * fscache_relinquish_cache - Reset cache state and release cookie
    203 * @cache: The cache cookie to be released
    204 *
    205 * Reset the state of a cache and release the caller's reference on a cache
    206 * cookie.
    207 */
    208void fscache_relinquish_cache(struct fscache_cache *cache)
    209{
    210	enum fscache_cache_trace where =
    211		(cache->state == FSCACHE_CACHE_IS_PREPARING) ?
    212		fscache_cache_put_prep_failed :
    213		fscache_cache_put_relinquish;
    214
    215	cache->ops = NULL;
    216	cache->cache_priv = NULL;
    217	fscache_set_cache_state(cache, FSCACHE_CACHE_IS_NOT_PRESENT);
    218	fscache_put_cache(cache, where);
    219}
    220EXPORT_SYMBOL(fscache_relinquish_cache);
    221
    222/**
    223 * fscache_add_cache - Declare a cache as being open for business
    224 * @cache: The cache-level cookie representing the cache
    225 * @ops: Table of cache operations to use
    226 * @cache_priv: Private data for the cache record
    227 *
    228 * Add a cache to the system, making it available for netfs's to use.
    229 *
    230 * See Documentation/filesystems/caching/backend-api.rst for a complete
    231 * description.
    232 */
    233int fscache_add_cache(struct fscache_cache *cache,
    234		      const struct fscache_cache_ops *ops,
    235		      void *cache_priv)
    236{
    237	int n_accesses;
    238
    239	_enter("{%s,%s}", ops->name, cache->name);
    240
    241	BUG_ON(fscache_cache_state(cache) != FSCACHE_CACHE_IS_PREPARING);
    242
    243	/* Get a ref on the cache cookie and keep its n_accesses counter raised
    244	 * by 1 to prevent wakeups from transitioning it to 0 until we're
    245	 * withdrawing caching services from it.
    246	 */
    247	n_accesses = atomic_inc_return(&cache->n_accesses);
    248	trace_fscache_access_cache(cache->debug_id, refcount_read(&cache->ref),
    249				   n_accesses, fscache_access_cache_pin);
    250
    251	down_write(&fscache_addremove_sem);
    252
    253	cache->ops = ops;
    254	cache->cache_priv = cache_priv;
    255	fscache_set_cache_state(cache, FSCACHE_CACHE_IS_ACTIVE);
    256
    257	up_write(&fscache_addremove_sem);
    258	pr_notice("Cache \"%s\" added (type %s)\n", cache->name, ops->name);
    259	_leave(" = 0 [%s]", cache->name);
    260	return 0;
    261}
    262EXPORT_SYMBOL(fscache_add_cache);
    263
    264/**
    265 * fscache_begin_cache_access - Pin a cache so it can be accessed
    266 * @cache: The cache-level cookie
    267 * @why: An indication of the circumstances of the access for tracing
    268 *
    269 * Attempt to pin the cache to prevent it from going away whilst we're
    270 * accessing it and returns true if successful.  This works as follows:
    271 *
    272 *  (1) If the cache tests as not live (state is not FSCACHE_CACHE_IS_ACTIVE),
    273 *      then we return false to indicate access was not permitted.
    274 *
    275 *  (2) If the cache tests as live, then we increment the n_accesses count and
    276 *      then recheck the liveness, ending the access if it ceased to be live.
    277 *
    278 *  (3) When we end the access, we decrement n_accesses and wake up the any
    279 *      waiters if it reaches 0.
    280 *
    281 *  (4) Whilst the cache is caching, n_accesses is kept artificially
    282 *      incremented to prevent wakeups from happening.
    283 *
    284 *  (5) When the cache is taken offline, the state is changed to prevent new
    285 *      accesses, n_accesses is decremented and we wait for n_accesses to
    286 *      become 0.
    287 */
    288bool fscache_begin_cache_access(struct fscache_cache *cache, enum fscache_access_trace why)
    289{
    290	int n_accesses;
    291
    292	if (!fscache_cache_is_live(cache))
    293		return false;
    294
    295	n_accesses = atomic_inc_return(&cache->n_accesses);
    296	smp_mb__after_atomic(); /* Reread live flag after n_accesses */
    297	trace_fscache_access_cache(cache->debug_id, refcount_read(&cache->ref),
    298				   n_accesses, why);
    299	if (!fscache_cache_is_live(cache)) {
    300		fscache_end_cache_access(cache, fscache_access_unlive);
    301		return false;
    302	}
    303	return true;
    304}
    305
    306/**
    307 * fscache_end_cache_access - Unpin a cache at the end of an access.
    308 * @cache: The cache-level cookie
    309 * @why: An indication of the circumstances of the access for tracing
    310 *
    311 * Unpin a cache after we've accessed it.  The @why indicator is merely
    312 * provided for tracing purposes.
    313 */
    314void fscache_end_cache_access(struct fscache_cache *cache, enum fscache_access_trace why)
    315{
    316	int n_accesses;
    317
    318	smp_mb__before_atomic();
    319	n_accesses = atomic_dec_return(&cache->n_accesses);
    320	trace_fscache_access_cache(cache->debug_id, refcount_read(&cache->ref),
    321				   n_accesses, why);
    322	if (n_accesses == 0)
    323		wake_up_var(&cache->n_accesses);
    324}
    325
    326/**
    327 * fscache_io_error - Note a cache I/O error
    328 * @cache: The record describing the cache
    329 *
    330 * Note that an I/O error occurred in a cache and that it should no longer be
    331 * used for anything.  This also reports the error into the kernel log.
    332 *
    333 * See Documentation/filesystems/caching/backend-api.rst for a complete
    334 * description.
    335 */
    336void fscache_io_error(struct fscache_cache *cache)
    337{
    338	if (fscache_set_cache_state_maybe(cache,
    339					  FSCACHE_CACHE_IS_ACTIVE,
    340					  FSCACHE_CACHE_GOT_IOERROR))
    341		pr_err("Cache '%s' stopped due to I/O error\n",
    342		       cache->name);
    343}
    344EXPORT_SYMBOL(fscache_io_error);
    345
    346/**
    347 * fscache_withdraw_cache - Withdraw a cache from the active service
    348 * @cache: The cache cookie
    349 *
    350 * Begin the process of withdrawing a cache from service.  This stops new
    351 * cache-level and volume-level accesses from taking place and waits for
    352 * currently ongoing cache-level accesses to end.
    353 */
    354void fscache_withdraw_cache(struct fscache_cache *cache)
    355{
    356	int n_accesses;
    357
    358	pr_notice("Withdrawing cache \"%s\" (%u objs)\n",
    359		  cache->name, atomic_read(&cache->object_count));
    360
    361	fscache_set_cache_state(cache, FSCACHE_CACHE_IS_WITHDRAWN);
    362
    363	/* Allow wakeups on dec-to-0 */
    364	n_accesses = atomic_dec_return(&cache->n_accesses);
    365	trace_fscache_access_cache(cache->debug_id, refcount_read(&cache->ref),
    366				   n_accesses, fscache_access_cache_unpin);
    367
    368	wait_var_event(&cache->n_accesses,
    369		       atomic_read(&cache->n_accesses) == 0);
    370}
    371EXPORT_SYMBOL(fscache_withdraw_cache);
    372
    373#ifdef CONFIG_PROC_FS
    374static const char fscache_cache_states[NR__FSCACHE_CACHE_STATE] = "-PAEW";
    375
    376/*
    377 * Generate a list of caches in /proc/fs/fscache/caches
    378 */
    379static int fscache_caches_seq_show(struct seq_file *m, void *v)
    380{
    381	struct fscache_cache *cache;
    382
    383	if (v == &fscache_caches) {
    384		seq_puts(m,
    385			 "CACHE    REF   VOLS  OBJS  ACCES S NAME\n"
    386			 "======== ===== ===== ===== ===== = ===============\n"
    387			 );
    388		return 0;
    389	}
    390
    391	cache = list_entry(v, struct fscache_cache, cache_link);
    392	seq_printf(m,
    393		   "%08x %5d %5d %5d %5d %c %s\n",
    394		   cache->debug_id,
    395		   refcount_read(&cache->ref),
    396		   atomic_read(&cache->n_volumes),
    397		   atomic_read(&cache->object_count),
    398		   atomic_read(&cache->n_accesses),
    399		   fscache_cache_states[cache->state],
    400		   cache->name ?: "-");
    401	return 0;
    402}
    403
    404static void *fscache_caches_seq_start(struct seq_file *m, loff_t *_pos)
    405	__acquires(fscache_addremove_sem)
    406{
    407	down_read(&fscache_addremove_sem);
    408	return seq_list_start_head(&fscache_caches, *_pos);
    409}
    410
    411static void *fscache_caches_seq_next(struct seq_file *m, void *v, loff_t *_pos)
    412{
    413	return seq_list_next(v, &fscache_caches, _pos);
    414}
    415
    416static void fscache_caches_seq_stop(struct seq_file *m, void *v)
    417	__releases(fscache_addremove_sem)
    418{
    419	up_read(&fscache_addremove_sem);
    420}
    421
    422const struct seq_operations fscache_caches_seq_ops = {
    423	.start  = fscache_caches_seq_start,
    424	.next   = fscache_caches_seq_next,
    425	.stop   = fscache_caches_seq_stop,
    426	.show   = fscache_caches_seq_show,
    427};
    428#endif /* CONFIG_PROC_FS */