diff options
Diffstat (limited to 'fs/afs/vlocation.c')
-rw-r--r-- | fs/afs/vlocation.c | 954 |
1 files changed, 954 insertions, 0 deletions
diff --git a/fs/afs/vlocation.c b/fs/afs/vlocation.c new file mode 100644 index 000000000000..eced20618ecc --- /dev/null +++ b/fs/afs/vlocation.c | |||
@@ -0,0 +1,954 @@ | |||
1 | /* vlocation.c: volume location management | ||
2 | * | ||
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 | |||
29 | static void afs_vlocation_update_timer(struct afs_timer *timer); | ||
30 | static void afs_vlocation_update_attend(struct afs_async_op *op); | ||
31 | static void afs_vlocation_update_discard(struct afs_async_op *op); | ||
32 | static void __afs_put_vlocation(struct afs_vlocation *vlocation); | ||
33 | |||
34 | static 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 | |||
45 | static const struct afs_timer_ops afs_vlocation_timer_ops = { | ||
46 | .timed_out = __afs_vlocation_timeout, | ||
47 | }; | ||
48 | |||
49 | static const struct afs_timer_ops afs_vlocation_update_timer_ops = { | ||
50 | .timed_out = afs_vlocation_update_timer, | ||
51 | }; | ||
52 | |||
53 | static 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 | |||
58 | static LIST_HEAD(afs_vlocation_update_pendq); /* queue of VLs awaiting update */ | ||
59 | static struct afs_vlocation *afs_vlocation_update; /* VL currently being updated */ | ||
60 | static DEFINE_SPINLOCK(afs_vlocation_update_lock); /* lock guarding update queue */ | ||
61 | |||
62 | #ifdef AFS_CACHING_SUPPORT | ||
63 | static cachefs_match_val_t afs_vlocation_cache_match(void *target, | ||
64 | const void *entry); | ||
65 | static void afs_vlocation_cache_update(void *source, void *entry); | ||
66 | |||
67 | struct 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 | |||
76 | /*****************************************************************************/ | ||
77 | /* | ||
78 | * iterate through the VL servers in a cell until one of them admits knowing | ||
79 | * about the volume in question | ||
80 | * - caller must have cell->vl_sem write-locked | ||
81 | */ | ||
82 | static int afs_vlocation_access_vl_by_name(struct afs_vlocation *vlocation, | ||
83 | const char *name, | ||
84 | unsigned namesz, | ||
85 | struct afs_cache_vlocation *vldb) | ||
86 | { | ||
87 | struct afs_server *server = NULL; | ||
88 | struct afs_cell *cell = vlocation->cell; | ||
89 | int count, ret; | ||
90 | |||
91 | _enter("%s,%*.*s,%u", cell->name, namesz, namesz, name, namesz); | ||
92 | |||
93 | ret = -ENOMEDIUM; | ||
94 | for (count = cell->vl_naddrs; count > 0; count--) { | ||
95 | _debug("CellServ[%hu]: %08x", | ||
96 | cell->vl_curr_svix, | ||
97 | cell->vl_addrs[cell->vl_curr_svix].s_addr); | ||
98 | |||
99 | /* try and create a server */ | ||
100 | ret = afs_server_lookup(cell, | ||
101 | &cell->vl_addrs[cell->vl_curr_svix], | ||
102 | &server); | ||
103 | switch (ret) { | ||
104 | case 0: | ||
105 | break; | ||
106 | case -ENOMEM: | ||
107 | case -ENONET: | ||
108 | goto out; | ||
109 | default: | ||
110 | goto rotate; | ||
111 | } | ||
112 | |||
113 | /* attempt to access the VL server */ | ||
114 | ret = afs_rxvl_get_entry_by_name(server, name, namesz, vldb); | ||
115 | switch (ret) { | ||
116 | case 0: | ||
117 | afs_put_server(server); | ||
118 | goto out; | ||
119 | case -ENOMEM: | ||
120 | case -ENONET: | ||
121 | case -ENETUNREACH: | ||
122 | case -EHOSTUNREACH: | ||
123 | case -ECONNREFUSED: | ||
124 | down_write(&server->sem); | ||
125 | if (server->vlserver) { | ||
126 | rxrpc_put_connection(server->vlserver); | ||
127 | server->vlserver = NULL; | ||
128 | } | ||
129 | up_write(&server->sem); | ||
130 | afs_put_server(server); | ||
131 | if (ret == -ENOMEM || ret == -ENONET) | ||
132 | goto out; | ||
133 | goto rotate; | ||
134 | case -ENOMEDIUM: | ||
135 | afs_put_server(server); | ||
136 | goto out; | ||
137 | default: | ||
138 | afs_put_server(server); | ||
139 | ret = -ENOMEDIUM; | ||
140 | goto rotate; | ||
141 | } | ||
142 | |||
143 | /* rotate the server records upon lookup failure */ | ||
144 | rotate: | ||
145 | cell->vl_curr_svix++; | ||
146 | cell->vl_curr_svix %= cell->vl_naddrs; | ||
147 | } | ||
148 | |||
149 | out: | ||
150 | _leave(" = %d", ret); | ||
151 | return ret; | ||
152 | |||
153 | } /* end afs_vlocation_access_vl_by_name() */ | ||
154 | |||
155 | /*****************************************************************************/ | ||
156 | /* | ||
157 | * iterate through the VL servers in a cell until one of them admits knowing | ||
158 | * about the volume in question | ||
159 | * - caller must have cell->vl_sem write-locked | ||
160 | */ | ||
161 | static int afs_vlocation_access_vl_by_id(struct afs_vlocation *vlocation, | ||
162 | afs_volid_t volid, | ||
163 | afs_voltype_t voltype, | ||
164 | struct afs_cache_vlocation *vldb) | ||
165 | { | ||
166 | struct afs_server *server = NULL; | ||
167 | struct afs_cell *cell = vlocation->cell; | ||
168 | int count, ret; | ||
169 | |||
170 | _enter("%s,%x,%d,", cell->name, volid, voltype); | ||
171 | |||
172 | ret = -ENOMEDIUM; | ||
173 | for (count = cell->vl_naddrs; count > 0; count--) { | ||
174 | _debug("CellServ[%hu]: %08x", | ||
175 | cell->vl_curr_svix, | ||
176 | cell->vl_addrs[cell->vl_curr_svix].s_addr); | ||
177 | |||
178 | /* try and create a server */ | ||
179 | ret = afs_server_lookup(cell, | ||
180 | &cell->vl_addrs[cell->vl_curr_svix], | ||
181 | &server); | ||
182 | switch (ret) { | ||
183 | case 0: | ||
184 | break; | ||
185 | case -ENOMEM: | ||
186 | case -ENONET: | ||
187 | goto out; | ||
188 | default: | ||
189 | goto rotate; | ||
190 | } | ||
191 | |||
192 | /* attempt to access the VL server */ | ||
193 | ret = afs_rxvl_get_entry_by_id(server, volid, voltype, vldb); | ||
194 | switch (ret) { | ||
195 | case 0: | ||
196 | afs_put_server(server); | ||
197 | goto out; | ||
198 | case -ENOMEM: | ||
199 | case -ENONET: | ||
200 | case -ENETUNREACH: | ||
201 | case -EHOSTUNREACH: | ||
202 | case -ECONNREFUSED: | ||
203 | down_write(&server->sem); | ||
204 | if (server->vlserver) { | ||
205 | rxrpc_put_connection(server->vlserver); | ||
206 | server->vlserver = NULL; | ||
207 | } | ||
208 | up_write(&server->sem); | ||
209 | afs_put_server(server); | ||
210 | if (ret == -ENOMEM || ret == -ENONET) | ||
211 | goto out; | ||
212 | goto rotate; | ||
213 | case -ENOMEDIUM: | ||
214 | afs_put_server(server); | ||
215 | goto out; | ||
216 | default: | ||
217 | afs_put_server(server); | ||
218 | ret = -ENOMEDIUM; | ||
219 | goto rotate; | ||
220 | } | ||
221 | |||
222 | /* rotate the server records upon lookup failure */ | ||
223 | rotate: | ||
224 | cell->vl_curr_svix++; | ||
225 | cell->vl_curr_svix %= cell->vl_naddrs; | ||
226 | } | ||
227 | |||
228 | out: | ||
229 | _leave(" = %d", ret); | ||
230 | return ret; | ||
231 | |||
232 | } /* end afs_vlocation_access_vl_by_id() */ | ||
233 | |||
234 | /*****************************************************************************/ | ||
235 | /* | ||
236 | * lookup volume location | ||
237 | * - caller must have cell->vol_sem write-locked | ||
238 | * - iterate through the VL servers in a cell until one of them admits knowing | ||
239 | * about the volume in question | ||
240 | * - lookup in the local cache if not able to find on the VL server | ||
241 | * - insert/update in the local cache if did get a VL response | ||
242 | */ | ||
243 | int afs_vlocation_lookup(struct afs_cell *cell, | ||
244 | const char *name, | ||
245 | unsigned namesz, | ||
246 | struct afs_vlocation **_vlocation) | ||
247 | { | ||
248 | struct afs_cache_vlocation vldb; | ||
249 | struct afs_vlocation *vlocation; | ||
250 | afs_voltype_t voltype; | ||
251 | afs_volid_t vid; | ||
252 | int active = 0, ret; | ||
253 | |||
254 | _enter("{%s},%*.*s,%u,", cell->name, namesz, namesz, name, namesz); | ||
255 | |||
256 | if (namesz > sizeof(vlocation->vldb.name)) { | ||
257 | _leave(" = -ENAMETOOLONG"); | ||
258 | return -ENAMETOOLONG; | ||
259 | } | ||
260 | |||
261 | /* search the cell's active list first */ | ||
262 | list_for_each_entry(vlocation, &cell->vl_list, link) { | ||
263 | if (namesz < sizeof(vlocation->vldb.name) && | ||
264 | vlocation->vldb.name[namesz] != '\0') | ||
265 | continue; | ||
266 | |||
267 | if (memcmp(vlocation->vldb.name, name, namesz) == 0) | ||
268 | goto found_in_memory; | ||
269 | } | ||
270 | |||
271 | /* search the cell's graveyard list second */ | ||
272 | spin_lock(&cell->vl_gylock); | ||
273 | list_for_each_entry(vlocation, &cell->vl_graveyard, link) { | ||
274 | if (namesz < sizeof(vlocation->vldb.name) && | ||
275 | vlocation->vldb.name[namesz] != '\0') | ||
276 | continue; | ||
277 | |||
278 | if (memcmp(vlocation->vldb.name, name, namesz) == 0) | ||
279 | goto found_in_graveyard; | ||
280 | } | ||
281 | spin_unlock(&cell->vl_gylock); | ||
282 | |||
283 | /* not in the cell's in-memory lists - create a new record */ | ||
284 | vlocation = kmalloc(sizeof(struct afs_vlocation), GFP_KERNEL); | ||
285 | if (!vlocation) | ||
286 | return -ENOMEM; | ||
287 | |||
288 | memset(vlocation, 0, sizeof(struct afs_vlocation)); | ||
289 | atomic_set(&vlocation->usage, 1); | ||
290 | INIT_LIST_HEAD(&vlocation->link); | ||
291 | rwlock_init(&vlocation->lock); | ||
292 | memcpy(vlocation->vldb.name, name, namesz); | ||
293 | |||
294 | afs_timer_init(&vlocation->timeout, &afs_vlocation_timer_ops); | ||
295 | afs_timer_init(&vlocation->upd_timer, &afs_vlocation_update_timer_ops); | ||
296 | afs_async_op_init(&vlocation->upd_op, &afs_vlocation_update_op_ops); | ||
297 | |||
298 | afs_get_cell(cell); | ||
299 | vlocation->cell = cell; | ||
300 | |||
301 | list_add_tail(&vlocation->link, &cell->vl_list); | ||
302 | |||
303 | #ifdef AFS_CACHING_SUPPORT | ||
304 | /* we want to store it in the cache, plus it might already be | ||
305 | * encached */ | ||
306 | cachefs_acquire_cookie(cell->cache, | ||
307 | &afs_volume_cache_index_def, | ||
308 | vlocation, | ||
309 | &vlocation->cache); | ||
310 | |||
311 | if (vlocation->valid) | ||
312 | goto found_in_cache; | ||
313 | #endif | ||
314 | |||
315 | /* try to look up an unknown volume in the cell VL databases by name */ | ||
316 | ret = afs_vlocation_access_vl_by_name(vlocation, name, namesz, &vldb); | ||
317 | if (ret < 0) { | ||
318 | printk("kAFS: failed to locate '%*.*s' in cell '%s'\n", | ||
319 | namesz, namesz, name, cell->name); | ||
320 | goto error; | ||
321 | } | ||
322 | |||
323 | goto found_on_vlserver; | ||
324 | |||
325 | found_in_graveyard: | ||
326 | /* found in the graveyard - resurrect */ | ||
327 | _debug("found in graveyard"); | ||
328 | atomic_inc(&vlocation->usage); | ||
329 | list_del(&vlocation->link); | ||
330 | list_add_tail(&vlocation->link, &cell->vl_list); | ||
331 | spin_unlock(&cell->vl_gylock); | ||
332 | |||
333 | afs_kafstimod_del_timer(&vlocation->timeout); | ||
334 | goto active; | ||
335 | |||
336 | found_in_memory: | ||
337 | /* found in memory - check to see if it's active */ | ||
338 | _debug("found in memory"); | ||
339 | atomic_inc(&vlocation->usage); | ||
340 | |||
341 | active: | ||
342 | active = 1; | ||
343 | |||
344 | #ifdef AFS_CACHING_SUPPORT | ||
345 | found_in_cache: | ||
346 | #endif | ||
347 | /* try to look up a cached volume in the cell VL databases by ID */ | ||
348 | _debug("found in cache"); | ||
349 | |||
350 | _debug("Locally Cached: %s %02x { %08x(%x) %08x(%x) %08x(%x) }", | ||
351 | vlocation->vldb.name, | ||
352 | vlocation->vldb.vidmask, | ||
353 | ntohl(vlocation->vldb.servers[0].s_addr), | ||
354 | vlocation->vldb.srvtmask[0], | ||
355 | ntohl(vlocation->vldb.servers[1].s_addr), | ||
356 | vlocation->vldb.srvtmask[1], | ||
357 | ntohl(vlocation->vldb.servers[2].s_addr), | ||
358 | vlocation->vldb.srvtmask[2] | ||
359 | ); | ||
360 | |||
361 | _debug("Vids: %08x %08x %08x", | ||
362 | vlocation->vldb.vid[0], | ||
363 | vlocation->vldb.vid[1], | ||
364 | vlocation->vldb.vid[2]); | ||
365 | |||
366 | if (vlocation->vldb.vidmask & AFS_VOL_VTM_RW) { | ||
367 | vid = vlocation->vldb.vid[0]; | ||
368 | voltype = AFSVL_RWVOL; | ||
369 | } | ||
370 | else if (vlocation->vldb.vidmask & AFS_VOL_VTM_RO) { | ||
371 | vid = vlocation->vldb.vid[1]; | ||
372 | voltype = AFSVL_ROVOL; | ||
373 | } | ||
374 | else if (vlocation->vldb.vidmask & AFS_VOL_VTM_BAK) { | ||
375 | vid = vlocation->vldb.vid[2]; | ||
376 | voltype = AFSVL_BACKVOL; | ||
377 | } | ||
378 | else { | ||
379 | BUG(); | ||
380 | vid = 0; | ||
381 | voltype = 0; | ||
382 | } | ||
383 | |||
384 | ret = afs_vlocation_access_vl_by_id(vlocation, vid, voltype, &vldb); | ||
385 | switch (ret) { | ||
386 | /* net error */ | ||
387 | default: | ||
388 | printk("kAFS: failed to volume '%*.*s' (%x) up in '%s': %d\n", | ||
389 | namesz, namesz, name, vid, cell->name, ret); | ||
390 | goto error; | ||
391 | |||
392 | /* pulled from local cache into memory */ | ||
393 | case 0: | ||
394 | goto found_on_vlserver; | ||
395 | |||
396 | /* uh oh... looks like the volume got deleted */ | ||
397 | case -ENOMEDIUM: | ||
398 | printk("kAFS: volume '%*.*s' (%x) does not exist '%s'\n", | ||
399 | namesz, namesz, name, vid, cell->name); | ||
400 | |||
401 | /* TODO: make existing record unavailable */ | ||
402 | goto error; | ||
403 | } | ||
404 | |||
405 | found_on_vlserver: | ||
406 | _debug("Done VL Lookup: %*.*s %02x { %08x(%x) %08x(%x) %08x(%x) }", | ||
407 | namesz, namesz, name, | ||
408 | vldb.vidmask, | ||
409 | ntohl(vldb.servers[0].s_addr), vldb.srvtmask[0], | ||
410 | ntohl(vldb.servers[1].s_addr), vldb.srvtmask[1], | ||
411 | ntohl(vldb.servers[2].s_addr), vldb.srvtmask[2] | ||
412 | ); | ||
413 | |||
414 | _debug("Vids: %08x %08x %08x", vldb.vid[0], vldb.vid[1], vldb.vid[2]); | ||
415 | |||
416 | if ((namesz < sizeof(vlocation->vldb.name) && | ||
417 | vlocation->vldb.name[namesz] != '\0') || | ||
418 | memcmp(vldb.name, name, namesz) != 0) | ||
419 | printk("kAFS: name of volume '%*.*s' changed to '%s' on server\n", | ||
420 | namesz, namesz, name, vldb.name); | ||
421 | |||
422 | memcpy(&vlocation->vldb, &vldb, sizeof(vlocation->vldb)); | ||
423 | |||
424 | afs_kafstimod_add_timer(&vlocation->upd_timer, 10 * HZ); | ||
425 | |||
426 | #ifdef AFS_CACHING_SUPPORT | ||
427 | /* update volume entry in local cache */ | ||
428 | cachefs_update_cookie(vlocation->cache); | ||
429 | #endif | ||
430 | |||
431 | *_vlocation = vlocation; | ||
432 | _leave(" = 0 (%p)",vlocation); | ||
433 | return 0; | ||
434 | |||
435 | error: | ||
436 | if (vlocation) { | ||
437 | if (active) { | ||
438 | __afs_put_vlocation(vlocation); | ||
439 | } | ||
440 | else { | ||
441 | list_del(&vlocation->link); | ||
442 | #ifdef AFS_CACHING_SUPPORT | ||
443 | cachefs_relinquish_cookie(vlocation->cache, 0); | ||
444 | #endif | ||
445 | afs_put_cell(vlocation->cell); | ||
446 | kfree(vlocation); | ||
447 | } | ||
448 | } | ||
449 | |||
450 | _leave(" = %d", ret); | ||
451 | return ret; | ||
452 | } /* end afs_vlocation_lookup() */ | ||
453 | |||
454 | /*****************************************************************************/ | ||
455 | /* | ||
456 | * finish using a volume location record | ||
457 | * - caller must have cell->vol_sem write-locked | ||
458 | */ | ||
459 | static void __afs_put_vlocation(struct afs_vlocation *vlocation) | ||
460 | { | ||
461 | struct afs_cell *cell; | ||
462 | |||
463 | if (!vlocation) | ||
464 | return; | ||
465 | |||
466 | _enter("%s", vlocation->vldb.name); | ||
467 | |||
468 | cell = vlocation->cell; | ||
469 | |||
470 | /* sanity check */ | ||
471 | BUG_ON(atomic_read(&vlocation->usage) <= 0); | ||
472 | |||
473 | spin_lock(&cell->vl_gylock); | ||
474 | if (likely(!atomic_dec_and_test(&vlocation->usage))) { | ||
475 | spin_unlock(&cell->vl_gylock); | ||
476 | _leave(""); | ||
477 | return; | ||
478 | } | ||
479 | |||
480 | /* move to graveyard queue */ | ||
481 | list_del(&vlocation->link); | ||
482 | list_add_tail(&vlocation->link,&cell->vl_graveyard); | ||
483 | |||
484 | /* remove from pending timeout queue (refcounted if actually being | ||
485 | * updated) */ | ||
486 | list_del_init(&vlocation->upd_op.link); | ||
487 | |||
488 | /* time out in 10 secs */ | ||
489 | afs_kafstimod_del_timer(&vlocation->upd_timer); | ||
490 | afs_kafstimod_add_timer(&vlocation->timeout, 10 * HZ); | ||
491 | |||
492 | spin_unlock(&cell->vl_gylock); | ||
493 | |||
494 | _leave(" [killed]"); | ||
495 | } /* end __afs_put_vlocation() */ | ||
496 | |||
497 | /*****************************************************************************/ | ||
498 | /* | ||
499 | * finish using a volume location record | ||
500 | */ | ||
501 | void afs_put_vlocation(struct afs_vlocation *vlocation) | ||
502 | { | ||
503 | if (vlocation) { | ||
504 | struct afs_cell *cell = vlocation->cell; | ||
505 | |||
506 | down_write(&cell->vl_sem); | ||
507 | __afs_put_vlocation(vlocation); | ||
508 | up_write(&cell->vl_sem); | ||
509 | } | ||
510 | } /* end afs_put_vlocation() */ | ||
511 | |||
512 | /*****************************************************************************/ | ||
513 | /* | ||
514 | * timeout vlocation record | ||
515 | * - removes from the cell's graveyard if the usage count is zero | ||
516 | */ | ||
517 | void afs_vlocation_do_timeout(struct afs_vlocation *vlocation) | ||
518 | { | ||
519 | struct afs_cell *cell; | ||
520 | |||
521 | _enter("%s", vlocation->vldb.name); | ||
522 | |||
523 | cell = vlocation->cell; | ||
524 | |||
525 | BUG_ON(atomic_read(&vlocation->usage) < 0); | ||
526 | |||
527 | /* remove from graveyard if still dead */ | ||
528 | spin_lock(&cell->vl_gylock); | ||
529 | if (atomic_read(&vlocation->usage) == 0) | ||
530 | list_del_init(&vlocation->link); | ||
531 | else | ||
532 | vlocation = NULL; | ||
533 | spin_unlock(&cell->vl_gylock); | ||
534 | |||
535 | if (!vlocation) { | ||
536 | _leave(""); | ||
537 | return; /* resurrected */ | ||
538 | } | ||
539 | |||
540 | /* we can now destroy it properly */ | ||
541 | #ifdef AFS_CACHING_SUPPORT | ||
542 | cachefs_relinquish_cookie(vlocation->cache, 0); | ||
543 | #endif | ||
544 | afs_put_cell(cell); | ||
545 | |||
546 | kfree(vlocation); | ||
547 | |||
548 | _leave(" [destroyed]"); | ||
549 | } /* end afs_vlocation_do_timeout() */ | ||
550 | |||
551 | /*****************************************************************************/ | ||
552 | /* | ||
553 | * send an update operation to the currently selected server | ||
554 | */ | ||
555 | static int afs_vlocation_update_begin(struct afs_vlocation *vlocation) | ||
556 | { | ||
557 | afs_voltype_t voltype; | ||
558 | afs_volid_t vid; | ||
559 | int ret; | ||
560 | |||
561 | _enter("%s{ufs=%u ucs=%u}", | ||
562 | vlocation->vldb.name, | ||
563 | vlocation->upd_first_svix, | ||
564 | vlocation->upd_curr_svix); | ||
565 | |||
566 | /* try to look up a cached volume in the cell VL databases by ID */ | ||
567 | if (vlocation->vldb.vidmask & AFS_VOL_VTM_RW) { | ||
568 | vid = vlocation->vldb.vid[0]; | ||
569 | voltype = AFSVL_RWVOL; | ||
570 | } | ||
571 | else if (vlocation->vldb.vidmask & AFS_VOL_VTM_RO) { | ||
572 | vid = vlocation->vldb.vid[1]; | ||
573 | voltype = AFSVL_ROVOL; | ||
574 | } | ||
575 | else if (vlocation->vldb.vidmask & AFS_VOL_VTM_BAK) { | ||
576 | vid = vlocation->vldb.vid[2]; | ||
577 | voltype = AFSVL_BACKVOL; | ||
578 | } | ||
579 | else { | ||
580 | BUG(); | ||
581 | vid = 0; | ||
582 | voltype = 0; | ||
583 | } | ||
584 | |||
585 | /* contact the chosen server */ | ||
586 | ret = afs_server_lookup( | ||
587 | vlocation->cell, | ||
588 | &vlocation->cell->vl_addrs[vlocation->upd_curr_svix], | ||
589 | &vlocation->upd_op.server); | ||
590 | |||
591 | switch (ret) { | ||
592 | case 0: | ||
593 | break; | ||
594 | case -ENOMEM: | ||
595 | case -ENONET: | ||
596 | default: | ||
597 | _leave(" = %d", ret); | ||
598 | return ret; | ||
599 | } | ||
600 | |||
601 | /* initiate the update operation */ | ||
602 | ret = afs_rxvl_get_entry_by_id_async(&vlocation->upd_op, vid, voltype); | ||
603 | if (ret < 0) { | ||
604 | _leave(" = %d", ret); | ||
605 | return ret; | ||
606 | } | ||
607 | |||
608 | _leave(" = %d", ret); | ||
609 | return ret; | ||
610 | } /* end afs_vlocation_update_begin() */ | ||
611 | |||
612 | /*****************************************************************************/ | ||
613 | /* | ||
614 | * abandon updating a VL record | ||
615 | * - does not restart the update timer | ||
616 | */ | ||
617 | static void afs_vlocation_update_abandon(struct afs_vlocation *vlocation, | ||
618 | afs_vlocation_upd_t state, | ||
619 | int ret) | ||
620 | { | ||
621 | _enter("%s,%u", vlocation->vldb.name, state); | ||
622 | |||
623 | if (ret < 0) | ||
624 | printk("kAFS: Abandoning VL update '%s': %d\n", | ||
625 | vlocation->vldb.name, ret); | ||
626 | |||
627 | /* discard the server record */ | ||
628 | afs_put_server(vlocation->upd_op.server); | ||
629 | vlocation->upd_op.server = NULL; | ||
630 | |||
631 | spin_lock(&afs_vlocation_update_lock); | ||
632 | afs_vlocation_update = NULL; | ||
633 | vlocation->upd_state = state; | ||
634 | |||
635 | /* TODO: start updating next VL record on pending list */ | ||
636 | |||
637 | spin_unlock(&afs_vlocation_update_lock); | ||
638 | |||
639 | _leave(""); | ||
640 | } /* end afs_vlocation_update_abandon() */ | ||
641 | |||
642 | /*****************************************************************************/ | ||
643 | /* | ||
644 | * handle periodic update timeouts and busy retry timeouts | ||
645 | * - called from kafstimod | ||
646 | */ | ||
647 | static void afs_vlocation_update_timer(struct afs_timer *timer) | ||
648 | { | ||
649 | struct afs_vlocation *vlocation = | ||
650 | list_entry(timer, struct afs_vlocation, upd_timer); | ||
651 | int ret; | ||
652 | |||
653 | _enter("%s", vlocation->vldb.name); | ||
654 | |||
655 | /* only update if not in the graveyard (defend against putting too) */ | ||
656 | spin_lock(&vlocation->cell->vl_gylock); | ||
657 | |||
658 | if (!atomic_read(&vlocation->usage)) | ||
659 | goto out_unlock1; | ||
660 | |||
661 | spin_lock(&afs_vlocation_update_lock); | ||
662 | |||
663 | /* if we were woken up due to EBUSY sleep then restart immediately if | ||
664 | * possible or else jump to front of pending queue */ | ||
665 | if (vlocation->upd_state == AFS_VLUPD_BUSYSLEEP) { | ||
666 | if (afs_vlocation_update) { | ||
667 | list_add(&vlocation->upd_op.link, | ||
668 | &afs_vlocation_update_pendq); | ||
669 | } | ||
670 | else { | ||
671 | afs_get_vlocation(vlocation); | ||
672 | afs_vlocation_update = vlocation; | ||
673 | vlocation->upd_state = AFS_VLUPD_INPROGRESS; | ||
674 | } | ||
675 | goto out_unlock2; | ||
676 | } | ||
677 | |||
678 | /* put on pending queue if there's already another update in progress */ | ||
679 | if (afs_vlocation_update) { | ||
680 | vlocation->upd_state = AFS_VLUPD_PENDING; | ||
681 | list_add_tail(&vlocation->upd_op.link, | ||
682 | &afs_vlocation_update_pendq); | ||
683 | goto out_unlock2; | ||
684 | } | ||
685 | |||
686 | /* hold a ref on it while actually updating */ | ||
687 | afs_get_vlocation(vlocation); | ||
688 | afs_vlocation_update = vlocation; | ||
689 | vlocation->upd_state = AFS_VLUPD_INPROGRESS; | ||
690 | |||
691 | spin_unlock(&afs_vlocation_update_lock); | ||
692 | spin_unlock(&vlocation->cell->vl_gylock); | ||
693 | |||
694 | /* okay... we can start the update */ | ||
695 | _debug("BEGIN VL UPDATE [%s]", vlocation->vldb.name); | ||
696 | vlocation->upd_first_svix = vlocation->cell->vl_curr_svix; | ||
697 | vlocation->upd_curr_svix = vlocation->upd_first_svix; | ||
698 | vlocation->upd_rej_cnt = 0; | ||
699 | vlocation->upd_busy_cnt = 0; | ||
700 | |||
701 | ret = afs_vlocation_update_begin(vlocation); | ||
702 | if (ret < 0) { | ||
703 | afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, ret); | ||
704 | afs_kafstimod_add_timer(&vlocation->upd_timer, | ||
705 | AFS_VLDB_TIMEOUT); | ||
706 | afs_put_vlocation(vlocation); | ||
707 | } | ||
708 | |||
709 | _leave(""); | ||
710 | return; | ||
711 | |||
712 | out_unlock2: | ||
713 | spin_unlock(&afs_vlocation_update_lock); | ||
714 | out_unlock1: | ||
715 | spin_unlock(&vlocation->cell->vl_gylock); | ||
716 | _leave(""); | ||
717 | return; | ||
718 | |||
719 | } /* end afs_vlocation_update_timer() */ | ||
720 | |||
721 | /*****************************************************************************/ | ||
722 | /* | ||
723 | * attend to an update operation upon which an event happened | ||
724 | * - called in kafsasyncd context | ||
725 | */ | ||
726 | static void afs_vlocation_update_attend(struct afs_async_op *op) | ||
727 | { | ||
728 | struct afs_cache_vlocation vldb; | ||
729 | struct afs_vlocation *vlocation = | ||
730 | list_entry(op, struct afs_vlocation, upd_op); | ||
731 | unsigned tmp; | ||
732 | int ret; | ||
733 | |||
734 | _enter("%s", vlocation->vldb.name); | ||
735 | |||
736 | ret = afs_rxvl_get_entry_by_id_async2(op, &vldb); | ||
737 | switch (ret) { | ||
738 | case -EAGAIN: | ||
739 | _leave(" [unfinished]"); | ||
740 | return; | ||
741 | |||
742 | case 0: | ||
743 | _debug("END VL UPDATE: %d\n", ret); | ||
744 | vlocation->valid = 1; | ||
745 | |||
746 | _debug("Done VL Lookup: %02x { %08x(%x) %08x(%x) %08x(%x) }", | ||
747 | vldb.vidmask, | ||
748 | ntohl(vldb.servers[0].s_addr), vldb.srvtmask[0], | ||
749 | ntohl(vldb.servers[1].s_addr), vldb.srvtmask[1], | ||
750 | ntohl(vldb.servers[2].s_addr), vldb.srvtmask[2] | ||
751 | ); | ||
752 | |||
753 | _debug("Vids: %08x %08x %08x", | ||
754 | vldb.vid[0], vldb.vid[1], vldb.vid[2]); | ||
755 | |||
756 | afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, 0); | ||
757 | |||
758 | down_write(&vlocation->cell->vl_sem); | ||
759 | |||
760 | /* actually update the cache */ | ||
761 | if (strncmp(vldb.name, vlocation->vldb.name, | ||
762 | sizeof(vlocation->vldb.name)) != 0) | ||
763 | printk("kAFS: name of volume '%s'" | ||
764 | " changed to '%s' on server\n", | ||
765 | vlocation->vldb.name, vldb.name); | ||
766 | |||
767 | memcpy(&vlocation->vldb, &vldb, sizeof(vlocation->vldb)); | ||
768 | |||
769 | #if 0 | ||
770 | /* TODO update volume entry in local cache */ | ||
771 | #endif | ||
772 | |||
773 | up_write(&vlocation->cell->vl_sem); | ||
774 | |||
775 | if (ret < 0) | ||
776 | printk("kAFS: failed to update local cache: %d\n", ret); | ||
777 | |||
778 | afs_kafstimod_add_timer(&vlocation->upd_timer, | ||
779 | AFS_VLDB_TIMEOUT); | ||
780 | afs_put_vlocation(vlocation); | ||
781 | _leave(" [found]"); | ||
782 | return; | ||
783 | |||
784 | case -ENOMEDIUM: | ||
785 | vlocation->upd_rej_cnt++; | ||
786 | goto try_next; | ||
787 | |||
788 | /* the server is locked - retry in a very short while */ | ||
789 | case -EBUSY: | ||
790 | vlocation->upd_busy_cnt++; | ||
791 | if (vlocation->upd_busy_cnt > 3) | ||
792 | goto try_next; /* too many retries */ | ||
793 | |||
794 | afs_vlocation_update_abandon(vlocation, | ||
795 | AFS_VLUPD_BUSYSLEEP, 0); | ||
796 | afs_kafstimod_add_timer(&vlocation->upd_timer, HZ / 2); | ||
797 | afs_put_vlocation(vlocation); | ||
798 | _leave(" [busy]"); | ||
799 | return; | ||
800 | |||
801 | case -ENETUNREACH: | ||
802 | case -EHOSTUNREACH: | ||
803 | case -ECONNREFUSED: | ||
804 | case -EREMOTEIO: | ||
805 | /* record bad vlserver info in the cell too | ||
806 | * - TODO: use down_write_trylock() if available | ||
807 | */ | ||
808 | if (vlocation->upd_curr_svix == vlocation->cell->vl_curr_svix) | ||
809 | vlocation->cell->vl_curr_svix = | ||
810 | vlocation->cell->vl_curr_svix % | ||
811 | vlocation->cell->vl_naddrs; | ||
812 | |||
813 | case -EBADRQC: | ||
814 | case -EINVAL: | ||
815 | case -EACCES: | ||
816 | case -EBADMSG: | ||
817 | goto try_next; | ||
818 | |||
819 | default: | ||
820 | goto abandon; | ||
821 | } | ||
822 | |||
823 | /* try contacting the next server */ | ||
824 | try_next: | ||
825 | vlocation->upd_busy_cnt = 0; | ||
826 | |||
827 | /* discard the server record */ | ||
828 | afs_put_server(vlocation->upd_op.server); | ||
829 | vlocation->upd_op.server = NULL; | ||
830 | |||
831 | tmp = vlocation->cell->vl_naddrs; | ||
832 | if (tmp == 0) | ||
833 | goto abandon; | ||
834 | |||
835 | vlocation->upd_curr_svix++; | ||
836 | if (vlocation->upd_curr_svix >= tmp) | ||
837 | vlocation->upd_curr_svix = 0; | ||
838 | if (vlocation->upd_first_svix >= tmp) | ||
839 | vlocation->upd_first_svix = tmp - 1; | ||
840 | |||
841 | /* move to the next server */ | ||
842 | if (vlocation->upd_curr_svix != vlocation->upd_first_svix) { | ||
843 | afs_vlocation_update_begin(vlocation); | ||
844 | _leave(" [next]"); | ||
845 | return; | ||
846 | } | ||
847 | |||
848 | /* run out of servers to try - was the volume rejected? */ | ||
849 | if (vlocation->upd_rej_cnt > 0) { | ||
850 | printk("kAFS: Active volume no longer valid '%s'\n", | ||
851 | vlocation->vldb.name); | ||
852 | vlocation->valid = 0; | ||
853 | afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, 0); | ||
854 | afs_kafstimod_add_timer(&vlocation->upd_timer, | ||
855 | AFS_VLDB_TIMEOUT); | ||
856 | afs_put_vlocation(vlocation); | ||
857 | _leave(" [invalidated]"); | ||
858 | return; | ||
859 | } | ||
860 | |||
861 | /* abandon the update */ | ||
862 | abandon: | ||
863 | afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, ret); | ||
864 | afs_kafstimod_add_timer(&vlocation->upd_timer, HZ * 10); | ||
865 | afs_put_vlocation(vlocation); | ||
866 | _leave(" [abandoned]"); | ||
867 | |||
868 | } /* end afs_vlocation_update_attend() */ | ||
869 | |||
870 | /*****************************************************************************/ | ||
871 | /* | ||
872 | * deal with an update operation being discarded | ||
873 | * - called in kafsasyncd context when it's dying due to rmmod | ||
874 | * - the call has already been aborted and put()'d | ||
875 | */ | ||
876 | static void afs_vlocation_update_discard(struct afs_async_op *op) | ||
877 | { | ||
878 | struct afs_vlocation *vlocation = | ||
879 | list_entry(op, struct afs_vlocation, upd_op); | ||
880 | |||
881 | _enter("%s", vlocation->vldb.name); | ||
882 | |||
883 | afs_put_server(op->server); | ||
884 | op->server = NULL; | ||
885 | |||
886 | afs_put_vlocation(vlocation); | ||
887 | |||
888 | _leave(""); | ||
889 | } /* end afs_vlocation_update_discard() */ | ||
890 | |||
891 | /*****************************************************************************/ | ||
892 | /* | ||
893 | * match a VLDB record stored in the cache | ||
894 | * - may also load target from entry | ||
895 | */ | ||
896 | #ifdef AFS_CACHING_SUPPORT | ||
897 | static cachefs_match_val_t afs_vlocation_cache_match(void *target, | ||
898 | const void *entry) | ||
899 | { | ||
900 | const struct afs_cache_vlocation *vldb = entry; | ||
901 | struct afs_vlocation *vlocation = target; | ||
902 | |||
903 | _enter("{%s},{%s}", vlocation->vldb.name, vldb->name); | ||
904 | |||
905 | if (strncmp(vlocation->vldb.name, vldb->name, sizeof(vldb->name)) == 0 | ||
906 | ) { | ||
907 | if (!vlocation->valid || | ||
908 | vlocation->vldb.rtime == vldb->rtime | ||
909 | ) { | ||
910 | vlocation->vldb = *vldb; | ||
911 | vlocation->valid = 1; | ||
912 | _leave(" = SUCCESS [c->m]"); | ||
913 | return CACHEFS_MATCH_SUCCESS; | ||
914 | } | ||
915 | /* need to update cache if cached info differs */ | ||
916 | else if (memcmp(&vlocation->vldb, vldb, sizeof(*vldb)) != 0) { | ||
917 | /* delete if VIDs for this name differ */ | ||
918 | if (memcmp(&vlocation->vldb.vid, | ||
919 | &vldb->vid, | ||
920 | sizeof(vldb->vid)) != 0) { | ||
921 | _leave(" = DELETE"); | ||
922 | return CACHEFS_MATCH_SUCCESS_DELETE; | ||
923 | } | ||
924 | |||
925 | _leave(" = UPDATE"); | ||
926 | return CACHEFS_MATCH_SUCCESS_UPDATE; | ||
927 | } | ||
928 | else { | ||
929 | _leave(" = SUCCESS"); | ||
930 | return CACHEFS_MATCH_SUCCESS; | ||
931 | } | ||
932 | } | ||
933 | |||
934 | _leave(" = FAILED"); | ||
935 | return CACHEFS_MATCH_FAILED; | ||
936 | } /* end afs_vlocation_cache_match() */ | ||
937 | #endif | ||
938 | |||
939 | /*****************************************************************************/ | ||
940 | /* | ||
941 | * update a VLDB record stored in the cache | ||
942 | */ | ||
943 | #ifdef AFS_CACHING_SUPPORT | ||
944 | static void afs_vlocation_cache_update(void *source, void *entry) | ||
945 | { | ||
946 | struct afs_cache_vlocation *vldb = entry; | ||
947 | struct afs_vlocation *vlocation = source; | ||
948 | |||
949 | _enter(""); | ||
950 | |||
951 | *vldb = vlocation->vldb; | ||
952 | |||
953 | } /* end afs_vlocation_cache_update() */ | ||
954 | #endif | ||