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 (10765B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/* AFS volume management
      3 *
      4 * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
      5 * Written by David Howells (dhowells@redhat.com)
      6 */
      7
      8#include <linux/kernel.h>
      9#include <linux/slab.h>
     10#include "internal.h"
     11
     12static unsigned __read_mostly afs_volume_record_life = 60 * 60;
     13
     14/*
     15 * Insert a volume into a cell.  If there's an existing volume record, that is
     16 * returned instead with a ref held.
     17 */
     18static struct afs_volume *afs_insert_volume_into_cell(struct afs_cell *cell,
     19						      struct afs_volume *volume)
     20{
     21	struct afs_volume *p;
     22	struct rb_node *parent = NULL, **pp;
     23
     24	write_seqlock(&cell->volume_lock);
     25
     26	pp = &cell->volumes.rb_node;
     27	while (*pp) {
     28		parent = *pp;
     29		p = rb_entry(parent, struct afs_volume, cell_node);
     30		if (p->vid < volume->vid) {
     31			pp = &(*pp)->rb_left;
     32		} else if (p->vid > volume->vid) {
     33			pp = &(*pp)->rb_right;
     34		} else {
     35			volume = afs_get_volume(p, afs_volume_trace_get_cell_insert);
     36			goto found;
     37		}
     38	}
     39
     40	rb_link_node_rcu(&volume->cell_node, parent, pp);
     41	rb_insert_color(&volume->cell_node, &cell->volumes);
     42	hlist_add_head_rcu(&volume->proc_link, &cell->proc_volumes);
     43
     44found:
     45	write_sequnlock(&cell->volume_lock);
     46	return volume;
     47
     48}
     49
     50static void afs_remove_volume_from_cell(struct afs_volume *volume)
     51{
     52	struct afs_cell *cell = volume->cell;
     53
     54	if (!hlist_unhashed(&volume->proc_link)) {
     55		trace_afs_volume(volume->vid, atomic_read(&volume->usage),
     56				 afs_volume_trace_remove);
     57		write_seqlock(&cell->volume_lock);
     58		hlist_del_rcu(&volume->proc_link);
     59		rb_erase(&volume->cell_node, &cell->volumes);
     60		write_sequnlock(&cell->volume_lock);
     61	}
     62}
     63
     64/*
     65 * Allocate a volume record and load it up from a vldb record.
     66 */
     67static struct afs_volume *afs_alloc_volume(struct afs_fs_context *params,
     68					   struct afs_vldb_entry *vldb,
     69					   unsigned long type_mask)
     70{
     71	struct afs_server_list *slist;
     72	struct afs_volume *volume;
     73	int ret = -ENOMEM, nr_servers = 0, i;
     74
     75	for (i = 0; i < vldb->nr_servers; i++)
     76		if (vldb->fs_mask[i] & type_mask)
     77			nr_servers++;
     78
     79	volume = kzalloc(sizeof(struct afs_volume), GFP_KERNEL);
     80	if (!volume)
     81		goto error_0;
     82
     83	volume->vid		= vldb->vid[params->type];
     84	volume->update_at	= ktime_get_real_seconds() + afs_volume_record_life;
     85	volume->cell		= afs_get_cell(params->cell, afs_cell_trace_get_vol);
     86	volume->type		= params->type;
     87	volume->type_force	= params->force;
     88	volume->name_len	= vldb->name_len;
     89
     90	atomic_set(&volume->usage, 1);
     91	INIT_HLIST_NODE(&volume->proc_link);
     92	rwlock_init(&volume->servers_lock);
     93	rwlock_init(&volume->cb_v_break_lock);
     94	memcpy(volume->name, vldb->name, vldb->name_len + 1);
     95
     96	slist = afs_alloc_server_list(params->cell, params->key, vldb, type_mask);
     97	if (IS_ERR(slist)) {
     98		ret = PTR_ERR(slist);
     99		goto error_1;
    100	}
    101
    102	refcount_set(&slist->usage, 1);
    103	rcu_assign_pointer(volume->servers, slist);
    104	trace_afs_volume(volume->vid, 1, afs_volume_trace_alloc);
    105	return volume;
    106
    107error_1:
    108	afs_put_cell(volume->cell, afs_cell_trace_put_vol);
    109	kfree(volume);
    110error_0:
    111	return ERR_PTR(ret);
    112}
    113
    114/*
    115 * Look up or allocate a volume record.
    116 */
    117static struct afs_volume *afs_lookup_volume(struct afs_fs_context *params,
    118					    struct afs_vldb_entry *vldb,
    119					    unsigned long type_mask)
    120{
    121	struct afs_volume *candidate, *volume;
    122
    123	candidate = afs_alloc_volume(params, vldb, type_mask);
    124	if (IS_ERR(candidate))
    125		return candidate;
    126
    127	volume = afs_insert_volume_into_cell(params->cell, candidate);
    128	if (volume != candidate)
    129		afs_put_volume(params->net, candidate, afs_volume_trace_put_cell_dup);
    130	return volume;
    131}
    132
    133/*
    134 * Look up a VLDB record for a volume.
    135 */
    136static struct afs_vldb_entry *afs_vl_lookup_vldb(struct afs_cell *cell,
    137						 struct key *key,
    138						 const char *volname,
    139						 size_t volnamesz)
    140{
    141	struct afs_vldb_entry *vldb = ERR_PTR(-EDESTADDRREQ);
    142	struct afs_vl_cursor vc;
    143	int ret;
    144
    145	if (!afs_begin_vlserver_operation(&vc, cell, key))
    146		return ERR_PTR(-ERESTARTSYS);
    147
    148	while (afs_select_vlserver(&vc)) {
    149		vldb = afs_vl_get_entry_by_name_u(&vc, volname, volnamesz);
    150	}
    151
    152	ret = afs_end_vlserver_operation(&vc);
    153	return ret < 0 ? ERR_PTR(ret) : vldb;
    154}
    155
    156/*
    157 * Look up a volume in the VL server and create a candidate volume record for
    158 * it.
    159 *
    160 * The volume name can be one of the following:
    161 *	"%[cell:]volume[.]"		R/W volume
    162 *	"#[cell:]volume[.]"		R/O or R/W volume (rwparent=0),
    163 *					 or R/W (rwparent=1) volume
    164 *	"%[cell:]volume.readonly"	R/O volume
    165 *	"#[cell:]volume.readonly"	R/O volume
    166 *	"%[cell:]volume.backup"		Backup volume
    167 *	"#[cell:]volume.backup"		Backup volume
    168 *
    169 * The cell name is optional, and defaults to the current cell.
    170 *
    171 * See "The Rules of Mount Point Traversal" in Chapter 5 of the AFS SysAdmin
    172 * Guide
    173 * - Rule 1: Explicit type suffix forces access of that type or nothing
    174 *           (no suffix, then use Rule 2 & 3)
    175 * - Rule 2: If parent volume is R/O, then mount R/O volume by preference, R/W
    176 *           if not available
    177 * - Rule 3: If parent volume is R/W, then only mount R/W volume unless
    178 *           explicitly told otherwise
    179 */
    180struct afs_volume *afs_create_volume(struct afs_fs_context *params)
    181{
    182	struct afs_vldb_entry *vldb;
    183	struct afs_volume *volume;
    184	unsigned long type_mask = 1UL << params->type;
    185
    186	vldb = afs_vl_lookup_vldb(params->cell, params->key,
    187				  params->volname, params->volnamesz);
    188	if (IS_ERR(vldb))
    189		return ERR_CAST(vldb);
    190
    191	if (test_bit(AFS_VLDB_QUERY_ERROR, &vldb->flags)) {
    192		volume = ERR_PTR(vldb->error);
    193		goto error;
    194	}
    195
    196	/* Make the final decision on the type we want */
    197	volume = ERR_PTR(-ENOMEDIUM);
    198	if (params->force) {
    199		if (!(vldb->flags & type_mask))
    200			goto error;
    201	} else if (test_bit(AFS_VLDB_HAS_RO, &vldb->flags)) {
    202		params->type = AFSVL_ROVOL;
    203	} else if (test_bit(AFS_VLDB_HAS_RW, &vldb->flags)) {
    204		params->type = AFSVL_RWVOL;
    205	} else {
    206		goto error;
    207	}
    208
    209	type_mask = 1UL << params->type;
    210	volume = afs_lookup_volume(params, vldb, type_mask);
    211
    212error:
    213	kfree(vldb);
    214	return volume;
    215}
    216
    217/*
    218 * Destroy a volume record
    219 */
    220static void afs_destroy_volume(struct afs_net *net, struct afs_volume *volume)
    221{
    222	_enter("%p", volume);
    223
    224#ifdef CONFIG_AFS_FSCACHE
    225	ASSERTCMP(volume->cache, ==, NULL);
    226#endif
    227
    228	afs_remove_volume_from_cell(volume);
    229	afs_put_serverlist(net, rcu_access_pointer(volume->servers));
    230	afs_put_cell(volume->cell, afs_cell_trace_put_vol);
    231	trace_afs_volume(volume->vid, atomic_read(&volume->usage),
    232			 afs_volume_trace_free);
    233	kfree_rcu(volume, rcu);
    234
    235	_leave(" [destroyed]");
    236}
    237
    238/*
    239 * Get a reference on a volume record.
    240 */
    241struct afs_volume *afs_get_volume(struct afs_volume *volume,
    242				  enum afs_volume_trace reason)
    243{
    244	if (volume) {
    245		int u = atomic_inc_return(&volume->usage);
    246		trace_afs_volume(volume->vid, u, reason);
    247	}
    248	return volume;
    249}
    250
    251
    252/*
    253 * Drop a reference on a volume record.
    254 */
    255void afs_put_volume(struct afs_net *net, struct afs_volume *volume,
    256		    enum afs_volume_trace reason)
    257{
    258	if (volume) {
    259		afs_volid_t vid = volume->vid;
    260		int u = atomic_dec_return(&volume->usage);
    261		trace_afs_volume(vid, u, reason);
    262		if (u == 0)
    263			afs_destroy_volume(net, volume);
    264	}
    265}
    266
    267/*
    268 * Activate a volume.
    269 */
    270int afs_activate_volume(struct afs_volume *volume)
    271{
    272#ifdef CONFIG_AFS_FSCACHE
    273	struct fscache_volume *vcookie;
    274	char *name;
    275
    276	name = kasprintf(GFP_KERNEL, "afs,%s,%llx",
    277			 volume->cell->name, volume->vid);
    278	if (!name)
    279		return -ENOMEM;
    280
    281	vcookie = fscache_acquire_volume(name, NULL, NULL, 0);
    282	if (IS_ERR(vcookie)) {
    283		if (vcookie != ERR_PTR(-EBUSY)) {
    284			kfree(name);
    285			return PTR_ERR(vcookie);
    286		}
    287		pr_err("AFS: Cache volume key already in use (%s)\n", name);
    288		vcookie = NULL;
    289	}
    290	volume->cache = vcookie;
    291	kfree(name);
    292#endif
    293	return 0;
    294}
    295
    296/*
    297 * Deactivate a volume.
    298 */
    299void afs_deactivate_volume(struct afs_volume *volume)
    300{
    301	_enter("%s", volume->name);
    302
    303#ifdef CONFIG_AFS_FSCACHE
    304	fscache_relinquish_volume(volume->cache, NULL,
    305				  test_bit(AFS_VOLUME_DELETED, &volume->flags));
    306	volume->cache = NULL;
    307#endif
    308
    309	_leave("");
    310}
    311
    312/*
    313 * Query the VL service to update the volume status.
    314 */
    315static int afs_update_volume_status(struct afs_volume *volume, struct key *key)
    316{
    317	struct afs_server_list *new, *old, *discard;
    318	struct afs_vldb_entry *vldb;
    319	char idbuf[16];
    320	int ret, idsz;
    321
    322	_enter("");
    323
    324	/* We look up an ID by passing it as a decimal string in the
    325	 * operation's name parameter.
    326	 */
    327	idsz = sprintf(idbuf, "%llu", volume->vid);
    328
    329	vldb = afs_vl_lookup_vldb(volume->cell, key, idbuf, idsz);
    330	if (IS_ERR(vldb)) {
    331		ret = PTR_ERR(vldb);
    332		goto error;
    333	}
    334
    335	/* See if the volume got renamed. */
    336	if (vldb->name_len != volume->name_len ||
    337	    memcmp(vldb->name, volume->name, vldb->name_len) != 0) {
    338		/* TODO: Use RCU'd string. */
    339		memcpy(volume->name, vldb->name, AFS_MAXVOLNAME);
    340		volume->name_len = vldb->name_len;
    341	}
    342
    343	/* See if the volume's server list got updated. */
    344	new = afs_alloc_server_list(volume->cell, key,
    345				    vldb, (1 << volume->type));
    346	if (IS_ERR(new)) {
    347		ret = PTR_ERR(new);
    348		goto error_vldb;
    349	}
    350
    351	write_lock(&volume->servers_lock);
    352
    353	discard = new;
    354	old = rcu_dereference_protected(volume->servers,
    355					lockdep_is_held(&volume->servers_lock));
    356	if (afs_annotate_server_list(new, old)) {
    357		new->seq = volume->servers_seq + 1;
    358		rcu_assign_pointer(volume->servers, new);
    359		smp_wmb();
    360		volume->servers_seq++;
    361		discard = old;
    362	}
    363
    364	volume->update_at = ktime_get_real_seconds() + afs_volume_record_life;
    365	write_unlock(&volume->servers_lock);
    366	ret = 0;
    367
    368	afs_put_serverlist(volume->cell->net, discard);
    369error_vldb:
    370	kfree(vldb);
    371error:
    372	_leave(" = %d", ret);
    373	return ret;
    374}
    375
    376/*
    377 * Make sure the volume record is up to date.
    378 */
    379int afs_check_volume_status(struct afs_volume *volume, struct afs_operation *op)
    380{
    381	int ret, retries = 0;
    382
    383	_enter("");
    384
    385retry:
    386	if (test_bit(AFS_VOLUME_WAIT, &volume->flags))
    387		goto wait;
    388	if (volume->update_at <= ktime_get_real_seconds() ||
    389	    test_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags))
    390		goto update;
    391	_leave(" = 0");
    392	return 0;
    393
    394update:
    395	if (!test_and_set_bit_lock(AFS_VOLUME_UPDATING, &volume->flags)) {
    396		clear_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);
    397		ret = afs_update_volume_status(volume, op->key);
    398		if (ret < 0)
    399			set_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);
    400		clear_bit_unlock(AFS_VOLUME_WAIT, &volume->flags);
    401		clear_bit_unlock(AFS_VOLUME_UPDATING, &volume->flags);
    402		wake_up_bit(&volume->flags, AFS_VOLUME_WAIT);
    403		_leave(" = %d", ret);
    404		return ret;
    405	}
    406
    407wait:
    408	if (!test_bit(AFS_VOLUME_WAIT, &volume->flags)) {
    409		_leave(" = 0 [no wait]");
    410		return 0;
    411	}
    412
    413	ret = wait_on_bit(&volume->flags, AFS_VOLUME_WAIT,
    414			  (op->flags & AFS_OPERATION_UNINTR) ?
    415			  TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE);
    416	if (ret == -ERESTARTSYS) {
    417		_leave(" = %d", ret);
    418		return ret;
    419	}
    420
    421	retries++;
    422	if (retries == 4) {
    423		_leave(" = -ESTALE");
    424		return -ESTALE;
    425	}
    426	goto retry;
    427}