volume.c 8.9 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 229
					       &volume->vid, sizeof(volume->vid),
					       NULL, 0,
230
					       volume, true);
L
Linus Torvalds 已提交
231 232
#endif

233 234 235 236
	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 已提交
237

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

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

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

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

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

268
	_enter("");
L
Linus Torvalds 已提交
269

270 271 272 273
	/* 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 已提交
274

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

281 282 283 284 285 286 287 288 289 290
	/* 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 已提交
291
				    vldb, (1 << volume->type));
292 293 294 295
	if (IS_ERR(new)) {
		ret = PTR_ERR(new);
		goto error_vldb;
	}
L
Linus Torvalds 已提交
296

297
	write_lock(&volume->servers_lock);
L
Linus Torvalds 已提交
298

299 300 301 302 303 304 305 306
	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 已提交
307 308
	}

309 310 311 312
	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 已提交
313

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

322 323 324 325 326 327 328
/*
 * 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 已提交
329

330 331 332 333 334 335 336 337 338 339
	_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;
340
	}
L
Linus Torvalds 已提交
341

342 343 344 345 346 347 348 349
	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 已提交
350

351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
	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 已提交
368
}