aboutsummaryrefslogtreecommitdiff
path: root/fs/afs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/afs
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'fs/afs')
-rw-r--r--fs/afs/Makefile28
-rw-r--r--fs/afs/cache.h27
-rw-r--r--fs/afs/callback.c168
-rw-r--r--fs/afs/cell.c569
-rw-r--r--fs/afs/cell.h78
-rw-r--r--fs/afs/cmservice.c652
-rw-r--r--fs/afs/cmservice.h29
-rw-r--r--fs/afs/dir.c666
-rw-r--r--fs/afs/errors.h34
-rw-r--r--fs/afs/file.c305
-rw-r--r--fs/afs/fsclient.c837
-rw-r--r--fs/afs/fsclient.h54
-rw-r--r--fs/afs/inode.c287
-rw-r--r--fs/afs/internal.h140
-rw-r--r--fs/afs/kafsasyncd.c257
-rw-r--r--fs/afs/kafsasyncd.h52
-rw-r--r--fs/afs/kafstimod.c204
-rw-r--r--fs/afs/kafstimod.h49
-rw-r--r--fs/afs/main.c286
-rw-r--r--fs/afs/misc.c39
-rw-r--r--fs/afs/mntpt.c287
-rw-r--r--fs/afs/mount.h23
-rw-r--r--fs/afs/proc.c857
-rw-r--r--fs/afs/server.c502
-rw-r--r--fs/afs/server.h102
-rw-r--r--fs/afs/super.c441
-rw-r--r--fs/afs/super.h43
-rw-r--r--fs/afs/transport.h21
-rw-r--r--fs/afs/types.h125
-rw-r--r--fs/afs/vlclient.c695
-rw-r--r--fs/afs/vlclient.h93
-rw-r--r--fs/afs/vlocation.c954
-rw-r--r--fs/afs/vnode.c395
-rw-r--r--fs/afs/vnode.h94
-rw-r--r--fs/afs/volume.c520
-rw-r--r--fs/afs/volume.h142
36 files changed, 10055 insertions, 0 deletions
diff --git a/fs/afs/Makefile b/fs/afs/Makefile
new file mode 100644
index 00000000000..4029c9da4b8
--- /dev/null
+++ b/fs/afs/Makefile
@@ -0,0 +1,28 @@
+#
+# Makefile for Red Hat Linux AFS client.
+#
+
+#CFLAGS += -finstrument-functions
+
+kafs-objs := \
+ callback.o \
+ cell.o \
+ cmservice.o \
+ dir.o \
+ file.o \
+ fsclient.o \
+ inode.o \
+ kafsasyncd.o \
+ kafstimod.o \
+ main.o \
+ misc.o \
+ mntpt.o \
+ proc.o \
+ server.o \
+ super.o \
+ vlclient.o \
+ vlocation.o \
+ vnode.o \
+ volume.o
+
+obj-$(CONFIG_AFS_FS) := kafs.o
diff --git a/fs/afs/cache.h b/fs/afs/cache.h
new file mode 100644
index 00000000000..9eb7722b34d
--- /dev/null
+++ b/fs/afs/cache.h
@@ -0,0 +1,27 @@
+/* cache.h: AFS local cache management interface
+ *
+ * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_AFS_CACHE_H
+#define _LINUX_AFS_CACHE_H
+
+#undef AFS_CACHING_SUPPORT
+
+#include <linux/mm.h>
+#ifdef AFS_CACHING_SUPPORT
+#include <linux/cachefs.h>
+#endif
+#include "types.h"
+
+#ifdef __KERNEL__
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_AFS_CACHE_H */
diff --git a/fs/afs/callback.c b/fs/afs/callback.c
new file mode 100644
index 00000000000..2fd62f89ae0
--- /dev/null
+++ b/fs/afs/callback.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2002 Red Hat, Inc. All rights reserved.
+ *
+ * This software may be freely redistributed under the terms of the
+ * GNU General Public License.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Authors: David Woodhouse <dwmw2@cambridge.redhat.com>
+ * David Howells <dhowells@redhat.com>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include "server.h"
+#include "vnode.h"
+#include "internal.h"
+
+/*****************************************************************************/
+/*
+ * allow the fileserver to request callback state (re-)initialisation
+ */
+int SRXAFSCM_InitCallBackState(struct afs_server *server)
+{
+ struct list_head callbacks;
+
+ _enter("%p", server);
+
+ INIT_LIST_HEAD(&callbacks);
+
+ /* transfer the callback list from the server to a temp holding area */
+ spin_lock(&server->cb_lock);
+
+ list_add(&callbacks, &server->cb_promises);
+ list_del_init(&server->cb_promises);
+
+ /* munch our way through the list, grabbing the inode, dropping all the
+ * locks and regetting them in the right order
+ */
+ while (!list_empty(&callbacks)) {
+ struct afs_vnode *vnode;
+ struct inode *inode;
+
+ vnode = list_entry(callbacks.next, struct afs_vnode, cb_link);
+ list_del_init(&vnode->cb_link);
+
+ /* try and grab the inode - may fail */
+ inode = igrab(AFS_VNODE_TO_I(vnode));
+ if (inode) {
+ int release = 0;
+
+ spin_unlock(&server->cb_lock);
+ spin_lock(&vnode->lock);
+
+ if (vnode->cb_server == server) {
+ vnode->cb_server = NULL;
+ afs_kafstimod_del_timer(&vnode->cb_timeout);
+ spin_lock(&afs_cb_hash_lock);
+ list_del_init(&vnode->cb_hash_link);
+ spin_unlock(&afs_cb_hash_lock);
+ release = 1;
+ }
+
+ spin_unlock(&vnode->lock);
+
+ iput(inode);
+ afs_put_server(server);
+
+ spin_lock(&server->cb_lock);
+ }
+ }
+
+ spin_unlock(&server->cb_lock);
+
+ _leave(" = 0");
+ return 0;
+} /* end SRXAFSCM_InitCallBackState() */
+
+/*****************************************************************************/
+/*
+ * allow the fileserver to break callback promises
+ */
+int SRXAFSCM_CallBack(struct afs_server *server, size_t count,
+ struct afs_callback callbacks[])
+{
+ _enter("%p,%u,", server, count);
+
+ for (; count > 0; callbacks++, count--) {
+ struct afs_vnode *vnode = NULL;
+ struct inode *inode = NULL;
+ int valid = 0;
+
+ _debug("- Fid { vl=%08x n=%u u=%u } CB { v=%u x=%u t=%u }",
+ callbacks->fid.vid,
+ callbacks->fid.vnode,
+ callbacks->fid.unique,
+ callbacks->version,
+ callbacks->expiry,
+ callbacks->type
+ );
+
+ /* find the inode for this fid */
+ spin_lock(&afs_cb_hash_lock);
+
+ list_for_each_entry(vnode,
+ &afs_cb_hash(server, &callbacks->fid),
+ cb_hash_link) {
+ if (memcmp(&vnode->fid, &callbacks->fid,
+ sizeof(struct afs_fid)) != 0)
+ continue;
+
+ /* right vnode, but is it same server? */
+ if (vnode->cb_server != server)
+ break; /* no */
+
+ /* try and nail the inode down */
+ inode = igrab(AFS_VNODE_TO_I(vnode));
+ break;
+ }
+
+ spin_unlock(&afs_cb_hash_lock);
+
+ if (inode) {
+ /* we've found the record for this vnode */
+ spin_lock(&vnode->lock);
+ if (vnode->cb_server == server) {
+ /* the callback _is_ on the calling server */
+ vnode->cb_server = NULL;
+ valid = 1;
+
+ afs_kafstimod_del_timer(&vnode->cb_timeout);
+ vnode->flags |= AFS_VNODE_CHANGED;
+
+ spin_lock(&server->cb_lock);
+ list_del_init(&vnode->cb_link);
+ spin_unlock(&server->cb_lock);
+
+ spin_lock(&afs_cb_hash_lock);
+ list_del_init(&vnode->cb_hash_link);
+ spin_unlock(&afs_cb_hash_lock);
+ }
+ spin_unlock(&vnode->lock);
+
+ if (valid) {
+ invalidate_remote_inode(inode);
+ afs_put_server(server);
+ }
+ iput(inode);
+ }
+ }
+
+ _leave(" = 0");
+ return 0;
+} /* end SRXAFSCM_CallBack() */
+
+/*****************************************************************************/
+/*
+ * allow the fileserver to see if the cache manager is still alive
+ */
+int SRXAFSCM_Probe(struct afs_server *server)
+{
+ _debug("SRXAFSCM_Probe(%p)\n", server);
+ return 0;
+} /* end SRXAFSCM_Probe() */
diff --git a/fs/afs/cell.c b/fs/afs/cell.c
new file mode 100644
index 00000000000..009a9ae88d6
--- /dev/null
+++ b/fs/afs/cell.c
@@ -0,0 +1,569 @@
+/* cell.c: AFS cell and server record management
+ *
+ * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#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 "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 DEFINE_RWLOCK(afs_cells_lock);
+static DECLARE_RWSEM(afs_cells_sem); /* add/remove serialisation */
+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 *cell;
+ char *next;
+ int ret;
+
+ _enter("%s", name);
+
+ BUG_ON(!name); /* TODO: want to look up "this cell" in the cache */
+
+ /* allocate and initialise a cell record */
+ cell = kmalloc(sizeof(struct afs_cell) + strlen(name) + 1, GFP_KERNEL);
+ if (!cell) {
+ _leave(" = -ENOMEM");
+ return -ENOMEM;
+ }
+
+ down_write(&afs_cells_sem);
+
+ memset(cell, 0, sizeof(struct afs_cell));
+ atomic_set(&cell->usage, 0);
+
+ 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);
+
+ 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);
+
+ /* fill in the VL server list from the rest of the string */
+ ret = -EINVAL;
+ do {
+ unsigned a, b, c, d;
+
+ next = strchr(vllist, ':');
+ if (next)
+ *next++ = 0;
+
+ if (sscanf(vllist, "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
+ goto badaddr;
+
+ if (a > 255 || b > 255 || c > 255 || d > 255)
+ goto badaddr;
+
+ 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(vllist = next, vllist);
+
+ /* add a proc dir 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);
+#endif
+
+ /* add to the cell lists */
+ write_lock(&afs_cells_lock);
+ list_add_tail(&cell->link, &afs_cells);
+ write_unlock(&afs_cells_lock);
+
+ 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;
+
+ badaddr:
+ printk(KERN_ERR "kAFS: bad VL server IP address: '%s'\n", vllist);
+ error:
+ up_write(&afs_cells_sem);
+ kfree(cell);
+ _leave(" = %d", ret);
+ return ret;
+} /* end afs_cell_create() */
+
+/*****************************************************************************/
+/*
+ * initialise the cell database from module parameters
+ */
+int afs_cell_init(char *rootcell)
+{
+ struct afs_cell *old_root, *new_root;
+ char *cp;
+ int ret;
+
+ _enter("");
+
+ if (!rootcell) {
+ /* module is loaded with no parameters, or built statically.
+ * - in the future we might initialize cell DB here.
+ */
+ _leave(" = 0 (but 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);
+ 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;
+ }
+
+ /* as afs_put_cell() takes locks by itself, we have to do
+ * a little gymnastics to be race-free.
+ */
+ afs_get_cell(new_root);
+
+ 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);
+ }
+ afs_cell_root = new_root;
+ write_unlock(&afs_cells_lock);
+
+ _leave(" = %d", ret);
+ return ret;
+
+} /* end afs_cell_init() */
+
+/*****************************************************************************/
+/*
+ * lookup a cell record
+ */
+int afs_cell_lookup(const char *name, unsigned namesz, struct afs_cell **_cell)
+{
+ struct afs_cell *cell;
+ int ret;
+
+ _enter("\"%*.*s\",", namesz, namesz, name ? name : "");
+
+ *_cell = NULL;
+
+ 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;
+ 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
+ * when root cell is not set. Return an impossibly
+ * bizzare errno to alert the user. Things like
+ * ENOENT might be "more appropriate" but they happen
+ * for other reasons.
+ */
+ ret = -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() */
+
+/*****************************************************************************/
+/*
+ * try and get a cell record
+ */
+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() */
+
+/*****************************************************************************/
+/*
+ * destroy a cell record
+ */
+void afs_put_cell(struct afs_cell *cell)
+{
+ if (!cell)
+ return;
+
+ _enter("%p{%d,%s}", cell, atomic_read(&cell->usage), cell->name);
+
+ /* sanity check */
+ BUG_ON(atomic_read(&cell->usage) <= 0);
+
+ /* to prevent a race, the decrement and the dequeue must be effectively
+ * atomic */
+ write_lock(&afs_cells_lock);
+
+ if (likely(!atomic_dec_and_test(&cell->usage))) {
+ write_unlock(&afs_cells_lock);
+ _leave("");
+ return;
+ }
+
+ 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));
+
+ _leave(" [unused]");
+} /* end afs_put_cell() */
+
+/*****************************************************************************/
+/*
+ * destroy a cell record
+ */
+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);
+
+ /* sanity check */
+ BUG_ON(atomic_read(&cell->usage) != 0);
+
+ list_del_init(&cell->link);
+
+ write_unlock(&afs_cells_lock);
+
+ down_write(&afs_cells_sem);
+
+ afs_proc_cell_remove(cell);
+
+ down_write(&afs_proc_cells_sem);
+ list_del_init(&cell->proc_link);
+ up_write(&afs_proc_cells_sem);
+
+#ifdef AFS_CACHING_SUPPORT
+ 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]");
+} /* 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);
+
+ while (!list_empty(&afs_cells)) {
+ cell = NULL;
+
+ /* remove the next cell from the front of the list */
+ write_lock(&afs_cells_lock);
+
+ if (!list_empty(&afs_cells)) {
+ cell = list_entry(afs_cells.next,
+ struct afs_cell, link);
+ list_del_init(&cell->link);
+ }
+
+ write_unlock(&afs_cells_lock);
+
+ if (cell) {
+ _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);
+ }
+ }
+
+ _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
diff --git a/fs/afs/cell.h b/fs/afs/cell.h
new file mode 100644
index 00000000000..48349108fb0
--- /dev/null
+++ b/fs/afs/cell.h
@@ -0,0 +1,78 @@
+/* cell.h: AFS cell record
+ *
+ * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_AFS_CELL_H
+#define _LINUX_AFS_CELL_H
+
+#include "types.h"
+#include "cache.h"
+
+#define AFS_CELL_MAX_ADDRS 15
+
+extern volatile int afs_cells_being_purged; /* T when cells are being purged by rmmod */
+
+/*****************************************************************************/
+/*
+ * entry in the cached cell catalogue
+ */
+struct afs_cache_cell
+{
+ char name[64]; /* cell name (padded with NULs) */
+ struct in_addr vl_servers[15]; /* cached cell VL servers */
+};
+
+/*****************************************************************************/
+/*
+ * AFS cell record
+ */
+struct afs_cell
+{
+ atomic_t usage;
+ struct list_head link; /* main cell list link */
+ struct list_head proc_link; /* /proc cell list link */
+ struct proc_dir_entry *proc_dir; /* /proc dir for this cell */
+#ifdef AFS_CACHING_SUPPORT
+ struct cachefs_cookie *cache; /* caching cookie */
+#endif
+
+ /* server record management */
+ rwlock_t sv_lock; /* active server list lock */
+ struct list_head sv_list; /* active server list */
+ struct list_head sv_graveyard; /* inactive server list */
+ spinlock_t sv_gylock; /* inactive server list lock */
+
+ /* volume location record management */
+ struct rw_semaphore vl_sem; /* volume management serialisation semaphore */
+ struct list_head vl_list; /* cell's active VL record list */
+ struct list_head vl_graveyard; /* cell's inactive VL record list */
+ spinlock_t vl_gylock; /* graveyard lock */
+ unsigned short vl_naddrs; /* number of VL servers in addr list */
+ unsigned short vl_curr_svix; /* current server index */
+ struct in_addr vl_addrs[AFS_CELL_MAX_ADDRS]; /* cell VL server addresses */
+
+ char name[0]; /* cell name - must go last */
+};
+
+extern int afs_cell_init(char *rootcell);
+
+extern int afs_cell_create(const char *name, char *vllist, struct afs_cell **_cell);
+
+extern int afs_cell_lookup(const char *name, unsigned nmsize, struct afs_cell **_cell);
+
+#define afs_get_cell(C) do { atomic_inc(&(C)->usage); } while(0)
+
+extern struct afs_cell *afs_get_cell_maybe(struct afs_cell **_cell);
+
+extern void afs_put_cell(struct afs_cell *cell);
+
+extern void afs_cell_purge(void);
+
+#endif /* _LINUX_AFS_CELL_H */
diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c
new file mode 100644
index 00000000000..0a57fd7c726
--- /dev/null
+++ b/fs/afs/cmservice.c
@@ -0,0 +1,652 @@
+/* cmservice.c: AFS Cache Manager Service
+ *
+ * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h&