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); | |
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 |