diff options
Diffstat (limited to 'fs/afs/cell.c')
-rw-r--r-- | fs/afs/cell.c | 569 |
1 files changed, 569 insertions, 0 deletions
diff --git a/fs/afs/cell.c b/fs/afs/cell.c new file mode 100644 index 000000000000..009a9ae88d61 --- /dev/null +++ b/fs/afs/cell.c | |||
@@ -0,0 +1,569 @@ | |||
1 | /* cell.c: AFS cell and server record 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/module.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <rxrpc/peer.h> | ||
16 | #include <rxrpc/connection.h> | ||
17 | #include "volume.h" | ||
18 | #include "cell.h" | ||
19 | #include "server.h" | ||
20 | #include "transport.h" | ||
21 | #include "vlclient.h" | ||
22 | #include "kafstimod.h" | ||
23 | #include "super.h" | ||
24 | #include "internal.h" | ||
25 | |||
26 | DECLARE_RWSEM(afs_proc_cells_sem); | ||
27 | LIST_HEAD(afs_proc_cells); | ||
28 | |||
29 | static struct list_head afs_cells = LIST_HEAD_INIT(afs_cells); | ||
30 | static DEFINE_RWLOCK(afs_cells_lock); | ||
31 | static DECLARE_RWSEM(afs_cells_sem); /* add/remove serialisation */ | ||
32 | static struct afs_cell *afs_cell_root; | ||
33 | |||
34 | #ifdef AFS_CACHING_SUPPORT | ||
35 | static cachefs_match_val_t afs_cell_cache_match(void *target, | ||
36 | const void *entry); | ||
37 | static void afs_cell_cache_update(void *source, void *entry); | ||
38 | |||
39 | struct cachefs_index_def afs_cache_cell_index_def = { | ||
40 | .name = "cell_ix", | ||
41 | .data_size = sizeof(struct afs_cache_cell), | ||
42 | .keys[0] = { CACHEFS_INDEX_KEYS_ASCIIZ, 64 }, | ||
43 | .match = afs_cell_cache_match, | ||
44 | .update = afs_cell_cache_update, | ||
45 | }; | ||
46 | #endif | ||
47 | |||
48 | /*****************************************************************************/ | ||
49 | /* | ||
50 | * create a cell record | ||
51 | * - "name" is the name of the cell | ||
52 | * - "vllist" is a colon separated list of IP addresses in "a.b.c.d" format | ||
53 | */ | ||
54 | int afs_cell_create(const char *name, char *vllist, struct afs_cell **_cell) | ||
55 | { | ||
56 | struct afs_cell *cell; | ||
57 | char *next; | ||
58 | int ret; | ||
59 | |||
60 | _enter("%s", name); | ||
61 | |||
62 | BUG_ON(!name); /* TODO: want to look up "this cell" in the cache */ | ||
63 | |||
64 | /* allocate and initialise a cell record */ | ||
65 | cell = kmalloc(sizeof(struct afs_cell) + strlen(name) + 1, GFP_KERNEL); | ||
66 | if (!cell) { | ||
67 | _leave(" = -ENOMEM"); | ||
68 | return -ENOMEM; | ||
69 | } | ||
70 | |||
71 | down_write(&afs_cells_sem); | ||
72 | |||
73 | memset(cell, 0, sizeof(struct afs_cell)); | ||
74 | atomic_set(&cell->usage, 0); | ||
75 | |||
76 | INIT_LIST_HEAD(&cell->link); | ||
77 | |||
78 | rwlock_init(&cell->sv_lock); | ||
79 | INIT_LIST_HEAD(&cell->sv_list); | ||
80 | INIT_LIST_HEAD(&cell->sv_graveyard); | ||
81 | spin_lock_init(&cell->sv_gylock); | ||
82 | |||
83 | init_rwsem(&cell->vl_sem); | ||
84 | INIT_LIST_HEAD(&cell->vl_list); | ||
85 | INIT_LIST_HEAD(&cell->vl_graveyard); | ||
86 | spin_lock_init(&cell->vl_gylock); | ||
87 | |||
88 | strcpy(cell->name,name); | ||
89 | |||
90 | /* fill in the VL server list from the rest of the string */ | ||
91 | ret = -EINVAL; | ||
92 | do { | ||
93 | unsigned a, b, c, d; | ||
94 | |||
95 | next = strchr(vllist, ':'); | ||
96 | if (next) | ||
97 | *next++ = 0; | ||
98 | |||
99 | if (sscanf(vllist, "%u.%u.%u.%u", &a, &b, &c, &d) != 4) | ||
100 | goto badaddr; | ||
101 | |||
102 | if (a > 255 || b > 255 || c > 255 || d > 255) | ||
103 | goto badaddr; | ||
104 | |||
105 | cell->vl_addrs[cell->vl_naddrs++].s_addr = | ||
106 | htonl((a << 24) | (b << 16) | (c << 8) | d); | ||
107 | |||
108 | if (cell->vl_naddrs >= AFS_CELL_MAX_ADDRS) | ||
109 | break; | ||
110 | |||
111 | } while(vllist = next, vllist); | ||
112 | |||
113 | /* add a proc dir for this cell */ | ||
114 | ret = afs_proc_cell_setup(cell); | ||
115 | if (ret < 0) | ||
116 | goto error; | ||
117 | |||
118 | #ifdef AFS_CACHING_SUPPORT | ||
119 | /* put it up for caching */ | ||
120 | cachefs_acquire_cookie(afs_cache_netfs.primary_index, | ||
121 | &afs_vlocation_cache_index_def, | ||
122 | cell, | ||
123 | &cell->cache); | ||
124 | #endif | ||
125 | |||
126 | /* add to the cell lists */ | ||
127 | write_lock(&afs_cells_lock); | ||
128 | list_add_tail(&cell->link, &afs_cells); | ||
129 | write_unlock(&afs_cells_lock); | ||
130 | |||
131 | down_write(&afs_proc_cells_sem); | ||
132 | list_add_tail(&cell->proc_link, &afs_proc_cells); | ||
133 | up_write(&afs_proc_cells_sem); | ||
134 | |||
135 | *_cell = cell; | ||
136 | up_write(&afs_cells_sem); | ||
137 | |||
138 | _leave(" = 0 (%p)", cell); | ||
139 | return 0; | ||
140 | |||
141 | badaddr: | ||
142 | printk(KERN_ERR "kAFS: bad VL server IP address: '%s'\n", vllist); | ||
143 | error: | ||
144 | up_write(&afs_cells_sem); | ||
145 | kfree(cell); | ||
146 | _leave(" = %d", ret); | ||
147 | return ret; | ||
148 | } /* end afs_cell_create() */ | ||
149 | |||
150 | /*****************************************************************************/ | ||
151 | /* | ||
152 | * initialise the cell database from module parameters | ||
153 | */ | ||
154 | int afs_cell_init(char *rootcell) | ||
155 | { | ||
156 | struct afs_cell *old_root, *new_root; | ||
157 | char *cp; | ||
158 | int ret; | ||
159 | |||
160 | _enter(""); | ||
161 | |||
162 | if (!rootcell) { | ||
163 | /* module is loaded with no parameters, or built statically. | ||
164 | * - in the future we might initialize cell DB here. | ||
165 | */ | ||
166 | _leave(" = 0 (but no root)"); | ||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | cp = strchr(rootcell, ':'); | ||
171 | if (!cp) { | ||
172 | printk(KERN_ERR "kAFS: no VL server IP addresses specified\n"); | ||
173 | _leave(" = %d (no colon)", -EINVAL); | ||
174 | return -EINVAL; | ||
175 | } | ||
176 | |||
177 | /* allocate a cell record for the root cell */ | ||
178 | *cp++ = 0; | ||
179 | ret = afs_cell_create(rootcell, cp, &new_root); | ||
180 | if (ret < 0) { | ||
181 | _leave(" = %d", ret); | ||
182 | return ret; | ||
183 | } | ||
184 | |||
185 | /* as afs_put_cell() takes locks by itself, we have to do | ||
186 | * a little gymnastics to be race-free. | ||
187 | */ | ||
188 | afs_get_cell(new_root); | ||
189 | |||
190 | write_lock(&afs_cells_lock); | ||
191 | while (afs_cell_root) { | ||
192 | old_root = afs_cell_root; | ||
193 | afs_cell_root = NULL; | ||
194 | write_unlock(&afs_cells_lock); | ||
195 | afs_put_cell(old_root); | ||
196 | write_lock(&afs_cells_lock); | ||
197 | } | ||
198 | afs_cell_root = new_root; | ||
199 | write_unlock(&afs_cells_lock); | ||
200 | |||
201 | _leave(" = %d", ret); | ||
202 | return ret; | ||
203 | |||
204 | } /* end afs_cell_init() */ | ||
205 | |||
206 | /*****************************************************************************/ | ||
207 | /* | ||
208 | * lookup a cell record | ||
209 | */ | ||
210 | int afs_cell_lookup(const char *name, unsigned namesz, struct afs_cell **_cell) | ||
211 | { | ||
212 | struct afs_cell *cell; | ||
213 | int ret; | ||
214 | |||
215 | _enter("\"%*.*s\",", namesz, namesz, name ? name : ""); | ||
216 | |||
217 | *_cell = NULL; | ||
218 | |||
219 | if (name) { | ||
220 | /* if the cell was named, look for it in the cell record list */ | ||
221 | ret = -ENOENT; | ||
222 | cell = NULL; | ||
223 | read_lock(&afs_cells_lock); | ||
224 | |||
225 | list_for_each_entry(cell, &afs_cells, link) { | ||
226 | if (strncmp(cell->name, name, namesz) == 0) { | ||
227 | afs_get_cell(cell); | ||
228 | goto found; | ||
229 | } | ||
230 | } | ||
231 | cell = NULL; | ||
232 | found: | ||
233 | |||
234 | read_unlock(&afs_cells_lock); | ||
235 | |||
236 | if (cell) | ||
237 | ret = 0; | ||
238 | } | ||
239 | else { | ||
240 | read_lock(&afs_cells_lock); | ||
241 | |||
242 | cell = afs_cell_root; | ||
243 | if (!cell) { | ||
244 | /* this should not happen unless user tries to mount | ||
245 | * when root cell is not set. Return an impossibly | ||
246 | * bizzare errno to alert the user. Things like | ||
247 | * ENOENT might be "more appropriate" but they happen | ||
248 | * for other reasons. | ||
249 | */ | ||
250 | ret = -EDESTADDRREQ; | ||
251 | } | ||
252 | else { | ||
253 | afs_get_cell(cell); | ||
254 | ret = 0; | ||
255 | } | ||
256 | |||
257 | read_unlock(&afs_cells_lock); | ||
258 | } | ||
259 | |||
260 | *_cell = cell; | ||
261 | _leave(" = %d (%p)", ret, cell); | ||
262 | return ret; | ||
263 | |||
264 | } /* end afs_cell_lookup() */ | ||
265 | |||
266 | /*****************************************************************************/ | ||
267 | /* | ||
268 | * try and get a cell record | ||
269 | */ | ||
270 | struct afs_cell *afs_get_cell_maybe(struct afs_cell **_cell) | ||
271 | { | ||
272 | struct afs_cell *cell; | ||
273 | |||
274 | write_lock(&afs_cells_lock); | ||
275 | |||
276 | cell = *_cell; | ||
277 | if (cell && !list_empty(&cell->link)) | ||
278 | afs_get_cell(cell); | ||
279 | else | ||
280 | cell = NULL; | ||
281 | |||
282 | write_unlock(&afs_cells_lock); | ||
283 | |||
284 | return cell; | ||
285 | } /* end afs_get_cell_maybe() */ | ||
286 | |||
287 | /*****************************************************************************/ | ||
288 | /* | ||
289 | * destroy a cell record | ||
290 | */ | ||
291 | void afs_put_cell(struct afs_cell *cell) | ||
292 | { | ||
293 | if (!cell) | ||
294 | return; | ||
295 | |||
296 | _enter("%p{%d,%s}", cell, atomic_read(&cell->usage), cell->name); | ||
297 | |||
298 | /* sanity check */ | ||
299 | BUG_ON(atomic_read(&cell->usage) <= 0); | ||
300 | |||
301 | /* to prevent a race, the decrement and the dequeue must be effectively | ||
302 | * atomic */ | ||
303 | write_lock(&afs_cells_lock); | ||
304 | |||
305 | if (likely(!atomic_dec_and_test(&cell->usage))) { | ||
306 | write_unlock(&afs_cells_lock); | ||
307 | _leave(""); | ||
308 | return; | ||
309 | } | ||
310 | |||
311 | write_unlock(&afs_cells_lock); | ||
312 | |||
313 | BUG_ON(!list_empty(&cell->sv_list)); | ||
314 | BUG_ON(!list_empty(&cell->sv_graveyard)); | ||
315 | BUG_ON(!list_empty(&cell->vl_list)); | ||
316 | BUG_ON(!list_empty(&cell->vl_graveyard)); | ||
317 | |||
318 | _leave(" [unused]"); | ||
319 | } /* end afs_put_cell() */ | ||
320 | |||
321 | /*****************************************************************************/ | ||
322 | /* | ||
323 | * destroy a cell record | ||
324 | */ | ||
325 | static void afs_cell_destroy(struct afs_cell *cell) | ||
326 | { | ||
327 | _enter("%p{%d,%s}", cell, atomic_read(&cell->usage), cell->name); | ||
328 | |||
329 | /* to prevent a race, the decrement and the dequeue must be effectively | ||
330 | * atomic */ | ||
331 | write_lock(&afs_cells_lock); | ||
332 | |||
333 | /* sanity check */ | ||
334 | BUG_ON(atomic_read(&cell->usage) != 0); | ||
335 | |||
336 | list_del_init(&cell->link); | ||
337 | |||
338 | write_unlock(&afs_cells_lock); | ||
339 | |||
340 | down_write(&afs_cells_sem); | ||
341 | |||
342 | afs_proc_cell_remove(cell); | ||
343 | |||
344 | down_write(&afs_proc_cells_sem); | ||
345 | list_del_init(&cell->proc_link); | ||
346 | up_write(&afs_proc_cells_sem); | ||
347 | |||
348 | #ifdef AFS_CACHING_SUPPORT | ||
349 | cachefs_relinquish_cookie(cell->cache, 0); | ||
350 | #endif | ||
351 | |||
352 | up_write(&afs_cells_sem); | ||
353 | |||
354 | BUG_ON(!list_empty(&cell->sv_list)); | ||
355 | BUG_ON(!list_empty(&cell->sv_graveyard)); | ||
356 | BUG_ON(!list_empty(&cell->vl_list)); | ||
357 | BUG_ON(!list_empty(&cell->vl_graveyard)); | ||
358 | |||
359 | /* finish cleaning up the cell */ | ||
360 | kfree(cell); | ||
361 | |||
362 | _leave(" [destroyed]"); | ||
363 | } /* end afs_cell_destroy() */ | ||
364 | |||
365 | /*****************************************************************************/ | ||
366 | /* | ||
367 | * lookup the server record corresponding to an Rx RPC peer | ||
368 | */ | ||
369 | int afs_server_find_by_peer(const struct rxrpc_peer *peer, | ||
370 | struct afs_server **_server) | ||
371 | { | ||
372 | struct afs_server *server; | ||
373 | struct afs_cell *cell; | ||
374 | |||
375 | _enter("%p{a=%08x},", peer, ntohl(peer->addr.s_addr)); | ||
376 | |||
377 | /* search the cell list */ | ||
378 | read_lock(&afs_cells_lock); | ||
379 | |||
380 | list_for_each_entry(cell, &afs_cells, link) { | ||
381 | |||
382 | _debug("? cell %s",cell->name); | ||
383 | |||
384 | write_lock(&cell->sv_lock); | ||
385 | |||
386 | /* check the active list */ | ||
387 | list_for_each_entry(server, &cell->sv_list, link) { | ||
388 | _debug("?? server %08x", ntohl(server->addr.s_addr)); | ||
389 | |||
390 | if (memcmp(&server->addr, &peer->addr, | ||
391 | sizeof(struct in_addr)) == 0) | ||
392 | goto found_server; | ||
393 | } | ||
394 | |||
395 | /* check the inactive list */ | ||
396 | spin_lock(&cell->sv_gylock); | ||
397 | list_for_each_entry(server, &cell->sv_graveyard, link) { | ||
398 | _debug("?? dead server %08x", | ||
399 | ntohl(server->addr.s_addr)); | ||
400 | |||
401 | if (memcmp(&server->addr, &peer->addr, | ||
402 | sizeof(struct in_addr)) == 0) | ||
403 | goto found_dead_server; | ||
404 | } | ||
405 | spin_unlock(&cell->sv_gylock); | ||
406 | |||
407 | write_unlock(&cell->sv_lock); | ||
408 | } | ||
409 | read_unlock(&afs_cells_lock); | ||
410 | |||
411 | _leave(" = -ENOENT"); | ||
412 | return -ENOENT; | ||
413 | |||
414 | /* we found it in the graveyard - resurrect it */ | ||
415 | found_dead_server: | ||
416 | list_del(&server->link); | ||
417 | list_add_tail(&server->link, &cell->sv_list); | ||
418 | afs_get_server(server); | ||
419 | afs_kafstimod_del_timer(&server->timeout); | ||
420 | spin_unlock(&cell->sv_gylock); | ||
421 | goto success; | ||
422 | |||
423 | /* we found it - increment its ref count and return it */ | ||
424 | found_server: | ||
425 | afs_get_server(server); | ||
426 | |||
427 | success: | ||
428 | write_unlock(&cell->sv_lock); | ||
429 | read_unlock(&afs_cells_lock); | ||
430 | |||
431 | *_server = server; | ||
432 | _leave(" = 0 (s=%p c=%p)", server, cell); | ||
433 | return 0; | ||
434 | |||
435 | } /* end afs_server_find_by_peer() */ | ||
436 | |||
437 | /*****************************************************************************/ | ||
438 | /* | ||
439 | * purge in-memory cell database on module unload or afs_init() failure | ||
440 | * - the timeout daemon is stopped before calling this | ||
441 | */ | ||
442 | void afs_cell_purge(void) | ||
443 | { | ||
444 | struct afs_vlocation *vlocation; | ||
445 | struct afs_cell *cell; | ||
446 | |||
447 | _enter(""); | ||
448 | |||
449 | afs_put_cell(afs_cell_root); | ||
450 | |||
451 | while (!list_empty(&afs_cells)) { | ||
452 | cell = NULL; | ||
453 | |||
454 | /* remove the next cell from the front of the list */ | ||
455 | write_lock(&afs_cells_lock); | ||
456 | |||
457 | if (!list_empty(&afs_cells)) { | ||
458 | cell = list_entry(afs_cells.next, | ||
459 | struct afs_cell, link); | ||
460 | list_del_init(&cell->link); | ||
461 | } | ||
462 | |||
463 | write_unlock(&afs_cells_lock); | ||
464 | |||
465 | if (cell) { | ||
466 | _debug("PURGING CELL %s (%d)", | ||
467 | cell->name, atomic_read(&cell->usage)); | ||
468 | |||
469 | BUG_ON(!list_empty(&cell->sv_list)); | ||
470 | BUG_ON(!list_empty(&cell->vl_list)); | ||
471 | |||
472 | /* purge the cell's VL graveyard list */ | ||
473 | _debug(" - clearing VL graveyard"); | ||
474 | |||
475 | spin_lock(&cell->vl_gylock); | ||
476 | |||
477 | while (!list_empty(&cell->vl_graveyard)) { | ||
478 | vlocation = list_entry(cell->vl_graveyard.next, | ||
479 | struct afs_vlocation, | ||
480 | link); | ||
481 | list_del_init(&vlocation->link); | ||
482 | |||
483 | afs_kafstimod_del_timer(&vlocation->timeout); | ||
484 | |||
485 | spin_unlock(&cell->vl_gylock); | ||
486 | |||
487 | afs_vlocation_do_timeout(vlocation); | ||
488 | /* TODO: race if move to use krxtimod instead | ||
489 | * of kafstimod */ | ||
490 | |||
491 | spin_lock(&cell->vl_gylock); | ||
492 | } | ||
493 | |||
494 | spin_unlock(&cell->vl_gylock); | ||
495 | |||
496 | /* purge the cell's server graveyard list */ | ||
497 | _debug(" - clearing server graveyard"); | ||
498 | |||
499 | spin_lock(&cell->sv_gylock); | ||
500 | |||
501 | while (!list_empty(&cell->sv_graveyard)) { | ||
502 | struct afs_server *server; | ||
503 | |||
504 | server = list_entry(cell->sv_graveyard.next, | ||
505 | struct afs_server, link); | ||
506 | list_del_init(&server->link); | ||
507 | |||
508 | afs_kafstimod_del_timer(&server->timeout); | ||
509 | |||
510 | spin_unlock(&cell->sv_gylock); | ||
511 | |||
512 | afs_server_do_timeout(server); | ||
513 | |||
514 | spin_lock(&cell->sv_gylock); | ||
515 | } | ||
516 | |||
517 | spin_unlock(&cell->sv_gylock); | ||
518 | |||
519 | /* now the cell should be left with no references */ | ||
520 | afs_cell_destroy(cell); | ||
521 | } | ||
522 | } | ||
523 | |||
524 | _leave(""); | ||
525 | } /* end afs_cell_purge() */ | ||
526 | |||
527 | /*****************************************************************************/ | ||
528 | /* | ||
529 | * match a cell record obtained from the cache | ||
530 | */ | ||
531 | #ifdef AFS_CACHING_SUPPORT | ||
532 | static cachefs_match_val_t afs_cell_cache_match(void *target, | ||
533 | const void *entry) | ||
534 | { | ||
535 | const struct afs_cache_cell *ccell = entry; | ||
536 | struct afs_cell *cell = target; | ||
537 | |||
538 | _enter("{%s},{%s}", ccell->name, cell->name); | ||
539 | |||
540 | if (strncmp(ccell->name, cell->name, sizeof(ccell->name)) == 0) { | ||
541 | _leave(" = SUCCESS"); | ||
542 | return CACHEFS_MATCH_SUCCESS; | ||
543 | } | ||
544 | |||
545 | _leave(" = FAILED"); | ||
546 | return CACHEFS_MATCH_FAILED; | ||
547 | } /* end afs_cell_cache_match() */ | ||
548 | #endif | ||
549 | |||
550 | /*****************************************************************************/ | ||
551 | /* | ||
552 | * update a cell record in the cache | ||
553 | */ | ||
554 | #ifdef AFS_CACHING_SUPPORT | ||
555 | static void afs_cell_cache_update(void *source, void *entry) | ||
556 | { | ||
557 | struct afs_cache_cell *ccell = entry; | ||
558 | struct afs_cell *cell = source; | ||
559 | |||
560 | _enter("%p,%p", source, entry); | ||
561 | |||
562 | strncpy(ccell->name, cell->name, sizeof(ccell->name)); | ||
563 | |||
564 | memcpy(ccell->vl_servers, | ||
565 | cell->vl_addrs, | ||
566 | min(sizeof(ccell->vl_servers), sizeof(cell->vl_addrs))); | ||
567 | |||
568 | } /* end afs_cell_cache_update() */ | ||
569 | #endif | ||