Commit | Line | Data |
---|---|---|
1da177e4 LT |
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); | |
f116629d | 329 | list_move_tail(&vlocation->link, &cell->vl_list); |
1da177e4 LT |
330 | spin_unlock(&cell->vl_gylock); |
331 | ||
332 | afs_kafstimod_del_timer(&vlocation->timeout); | |
333 | goto active; | |
334 | ||
335 | found_in_memory: | |
336 | /* found in memory - check to see if it's active */ | |
337 | _debug("found in memory"); | |
338 | atomic_inc(&vlocation->usage); | |
339 | ||
340 | active: | |
341 | active = 1; | |
342 | ||
343 | #ifdef AFS_CACHING_SUPPORT | |
344 | found_in_cache: | |
345 | #endif | |
346 | /* try to look up a cached volume in the cell VL databases by ID */ | |
347 | _debug("found in cache"); | |
348 | ||
349 | _debug("Locally Cached: %s %02x { %08x(%x) %08x(%x) %08x(%x) }", | |
350 | vlocation->vldb.name, | |
351 | vlocation->vldb.vidmask, | |
352 | ntohl(vlocation->vldb.servers[0].s_addr), | |
353 | vlocation->vldb.srvtmask[0], | |
354 | ntohl(vlocation->vldb.servers[1].s_addr), | |
355 | vlocation->vldb.srvtmask[1], | |
356 | ntohl(vlocation->vldb.servers[2].s_addr), | |
357 | vlocation->vldb.srvtmask[2] | |
358 | ); | |
359 | ||
360 | _debug("Vids: %08x %08x %08x", | |
361 | vlocation->vldb.vid[0], | |
362 | vlocation->vldb.vid[1], | |
363 | vlocation->vldb.vid[2]); | |
364 | ||
365 | if (vlocation->vldb.vidmask & AFS_VOL_VTM_RW) { | |
366 | vid = vlocation->vldb.vid[0]; | |
367 | voltype = AFSVL_RWVOL; | |
368 | } | |
369 | else if (vlocation->vldb.vidmask & AFS_VOL_VTM_RO) { | |
370 | vid = vlocation->vldb.vid[1]; | |
371 | voltype = AFSVL_ROVOL; | |
372 | } | |
373 | else if (vlocation->vldb.vidmask & AFS_VOL_VTM_BAK) { | |
374 | vid = vlocation->vldb.vid[2]; | |
375 | voltype = AFSVL_BACKVOL; | |
376 | } | |
377 | else { | |
378 | BUG(); | |
379 | vid = 0; | |
380 | voltype = 0; | |
381 | } | |
382 | ||
383 | ret = afs_vlocation_access_vl_by_id(vlocation, vid, voltype, &vldb); | |
384 | switch (ret) { | |
385 | /* net error */ | |
386 | default: | |
387 | printk("kAFS: failed to volume '%*.*s' (%x) up in '%s': %d\n", | |
388 | namesz, namesz, name, vid, cell->name, ret); | |
389 | goto error; | |
390 | ||
391 | /* pulled from local cache into memory */ | |
392 | case 0: | |
393 | goto found_on_vlserver; | |
394 | ||
395 | /* uh oh... looks like the volume got deleted */ | |
396 | case -ENOMEDIUM: | |
397 | printk("kAFS: volume '%*.*s' (%x) does not exist '%s'\n", | |
398 | namesz, namesz, name, vid, cell->name); | |
399 | ||
400 | /* TODO: make existing record unavailable */ | |
401 | goto error; | |
402 | } | |
403 | ||
404 | found_on_vlserver: | |
405 | _debug("Done VL Lookup: %*.*s %02x { %08x(%x) %08x(%x) %08x(%x) }", | |
406 | namesz, namesz, name, | |
407 | vldb.vidmask, | |
408 | ntohl(vldb.servers[0].s_addr), vldb.srvtmask[0], | |
409 | ntohl(vldb.servers[1].s_addr), vldb.srvtmask[1], | |
410 | ntohl(vldb.servers[2].s_addr), vldb.srvtmask[2] | |
411 | ); | |
412 | ||
413 | _debug("Vids: %08x %08x %08x", vldb.vid[0], vldb.vid[1], vldb.vid[2]); | |
414 | ||
415 | if ((namesz < sizeof(vlocation->vldb.name) && | |
416 | vlocation->vldb.name[namesz] != '\0') || | |
417 | memcmp(vldb.name, name, namesz) != 0) | |
418 | printk("kAFS: name of volume '%*.*s' changed to '%s' on server\n", | |
419 | namesz, namesz, name, vldb.name); | |
420 | ||
421 | memcpy(&vlocation->vldb, &vldb, sizeof(vlocation->vldb)); | |
422 | ||
423 | afs_kafstimod_add_timer(&vlocation->upd_timer, 10 * HZ); | |
424 | ||
425 | #ifdef AFS_CACHING_SUPPORT | |
426 | /* update volume entry in local cache */ | |
427 | cachefs_update_cookie(vlocation->cache); | |
428 | #endif | |
429 | ||
430 | *_vlocation = vlocation; | |
431 | _leave(" = 0 (%p)",vlocation); | |
432 | return 0; | |
433 | ||
434 | error: | |
435 | if (vlocation) { | |
436 | if (active) { | |
437 | __afs_put_vlocation(vlocation); | |
438 | } | |
439 | else { | |
440 | list_del(&vlocation->link); | |
441 | #ifdef AFS_CACHING_SUPPORT | |
442 | cachefs_relinquish_cookie(vlocation->cache, 0); | |
443 | #endif | |
444 | afs_put_cell(vlocation->cell); | |
445 | kfree(vlocation); | |
446 | } | |
447 | } | |
448 | ||
449 | _leave(" = %d", ret); | |
450 | return ret; | |
451 | } /* end afs_vlocation_lookup() */ | |
452 | ||
453 | /*****************************************************************************/ | |
454 | /* | |
455 | * finish using a volume location record | |
456 | * - caller must have cell->vol_sem write-locked | |
457 | */ | |
458 | static void __afs_put_vlocation(struct afs_vlocation *vlocation) | |
459 | { | |
460 | struct afs_cell *cell; | |
461 | ||
462 | if (!vlocation) | |
463 | return; | |
464 | ||
465 | _enter("%s", vlocation->vldb.name); | |
466 | ||
467 | cell = vlocation->cell; | |
468 | ||
469 | /* sanity check */ | |
470 | BUG_ON(atomic_read(&vlocation->usage) <= 0); | |
471 | ||
472 | spin_lock(&cell->vl_gylock); | |
473 | if (likely(!atomic_dec_and_test(&vlocation->usage))) { | |
474 | spin_unlock(&cell->vl_gylock); | |
475 | _leave(""); | |
476 | return; | |
477 | } | |
478 | ||
479 | /* move to graveyard queue */ | |
f116629d | 480 | list_move_tail(&vlocation->link,&cell->vl_graveyard); |
1da177e4 LT |
481 | |
482 | /* remove from pending timeout queue (refcounted if actually being | |
483 | * updated) */ | |
484 | list_del_init(&vlocation->upd_op.link); | |
485 | ||
486 | /* time out in 10 secs */ | |
487 | afs_kafstimod_del_timer(&vlocation->upd_timer); | |
488 | afs_kafstimod_add_timer(&vlocation->timeout, 10 * HZ); | |
489 | ||
490 | spin_unlock(&cell->vl_gylock); | |
491 | ||
492 | _leave(" [killed]"); | |
493 | } /* end __afs_put_vlocation() */ | |
494 | ||
495 | /*****************************************************************************/ | |
496 | /* | |
497 | * finish using a volume location record | |
498 | */ | |
499 | void afs_put_vlocation(struct afs_vlocation *vlocation) | |
500 | { | |
501 | if (vlocation) { | |
502 | struct afs_cell *cell = vlocation->cell; | |
503 | ||
504 | down_write(&cell->vl_sem); | |
505 | __afs_put_vlocation(vlocation); | |
506 | up_write(&cell->vl_sem); | |
507 | } | |
508 | } /* end afs_put_vlocation() */ | |
509 | ||
510 | /*****************************************************************************/ | |
511 | /* | |
512 | * timeout vlocation record | |
513 | * - removes from the cell's graveyard if the usage count is zero | |
514 | */ | |
515 | void afs_vlocation_do_timeout(struct afs_vlocation *vlocation) | |
516 | { | |
517 | struct afs_cell *cell; | |
518 | ||
519 | _enter("%s", vlocation->vldb.name); | |
520 | ||
521 | cell = vlocation->cell; | |
522 | ||
523 | BUG_ON(atomic_read(&vlocation->usage) < 0); | |
524 | ||
525 | /* remove from graveyard if still dead */ | |
526 | spin_lock(&cell->vl_gylock); | |
527 | if (atomic_read(&vlocation->usage) == 0) | |
528 | list_del_init(&vlocation->link); | |
529 | else | |
530 | vlocation = NULL; | |
531 | spin_unlock(&cell->vl_gylock); | |
532 | ||
533 | if (!vlocation) { | |
534 | _leave(""); | |
535 | return; /* resurrected */ | |
536 | } | |
537 | ||
538 | /* we can now destroy it properly */ | |
539 | #ifdef AFS_CACHING_SUPPORT | |
540 | cachefs_relinquish_cookie(vlocation->cache, 0); | |
541 | #endif | |
542 | afs_put_cell(cell); | |
543 | ||
544 | kfree(vlocation); | |
545 | ||
546 | _leave(" [destroyed]"); | |
547 | } /* end afs_vlocation_do_timeout() */ | |
548 | ||
549 | /*****************************************************************************/ | |
550 | /* | |
551 | * send an update operation to the currently selected server | |
552 | */ | |
553 | static int afs_vlocation_update_begin(struct afs_vlocation *vlocation) | |
554 | { | |
555 | afs_voltype_t voltype; | |
556 | afs_volid_t vid; | |
557 | int ret; | |
558 | ||
559 | _enter("%s{ufs=%u ucs=%u}", | |
560 | vlocation->vldb.name, | |
561 | vlocation->upd_first_svix, | |
562 | vlocation->upd_curr_svix); | |
563 | ||
564 | /* try to look up a cached volume in the cell VL databases by ID */ | |
565 | if (vlocation->vldb.vidmask & AFS_VOL_VTM_RW) { | |
566 | vid = vlocation->vldb.vid[0]; | |
567 | voltype = AFSVL_RWVOL; | |
568 | } | |
569 | else if (vlocation->vldb.vidmask & AFS_VOL_VTM_RO) { | |
570 | vid = vlocation->vldb.vid[1]; | |
571 | voltype = AFSVL_ROVOL; | |
572 | } | |
573 | else if (vlocation->vldb.vidmask & AFS_VOL_VTM_BAK) { | |
574 | vid = vlocation->vldb.vid[2]; | |
575 | voltype = AFSVL_BACKVOL; | |
576 | } | |
577 | else { | |
578 | BUG(); | |
579 | vid = 0; | |
580 | voltype = 0; | |
581 | } | |
582 | ||
583 | /* contact the chosen server */ | |
584 | ret = afs_server_lookup( | |
585 | vlocation->cell, | |
586 | &vlocation->cell->vl_addrs[vlocation->upd_curr_svix], | |
587 | &vlocation->upd_op.server); | |
588 | ||
589 | switch (ret) { | |
590 | case 0: | |
591 | break; | |
592 | case -ENOMEM: | |
593 | case -ENONET: | |
594 | default: | |
595 | _leave(" = %d", ret); | |
596 | return ret; | |
597 | } | |
598 | ||
599 | /* initiate the update operation */ | |
600 | ret = afs_rxvl_get_entry_by_id_async(&vlocation->upd_op, vid, voltype); | |
601 | if (ret < 0) { | |
602 | _leave(" = %d", ret); | |
603 | return ret; | |
604 | } | |
605 | ||
606 | _leave(" = %d", ret); | |
607 | return ret; | |
608 | } /* end afs_vlocation_update_begin() */ | |
609 | ||
610 | /*****************************************************************************/ | |
611 | /* | |
612 | * abandon updating a VL record | |
613 | * - does not restart the update timer | |
614 | */ | |
615 | static void afs_vlocation_update_abandon(struct afs_vlocation *vlocation, | |
616 | afs_vlocation_upd_t state, | |
617 | int ret) | |
618 | { | |
619 | _enter("%s,%u", vlocation->vldb.name, state); | |
620 | ||
621 | if (ret < 0) | |
622 | printk("kAFS: Abandoning VL update '%s': %d\n", | |
623 | vlocation->vldb.name, ret); | |
624 | ||
625 | /* discard the server record */ | |
626 | afs_put_server(vlocation->upd_op.server); | |
627 | vlocation->upd_op.server = NULL; | |
628 | ||
629 | spin_lock(&afs_vlocation_update_lock); | |
630 | afs_vlocation_update = NULL; | |
631 | vlocation->upd_state = state; | |
632 | ||
633 | /* TODO: start updating next VL record on pending list */ | |
634 | ||
635 | spin_unlock(&afs_vlocation_update_lock); | |
636 | ||
637 | _leave(""); | |
638 | } /* end afs_vlocation_update_abandon() */ | |
639 | ||
640 | /*****************************************************************************/ | |
641 | /* | |
642 | * handle periodic update timeouts and busy retry timeouts | |
643 | * - called from kafstimod | |
644 | */ | |
645 | static void afs_vlocation_update_timer(struct afs_timer *timer) | |
646 | { | |
647 | struct afs_vlocation *vlocation = | |
648 | list_entry(timer, struct afs_vlocation, upd_timer); | |
649 | int ret; | |
650 | ||
651 | _enter("%s", vlocation->vldb.name); | |
652 | ||
653 | /* only update if not in the graveyard (defend against putting too) */ | |
654 | spin_lock(&vlocation->cell->vl_gylock); | |
655 | ||
656 | if (!atomic_read(&vlocation->usage)) | |
657 | goto out_unlock1; | |
658 | ||
659 | spin_lock(&afs_vlocation_update_lock); | |
660 | ||
661 | /* if we were woken up due to EBUSY sleep then restart immediately if | |
662 | * possible or else jump to front of pending queue */ | |
663 | if (vlocation->upd_state == AFS_VLUPD_BUSYSLEEP) { | |
664 | if (afs_vlocation_update) { | |
665 | list_add(&vlocation->upd_op.link, | |
666 | &afs_vlocation_update_pendq); | |
667 | } | |
668 | else { | |
669 | afs_get_vlocation(vlocation); | |
670 | afs_vlocation_update = vlocation; | |
671 | vlocation->upd_state = AFS_VLUPD_INPROGRESS; | |
672 | } | |
673 | goto out_unlock2; | |
674 | } | |
675 | ||
676 | /* put on pending queue if there's already another update in progress */ | |
677 | if (afs_vlocation_update) { | |
678 | vlocation->upd_state = AFS_VLUPD_PENDING; | |
679 | list_add_tail(&vlocation->upd_op.link, | |
680 | &afs_vlocation_update_pendq); | |
681 | goto out_unlock2; | |
682 | } | |
683 | ||
684 | /* hold a ref on it while actually updating */ | |
685 | afs_get_vlocation(vlocation); | |
686 | afs_vlocation_update = vlocation; | |
687 | vlocation->upd_state = AFS_VLUPD_INPROGRESS; | |
688 | ||
689 | spin_unlock(&afs_vlocation_update_lock); | |
690 | spin_unlock(&vlocation->cell->vl_gylock); | |
691 | ||
692 | /* okay... we can start the update */ | |
693 | _debug("BEGIN VL UPDATE [%s]", vlocation->vldb.name); | |
694 | vlocation->upd_first_svix = vlocation->cell->vl_curr_svix; | |
695 | vlocation->upd_curr_svix = vlocation->upd_first_svix; | |
696 | vlocation->upd_rej_cnt = 0; | |
697 | vlocation->upd_busy_cnt = 0; | |
698 | ||
699 | ret = afs_vlocation_update_begin(vlocation); | |
700 | if (ret < 0) { | |
701 | afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, ret); | |
702 | afs_kafstimod_add_timer(&vlocation->upd_timer, | |
703 | AFS_VLDB_TIMEOUT); | |
704 | afs_put_vlocation(vlocation); | |
705 | } | |
706 | ||
707 | _leave(""); | |
708 | return; | |
709 | ||
710 | out_unlock2: | |
711 | spin_unlock(&afs_vlocation_update_lock); | |
712 | out_unlock1: | |
713 | spin_unlock(&vlocation->cell->vl_gylock); | |
714 | _leave(""); | |
715 | return; | |
716 | ||
717 | } /* end afs_vlocation_update_timer() */ | |
718 | ||
719 | /*****************************************************************************/ | |
720 | /* | |
721 | * attend to an update operation upon which an event happened | |
722 | * - called in kafsasyncd context | |
723 | */ | |
724 | static void afs_vlocation_update_attend(struct afs_async_op *op) | |
725 | { | |
726 | struct afs_cache_vlocation vldb; | |
727 | struct afs_vlocation *vlocation = | |
728 | list_entry(op, struct afs_vlocation, upd_op); | |
729 | unsigned tmp; | |
730 | int ret; | |
731 | ||
732 | _enter("%s", vlocation->vldb.name); | |
733 | ||
734 | ret = afs_rxvl_get_entry_by_id_async2(op, &vldb); | |
735 | switch (ret) { | |
736 | case -EAGAIN: | |
737 | _leave(" [unfinished]"); | |
738 | return; | |
739 | ||
740 | case 0: | |
741 | _debug("END VL UPDATE: %d\n", ret); | |
742 | vlocation->valid = 1; | |
743 | ||
744 | _debug("Done VL Lookup: %02x { %08x(%x) %08x(%x) %08x(%x) }", | |
745 | vldb.vidmask, | |
746 | ntohl(vldb.servers[0].s_addr), vldb.srvtmask[0], | |
747 | ntohl(vldb.servers[1].s_addr), vldb.srvtmask[1], | |
748 | ntohl(vldb.servers[2].s_addr), vldb.srvtmask[2] | |
749 | ); | |
750 | ||
751 | _debug("Vids: %08x %08x %08x", | |
752 | vldb.vid[0], vldb.vid[1], vldb.vid[2]); | |
753 | ||
754 | afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, 0); | |
755 | ||
756 | down_write(&vlocation->cell->vl_sem); | |
757 | ||
758 | /* actually update the cache */ | |
759 | if (strncmp(vldb.name, vlocation->vldb.name, | |
760 | sizeof(vlocation->vldb.name)) != 0) | |
761 | printk("kAFS: name of volume '%s'" | |
762 | " changed to '%s' on server\n", | |
763 | vlocation->vldb.name, vldb.name); | |
764 | ||
765 | memcpy(&vlocation->vldb, &vldb, sizeof(vlocation->vldb)); | |
766 | ||
767 | #if 0 | |
768 | /* TODO update volume entry in local cache */ | |
769 | #endif | |
770 | ||
771 | up_write(&vlocation->cell->vl_sem); | |
772 | ||
773 | if (ret < 0) | |
774 | printk("kAFS: failed to update local cache: %d\n", ret); | |
775 | ||
776 | afs_kafstimod_add_timer(&vlocation->upd_timer, | |
777 | AFS_VLDB_TIMEOUT); | |
778 | afs_put_vlocation(vlocation); | |
779 | _leave(" [found]"); | |
780 | return; | |
781 | ||
782 | case -ENOMEDIUM: | |
783 | vlocation->upd_rej_cnt++; | |
784 | goto try_next; | |
785 | ||
786 | /* the server is locked - retry in a very short while */ | |
787 | case -EBUSY: | |
788 | vlocation->upd_busy_cnt++; | |
789 | if (vlocation->upd_busy_cnt > 3) | |
790 | goto try_next; /* too many retries */ | |
791 | ||
792 | afs_vlocation_update_abandon(vlocation, | |
793 | AFS_VLUPD_BUSYSLEEP, 0); | |
794 | afs_kafstimod_add_timer(&vlocation->upd_timer, HZ / 2); | |
795 | afs_put_vlocation(vlocation); | |
796 | _leave(" [busy]"); | |
797 | return; | |
798 | ||
799 | case -ENETUNREACH: | |
800 | case -EHOSTUNREACH: | |
801 | case -ECONNREFUSED: | |
802 | case -EREMOTEIO: | |
803 | /* record bad vlserver info in the cell too | |
804 | * - TODO: use down_write_trylock() if available | |
805 | */ | |
806 | if (vlocation->upd_curr_svix == vlocation->cell->vl_curr_svix) | |
807 | vlocation->cell->vl_curr_svix = | |
808 | vlocation->cell->vl_curr_svix % | |
809 | vlocation->cell->vl_naddrs; | |
810 | ||
811 | case -EBADRQC: | |
812 | case -EINVAL: | |
813 | case -EACCES: | |
814 | case -EBADMSG: | |
815 | goto try_next; | |
816 | ||
817 | default: | |
818 | goto abandon; | |
819 | } | |
820 | ||
821 | /* try contacting the next server */ | |
822 | try_next: | |
823 | vlocation->upd_busy_cnt = 0; | |
824 | ||
825 | /* discard the server record */ | |
826 | afs_put_server(vlocation->upd_op.server); | |
827 | vlocation->upd_op.server = NULL; | |
828 | ||
829 | tmp = vlocation->cell->vl_naddrs; | |
830 | if (tmp == 0) | |
831 | goto abandon; | |
832 | ||
833 | vlocation->upd_curr_svix++; | |
834 | if (vlocation->upd_curr_svix >= tmp) | |
835 | vlocation->upd_curr_svix = 0; | |
836 | if (vlocation->upd_first_svix >= tmp) | |
837 | vlocation->upd_first_svix = tmp - 1; | |
838 | ||
839 | /* move to the next server */ | |
840 | if (vlocation->upd_curr_svix != vlocation->upd_first_svix) { | |
841 | afs_vlocation_update_begin(vlocation); | |
842 | _leave(" [next]"); | |
843 | return; | |
844 | } | |
845 | ||
846 | /* run out of servers to try - was the volume rejected? */ | |
847 | if (vlocation->upd_rej_cnt > 0) { | |
848 | printk("kAFS: Active volume no longer valid '%s'\n", | |
849 | vlocation->vldb.name); | |
850 | vlocation->valid = 0; | |
851 | afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, 0); | |
852 | afs_kafstimod_add_timer(&vlocation->upd_timer, | |
853 | AFS_VLDB_TIMEOUT); | |
854 | afs_put_vlocation(vlocation); | |
855 | _leave(" [invalidated]"); | |
856 | return; | |
857 | } | |
858 | ||
859 | /* abandon the update */ | |
860 | abandon: | |
861 | afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, ret); | |
862 | afs_kafstimod_add_timer(&vlocation->upd_timer, HZ * 10); | |
863 | afs_put_vlocation(vlocation); | |
864 | _leave(" [abandoned]"); | |
865 | ||
866 | } /* end afs_vlocation_update_attend() */ | |
867 | ||
868 | /*****************************************************************************/ | |
869 | /* | |
870 | * deal with an update operation being discarded | |
871 | * - called in kafsasyncd context when it's dying due to rmmod | |
872 | * - the call has already been aborted and put()'d | |
873 | */ | |
874 | static void afs_vlocation_update_discard(struct afs_async_op *op) | |
875 | { | |
876 | struct afs_vlocation *vlocation = | |
877 | list_entry(op, struct afs_vlocation, upd_op); | |
878 | ||
879 | _enter("%s", vlocation->vldb.name); | |
880 | ||
881 | afs_put_server(op->server); | |
882 | op->server = NULL; | |
883 | ||
884 | afs_put_vlocation(vlocation); | |
885 | ||
886 | _leave(""); | |
887 | } /* end afs_vlocation_update_discard() */ | |
888 | ||
889 | /*****************************************************************************/ | |
890 | /* | |
891 | * match a VLDB record stored in the cache | |
892 | * - may also load target from entry | |
893 | */ | |
894 | #ifdef AFS_CACHING_SUPPORT | |
895 | static cachefs_match_val_t afs_vlocation_cache_match(void *target, | |
896 | const void *entry) | |
897 | { | |
898 | const struct afs_cache_vlocation *vldb = entry; | |
899 | struct afs_vlocation *vlocation = target; | |
900 | ||
901 | _enter("{%s},{%s}", vlocation->vldb.name, vldb->name); | |
902 | ||
903 | if (strncmp(vlocation->vldb.name, vldb->name, sizeof(vldb->name)) == 0 | |
904 | ) { | |
905 | if (!vlocation->valid || | |
906 | vlocation->vldb.rtime == vldb->rtime | |
907 | ) { | |
908 | vlocation->vldb = *vldb; | |
909 | vlocation->valid = 1; | |
910 | _leave(" = SUCCESS [c->m]"); | |
911 | return CACHEFS_MATCH_SUCCESS; | |
912 | } | |
913 | /* need to update cache if cached info differs */ | |
914 | else if (memcmp(&vlocation->vldb, vldb, sizeof(*vldb)) != 0) { | |
915 | /* delete if VIDs for this name differ */ | |
916 | if (memcmp(&vlocation->vldb.vid, | |
917 | &vldb->vid, | |
918 | sizeof(vldb->vid)) != 0) { | |
919 | _leave(" = DELETE"); | |
920 | return CACHEFS_MATCH_SUCCESS_DELETE; | |
921 | } | |
922 | ||
923 | _leave(" = UPDATE"); | |
924 | return CACHEFS_MATCH_SUCCESS_UPDATE; | |
925 | } | |
926 | else { | |
927 | _leave(" = SUCCESS"); | |
928 | return CACHEFS_MATCH_SUCCESS; | |
929 | } | |
930 | } | |
931 | ||
932 | _leave(" = FAILED"); | |
933 | return CACHEFS_MATCH_FAILED; | |
934 | } /* end afs_vlocation_cache_match() */ | |
935 | #endif | |
936 | ||
937 | /*****************************************************************************/ | |
938 | /* | |
939 | * update a VLDB record stored in the cache | |
940 | */ | |
941 | #ifdef AFS_CACHING_SUPPORT | |
942 | static void afs_vlocation_cache_update(void *source, void *entry) | |
943 | { | |
944 | struct afs_cache_vlocation *vldb = entry; | |
945 | struct afs_vlocation *vlocation = source; | |
946 | ||
947 | _enter(""); | |
948 | ||
949 | *vldb = vlocation->vldb; | |
950 | ||
951 | } /* end afs_vlocation_cache_update() */ | |
952 | #endif |