diff options
Diffstat (limited to 'fs/afs/cell.c')
-rw-r--r-- | fs/afs/cell.c | 344 |
1 files changed, 70 insertions, 274 deletions
diff --git a/fs/afs/cell.c b/fs/afs/cell.c index 28ed84ec8ff7..733c60246ab0 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -11,15 +11,6 @@ #include <linux/module.h> #include <linux/slab.h> -#include <rxrpc/peer.h> -#include <rxrpc/connection.h> -#include "volume.h" -#include "cell.h" -#include "server.h" -#include "transport.h" -#include "vlclient.h" -#include "kafstimod.h" -#include "super.h" #include "internal.h" DECLARE_RWSEM(afs_proc_cells_sem); @@ -28,34 +19,21 @@ LIST_HEAD(afs_proc_cells); static struct list_head afs_cells = LIST_HEAD_INIT(afs_cells); static DEFINE_RWLOCK(afs_cells_lock); static DECLARE_RWSEM(afs_cells_sem); /* add/remove serialisation */ +static DECLARE_WAIT_QUEUE_HEAD(afs_cells_freeable_wq); static struct afs_cell *afs_cell_root; -#ifdef AFS_CACHING_SUPPORT -static cachefs_match_val_t afs_cell_cache_match(void *target, - const void *entry); -static void afs_cell_cache_update(void *source, void *entry); - -struct cachefs_index_def afs_cache_cell_index_def = { - .name = "cell_ix", - .data_size = sizeof(struct afs_cache_cell), - .keys[0] = { CACHEFS_INDEX_KEYS_ASCIIZ, 64 }, - .match = afs_cell_cache_match, - .update = afs_cell_cache_update, -}; -#endif - /* * create a cell record * - "name" is the name of the cell * - "vllist" is a colon separated list of IP addresses in "a.b.c.d" format */ -int afs_cell_create(const char *name, char *vllist, struct afs_cell **_cell) +struct afs_cell *afs_cell_create(const char *name, char *vllist) { struct afs_cell *cell; char *next; int ret; - _enter("%s", name); + _enter("%s,%s", name, vllist); BUG_ON(!name); /* TODO: want to look up "this cell" in the cache */ @@ -63,27 +41,24 @@ int afs_cell_create(const char *name, char *vllist, struct afs_cell **_cell) cell = kmalloc(sizeof(struct afs_cell) + strlen(name) + 1, GFP_KERNEL); if (!cell) { _leave(" = -ENOMEM"); - return -ENOMEM; + return ERR_PTR(-ENOMEM); } down_write(&afs_cells_sem); memset(cell, 0, sizeof(struct afs_cell)); - atomic_set(&cell->usage, 0); + atomic_set(&cell->usage, 1); INIT_LIST_HEAD(&cell->link); - rwlock_init(&cell->sv_lock); - INIT_LIST_HEAD(&cell->sv_list); - INIT_LIST_HEAD(&cell->sv_graveyard); - spin_lock_init(&cell->sv_gylock); + rwlock_init(&cell->servers_lock); + INIT_LIST_HEAD(&cell->servers); init_rwsem(&cell->vl_sem); INIT_LIST_HEAD(&cell->vl_list); - INIT_LIST_HEAD(&cell->vl_graveyard); - spin_lock_init(&cell->vl_gylock); + spin_lock_init(&cell->vl_lock); - strcpy(cell->name,name); + strcpy(cell->name, name); /* fill in the VL server list from the rest of the string */ ret = -EINVAL; @@ -106,9 +81,9 @@ int afs_cell_create(const char *name, char *vllist, struct afs_cell **_cell) if (cell->vl_naddrs >= AFS_CELL_MAX_ADDRS) break; - } while(vllist = next, vllist); + } while ((vllist = next)); - /* add a proc dir for this cell */ + /* add a proc directory for this cell */ ret = afs_proc_cell_setup(cell); if (ret < 0) goto error; @@ -129,30 +104,29 @@ int afs_cell_create(const char *name, char *vllist, struct afs_cell **_cell) down_write(&afs_proc_cells_sem); list_add_tail(&cell->proc_link, &afs_proc_cells); up_write(&afs_proc_cells_sem); - - *_cell = cell; up_write(&afs_cells_sem); - _leave(" = 0 (%p)", cell); - return 0; + _leave(" = %p", cell); + return cell; badaddr: - printk(KERN_ERR "kAFS: bad VL server IP address: '%s'\n", vllist); + printk(KERN_ERR "kAFS: bad VL server IP address\n"); error: up_write(&afs_cells_sem); kfree(cell); _leave(" = %d", ret); - return ret; + return ERR_PTR(ret); } /* - * initialise the cell database from module parameters + * set the root cell information + * - can be called with a module parameter string + * - can be called from a write to /proc/fs/afs/rootcell */ int afs_cell_init(char *rootcell) { struct afs_cell *old_root, *new_root; char *cp; - int ret; _enter(""); @@ -160,79 +134,60 @@ int afs_cell_init(char *rootcell) /* module is loaded with no parameters, or built statically. * - in the future we might initialize cell DB here. */ - _leave(" = 0 (but no root)"); + _leave(" = 0 [no root]"); return 0; } cp = strchr(rootcell, ':'); if (!cp) { printk(KERN_ERR "kAFS: no VL server IP addresses specified\n"); - _leave(" = %d (no colon)", -EINVAL); + _leave(" = -EINVAL"); return -EINVAL; } /* allocate a cell record for the root cell */ *cp++ = 0; - ret = afs_cell_create(rootcell, cp, &new_root); - if (ret < 0) { - _leave(" = %d", ret); - return ret; + new_root = afs_cell_create(rootcell, cp); + if (IS_ERR(new_root)) { + _leave(" = %ld", PTR_ERR(new_root)); + return PTR_ERR(new_root); } - /* as afs_put_cell() takes locks by itself, we have to do - * a little gymnastics to be race-free. - */ - afs_get_cell(new_root); - + /* install the new cell */ write_lock(&afs_cells_lock); - while (afs_cell_root) { - old_root = afs_cell_root; - afs_cell_root = NULL; - write_unlock(&afs_cells_lock); - afs_put_cell(old_root); - write_lock(&afs_cells_lock); - } + old_root = afs_cell_root; afs_cell_root = new_root; write_unlock(&afs_cells_lock); + afs_put_cell(old_root); - _leave(" = %d", ret); - return ret; + _leave(" = 0"); + return 0; } /* * lookup a cell record */ -int afs_cell_lookup(const char *name, unsigned namesz, struct afs_cell **_cell) +struct afs_cell *afs_cell_lookup(const char *name, unsigned namesz) { struct afs_cell *cell; - int ret; _enter("\"%*.*s\",", namesz, namesz, name ? name : ""); - *_cell = NULL; + down_read(&afs_cells_sem); + read_lock(&afs_cells_lock); if (name) { /* if the cell was named, look for it in the cell record list */ - ret = -ENOENT; - cell = NULL; - read_lock(&afs_cells_lock); - list_for_each_entry(cell, &afs_cells, link) { if (strncmp(cell->name, name, namesz) == 0) { afs_get_cell(cell); goto found; } } - cell = NULL; + cell = ERR_PTR(-ENOENT); found: - - read_unlock(&afs_cells_lock); - - if (cell) - ret = 0; + ; } else { - read_lock(&afs_cells_lock); - cell = afs_cell_root; if (!cell) { /* this should not happen unless user tries to mount @@ -241,37 +196,32 @@ int afs_cell_lookup(const char *name, unsigned namesz, struct afs_cell **_cell) * ENOENT might be "more appropriate" but they happen * for other reasons. */ - ret = -EDESTADDRREQ; + cell = ERR_PTR(-EDESTADDRREQ); } else { afs_get_cell(cell); - ret = 0; } - read_unlock(&afs_cells_lock); } - *_cell = cell; - _leave(" = %d (%p)", ret, cell); - return ret; + read_unlock(&afs_cells_lock); + up_read(&afs_cells_sem); + _leave(" = %p", cell); + return cell; } /* * try and get a cell record */ -struct afs_cell *afs_get_cell_maybe(struct afs_cell **_cell) +struct afs_cell *afs_get_cell_maybe(struct afs_cell *cell) { - struct afs_cell *cell; - write_lock(&afs_cells_lock); - cell = *_cell; if (cell && !list_empty(&cell->link)) afs_get_cell(cell); else cell = NULL; write_unlock(&afs_cells_lock); - return cell; } @@ -285,8 +235,7 @@ void afs_put_cell(struct afs_cell *cell) _enter("%p{%d,%s}", cell, atomic_read(&cell->usage), cell->name); - /* sanity check */ - BUG_ON(atomic_read(&cell->usage) <= 0); + ASSERTCMP(atomic_read(&cell->usage), >, 0); /* to prevent a race, the decrement and the dequeue must be effectively * atomic */ @@ -298,35 +247,49 @@ void afs_put_cell(struct afs_cell *cell) return; } + ASSERT(list_empty(&cell->servers)); + ASSERT(list_empty(&cell->vl_list)); + write_unlock(&afs_cells_lock); - BUG_ON(!list_empty(&cell->sv_list)); - BUG_ON(!list_empty(&cell->sv_graveyard)); - BUG_ON(!list_empty(&cell->vl_list)); - BUG_ON(!list_empty(&cell->vl_graveyard)); + wake_up(&afs_cells_freeable_wq); _leave(" [unused]"); } /* * destroy a cell record + * - must be called with the afs_cells_sem write-locked + * - cell->link should have been broken by the caller */ static void afs_cell_destroy(struct afs_cell *cell) { _enter("%p{%d,%s}", cell, atomic_read(&cell->usage), cell->name); - /* to prevent a race, the decrement and the dequeue must be effectively - * atomic */ - write_lock(&afs_cells_lock); + ASSERTCMP(atomic_read(&cell->usage), >=, 0); + ASSERT(list_empty(&cell->link)); - /* sanity check */ - BUG_ON(atomic_read(&cell->usage) != 0); + /* wait for everyone to stop using the cell */ + if (atomic_read(&cell->usage) > 0) { + DECLARE_WAITQUEUE(myself, current); - list_del_init(&cell->link); + _debug("wait for cell %s", cell->name); + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&afs_cells_freeable_wq, &myself); - write_unlock(&afs_cells_lock); + while (atomic_read(&cell->usage) > 0) { + schedule(); + set_current_state(TASK_UNINTERRUPTIBLE); + } - down_write(&afs_cells_sem); + remove_wait_queue(&afs_cells_freeable_wq, &myself); + set_current_state(TASK_RUNNING); + } + + _debug("cell dead"); + ASSERTCMP(atomic_read(&cell->usage), ==, 0); + ASSERT(list_empty(&cell->servers)); + ASSERT(list_empty(&cell->vl_list)); afs_proc_cell_remove(cell); @@ -338,101 +301,25 @@ static void afs_cell_destroy(struct afs_cell *cell) cachefs_relinquish_cookie(cell->cache, 0); #endif - up_write(&afs_cells_sem); - - BUG_ON(!list_empty(&cell->sv_list)); - BUG_ON(!list_empty(&cell->sv_graveyard)); - BUG_ON(!list_empty(&cell->vl_list)); - BUG_ON(!list_empty(&cell->vl_graveyard)); - - /* finish cleaning up the cell */ kfree(cell); _leave(" [destroyed]"); } /* - * lookup the server record corresponding to an Rx RPC peer - */ -int afs_server_find_by_peer(const struct rxrpc_peer *peer, - struct afs_server **_server) -{ - struct afs_server *server; - struct afs_cell *cell; - - _enter("%p{a=%08x},", peer, ntohl(peer->addr.s_addr)); - - /* search the cell list */ - read_lock(&afs_cells_lock); - - list_for_each_entry(cell, &afs_cells, link) { - - _debug("? cell %s",cell->name); - - write_lock(&cell->sv_lock); - - /* check the active list */ - list_for_each_entry(server, &cell->sv_list, link) { - _debug("?? server %08x", ntohl(server->addr.s_addr)); - - if (memcmp(&server->addr, &peer->addr, - sizeof(struct in_addr)) == 0) - goto found_server; - } - - /* check the inactive list */ - spin_lock(&cell->sv_gylock); - list_for_each_entry(server, &cell->sv_graveyard, link) { - _debug("?? dead server %08x", - ntohl(server->addr.s_addr)); - - if (memcmp(&server->addr, &peer->addr, - sizeof(struct in_addr)) == 0) - goto found_dead_server; - } - spin_unlock(&cell->sv_gylock); - - write_unlock(&cell->sv_lock); - } - read_unlock(&afs_cells_lock); - - _leave(" = -ENOENT"); - return -ENOENT; - - /* we found it in the graveyard - resurrect it */ -found_dead_server: - list_move_tail(&server->link, &cell->sv_list); - afs_get_server(server); - afs_kafstimod_del_timer(&server->timeout); - spin_unlock(&cell->sv_gylock); - goto success; - - /* we found it - increment its ref count and return it */ -found_server: - afs_get_server(server); - -success: - write_unlock(&cell->sv_lock); - read_unlock(&afs_cells_lock); - - *_server = server; - _leave(" = 0 (s=%p c=%p)", server, cell); - return 0; -} - -/* * purge in-memory cell database on module unload or afs_init() failure * - the timeout daemon is stopped before calling this */ void afs_cell_purge(void) { - struct afs_vlocation *vlocation; struct afs_cell *cell; _enter(""); afs_put_cell(afs_cell_root); + down_write(&afs_cells_sem); + while (!list_empty(&afs_cells)) { cell = NULL; @@ -451,102 +338,11 @@ void afs_cell_purge(void) _debug("PURGING CELL %s (%d)", cell->name, atomic_read(&cell->usage)); - BUG_ON(!list_empty(&cell->sv_list)); - BUG_ON(!list_empty(&cell->vl_list)); - - /* purge the cell's VL graveyard list */ - _debug(" - clearing VL graveyard"); - - spin_lock(&cell->vl_gylock); - - while (!list_empty(&cell->vl_graveyard)) { - vlocation = list_entry(cell->vl_graveyard.next, - struct afs_vlocation, - link); - list_del_init(&vlocation->link); - - afs_kafstimod_del_timer(&vlocation->timeout); - - spin_unlock(&cell->vl_gylock); - - afs_vlocation_do_timeout(vlocation); - /* TODO: race if move to use krxtimod instead - * of kafstimod */ - - spin_lock(&cell->vl_gylock); - } - - spin_unlock(&cell->vl_gylock); - - /* purge the cell's server graveyard list */ - _debug(" - clearing server graveyard"); - - spin_lock(&cell->sv_gylock); - - while (!list_empty(&cell->sv_graveyard)) { - struct afs_server *server; - - server = list_entry(cell->sv_graveyard.next, - struct afs_server, link); - list_del_init(&server->link); - - afs_kafstimod_del_timer(&server->timeout); - - spin_unlock(&cell->sv_gylock); - - afs_server_do_timeout(server); - - spin_lock(&cell->sv_gylock); - } - - spin_unlock(&cell->sv_gylock); - /* now the cell should be left with no references */ afs_cell_destroy(cell); } } + up_write(&afs_cells_sem); _leave(""); } - -/* - * match a cell record obtained from the cache - */ -#ifdef AFS_CACHING_SUPPORT -static cachefs_match_val_t afs_cell_cache_match(void *target, - const void *entry) -{ - const struct afs_cache_cell *ccell = entry; - struct afs_cell *cell = target; - - _enter("{%s},{%s}", ccell->name, cell->name); - - if (strncmp(ccell->name, cell->name, sizeof(ccell->name)) == 0) { - _leave(" = SUCCESS"); - return CACHEFS_MATCH_SUCCESS; - } - - _leave(" = FAILED"); - return CACHEFS_MATCH_FAILED; -} -#endif - -/* - * update a cell record in the cache - */ -#ifdef AFS_CACHING_SUPPORT -static void afs_cell_cache_update(void *source, void *entry) -{ - struct afs_cache_cell *ccell = entry; - struct afs_cell *cell = source; - - _enter("%p,%p", source, entry); - - strncpy(ccell->name, cell->name, sizeof(ccell->name)); - - memcpy(ccell->vl_servers, - cell->vl_addrs, - min(sizeof(ccell->vl_servers), sizeof(cell->vl_addrs))); - -} -#endif |