writeback: get rid of wbc->for_writepages
[safe/jmp/linux-2.6] / fs / afs / cell.c
index 009a9ae..e19c13f 100644 (file)
@@ -1,4 +1,4 @@
-/* cell.c: AFS cell and server record management
+/* AFS cell and server record management
  *
  * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  */
 
 #include <linux/module.h>
-#include <linux/sched.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 <linux/key.h>
+#include <linux/ctype.h>
+#include <linux/sched.h>
+#include <keys/rxrpc-type.h>
 #include "internal.h"
 
 DECLARE_RWSEM(afs_proc_cells_sem);
 LIST_HEAD(afs_proc_cells);
 
-static struct list_head afs_cells = LIST_HEAD_INIT(afs_cells);
+static LIST_HEAD(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
+ * allocate a cell record and fill in its name, VL server address list and
+ * allocate an anonymous key
  */
-int afs_cell_create(const char *name, char *vllist, struct afs_cell **_cell)
+static struct afs_cell *afs_cell_alloc(const char *name, char *vllist)
 {
        struct afs_cell *cell;
-       char *next;
+       struct key *key;
+       size_t namelen;
+       char keyname[4 + AFS_MAXCELLNAME + 1], *cp, *dp, *next;
        int ret;
 
-       _enter("%s", name);
+       _enter("%s,%s", name, vllist);
 
        BUG_ON(!name); /* TODO: want to look up "this cell" in the cache */
 
+       namelen = strlen(name);
+       if (namelen > AFS_MAXCELLNAME)
+               return ERR_PTR(-ENAMETOOLONG);
+
        /* allocate and initialise a cell record */
-       cell = kmalloc(sizeof(struct afs_cell) + strlen(name) + 1, GFP_KERNEL);
+       cell = kzalloc(sizeof(struct afs_cell) + namelen + 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);
+       memcpy(cell->name, name, namelen);
+       cell->name[namelen] = 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);
-
-       strcpy(cell->name,name);
+       spin_lock_init(&cell->vl_lock);
 
        /* fill in the VL server list from the rest of the string */
-       ret = -EINVAL;
        do {
                unsigned a, b, c, d;
 
@@ -97,30 +73,85 @@ int afs_cell_create(const char *name, char *vllist, struct afs_cell **_cell)
                        *next++ = 0;
 
                if (sscanf(vllist, "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
-                       goto badaddr;
+                       goto bad_address;
 
                if (a > 255 || b > 255 || c > 255 || d > 255)
-                       goto badaddr;
+                       goto bad_address;
 
                cell->vl_addrs[cell->vl_naddrs++].s_addr =
                        htonl((a << 24) | (b << 16) | (c << 8) | d);
 
-               if (cell->vl_naddrs >= AFS_CELL_MAX_ADDRS)
-                       break;
+       } while (cell->vl_naddrs < AFS_CELL_MAX_ADDRS && (vllist = next));
+
+       /* create a key to represent an anonymous user */
+       memcpy(keyname, "afs@", 4);
+       dp = keyname + 4;
+       cp = cell->name;
+       do {
+               *dp++ = toupper(*cp);
+       } while (*cp++);
+
+       key = rxrpc_get_null_key(keyname);
+       if (IS_ERR(key)) {
+               _debug("no key");
+               ret = PTR_ERR(key);
+               goto error;
+       }
+       cell->anonymous_key = key;
+
+       _debug("anon key %p{%x}",
+              cell->anonymous_key, key_serial(cell->anonymous_key));
+
+       _leave(" = %p", cell);
+       return cell;
+
+bad_address:
+       printk(KERN_ERR "kAFS: bad VL server IP address\n");
+       ret = -EINVAL;
+error:
+       key_put(cell->anonymous_key);
+       kfree(cell);
+       _leave(" = %d", ret);
+       return ERR_PTR(ret);
+}
+
+/*
+ * 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
+ */
+struct afs_cell *afs_cell_create(const char *name, char *vllist)
+{
+       struct afs_cell *cell;
+       int ret;
+
+       _enter("%s,%s", name, vllist);
+
+       down_write(&afs_cells_sem);
+       read_lock(&afs_cells_lock);
+       list_for_each_entry(cell, &afs_cells, link) {
+               if (strcasecmp(cell->name, name) == 0)
+                       goto duplicate_name;
+       }
+       read_unlock(&afs_cells_lock);
 
-       } while(vllist = next, vllist);
+       cell = afs_cell_alloc(name, vllist);
+       if (IS_ERR(cell)) {
+               _leave(" = %ld", PTR_ERR(cell));
+               up_write(&afs_cells_sem);
+               return cell;
+       }
 
-       /* 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;
 
-#ifdef AFS_CACHING_SUPPORT
-       /* put it up for caching */
-       cachefs_acquire_cookie(afs_cache_netfs.primary_index,
-                              &afs_vlocation_cache_index_def,
-                              cell,
-                              &cell->cache);
+#ifdef CONFIG_AFS_FSCACHE
+       /* put it up for caching (this never returns an error) */
+       cell->cache = fscache_acquire_cookie(afs_cache_netfs.primary_index,
+                                            &afs_cell_cache_index_def,
+                                            cell);
 #endif
 
        /* add to the cell lists */
@@ -131,31 +162,33 @@ 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);
- error:
+error:
        up_write(&afs_cells_sem);
+       key_put(cell->anonymous_key);
        kfree(cell);
        _leave(" = %d", ret);
-       return ret;
-} /* end afs_cell_create() */
+       return ERR_PTR(ret);
+
+duplicate_name:
+       read_unlock(&afs_cells_lock);
+       up_write(&afs_cells_sem);
+       return ERR_PTR(-EEXIST);
+}
 
-/*****************************************************************************/
 /*
- * 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("");
 
@@ -163,82 +196,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;
-
-} /* end afs_cell_init() */
+       _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);
-
+               ;
+       } else {
                cell = afs_cell_root;
                if (!cell) {
                        /* this should not happen unless user tries to mount
@@ -247,44 +258,37 @@ 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;
-               }
-               else {
+                       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;
-
-} /* end afs_cell_lookup() */
+       read_unlock(&afs_cells_lock);
+       up_read(&afs_cells_sem);
+       _leave(" = %p", cell);
+       return cell;
+}
 
-/*****************************************************************************/
+#if 0
 /*
  * 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;
-} /* end afs_get_cell_maybe() */
+}
+#endif  /*  0  */
 
-/*****************************************************************************/
 /*
  * destroy a cell record
  */
@@ -295,8 +299,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 */
@@ -308,36 +311,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]");
-} /* end afs_put_cell() */
+}
 
-/*****************************************************************************/
 /*
  * 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);
 
@@ -345,109 +361,29 @@ static void afs_cell_destroy(struct afs_cell *cell)
        list_del_init(&cell->proc_link);
        up_write(&afs_proc_cells_sem);
 
-#ifdef AFS_CACHING_SUPPORT
-       cachefs_relinquish_cookie(cell->cache, 0);
+#ifdef CONFIG_AFS_FSCACHE
+       fscache_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 */
+       key_put(cell->anonymous_key);
        kfree(cell);
 
        _leave(" [destroyed]");
-} /* end afs_cell_destroy() */
+}
 
-/*****************************************************************************/
-/*
- * 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_del(&server->link);
-       list_add_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;
-
-} /* end afs_server_find_by_peer() */
-
-/*****************************************************************************/
 /*
  * 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;
 
@@ -466,104 +402,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("");
-} /* end afs_cell_purge() */
-
-/*****************************************************************************/
-/*
- * 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;
-} /* end afs_cell_cache_match() */
-#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)));
-
-} /* end afs_cell_cache_update() */
-#endif
+}