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

volume.c (15008B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/* Volume-level cache cookie 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 COOKIE
      9#include <linux/export.h>
     10#include <linux/slab.h>
     11#include "internal.h"
     12
     13#define fscache_volume_hash_shift 10
     14static struct hlist_bl_head fscache_volume_hash[1 << fscache_volume_hash_shift];
     15static atomic_t fscache_volume_debug_id;
     16static LIST_HEAD(fscache_volumes);
     17
     18static void fscache_create_volume_work(struct work_struct *work);
     19
     20struct fscache_volume *fscache_get_volume(struct fscache_volume *volume,
     21					  enum fscache_volume_trace where)
     22{
     23	int ref;
     24
     25	__refcount_inc(&volume->ref, &ref);
     26	trace_fscache_volume(volume->debug_id, ref + 1, where);
     27	return volume;
     28}
     29
     30static void fscache_see_volume(struct fscache_volume *volume,
     31			       enum fscache_volume_trace where)
     32{
     33	int ref = refcount_read(&volume->ref);
     34
     35	trace_fscache_volume(volume->debug_id, ref, where);
     36}
     37
     38/*
     39 * Pin the cache behind a volume so that we can access it.
     40 */
     41static void __fscache_begin_volume_access(struct fscache_volume *volume,
     42					  struct fscache_cookie *cookie,
     43					  enum fscache_access_trace why)
     44{
     45	int n_accesses;
     46
     47	n_accesses = atomic_inc_return(&volume->n_accesses);
     48	smp_mb__after_atomic();
     49	trace_fscache_access_volume(volume->debug_id, cookie ? cookie->debug_id : 0,
     50				    refcount_read(&volume->ref),
     51				    n_accesses, why);
     52}
     53
     54/**
     55 * fscache_begin_volume_access - Pin a cache so a volume can be accessed
     56 * @volume: The volume cookie
     57 * @cookie: A datafile cookie for a tracing reference (or NULL)
     58 * @why: An indication of the circumstances of the access for tracing
     59 *
     60 * Attempt to pin the cache to prevent it from going away whilst we're
     61 * accessing a volume and returns true if successful.  This works as follows:
     62 *
     63 *  (1) If the cache tests as not live (state is not FSCACHE_CACHE_IS_ACTIVE),
     64 *      then we return false to indicate access was not permitted.
     65 *
     66 *  (2) If the cache tests as live, then we increment the volume's n_accesses
     67 *      count and then recheck the cache liveness, ending the access if it
     68 *      ceased to be live.
     69 *
     70 *  (3) When we end the access, we decrement the volume's n_accesses and wake
     71 *      up the any waiters if it reaches 0.
     72 *
     73 *  (4) Whilst the cache is caching, the volume's n_accesses is kept
     74 *      artificially incremented to prevent wakeups from happening.
     75 *
     76 *  (5) When the cache is taken offline, the state is changed to prevent new
     77 *      accesses, the volume's n_accesses is decremented and we wait for it to
     78 *      become 0.
     79 *
     80 * The datafile @cookie and the @why indicator are merely provided for tracing
     81 * purposes.
     82 */
     83bool fscache_begin_volume_access(struct fscache_volume *volume,
     84				 struct fscache_cookie *cookie,
     85				 enum fscache_access_trace why)
     86{
     87	if (!fscache_cache_is_live(volume->cache))
     88		return false;
     89	__fscache_begin_volume_access(volume, cookie, why);
     90	if (!fscache_cache_is_live(volume->cache)) {
     91		fscache_end_volume_access(volume, cookie, fscache_access_unlive);
     92		return false;
     93	}
     94	return true;
     95}
     96
     97/**
     98 * fscache_end_volume_access - Unpin a cache at the end of an access.
     99 * @volume: The volume cookie
    100 * @cookie: A datafile cookie for a tracing reference (or NULL)
    101 * @why: An indication of the circumstances of the access for tracing
    102 *
    103 * Unpin a cache volume after we've accessed it.  The datafile @cookie and the
    104 * @why indicator are merely provided for tracing purposes.
    105 */
    106void fscache_end_volume_access(struct fscache_volume *volume,
    107			       struct fscache_cookie *cookie,
    108			       enum fscache_access_trace why)
    109{
    110	int n_accesses;
    111
    112	smp_mb__before_atomic();
    113	n_accesses = atomic_dec_return(&volume->n_accesses);
    114	trace_fscache_access_volume(volume->debug_id, cookie ? cookie->debug_id : 0,
    115				    refcount_read(&volume->ref),
    116				    n_accesses, why);
    117	if (n_accesses == 0)
    118		wake_up_var(&volume->n_accesses);
    119}
    120EXPORT_SYMBOL(fscache_end_volume_access);
    121
    122static bool fscache_volume_same(const struct fscache_volume *a,
    123				const struct fscache_volume *b)
    124{
    125	size_t klen;
    126
    127	if (a->key_hash	!= b->key_hash ||
    128	    a->cache	!= b->cache ||
    129	    a->key[0]	!= b->key[0])
    130		return false;
    131
    132	klen = round_up(a->key[0] + 1, sizeof(__le32));
    133	return memcmp(a->key, b->key, klen) == 0;
    134}
    135
    136static bool fscache_is_acquire_pending(struct fscache_volume *volume)
    137{
    138	return test_bit(FSCACHE_VOLUME_ACQUIRE_PENDING, &volume->flags);
    139}
    140
    141static void fscache_wait_on_volume_collision(struct fscache_volume *candidate,
    142					     unsigned int collidee_debug_id)
    143{
    144	wait_var_event_timeout(&candidate->flags,
    145			       !fscache_is_acquire_pending(candidate), 20 * HZ);
    146	if (fscache_is_acquire_pending(candidate)) {
    147		pr_notice("Potential volume collision new=%08x old=%08x",
    148			  candidate->debug_id, collidee_debug_id);
    149		fscache_stat(&fscache_n_volumes_collision);
    150		wait_var_event(&candidate->flags, !fscache_is_acquire_pending(candidate));
    151	}
    152}
    153
    154/*
    155 * Attempt to insert the new volume into the hash.  If there's a collision, we
    156 * wait for the old volume to complete if it's being relinquished and an error
    157 * otherwise.
    158 */
    159static bool fscache_hash_volume(struct fscache_volume *candidate)
    160{
    161	struct fscache_volume *cursor;
    162	struct hlist_bl_head *h;
    163	struct hlist_bl_node *p;
    164	unsigned int bucket, collidee_debug_id = 0;
    165
    166	bucket = candidate->key_hash & (ARRAY_SIZE(fscache_volume_hash) - 1);
    167	h = &fscache_volume_hash[bucket];
    168
    169	hlist_bl_lock(h);
    170	hlist_bl_for_each_entry(cursor, p, h, hash_link) {
    171		if (fscache_volume_same(candidate, cursor)) {
    172			if (!test_bit(FSCACHE_VOLUME_RELINQUISHED, &cursor->flags))
    173				goto collision;
    174			fscache_see_volume(cursor, fscache_volume_get_hash_collision);
    175			set_bit(FSCACHE_VOLUME_COLLIDED_WITH, &cursor->flags);
    176			set_bit(FSCACHE_VOLUME_ACQUIRE_PENDING, &candidate->flags);
    177			collidee_debug_id = cursor->debug_id;
    178			break;
    179		}
    180	}
    181
    182	hlist_bl_add_head(&candidate->hash_link, h);
    183	hlist_bl_unlock(h);
    184
    185	if (fscache_is_acquire_pending(candidate))
    186		fscache_wait_on_volume_collision(candidate, collidee_debug_id);
    187	return true;
    188
    189collision:
    190	fscache_see_volume(cursor, fscache_volume_collision);
    191	hlist_bl_unlock(h);
    192	return false;
    193}
    194
    195/*
    196 * Allocate and initialise a volume representation cookie.
    197 */
    198static struct fscache_volume *fscache_alloc_volume(const char *volume_key,
    199						   const char *cache_name,
    200						   const void *coherency_data,
    201						   size_t coherency_len)
    202{
    203	struct fscache_volume *volume;
    204	struct fscache_cache *cache;
    205	size_t klen, hlen;
    206	char *key;
    207
    208	if (!coherency_data)
    209		coherency_len = 0;
    210
    211	cache = fscache_lookup_cache(cache_name, false);
    212	if (IS_ERR(cache))
    213		return NULL;
    214
    215	volume = kzalloc(struct_size(volume, coherency, coherency_len),
    216			 GFP_KERNEL);
    217	if (!volume)
    218		goto err_cache;
    219
    220	volume->cache = cache;
    221	volume->coherency_len = coherency_len;
    222	if (coherency_data)
    223		memcpy(volume->coherency, coherency_data, coherency_len);
    224	INIT_LIST_HEAD(&volume->proc_link);
    225	INIT_WORK(&volume->work, fscache_create_volume_work);
    226	refcount_set(&volume->ref, 1);
    227	spin_lock_init(&volume->lock);
    228
    229	/* Stick the length on the front of the key and pad it out to make
    230	 * hashing easier.
    231	 */
    232	klen = strlen(volume_key);
    233	hlen = round_up(1 + klen + 1, sizeof(__le32));
    234	key = kzalloc(hlen, GFP_KERNEL);
    235	if (!key)
    236		goto err_vol;
    237	key[0] = klen;
    238	memcpy(key + 1, volume_key, klen);
    239
    240	volume->key = key;
    241	volume->key_hash = fscache_hash(0, key, hlen);
    242
    243	volume->debug_id = atomic_inc_return(&fscache_volume_debug_id);
    244	down_write(&fscache_addremove_sem);
    245	atomic_inc(&cache->n_volumes);
    246	list_add_tail(&volume->proc_link, &fscache_volumes);
    247	fscache_see_volume(volume, fscache_volume_new_acquire);
    248	fscache_stat(&fscache_n_volumes);
    249	up_write(&fscache_addremove_sem);
    250	_leave(" = v=%x", volume->debug_id);
    251	return volume;
    252
    253err_vol:
    254	kfree(volume);
    255err_cache:
    256	fscache_put_cache(cache, fscache_cache_put_alloc_volume);
    257	fscache_stat(&fscache_n_volumes_nomem);
    258	return NULL;
    259}
    260
    261/*
    262 * Create a volume's representation on disk.  Have a volume ref and a cache
    263 * access we have to release.
    264 */
    265static void fscache_create_volume_work(struct work_struct *work)
    266{
    267	const struct fscache_cache_ops *ops;
    268	struct fscache_volume *volume =
    269		container_of(work, struct fscache_volume, work);
    270
    271	fscache_see_volume(volume, fscache_volume_see_create_work);
    272
    273	ops = volume->cache->ops;
    274	if (ops->acquire_volume)
    275		ops->acquire_volume(volume);
    276	fscache_end_cache_access(volume->cache,
    277				 fscache_access_acquire_volume_end);
    278
    279	clear_bit_unlock(FSCACHE_VOLUME_CREATING, &volume->flags);
    280	wake_up_bit(&volume->flags, FSCACHE_VOLUME_CREATING);
    281	fscache_put_volume(volume, fscache_volume_put_create_work);
    282}
    283
    284/*
    285 * Dispatch a worker thread to create a volume's representation on disk.
    286 */
    287void fscache_create_volume(struct fscache_volume *volume, bool wait)
    288{
    289	if (test_and_set_bit(FSCACHE_VOLUME_CREATING, &volume->flags))
    290		goto maybe_wait;
    291	if (volume->cache_priv)
    292		goto no_wait; /* We raced */
    293	if (!fscache_begin_cache_access(volume->cache,
    294					fscache_access_acquire_volume))
    295		goto no_wait;
    296
    297	fscache_get_volume(volume, fscache_volume_get_create_work);
    298	if (!schedule_work(&volume->work))
    299		fscache_put_volume(volume, fscache_volume_put_create_work);
    300
    301maybe_wait:
    302	if (wait) {
    303		fscache_see_volume(volume, fscache_volume_wait_create_work);
    304		wait_on_bit(&volume->flags, FSCACHE_VOLUME_CREATING,
    305			    TASK_UNINTERRUPTIBLE);
    306	}
    307	return;
    308no_wait:
    309	clear_bit_unlock(FSCACHE_VOLUME_CREATING, &volume->flags);
    310	wake_up_bit(&volume->flags, FSCACHE_VOLUME_CREATING);
    311}
    312
    313/*
    314 * Acquire a volume representation cookie and link it to a (proposed) cache.
    315 */
    316struct fscache_volume *__fscache_acquire_volume(const char *volume_key,
    317						const char *cache_name,
    318						const void *coherency_data,
    319						size_t coherency_len)
    320{
    321	struct fscache_volume *volume;
    322
    323	volume = fscache_alloc_volume(volume_key, cache_name,
    324				      coherency_data, coherency_len);
    325	if (!volume)
    326		return ERR_PTR(-ENOMEM);
    327
    328	if (!fscache_hash_volume(volume)) {
    329		fscache_put_volume(volume, fscache_volume_put_hash_collision);
    330		return ERR_PTR(-EBUSY);
    331	}
    332
    333	fscache_create_volume(volume, false);
    334	return volume;
    335}
    336EXPORT_SYMBOL(__fscache_acquire_volume);
    337
    338static void fscache_wake_pending_volume(struct fscache_volume *volume,
    339					struct hlist_bl_head *h)
    340{
    341	struct fscache_volume *cursor;
    342	struct hlist_bl_node *p;
    343
    344	hlist_bl_for_each_entry(cursor, p, h, hash_link) {
    345		if (fscache_volume_same(cursor, volume)) {
    346			fscache_see_volume(cursor, fscache_volume_see_hash_wake);
    347			clear_bit(FSCACHE_VOLUME_ACQUIRE_PENDING, &cursor->flags);
    348			wake_up_bit(&cursor->flags, FSCACHE_VOLUME_ACQUIRE_PENDING);
    349			return;
    350		}
    351	}
    352}
    353
    354/*
    355 * Remove a volume cookie from the hash table.
    356 */
    357static void fscache_unhash_volume(struct fscache_volume *volume)
    358{
    359	struct hlist_bl_head *h;
    360	unsigned int bucket;
    361
    362	bucket = volume->key_hash & (ARRAY_SIZE(fscache_volume_hash) - 1);
    363	h = &fscache_volume_hash[bucket];
    364
    365	hlist_bl_lock(h);
    366	hlist_bl_del(&volume->hash_link);
    367	if (test_bit(FSCACHE_VOLUME_COLLIDED_WITH, &volume->flags))
    368		fscache_wake_pending_volume(volume, h);
    369	hlist_bl_unlock(h);
    370}
    371
    372/*
    373 * Drop a cache's volume attachments.
    374 */
    375static void fscache_free_volume(struct fscache_volume *volume)
    376{
    377	struct fscache_cache *cache = volume->cache;
    378
    379	if (volume->cache_priv) {
    380		__fscache_begin_volume_access(volume, NULL,
    381					      fscache_access_relinquish_volume);
    382		if (volume->cache_priv)
    383			cache->ops->free_volume(volume);
    384		fscache_end_volume_access(volume, NULL,
    385					  fscache_access_relinquish_volume_end);
    386	}
    387
    388	down_write(&fscache_addremove_sem);
    389	list_del_init(&volume->proc_link);
    390	atomic_dec(&volume->cache->n_volumes);
    391	up_write(&fscache_addremove_sem);
    392
    393	if (!hlist_bl_unhashed(&volume->hash_link))
    394		fscache_unhash_volume(volume);
    395
    396	trace_fscache_volume(volume->debug_id, 0, fscache_volume_free);
    397	kfree(volume->key);
    398	kfree(volume);
    399	fscache_stat_d(&fscache_n_volumes);
    400	fscache_put_cache(cache, fscache_cache_put_volume);
    401}
    402
    403/*
    404 * Drop a reference to a volume cookie.
    405 */
    406void fscache_put_volume(struct fscache_volume *volume,
    407			enum fscache_volume_trace where)
    408{
    409	if (volume) {
    410		unsigned int debug_id = volume->debug_id;
    411		bool zero;
    412		int ref;
    413
    414		zero = __refcount_dec_and_test(&volume->ref, &ref);
    415		trace_fscache_volume(debug_id, ref - 1, where);
    416		if (zero)
    417			fscache_free_volume(volume);
    418	}
    419}
    420
    421/*
    422 * Relinquish a volume representation cookie.
    423 */
    424void __fscache_relinquish_volume(struct fscache_volume *volume,
    425				 const void *coherency_data,
    426				 bool invalidate)
    427{
    428	if (WARN_ON(test_and_set_bit(FSCACHE_VOLUME_RELINQUISHED, &volume->flags)))
    429		return;
    430
    431	if (invalidate) {
    432		set_bit(FSCACHE_VOLUME_INVALIDATE, &volume->flags);
    433	} else if (coherency_data) {
    434		memcpy(volume->coherency, coherency_data, volume->coherency_len);
    435	}
    436
    437	fscache_put_volume(volume, fscache_volume_put_relinquish);
    438}
    439EXPORT_SYMBOL(__fscache_relinquish_volume);
    440
    441/**
    442 * fscache_withdraw_volume - Withdraw a volume from being cached
    443 * @volume: Volume cookie
    444 *
    445 * Withdraw a cache volume from service, waiting for all accesses to complete
    446 * before returning.
    447 */
    448void fscache_withdraw_volume(struct fscache_volume *volume)
    449{
    450	int n_accesses;
    451
    452	_debug("withdraw V=%x", volume->debug_id);
    453
    454	/* Allow wakeups on dec-to-0 */
    455	n_accesses = atomic_dec_return(&volume->n_accesses);
    456	trace_fscache_access_volume(volume->debug_id, 0,
    457				    refcount_read(&volume->ref),
    458				    n_accesses, fscache_access_cache_unpin);
    459
    460	wait_var_event(&volume->n_accesses,
    461		       atomic_read(&volume->n_accesses) == 0);
    462}
    463EXPORT_SYMBOL(fscache_withdraw_volume);
    464
    465#ifdef CONFIG_PROC_FS
    466/*
    467 * Generate a list of volumes in /proc/fs/fscache/volumes
    468 */
    469static int fscache_volumes_seq_show(struct seq_file *m, void *v)
    470{
    471	struct fscache_volume *volume;
    472
    473	if (v == &fscache_volumes) {
    474		seq_puts(m,
    475			 "VOLUME   REF   nCOOK ACC FL CACHE           KEY\n"
    476			 "======== ===== ===== === == =============== ================\n");
    477		return 0;
    478	}
    479
    480	volume = list_entry(v, struct fscache_volume, proc_link);
    481	seq_printf(m,
    482		   "%08x %5d %5d %3d %02lx %-15.15s %s\n",
    483		   volume->debug_id,
    484		   refcount_read(&volume->ref),
    485		   atomic_read(&volume->n_cookies),
    486		   atomic_read(&volume->n_accesses),
    487		   volume->flags,
    488		   volume->cache->name ?: "-",
    489		   volume->key + 1);
    490	return 0;
    491}
    492
    493static void *fscache_volumes_seq_start(struct seq_file *m, loff_t *_pos)
    494	__acquires(&fscache_addremove_sem)
    495{
    496	down_read(&fscache_addremove_sem);
    497	return seq_list_start_head(&fscache_volumes, *_pos);
    498}
    499
    500static void *fscache_volumes_seq_next(struct seq_file *m, void *v, loff_t *_pos)
    501{
    502	return seq_list_next(v, &fscache_volumes, _pos);
    503}
    504
    505static void fscache_volumes_seq_stop(struct seq_file *m, void *v)
    506	__releases(&fscache_addremove_sem)
    507{
    508	up_read(&fscache_addremove_sem);
    509}
    510
    511const struct seq_operations fscache_volumes_seq_ops = {
    512	.start  = fscache_volumes_seq_start,
    513	.next   = fscache_volumes_seq_next,
    514	.stop   = fscache_volumes_seq_stop,
    515	.show   = fscache_volumes_seq_show,
    516};
    517#endif /* CONFIG_PROC_FS */