volume.c 8.8 KB
Newer Older
D
David Howells 已提交
1
/* AFS volume management
L
Linus Torvalds 已提交
2
 *
3
 * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
L
Linus Torvalds 已提交
4 5 6 7 8 9 10 11 12 13 14 15
 * Written by David Howells (dhowells@redhat.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 */

#include <linux/kernel.h>
#include <linux/slab.h>
#include "internal.h"

16 17 18 19
unsigned __read_mostly afs_volume_gc_delay = 10;
unsigned __read_mostly afs_volume_record_life = 60 * 60;

static const char *const afs_voltypes[] = { "R/W", "R/O", "BAK" };
L
Linus Torvalds 已提交
20 21

/*
22 23 24 25 26 27 28 29
 * Allocate a volume record and load it up from a vldb record.
 */
static struct afs_volume *afs_alloc_volume(struct afs_mount_params *params,
					   struct afs_vldb_entry *vldb,
					   unsigned long type_mask)
{
	struct afs_server_list *slist;
	struct afs_volume *volume;
D
David Howells 已提交
30
	int ret = -ENOMEM, nr_servers = 0, i;
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62

	for (i = 0; i < vldb->nr_servers; i++)
		if (vldb->fs_mask[i] & type_mask)
			nr_servers++;

	volume = kzalloc(sizeof(struct afs_volume), GFP_KERNEL);
	if (!volume)
		goto error_0;

	volume->vid		= vldb->vid[params->type];
	volume->update_at	= ktime_get_real_seconds() + afs_volume_record_life;
	volume->cell		= afs_get_cell(params->cell);
	volume->type		= params->type;
	volume->type_force	= params->force;
	volume->name_len	= vldb->name_len;

	atomic_set(&volume->usage, 1);
	INIT_LIST_HEAD(&volume->proc_link);
	rwlock_init(&volume->servers_lock);
	memcpy(volume->name, vldb->name, vldb->name_len + 1);

	slist = afs_alloc_server_list(params->cell, params->key, vldb, type_mask);
	if (IS_ERR(slist)) {
		ret = PTR_ERR(slist);
		goto error_1;
	}

	refcount_set(&slist->usage, 1);
	volume->servers = slist;
	return volume;

error_1:
D
David Howells 已提交
63
	afs_put_cell(params->net, volume->cell);
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
	kfree(volume);
error_0:
	return ERR_PTR(ret);
}

/*
 * Look up a VLDB record for a volume.
 */
static struct afs_vldb_entry *afs_vl_lookup_vldb(struct afs_cell *cell,
						 struct key *key,
						 const char *volname,
						 size_t volnamesz)
{
	struct afs_addr_cursor ac;
	struct afs_vldb_entry *vldb;
	int ret;

	ret = afs_set_vl_cursor(&ac, cell);
	if (ret < 0)
		return ERR_PTR(ret);

	while (afs_iterate_addresses(&ac)) {
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
		if (!test_bit(ac.index, &ac.alist->probed)) {
			ret = afs_vl_get_capabilities(cell->net, &ac, key);
			switch (ret) {
			case VL_SERVICE:
				clear_bit(ac.index, &ac.alist->yfs);
				set_bit(ac.index, &ac.alist->probed);
				ac.addr->srx_service = ret;
				break;
			case YFS_VL_SERVICE:
				set_bit(ac.index, &ac.alist->yfs);
				set_bit(ac.index, &ac.alist->probed);
				ac.addr->srx_service = ret;
				break;
			}
		}
		
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
		vldb = afs_vl_get_entry_by_name_u(cell->net, &ac, key,
						  volname, volnamesz);
		switch (ac.error) {
		case 0:
			afs_end_cursor(&ac);
			return vldb;
		case -ECONNABORTED:
			ac.error = afs_abort_to_error(ac.abort_code);
			goto error;
		case -ENOMEM:
		case -ENONET:
			goto error;
		case -ENETUNREACH:
		case -EHOSTUNREACH:
		case -ECONNREFUSED:
			break;
		default:
			ac.error = -EIO;
			goto error;
		}
	}

error:
	return ERR_PTR(afs_end_cursor(&ac));
}

/*
 * Look up a volume in the VL server and create a candidate volume record for
 * it.
 *
 * The volume name can be one of the following:
L
Linus Torvalds 已提交
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
 *	"%[cell:]volume[.]"		R/W volume
 *	"#[cell:]volume[.]"		R/O or R/W volume (rwparent=0),
 *					 or R/W (rwparent=1) volume
 *	"%[cell:]volume.readonly"	R/O volume
 *	"#[cell:]volume.readonly"	R/O volume
 *	"%[cell:]volume.backup"		Backup volume
 *	"#[cell:]volume.backup"		Backup volume
 *
 * The cell name is optional, and defaults to the current cell.
 *
 * See "The Rules of Mount Point Traversal" in Chapter 5 of the AFS SysAdmin
 * Guide
 * - Rule 1: Explicit type suffix forces access of that type or nothing
 *           (no suffix, then use Rule 2 & 3)
 * - Rule 2: If parent volume is R/O, then mount R/O volume by preference, R/W
 *           if not available
 * - Rule 3: If parent volume is R/W, then only mount R/W volume unless
 *           explicitly told otherwise
 */
152
struct afs_volume *afs_create_volume(struct afs_mount_params *params)
L
Linus Torvalds 已提交
153
{
154 155 156
	struct afs_vldb_entry *vldb;
	struct afs_volume *volume;
	unsigned long type_mask = 1UL << params->type;
L
Linus Torvalds 已提交
157

158 159 160 161
	vldb = afs_vl_lookup_vldb(params->cell, params->key,
				  params->volname, params->volnamesz);
	if (IS_ERR(vldb))
		return ERR_CAST(vldb);
L
Linus Torvalds 已提交
162

163 164 165 166
	if (test_bit(AFS_VLDB_QUERY_ERROR, &vldb->flags)) {
		volume = ERR_PTR(vldb->error);
		goto error;
	}
L
Linus Torvalds 已提交
167

168 169
	/* Make the final decision on the type we want */
	volume = ERR_PTR(-ENOMEDIUM);
D
David Howells 已提交
170
	if (params->force) {
171
		if (!(vldb->flags & type_mask))
L
Linus Torvalds 已提交
172
			goto error;
173
	} else if (test_bit(AFS_VLDB_HAS_RO, &vldb->flags)) {
D
David Howells 已提交
174
		params->type = AFSVL_ROVOL;
175
	} else if (test_bit(AFS_VLDB_HAS_RW, &vldb->flags)) {
D
David Howells 已提交
176
		params->type = AFSVL_RWVOL;
D
David Howells 已提交
177
	} else {
L
Linus Torvalds 已提交
178 179 180
		goto error;
	}

181 182
	type_mask = 1UL << params->type;
	volume = afs_alloc_volume(params, vldb, type_mask);
L
Linus Torvalds 已提交
183

184 185 186 187
error:
	kfree(vldb);
	return volume;
}
L
Linus Torvalds 已提交
188

189 190 191 192 193 194
/*
 * Destroy a volume record
 */
static void afs_destroy_volume(struct afs_net *net, struct afs_volume *volume)
{
	_enter("%p", volume);
L
Linus Torvalds 已提交
195

196 197 198
#ifdef CONFIG_AFS_FSCACHE
	ASSERTCMP(volume->cache, ==, NULL);
#endif
L
Linus Torvalds 已提交
199

200 201 202
	afs_put_serverlist(net, volume->servers);
	afs_put_cell(net, volume->cell);
	kfree(volume);
L
Linus Torvalds 已提交
203

204 205 206 207 208 209 210 211 212 213 214 215 216
	_leave(" [destroyed]");
}

/*
 * Drop a reference on a volume record.
 */
void afs_put_volume(struct afs_cell *cell, struct afs_volume *volume)
{
	if (volume) {
		_enter("%s", volume->name);

		if (atomic_dec_and_test(&volume->usage))
			afs_destroy_volume(cell->net, volume);
L
Linus Torvalds 已提交
217
	}
218
}
L
Linus Torvalds 已提交
219

220 221 222 223 224
/*
 * Activate a volume.
 */
void afs_activate_volume(struct afs_volume *volume)
{
D
David Howells 已提交
225
#ifdef CONFIG_AFS_FSCACHE
226
	volume->cache = fscache_acquire_cookie(volume->cell->cache,
D
David Howells 已提交
227
					       &afs_volume_cache_index_def,
228
					       volume, true);
L
Linus Torvalds 已提交
229 230
#endif

231 232 233 234
	write_lock(&volume->cell->proc_lock);
	list_add_tail(&volume->proc_link, &volume->cell->proc_volumes);
	write_unlock(&volume->cell->proc_lock);
}
L
Linus Torvalds 已提交
235

236 237 238 239 240 241
/*
 * Deactivate a volume.
 */
void afs_deactivate_volume(struct afs_volume *volume)
{
	_enter("%s", volume->name);
L
Linus Torvalds 已提交
242

243 244 245
	write_lock(&volume->cell->proc_lock);
	list_del_init(&volume->proc_link);
	write_unlock(&volume->cell->proc_lock);
L
Linus Torvalds 已提交
246

247 248 249 250 251
#ifdef CONFIG_AFS_FSCACHE
	fscache_relinquish_cookie(volume->cache,
				  test_bit(AFS_VOLUME_DELETED, &volume->flags));
	volume->cache = NULL;
#endif
L
Linus Torvalds 已提交
252

253
	_leave("");
D
David Howells 已提交
254
}
L
Linus Torvalds 已提交
255 256

/*
257
 * Query the VL service to update the volume status.
L
Linus Torvalds 已提交
258
 */
259
static int afs_update_volume_status(struct afs_volume *volume, struct key *key)
L
Linus Torvalds 已提交
260
{
261 262 263 264
	struct afs_server_list *new, *old, *discard;
	struct afs_vldb_entry *vldb;
	char idbuf[16];
	int ret, idsz;
L
Linus Torvalds 已提交
265

266
	_enter("");
L
Linus Torvalds 已提交
267

268 269 270 271
	/* We look up an ID by passing it as a decimal string in the
	 * operation's name parameter.
	 */
	idsz = sprintf(idbuf, "%u", volume->vid);
L
Linus Torvalds 已提交
272

273 274 275 276 277
	vldb = afs_vl_lookup_vldb(volume->cell, key, idbuf, idsz);
	if (IS_ERR(vldb)) {
		ret = PTR_ERR(vldb);
		goto error;
	}
L
Linus Torvalds 已提交
278

279 280 281 282 283 284 285 286 287 288
	/* See if the volume got renamed. */
	if (vldb->name_len != volume->name_len ||
	    memcmp(vldb->name, volume->name, vldb->name_len) != 0) {
		/* TODO: Use RCU'd string. */
		memcpy(volume->name, vldb->name, AFS_MAXVOLNAME);
		volume->name_len = vldb->name_len;
	}

	/* See if the volume's server list got updated. */
	new = afs_alloc_server_list(volume->cell, key,
D
David Howells 已提交
289
				    vldb, (1 << volume->type));
290 291 292 293
	if (IS_ERR(new)) {
		ret = PTR_ERR(new);
		goto error_vldb;
	}
L
Linus Torvalds 已提交
294

295
	write_lock(&volume->servers_lock);
L
Linus Torvalds 已提交
296

297 298 299 300 301 302 303 304
	discard = new;
	old = volume->servers;
	if (afs_annotate_server_list(new, old)) {
		new->seq = volume->servers_seq + 1;
		volume->servers = new;
		smp_wmb();
		volume->servers_seq++;
		discard = old;
L
Linus Torvalds 已提交
305 306
	}

307 308 309 310
	volume->update_at = ktime_get_real_seconds() + afs_volume_record_life;
	clear_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);
	write_unlock(&volume->servers_lock);
	ret = 0;
L
Linus Torvalds 已提交
311

312 313 314 315 316 317 318
	afs_put_serverlist(volume->cell->net, discard);
error_vldb:
	kfree(vldb);
error:
	_leave(" = %d", ret);
	return ret;
}
L
Linus Torvalds 已提交
319

320 321 322 323 324 325 326
/*
 * Make sure the volume record is up to date.
 */
int afs_check_volume_status(struct afs_volume *volume, struct key *key)
{
	time64_t now = ktime_get_real_seconds();
	int ret, retries = 0;
L
Linus Torvalds 已提交
327

328 329 330 331 332 333 334 335 336 337
	_enter("");

	if (volume->update_at <= now)
		set_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);

retry:
	if (!test_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags) &&
	    !test_bit(AFS_VOLUME_WAIT, &volume->flags)) {
		_leave(" = 0");
		return 0;
338
	}
L
Linus Torvalds 已提交
339

340 341 342 343 344 345 346 347
	if (!test_and_set_bit_lock(AFS_VOLUME_UPDATING, &volume->flags)) {
		ret = afs_update_volume_status(volume, key);
		clear_bit_unlock(AFS_VOLUME_WAIT, &volume->flags);
		clear_bit_unlock(AFS_VOLUME_UPDATING, &volume->flags);
		wake_up_bit(&volume->flags, AFS_VOLUME_WAIT);
		_leave(" = %d", ret);
		return ret;
	}
L
Linus Torvalds 已提交
348

349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
	if (!test_bit(AFS_VOLUME_WAIT, &volume->flags)) {
		_leave(" = 0 [no wait]");
		return 0;
	}

	ret = wait_on_bit(&volume->flags, AFS_VOLUME_WAIT, TASK_INTERRUPTIBLE);
	if (ret == -ERESTARTSYS) {
		_leave(" = %d", ret);
		return ret;
	}

	retries++;
	if (retries == 4) {
		_leave(" = -ESTALE");
		return -ESTALE;
	}
	goto retry;
D
David Howells 已提交
366
}