blob: e48728c92175a16d6b7cdf1d07758b4c0f9d48ab [file] [log] [blame]
David Howellsec268152007-04-26 15:49:28 -07001/* volume location management
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 *
3 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12#include <linux/kernel.h>
13#include <linux/module.h>
14#include <linux/init.h>
15#include <linux/slab.h>
16#include <linux/fs.h>
17#include <linux/pagemap.h>
18#include "volume.h"
19#include "cell.h"
20#include "cmservice.h"
21#include "fsclient.h"
22#include "vlclient.h"
23#include "kafstimod.h"
24#include <rxrpc/connection.h>
25#include "internal.h"
26
27#define AFS_VLDB_TIMEOUT HZ*1000
28
29static void afs_vlocation_update_timer(struct afs_timer *timer);
30static void afs_vlocation_update_attend(struct afs_async_op *op);
31static void afs_vlocation_update_discard(struct afs_async_op *op);
32static void __afs_put_vlocation(struct afs_vlocation *vlocation);
33
34static void __afs_vlocation_timeout(struct afs_timer *timer)
35{
36 struct afs_vlocation *vlocation =
37 list_entry(timer, struct afs_vlocation, timeout);
38
39 _debug("VL TIMEOUT [%s{u=%d}]",
40 vlocation->vldb.name, atomic_read(&vlocation->usage));
41
42 afs_vlocation_do_timeout(vlocation);
43}
44
45static const struct afs_timer_ops afs_vlocation_timer_ops = {
46 .timed_out = __afs_vlocation_timeout,
47};
48
49static const struct afs_timer_ops afs_vlocation_update_timer_ops = {
50 .timed_out = afs_vlocation_update_timer,
51};
52
53static const struct afs_async_op_ops afs_vlocation_update_op_ops = {
54 .attend = afs_vlocation_update_attend,
55 .discard = afs_vlocation_update_discard,
56};
57
58static LIST_HEAD(afs_vlocation_update_pendq); /* queue of VLs awaiting update */
59static struct afs_vlocation *afs_vlocation_update; /* VL currently being updated */
60static DEFINE_SPINLOCK(afs_vlocation_update_lock); /* lock guarding update queue */
61
62#ifdef AFS_CACHING_SUPPORT
63static cachefs_match_val_t afs_vlocation_cache_match(void *target,
64 const void *entry);
65static void afs_vlocation_cache_update(void *source, void *entry);
66
67struct cachefs_index_def afs_vlocation_cache_index_def = {
68 .name = "vldb",
69 .data_size = sizeof(struct afs_cache_vlocation),
70 .keys[0] = { CACHEFS_INDEX_KEYS_ASCIIZ, 64 },
71 .match = afs_vlocation_cache_match,
72 .update = afs_vlocation_cache_update,
73};
74#endif
75
Linus Torvalds1da177e2005-04-16 15:20:36 -070076/*
77 * iterate through the VL servers in a cell until one of them admits knowing
78 * about the volume in question
79 * - caller must have cell->vl_sem write-locked
80 */
81static int afs_vlocation_access_vl_by_name(struct afs_vlocation *vlocation,
82 const char *name,
83 unsigned namesz,
84 struct afs_cache_vlocation *vldb)
85{
86 struct afs_server *server = NULL;
87 struct afs_cell *cell = vlocation->cell;
88 int count, ret;
89
90 _enter("%s,%*.*s,%u", cell->name, namesz, namesz, name, namesz);
91
92 ret = -ENOMEDIUM;
93 for (count = cell->vl_naddrs; count > 0; count--) {
94 _debug("CellServ[%hu]: %08x",
95 cell->vl_curr_svix,
96 cell->vl_addrs[cell->vl_curr_svix].s_addr);
97
98 /* try and create a server */
99 ret = afs_server_lookup(cell,
100 &cell->vl_addrs[cell->vl_curr_svix],
101 &server);
102 switch (ret) {
103 case 0:
104 break;
105 case -ENOMEM:
106 case -ENONET:
107 goto out;
108 default:
109 goto rotate;
110 }
111
112 /* attempt to access the VL server */
113 ret = afs_rxvl_get_entry_by_name(server, name, namesz, vldb);
114 switch (ret) {
115 case 0:
116 afs_put_server(server);
117 goto out;
118 case -ENOMEM:
119 case -ENONET:
120 case -ENETUNREACH:
121 case -EHOSTUNREACH:
122 case -ECONNREFUSED:
123 down_write(&server->sem);
124 if (server->vlserver) {
125 rxrpc_put_connection(server->vlserver);
126 server->vlserver = NULL;
127 }
128 up_write(&server->sem);
129 afs_put_server(server);
130 if (ret == -ENOMEM || ret == -ENONET)
131 goto out;
132 goto rotate;
133 case -ENOMEDIUM:
134 afs_put_server(server);
135 goto out;
136 default:
137 afs_put_server(server);
138 ret = -ENOMEDIUM;
139 goto rotate;
140 }
141
142 /* rotate the server records upon lookup failure */
143 rotate:
144 cell->vl_curr_svix++;
145 cell->vl_curr_svix %= cell->vl_naddrs;
146 }
147
David Howellsec268152007-04-26 15:49:28 -0700148out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149 _leave(" = %d", ret);
150 return ret;
David Howellsec268152007-04-26 15:49:28 -0700151}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153/*
154 * iterate through the VL servers in a cell until one of them admits knowing
155 * about the volume in question
156 * - caller must have cell->vl_sem write-locked
157 */
158static int afs_vlocation_access_vl_by_id(struct afs_vlocation *vlocation,
159 afs_volid_t volid,
160 afs_voltype_t voltype,
161 struct afs_cache_vlocation *vldb)
162{
163 struct afs_server *server = NULL;
164 struct afs_cell *cell = vlocation->cell;
165 int count, ret;
166
167 _enter("%s,%x,%d,", cell->name, volid, voltype);
168
169 ret = -ENOMEDIUM;
170 for (count = cell->vl_naddrs; count > 0; count--) {
171 _debug("CellServ[%hu]: %08x",
172 cell->vl_curr_svix,
173 cell->vl_addrs[cell->vl_curr_svix].s_addr);
174
175 /* try and create a server */
176 ret = afs_server_lookup(cell,
177 &cell->vl_addrs[cell->vl_curr_svix],
178 &server);
179 switch (ret) {
180 case 0:
181 break;
182 case -ENOMEM:
183 case -ENONET:
184 goto out;
185 default:
186 goto rotate;
187 }
188
189 /* attempt to access the VL server */
190 ret = afs_rxvl_get_entry_by_id(server, volid, voltype, vldb);
191 switch (ret) {
192 case 0:
193 afs_put_server(server);
194 goto out;
195 case -ENOMEM:
196 case -ENONET:
197 case -ENETUNREACH:
198 case -EHOSTUNREACH:
199 case -ECONNREFUSED:
200 down_write(&server->sem);
201 if (server->vlserver) {
202 rxrpc_put_connection(server->vlserver);
203 server->vlserver = NULL;
204 }
205 up_write(&server->sem);
206 afs_put_server(server);
207 if (ret == -ENOMEM || ret == -ENONET)
208 goto out;
209 goto rotate;
210 case -ENOMEDIUM:
211 afs_put_server(server);
212 goto out;
213 default:
214 afs_put_server(server);
215 ret = -ENOMEDIUM;
216 goto rotate;
217 }
218
219 /* rotate the server records upon lookup failure */
220 rotate:
221 cell->vl_curr_svix++;
222 cell->vl_curr_svix %= cell->vl_naddrs;
223 }
224
David Howellsec268152007-04-26 15:49:28 -0700225out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 _leave(" = %d", ret);
227 return ret;
David Howellsec268152007-04-26 15:49:28 -0700228}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230/*
231 * lookup volume location
232 * - caller must have cell->vol_sem write-locked
233 * - iterate through the VL servers in a cell until one of them admits knowing
234 * about the volume in question
235 * - lookup in the local cache if not able to find on the VL server
236 * - insert/update in the local cache if did get a VL response
237 */
238int afs_vlocation_lookup(struct afs_cell *cell,
239 const char *name,
240 unsigned namesz,
241 struct afs_vlocation **_vlocation)
242{
243 struct afs_cache_vlocation vldb;
244 struct afs_vlocation *vlocation;
245 afs_voltype_t voltype;
246 afs_volid_t vid;
247 int active = 0, ret;
248
249 _enter("{%s},%*.*s,%u,", cell->name, namesz, namesz, name, namesz);
250
251 if (namesz > sizeof(vlocation->vldb.name)) {
252 _leave(" = -ENAMETOOLONG");
253 return -ENAMETOOLONG;
254 }
255
256 /* search the cell's active list first */
257 list_for_each_entry(vlocation, &cell->vl_list, link) {
258 if (namesz < sizeof(vlocation->vldb.name) &&
259 vlocation->vldb.name[namesz] != '\0')
260 continue;
261
262 if (memcmp(vlocation->vldb.name, name, namesz) == 0)
263 goto found_in_memory;
264 }
265
266 /* search the cell's graveyard list second */
267 spin_lock(&cell->vl_gylock);
268 list_for_each_entry(vlocation, &cell->vl_graveyard, link) {
269 if (namesz < sizeof(vlocation->vldb.name) &&
270 vlocation->vldb.name[namesz] != '\0')
271 continue;
272
273 if (memcmp(vlocation->vldb.name, name, namesz) == 0)
274 goto found_in_graveyard;
275 }
276 spin_unlock(&cell->vl_gylock);
277
278 /* not in the cell's in-memory lists - create a new record */
Panagiotis Issarisf8314dc2006-09-27 01:49:37 -0700279 vlocation = kzalloc(sizeof(struct afs_vlocation), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 if (!vlocation)
281 return -ENOMEM;
282
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 atomic_set(&vlocation->usage, 1);
284 INIT_LIST_HEAD(&vlocation->link);
285 rwlock_init(&vlocation->lock);
286 memcpy(vlocation->vldb.name, name, namesz);
287
288 afs_timer_init(&vlocation->timeout, &afs_vlocation_timer_ops);
289 afs_timer_init(&vlocation->upd_timer, &afs_vlocation_update_timer_ops);
290 afs_async_op_init(&vlocation->upd_op, &afs_vlocation_update_op_ops);
291
292 afs_get_cell(cell);
293 vlocation->cell = cell;
294
295 list_add_tail(&vlocation->link, &cell->vl_list);
296
297#ifdef AFS_CACHING_SUPPORT
298 /* we want to store it in the cache, plus it might already be
299 * encached */
300 cachefs_acquire_cookie(cell->cache,
301 &afs_volume_cache_index_def,
302 vlocation,
303 &vlocation->cache);
304
305 if (vlocation->valid)
306 goto found_in_cache;
307#endif
308
309 /* try to look up an unknown volume in the cell VL databases by name */
310 ret = afs_vlocation_access_vl_by_name(vlocation, name, namesz, &vldb);
311 if (ret < 0) {
312 printk("kAFS: failed to locate '%*.*s' in cell '%s'\n",
313 namesz, namesz, name, cell->name);
314 goto error;
315 }
316
317 goto found_on_vlserver;
318
David Howellsec268152007-04-26 15:49:28 -0700319found_in_graveyard:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 /* found in the graveyard - resurrect */
321 _debug("found in graveyard");
322 atomic_inc(&vlocation->usage);
Akinobu Mitaf1166292006-06-26 00:24:46 -0700323 list_move_tail(&vlocation->link, &cell->vl_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 spin_unlock(&cell->vl_gylock);
325
326 afs_kafstimod_del_timer(&vlocation->timeout);
327 goto active;
328
David Howellsec268152007-04-26 15:49:28 -0700329found_in_memory:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 /* found in memory - check to see if it's active */
331 _debug("found in memory");
332 atomic_inc(&vlocation->usage);
333
David Howellsec268152007-04-26 15:49:28 -0700334active:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 active = 1;
336
337#ifdef AFS_CACHING_SUPPORT
David Howellsec268152007-04-26 15:49:28 -0700338found_in_cache:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339#endif
340 /* try to look up a cached volume in the cell VL databases by ID */
341 _debug("found in cache");
342
343 _debug("Locally Cached: %s %02x { %08x(%x) %08x(%x) %08x(%x) }",
344 vlocation->vldb.name,
345 vlocation->vldb.vidmask,
346 ntohl(vlocation->vldb.servers[0].s_addr),
347 vlocation->vldb.srvtmask[0],
348 ntohl(vlocation->vldb.servers[1].s_addr),
349 vlocation->vldb.srvtmask[1],
350 ntohl(vlocation->vldb.servers[2].s_addr),
351 vlocation->vldb.srvtmask[2]
352 );
353
354 _debug("Vids: %08x %08x %08x",
355 vlocation->vldb.vid[0],
356 vlocation->vldb.vid[1],
357 vlocation->vldb.vid[2]);
358
359 if (vlocation->vldb.vidmask & AFS_VOL_VTM_RW) {
360 vid = vlocation->vldb.vid[0];
361 voltype = AFSVL_RWVOL;
David Howellsec268152007-04-26 15:49:28 -0700362 } else if (vlocation->vldb.vidmask & AFS_VOL_VTM_RO) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 vid = vlocation->vldb.vid[1];
364 voltype = AFSVL_ROVOL;
David Howellsec268152007-04-26 15:49:28 -0700365 } else if (vlocation->vldb.vidmask & AFS_VOL_VTM_BAK) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 vid = vlocation->vldb.vid[2];
367 voltype = AFSVL_BACKVOL;
David Howellsec268152007-04-26 15:49:28 -0700368 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 BUG();
370 vid = 0;
371 voltype = 0;
372 }
373
374 ret = afs_vlocation_access_vl_by_id(vlocation, vid, voltype, &vldb);
375 switch (ret) {
376 /* net error */
377 default:
378 printk("kAFS: failed to volume '%*.*s' (%x) up in '%s': %d\n",
379 namesz, namesz, name, vid, cell->name, ret);
380 goto error;
381
382 /* pulled from local cache into memory */
383 case 0:
384 goto found_on_vlserver;
385
386 /* uh oh... looks like the volume got deleted */
387 case -ENOMEDIUM:
388 printk("kAFS: volume '%*.*s' (%x) does not exist '%s'\n",
389 namesz, namesz, name, vid, cell->name);
390
391 /* TODO: make existing record unavailable */
392 goto error;
393 }
394
David Howellsec268152007-04-26 15:49:28 -0700395found_on_vlserver:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 _debug("Done VL Lookup: %*.*s %02x { %08x(%x) %08x(%x) %08x(%x) }",
397 namesz, namesz, name,
398 vldb.vidmask,
399 ntohl(vldb.servers[0].s_addr), vldb.srvtmask[0],
400 ntohl(vldb.servers[1].s_addr), vldb.srvtmask[1],
401 ntohl(vldb.servers[2].s_addr), vldb.srvtmask[2]
402 );
403
404 _debug("Vids: %08x %08x %08x", vldb.vid[0], vldb.vid[1], vldb.vid[2]);
405
406 if ((namesz < sizeof(vlocation->vldb.name) &&
407 vlocation->vldb.name[namesz] != '\0') ||
408 memcmp(vldb.name, name, namesz) != 0)
409 printk("kAFS: name of volume '%*.*s' changed to '%s' on server\n",
410 namesz, namesz, name, vldb.name);
411
412 memcpy(&vlocation->vldb, &vldb, sizeof(vlocation->vldb));
413
414 afs_kafstimod_add_timer(&vlocation->upd_timer, 10 * HZ);
415
416#ifdef AFS_CACHING_SUPPORT
417 /* update volume entry in local cache */
418 cachefs_update_cookie(vlocation->cache);
419#endif
420
421 *_vlocation = vlocation;
422 _leave(" = 0 (%p)",vlocation);
423 return 0;
424
David Howellsec268152007-04-26 15:49:28 -0700425error:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 if (vlocation) {
427 if (active) {
428 __afs_put_vlocation(vlocation);
David Howellsec268152007-04-26 15:49:28 -0700429 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 list_del(&vlocation->link);
431#ifdef AFS_CACHING_SUPPORT
432 cachefs_relinquish_cookie(vlocation->cache, 0);
433#endif
434 afs_put_cell(vlocation->cell);
435 kfree(vlocation);
436 }
437 }
438
439 _leave(" = %d", ret);
440 return ret;
David Howellsec268152007-04-26 15:49:28 -0700441}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443/*
444 * finish using a volume location record
445 * - caller must have cell->vol_sem write-locked
446 */
447static void __afs_put_vlocation(struct afs_vlocation *vlocation)
448{
449 struct afs_cell *cell;
450
451 if (!vlocation)
452 return;
453
454 _enter("%s", vlocation->vldb.name);
455
456 cell = vlocation->cell;
457
458 /* sanity check */
459 BUG_ON(atomic_read(&vlocation->usage) <= 0);
460
461 spin_lock(&cell->vl_gylock);
462 if (likely(!atomic_dec_and_test(&vlocation->usage))) {
463 spin_unlock(&cell->vl_gylock);
464 _leave("");
465 return;
466 }
467
468 /* move to graveyard queue */
Akinobu Mitaf1166292006-06-26 00:24:46 -0700469 list_move_tail(&vlocation->link,&cell->vl_graveyard);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470
471 /* remove from pending timeout queue (refcounted if actually being
472 * updated) */
473 list_del_init(&vlocation->upd_op.link);
474
475 /* time out in 10 secs */
476 afs_kafstimod_del_timer(&vlocation->upd_timer);
477 afs_kafstimod_add_timer(&vlocation->timeout, 10 * HZ);
478
479 spin_unlock(&cell->vl_gylock);
480
481 _leave(" [killed]");
David Howellsec268152007-04-26 15:49:28 -0700482}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484/*
485 * finish using a volume location record
486 */
487void afs_put_vlocation(struct afs_vlocation *vlocation)
488{
489 if (vlocation) {
490 struct afs_cell *cell = vlocation->cell;
491
492 down_write(&cell->vl_sem);
493 __afs_put_vlocation(vlocation);
494 up_write(&cell->vl_sem);
495 }
David Howellsec268152007-04-26 15:49:28 -0700496}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498/*
499 * timeout vlocation record
500 * - removes from the cell's graveyard if the usage count is zero
501 */
502void afs_vlocation_do_timeout(struct afs_vlocation *vlocation)
503{
504 struct afs_cell *cell;
505
506 _enter("%s", vlocation->vldb.name);
507
508 cell = vlocation->cell;
509
510 BUG_ON(atomic_read(&vlocation->usage) < 0);
511
512 /* remove from graveyard if still dead */
513 spin_lock(&cell->vl_gylock);
514 if (atomic_read(&vlocation->usage) == 0)
515 list_del_init(&vlocation->link);
516 else
517 vlocation = NULL;
518 spin_unlock(&cell->vl_gylock);
519
520 if (!vlocation) {
521 _leave("");
522 return; /* resurrected */
523 }
524
525 /* we can now destroy it properly */
526#ifdef AFS_CACHING_SUPPORT
527 cachefs_relinquish_cookie(vlocation->cache, 0);
528#endif
529 afs_put_cell(cell);
530
531 kfree(vlocation);
532
533 _leave(" [destroyed]");
David Howellsec268152007-04-26 15:49:28 -0700534}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536/*
537 * send an update operation to the currently selected server
538 */
539static int afs_vlocation_update_begin(struct afs_vlocation *vlocation)
540{
541 afs_voltype_t voltype;
542 afs_volid_t vid;
543 int ret;
544
545 _enter("%s{ufs=%u ucs=%u}",
546 vlocation->vldb.name,
547 vlocation->upd_first_svix,
548 vlocation->upd_curr_svix);
549
550 /* try to look up a cached volume in the cell VL databases by ID */
551 if (vlocation->vldb.vidmask & AFS_VOL_VTM_RW) {
552 vid = vlocation->vldb.vid[0];
553 voltype = AFSVL_RWVOL;
David Howellsec268152007-04-26 15:49:28 -0700554 } else if (vlocation->vldb.vidmask & AFS_VOL_VTM_RO) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 vid = vlocation->vldb.vid[1];
556 voltype = AFSVL_ROVOL;
David Howellsec268152007-04-26 15:49:28 -0700557 } else if (vlocation->vldb.vidmask & AFS_VOL_VTM_BAK) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 vid = vlocation->vldb.vid[2];
559 voltype = AFSVL_BACKVOL;
David Howellsec268152007-04-26 15:49:28 -0700560 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 BUG();
562 vid = 0;
563 voltype = 0;
564 }
565
566 /* contact the chosen server */
567 ret = afs_server_lookup(
568 vlocation->cell,
569 &vlocation->cell->vl_addrs[vlocation->upd_curr_svix],
570 &vlocation->upd_op.server);
571
572 switch (ret) {
573 case 0:
574 break;
575 case -ENOMEM:
576 case -ENONET:
577 default:
578 _leave(" = %d", ret);
579 return ret;
580 }
581
582 /* initiate the update operation */
583 ret = afs_rxvl_get_entry_by_id_async(&vlocation->upd_op, vid, voltype);
584 if (ret < 0) {
585 _leave(" = %d", ret);
586 return ret;
587 }
588
589 _leave(" = %d", ret);
590 return ret;
David Howellsec268152007-04-26 15:49:28 -0700591}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593/*
594 * abandon updating a VL record
595 * - does not restart the update timer
596 */
597static void afs_vlocation_update_abandon(struct afs_vlocation *vlocation,
598 afs_vlocation_upd_t state,
599 int ret)
600{
601 _enter("%s,%u", vlocation->vldb.name, state);
602
603 if (ret < 0)
604 printk("kAFS: Abandoning VL update '%s': %d\n",
605 vlocation->vldb.name, ret);
606
607 /* discard the server record */
608 afs_put_server(vlocation->upd_op.server);
609 vlocation->upd_op.server = NULL;
610
611 spin_lock(&afs_vlocation_update_lock);
612 afs_vlocation_update = NULL;
613 vlocation->upd_state = state;
614
615 /* TODO: start updating next VL record on pending list */
616
617 spin_unlock(&afs_vlocation_update_lock);
618
619 _leave("");
David Howellsec268152007-04-26 15:49:28 -0700620}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622/*
623 * handle periodic update timeouts and busy retry timeouts
624 * - called from kafstimod
625 */
626static void afs_vlocation_update_timer(struct afs_timer *timer)
627{
628 struct afs_vlocation *vlocation =
629 list_entry(timer, struct afs_vlocation, upd_timer);
630 int ret;
631
632 _enter("%s", vlocation->vldb.name);
633
634 /* only update if not in the graveyard (defend against putting too) */
635 spin_lock(&vlocation->cell->vl_gylock);
636
637 if (!atomic_read(&vlocation->usage))
638 goto out_unlock1;
639
640 spin_lock(&afs_vlocation_update_lock);
641
642 /* if we were woken up due to EBUSY sleep then restart immediately if
643 * possible or else jump to front of pending queue */
644 if (vlocation->upd_state == AFS_VLUPD_BUSYSLEEP) {
645 if (afs_vlocation_update) {
646 list_add(&vlocation->upd_op.link,
647 &afs_vlocation_update_pendq);
David Howellsec268152007-04-26 15:49:28 -0700648 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 afs_get_vlocation(vlocation);
650 afs_vlocation_update = vlocation;
651 vlocation->upd_state = AFS_VLUPD_INPROGRESS;
652 }
653 goto out_unlock2;
654 }
655
656 /* put on pending queue if there's already another update in progress */
657 if (afs_vlocation_update) {
658 vlocation->upd_state = AFS_VLUPD_PENDING;
659 list_add_tail(&vlocation->upd_op.link,
660 &afs_vlocation_update_pendq);
661 goto out_unlock2;
662 }
663
664 /* hold a ref on it while actually updating */
665 afs_get_vlocation(vlocation);
666 afs_vlocation_update = vlocation;
667 vlocation->upd_state = AFS_VLUPD_INPROGRESS;
668
669 spin_unlock(&afs_vlocation_update_lock);
670 spin_unlock(&vlocation->cell->vl_gylock);
671
672 /* okay... we can start the update */
673 _debug("BEGIN VL UPDATE [%s]", vlocation->vldb.name);
674 vlocation->upd_first_svix = vlocation->cell->vl_curr_svix;
675 vlocation->upd_curr_svix = vlocation->upd_first_svix;
676 vlocation->upd_rej_cnt = 0;
677 vlocation->upd_busy_cnt = 0;
678
679 ret = afs_vlocation_update_begin(vlocation);
680 if (ret < 0) {
681 afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, ret);
682 afs_kafstimod_add_timer(&vlocation->upd_timer,
683 AFS_VLDB_TIMEOUT);
684 afs_put_vlocation(vlocation);
685 }
686
687 _leave("");
688 return;
689
David Howellsec268152007-04-26 15:49:28 -0700690out_unlock2:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 spin_unlock(&afs_vlocation_update_lock);
David Howellsec268152007-04-26 15:49:28 -0700692out_unlock1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 spin_unlock(&vlocation->cell->vl_gylock);
694 _leave("");
David Howellsec268152007-04-26 15:49:28 -0700695}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697/*
698 * attend to an update operation upon which an event happened
699 * - called in kafsasyncd context
700 */
701static void afs_vlocation_update_attend(struct afs_async_op *op)
702{
703 struct afs_cache_vlocation vldb;
704 struct afs_vlocation *vlocation =
705 list_entry(op, struct afs_vlocation, upd_op);
706 unsigned tmp;
707 int ret;
708
709 _enter("%s", vlocation->vldb.name);
710
711 ret = afs_rxvl_get_entry_by_id_async2(op, &vldb);
712 switch (ret) {
713 case -EAGAIN:
714 _leave(" [unfinished]");
715 return;
716
717 case 0:
718 _debug("END VL UPDATE: %d\n", ret);
719 vlocation->valid = 1;
720
721 _debug("Done VL Lookup: %02x { %08x(%x) %08x(%x) %08x(%x) }",
722 vldb.vidmask,
723 ntohl(vldb.servers[0].s_addr), vldb.srvtmask[0],
724 ntohl(vldb.servers[1].s_addr), vldb.srvtmask[1],
725 ntohl(vldb.servers[2].s_addr), vldb.srvtmask[2]
726 );
727
728 _debug("Vids: %08x %08x %08x",
729 vldb.vid[0], vldb.vid[1], vldb.vid[2]);
730
731 afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, 0);
732
733 down_write(&vlocation->cell->vl_sem);
734
735 /* actually update the cache */
736 if (strncmp(vldb.name, vlocation->vldb.name,
737 sizeof(vlocation->vldb.name)) != 0)
738 printk("kAFS: name of volume '%s'"
739 " changed to '%s' on server\n",
740 vlocation->vldb.name, vldb.name);
741
742 memcpy(&vlocation->vldb, &vldb, sizeof(vlocation->vldb));
743
744#if 0
745 /* TODO update volume entry in local cache */
746#endif
747
748 up_write(&vlocation->cell->vl_sem);
749
750 if (ret < 0)
751 printk("kAFS: failed to update local cache: %d\n", ret);
752
753 afs_kafstimod_add_timer(&vlocation->upd_timer,
754 AFS_VLDB_TIMEOUT);
755 afs_put_vlocation(vlocation);
756 _leave(" [found]");
757 return;
758
759 case -ENOMEDIUM:
760 vlocation->upd_rej_cnt++;
761 goto try_next;
762
763 /* the server is locked - retry in a very short while */
764 case -EBUSY:
765 vlocation->upd_busy_cnt++;
766 if (vlocation->upd_busy_cnt > 3)
767 goto try_next; /* too many retries */
768
769 afs_vlocation_update_abandon(vlocation,
770 AFS_VLUPD_BUSYSLEEP, 0);
771 afs_kafstimod_add_timer(&vlocation->upd_timer, HZ / 2);
772 afs_put_vlocation(vlocation);
773 _leave(" [busy]");
774 return;
775
776 case -ENETUNREACH:
777 case -EHOSTUNREACH:
778 case -ECONNREFUSED:
779 case -EREMOTEIO:
780 /* record bad vlserver info in the cell too
781 * - TODO: use down_write_trylock() if available
782 */
783 if (vlocation->upd_curr_svix == vlocation->cell->vl_curr_svix)
784 vlocation->cell->vl_curr_svix =
785 vlocation->cell->vl_curr_svix %
786 vlocation->cell->vl_naddrs;
787
788 case -EBADRQC:
789 case -EINVAL:
790 case -EACCES:
791 case -EBADMSG:
792 goto try_next;
793
794 default:
795 goto abandon;
796 }
797
798 /* try contacting the next server */
David Howellsec268152007-04-26 15:49:28 -0700799try_next:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 vlocation->upd_busy_cnt = 0;
801
802 /* discard the server record */
803 afs_put_server(vlocation->upd_op.server);
804 vlocation->upd_op.server = NULL;
805
806 tmp = vlocation->cell->vl_naddrs;
807 if (tmp == 0)
808 goto abandon;
809
810 vlocation->upd_curr_svix++;
811 if (vlocation->upd_curr_svix >= tmp)
812 vlocation->upd_curr_svix = 0;
813 if (vlocation->upd_first_svix >= tmp)
814 vlocation->upd_first_svix = tmp - 1;
815
816 /* move to the next server */
817 if (vlocation->upd_curr_svix != vlocation->upd_first_svix) {
818 afs_vlocation_update_begin(vlocation);
819 _leave(" [next]");
820 return;
821 }
822
823 /* run out of servers to try - was the volume rejected? */
824 if (vlocation->upd_rej_cnt > 0) {
825 printk("kAFS: Active volume no longer valid '%s'\n",
826 vlocation->vldb.name);
827 vlocation->valid = 0;
828 afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, 0);
829 afs_kafstimod_add_timer(&vlocation->upd_timer,
830 AFS_VLDB_TIMEOUT);
831 afs_put_vlocation(vlocation);
832 _leave(" [invalidated]");
833 return;
834 }
835
836 /* abandon the update */
David Howellsec268152007-04-26 15:49:28 -0700837abandon:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, ret);
839 afs_kafstimod_add_timer(&vlocation->upd_timer, HZ * 10);
840 afs_put_vlocation(vlocation);
841 _leave(" [abandoned]");
David Howellsec268152007-04-26 15:49:28 -0700842}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844/*
845 * deal with an update operation being discarded
846 * - called in kafsasyncd context when it's dying due to rmmod
847 * - the call has already been aborted and put()'d
848 */
849static void afs_vlocation_update_discard(struct afs_async_op *op)
850{
851 struct afs_vlocation *vlocation =
852 list_entry(op, struct afs_vlocation, upd_op);
853
854 _enter("%s", vlocation->vldb.name);
855
856 afs_put_server(op->server);
857 op->server = NULL;
858
859 afs_put_vlocation(vlocation);
860
861 _leave("");
David Howellsec268152007-04-26 15:49:28 -0700862}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864/*
865 * match a VLDB record stored in the cache
866 * - may also load target from entry
867 */
868#ifdef AFS_CACHING_SUPPORT
869static cachefs_match_val_t afs_vlocation_cache_match(void *target,
870 const void *entry)
871{
872 const struct afs_cache_vlocation *vldb = entry;
873 struct afs_vlocation *vlocation = target;
874
875 _enter("{%s},{%s}", vlocation->vldb.name, vldb->name);
876
877 if (strncmp(vlocation->vldb.name, vldb->name, sizeof(vldb->name)) == 0
878 ) {
879 if (!vlocation->valid ||
880 vlocation->vldb.rtime == vldb->rtime
881 ) {
882 vlocation->vldb = *vldb;
883 vlocation->valid = 1;
884 _leave(" = SUCCESS [c->m]");
885 return CACHEFS_MATCH_SUCCESS;
David Howellsec268152007-04-26 15:49:28 -0700886 } else if (memcmp(&vlocation->vldb, vldb, sizeof(*vldb)) != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887 /* delete if VIDs for this name differ */
888 if (memcmp(&vlocation->vldb.vid,
889 &vldb->vid,
890 sizeof(vldb->vid)) != 0) {
891 _leave(" = DELETE");
892 return CACHEFS_MATCH_SUCCESS_DELETE;
893 }
894
895 _leave(" = UPDATE");
896 return CACHEFS_MATCH_SUCCESS_UPDATE;
David Howellsec268152007-04-26 15:49:28 -0700897 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898 _leave(" = SUCCESS");
899 return CACHEFS_MATCH_SUCCESS;
900 }
901 }
902
903 _leave(" = FAILED");
904 return CACHEFS_MATCH_FAILED;
David Howellsec268152007-04-26 15:49:28 -0700905}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906#endif
907
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908/*
909 * update a VLDB record stored in the cache
910 */
911#ifdef AFS_CACHING_SUPPORT
912static void afs_vlocation_cache_update(void *source, void *entry)
913{
914 struct afs_cache_vlocation *vldb = entry;
915 struct afs_vlocation *vlocation = source;
916
917 _enter("");
918
919 *vldb = vlocation->vldb;
David Howellsec268152007-04-26 15:49:28 -0700920}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921#endif