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

interface.c (12539B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/* FS-Cache interface to CacheFiles
      3 *
      4 * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved.
      5 * Written by David Howells (dhowells@redhat.com)
      6 */
      7
      8#include <linux/slab.h>
      9#include <linux/mount.h>
     10#include <linux/xattr.h>
     11#include <linux/file.h>
     12#include <linux/falloc.h>
     13#include <trace/events/fscache.h>
     14#include "internal.h"
     15
     16static atomic_t cachefiles_object_debug_id;
     17
     18/*
     19 * Allocate a cache object record.
     20 */
     21static
     22struct cachefiles_object *cachefiles_alloc_object(struct fscache_cookie *cookie)
     23{
     24	struct fscache_volume *vcookie = cookie->volume;
     25	struct cachefiles_volume *volume = vcookie->cache_priv;
     26	struct cachefiles_object *object;
     27
     28	_enter("{%s},%x,", vcookie->key, cookie->debug_id);
     29
     30	object = kmem_cache_zalloc(cachefiles_object_jar, GFP_KERNEL);
     31	if (!object)
     32		return NULL;
     33
     34	refcount_set(&object->ref, 1);
     35
     36	spin_lock_init(&object->lock);
     37	INIT_LIST_HEAD(&object->cache_link);
     38	object->volume = volume;
     39	object->debug_id = atomic_inc_return(&cachefiles_object_debug_id);
     40	object->cookie = fscache_get_cookie(cookie, fscache_cookie_get_attach_object);
     41
     42	fscache_count_object(vcookie->cache);
     43	trace_cachefiles_ref(object->debug_id, cookie->debug_id, 1,
     44			     cachefiles_obj_new);
     45	return object;
     46}
     47
     48/*
     49 * Note that an object has been seen.
     50 */
     51void cachefiles_see_object(struct cachefiles_object *object,
     52			   enum cachefiles_obj_ref_trace why)
     53{
     54	trace_cachefiles_ref(object->debug_id, object->cookie->debug_id,
     55			     refcount_read(&object->ref), why);
     56}
     57
     58/*
     59 * Increment the usage count on an object;
     60 */
     61struct cachefiles_object *cachefiles_grab_object(struct cachefiles_object *object,
     62						 enum cachefiles_obj_ref_trace why)
     63{
     64	int r;
     65
     66	__refcount_inc(&object->ref, &r);
     67	trace_cachefiles_ref(object->debug_id, object->cookie->debug_id, r, why);
     68	return object;
     69}
     70
     71/*
     72 * dispose of a reference to an object
     73 */
     74void cachefiles_put_object(struct cachefiles_object *object,
     75			   enum cachefiles_obj_ref_trace why)
     76{
     77	unsigned int object_debug_id = object->debug_id;
     78	unsigned int cookie_debug_id = object->cookie->debug_id;
     79	struct fscache_cache *cache;
     80	bool done;
     81	int r;
     82
     83	done = __refcount_dec_and_test(&object->ref, &r);
     84	trace_cachefiles_ref(object_debug_id, cookie_debug_id, r, why);
     85	if (done) {
     86		_debug("- kill object OBJ%x", object_debug_id);
     87
     88		ASSERTCMP(object->file, ==, NULL);
     89
     90		kfree(object->d_name);
     91
     92		cache = object->volume->cache->cache;
     93		fscache_put_cookie(object->cookie, fscache_cookie_put_object);
     94		object->cookie = NULL;
     95		kmem_cache_free(cachefiles_object_jar, object);
     96		fscache_uncount_object(cache);
     97	}
     98
     99	_leave("");
    100}
    101
    102/*
    103 * Adjust the size of a cache file if necessary to match the DIO size.  We keep
    104 * the EOF marker a multiple of DIO blocks so that we don't fall back to doing
    105 * non-DIO for a partial block straddling the EOF, but we also have to be
    106 * careful of someone expanding the file and accidentally accreting the
    107 * padding.
    108 */
    109static int cachefiles_adjust_size(struct cachefiles_object *object)
    110{
    111	struct iattr newattrs;
    112	struct file *file = object->file;
    113	uint64_t ni_size;
    114	loff_t oi_size;
    115	int ret;
    116
    117	ni_size = object->cookie->object_size;
    118	ni_size = round_up(ni_size, CACHEFILES_DIO_BLOCK_SIZE);
    119
    120	_enter("{OBJ%x},[%llu]",
    121	       object->debug_id, (unsigned long long) ni_size);
    122
    123	if (!file)
    124		return -ENOBUFS;
    125
    126	oi_size = i_size_read(file_inode(file));
    127	if (oi_size == ni_size)
    128		return 0;
    129
    130	inode_lock(file_inode(file));
    131
    132	/* if there's an extension to a partial page at the end of the backing
    133	 * file, we need to discard the partial page so that we pick up new
    134	 * data after it */
    135	if (oi_size & ~PAGE_MASK && ni_size > oi_size) {
    136		_debug("discard tail %llx", oi_size);
    137		newattrs.ia_valid = ATTR_SIZE;
    138		newattrs.ia_size = oi_size & PAGE_MASK;
    139		ret = cachefiles_inject_remove_error();
    140		if (ret == 0)
    141			ret = notify_change(&init_user_ns, file->f_path.dentry,
    142					    &newattrs, NULL);
    143		if (ret < 0)
    144			goto truncate_failed;
    145	}
    146
    147	newattrs.ia_valid = ATTR_SIZE;
    148	newattrs.ia_size = ni_size;
    149	ret = cachefiles_inject_write_error();
    150	if (ret == 0)
    151		ret = notify_change(&init_user_ns, file->f_path.dentry,
    152				    &newattrs, NULL);
    153
    154truncate_failed:
    155	inode_unlock(file_inode(file));
    156
    157	if (ret < 0)
    158		trace_cachefiles_io_error(NULL, file_inode(file), ret,
    159					  cachefiles_trace_notify_change_error);
    160	if (ret == -EIO) {
    161		cachefiles_io_error_obj(object, "Size set failed");
    162		ret = -ENOBUFS;
    163	}
    164
    165	_leave(" = %d", ret);
    166	return ret;
    167}
    168
    169/*
    170 * Attempt to look up the nominated node in this cache
    171 */
    172static bool cachefiles_lookup_cookie(struct fscache_cookie *cookie)
    173{
    174	struct cachefiles_object *object;
    175	struct cachefiles_cache *cache = cookie->volume->cache->cache_priv;
    176	const struct cred *saved_cred;
    177	bool success;
    178
    179	object = cachefiles_alloc_object(cookie);
    180	if (!object)
    181		goto fail;
    182
    183	_enter("{OBJ%x}", object->debug_id);
    184
    185	if (!cachefiles_cook_key(object))
    186		goto fail_put;
    187
    188	cookie->cache_priv = object;
    189
    190	cachefiles_begin_secure(cache, &saved_cred);
    191
    192	success = cachefiles_look_up_object(object);
    193	if (!success)
    194		goto fail_withdraw;
    195
    196	cachefiles_see_object(object, cachefiles_obj_see_lookup_cookie);
    197
    198	spin_lock(&cache->object_list_lock);
    199	list_add(&object->cache_link, &cache->object_list);
    200	spin_unlock(&cache->object_list_lock);
    201	cachefiles_adjust_size(object);
    202
    203	cachefiles_end_secure(cache, saved_cred);
    204	_leave(" = t");
    205	return true;
    206
    207fail_withdraw:
    208	cachefiles_end_secure(cache, saved_cred);
    209	cachefiles_see_object(object, cachefiles_obj_see_lookup_failed);
    210	fscache_caching_failed(cookie);
    211	_debug("failed c=%08x o=%08x", cookie->debug_id, object->debug_id);
    212	/* The caller holds an access count on the cookie, so we need them to
    213	 * drop it before we can withdraw the object.
    214	 */
    215	return false;
    216
    217fail_put:
    218	cachefiles_put_object(object, cachefiles_obj_put_alloc_fail);
    219fail:
    220	return false;
    221}
    222
    223/*
    224 * Shorten the backing object to discard any dirty data and free up
    225 * any unused granules.
    226 */
    227static bool cachefiles_shorten_object(struct cachefiles_object *object,
    228				      struct file *file, loff_t new_size)
    229{
    230	struct cachefiles_cache *cache = object->volume->cache;
    231	struct inode *inode = file_inode(file);
    232	loff_t i_size, dio_size;
    233	int ret;
    234
    235	dio_size = round_up(new_size, CACHEFILES_DIO_BLOCK_SIZE);
    236	i_size = i_size_read(inode);
    237
    238	trace_cachefiles_trunc(object, inode, i_size, dio_size,
    239			       cachefiles_trunc_shrink);
    240	ret = cachefiles_inject_remove_error();
    241	if (ret == 0)
    242		ret = vfs_truncate(&file->f_path, dio_size);
    243	if (ret < 0) {
    244		trace_cachefiles_io_error(object, file_inode(file), ret,
    245					  cachefiles_trace_trunc_error);
    246		cachefiles_io_error_obj(object, "Trunc-to-size failed %d", ret);
    247		cachefiles_remove_object_xattr(cache, object, file->f_path.dentry);
    248		return false;
    249	}
    250
    251	if (new_size < dio_size) {
    252		trace_cachefiles_trunc(object, inode, dio_size, new_size,
    253				       cachefiles_trunc_dio_adjust);
    254		ret = cachefiles_inject_write_error();
    255		if (ret == 0)
    256			ret = vfs_fallocate(file, FALLOC_FL_ZERO_RANGE,
    257					    new_size, dio_size - new_size);
    258		if (ret < 0) {
    259			trace_cachefiles_io_error(object, file_inode(file), ret,
    260						  cachefiles_trace_fallocate_error);
    261			cachefiles_io_error_obj(object, "Trunc-to-dio-size failed %d", ret);
    262			cachefiles_remove_object_xattr(cache, object, file->f_path.dentry);
    263			return false;
    264		}
    265	}
    266
    267	return true;
    268}
    269
    270/*
    271 * Resize the backing object.
    272 */
    273static void cachefiles_resize_cookie(struct netfs_cache_resources *cres,
    274				     loff_t new_size)
    275{
    276	struct cachefiles_object *object = cachefiles_cres_object(cres);
    277	struct cachefiles_cache *cache = object->volume->cache;
    278	struct fscache_cookie *cookie = object->cookie;
    279	const struct cred *saved_cred;
    280	struct file *file = cachefiles_cres_file(cres);
    281	loff_t old_size = cookie->object_size;
    282
    283	_enter("%llu->%llu", old_size, new_size);
    284
    285	if (new_size < old_size) {
    286		cachefiles_begin_secure(cache, &saved_cred);
    287		cachefiles_shorten_object(object, file, new_size);
    288		cachefiles_end_secure(cache, saved_cred);
    289		object->cookie->object_size = new_size;
    290		return;
    291	}
    292
    293	/* The file is being expanded.  We don't need to do anything
    294	 * particularly.  cookie->initial_size doesn't change and so the point
    295	 * at which we have to download before doesn't change.
    296	 */
    297	cookie->object_size = new_size;
    298}
    299
    300/*
    301 * Commit changes to the object as we drop it.
    302 */
    303static void cachefiles_commit_object(struct cachefiles_object *object,
    304				     struct cachefiles_cache *cache)
    305{
    306	bool update = false;
    307
    308	if (test_and_clear_bit(FSCACHE_COOKIE_LOCAL_WRITE, &object->cookie->flags))
    309		update = true;
    310	if (test_and_clear_bit(FSCACHE_COOKIE_NEEDS_UPDATE, &object->cookie->flags))
    311		update = true;
    312	if (update)
    313		cachefiles_set_object_xattr(object);
    314
    315	if (test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags))
    316		cachefiles_commit_tmpfile(cache, object);
    317}
    318
    319/*
    320 * Finalise and object and close the VFS structs that we have.
    321 */
    322static void cachefiles_clean_up_object(struct cachefiles_object *object,
    323				       struct cachefiles_cache *cache)
    324{
    325	if (test_bit(FSCACHE_COOKIE_RETIRED, &object->cookie->flags)) {
    326		if (!test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags)) {
    327			cachefiles_see_object(object, cachefiles_obj_see_clean_delete);
    328			_debug("- inval object OBJ%x", object->debug_id);
    329			cachefiles_delete_object(object, FSCACHE_OBJECT_WAS_RETIRED);
    330		} else {
    331			cachefiles_see_object(object, cachefiles_obj_see_clean_drop_tmp);
    332			_debug("- inval object OBJ%x tmpfile", object->debug_id);
    333		}
    334	} else {
    335		cachefiles_see_object(object, cachefiles_obj_see_clean_commit);
    336		cachefiles_commit_object(object, cache);
    337	}
    338
    339	cachefiles_unmark_inode_in_use(object, object->file);
    340	if (object->file) {
    341		fput(object->file);
    342		object->file = NULL;
    343	}
    344}
    345
    346/*
    347 * Withdraw caching for a cookie.
    348 */
    349static void cachefiles_withdraw_cookie(struct fscache_cookie *cookie)
    350{
    351	struct cachefiles_object *object = cookie->cache_priv;
    352	struct cachefiles_cache *cache = object->volume->cache;
    353	const struct cred *saved_cred;
    354
    355	_enter("o=%x", object->debug_id);
    356	cachefiles_see_object(object, cachefiles_obj_see_withdraw_cookie);
    357
    358	if (!list_empty(&object->cache_link)) {
    359		spin_lock(&cache->object_list_lock);
    360		cachefiles_see_object(object, cachefiles_obj_see_withdrawal);
    361		list_del_init(&object->cache_link);
    362		spin_unlock(&cache->object_list_lock);
    363	}
    364
    365	cachefiles_ondemand_clean_object(object);
    366
    367	if (object->file) {
    368		cachefiles_begin_secure(cache, &saved_cred);
    369		cachefiles_clean_up_object(object, cache);
    370		cachefiles_end_secure(cache, saved_cred);
    371	}
    372
    373	cookie->cache_priv = NULL;
    374	cachefiles_put_object(object, cachefiles_obj_put_detach);
    375}
    376
    377/*
    378 * Invalidate the storage associated with a cookie.
    379 */
    380static bool cachefiles_invalidate_cookie(struct fscache_cookie *cookie)
    381{
    382	struct cachefiles_object *object = cookie->cache_priv;
    383	struct file *new_file, *old_file;
    384	bool old_tmpfile;
    385
    386	_enter("o=%x,[%llu]", object->debug_id, object->cookie->object_size);
    387
    388	old_tmpfile = test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags);
    389
    390	if (!object->file) {
    391		fscache_resume_after_invalidation(cookie);
    392		_leave(" = t [light]");
    393		return true;
    394	}
    395
    396	new_file = cachefiles_create_tmpfile(object);
    397	if (IS_ERR(new_file))
    398		goto failed;
    399
    400	/* Substitute the VFS target */
    401	_debug("sub");
    402	spin_lock(&object->lock);
    403
    404	old_file = object->file;
    405	object->file = new_file;
    406	object->content_info = CACHEFILES_CONTENT_NO_DATA;
    407	set_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags);
    408	set_bit(FSCACHE_COOKIE_NEEDS_UPDATE, &object->cookie->flags);
    409
    410	spin_unlock(&object->lock);
    411	_debug("subbed");
    412
    413	/* Allow I/O to take place again */
    414	fscache_resume_after_invalidation(cookie);
    415
    416	if (old_file) {
    417		if (!old_tmpfile) {
    418			struct cachefiles_volume *volume = object->volume;
    419			struct dentry *fan = volume->fanout[(u8)cookie->key_hash];
    420
    421			inode_lock_nested(d_inode(fan), I_MUTEX_PARENT);
    422			cachefiles_bury_object(volume->cache, object, fan,
    423					       old_file->f_path.dentry,
    424					       FSCACHE_OBJECT_INVALIDATED);
    425		}
    426		fput(old_file);
    427	}
    428
    429	_leave(" = t");
    430	return true;
    431
    432failed:
    433	_leave(" = f");
    434	return false;
    435}
    436
    437const struct fscache_cache_ops cachefiles_cache_ops = {
    438	.name			= "cachefiles",
    439	.acquire_volume		= cachefiles_acquire_volume,
    440	.free_volume		= cachefiles_free_volume,
    441	.lookup_cookie		= cachefiles_lookup_cookie,
    442	.withdraw_cookie	= cachefiles_withdraw_cookie,
    443	.invalidate_cookie	= cachefiles_invalidate_cookie,
    444	.begin_operation	= cachefiles_begin_operation,
    445	.resize_cookie		= cachefiles_resize_cookie,
    446	.prepare_to_write	= cachefiles_prepare_to_write,
    447};