vlocation.c 17.3 KB
Newer Older
1
/* AFS volume location 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 16
 * 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/module.h>
#include <linux/init.h>
#include "internal.h"

17 18
unsigned afs_vlocation_timeout = 10;	/* volume location timeout in seconds */
unsigned afs_vlocation_update_timeout = 10 * 60;
L
Linus Torvalds 已提交
19

20 21
static void afs_vlocation_reaper(struct work_struct *);
static void afs_vlocation_updater(struct work_struct *);
L
Linus Torvalds 已提交
22

23 24 25 26 27 28 29
static LIST_HEAD(afs_vlocation_updates);
static LIST_HEAD(afs_vlocation_graveyard);
static DEFINE_SPINLOCK(afs_vlocation_updates_lock);
static DEFINE_SPINLOCK(afs_vlocation_graveyard_lock);
static DECLARE_DELAYED_WORK(afs_vlocation_reap, afs_vlocation_reaper);
static DECLARE_DELAYED_WORK(afs_vlocation_update, afs_vlocation_updater);
static struct workqueue_struct *afs_vlocation_update_worker;
L
Linus Torvalds 已提交
30 31 32 33 34

/*
 * iterate through the VL servers in a cell until one of them admits knowing
 * about the volume in question
 */
35
static int afs_vlocation_access_vl_by_name(struct afs_vlocation *vl,
D
David Howells 已提交
36
					   struct key *key,
L
Linus Torvalds 已提交
37 38
					   struct afs_cache_vlocation *vldb)
{
39 40
	struct afs_cell *cell = vl->cell;
	struct in_addr addr;
L
Linus Torvalds 已提交
41 42
	int count, ret;

43
	_enter("%s,%s", cell->name, vl->vldb.name);
L
Linus Torvalds 已提交
44

45
	down_write(&vl->cell->vl_sem);
L
Linus Torvalds 已提交
46 47
	ret = -ENOMEDIUM;
	for (count = cell->vl_naddrs; count > 0; count--) {
48 49 50
		addr = cell->vl_addrs[cell->vl_curr_svix];

		_debug("CellServ[%hu]: %08x", cell->vl_curr_svix, addr.s_addr);
L
Linus Torvalds 已提交
51 52

		/* attempt to access the VL server */
D
David Howells 已提交
53
		ret = afs_vl_get_entry_by_name(&addr, key, vl->vldb.name, vldb,
54
					       &afs_sync_call);
L
Linus Torvalds 已提交
55 56 57 58 59 60 61 62 63 64 65 66 67 68
		switch (ret) {
		case 0:
			goto out;
		case -ENOMEM:
		case -ENONET:
		case -ENETUNREACH:
		case -EHOSTUNREACH:
		case -ECONNREFUSED:
			if (ret == -ENOMEM || ret == -ENONET)
				goto out;
			goto rotate;
		case -ENOMEDIUM:
			goto out;
		default:
69
			ret = -EIO;
L
Linus Torvalds 已提交
70 71 72 73 74 75 76 77 78
			goto rotate;
		}

		/* rotate the server records upon lookup failure */
	rotate:
		cell->vl_curr_svix++;
		cell->vl_curr_svix %= cell->vl_naddrs;
	}

D
David Howells 已提交
79
out:
80
	up_write(&vl->cell->vl_sem);
L
Linus Torvalds 已提交
81 82
	_leave(" = %d", ret);
	return ret;
D
David Howells 已提交
83
}
L
Linus Torvalds 已提交
84 85 86 87 88

/*
 * iterate through the VL servers in a cell until one of them admits knowing
 * about the volume in question
 */
89
static int afs_vlocation_access_vl_by_id(struct afs_vlocation *vl,
D
David Howells 已提交
90
					 struct key *key,
L
Linus Torvalds 已提交
91 92 93 94
					 afs_volid_t volid,
					 afs_voltype_t voltype,
					 struct afs_cache_vlocation *vldb)
{
95 96
	struct afs_cell *cell = vl->cell;
	struct in_addr addr;
L
Linus Torvalds 已提交
97 98 99 100
	int count, ret;

	_enter("%s,%x,%d,", cell->name, volid, voltype);

101
	down_write(&vl->cell->vl_sem);
L
Linus Torvalds 已提交
102 103
	ret = -ENOMEDIUM;
	for (count = cell->vl_naddrs; count > 0; count--) {
104 105 106
		addr = cell->vl_addrs[cell->vl_curr_svix];

		_debug("CellServ[%hu]: %08x", cell->vl_curr_svix, addr.s_addr);
L
Linus Torvalds 已提交
107 108

		/* attempt to access the VL server */
D
David Howells 已提交
109
		ret = afs_vl_get_entry_by_id(&addr, key, volid, voltype, vldb,
110
					     &afs_sync_call);
L
Linus Torvalds 已提交
111 112 113 114 115 116 117 118 119 120 121
		switch (ret) {
		case 0:
			goto out;
		case -ENOMEM:
		case -ENONET:
		case -ENETUNREACH:
		case -EHOSTUNREACH:
		case -ECONNREFUSED:
			if (ret == -ENOMEM || ret == -ENONET)
				goto out;
			goto rotate;
122 123 124 125 126 127 128 129 130 131 132 133
		case -EBUSY:
			vl->upd_busy_cnt++;
			if (vl->upd_busy_cnt <= 3) {
				if (vl->upd_busy_cnt > 1) {
					/* second+ BUSY - sleep a little bit */
					set_current_state(TASK_UNINTERRUPTIBLE);
					schedule_timeout(1);
					__set_current_state(TASK_RUNNING);
				}
				continue;
			}
			break;
L
Linus Torvalds 已提交
134
		case -ENOMEDIUM:
135 136
			vl->upd_rej_cnt++;
			goto rotate;
L
Linus Torvalds 已提交
137
		default:
138
			ret = -EIO;
L
Linus Torvalds 已提交
139 140 141 142 143 144 145
			goto rotate;
		}

		/* rotate the server records upon lookup failure */
	rotate:
		cell->vl_curr_svix++;
		cell->vl_curr_svix %= cell->vl_naddrs;
146
		vl->upd_busy_cnt = 0;
L
Linus Torvalds 已提交
147 148
	}

D
David Howells 已提交
149
out:
150 151 152 153 154 155 156 157 158
	if (ret < 0 && vl->upd_rej_cnt > 0) {
		printk(KERN_NOTICE "kAFS:"
		       " Active volume no longer valid '%s'\n",
		       vl->vldb.name);
		vl->valid = 0;
		ret = -ENOMEDIUM;
	}

	up_write(&vl->cell->vl_sem);
L
Linus Torvalds 已提交
159 160
	_leave(" = %d", ret);
	return ret;
D
David Howells 已提交
161
}
L
Linus Torvalds 已提交
162 163

/*
164
 * allocate a volume location record
L
Linus Torvalds 已提交
165
 */
166 167 168
static struct afs_vlocation *afs_vlocation_alloc(struct afs_cell *cell,
						 const char *name,
						 size_t namesz)
L
Linus Torvalds 已提交
169
{
170 171 172 173 174 175 176 177 178 179 180 181 182
	struct afs_vlocation *vl;

	vl = kzalloc(sizeof(struct afs_vlocation), GFP_KERNEL);
	if (vl) {
		vl->cell = cell;
		vl->state = AFS_VL_NEW;
		atomic_set(&vl->usage, 1);
		INIT_LIST_HEAD(&vl->link);
		INIT_LIST_HEAD(&vl->grave);
		INIT_LIST_HEAD(&vl->update);
		init_waitqueue_head(&vl->waitq);
		rwlock_init(&vl->lock);
		memcpy(vl->vldb.name, name, namesz);
L
Linus Torvalds 已提交
183 184
	}

185 186 187
	_leave(" = %p", vl);
	return vl;
}
L
Linus Torvalds 已提交
188

189 190 191 192
/*
 * update record if we found it in the cache
 */
static int afs_vlocation_update_record(struct afs_vlocation *vl,
D
David Howells 已提交
193
				       struct key *key,
194 195 196 197 198
				       struct afs_cache_vlocation *vldb)
{
	afs_voltype_t voltype;
	afs_volid_t vid;
	int ret;
L
Linus Torvalds 已提交
199 200 201

	/* try to look up a cached volume in the cell VL databases by ID */
	_debug("Locally Cached: %s %02x { %08x(%x) %08x(%x) %08x(%x) }",
202 203 204 205 206 207 208 209
	       vl->vldb.name,
	       vl->vldb.vidmask,
	       ntohl(vl->vldb.servers[0].s_addr),
	       vl->vldb.srvtmask[0],
	       ntohl(vl->vldb.servers[1].s_addr),
	       vl->vldb.srvtmask[1],
	       ntohl(vl->vldb.servers[2].s_addr),
	       vl->vldb.srvtmask[2]);
L
Linus Torvalds 已提交
210 211

	_debug("Vids: %08x %08x %08x",
212 213 214
	       vl->vldb.vid[0],
	       vl->vldb.vid[1],
	       vl->vldb.vid[2]);
L
Linus Torvalds 已提交
215

216 217
	if (vl->vldb.vidmask & AFS_VOL_VTM_RW) {
		vid = vl->vldb.vid[0];
L
Linus Torvalds 已提交
218
		voltype = AFSVL_RWVOL;
219 220
	} else if (vl->vldb.vidmask & AFS_VOL_VTM_RO) {
		vid = vl->vldb.vid[1];
L
Linus Torvalds 已提交
221
		voltype = AFSVL_ROVOL;
222 223
	} else if (vl->vldb.vidmask & AFS_VOL_VTM_BAK) {
		vid = vl->vldb.vid[2];
L
Linus Torvalds 已提交
224
		voltype = AFSVL_BACKVOL;
D
David Howells 已提交
225
	} else {
L
Linus Torvalds 已提交
226 227 228 229 230
		BUG();
		vid = 0;
		voltype = 0;
	}

231 232 233
	/* contact the server to make sure the volume is still available
	 * - TODO: need to handle disconnected operation here
	 */
D
David Howells 已提交
234
	ret = afs_vlocation_access_vl_by_id(vl, key, vid, voltype, vldb);
L
Linus Torvalds 已提交
235 236 237
	switch (ret) {
		/* net error */
	default:
238 239 240 241 242
		printk(KERN_WARNING "kAFS:"
		       " failed to update volume '%s' (%x) up in '%s': %d\n",
		       vl->vldb.name, vid, vl->cell->name, ret);
		_leave(" = %d", ret);
		return ret;
L
Linus Torvalds 已提交
243 244 245

		/* pulled from local cache into memory */
	case 0:
246 247
		_leave(" = 0");
		return 0;
L
Linus Torvalds 已提交
248 249 250

		/* uh oh... looks like the volume got deleted */
	case -ENOMEDIUM:
251 252 253
		printk(KERN_ERR "kAFS:"
		       " volume '%s' (%x) does not exist '%s'\n",
		       vl->vldb.name, vid, vl->cell->name);
L
Linus Torvalds 已提交
254 255

		/* TODO: make existing record unavailable */
256 257
		_leave(" = %d", ret);
		return ret;
L
Linus Torvalds 已提交
258
	}
259
}
L
Linus Torvalds 已提交
260

261 262 263 264 265 266 267 268 269 270 271
/*
 * apply the update to a VL record
 */
static void afs_vlocation_apply_update(struct afs_vlocation *vl,
				       struct afs_cache_vlocation *vldb)
{
	_debug("Done VL Lookup: %s %02x { %08x(%x) %08x(%x) %08x(%x) }",
	       vldb->name, vldb->vidmask,
	       ntohl(vldb->servers[0].s_addr), vldb->srvtmask[0],
	       ntohl(vldb->servers[1].s_addr), vldb->srvtmask[1],
	       ntohl(vldb->servers[2].s_addr), vldb->srvtmask[2]);
L
Linus Torvalds 已提交
272

273 274
	_debug("Vids: %08x %08x %08x",
	       vldb->vid[0], vldb->vid[1], vldb->vid[2]);
L
Linus Torvalds 已提交
275

276 277 278 279
	if (strcmp(vldb->name, vl->vldb.name) != 0)
		printk(KERN_NOTICE "kAFS:"
		       " name of volume '%s' changed to '%s' on server\n",
		       vl->vldb.name, vldb->name);
L
Linus Torvalds 已提交
280

281
	vl->vldb = *vldb;
L
Linus Torvalds 已提交
282 283 284

#ifdef AFS_CACHING_SUPPORT
	/* update volume entry in local cache */
285
	cachefs_update_cookie(vl->cache);
L
Linus Torvalds 已提交
286
#endif
D
David Howells 已提交
287
}
L
Linus Torvalds 已提交
288 289

/*
290 291
 * fill in a volume location record, consulting the cache and the VL server
 * both
L
Linus Torvalds 已提交
292
 */
D
David Howells 已提交
293 294
static int afs_vlocation_fill_in_record(struct afs_vlocation *vl,
					struct key *key)
L
Linus Torvalds 已提交
295
{
296 297
	struct afs_cache_vlocation vldb;
	int ret;
L
Linus Torvalds 已提交
298

299
	_enter("");
L
Linus Torvalds 已提交
300

301
	ASSERTCMP(vl->valid, ==, 0);
L
Linus Torvalds 已提交
302

303
	memset(&vldb, 0, sizeof(vldb));
L
Linus Torvalds 已提交
304

305 306 307 308 309 310 311
	/* see if we have an in-cache copy (will set vl->valid if there is) */
#ifdef AFS_CACHING_SUPPORT
	cachefs_acquire_cookie(cell->cache,
			       &afs_volume_cache_index_def,
			       vlocation,
			       &vl->cache);
#endif
L
Linus Torvalds 已提交
312

313 314 315 316
	if (vl->valid) {
		/* try to update a known volume in the cell VL databases by
		 * ID as the name may have changed */
		_debug("found in cache");
D
David Howells 已提交
317
		ret = afs_vlocation_update_record(vl, key, &vldb);
318 319 320
	} else {
		/* try to look up an unknown volume in the cell VL databases by
		 * name */
D
David Howells 已提交
321
		ret = afs_vlocation_access_vl_by_name(vl, key, &vldb);
322 323 324 325 326
		if (ret < 0) {
			printk("kAFS: failed to locate '%s' in cell '%s'\n",
			       vl->vldb.name, vl->cell->name);
			return ret;
		}
L
Linus Torvalds 已提交
327 328
	}

329 330 331
	afs_vlocation_apply_update(vl, &vldb);
	_leave(" = 0");
	return 0;
D
David Howells 已提交
332
}
L
Linus Torvalds 已提交
333 334

/*
335
 * queue a vlocation record for updates
L
Linus Torvalds 已提交
336
 */
337
void afs_vlocation_queue_for_updates(struct afs_vlocation *vl)
L
Linus Torvalds 已提交
338
{
339
	struct afs_vlocation *xvl;
L
Linus Torvalds 已提交
340

341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
	/* wait at least 10 minutes before updating... */
	vl->update_at = get_seconds() + afs_vlocation_update_timeout;

	spin_lock(&afs_vlocation_updates_lock);

	if (!list_empty(&afs_vlocation_updates)) {
		/* ... but wait at least 1 second more than the newest record
		 * already queued so that we don't spam the VL server suddenly
		 * with lots of requests
		 */
		xvl = list_entry(afs_vlocation_updates.prev,
				 struct afs_vlocation, update);
		if (vl->update_at <= xvl->update_at)
			vl->update_at = xvl->update_at + 1;
	} else {
		queue_delayed_work(afs_vlocation_update_worker,
				   &afs_vlocation_update,
				   afs_vlocation_update_timeout * HZ);
L
Linus Torvalds 已提交
359
	}
360 361 362

	list_add_tail(&vl->update, &afs_vlocation_updates);
	spin_unlock(&afs_vlocation_updates_lock);
D
David Howells 已提交
363
}
L
Linus Torvalds 已提交
364 365

/*
366 367 368 369 370
 * lookup volume location
 * - iterate through the VL servers in a cell until one of them admits knowing
 *   about the volume in question
 * - lookup in the local cache if not able to find on the VL server
 * - insert/update in the local cache if did get a VL response
L
Linus Torvalds 已提交
371
 */
372
struct afs_vlocation *afs_vlocation_lookup(struct afs_cell *cell,
D
David Howells 已提交
373
					   struct key *key,
374 375
					   const char *name,
					   size_t namesz)
L
Linus Torvalds 已提交
376
{
377 378
	struct afs_vlocation *vl;
	int ret;
L
Linus Torvalds 已提交
379

D
David Howells 已提交
380 381 382
	_enter("{%s},{%x},%*.*s,%zu",
	       cell->name, key_serial(key),
	       (int) namesz, (int) namesz, name, namesz);
L
Linus Torvalds 已提交
383

384 385 386 387
	if (namesz > sizeof(vl->vldb.name)) {
		_leave(" = -ENAMETOOLONG");
		return ERR_PTR(-ENAMETOOLONG);
	}
L
Linus Torvalds 已提交
388

389 390 391 392 393 394 395 396
	/* see if we have an in-memory copy first */
	down_write(&cell->vl_sem);
	spin_lock(&cell->vl_lock);
	list_for_each_entry(vl, &cell->vl_list, link) {
		if (vl->vldb.name[namesz] != '\0')
			continue;
		if (memcmp(vl->vldb.name, name, namesz) == 0)
			goto found_in_memory;
L
Linus Torvalds 已提交
397
	}
398
	spin_unlock(&cell->vl_lock);
L
Linus Torvalds 已提交
399

400 401 402 403 404 405
	/* not in the cell's in-memory lists - create a new record */
	vl = afs_vlocation_alloc(cell, name, namesz);
	if (!vl) {
		up_write(&cell->vl_sem);
		return ERR_PTR(-ENOMEM);
	}
L
Linus Torvalds 已提交
406

407
	afs_get_cell(cell);
L
Linus Torvalds 已提交
408

409 410 411
	list_add_tail(&vl->link, &cell->vl_list);
	vl->state = AFS_VL_CREATING;
	up_write(&cell->vl_sem);
L
Linus Torvalds 已提交
412

413
fill_in_record:
D
David Howells 已提交
414
	ret = afs_vlocation_fill_in_record(vl, key);
415 416 417 418
	if (ret < 0)
		goto error_abandon;
	vl->state = AFS_VL_VALID;
	wake_up(&vl->waitq);
L
Linus Torvalds 已提交
419

420 421 422
	/* schedule for regular updates */
	afs_vlocation_queue_for_updates(vl);
	goto success;
L
Linus Torvalds 已提交
423

424 425 426 427 428 429 430 431 432
found_in_memory:
	/* found in memory */
	_debug("found in memory");
	atomic_inc(&vl->usage);
	spin_unlock(&cell->vl_lock);
	if (!list_empty(&vl->grave)) {
		spin_lock(&afs_vlocation_graveyard_lock);
		list_del_init(&vl->grave);
		spin_unlock(&afs_vlocation_graveyard_lock);
L
Linus Torvalds 已提交
433
	}
434
	up_write(&cell->vl_sem);
L
Linus Torvalds 已提交
435

436 437 438
	/* see if it was an abandoned record that we might try filling in */
	while (vl->state != AFS_VL_VALID) {
		afs_vlocation_state_t state = vl->state;
L
Linus Torvalds 已提交
439

440
		_debug("invalid [state %d]", state);
L
Linus Torvalds 已提交
441

442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
		if ((state == AFS_VL_NEW || state == AFS_VL_NO_VOLUME)) {
			if (cmpxchg(&vl->state, state, AFS_VL_CREATING) ==
			    state)
				goto fill_in_record;
			continue;
		}

		/* must now wait for creation or update by someone else to
		 * complete */
		_debug("wait");

		ret = wait_event_interruptible(
			vl->waitq,
			vl->state == AFS_VL_NEW ||
			vl->state == AFS_VL_VALID ||
			vl->state == AFS_VL_NO_VOLUME);
		if (ret < 0)
			goto error;
L
Linus Torvalds 已提交
460 461
	}

462 463 464 465 466 467 468 469 470 471
success:
	_leave(" = %p",vl);
	return vl;

error_abandon:
	vl->state = AFS_VL_NEW;
	wake_up(&vl->waitq);
error:
	ASSERT(vl != NULL);
	afs_put_vlocation(vl);
L
Linus Torvalds 已提交
472
	_leave(" = %d", ret);
473
	return ERR_PTR(ret);
D
David Howells 已提交
474
}
L
Linus Torvalds 已提交
475 476

/*
477
 * finish using a volume location record
L
Linus Torvalds 已提交
478
 */
479
void afs_put_vlocation(struct afs_vlocation *vl)
L
Linus Torvalds 已提交
480
{
481 482
	if (!vl)
		return;
L
Linus Torvalds 已提交
483

484
	_enter("%s", vl->vldb.name);
L
Linus Torvalds 已提交
485

486
	ASSERTCMP(atomic_read(&vl->usage), >, 0);
L
Linus Torvalds 已提交
487

488 489 490 491
	if (likely(!atomic_dec_and_test(&vl->usage))) {
		_leave("");
		return;
	}
L
Linus Torvalds 已提交
492

493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
	spin_lock(&afs_vlocation_graveyard_lock);
	if (atomic_read(&vl->usage) == 0) {
		_debug("buried");
		list_move_tail(&vl->grave, &afs_vlocation_graveyard);
		vl->time_of_death = get_seconds();
		schedule_delayed_work(&afs_vlocation_reap,
				      afs_vlocation_timeout * HZ);

		/* suspend updates on this record */
		if (!list_empty(&vl->update)) {
			spin_lock(&afs_vlocation_updates_lock);
			list_del_init(&vl->update);
			spin_unlock(&afs_vlocation_updates_lock);
		}
	}
	spin_unlock(&afs_vlocation_graveyard_lock);
	_leave(" [killed?]");
D
David Howells 已提交
510
}
L
Linus Torvalds 已提交
511 512

/*
513
 * destroy a dead volume location record
L
Linus Torvalds 已提交
514
 */
515
static void afs_vlocation_destroy(struct afs_vlocation *vl)
L
Linus Torvalds 已提交
516
{
517
	_enter("%p", vl);
L
Linus Torvalds 已提交
518

519 520 521
#ifdef AFS_CACHING_SUPPORT
	cachefs_relinquish_cookie(vl->cache, 0);
#endif
L
Linus Torvalds 已提交
522

523 524 525 526 527 528 529 530 531 532 533 534 535
	afs_put_cell(vl->cell);
	kfree(vl);
}

/*
 * reap dead volume location records
 */
static void afs_vlocation_reaper(struct work_struct *work)
{
	LIST_HEAD(corpses);
	struct afs_vlocation *vl;
	unsigned long delay, expiry;
	time_t now;
L
Linus Torvalds 已提交
536

537
	_enter("");
L
Linus Torvalds 已提交
538

539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
	now = get_seconds();
	spin_lock(&afs_vlocation_graveyard_lock);

	while (!list_empty(&afs_vlocation_graveyard)) {
		vl = list_entry(afs_vlocation_graveyard.next,
				struct afs_vlocation, grave);

		_debug("check %p", vl);

		/* the queue is ordered most dead first */
		expiry = vl->time_of_death + afs_vlocation_timeout;
		if (expiry > now) {
			delay = (expiry - now) * HZ;
			_debug("delay %lu", delay);
			if (!schedule_delayed_work(&afs_vlocation_reap,
						   delay)) {
				cancel_delayed_work(&afs_vlocation_reap);
				schedule_delayed_work(&afs_vlocation_reap,
						      delay);
			}
			break;
		}
L
Linus Torvalds 已提交
561

562 563 564 565
		spin_lock(&vl->cell->vl_lock);
		if (atomic_read(&vl->usage) > 0) {
			_debug("no reap");
			list_del_init(&vl->grave);
D
David Howells 已提交
566
		} else {
567 568 569
			_debug("reap");
			list_move_tail(&vl->grave, &corpses);
			list_del_init(&vl->link);
L
Linus Torvalds 已提交
570
		}
571
		spin_unlock(&vl->cell->vl_lock);
L
Linus Torvalds 已提交
572 573
	}

574
	spin_unlock(&afs_vlocation_graveyard_lock);
L
Linus Torvalds 已提交
575

576 577 578 579 580
	/* now reap the corpses we've extracted */
	while (!list_empty(&corpses)) {
		vl = list_entry(corpses.next, struct afs_vlocation, grave);
		list_del(&vl->grave);
		afs_vlocation_destroy(vl);
L
Linus Torvalds 已提交
581 582 583
	}

	_leave("");
584
}
L
Linus Torvalds 已提交
585

586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612
/*
 * initialise the VL update process
 */
int __init afs_vlocation_update_init(void)
{
	afs_vlocation_update_worker =
		create_singlethread_workqueue("kafs_vlupdated");
	return afs_vlocation_update_worker ? 0 : -ENOMEM;
}

/*
 * discard all the volume location records for rmmod
 */
void __exit afs_vlocation_purge(void)
{
	afs_vlocation_timeout = 0;

	spin_lock(&afs_vlocation_updates_lock);
	list_del_init(&afs_vlocation_updates);
	spin_unlock(&afs_vlocation_updates_lock);
	cancel_delayed_work(&afs_vlocation_update);
	queue_delayed_work(afs_vlocation_update_worker,
			   &afs_vlocation_update, 0);
	destroy_workqueue(afs_vlocation_update_worker);

	cancel_delayed_work(&afs_vlocation_reap);
	schedule_delayed_work(&afs_vlocation_reap, 0);
D
David Howells 已提交
613
}
L
Linus Torvalds 已提交
614 615

/*
616
 * update a volume location
L
Linus Torvalds 已提交
617
 */
618
static void afs_vlocation_updater(struct work_struct *work)
L
Linus Torvalds 已提交
619 620
{
	struct afs_cache_vlocation vldb;
621 622 623
	struct afs_vlocation *vl, *xvl;
	time_t now;
	long timeout;
L
Linus Torvalds 已提交
624 625
	int ret;

626
	_enter("");
L
Linus Torvalds 已提交
627

628
	now = get_seconds();
L
Linus Torvalds 已提交
629

630 631 632 633 634 635 636 637
	/* find a record to update */
	spin_lock(&afs_vlocation_updates_lock);
	for (;;) {
		if (list_empty(&afs_vlocation_updates)) {
			spin_unlock(&afs_vlocation_updates_lock);
			_leave(" [nothing]");
			return;
		}
L
Linus Torvalds 已提交
638

639 640 641 642 643
		vl = list_entry(afs_vlocation_updates.next,
				struct afs_vlocation, update);
		if (atomic_read(&vl->usage) > 0)
			break;
		list_del_init(&vl->update);
L
Linus Torvalds 已提交
644 645
	}

646 647 648 649 650 651
	timeout = vl->update_at - now;
	if (timeout > 0) {
		queue_delayed_work(afs_vlocation_update_worker,
				   &afs_vlocation_update, timeout * HZ);
		spin_unlock(&afs_vlocation_updates_lock);
		_leave(" [nothing]");
L
Linus Torvalds 已提交
652 653 654
		return;
	}

655 656 657
	list_del_init(&vl->update);
	atomic_inc(&vl->usage);
	spin_unlock(&afs_vlocation_updates_lock);
L
Linus Torvalds 已提交
658

659 660 661 662 663
	/* we can now perform the update */
	_debug("update %s", vl->vldb.name);
	vl->state = AFS_VL_UPDATING;
	vl->upd_rej_cnt = 0;
	vl->upd_busy_cnt = 0;
L
Linus Torvalds 已提交
664

D
David Howells 已提交
665
	ret = afs_vlocation_update_record(vl, NULL, &vldb);
666 667 668 669 670 671 672 673 674 675 676 677
	switch (ret) {
	case 0:
		afs_vlocation_apply_update(vl, &vldb);
		vl->state = AFS_VL_VALID;
		break;
	case -ENOMEDIUM:
		vl->state = AFS_VL_VOLUME_DELETED;
		break;
	default:
		vl->state = AFS_VL_UNCERTAIN;
		break;
	}
L
Linus Torvalds 已提交
678

679 680 681
	/* and then reschedule */
	_debug("reschedule");
	vl->update_at = get_seconds() + afs_vlocation_update_timeout;
L
Linus Torvalds 已提交
682

683
	spin_lock(&afs_vlocation_updates_lock);
L
Linus Torvalds 已提交
684

685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
	if (!list_empty(&afs_vlocation_updates)) {
		/* next update in 10 minutes, but wait at least 1 second more
		 * than the newest record already queued so that we don't spam
		 * the VL server suddenly with lots of requests
		 */
		xvl = list_entry(afs_vlocation_updates.prev,
				 struct afs_vlocation, update);
		if (vl->update_at <= xvl->update_at)
			vl->update_at = xvl->update_at + 1;
		xvl = list_entry(afs_vlocation_updates.next,
				 struct afs_vlocation, update);
		timeout = xvl->update_at - now;
		if (timeout < 0)
			timeout = 0;
	} else {
		timeout = afs_vlocation_update_timeout;
L
Linus Torvalds 已提交
701 702
	}

703
	ASSERT(list_empty(&vl->update));
L
Linus Torvalds 已提交
704

705
	list_add_tail(&vl->update, &afs_vlocation_updates);
L
Linus Torvalds 已提交
706

707 708 709 710 711
	_debug("timeout %ld", timeout);
	queue_delayed_work(afs_vlocation_update_worker,
			   &afs_vlocation_update, timeout * HZ);
	spin_unlock(&afs_vlocation_updates_lock);
	afs_put_vlocation(vl);
D
David Howells 已提交
712
}