Merge tag 'afs-next-20171113' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowel...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 16 Nov 2017 19:41:22 +0000 (11:41 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 16 Nov 2017 19:41:22 +0000 (11:41 -0800)
Pull AFS updates from David Howells:
 "kAFS filesystem driver overhaul.

  The major points of the overhaul are:

   (1) Preliminary groundwork is laid for supporting network-namespacing
       of kAFS. The remainder of the namespacing work requires some way
       to pass namespace information to submounts triggered by an
       automount. This requires something like the mount overhaul that's
       in progress.

   (2) sockaddr_rxrpc is used in preference to in_addr for holding
       addresses internally and add support for talking to the YFS VL
       server. With this, kAFS can do everything over IPv6 as well as
       IPv4 if it's talking to servers that support it.

   (3) Callback handling is overhauled to be generally passive rather
       than active. 'Callbacks' are promises by the server to tell us
       about data and metadata changes. Callbacks are now checked when
       we next touch an inode rather than actively going and looking for
       it where possible.

   (4) File access permit caching is overhauled to store the caching
       information per-inode rather than per-directory, shared over
       subordinate files. Whilst older AFS servers only allow ACLs on
       directories (shared to the files in that directory), newer AFS
       servers break that restriction.

       To improve memory usage and to make it easier to do mass-key
       removal, permit combinations are cached and shared.

   (5) Cell database management is overhauled to allow lighter locks to
       be used and to make cell records autonomous state machines that
       look after getting their own DNS records and cleaning themselves
       up, in particular preventing races in acquiring and relinquishing
       the fscache token for the cell.

   (6) Volume caching is overhauled. The afs_vlocation record is got rid
       of to simplify things and the superblock is now keyed on the cell
       and the numeric volume ID only. The volume record is tied to a
       superblock and normal superblock management is used to mediate
       the lifetime of the volume fscache token.

   (7) File server record caching is overhauled to make server records
       independent of cells and volumes. A server can be in multiple
       cells (in such a case, the administrator must make sure that the
       VL services for all cells correctly reflect the volumes shared
       between those cells).

       Server records are now indexed using the UUID of the server
       rather than the address since a server can have multiple
       addresses.

   (8) File server rotation is overhauled to handle VMOVED, VBUSY (and
       similar), VOFFLINE and VNOVOL indications and to handle rotation
       both of servers and addresses of those servers. The rotation will
       also wait and retry if the server says it is busy.

   (9) Data writeback is overhauled. Each inode no longer stores a list
       of modified sections tagged with the key that authorised it in
       favour of noting the modified region of a page in page->private
       and storing a list of keys that made modifications in the inode.

       This simplifies things and allows other keys to be used to
       actually write to the server if a key that made a modification
       becomes useless.

  (10) Writable mmap() is implemented. This allows a kernel to be build
       entirely on AFS.

  Note that Pre AFS-3.4 servers are no longer supported, though this can
  be added back if necessary (AFS-3.4 was released in 1998)"

* tag 'afs-next-20171113' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs: (35 commits)
  afs: Protect call->state changes against signals
  afs: Trace page dirty/clean
  afs: Implement shared-writeable mmap
  afs: Get rid of the afs_writeback record
  afs: Introduce a file-private data record
  afs: Use a dynamic port if 7001 is in use
  afs: Fix directory read/modify race
  afs: Trace the sending of pages
  afs: Trace the initiation and completion of client calls
  afs: Fix documentation on # vs % prefix in mount source specification
  afs: Fix total-length calculation for multiple-page send
  afs: Only progress call state at end of Tx phase from rxrpc callback
  afs: Make use of the YFS service upgrade to fully support IPv6
  afs: Overhaul volume and server record caching and fileserver rotation
  afs: Move server rotation code into its own file
  afs: Add an address list concept
  afs: Overhaul cell database management
  afs: Overhaul permit caching
  afs: Overhaul the callback handling
  afs: Rename struct afs_call server member to cm_server
  ...

47 files changed:
Documentation/filesystems/afs.txt
arch/mips/kernel/traps.c
drivers/gpu/drm/drm_dp_aux_dev.c
drivers/gpu/drm/i915/selftests/intel_breadcrumbs.c
drivers/media/platform/qcom/venus/hfi.c
fs/afs/Makefile
fs/afs/addr_list.c [new file with mode: 0644]
fs/afs/afs.h
fs/afs/afs_fs.h
fs/afs/afs_vl.h
fs/afs/cache.c
fs/afs/callback.c
fs/afs/cell.c
fs/afs/cmservice.c
fs/afs/dir.c
fs/afs/file.c
fs/afs/flock.c
fs/afs/fsclient.c
fs/afs/inode.c
fs/afs/internal.h
fs/afs/main.c
fs/afs/misc.c
fs/afs/proc.c
fs/afs/rotate.c [new file with mode: 0644]
fs/afs/rxrpc.c
fs/afs/security.c
fs/afs/server.c
fs/afs/server_list.c [new file with mode: 0644]
fs/afs/super.c
fs/afs/vlclient.c
fs/afs/vlocation.c [deleted file]
fs/afs/vnode.c [deleted file]
fs/afs/volume.c
fs/afs/write.c
fs/afs/xattr.c
fs/btrfs/extent-tree.c
fs/fscache/cookie.c
fs/fscache/internal.h
fs/fscache/main.c
fs/nfs/inode.c
fs/nfs/internal.h
fs/ocfs2/filecheck.c
include/linux/wait_bit.h
include/trace/events/afs.h
include/uapi/linux/magic.h
kernel/sched/wait_bit.c
mm/filemap.c

index 060da408923b26bea0017d86ada9369ae8cd91f4..ba99b5ac4fd80284adfbc2f6404a7d3da3fbb665 100644 (file)
@@ -91,8 +91,8 @@ Filesystems can be mounted anywhere by commands similar to the following:
        mount -t afs "#root.cell." /afs/cambridge
 
 Where the initial character is either a hash or a percent symbol depending on
-whether you definitely want a R/W volume (hash) or whether you'd prefer a R/O
-volume, but are willing to use a R/W volume instead (percent).
+whether you definitely want a R/W volume (percent) or whether you'd prefer a
+R/O volume, but are willing to use a R/W volume instead (hash).
 
 The name of the volume can be suffixes with ".backup" or ".readonly" to
 specify connection to only volumes of those types.
index 5669d3b8bd382760aa4f7cefa243a13195035928..5d19ed07e99df29938d584e27274acb84f10af59 100644 (file)
@@ -1233,18 +1233,6 @@ static int default_cu2_call(struct notifier_block *nfb, unsigned long action,
        return NOTIFY_OK;
 }
 
-static int wait_on_fp_mode_switch(atomic_t *p)
-{
-       /*
-        * The FP mode for this task is currently being switched. That may
-        * involve modifications to the format of this tasks FP context which
-        * make it unsafe to proceed with execution for the moment. Instead,
-        * schedule some other task.
-        */
-       schedule();
-       return 0;
-}
-
 static int enable_restore_fp_context(int msa)
 {
        int err, was_fpu_owner, prior_msa;
@@ -1254,7 +1242,7 @@ static int enable_restore_fp_context(int msa)
         * complete before proceeding.
         */
        wait_on_atomic_t(&current->mm->context.fp_mode_switching,
-                        wait_on_fp_mode_switch, TASK_KILLABLE);
+                        atomic_t_wait, TASK_KILLABLE);
 
        if (!used_math()) {
                /* First time FP context user. */
index d34e5096887a4d9d0036967edfa3c7f9db8aa5b6..053044201e315eb7ce618a59425288b54026c865 100644 (file)
@@ -263,12 +263,6 @@ static struct drm_dp_aux_dev *drm_dp_aux_dev_get_by_aux(struct drm_dp_aux *aux)
        return aux_dev;
 }
 
-static int auxdev_wait_atomic_t(atomic_t *p)
-{
-       schedule();
-       return 0;
-}
-
 void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux)
 {
        struct drm_dp_aux_dev *aux_dev;
@@ -283,7 +277,7 @@ void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux)
        mutex_unlock(&aux_idr_mutex);
 
        atomic_dec(&aux_dev->usecount);
-       wait_on_atomic_t(&aux_dev->usecount, auxdev_wait_atomic_t,
+       wait_on_atomic_t(&aux_dev->usecount, atomic_t_wait,
                         TASK_UNINTERRUPTIBLE);
 
        minor = aux_dev->index;
index 828904b7d46831f4dc6040cdf7085ffff515275b..54fc571b11021a079492199519b9c36f8ecfae99 100644 (file)
@@ -271,13 +271,7 @@ struct igt_wakeup {
        u32 seqno;
 };
 
-static int wait_atomic(atomic_t *p)
-{
-       schedule();
-       return 0;
-}
-
-static int wait_atomic_timeout(atomic_t *p)
+static int wait_atomic_timeout(atomic_t *p, unsigned int mode)
 {
        return schedule_timeout(10 * HZ) ? 0 : -ETIMEDOUT;
 }
@@ -348,7 +342,7 @@ static void igt_wake_all_sync(atomic_t *ready,
        atomic_set(ready, 0);
        wake_up_all(wq);
 
-       wait_on_atomic_t(set, wait_atomic, TASK_UNINTERRUPTIBLE);
+       wait_on_atomic_t(set, atomic_t_wait, TASK_UNINTERRUPTIBLE);
        atomic_set(ready, count);
        atomic_set(done, count);
 }
index ba29fd4d49847cc3d1956eefd224823d42cdb14e..1baf78d3c02d09e4085ffe54623b2b79db5657a2 100644 (file)
@@ -88,12 +88,6 @@ unlock:
        return ret;
 }
 
-static int core_deinit_wait_atomic_t(atomic_t *p)
-{
-       schedule();
-       return 0;
-}
-
 int hfi_core_deinit(struct venus_core *core, bool blocking)
 {
        int ret = 0, empty;
@@ -112,7 +106,7 @@ int hfi_core_deinit(struct venus_core *core, bool blocking)
 
        if (!empty) {
                mutex_unlock(&core->lock);
-               wait_on_atomic_t(&core->insts_count, core_deinit_wait_atomic_t,
+               wait_on_atomic_t(&core->insts_count, atomic_t_wait,
                                 TASK_UNINTERRUPTIBLE);
                mutex_lock(&core->lock);
        }
index 641148208e9078da9b1ba1a55badc2673bb38f4b..45b7fc405fa646a337bd622b9adbc934fbbb78ac 100644 (file)
@@ -7,6 +7,7 @@ afs-cache-$(CONFIG_AFS_FSCACHE) := cache.o
 
 kafs-objs := \
        $(afs-cache-y) \
+       addr_list.o \
        callback.o \
        cell.o \
        cmservice.o \
@@ -19,14 +20,14 @@ kafs-objs := \
        misc.o \
        mntpt.o \
        proc.o \
+       rotate.o \
        rxrpc.o \
        security.o \
        server.o \
+       server_list.o \
        super.o \
        netdevices.o \
        vlclient.o \
-       vlocation.o \
-       vnode.o \
        volume.o \
        write.o \
        xattr.o
diff --git a/fs/afs/addr_list.c b/fs/afs/addr_list.c
new file mode 100644 (file)
index 0000000..a537368
--- /dev/null
@@ -0,0 +1,381 @@
+/* Server address list management
+ *
+ * Copyright (C) 2017 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 Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/dns_resolver.h>
+#include <linux/inet.h>
+#include <keys/rxrpc-type.h>
+#include "internal.h"
+#include "afs_fs.h"
+
+//#define AFS_MAX_ADDRESSES
+//     ((unsigned int)((PAGE_SIZE - sizeof(struct afs_addr_list)) /
+//                     sizeof(struct sockaddr_rxrpc)))
+#define AFS_MAX_ADDRESSES ((unsigned int)(sizeof(unsigned long) * 8))
+
+/*
+ * Release an address list.
+ */
+void afs_put_addrlist(struct afs_addr_list *alist)
+{
+       if (alist && refcount_dec_and_test(&alist->usage))
+               call_rcu(&alist->rcu, (rcu_callback_t)kfree);
+}
+
+/*
+ * Allocate an address list.
+ */
+struct afs_addr_list *afs_alloc_addrlist(unsigned int nr,
+                                        unsigned short service,
+                                        unsigned short port)
+{
+       struct afs_addr_list *alist;
+       unsigned int i;
+
+       _enter("%u,%u,%u", nr, service, port);
+
+       alist = kzalloc(sizeof(*alist) + sizeof(alist->addrs[0]) * nr,
+                       GFP_KERNEL);
+       if (!alist)
+               return NULL;
+
+       refcount_set(&alist->usage, 1);
+
+       for (i = 0; i < nr; i++) {
+               struct sockaddr_rxrpc *srx = &alist->addrs[i];
+               srx->srx_family                 = AF_RXRPC;
+               srx->srx_service                = service;
+               srx->transport_type             = SOCK_DGRAM;
+               srx->transport_len              = sizeof(srx->transport.sin6);
+               srx->transport.sin6.sin6_family = AF_INET6;
+               srx->transport.sin6.sin6_port   = htons(port);
+       }
+
+       return alist;
+}
+
+/*
+ * Parse a text string consisting of delimited addresses.
+ */
+struct afs_addr_list *afs_parse_text_addrs(const char *text, size_t len,
+                                          char delim,
+                                          unsigned short service,
+                                          unsigned short port)
+{
+       struct afs_addr_list *alist;
+       const char *p, *end = text + len;
+       unsigned int nr = 0;
+
+       _enter("%*.*s,%c", (int)len, (int)len, text, delim);
+
+       if (!len)
+               return ERR_PTR(-EDESTADDRREQ);
+
+       if (delim == ':' && (memchr(text, ',', len) || !memchr(text, '.', len)))
+               delim = ',';
+
+       /* Count the addresses */
+       p = text;
+       do {
+               if (!*p)
+                       return ERR_PTR(-EINVAL);
+               if (*p == delim)
+                       continue;
+               nr++;
+               if (*p == '[') {
+                       p++;
+                       if (p == end)
+                               return ERR_PTR(-EINVAL);
+                       p = memchr(p, ']', end - p);
+                       if (!p)
+                               return ERR_PTR(-EINVAL);
+                       p++;
+                       if (p >= end)
+                               break;
+               }
+
+               p = memchr(p, delim, end - p);
+               if (!p)
+                       break;
+               p++;
+       } while (p < end);
+
+       _debug("%u/%u addresses", nr, AFS_MAX_ADDRESSES);
+       if (nr > AFS_MAX_ADDRESSES)
+               nr = AFS_MAX_ADDRESSES;
+
+       alist = afs_alloc_addrlist(nr, service, port);
+       if (!alist)
+               return ERR_PTR(-ENOMEM);
+
+       /* Extract the addresses */
+       p = text;
+       do {
+               struct sockaddr_rxrpc *srx = &alist->addrs[alist->nr_addrs];
+               char tdelim = delim;
+
+               if (*p == delim) {
+                       p++;
+                       continue;
+               }
+
+               if (*p == '[') {
+                       p++;
+                       tdelim = ']';
+               }
+
+               if (in4_pton(p, end - p,
+                            (u8 *)&srx->transport.sin6.sin6_addr.s6_addr32[3],
+                            tdelim, &p)) {
+                       srx->transport.sin6.sin6_addr.s6_addr32[0] = 0;
+                       srx->transport.sin6.sin6_addr.s6_addr32[1] = 0;
+                       srx->transport.sin6.sin6_addr.s6_addr32[2] = htonl(0xffff);
+               } else if (in6_pton(p, end - p,
+                                   srx->transport.sin6.sin6_addr.s6_addr,
+                                   tdelim, &p)) {
+                       /* Nothing to do */
+               } else {
+                       goto bad_address;
+               }
+
+               if (tdelim == ']') {
+                       if (p == end || *p != ']')
+                               goto bad_address;
+                       p++;
+               }
+
+               if (p < end) {
+                       if (*p == '+') {
+                               /* Port number specification "+1234" */
+                               unsigned int xport = 0;
+                               p++;
+                               if (p >= end || !isdigit(*p))
+                                       goto bad_address;
+                               do {
+                                       xport *= 10;
+                                       xport += *p - '0';
+                                       if (xport > 65535)
+                                               goto bad_address;
+                                       p++;
+                               } while (p < end && isdigit(*p));
+                               srx->transport.sin6.sin6_port = htons(xport);
+                       } else if (*p == delim) {
+                               p++;
+                       } else {
+                               goto bad_address;
+                       }
+               }
+
+               alist->nr_addrs++;
+       } while (p < end && alist->nr_addrs < AFS_MAX_ADDRESSES);
+
+       _leave(" = [nr %u]", alist->nr_addrs);
+       return alist;
+
+bad_address:
+       kfree(alist);
+       return ERR_PTR(-EINVAL);
+}
+
+/*
+ * Compare old and new address lists to see if there's been any change.
+ * - How to do this in better than O(Nlog(N)) time?
+ *   - We don't really want to sort the address list, but would rather take the
+ *     list as we got it so as not to undo record rotation by the DNS server.
+ */
+#if 0
+static int afs_cmp_addr_list(const struct afs_addr_list *a1,
+                            const struct afs_addr_list *a2)
+{
+}
+#endif
+
+/*
+ * Perform a DNS query for VL servers and build a up an address list.
+ */
+struct afs_addr_list *afs_dns_query(struct afs_cell *cell, time64_t *_expiry)
+{
+       struct afs_addr_list *alist;
+       char *vllist = NULL;
+       int ret;
+
+       _enter("%s", cell->name);
+
+       ret = dns_query("afsdb", cell->name, cell->name_len,
+                       "ipv4", &vllist, _expiry);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       alist = afs_parse_text_addrs(vllist, strlen(vllist), ',',
+                                    VL_SERVICE, AFS_VL_PORT);
+       if (IS_ERR(alist)) {
+               kfree(vllist);
+               if (alist != ERR_PTR(-ENOMEM))
+                       pr_err("Failed to parse DNS data\n");
+               return alist;
+       }
+
+       kfree(vllist);
+       return alist;
+}
+
+/*
+ * Merge an IPv4 entry into a fileserver address list.
+ */
+void afs_merge_fs_addr4(struct afs_addr_list *alist, __be32 xdr, u16 port)
+{
+       struct sockaddr_in6 *a;
+       __be16 xport = htons(port);
+       int i;
+
+       for (i = 0; i < alist->nr_ipv4; i++) {
+               a = &alist->addrs[i].transport.sin6;
+               if (xdr == a->sin6_addr.s6_addr32[3] &&
+                   xport == a->sin6_port)
+                       return;
+               if (xdr == a->sin6_addr.s6_addr32[3] &&
+                   xport < a->sin6_port)
+                       break;
+               if (xdr < a->sin6_addr.s6_addr32[3])
+                       break;
+       }
+
+       if (i < alist->nr_addrs)
+               memmove(alist->addrs + i + 1,
+                       alist->addrs + i,
+                       sizeof(alist->addrs[0]) * (alist->nr_addrs - i));
+
+       a = &alist->addrs[i].transport.sin6;
+       a->sin6_port              = xport;
+       a->sin6_addr.s6_addr32[0] = 0;
+       a->sin6_addr.s6_addr32[1] = 0;
+       a->sin6_addr.s6_addr32[2] = htonl(0xffff);
+       a->sin6_addr.s6_addr32[3] = xdr;
+       alist->nr_ipv4++;
+       alist->nr_addrs++;
+}
+
+/*
+ * Merge an IPv6 entry into a fileserver address list.
+ */
+void afs_merge_fs_addr6(struct afs_addr_list *alist, __be32 *xdr, u16 port)
+{
+       struct sockaddr_in6 *a;
+       __be16 xport = htons(port);
+       int i, diff;
+
+       for (i = alist->nr_ipv4; i < alist->nr_addrs; i++) {
+               a = &alist->addrs[i].transport.sin6;
+               diff = memcmp(xdr, &a->sin6_addr, 16);
+               if (diff == 0 &&
+                   xport == a->sin6_port)
+                       return;
+               if (diff == 0 &&
+                   xport < a->sin6_port)
+                       break;
+               if (diff < 0)
+                       break;
+       }
+
+       if (i < alist->nr_addrs)
+               memmove(alist->addrs + i + 1,
+                       alist->addrs + i,
+                       sizeof(alist->addrs[0]) * (alist->nr_addrs - i));
+
+       a = &alist->addrs[i].transport.sin6;
+       a->sin6_port              = xport;
+       a->sin6_addr.s6_addr32[0] = xdr[0];
+       a->sin6_addr.s6_addr32[1] = xdr[1];
+       a->sin6_addr.s6_addr32[2] = xdr[2];
+       a->sin6_addr.s6_addr32[3] = xdr[3];
+       alist->nr_addrs++;
+}
+
+/*
+ * Get an address to try.
+ */
+bool afs_iterate_addresses(struct afs_addr_cursor *ac)
+{
+       _enter("%hu+%hd", ac->start, (short)ac->index);
+
+       if (!ac->alist)
+               return false;
+
+       if (ac->begun) {
+               ac->index++;
+               if (ac->index == ac->alist->nr_addrs)
+                       ac->index = 0;
+
+               if (ac->index == ac->start) {
+                       ac->error = -EDESTADDRREQ;
+                       return false;
+               }
+       }
+
+       ac->begun = true;
+       ac->responded = false;
+       ac->addr = &ac->alist->addrs[ac->index];
+       return true;
+}
+
+/*
+ * Release an address list cursor.
+ */
+int afs_end_cursor(struct afs_addr_cursor *ac)
+{
+       if (ac->responded && ac->index != ac->start)
+               WRITE_ONCE(ac->alist->index, ac->index);
+
+       afs_put_addrlist(ac->alist);
+       ac->alist = NULL;
+       return ac->error;
+}
+
+/*
+ * Set the address cursor for iterating over VL servers.
+ */
+int afs_set_vl_cursor(struct afs_addr_cursor *ac, struct afs_cell *cell)
+{
+       struct afs_addr_list *alist;
+       int ret;
+
+       if (!rcu_access_pointer(cell->vl_addrs)) {
+               ret = wait_on_bit(&cell->flags, AFS_CELL_FL_NO_LOOKUP_YET,
+                                 TASK_INTERRUPTIBLE);
+               if (ret < 0)
+                       return ret;
+
+               if (!rcu_access_pointer(cell->vl_addrs) &&
+                   ktime_get_real_seconds() < cell->dns_expiry)
+                       return cell->error;
+       }
+
+       read_lock(&cell->vl_addrs_lock);
+       alist = rcu_dereference_protected(cell->vl_addrs,
+                                         lockdep_is_held(&cell->vl_addrs_lock));
+       if (alist->nr_addrs > 0)
+               afs_get_addrlist(alist);
+       else
+               alist = NULL;
+       read_unlock(&cell->vl_addrs_lock);
+
+       if (!alist)
+               return -EDESTADDRREQ;
+
+       ac->alist = alist;
+       ac->addr = NULL;
+       ac->start = READ_ONCE(alist->index);
+       ac->index = ac->start;
+       ac->error = 0;
+       ac->begun = false;
+       return 0;
+}
index 3c462ff6db639a22ce6393ab65c2f84f1d1fe038..b94d0edc2b78629bf4475c689d9634a57d681e56 100644 (file)
 
 #include <linux/in.h>
 
-#define AFS_MAXCELLNAME        64              /* maximum length of a cell name */
-#define AFS_MAXVOLNAME 64              /* maximum length of a volume name */
-#define AFSNAMEMAX     256             /* maximum length of a filename plus NUL */
-#define AFSPATHMAX     1024            /* maximum length of a pathname plus NUL */
-#define AFSOPAQUEMAX   1024            /* maximum length of an opaque field */
+#define AFS_MAXCELLNAME                64      /* Maximum length of a cell name */
+#define AFS_MAXVOLNAME         64      /* Maximum length of a volume name */
+#define AFS_MAXNSERVERS                8       /* Maximum servers in a basic volume record */
+#define AFS_NMAXNSERVERS       13      /* Maximum servers in a N/U-class volume record */
+#define AFS_MAXTYPES           3       /* Maximum number of volume types */
+#define AFSNAMEMAX             256     /* Maximum length of a filename plus NUL */
+#define AFSPATHMAX             1024    /* Maximum length of a pathname plus NUL */
+#define AFSOPAQUEMAX           1024    /* Maximum length of an opaque field */
 
 typedef unsigned                       afs_volid_t;
 typedef unsigned                       afs_vnodeid_t;
@@ -72,6 +75,15 @@ struct afs_callback {
 
 #define AFSCBMAX 50    /* maximum callbacks transferred per bulk op */
 
+struct afs_uuid {
+       __be32          time_low;                       /* low part of timestamp */
+       __be16          time_mid;                       /* mid part of timestamp */
+       __be16          time_hi_and_version;            /* high part of timestamp and version  */
+       __s8            clock_seq_hi_and_reserved;      /* clock seq hi and variant */
+       __s8            clock_seq_low;                  /* clock seq low */
+       __s8            node[6];                        /* spatially unique node ID (MAC addr) */
+};
+
 /*
  * AFS volume information
  */
@@ -124,7 +136,6 @@ struct afs_file_status {
        afs_access_t            caller_access;  /* access rights for authenticated caller */
        afs_access_t            anon_access;    /* access rights for unauthenticated caller */
        umode_t                 mode;           /* UNIX mode */
-       struct afs_fid          parent;         /* parent dir ID for non-dirs only */
        time_t                  mtime_client;   /* last time client changed data */
        time_t                  mtime_server;   /* last time server changed data */
        s32                     lock_count;     /* file lock count (0=UNLK -1=WRLCK +ve=#RDLCK */
@@ -167,4 +178,16 @@ struct afs_volume_status {
 
 #define AFS_BLOCK_SIZE 1024
 
+/*
+ * XDR encoding of UUID in AFS.
+ */
+struct afs_uuid__xdr {
+       __be32          time_low;
+       __be32          time_mid;
+       __be32          time_hi_and_version;
+       __be32          clock_seq_hi_and_reserved;
+       __be32          clock_seq_low;
+       __be32          node[6];
+};
+
 #endif /* AFS_H */
index eb647323d8f03a3f6634d4f83d0aa648f9093201..d47b6d01e4c0222b9407f0928f6f197ae7e594f9 100644 (file)
@@ -37,9 +37,12 @@ enum AFS_FS_Operations {
        FSLOOKUP                = 161,  /* AFS lookup file in directory */
        FSFETCHDATA64           = 65537, /* AFS Fetch file data */
        FSSTOREDATA64           = 65538, /* AFS Store file data */
+       FSGIVEUPALLCALLBACKS    = 65539, /* AFS Give up all outstanding callbacks on a server */
+       FSGETCAPABILITIES       = 65540, /* Probe and get the capabilities of a fileserver */
 };
 
 enum AFS_FS_Errors {
+       VRESTARTING     = -100, /* Server is restarting */
        VSALVAGE        = 101,  /* volume needs salvaging */
        VNOVNODE        = 102,  /* no such file/dir (vnode) */
        VNOVOL          = 103,  /* no such volume or volume unavailable */
@@ -51,6 +54,9 @@ enum AFS_FS_Errors {
        VOVERQUOTA      = 109,  /* volume's maximum quota exceeded */
        VBUSY           = 110,  /* volume is temporarily unavailable */
        VMOVED          = 111,  /* volume moved to new server - ask this FS where */
+       VIO             = 112,  /* I/O error in volume */
+       VSALVAGING      = 113,  /* Volume is being salvaged */
+       VRESTRICTED     = 120,  /* Volume is restricted from using  */
 };
 
 #endif /* AFS_FS_H */
index 800f607ffaf541b799291d76acd18474bbdbd194..e3c4688f573bceae235c965fb1b7c3597dcd8b85 100644 (file)
 
 #define AFS_VL_PORT            7003    /* volume location service port */
 #define VL_SERVICE             52      /* RxRPC service ID for the Volume Location service */
+#define YFS_VL_SERVICE         2503    /* Service ID for AuriStor upgraded VL service */
 
 enum AFSVL_Operations {
-       VLGETENTRYBYID          = 503,  /* AFS Get Cache Entry By ID operation ID */
-       VLGETENTRYBYNAME        = 504,  /* AFS Get Cache Entry By Name operation ID */
-       VLPROBE                 = 514,  /* AFS Probe Volume Location Service operation ID */
+       VLGETENTRYBYID          = 503,  /* AFS Get VLDB entry by ID */
+       VLGETENTRYBYNAME        = 504,  /* AFS Get VLDB entry by name */
+       VLPROBE                 = 514,  /* AFS probe VL service */
+       VLGETENTRYBYIDU         = 526,  /* AFS Get VLDB entry by ID (UUID-variant) */
+       VLGETENTRYBYNAMEU       = 527,  /* AFS Get VLDB entry by name (UUID-variant) */
+       VLGETADDRSU             = 533,  /* AFS Get addrs for fileserver */
+       YVLGETENDPOINTS         = 64002, /* YFS Get endpoints for file/volume server */
+       VLGETCAPABILITIES       = 65537, /* AFS Get server capabilities */
 };
 
 enum AFSVL_Errors {
@@ -54,6 +60,19 @@ enum AFSVL_Errors {
        AFSVL_NOMEM             = 363547,       /* malloc/realloc failed to alloc enough memory */
 };
 
+enum {
+       YFS_SERVER_INDEX        = 0,
+       YFS_SERVER_UUID         = 1,
+       YFS_SERVER_ENDPOINT     = 2,
+};
+
+enum {
+       YFS_ENDPOINT_IPV4       = 0,
+       YFS_ENDPOINT_IPV6       = 1,
+};
+
+#define YFS_MAXENDPOINTS       16
+
 /*
  * maps to "struct vldbentry" in vvl-spec.pdf
  */
@@ -74,11 +93,57 @@ struct afs_vldbentry {
                struct in_addr  addr;           /* server address */
                unsigned        partition;      /* partition ID on this server */
                unsigned        flags;          /* server specific flags */
-#define AFS_VLSF_NEWREPSITE    0x0001  /* unused */
+#define AFS_VLSF_NEWREPSITE    0x0001  /* Ignore all 'non-new' servers */
 #define AFS_VLSF_ROVOL         0x0002  /* this server holds a R/O instance of the volume */
 #define AFS_VLSF_RWVOL         0x0004  /* this server holds a R/W instance of the volume */
 #define AFS_VLSF_BACKVOL       0x0008  /* this server holds a backup instance of the volume */
+#define AFS_VLSF_UUID          0x0010  /* This server is referred to by its UUID */
+#define AFS_VLSF_DONTUSE       0x0020  /* This server ref should be ignored */
        } servers[8];
 };
 
+#define AFS_VLDB_MAXNAMELEN 65
+
+
+struct afs_ListAddrByAttributes__xdr {
+       __be32                  Mask;
+#define AFS_VLADDR_IPADDR      0x1     /* Match by ->ipaddr */
+#define AFS_VLADDR_INDEX       0x2     /* Match by ->index */
+#define AFS_VLADDR_UUID                0x4     /* Match by ->uuid */
+       __be32                  ipaddr;
+       __be32                  index;
+       __be32                  spare;
+       struct afs_uuid__xdr    uuid;
+};
+
+struct afs_uvldbentry__xdr {
+       __be32                  name[AFS_VLDB_MAXNAMELEN];
+       __be32                  nServers;
+       struct afs_uuid__xdr    serverNumber[AFS_NMAXNSERVERS];
+       __be32                  serverUnique[AFS_NMAXNSERVERS];
+       __be32                  serverPartition[AFS_NMAXNSERVERS];
+       __be32                  serverFlags[AFS_NMAXNSERVERS];
+       __be32                  volumeId[AFS_MAXTYPES];
+       __be32                  cloneId;
+       __be32                  flags;
+       __be32                  spares1;
+       __be32                  spares2;
+       __be32                  spares3;
+       __be32                  spares4;
+       __be32                  spares5;
+       __be32                  spares6;
+       __be32                  spares7;
+       __be32                  spares8;
+       __be32                  spares9;
+};
+
+struct afs_address_list {
+       refcount_t              usage;
+       unsigned int            version;
+       unsigned int            nr_addrs;
+       struct sockaddr_rxrpc   addrs[];
+};
+
+extern void afs_put_address_list(struct afs_address_list *alist);
+
 #endif /* AFS_VL_H */
index 1fe855191261fce57a85be27f59690743957ecd6..f62ff71d28c9914e3c681db96969163191132c48 100644 (file)
 
 static uint16_t afs_cell_cache_get_key(const void *cookie_netfs_data,
                                       void *buffer, uint16_t buflen);
-static uint16_t afs_cell_cache_get_aux(const void *cookie_netfs_data,
-                                      void *buffer, uint16_t buflen);
-static enum fscache_checkaux afs_cell_cache_check_aux(void *cookie_netfs_data,
-                                                     const void *buffer,
-                                                     uint16_t buflen);
-
-static uint16_t afs_vlocation_cache_get_key(const void *cookie_netfs_data,
-                                           void *buffer, uint16_t buflen);
-static uint16_t afs_vlocation_cache_get_aux(const void *cookie_netfs_data,
-                                           void *buffer, uint16_t buflen);
-static enum fscache_checkaux afs_vlocation_cache_check_aux(
-       void *cookie_netfs_data, const void *buffer, uint16_t buflen);
-
 static uint16_t afs_volume_cache_get_key(const void *cookie_netfs_data,
                                         void *buffer, uint16_t buflen);
 
@@ -42,23 +29,13 @@ static enum fscache_checkaux afs_vnode_cache_check_aux(void *cookie_netfs_data,
 
 struct fscache_netfs afs_cache_netfs = {
        .name                   = "afs",
-       .version                = 0,
+       .version                = 1,
 };
 
 struct fscache_cookie_def afs_cell_cache_index_def = {
        .name           = "AFS.cell",
        .type           = FSCACHE_COOKIE_TYPE_INDEX,
        .get_key        = afs_cell_cache_get_key,
-       .get_aux        = afs_cell_cache_get_aux,
-       .check_aux      = afs_cell_cache_check_aux,
-};
-
-struct fscache_cookie_def afs_vlocation_cache_index_def = {
-       .name                   = "AFS.vldb",
-       .type                   = FSCACHE_COOKIE_TYPE_INDEX,
-       .get_key                = afs_vlocation_cache_get_key,
-       .get_aux                = afs_vlocation_cache_get_aux,
-       .check_aux              = afs_vlocation_cache_check_aux,
 };
 
 struct fscache_cookie_def afs_volume_cache_index_def = {
@@ -95,150 +72,26 @@ static uint16_t afs_cell_cache_get_key(const void *cookie_netfs_data,
        return klen;
 }
 
-/*
- * provide new auxiliary cache data
- */
-static uint16_t afs_cell_cache_get_aux(const void *cookie_netfs_data,
-                                      void *buffer, uint16_t bufmax)
-{
-       const struct afs_cell *cell = cookie_netfs_data;
-       uint16_t dlen;
-
-       _enter("%p,%p,%u", cell, buffer, bufmax);
-
-       dlen = cell->vl_naddrs * sizeof(cell->vl_addrs[0]);
-       dlen = min(dlen, bufmax);
-       dlen &= ~(sizeof(cell->vl_addrs[0]) - 1);
-
-       memcpy(buffer, cell->vl_addrs, dlen);
-       return dlen;
-}
-
-/*
- * check that the auxiliary data indicates that the entry is still valid
- */
-static enum fscache_checkaux afs_cell_cache_check_aux(void *cookie_netfs_data,
-                                                     const void *buffer,
-                                                     uint16_t buflen)
-{
-       _leave(" = OKAY");
-       return FSCACHE_CHECKAUX_OKAY;
-}
-
-/*****************************************************************************/
-/*
- * set the key for the index entry
- */
-static uint16_t afs_vlocation_cache_get_key(const void *cookie_netfs_data,
-                                           void *buffer, uint16_t bufmax)
-{
-       const struct afs_vlocation *vlocation = cookie_netfs_data;
-       uint16_t klen;
-
-       _enter("{%s},%p,%u", vlocation->vldb.name, buffer, bufmax);
-
-       klen = strnlen(vlocation->vldb.name, sizeof(vlocation->vldb.name));
-       if (klen > bufmax)
-               return 0;
-
-       memcpy(buffer, vlocation->vldb.name, klen);
-
-       _leave(" = %u", klen);
-       return klen;
-}
-
-/*
- * provide new auxiliary cache data
- */
-static uint16_t afs_vlocation_cache_get_aux(const void *cookie_netfs_data,
-                                           void *buffer, uint16_t bufmax)
-{
-       const struct afs_vlocation *vlocation = cookie_netfs_data;
-       uint16_t dlen;
-
-       _enter("{%s},%p,%u", vlocation->vldb.name, buffer, bufmax);
-
-       dlen = sizeof(struct afs_cache_vlocation);
-       dlen -= offsetof(struct afs_cache_vlocation, nservers);
-       if (dlen > bufmax)
-               return 0;
-
-       memcpy(buffer, (uint8_t *)&vlocation->vldb.nservers, dlen);
-
-       _leave(" = %u", dlen);
-       return dlen;
-}
-
-/*
- * check that the auxiliary data indicates that the entry is still valid
- */
-static
-enum fscache_checkaux afs_vlocation_cache_check_aux(void *cookie_netfs_data,
-                                                   const void *buffer,
-                                                   uint16_t buflen)
-{
-       const struct afs_cache_vlocation *cvldb;
-       struct afs_vlocation *vlocation = cookie_netfs_data;
-       uint16_t dlen;
-
-       _enter("{%s},%p,%u", vlocation->vldb.name, buffer, buflen);
-
-       /* check the size of the data is what we're expecting */
-       dlen = sizeof(struct afs_cache_vlocation);
-       dlen -= offsetof(struct afs_cache_vlocation, nservers);
-       if (dlen != buflen)
-               return FSCACHE_CHECKAUX_OBSOLETE;
-
-       cvldb = container_of(buffer, struct afs_cache_vlocation, nservers);
-
-       /* if what's on disk is more valid than what's in memory, then use the
-        * VL record from the cache */
-       if (!vlocation->valid || vlocation->vldb.rtime == cvldb->rtime) {
-               memcpy((uint8_t *)&vlocation->vldb.nservers, buffer, dlen);
-               vlocation->valid = 1;
-               _leave(" = SUCCESS [c->m]");
-               return FSCACHE_CHECKAUX_OKAY;
-       }
-
-       /* need to update the cache if the cached info differs */
-       if (memcmp(&vlocation->vldb, buffer, dlen) != 0) {
-               /* delete if the volume IDs for this name differ */
-               if (memcmp(&vlocation->vldb.vid, &cvldb->vid,
-                          sizeof(cvldb->vid)) != 0
-                   ) {
-                       _leave(" = OBSOLETE");
-                       return FSCACHE_CHECKAUX_OBSOLETE;
-               }
-
-               _leave(" = UPDATE");
-               return FSCACHE_CHECKAUX_NEEDS_UPDATE;
-       }
-
-       _leave(" = OKAY");
-       return FSCACHE_CHECKAUX_OKAY;
-}
-
 /*****************************************************************************/
 /*
  * set the key for the volume index entry
  */
 static uint16_t afs_volume_cache_get_key(const void *cookie_netfs_data,
-                                       void *buffer, uint16_t bufmax)
+                                        void *buffer, uint16_t bufmax)
 {
        const struct afs_volume *volume = cookie_netfs_data;
-       uint16_t klen;
+       struct {
+               u64 volid;
+       } __packed key;
 
        _enter("{%u},%p,%u", volume->type, buffer, bufmax);
 
-       klen = sizeof(volume->type);
-       if (klen > bufmax)
+       if (bufmax < sizeof(key))
                return 0;
 
-       memcpy(buffer, &volume->type, sizeof(volume->type));
-
-       _leave(" = %u", klen);
-       return klen;
-
+       key.volid = volume->vid;
+       memcpy(buffer, &key, sizeof(key));
+       return sizeof(key);
 }
 
 /*****************************************************************************/
@@ -249,20 +102,25 @@ static uint16_t afs_vnode_cache_get_key(const void *cookie_netfs_data,
                                        void *buffer, uint16_t bufmax)
 {
        const struct afs_vnode *vnode = cookie_netfs_data;
-       uint16_t klen;
+       struct {
+               u32 vnode_id[3];
+       } __packed key;
 
        _enter("{%x,%x,%llx},%p,%u",
               vnode->fid.vnode, vnode->fid.unique, vnode->status.data_version,
               buffer, bufmax);
 
-       klen = sizeof(vnode->fid.vnode);
-       if (klen > bufmax)
-               return 0;
+       /* Allow for a 96-bit key */
+       memset(&key, 0, sizeof(key));
+       key.vnode_id[0] = vnode->fid.vnode;
+       key.vnode_id[1] = 0;
+       key.vnode_id[2] = 0;
 
-       memcpy(buffer, &vnode->fid.vnode, sizeof(vnode->fid.vnode));
+       if (sizeof(key) > bufmax)
+               return 0;
 
-       _leave(" = %u", klen);
-       return klen;
+       memcpy(buffer, &key, sizeof(key));
+       return sizeof(key);
 }
 
 /*
@@ -280,6 +138,11 @@ static void afs_vnode_cache_get_attr(const void *cookie_netfs_data,
        *size = vnode->status.size;
 }
 
+struct afs_vnode_cache_aux {
+       u64 data_version;
+       u32 fid_unique;
+} __packed;
+
 /*
  * provide new auxiliary cache data
  */
@@ -287,23 +150,21 @@ static uint16_t afs_vnode_cache_get_aux(const void *cookie_netfs_data,
                                        void *buffer, uint16_t bufmax)
 {
        const struct afs_vnode *vnode = cookie_netfs_data;
-       uint16_t dlen;
+       struct afs_vnode_cache_aux aux;
 
        _enter("{%x,%x,%Lx},%p,%u",
               vnode->fid.vnode, vnode->fid.unique, vnode->status.data_version,
               buffer, bufmax);
 
-       dlen = sizeof(vnode->fid.unique) + sizeof(vnode->status.data_version);
-       if (dlen > bufmax)
-               return 0;
+       memset(&aux, 0, sizeof(aux));
+       aux.data_version = vnode->status.data_version;
+       aux.fid_unique = vnode->fid.unique;
 
-       memcpy(buffer, &vnode->fid.unique, sizeof(vnode->fid.unique));
-       buffer += sizeof(vnode->fid.unique);
-       memcpy(buffer, &vnode->status.data_version,
-              sizeof(vnode->status.data_version));
+       if (bufmax < sizeof(aux))
+               return 0;
 
-       _leave(" = %u", dlen);
-       return dlen;
+       memcpy(buffer, &aux, sizeof(aux));
+       return sizeof(aux);
 }
 
 /*
@@ -314,43 +175,29 @@ static enum fscache_checkaux afs_vnode_cache_check_aux(void *cookie_netfs_data,
                                                       uint16_t buflen)
 {
        struct afs_vnode *vnode = cookie_netfs_data;
-       uint16_t dlen;
+       struct afs_vnode_cache_aux aux;
 
        _enter("{%x,%x,%llx},%p,%u",
               vnode->fid.vnode, vnode->fid.unique, vnode->status.data_version,
               buffer, buflen);
 
+       memcpy(&aux, buffer, sizeof(aux));
+
        /* check the size of the data is what we're expecting */
-       dlen = sizeof(vnode->fid.unique) + sizeof(vnode->status.data_version);
-       if (dlen != buflen) {
-               _leave(" = OBSOLETE [len %hx != %hx]", dlen, buflen);
+       if (buflen != sizeof(aux)) {
+               _leave(" = OBSOLETE [len %hx != %zx]", buflen, sizeof(aux));
                return FSCACHE_CHECKAUX_OBSOLETE;
        }
 
-       if (memcmp(buffer,
-                  &vnode->fid.unique,
-                  sizeof(vnode->fid.unique)
-                  ) != 0) {
-               unsigned unique;
-
-               memcpy(&unique, buffer, sizeof(unique));
-
+       if (vnode->fid.unique != aux.fid_unique) {
                _leave(" = OBSOLETE [uniq %x != %x]",
-                      unique, vnode->fid.unique);
+                      aux.fid_unique, vnode->fid.unique);
                return FSCACHE_CHECKAUX_OBSOLETE;
        }
 
-       if (memcmp(buffer + sizeof(vnode->fid.unique),
-                  &vnode->status.data_version,
-                  sizeof(vnode->status.data_version)
-                  ) != 0) {
-               afs_dataversion_t version;
-
-               memcpy(&version, buffer + sizeof(vnode->fid.unique),
-                      sizeof(version));
-
+       if (vnode->status.data_version != aux.data_version) {
                _leave(" = OBSOLETE [vers %llx != %llx]",
-                      version, vnode->status.data_version);
+                      aux.data_version, vnode->status.data_version);
                return FSCACHE_CHECKAUX_OBSOLETE;
        }
 
index 25d404d22caebcfd6b6b60d6287e36258f1185eb..f4291b57605437de51faef59727ff99d565ac19c 100644 (file)
 #include <linux/sched.h>
 #include "internal.h"
 
-#if 0
-unsigned afs_vnode_update_timeout = 10;
-#endif  /*  0  */
-
-#define afs_breakring_space(server) \
-       CIRC_SPACE((server)->cb_break_head, (server)->cb_break_tail,    \
-                  ARRAY_SIZE((server)->cb_break))
-
-//static void afs_callback_updater(struct work_struct *);
-
-static struct workqueue_struct *afs_callback_update_worker;
-
 /*
- * allow the fileserver to request callback state (re-)initialisation
+ * Set up an interest-in-callbacks record for a volume on a server and
+ * register it with the server.
+ * - Called with volume->server_sem held.
  */
-void afs_init_callback_state(struct afs_server *server)
+int afs_register_server_cb_interest(struct afs_vnode *vnode,
+                                   struct afs_server_entry *entry)
 {
-       struct afs_vnode *vnode;
-
-       _enter("{%p}", server);
+       struct afs_cb_interest *cbi = entry->cb_interest, *vcbi, *new, *x;
+       struct afs_server *server = entry->server;
+
+again:
+       vcbi = vnode->cb_interest;
+       if (vcbi) {
+               if (vcbi == cbi)
+                       return 0;
+
+               if (cbi && vcbi->server == cbi->server) {
+                       write_seqlock(&vnode->cb_lock);
+                       vnode->cb_interest = afs_get_cb_interest(cbi);
+                       write_sequnlock(&vnode->cb_lock);
+                       afs_put_cb_interest(afs_v2net(vnode), cbi);
+                       return 0;
+               }
 
-       spin_lock(&server->cb_lock);
+               if (!cbi && vcbi->server == server) {
+                       afs_get_cb_interest(vcbi);
+                       x = cmpxchg(&entry->cb_interest, cbi, vcbi);
+                       if (x != cbi) {
+                               cbi = x;
+                               afs_put_cb_interest(afs_v2net(vnode), vcbi);
+                               goto again;
+                       }
+                       return 0;
+               }
+       }
 
-       /* kill all the promises on record from this server */
-       while (!RB_EMPTY_ROOT(&server->cb_promises)) {
-               vnode = rb_entry(server->cb_promises.rb_node,
-                                struct afs_vnode, cb_promise);
-               _debug("UNPROMISE { vid=%x:%u uq=%u}",
-                      vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);
-               rb_erase(&vnode->cb_promise, &server->cb_promises);
-               vnode->cb_promised = false;
+       if (!cbi) {
+               new = kzalloc(sizeof(struct afs_cb_interest), GFP_KERNEL);
+               if (!new)
+                       return -ENOMEM;
+
+               refcount_set(&new->usage, 1);
+               new->sb = vnode->vfs_inode.i_sb;
+               new->vid = vnode->volume->vid;
+               new->server = afs_get_server(server);
+               INIT_LIST_HEAD(&new->cb_link);
+
+               write_lock(&server->cb_break_lock);
+               list_add_tail(&new->cb_link, &server->cb_interests);
+               write_unlock(&server->cb_break_lock);
+
+               x = cmpxchg(&entry->cb_interest, cbi, new);
+               if (x == cbi) {
+                       cbi = new;
+               } else {
+                       cbi = x;
+                       afs_put_cb_interest(afs_v2net(vnode), new);
+               }
        }
 
-       spin_unlock(&server->cb_lock);
-       _leave("");
+       ASSERT(cbi);
+
+       /* Change the server the vnode is using.  This entails scrubbing any
+        * interest the vnode had in the previous server it was using.
+        */
+       write_seqlock(&vnode->cb_lock);
+
+       vnode->cb_interest = afs_get_cb_interest(cbi);
+       vnode->cb_s_break = cbi->server->cb_s_break;
+       clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
+
+       write_sequnlock(&vnode->cb_lock);
+       return 0;
 }
 
 /*
- * handle the data invalidation side of a callback being broken
+ * Set a vnode's interest on a server.
  */
-void afs_broken_callback_work(struct work_struct *work)
+void afs_set_cb_interest(struct afs_vnode *vnode, struct afs_cb_interest *cbi)
 {
-       struct afs_vnode *vnode =
-               container_of(work, struct afs_vnode, cb_broken_work);
+       struct afs_cb_interest *old_cbi = NULL;
 
-       _enter("");
-
-       if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
+       if (vnode->cb_interest == cbi)
                return;
 
-       /* we're only interested in dealing with a broken callback on *this*
-        * vnode and only if no-one else has dealt with it yet */
-       if (!mutex_trylock(&vnode->validate_lock))
-               return; /* someone else is dealing with it */
-
-       if (test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) {
-               if (S_ISDIR(vnode->vfs_inode.i_mode))
-                       afs_clear_permits(vnode);
-
-               if (afs_vnode_fetch_status(vnode, NULL, NULL) < 0)
-                       goto out;
-
-               if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
-                       goto out;
-
-               /* if the vnode's data version number changed then its contents
-                * are different */
-               if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags))
-                       afs_zap_data(vnode);
+       write_seqlock(&vnode->cb_lock);
+       if (vnode->cb_interest != cbi) {
+               afs_get_cb_interest(cbi);
+               old_cbi = vnode->cb_interest;
+               vnode->cb_interest = cbi;
        }
+       write_sequnlock(&vnode->cb_lock);
+       afs_put_cb_interest(afs_v2net(vnode), cbi);
+}
 
-out:
-       mutex_unlock(&vnode->validate_lock);
-
-       /* avoid the potential race whereby the mutex_trylock() in this
-        * function happens again between the clear_bit() and the
-        * mutex_unlock() */
-       if (test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) {
-               _debug("requeue");
-               queue_work(afs_callback_update_worker, &vnode->cb_broken_work);
+/*
+ * Remove an interest on a server.
+ */
+void afs_put_cb_interest(struct afs_net *net, struct afs_cb_interest *cbi)
+{
+       if (cbi && refcount_dec_and_test(&cbi->usage)) {
+               if (!list_empty(&cbi->cb_link)) {
+                       write_lock(&cbi->server->cb_break_lock);
+                       list_del_init(&cbi->cb_link);
+                       write_unlock(&cbi->server->cb_break_lock);
+                       afs_put_server(net, cbi->server);
+               }
+               kfree(cbi);
        }
-       _leave("");
+}
+
+/*
+ * allow the fileserver to request callback state (re-)initialisation
+ */
+void afs_init_callback_state(struct afs_server *server)
+{
+       if (!test_and_clear_bit(AFS_SERVER_FL_NEW, &server->flags))
+               server->cb_s_break++;
 }
 
 /*
  * actually break a callback
  */
-static void afs_break_callback(struct afs_server *server,
-                              struct afs_vnode *vnode)
+void afs_break_callback(struct afs_vnode *vnode)
 {
        _enter("");
 
-       set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
+       write_seqlock(&vnode->cb_lock);
+
+       if (test_and_clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
+               vnode->cb_break++;
+               afs_clear_permits(vnode);
 
-       if (vnode->cb_promised) {
                spin_lock(&vnode->lock);
 
                _debug("break callback");
 
-               spin_lock(&server->cb_lock);
-               if (vnode->cb_promised) {
-                       rb_erase(&vnode->cb_promise, &server->cb_promises);
-                       vnode->cb_promised = false;
-               }
-               spin_unlock(&server->cb_lock);
-
-               queue_work(afs_callback_update_worker, &vnode->cb_broken_work);
                if (list_empty(&vnode->granted_locks) &&
                    !list_empty(&vnode->pending_locks))
                        afs_lock_may_be_available(vnode);
                spin_unlock(&vnode->lock);
        }
+
+       write_sequnlock(&vnode->cb_lock);
 }
 
 /*
@@ -143,49 +176,31 @@ static void afs_break_callback(struct afs_server *server,
 static void afs_break_one_callback(struct afs_server *server,
                                   struct afs_fid *fid)
 {
+       struct afs_cb_interest *cbi;
+       struct afs_iget_data data;
        struct afs_vnode *vnode;
-       struct rb_node *p;
-
-       _debug("find");
-       spin_lock(&server->fs_lock);
-       p = server->fs_vnodes.rb_node;
-       while (p) {
-               vnode = rb_entry(p, struct afs_vnode, server_rb);
-               if (fid->vid < vnode->fid.vid)
-                       p = p->rb_left;
-               else if (fid->vid > vnode->fid.vid)
-                       p = p->rb_right;
-               else if (fid->vnode < vnode->fid.vnode)
-                       p = p->rb_left;
-               else if (fid->vnode > vnode->fid.vnode)
-                       p = p->rb_right;
-               else if (fid->unique < vnode->fid.unique)
-                       p = p->rb_left;
-               else if (fid->unique > vnode->fid.unique)
-                       p = p->rb_right;
-               else
-                       goto found;
-       }
-
-       /* not found so we just ignore it (it may have moved to another
-        * server) */
-not_available:
-       _debug("not avail");
-       spin_unlock(&server->fs_lock);
-       _leave("");
-       return;
+       struct inode *inode;
 
-found:
-       _debug("found");
-       ASSERTCMP(server, ==, vnode->server);
+       read_lock(&server->cb_break_lock);
 
-       if (!igrab(AFS_VNODE_TO_I(vnode)))
-               goto not_available;
-       spin_unlock(&server->fs_lock);
+       /* Step through all interested superblocks.  There may be more than one
+        * because of cell aliasing.
+        */
+       list_for_each_entry(cbi, &server->cb_interests, cb_link) {
+               if (cbi->vid != fid->vid)
+                       continue;
+
+               data.volume = NULL;
+               data.fid = *fid;
+               inode = ilookup5_nowait(cbi->sb, fid->vnode, afs_iget5_test, &data);
+               if (inode) {
+                       vnode = AFS_FS_I(inode);
+                       afs_break_callback(vnode);
+                       iput(inode);
+               }
+       }
 
-       afs_break_callback(server, vnode);
-       iput(&vnode->vfs_inode);
-       _leave("");
+       read_unlock(&server->cb_break_lock);
 }
 
 /*
@@ -216,261 +231,14 @@ void afs_break_callbacks(struct afs_server *server, size_t count,
 }
 
 /*
- * record the callback for breaking
- * - the caller must hold server->cb_lock
+ * Clear the callback interests in a server list.
  */
-static void afs_do_give_up_callback(struct afs_server *server,
-                                   struct afs_vnode *vnode)
+void afs_clear_callback_interests(struct afs_net *net, struct afs_server_list *slist)
 {
-       struct afs_callback *cb;
-
-       _enter("%p,%p", server, vnode);
-
-       cb = &server->cb_break[server->cb_break_head];
-       cb->fid         = vnode->fid;
-       cb->version     = vnode->cb_version;
-       cb->expiry      = vnode->cb_expiry;
-       cb->type        = vnode->cb_type;
-       smp_wmb();
-       server->cb_break_head =
-               (server->cb_break_head + 1) &
-               (ARRAY_SIZE(server->cb_break) - 1);
-
-       /* defer the breaking of callbacks to try and collect as many as
-        * possible to ship in one operation */
-       switch (atomic_inc_return(&server->cb_break_n)) {
-       case 1 ... AFSCBMAX - 1:
-               queue_delayed_work(afs_callback_update_worker,
-                                  &server->cb_break_work, HZ * 2);
-               break;
-       case AFSCBMAX:
-               afs_flush_callback_breaks(server);
-               break;
-       default:
-               break;
-       }
-
-       ASSERT(server->cb_promises.rb_node != NULL);
-       rb_erase(&vnode->cb_promise, &server->cb_promises);
-       vnode->cb_promised = false;
-       _leave("");
-}
-
-/*
- * discard the callback on a deleted item
- */
-void afs_discard_callback_on_delete(struct afs_vnode *vnode)
-{
-       struct afs_server *server = vnode->server;
+       int i;
 
-       _enter("%d", vnode->cb_promised);
-
-       if (!vnode->cb_promised) {
-               _leave(" [not promised]");
-               return;
-       }
-
-       ASSERT(server != NULL);
-
-       spin_lock(&server->cb_lock);
-       if (vnode->cb_promised) {
-               ASSERT(server->cb_promises.rb_node != NULL);
-               rb_erase(&vnode->cb_promise, &server->cb_promises);
-               vnode->cb_promised = false;
+       for (i = 0; i < slist->nr_servers; i++) {
+               afs_put_cb_interest(net, slist->servers[i].cb_interest);
+               slist->servers[i].cb_interest = NULL;
        }
-       spin_unlock(&server->cb_lock);
-       _leave("");
-}
-
-/*
- * give up the callback registered for a vnode on the file server when the
- * inode is being cleared
- */
-void afs_give_up_callback(struct afs_vnode *vnode)
-{
-       struct afs_server *server = vnode->server;
-
-       DECLARE_WAITQUEUE(myself, current);
-
-       _enter("%d", vnode->cb_promised);
-
-       _debug("GIVE UP INODE %p", &vnode->vfs_inode);
-
-       if (!vnode->cb_promised) {
-               _leave(" [not promised]");
-               return;
-       }
-
-       ASSERT(server != NULL);
-
-       spin_lock(&server->cb_lock);
-       if (vnode->cb_promised && afs_breakring_space(server) == 0) {
-               add_wait_queue(&server->cb_break_waitq, &myself);
-               for (;;) {
-                       set_current_state(TASK_UNINTERRUPTIBLE);
-                       if (!vnode->cb_promised ||
-                           afs_breakring_space(server) != 0)
-                               break;
-                       spin_unlock(&server->cb_lock);
-                       schedule();
-                       spin_lock(&server->cb_lock);
-               }
-               remove_wait_queue(&server->cb_break_waitq, &myself);
-               __set_current_state(TASK_RUNNING);
-       }
-
-       /* of course, it's always possible for the server to break this vnode's
-        * callback first... */
-       if (vnode->cb_promised)
-               afs_do_give_up_callback(server, vnode);
-
-       spin_unlock(&server->cb_lock);
-       _leave("");
-}
-
-/*
- * dispatch a deferred give up callbacks operation
- */
-void afs_dispatch_give_up_callbacks(struct work_struct *work)
-{
-       struct afs_server *server =
-               container_of(work, struct afs_server, cb_break_work.work);
-
-       _enter("");
-
-       /* tell the fileserver to discard the callback promises it has
-        * - in the event of ENOMEM or some other error, we just forget that we
-        *   had callbacks entirely, and the server will call us later to break
-        *   them
-        */
-       afs_fs_give_up_callbacks(server, true);
-}
-
-/*
- * flush the outstanding callback breaks on a server
- */
-void afs_flush_callback_breaks(struct afs_server *server)
-{
-       mod_delayed_work(afs_callback_update_worker, &server->cb_break_work, 0);
-}
-
-#if 0
-/*
- * update a bunch of callbacks
- */
-static void afs_callback_updater(struct work_struct *work)
-{
-       struct afs_server *server;
-       struct afs_vnode *vnode, *xvnode;
-       time64_t now;
-       long timeout;
-       int ret;
-
-       server = container_of(work, struct afs_server, updater);
-
-       _enter("");
-
-       now = ktime_get_real_seconds();
-
-       /* find the first vnode to update */
-       spin_lock(&server->cb_lock);
-       for (;;) {
-               if (RB_EMPTY_ROOT(&server->cb_promises)) {
-                       spin_unlock(&server->cb_lock);
-                       _leave(" [nothing]");
-                       return;
-               }
-
-               vnode = rb_entry(rb_first(&server->cb_promises),
-                                struct afs_vnode, cb_promise);
-               if (atomic_read(&vnode->usage) > 0)
-                       break;
-               rb_erase(&vnode->cb_promise, &server->cb_promises);
-               vnode->cb_promised = false;
-       }
-
-       timeout = vnode->update_at - now;
-       if (timeout > 0) {
-               queue_delayed_work(afs_vnode_update_worker,
-                                  &afs_vnode_update, timeout * HZ);
-               spin_unlock(&server->cb_lock);
-               _leave(" [nothing]");
-               return;
-       }
-
-       list_del_init(&vnode->update);
-       atomic_inc(&vnode->usage);
-       spin_unlock(&server->cb_lock);
-
-       /* we can now perform the update */
-       _debug("update %s", vnode->vldb.name);
-       vnode->state = AFS_VL_UPDATING;
-       vnode->upd_rej_cnt = 0;
-       vnode->upd_busy_cnt = 0;
-
-       ret = afs_vnode_update_record(vl, &vldb);
-       switch (ret) {
-       case 0:
-               afs_vnode_apply_update(vl, &vldb);
-               vnode->state = AFS_VL_UPDATING;
-               break;
-       case -ENOMEDIUM:
-               vnode->state = AFS_VL_VOLUME_DELETED;
-               break;
-       default:
-               vnode->state = AFS_VL_UNCERTAIN;
-               break;
-       }
-
-       /* and then reschedule */
-       _debug("reschedule");
-       vnode->update_at = ktime_get_real_seconds() +
-                       afs_vnode_update_timeout;
-
-       spin_lock(&server->cb_lock);
-
-       if (!list_empty(&server->cb_promises)) {
-               /* next update in 10 minutes, but wait at least 1 second more
-                * than the newest record already queued so that we don't spam
-                * the VL server suddenly with lots of requests
-                */
-               xvnode = list_entry(server->cb_promises.prev,
-                                   struct afs_vnode, update);
-               if (vnode->update_at <= xvnode->update_at)
-                       vnode->update_at = xvnode->update_at + 1;
-               xvnode = list_entry(server->cb_promises.next,
-                                   struct afs_vnode, update);
-               timeout = xvnode->update_at - now;
-               if (timeout < 0)
-                       timeout = 0;
-       } else {
-               timeout = afs_vnode_update_timeout;
-       }
-
-       list_add_tail(&vnode->update, &server->cb_promises);
-
-       _debug("timeout %ld", timeout);
-       queue_delayed_work(afs_vnode_update_worker,
-                          &afs_vnode_update, timeout * HZ);
-       spin_unlock(&server->cb_lock);
-       afs_put_vnode(vl);
-}
-#endif
-
-/*
- * initialise the callback update process
- */
-int __init afs_callback_update_init(void)
-{
-       afs_callback_update_worker = alloc_ordered_workqueue("kafs_callbackd",
-                                                            WQ_MEM_RECLAIM);
-       return afs_callback_update_worker ? 0 : -ENOMEM;
-}
-
-/*
- * shut down the callback update process
- */
-void afs_callback_update_kill(void)
-{
-       destroy_workqueue(afs_callback_update_worker);
 }
index ca0a3cf93791879578121b842891e97e5b0ffb1e..1858c91169e4fc213e77b548628386041790b0c0 100644 (file)
@@ -1,6 +1,6 @@
 /* AFS cell and server record management
  *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2002, 2017 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
  * This program is free software; you can redistribute it and/or
  * 2 of the License, or (at your option) any later version.
  */
 
-#include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/key.h>
 #include <linux/ctype.h>
 #include <linux/dns_resolver.h>
 #include <linux/sched.h>
+#include <linux/inet.h>
 #include <keys/rxrpc-type.h>
 #include "internal.h"
 
-DECLARE_RWSEM(afs_proc_cells_sem);
-LIST_HEAD(afs_proc_cells);
+unsigned __read_mostly afs_cell_gc_delay = 10;
 
-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;
+static void afs_manage_cell(struct work_struct *);
+
+static void afs_dec_cells_outstanding(struct afs_net *net)
+{
+       if (atomic_dec_and_test(&net->cells_outstanding))
+               wake_up_atomic_t(&net->cells_outstanding);
+}
 
 /*
- * allocate a cell record and fill in its name, VL server address list and
- * allocate an anonymous key
+ * Set the cell timer to fire after a given delay, assuming it's not already
+ * set for an earlier time.
  */
-static struct afs_cell *afs_cell_alloc(const char *name, unsigned namelen,
-                                      char *vllist)
+static void afs_set_cell_timer(struct afs_net *net, time64_t delay)
 {
-       struct afs_cell *cell;
-       struct key *key;
-       char keyname[4 + AFS_MAXCELLNAME + 1], *cp, *dp, *next;
-       char  *dvllist = NULL, *_vllist = NULL;
-       char  delimiter = ':';
-       int ret;
+       if (net->live) {
+               atomic_inc(&net->cells_outstanding);
+               if (timer_reduce(&net->cells_timer, jiffies + delay * HZ))
+                       afs_dec_cells_outstanding(net);
+       }
+}
 
-       _enter("%*.*s,%s", namelen, namelen, name ?: "", vllist);
+/*
+ * Look up and get an activation reference on a cell record under RCU
+ * conditions.  The caller must hold the RCU read lock.
+ */
+struct afs_cell *afs_lookup_cell_rcu(struct afs_net *net,
+                                    const char *name, unsigned int namesz)
+{
+       struct afs_cell *cell = NULL;
+       struct rb_node *p;
+       int n, seq = 0, ret = 0;
 
-       BUG_ON(!name); /* TODO: want to look up "this cell" in the cache */
+       _enter("%*.*s", namesz, namesz, name);
 
-       if (namelen > AFS_MAXCELLNAME) {
-               _leave(" = -ENAMETOOLONG");
+       if (name && namesz == 0)
+               return ERR_PTR(-EINVAL);
+       if (namesz > AFS_MAXCELLNAME)
                return ERR_PTR(-ENAMETOOLONG);
-       }
 
-       /* allocate and initialise a cell record */
-       cell = kzalloc(sizeof(struct afs_cell) + namelen + 1, GFP_KERNEL);
-       if (!cell) {
-               _leave(" = -ENOMEM");
-               return ERR_PTR(-ENOMEM);
-       }
+       do {
+               /* Unfortunately, rbtree walking doesn't give reliable results
+                * under just the RCU read lock, so we have to check for
+                * changes.
+                */
+               if (cell)
+                       afs_put_cell(net, cell);
+               cell = NULL;
+               ret = -ENOENT;
 
-       memcpy(cell->name, name, namelen);
-       cell->name[namelen] = 0;
-
-       atomic_set(&cell->usage, 1);
-       INIT_LIST_HEAD(&cell->link);
-       rwlock_init(&cell->servers_lock);
-       INIT_LIST_HEAD(&cell->servers);
-       init_rwsem(&cell->vl_sem);
-       INIT_LIST_HEAD(&cell->vl_list);
-       spin_lock_init(&cell->vl_lock);
-
-       /* if the ip address is invalid, try dns query */
-       if (!vllist || strlen(vllist) < 7) {
-               ret = dns_query("afsdb", name, namelen, "ipv4", &dvllist, NULL);
-               if (ret < 0) {
-                       if (ret == -ENODATA || ret == -EAGAIN || ret == -ENOKEY)
-                               /* translate these errors into something
-                                * userspace might understand */
-                               ret = -EDESTADDRREQ;
-                       _leave(" = %d", ret);
-                       return ERR_PTR(ret);
+               read_seqbegin_or_lock(&net->cells_lock, &seq);
+
+               if (!name) {
+                       cell = rcu_dereference_raw(net->ws_cell);
+                       if (cell) {
+                               afs_get_cell(cell);
+                               continue;
+                       }
+                       ret = -EDESTADDRREQ;
+                       continue;
                }
-               _vllist = dvllist;
 
-               /* change the delimiter for user-space reply */
-               delimiter = ',';
+               p = rcu_dereference_raw(net->cells.rb_node);
+               while (p) {
+                       cell = rb_entry(p, struct afs_cell, net_node);
+
+                       n = strncasecmp(cell->name, name,
+                                       min_t(size_t, cell->name_len, namesz));
+                       if (n == 0)
+                               n = cell->name_len - namesz;
+                       if (n < 0) {
+                               p = rcu_dereference_raw(p->rb_left);
+                       } else if (n > 0) {
+                               p = rcu_dereference_raw(p->rb_right);
+                       } else {
+                               if (atomic_inc_not_zero(&cell->usage)) {
+                                       ret = 0;
+                                       break;
+                               }
+                               /* We want to repeat the search, this time with
+                                * the lock properly locked.
+                                */
+                       }
+                       cell = NULL;
+               }
 
-       } else {
-               _vllist = vllist;
-       }
+       } while (need_seqretry(&net->cells_lock, seq));
 
-       /* fill in the VL server list from the rest of the string */
-       do {
-               unsigned a, b, c, d;
+       done_seqretry(&net->cells_lock, seq);
 
-               next = strchr(_vllist, delimiter);
-               if (next)
-                       *next++ = 0;
+       return ret == 0 ? cell : ERR_PTR(ret);
+}
 
-               if (sscanf(_vllist, "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
-                       goto bad_address;
+/*
+ * Set up a cell record and fill in its name, VL server address list and
+ * allocate an anonymous key
+ */
+static struct afs_cell *afs_alloc_cell(struct afs_net *net,
+                                      const char *name, unsigned int namelen,
+                                      const char *vllist)
+{
+       struct afs_cell *cell;
+       int i, ret;
 
-               if (a > 255 || b > 255 || c > 255 || d > 255)
-                       goto bad_address;
+       ASSERT(name);
+       if (namelen == 0)
+               return ERR_PTR(-EINVAL);
+       if (namelen > AFS_MAXCELLNAME) {
+               _leave(" = -ENAMETOOLONG");
+               return ERR_PTR(-ENAMETOOLONG);
+       }
 
-               cell->vl_addrs[cell->vl_naddrs++].s_addr =
-                       htonl((a << 24) | (b << 16) | (c << 8) | d);
+       _enter("%*.*s,%s", namelen, namelen, name, vllist);
 
-       } while (cell->vl_naddrs < AFS_CELL_MAX_ADDRS && (_vllist = next));
+       cell = kzalloc(sizeof(struct afs_cell), GFP_KERNEL);
+       if (!cell) {
+               _leave(" = -ENOMEM");
+               return ERR_PTR(-ENOMEM);
+       }
 
-       /* create a key to represent an anonymous user */
-       memcpy(keyname, "afs@", 4);
-       dp = keyname + 4;
-       cp = cell->name;
-       do {
-               *dp++ = toupper(*cp);
-       } while (*cp++);
+       cell->net = net;
+       cell->name_len = namelen;
+       for (i = 0; i < namelen; i++)
+               cell->name[i] = tolower(name[i]);
+
+       atomic_set(&cell->usage, 2);
+       INIT_WORK(&cell->manager, afs_manage_cell);
+       cell->flags = ((1 << AFS_CELL_FL_NOT_READY) |
+                      (1 << AFS_CELL_FL_NO_LOOKUP_YET));
+       INIT_LIST_HEAD(&cell->proc_volumes);
+       rwlock_init(&cell->proc_lock);
+       rwlock_init(&cell->vl_addrs_lock);
+
+       /* Fill in the VL server list if we were given a list of addresses to
+        * use.
+        */
+       if (vllist) {
+               struct afs_addr_list *alist;
+
+               alist = afs_parse_text_addrs(vllist, strlen(vllist), ':',
+                                            VL_SERVICE, AFS_VL_PORT);
+               if (IS_ERR(alist)) {
+                       ret = PTR_ERR(alist);
+                       goto parse_failed;
+               }
 
-       key = rxrpc_get_null_key(keyname);
-       if (IS_ERR(key)) {
-               _debug("no key");
-               ret = PTR_ERR(key);
-               goto error;
+               rcu_assign_pointer(cell->vl_addrs, alist);
+               cell->dns_expiry = TIME64_MAX;
        }
-       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(dvllist);
+parse_failed:
+       if (ret == -EINVAL)
+               printk(KERN_ERR "kAFS: bad VL server IP address\n");
        kfree(cell);
        _leave(" = %d", ret);
        return ERR_PTR(ret);
 }
 
 /*
- * afs_cell_crate() - create a cell record
- * @name:      is the name of the cell.
- * @namsesz:   is the strlen of the cell name.
- * @vllist:    is a colon separated list of IP addresses in "a.b.c.d" format.
- * @retref:    is T to return the cell reference when the cell exists.
+ * afs_lookup_cell - Look up or create a cell record.
+ * @net:       The network namespace
+ * @name:      The name of the cell.
+ * @namesz:    The strlen of the cell name.
+ * @vllist:    A colon/comma separated list of numeric IP addresses or NULL.
+ * @excl:      T if an error should be given if the cell name already exists.
+ *
+ * Look up a cell record by name and query the DNS for VL server addresses if
+ * needed.  Note that that actual DNS query is punted off to the manager thread
+ * so that this function can return immediately if interrupted whilst allowing
+ * cell records to be shared even if not yet fully constructed.
  */
-struct afs_cell *afs_cell_create(const char *name, unsigned namesz,
-                                char *vllist, bool retref)
+struct afs_cell *afs_lookup_cell(struct afs_net *net,
+                                const char *name, unsigned int namesz,
+                                const char *vllist, bool excl)
 {
-       struct afs_cell *cell;
-       int ret;
-
-       _enter("%*.*s,%s", namesz, namesz, name ?: "", vllist);
+       struct afs_cell *cell, *candidate, *cursor;
+       struct rb_node *parent, **pp;
+       int ret, n;
+
+       _enter("%s,%s", name, vllist);
+
+       if (!excl) {
+               rcu_read_lock();
+               cell = afs_lookup_cell_rcu(net, name, namesz);
+               rcu_read_unlock();
+               if (!IS_ERR(cell)) {
+                       if (excl) {
+                               afs_put_cell(net, cell);
+                               return ERR_PTR(-EEXIST);
+                       }
+                       goto wait_for_cell;
+               }
+       }
 
-       down_write(&afs_cells_sem);
-       read_lock(&afs_cells_lock);
-       list_for_each_entry(cell, &afs_cells, link) {
-               if (strncasecmp(cell->name, name, namesz) == 0)
-                       goto duplicate_name;
+       /* Assume we're probably going to create a cell and preallocate and
+        * mostly set up a candidate record.  We can then use this to stash the
+        * name, the net namespace and VL server addresses.
+        *
+        * We also want to do this before we hold any locks as it may involve
+        * upcalling to userspace to make DNS queries.
+        */
+       candidate = afs_alloc_cell(net, name, namesz, vllist);
+       if (IS_ERR(candidate)) {
+               _leave(" = %ld", PTR_ERR(candidate));
+               return candidate;
        }
-       read_unlock(&afs_cells_lock);
 
-       cell = afs_cell_alloc(name, namesz, vllist);
-       if (IS_ERR(cell)) {
-               _leave(" = %ld", PTR_ERR(cell));
-               up_write(&afs_cells_sem);
-               return cell;
+       /* Find the insertion point and check to see if someone else added a
+        * cell whilst we were allocating.
+        */
+       write_seqlock(&net->cells_lock);
+
+       pp = &net->cells.rb_node;
+       parent = NULL;
+       while (*pp) {
+               parent = *pp;
+               cursor = rb_entry(parent, struct afs_cell, net_node);
+
+               n = strncasecmp(cursor->name, name,
+                               min_t(size_t, cursor->name_len, namesz));
+               if (n == 0)
+                       n = cursor->name_len - namesz;
+               if (n < 0)
+                       pp = &(*pp)->rb_left;
+               else if (n > 0)
+                       pp = &(*pp)->rb_right;
+               else
+                       goto cell_already_exists;
        }
 
-       /* add a proc directory for this cell */
-       ret = afs_proc_cell_setup(cell);
-       if (ret < 0)
-               goto error;
+       cell = candidate;
+       candidate = NULL;
+       rb_link_node_rcu(&cell->net_node, parent, pp);
+       rb_insert_color(&cell->net_node, &net->cells);
+       atomic_inc(&net->cells_outstanding);
+       write_sequnlock(&net->cells_lock);
 
-#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, true);
-#endif
+       queue_work(afs_wq, &cell->manager);
 
-       /* add to the cell lists */
-       write_lock(&afs_cells_lock);
-       list_add_tail(&cell->link, &afs_cells);
-       write_unlock(&afs_cells_lock);
+wait_for_cell:
+       _debug("wait_for_cell");
+       ret = wait_on_bit(&cell->flags, AFS_CELL_FL_NOT_READY, TASK_INTERRUPTIBLE);
+       smp_rmb();
 
-       down_write(&afs_proc_cells_sem);
-       list_add_tail(&cell->proc_link, &afs_proc_cells);
-       up_write(&afs_proc_cells_sem);
-       up_write(&afs_cells_sem);
+       switch (READ_ONCE(cell->state)) {
+       case AFS_CELL_FAILED:
+               ret = cell->error;
+               goto error;
+       default:
+               _debug("weird %u %d", cell->state, cell->error);
+               goto error;
+       case AFS_CELL_ACTIVE:
+               break;
+       }
 
-       _leave(" = %p", cell);
+       _leave(" = %p [cell]", cell);
        return cell;
 
+cell_already_exists:
+       _debug("cell exists");
+       cell = cursor;
+       if (excl) {
+               ret = -EEXIST;
+       } else {
+               afs_get_cell(cursor);
+               ret = 0;
+       }
+       write_sequnlock(&net->cells_lock);
+       kfree(candidate);
+       if (ret == 0)
+               goto wait_for_cell;
+       goto error_noput;
 error:
-       up_write(&afs_cells_sem);
-       key_put(cell->anonymous_key);
-       kfree(cell);
-       _leave(" = %d", ret);
+       afs_put_cell(net, cell);
+error_noput:
+       _leave(" = %d [error]", ret);
        return ERR_PTR(ret);
-
-duplicate_name:
-       if (retref && !IS_ERR(cell))
-               afs_get_cell(cell);
-
-       read_unlock(&afs_cells_lock);
-       up_write(&afs_cells_sem);
-
-       if (retref) {
-               _leave(" = %p", cell);
-               return cell;
-       }
-
-       _leave(" = -EEXIST");
-       return ERR_PTR(-EEXIST);
 }
 
 /*
@@ -223,10 +306,11 @@ duplicate_name:
  * - 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)
+int afs_cell_init(struct afs_net *net, const char *rootcell)
 {
        struct afs_cell *old_root, *new_root;
-       char *cp;
+       const char *cp, *vllist;
+       size_t len;
 
        _enter("");
 
@@ -239,222 +323,453 @@ int afs_cell_init(char *rootcell)
        }
 
        cp = strchr(rootcell, ':');
-       if (!cp)
+       if (!cp) {
                _debug("kAFS: no VL server IP addresses specified");
-       else
-               *cp++ = 0;
+               vllist = NULL;
+               len = strlen(rootcell);
+       } else {
+               vllist = cp + 1;
+               len = cp - rootcell;
+       }
 
        /* allocate a cell record for the root cell */
-       new_root = afs_cell_create(rootcell, strlen(rootcell), cp, false);
+       new_root = afs_lookup_cell(net, rootcell, len, vllist, false);
        if (IS_ERR(new_root)) {
                _leave(" = %ld", PTR_ERR(new_root));
                return PTR_ERR(new_root);
        }
 
+       set_bit(AFS_CELL_FL_NO_GC, &new_root->flags);
+       afs_get_cell(new_root);
+
        /* install the new cell */
-       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);
+       write_seqlock(&net->cells_lock);
+       old_root = net->ws_cell;
+       net->ws_cell = new_root;
+       write_sequnlock(&net->cells_lock);
 
+       afs_put_cell(net, old_root);
        _leave(" = 0");
        return 0;
 }
 
 /*
- * lookup a cell record
+ * Update a cell's VL server address list from the DNS.
  */
-struct afs_cell *afs_cell_lookup(const char *name, unsigned namesz,
-                                bool dns_cell)
+static void afs_update_cell(struct afs_cell *cell)
 {
-       struct afs_cell *cell;
-
-       _enter("\"%*.*s\",", namesz, namesz, name ?: "");
-
-       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 */
-               list_for_each_entry(cell, &afs_cells, link) {
-                       if (strncmp(cell->name, name, namesz) == 0) {
-                               afs_get_cell(cell);
-                               goto found;
-                       }
+       struct afs_addr_list *alist, *old;
+       time64_t now, expiry;
+
+       _enter("%s", cell->name);
+
+       alist = afs_dns_query(cell, &expiry);
+       if (IS_ERR(alist)) {
+               switch (PTR_ERR(alist)) {
+               case -ENODATA:
+                       /* The DNS said that the cell does not exist */
+                       set_bit(AFS_CELL_FL_NOT_FOUND, &cell->flags);
+                       clear_bit(AFS_CELL_FL_DNS_FAIL, &cell->flags);
+                       cell->dns_expiry = ktime_get_real_seconds() + 61;
+                       break;
+
+               case -EAGAIN:
+               case -ECONNREFUSED:
+               default:
+                       set_bit(AFS_CELL_FL_DNS_FAIL, &cell->flags);
+                       cell->dns_expiry = ktime_get_real_seconds() + 10;
+                       break;
                }
-               cell = ERR_PTR(-ENOENT);
-               if (dns_cell)
-                       goto create_cell;
-       found:
-               ;
+
+               cell->error = -EDESTADDRREQ;
        } else {
-               cell = afs_cell_root;
-               if (!cell) {
-                       /* this should not happen unless user tries to mount
-                        * when root cell is not set. Return an impossibly
-                        * bizarre errno to alert the user. Things like
-                        * ENOENT might be "more appropriate" but they happen
-                        * for other reasons.
-                        */
-                       cell = ERR_PTR(-EDESTADDRREQ);
-               } else {
-                       afs_get_cell(cell);
-               }
+               clear_bit(AFS_CELL_FL_DNS_FAIL, &cell->flags);
+               clear_bit(AFS_CELL_FL_NOT_FOUND, &cell->flags);
+
+               /* Exclusion on changing vl_addrs is achieved by a
+                * non-reentrant work item.
+                */
+               old = rcu_dereference_protected(cell->vl_addrs, true);
+               rcu_assign_pointer(cell->vl_addrs, alist);
+               cell->dns_expiry = expiry;
 
+               if (old)
+                       afs_put_addrlist(old);
        }
 
-       read_unlock(&afs_cells_lock);
-       up_read(&afs_cells_sem);
-       _leave(" = %p", cell);
-       return cell;
+       if (test_and_clear_bit(AFS_CELL_FL_NO_LOOKUP_YET, &cell->flags))
+               wake_up_bit(&cell->flags, AFS_CELL_FL_NO_LOOKUP_YET);
 
-create_cell:
-       read_unlock(&afs_cells_lock);
-       up_read(&afs_cells_sem);
+       now = ktime_get_real_seconds();
+       afs_set_cell_timer(cell->net, cell->dns_expiry - now);
+       _leave("");
+}
 
-       cell = afs_cell_create(name, namesz, NULL, true);
+/*
+ * Destroy a cell record
+ */
+static void afs_cell_destroy(struct rcu_head *rcu)
+{
+       struct afs_cell *cell = container_of(rcu, struct afs_cell, rcu);
 
-       _leave(" = %p", cell);
-       return cell;
+       _enter("%p{%s}", cell, cell->name);
+
+       ASSERTCMP(atomic_read(&cell->usage), ==, 0);
+
+       afs_put_addrlist(cell->vl_addrs);
+       key_put(cell->anonymous_key);
+       kfree(cell);
+
+       _leave(" [destroyed]");
 }
 
-#if 0
 /*
- * try and get a cell record
+ * Queue the cell manager.
  */
-struct afs_cell *afs_get_cell_maybe(struct afs_cell *cell)
+static void afs_queue_cell_manager(struct afs_net *net)
 {
-       write_lock(&afs_cells_lock);
+       int outstanding = atomic_inc_return(&net->cells_outstanding);
 
-       if (cell && !list_empty(&cell->link))
-               afs_get_cell(cell);
-       else
-               cell = NULL;
+       _enter("%d", outstanding);
 
-       write_unlock(&afs_cells_lock);
-       return cell;
+       if (!queue_work(afs_wq, &net->cells_manager))
+               afs_dec_cells_outstanding(net);
 }
-#endif  /*  0  */
 
 /*
- * destroy a cell record
+ * Cell management timer.  We have an increment on cells_outstanding that we
+ * need to pass along to the work item.
  */
-void afs_put_cell(struct afs_cell *cell)
+void afs_cells_timer(struct timer_list *timer)
 {
-       if (!cell)
-               return;
+       struct afs_net *net = container_of(timer, struct afs_net, cells_timer);
 
-       _enter("%p{%d,%s}", cell, atomic_read(&cell->usage), cell->name);
+       _enter("");
+       if (!queue_work(afs_wq, &net->cells_manager))
+               afs_dec_cells_outstanding(net);
+}
 
-       ASSERTCMP(atomic_read(&cell->usage), >, 0);
+/*
+ * Get a reference on a cell record.
+ */
+struct afs_cell *afs_get_cell(struct afs_cell *cell)
+{
+       atomic_inc(&cell->usage);
+       return cell;
+}
 
-       /* to prevent a race, the decrement and the dequeue must be effectively
-        * atomic */
-       write_lock(&afs_cells_lock);
+/*
+ * Drop a reference on a cell record.
+ */
+void afs_put_cell(struct afs_net *net, struct afs_cell *cell)
+{
+       time64_t now, expire_delay;
 
-       if (likely(!atomic_dec_and_test(&cell->usage))) {
-               write_unlock(&afs_cells_lock);
-               _leave("");
+       if (!cell)
                return;
-       }
 
-       ASSERT(list_empty(&cell->servers));
-       ASSERT(list_empty(&cell->vl_list));
+       _enter("%s", cell->name);
 
-       write_unlock(&afs_cells_lock);
+       now = ktime_get_real_seconds();
+       cell->last_inactive = now;
+       expire_delay = 0;
+       if (!test_bit(AFS_CELL_FL_DNS_FAIL, &cell->flags) &&
+           !test_bit(AFS_CELL_FL_NOT_FOUND, &cell->flags))
+               expire_delay = afs_cell_gc_delay;
 
-       wake_up(&afs_cells_freeable_wq);
+       if (atomic_dec_return(&cell->usage) > 1)
+               return;
 
-       _leave(" [unused]");
+       /* 'cell' may now be garbage collected. */
+       afs_set_cell_timer(net, expire_delay);
 }
 
 /*
- * destroy a cell record
- * - must be called with the afs_cells_sem write-locked
- * - cell->link should have been broken by the caller
+ * Allocate a key to use as a placeholder for anonymous user security.
  */
-static void afs_cell_destroy(struct afs_cell *cell)
+static int afs_alloc_anon_key(struct afs_cell *cell)
 {
-       _enter("%p{%d,%s}", cell, atomic_read(&cell->usage), cell->name);
+       struct key *key;
+       char keyname[4 + AFS_MAXCELLNAME + 1], *cp, *dp;
 
-       ASSERTCMP(atomic_read(&cell->usage), >=, 0);
-       ASSERT(list_empty(&cell->link));
+       /* Create a key to represent an anonymous user. */
+       memcpy(keyname, "afs@", 4);
+       dp = keyname + 4;
+       cp = cell->name;
+       do {
+               *dp++ = tolower(*cp);
+       } while (*cp++);
 
-       /* wait for everyone to stop using the cell */
-       if (atomic_read(&cell->usage) > 0) {
-               DECLARE_WAITQUEUE(myself, current);
+       key = rxrpc_get_null_key(keyname);
+       if (IS_ERR(key))
+               return PTR_ERR(key);
 
-               _debug("wait for cell %s", cell->name);
-               set_current_state(TASK_UNINTERRUPTIBLE);
-               add_wait_queue(&afs_cells_freeable_wq, &myself);
+       cell->anonymous_key = key;
 
-               while (atomic_read(&cell->usage) > 0) {
-                       schedule();
-                       set_current_state(TASK_UNINTERRUPTIBLE);
-               }
+       _debug("anon key %p{%x}",
+              cell->anonymous_key, key_serial(cell->anonymous_key));
+       return 0;
+}
 
-               remove_wait_queue(&afs_cells_freeable_wq, &myself);
-               set_current_state(TASK_RUNNING);
+/*
+ * Activate a cell.
+ */
+static int afs_activate_cell(struct afs_net *net, struct afs_cell *cell)
+{
+       int ret;
+
+       if (!cell->anonymous_key) {
+               ret = afs_alloc_anon_key(cell);
+               if (ret < 0)
+                       return ret;
        }
 
-       _debug("cell dead");
-       ASSERTCMP(atomic_read(&cell->usage), ==, 0);
-       ASSERT(list_empty(&cell->servers));
-       ASSERT(list_empty(&cell->vl_list));
+#ifdef CONFIG_AFS_FSCACHE
+       cell->cache = fscache_acquire_cookie(afs_cache_netfs.primary_index,
+                                            &afs_cell_cache_index_def,
+                                            cell, true);
+#endif
+       ret = afs_proc_cell_setup(net, cell);
+       if (ret < 0)
+               return ret;
+       spin_lock(&net->proc_cells_lock);
+       list_add_tail(&cell->proc_link, &net->proc_cells);
+       spin_unlock(&net->proc_cells_lock);
+       return 0;
+}
 
-       afs_proc_cell_remove(cell);
+/*
+ * Deactivate a cell.
+ */
+static void afs_deactivate_cell(struct afs_net *net, struct afs_cell *cell)
+{
+       _enter("%s", cell->name);
+
+       afs_proc_cell_remove(net, cell);
 
-       down_write(&afs_proc_cells_sem);
+       spin_lock(&net->proc_cells_lock);
        list_del_init(&cell->proc_link);
-       up_write(&afs_proc_cells_sem);
+       spin_unlock(&net->proc_cells_lock);
 
 #ifdef CONFIG_AFS_FSCACHE
        fscache_relinquish_cookie(cell->cache, 0);
+       cell->cache = NULL;
 #endif
-       key_put(cell->anonymous_key);
-       kfree(cell);
 
-       _leave(" [destroyed]");
+       _leave("");
 }
 
 /*
- * purge in-memory cell database on module unload or afs_init() failure
- * - the timeout daemon is stopped before calling this
+ * Manage a cell record, initialising and destroying it, maintaining its DNS
+ * records.
  */
-void afs_cell_purge(void)
+static void afs_manage_cell(struct work_struct *work)
 {
-       struct afs_cell *cell;
+       struct afs_cell *cell = container_of(work, struct afs_cell, manager);
+       struct afs_net *net = cell->net;
+       bool deleted;
+       int ret, usage;
+
+       _enter("%s", cell->name);
+
+again:
+       _debug("state %u", cell->state);
+       switch (cell->state) {
+       case AFS_CELL_INACTIVE:
+       case AFS_CELL_FAILED:
+               write_seqlock(&net->cells_lock);
+               usage = 1;
+               deleted = atomic_try_cmpxchg_relaxed(&cell->usage, &usage, 0);
+               if (deleted)
+                       rb_erase(&cell->net_node, &net->cells);
+               write_sequnlock(&net->cells_lock);
+               if (deleted)
+                       goto final_destruction;
+               if (cell->state == AFS_CELL_FAILED)
+                       goto done;
+               cell->state = AFS_CELL_UNSET;
+               goto again;
+
+       case AFS_CELL_UNSET:
+               cell->state = AFS_CELL_ACTIVATING;
+               goto again;
+
+       case AFS_CELL_ACTIVATING:
+               ret = afs_activate_cell(net, cell);
+               if (ret < 0)
+                       goto activation_failed;
+
+               cell->state = AFS_CELL_ACTIVE;
+               smp_wmb();
+               clear_bit(AFS_CELL_FL_NOT_READY, &cell->flags);
+               wake_up_bit(&cell->flags, AFS_CELL_FL_NOT_READY);
+               goto again;
+
+       case AFS_CELL_ACTIVE:
+               if (atomic_read(&cell->usage) > 1) {
+                       time64_t now = ktime_get_real_seconds();
+                       if (cell->dns_expiry <= now && net->live)
+                               afs_update_cell(cell);
+                       goto done;
+               }
+               cell->state = AFS_CELL_DEACTIVATING;
+               goto again;
+
+       case AFS_CELL_DEACTIVATING:
+               set_bit(AFS_CELL_FL_NOT_READY, &cell->flags);
+               if (atomic_read(&cell->usage) > 1)
+                       goto reverse_deactivation;
+               afs_deactivate_cell(net, cell);
+               cell->state = AFS_CELL_INACTIVE;
+               goto again;
+
+       default:
+               break;
+       }
+       _debug("bad state %u", cell->state);
+       BUG(); /* Unhandled state */
+
+activation_failed:
+       cell->error = ret;
+       afs_deactivate_cell(net, cell);
+
+       cell->state = AFS_CELL_FAILED;
+       smp_wmb();
+       if (test_and_clear_bit(AFS_CELL_FL_NOT_READY, &cell->flags))
+               wake_up_bit(&cell->flags, AFS_CELL_FL_NOT_READY);
+       goto again;
+
+reverse_deactivation:
+       cell->state = AFS_CELL_ACTIVE;
+       smp_wmb();
+       clear_bit(AFS_CELL_FL_NOT_READY, &cell->flags);
+       wake_up_bit(&cell->flags, AFS_CELL_FL_NOT_READY);
+       _leave(" [deact->act]");
+       return;
+
+done:
+       _leave(" [done %u]", cell->state);
+       return;
+
+final_destruction:
+       call_rcu(&cell->rcu, afs_cell_destroy);
+       afs_dec_cells_outstanding(net);
+       _leave(" [destruct %d]", atomic_read(&net->cells_outstanding));
+}
+
+/*
+ * Manage the records of cells known to a network namespace.  This includes
+ * updating the DNS records and garbage collecting unused cells that were
+ * automatically added.
+ *
+ * Note that constructed cell records may only be removed from net->cells by
+ * this work item, so it is safe for this work item to stash a cursor pointing
+ * into the tree and then return to caller (provided it skips cells that are
+ * still under construction).
+ *
+ * Note also that we were given an increment on net->cells_outstanding by
+ * whoever queued us that we need to deal with before returning.
+ */
+void afs_manage_cells(struct work_struct *work)
+{
+       struct afs_net *net = container_of(work, struct afs_net, cells_manager);
+       struct rb_node *cursor;
+       time64_t now = ktime_get_real_seconds(), next_manage = TIME64_MAX;
+       bool purging = !net->live;
 
        _enter("");
 
-       afs_put_cell(afs_cell_root);
+       /* Trawl the cell database looking for cells that have expired from
+        * lack of use and cells whose DNS results have expired and dispatch
+        * their managers.
+        */
+       read_seqlock_excl(&net->cells_lock);
 
-       down_write(&afs_cells_sem);
+       for (cursor = rb_first(&net->cells); cursor; cursor = rb_next(cursor)) {
+               struct afs_cell *cell =
+                       rb_entry(cursor, struct afs_cell, net_node);
+               unsigned usage;
+               bool sched_cell = false;
 
-       while (!list_empty(&afs_cells)) {
-               cell = NULL;
+               usage = atomic_read(&cell->usage);
+               _debug("manage %s %u", cell->name, usage);
+
+               ASSERTCMP(usage, >=, 1);
 
-               /* remove the next cell from the front of the list */
-               write_lock(&afs_cells_lock);
+               if (purging) {
+                       if (test_and_clear_bit(AFS_CELL_FL_NO_GC, &cell->flags))
+                               usage = atomic_dec_return(&cell->usage);
+                       ASSERTCMP(usage, ==, 1);
+               }
+
+               if (usage == 1) {
+                       time64_t expire_at = cell->last_inactive;
+
+                       if (!test_bit(AFS_CELL_FL_DNS_FAIL, &cell->flags) &&
+                           !test_bit(AFS_CELL_FL_NOT_FOUND, &cell->flags))
+                               expire_at += afs_cell_gc_delay;
+                       if (purging || expire_at <= now)
+                               sched_cell = true;
+                       else if (expire_at < next_manage)
+                               next_manage = expire_at;
+               }
 
-               if (!list_empty(&afs_cells)) {
-                       cell = list_entry(afs_cells.next,
-                                         struct afs_cell, link);
-                       list_del_init(&cell->link);
+               if (!purging) {
+                       if (cell->dns_expiry <= now)
+                               sched_cell = true;
+                       else if (cell->dns_expiry <= next_manage)
+                               next_manage = cell->dns_expiry;
                }
 
-               write_unlock(&afs_cells_lock);
+               if (sched_cell)
+                       queue_work(afs_wq, &cell->manager);
+       }
+
+       read_sequnlock_excl(&net->cells_lock);
 
-               if (cell) {
-                       _debug("PURGING CELL %s (%d)",
-                              cell->name, atomic_read(&cell->usage));
+       /* Update the timer on the way out.  We have to pass an increment on
+        * cells_outstanding in the namespace that we are in to the timer or
+        * the work scheduler.
+        */
+       if (!purging && next_manage < TIME64_MAX) {
+               now = ktime_get_real_seconds();
 
-                       /* now the cell should be left with no references */
-                       afs_cell_destroy(cell);
+               if (next_manage - now <= 0) {
+                       if (queue_work(afs_wq, &net->cells_manager))
+                               atomic_inc(&net->cells_outstanding);
+               } else {
+                       afs_set_cell_timer(net, next_manage - now);
                }
        }
 
-       up_write(&afs_cells_sem);
+       afs_dec_cells_outstanding(net);
+       _leave(" [%d]", atomic_read(&net->cells_outstanding));
+}
+
+/*
+ * Purge in-memory cell database.
+ */
+void afs_cell_purge(struct afs_net *net)
+{
+       struct afs_cell *ws;
+
+       _enter("");
+
+       write_seqlock(&net->cells_lock);
+       ws = net->ws_cell;
+       net->ws_cell = NULL;
+       write_sequnlock(&net->cells_lock);
+       afs_put_cell(net, ws);
+
+       _debug("del timer");
+       if (del_timer_sync(&net->cells_timer))
+               atomic_dec(&net->cells_outstanding);
+
+       _debug("kick mgr");
+       afs_queue_cell_manager(net);
+
+       _debug("wait");
+       wait_on_atomic_t(&net->cells_outstanding, atomic_t_wait,
+                        TASK_UNINTERRUPTIBLE);
        _leave("");
 }
index 782d4d05a53ba332e2115891e343d41612cb1aa0..41e277f57b20f5e03833abc42aaacea75aff2f73 100644 (file)
@@ -41,7 +41,6 @@ static CM_NAME(CallBack);
 static const struct afs_call_type afs_SRXCBCallBack = {
        .name           = afs_SRXCBCallBack_name,
        .deliver        = afs_deliver_cb_callback,
-       .abort_to_error = afs_abort_to_error,
        .destructor     = afs_cm_destructor,
        .work           = SRXAFSCB_CallBack,
 };
@@ -53,7 +52,6 @@ static CM_NAME(InitCallBackState);
 static const struct afs_call_type afs_SRXCBInitCallBackState = {
        .name           = afs_SRXCBInitCallBackState_name,
        .deliver        = afs_deliver_cb_init_call_back_state,
-       .abort_to_error = afs_abort_to_error,
        .destructor     = afs_cm_destructor,
        .work           = SRXAFSCB_InitCallBackState,
 };
@@ -65,7 +63,6 @@ static CM_NAME(InitCallBackState3);
 static const struct afs_call_type afs_SRXCBInitCallBackState3 = {
        .name           = afs_SRXCBInitCallBackState3_name,
        .deliver        = afs_deliver_cb_init_call_back_state3,
-       .abort_to_error = afs_abort_to_error,
        .destructor     = afs_cm_destructor,
        .work           = SRXAFSCB_InitCallBackState,
 };
@@ -77,7 +74,6 @@ static CM_NAME(Probe);
 static const struct afs_call_type afs_SRXCBProbe = {
        .name           = afs_SRXCBProbe_name,
        .deliver        = afs_deliver_cb_probe,
-       .abort_to_error = afs_abort_to_error,
        .destructor     = afs_cm_destructor,
        .work           = SRXAFSCB_Probe,
 };
@@ -89,7 +85,6 @@ static CM_NAME(ProbeUuid);
 static const struct afs_call_type afs_SRXCBProbeUuid = {
        .name           = afs_SRXCBProbeUuid_name,
        .deliver        = afs_deliver_cb_probe_uuid,
-       .abort_to_error = afs_abort_to_error,
        .destructor     = afs_cm_destructor,
        .work           = SRXAFSCB_ProbeUuid,
 };
@@ -101,7 +96,6 @@ static CM_NAME(TellMeAboutYourself);
 static const struct afs_call_type afs_SRXCBTellMeAboutYourself = {
        .name           = afs_SRXCBTellMeAboutYourself_name,
        .deliver        = afs_deliver_cb_tell_me_about_yourself,
-       .abort_to_error = afs_abort_to_error,
        .destructor     = afs_cm_destructor,
        .work           = SRXAFSCB_TellMeAboutYourself,
 };
@@ -127,6 +121,9 @@ bool afs_cm_incoming_call(struct afs_call *call)
        case CBProbe:
                call->type = &afs_SRXCBProbe;
                return true;
+       case CBProbeUuid:
+               call->type = &afs_SRXCBProbeUuid;
+               return true;
        case CBTellMeAboutYourself:
                call->type = &afs_SRXCBTellMeAboutYourself;
                return true;
@@ -147,18 +144,16 @@ static void afs_cm_destructor(struct afs_call *call)
         * afs_deliver_cb_callback().
         */
        if (call->unmarshall == 5) {
-               ASSERT(call->server && call->count && call->request);
-               afs_break_callbacks(call->server, call->count, call->request);
+               ASSERT(call->cm_server && call->count && call->request);
+               afs_break_callbacks(call->cm_server, call->count, call->request);
        }
 
-       afs_put_server(call->server);
-       call->server = NULL;
        kfree(call->buffer);
        call->buffer = NULL;
 }
 
 /*
- * allow the fileserver to see if the cache manager is still alive
+ * The server supplied a list of callbacks that it wanted to break.
  */
 static void SRXAFSCB_CallBack(struct work_struct *work)
 {
@@ -173,7 +168,7 @@ static void SRXAFSCB_CallBack(struct work_struct *work)
         * yet */
        afs_send_empty_reply(call);
 
-       afs_break_callbacks(call->server, call->count, call->request);
+       afs_break_callbacks(call->cm_server, call->count, call->request);
        afs_put_call(call);
        _leave("");
 }
@@ -193,7 +188,6 @@ static int afs_deliver_cb_callback(struct afs_call *call)
 
        switch (call->unmarshall) {
        case 0:
-               rxrpc_kernel_get_peer(afs_socket, call->rxcall, &srx);
                call->offset = 0;
                call->unmarshall++;
 
@@ -286,14 +280,16 @@ static int afs_deliver_cb_callback(struct afs_call *call)
                break;
        }
 
-       call->state = AFS_CALL_REPLYING;
+       if (!afs_check_call_state(call, AFS_CALL_SV_REPLYING))
+               return -EIO;
 
        /* we'll need the file server record as that tells us which set of
         * vnodes to operate upon */
-       server = afs_find_server(&srx);
+       rxrpc_kernel_get_peer(call->net->socket, call->rxcall, &srx);
+       server = afs_find_server(call->net, &srx);
        if (!server)
                return -ENOTCONN;
-       call->server = server;
+       call->cm_server = server;
 
        return afs_queue_call_work(call);
 }
@@ -305,9 +301,9 @@ static void SRXAFSCB_InitCallBackState(struct work_struct *work)
 {
        struct afs_call *call = container_of(work, struct afs_call, work);
 
-       _enter("{%p}", call->server);
+       _enter("{%p}", call->cm_server);
 
-       afs_init_callback_state(call->server);
+       afs_init_callback_state(call->cm_server);
        afs_send_empty_reply(call);
        afs_put_call(call);
        _leave("");
@@ -324,21 +320,18 @@ static int afs_deliver_cb_init_call_back_state(struct afs_call *call)
 
        _enter("");
 
-       rxrpc_kernel_get_peer(afs_socket, call->rxcall, &srx);
+       rxrpc_kernel_get_peer(call->net->socket, call->rxcall, &srx);
 
        ret = afs_extract_data(call, NULL, 0, false);
        if (ret < 0)
                return ret;
 
-       /* no unmarshalling required */
-       call->state = AFS_CALL_REPLYING;
-
        /* we'll need the file server record as that tells us which set of
         * vnodes to operate upon */
-       server = afs_find_server(&srx);
+       server = afs_find_server(call->net, &srx);
        if (!server)
                return -ENOTCONN;
-       call->server = server;
+       call->cm_server = server;
 
        return afs_queue_call_work(call);
 }
@@ -357,8 +350,6 @@ static int afs_deliver_cb_init_call_back_state3(struct afs_call *call)
 
        _enter("");
 
-       rxrpc_kernel_get_peer(afs_socket, call->rxcall, &srx);
-
        _enter("{%u}", call->unmarshall);
 
        switch (call->unmarshall) {
@@ -402,15 +393,16 @@ static int afs_deliver_cb_init_call_back_state3(struct afs_call *call)
                break;
        }
 
-       /* no unmarshalling required */
-       call->state = AFS_CALL_REPLYING;
+       if (!afs_check_call_state(call, AFS_CALL_SV_REPLYING))
+               return -EIO;
 
        /* we'll need the file server record as that tells us which set of
         * vnodes to operate upon */
-       server = afs_find_server(&srx);
+       rxrpc_kernel_get_peer(call->net->socket, call->rxcall, &srx);
+       server = afs_find_server(call->net, &srx);
        if (!server)
                return -ENOTCONN;
-       call->server = server;
+       call->cm_server = server;
 
        return afs_queue_call_work(call);
 }
@@ -441,8 +433,8 @@ static int afs_deliver_cb_probe(struct afs_call *call)
        if (ret < 0)
                return ret;
 
-       /* no unmarshalling required */
-       call->state = AFS_CALL_REPLYING;
+       if (!afs_check_call_state(call, AFS_CALL_SV_REPLYING))
+               return -EIO;
 
        return afs_queue_call_work(call);
 }
@@ -461,7 +453,7 @@ static void SRXAFSCB_ProbeUuid(struct work_struct *work)
 
        _enter("");
 
-       if (memcmp(r, &afs_uuid, sizeof(afs_uuid)) == 0)
+       if (memcmp(r, &call->net->uuid, sizeof(call->net->uuid)) == 0)
                reply.match = htonl(0);
        else
                reply.match = htonl(1);
@@ -524,7 +516,8 @@ static int afs_deliver_cb_probe_uuid(struct afs_call *call)
                break;
        }
 
-       call->state = AFS_CALL_REPLYING;
+       if (!afs_check_call_state(call, AFS_CALL_SV_REPLYING))
+               return -EIO;
 
        return afs_queue_call_work(call);
 }
@@ -568,13 +561,13 @@ static void SRXAFSCB_TellMeAboutYourself(struct work_struct *work)
        memset(&reply, 0, sizeof(reply));
        reply.ia.nifs = htonl(nifs);
 
-       reply.ia.uuid[0] = afs_uuid.time_low;
-       reply.ia.uuid[1] = htonl(ntohs(afs_uuid.time_mid));
-       reply.ia.uuid[2] = htonl(ntohs(afs_uuid.time_hi_and_version));
-       reply.ia.uuid[3] = htonl((s8) afs_uuid.clock_seq_hi_and_reserved);
-       reply.ia.uuid[4] = htonl((s8) afs_uuid.clock_seq_low);
+       reply.ia.uuid[0] = call->net->uuid.time_low;
+       reply.ia.uuid[1] = htonl(ntohs(call->net->uuid.time_mid));
+       reply.ia.uuid[2] = htonl(ntohs(call->net->uuid.time_hi_and_version));
+       reply.ia.uuid[3] = htonl((s8) call->net->uuid.clock_seq_hi_and_reserved);
+       reply.ia.uuid[4] = htonl((s8) call->net->uuid.clock_seq_low);
        for (loop = 0; loop < 6; loop++)
-               reply.ia.uuid[loop + 5] = htonl((s8) afs_uuid.node[loop]);
+               reply.ia.uuid[loop + 5] = htonl((s8) call->net->uuid.node[loop]);
 
        if (ifs) {
                for (loop = 0; loop < nifs; loop++) {
@@ -605,8 +598,8 @@ static int afs_deliver_cb_tell_me_about_yourself(struct afs_call *call)
        if (ret < 0)
                return ret;
 
-       /* no unmarshalling required */
-       call->state = AFS_CALL_REPLYING;
+       if (!afs_check_call_state(call, AFS_CALL_SV_REPLYING))
+               return -EIO;
 
        return afs_queue_call_work(call);
 }
index 613a7705826377fa472cbd0b7bc7196071501c37..ab618d32554c648848b6001a3964b279deb794dc 100644 (file)
@@ -130,10 +130,11 @@ struct afs_lookup_cookie {
 /*
  * check that a directory page is valid
  */
-static inline bool afs_dir_check_page(struct inode *dir, struct page *page)
+bool afs_dir_check_page(struct inode *dir, struct page *page)
 {
        struct afs_dir_page *dbuf;
-       loff_t latter;
+       struct afs_vnode *vnode = AFS_FS_I(dir);
+       loff_t latter, i_size, off;
        int tmp, qty;
 
 #if 0
@@ -150,8 +151,15 @@ static inline bool afs_dir_check_page(struct inode *dir, struct page *page)
        }
 #endif
 
-       /* determine how many magic numbers there should be in this page */
-       latter = dir->i_size - page_offset(page);
+       /* Determine how many magic numbers there should be in this page, but
+        * we must take care because the directory may change size under us.
+        */
+       off = page_offset(page);
+       i_size = i_size_read(dir);
+       if (i_size <= off)
+               goto checked;
+
+       latter = i_size - off;
        if (latter >= PAGE_SIZE)
                qty = PAGE_SIZE;
        else
@@ -162,13 +170,15 @@ static inline bool afs_dir_check_page(struct inode *dir, struct page *page)
        dbuf = page_address(page);
        for (tmp = 0; tmp < qty; tmp++) {
                if (dbuf->blocks[tmp].pagehdr.magic != AFS_DIR_MAGIC) {
-                       printk("kAFS: %s(%lu): bad magic %d/%d is %04hx\n",
+                       printk("kAFS: %s(%lx): bad magic %d/%d is %04hx\n",
                               __func__, dir->i_ino, tmp, qty,
                               ntohs(dbuf->blocks[tmp].pagehdr.magic));
+                       trace_afs_dir_check_failed(vnode, off, i_size);
                        goto error;
                }
        }
 
+checked:
        SetPageChecked(page);
        return true;
 
@@ -183,6 +193,7 @@ error:
 static inline void afs_dir_put_page(struct page *page)
 {
        kunmap(page);
+       unlock_page(page);
        put_page(page);
 }
 
@@ -197,9 +208,10 @@ static struct page *afs_dir_get_page(struct inode *dir, unsigned long index,
 
        page = read_cache_page(dir->i_mapping, index, afs_page_filler, key);
        if (!IS_ERR(page)) {
+               lock_page(page);
                kmap(page);
                if (unlikely(!PageChecked(page))) {
-                       if (PageError(page) || !afs_dir_check_page(dir, page))
+                       if (PageError(page))
                                goto fail;
                }
        }
@@ -384,8 +396,7 @@ out:
  */
 static int afs_readdir(struct file *file, struct dir_context *ctx)
 {
-       return afs_dir_iterate(file_inode(file), 
-                             ctx, file->private_data);
+       return afs_dir_iterate(file_inode(file), ctx, afs_file_key(file));
 }
 
 /*
@@ -553,7 +564,7 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
        dentry->d_fsdata = (void *)(unsigned long) vnode->status.data_version;
 
        /* instantiate the dentry */
-       inode = afs_iget(dir->i_sb, key, &fid, NULL, NULL);
+       inode = afs_iget(dir->i_sb, key, &fid, NULL, NULL, NULL);
        key_put(key);
        if (IS_ERR(inode)) {
                _leave(" = %ld", PTR_ERR(inode));
@@ -581,6 +592,7 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
        struct afs_vnode *vnode, *dir;
        struct afs_fid uninitialized_var(fid);
        struct dentry *parent;
+       struct inode *inode;
        struct key *key;
        void *dir_version;
        int ret;
@@ -588,30 +600,39 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
        if (flags & LOOKUP_RCU)
                return -ECHILD;
 
-       vnode = AFS_FS_I(d_inode(dentry));
-
-       if (d_really_is_positive(dentry))
+       if (d_really_is_positive(dentry)) {
+               vnode = AFS_FS_I(d_inode(dentry));
                _enter("{v={%x:%u} n=%pd fl=%lx},",
                       vnode->fid.vid, vnode->fid.vnode, dentry,
                       vnode->flags);
-       else
+       } else {
                _enter("{neg n=%pd}", dentry);
+       }
 
        key = afs_request_key(AFS_FS_S(dentry->d_sb)->volume->cell);
        if (IS_ERR(key))
                key = NULL;
 
+       if (d_really_is_positive(dentry)) {
+               inode = d_inode(dentry);
+               if (inode) {
+                       vnode = AFS_FS_I(inode);
+                       afs_validate(vnode, key);
+                       if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
+                               goto out_bad;
+               }
+       }
+
        /* lock down the parent dentry so we can peer at it */
        parent = dget_parent(dentry);
        dir = AFS_FS_I(d_inode(parent));
 
        /* validate the parent directory */
-       if (test_bit(AFS_VNODE_MODIFIED, &dir->flags))
-               afs_validate(dir, key);
+       afs_validate(dir, key);
 
        if (test_bit(AFS_VNODE_DELETED, &dir->flags)) {
                _debug("%pd: parent dir deleted", dentry);
-               goto out_bad;
+               goto out_bad_parent;
        }
 
        dir_version = (void *) (unsigned long) dir->status.data_version;
@@ -626,13 +647,16 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
        case 0:
                /* the filename maps to something */
                if (d_really_is_negative(dentry))
-                       goto out_bad;
-               if (is_bad_inode(d_inode(dentry))) {
+                       goto out_bad_parent;
+               inode = d_inode(dentry);
+               if (is_bad_inode(inode)) {
                        printk("kAFS: afs_d_revalidate: %pd2 has bad inode\n",
                               dentry);
-                       goto out_bad;
+                       goto out_bad_parent;
                }
 
+               vnode = AFS_FS_I(inode);
+
                /* if the vnode ID has changed, then the dirent points to a
                 * different file */
                if (fid.vnode != vnode->fid.vnode) {
@@ -649,10 +673,10 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
                        _debug("%pd: file deleted (uq %u -> %u I:%u)",
                               dentry, fid.unique,
                               vnode->fid.unique,
-                              d_inode(dentry)->i_generation);
-                       spin_lock(&vnode->lock);
+                              vnode->vfs_inode.i_generation);
+                       write_seqlock(&vnode->cb_lock);
                        set_bit(AFS_VNODE_DELETED, &vnode->flags);
-                       spin_unlock(&vnode->lock);
+                       write_sequnlock(&vnode->cb_lock);
                        goto not_found;
                }
                goto out_valid;
@@ -667,7 +691,7 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
        default:
                _debug("failed to iterate dir %pd: %d",
                       parent, ret);
-               goto out_bad;
+               goto out_bad_parent;
        }
 
 out_valid:
@@ -683,9 +707,10 @@ not_found:
        dentry->d_flags |= DCACHE_NFSFS_RENAMED;
        spin_unlock(&dentry->d_lock);
 
-out_bad:
+out_bad_parent:
        _debug("dropping dentry %pd2", dentry);
        dput(parent);
+out_bad:
        key_put(key);
 
        _leave(" = 0 [bad]");
@@ -726,21 +751,49 @@ static void afs_d_release(struct dentry *dentry)
        _enter("%pd", dentry);
 }
 
+/*
+ * Create a new inode for create/mkdir/symlink
+ */
+static void afs_vnode_new_inode(struct afs_fs_cursor *fc,
+                               struct dentry *new_dentry,
+                               struct afs_fid *newfid,
+                               struct afs_file_status *newstatus,
+                               struct afs_callback *newcb)
+{
+       struct inode *inode;
+
+       if (fc->ac.error < 0)
+               return;
+
+       inode = afs_iget(fc->vnode->vfs_inode.i_sb, fc->key,
+                        newfid, newstatus, newcb, fc->cbi);
+       if (IS_ERR(inode)) {
+               /* ENOMEM or EINTR at a really inconvenient time - just abandon
+                * the new directory on the server.
+                */
+               fc->ac.error = PTR_ERR(inode);
+               return;
+       }
+
+       d_instantiate(new_dentry, inode);
+       if (d_unhashed(new_dentry))
+               d_rehash(new_dentry);
+}
+
 /*
  * create a directory on an AFS filesystem
  */
 static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
 {
-       struct afs_file_status status;
-       struct afs_callback cb;
-       struct afs_server *server;
-       struct afs_vnode *dvnode, *vnode;
-       struct afs_fid fid;
-       struct inode *inode;
+       struct afs_file_status newstatus;
+       struct afs_fs_cursor fc;
+       struct afs_callback newcb;
+       struct afs_vnode *dvnode = AFS_FS_I(dir);
+       struct afs_fid newfid;
        struct key *key;
        int ret;
 
-       dvnode = AFS_FS_I(dir);
+       mode |= S_IFDIR;
 
        _enter("{%x:%u},{%pd},%ho",
               dvnode->fid.vid, dvnode->fid.vnode, dentry, mode);
@@ -751,40 +804,27 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
                goto error;
        }
 
-       mode |= S_IFDIR;
-       ret = afs_vnode_create(dvnode, key, dentry->d_name.name,
-                              mode, &fid, &status, &cb, &server);
-       if (ret < 0)
-               goto mkdir_error;
+       ret = -ERESTARTSYS;
+       if (afs_begin_vnode_operation(&fc, dvnode, key)) {
+               while (afs_select_fileserver(&fc)) {
+                       fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
+                       afs_fs_create(&fc, dentry->d_name.name, mode,
+                                     &newfid, &newstatus, &newcb);
+               }
 
-       inode = afs_iget(dir->i_sb, key, &fid, &status, &cb);
-       if (IS_ERR(inode)) {
-               /* ENOMEM at a really inconvenient time - just abandon the new
-                * directory on the server */
-               ret = PTR_ERR(inode);
-               goto iget_error;
+               afs_check_for_remote_deletion(&fc, fc.vnode);
+               afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
+               afs_vnode_new_inode(&fc, dentry, &newfid, &newstatus, &newcb);
+               ret = afs_end_vnode_operation(&fc);
+               if (ret < 0)
+                       goto error_key;
        }
 
-       /* apply the status report we've got for the new vnode */
-       vnode = AFS_FS_I(inode);
-       spin_lock(&vnode->lock);
-       vnode->update_cnt++;
-       spin_unlock(&vnode->lock);
-       afs_vnode_finalise_status_update(vnode, server);
-       afs_put_server(server);
-
-       d_instantiate(dentry, inode);
-       if (d_unhashed(dentry)) {
-               _debug("not hashed");
-               d_rehash(dentry);
-       }
        key_put(key);
        _leave(" = 0");
        return 0;
 
-iget_error:
-       afs_put_server(server);
-mkdir_error:
+error_key:
        key_put(key);
 error:
        d_drop(dentry);
@@ -792,17 +832,30 @@ error:
        return ret;
 }
 
+/*
+ * Remove a subdir from a directory.
+ */
+static void afs_dir_remove_subdir(struct dentry *dentry)
+{
+       if (d_really_is_positive(dentry)) {
+               struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry));
+
+               clear_nlink(&vnode->vfs_inode);
+               set_bit(AFS_VNODE_DELETED, &vnode->flags);
+               clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
+       }
+}
+
 /*
  * remove a directory from an AFS filesystem
  */
 static int afs_rmdir(struct inode *dir, struct dentry *dentry)
 {
-       struct afs_vnode *dvnode, *vnode;
+       struct afs_fs_cursor fc;
+       struct afs_vnode *dvnode = AFS_FS_I(dir);
        struct key *key;
        int ret;
 
-       dvnode = AFS_FS_I(dir);
-
        _enter("{%x:%u},{%pd}",
               dvnode->fid.vid, dvnode->fid.vnode, dentry);
 
@@ -812,45 +865,69 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
                goto error;
        }
 
-       ret = afs_vnode_remove(dvnode, key, dentry->d_name.name, true);
-       if (ret < 0)
-               goto rmdir_error;
+       ret = -ERESTARTSYS;
+       if (afs_begin_vnode_operation(&fc, dvnode, key)) {
+               while (afs_select_fileserver(&fc)) {
+                       fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
+                       afs_fs_remove(&fc, dentry->d_name.name, true);
+               }
 
-       if (d_really_is_positive(dentry)) {
-               vnode = AFS_FS_I(d_inode(dentry));
-               clear_nlink(&vnode->vfs_inode);
-               set_bit(AFS_VNODE_DELETED, &vnode->flags);
-               afs_discard_callback_on_delete(vnode);
+               afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
+               ret = afs_end_vnode_operation(&fc);
+               if (ret == 0)
+                       afs_dir_remove_subdir(dentry);
        }
 
        key_put(key);
-       _leave(" = 0");
-       return 0;
-
-rmdir_error:
-       key_put(key);
 error:
-       _leave(" = %d", ret);
        return ret;
 }
 
 /*
- * remove a file from an AFS filesystem
+ * Remove a link to a file or symlink from a directory.
+ *
+ * If the file was not deleted due to excess hard links, the fileserver will
+ * break the callback promise on the file - if it had one - before it returns
+ * to us, and if it was deleted, it won't
+ *
+ * However, if we didn't have a callback promise outstanding, or it was
+ * outstanding on a different server, then it won't break it either...
+ */
+static int afs_dir_remove_link(struct dentry *dentry, struct key *key)
+{
+       int ret = 0;
+
+       if (d_really_is_positive(dentry)) {
+               struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry));
+
+               if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
+                       kdebug("AFS_VNODE_DELETED");
+               clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
+
+               ret = afs_validate(vnode, key);
+               if (ret == -ESTALE)
+                       ret = 0;
+               _debug("nlink %d [val %d]", vnode->vfs_inode.i_nlink, ret);
+       }
+
+       return ret;
+}
+
+/*
+ * Remove a file or symlink from an AFS filesystem.
  */
 static int afs_unlink(struct inode *dir, struct dentry *dentry)
 {
-       struct afs_vnode *dvnode, *vnode;
+       struct afs_fs_cursor fc;
+       struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode;
        struct key *key;
        int ret;
 
-       dvnode = AFS_FS_I(dir);
-
        _enter("{%x:%u},{%pd}",
               dvnode->fid.vid, dvnode->fid.vnode, dentry);
 
-       ret = -ENAMETOOLONG;
        if (dentry->d_name.len >= AFSNAMEMAX)
-               goto error;
+               return -ENAMETOOLONG;
 
        key = afs_request_key(dvnode->volume->cell);
        if (IS_ERR(key)) {
@@ -858,44 +935,28 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
                goto error;
        }
 
+       /* Try to make sure we have a callback promise on the victim. */
        if (d_really_is_positive(dentry)) {
                vnode = AFS_FS_I(d_inode(dentry));
-
-               /* make sure we have a callback promise on the victim */
                ret = afs_validate(vnode, key);
                if (ret < 0)
-                       goto error;
+                       goto error_key;
        }
 
-       ret = afs_vnode_remove(dvnode, key, dentry->d_name.name, false);
-       if (ret < 0)
-               goto remove_error;
+       ret = -ERESTARTSYS;
+       if (afs_begin_vnode_operation(&fc, dvnode, key)) {
+               while (afs_select_fileserver(&fc)) {
+                       fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
+                       afs_fs_remove(&fc, dentry->d_name.name, false);
+               }
 
-       if (d_really_is_positive(dentry)) {
-               /* if the file wasn't deleted due to excess hard links, the
-                * fileserver will break the callback promise on the file - if
-                * it had one - before it returns to us, and if it was deleted,
-                * it won't
-                *
-                * however, if we didn't have a callback promise outstanding,
-                * or it was outstanding on a different server, then it won't
-                * break it either...
-                */
-               vnode = AFS_FS_I(d_inode(dentry));
-               if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
-                       _debug("AFS_VNODE_DELETED");
-               if (test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags))
-                       _debug("AFS_VNODE_CB_BROKEN");
-               set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
-               ret = afs_validate(vnode, key);
-               _debug("nlink %d [val %d]", vnode->vfs_inode.i_nlink, ret);
+               afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
+               ret = afs_end_vnode_operation(&fc);
+               if (ret == 0)
+                       ret = afs_dir_remove_link(dentry, key);
        }
 
-       key_put(key);
-       _leave(" = 0");
-       return 0;
-
-remove_error:
+error_key:
        key_put(key);
 error:
        _leave(" = %d", ret);
@@ -908,60 +969,50 @@ error:
 static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
                      bool excl)
 {
-       struct afs_file_status status;
-       struct afs_callback cb;
-       struct afs_server *server;
-       struct afs_vnode *dvnode, *vnode;
-       struct afs_fid fid;
-       struct inode *inode;
+       struct afs_fs_cursor fc;
+       struct afs_file_status newstatus;
+       struct afs_callback newcb;
+       struct afs_vnode *dvnode = dvnode = AFS_FS_I(dir);
+       struct afs_fid newfid;
        struct key *key;
        int ret;
 
-       dvnode = AFS_FS_I(dir);
+       mode |= S_IFREG;
 
        _enter("{%x:%u},{%pd},%ho,",
               dvnode->fid.vid, dvnode->fid.vnode, dentry, mode);
 
+       ret = -ENAMETOOLONG;
+       if (dentry->d_name.len >= AFSNAMEMAX)
+               goto error;
+
        key = afs_request_key(dvnode->volume->cell);
        if (IS_ERR(key)) {
                ret = PTR_ERR(key);
                goto error;
        }
 
-       mode |= S_IFREG;
-       ret = afs_vnode_create(dvnode, key, dentry->d_name.name,
-                              mode, &fid, &status, &cb, &server);
-       if (ret < 0)
-               goto create_error;
+       ret = -ERESTARTSYS;
+       if (afs_begin_vnode_operation(&fc, dvnode, key)) {
+               while (afs_select_fileserver(&fc)) {
+                       fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
+                       afs_fs_create(&fc, dentry->d_name.name, mode,
+                                     &newfid, &newstatus, &newcb);
+               }
 
-       inode = afs_iget(dir->i_sb, key, &fid, &status, &cb);
-       if (IS_ERR(inode)) {
-               /* ENOMEM at a really inconvenient time - just abandon the new
-                * directory on the server */
-               ret = PTR_ERR(inode);
-               goto iget_error;
+               afs_check_for_remote_deletion(&fc, fc.vnode);
+               afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
+               afs_vnode_new_inode(&fc, dentry, &newfid, &newstatus, &newcb);
+               ret = afs_end_vnode_operation(&fc);
+               if (ret < 0)
+                       goto error_key;
        }
 
-       /* apply the status report we've got for the new vnode */
-       vnode = AFS_FS_I(inode);
-       spin_lock(&vnode->lock);
-       vnode->update_cnt++;
-       spin_unlock(&vnode->lock);
-       afs_vnode_finalise_status_update(vnode, server);
-       afs_put_server(server);
-
-       d_instantiate(dentry, inode);
-       if (d_unhashed(dentry)) {
-               _debug("not hashed");
-               d_rehash(dentry);
-       }
        key_put(key);
        _leave(" = 0");
        return 0;
 
-iget_error:
-       afs_put_server(server);
-create_error:
+error_key:
        key_put(key);
 error:
        d_drop(dentry);
@@ -975,6 +1026,7 @@ error:
 static int afs_link(struct dentry *from, struct inode *dir,
                    struct dentry *dentry)
 {
+       struct afs_fs_cursor fc;
        struct afs_vnode *dvnode, *vnode;
        struct key *key;
        int ret;
@@ -987,23 +1039,45 @@ static int afs_link(struct dentry *from, struct inode *dir,
               dvnode->fid.vid, dvnode->fid.vnode,
               dentry);
 
+       ret = -ENAMETOOLONG;
+       if (dentry->d_name.len >= AFSNAMEMAX)
+               goto error;
+
        key = afs_request_key(dvnode->volume->cell);
        if (IS_ERR(key)) {
                ret = PTR_ERR(key);
                goto error;
        }
 
-       ret = afs_vnode_link(dvnode, vnode, key, dentry->d_name.name);
-       if (ret < 0)
-               goto link_error;
+       ret = -ERESTARTSYS;
+       if (afs_begin_vnode_operation(&fc, dvnode, key)) {
+               if (mutex_lock_interruptible_nested(&vnode->io_lock, 1) < 0) {
+                       afs_end_vnode_operation(&fc);
+                       return -ERESTARTSYS;
+               }
+
+               while (afs_select_fileserver(&fc)) {
+                       fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
+                       fc.cb_break_2 = vnode->cb_break + vnode->cb_s_break;
+                       afs_fs_link(&fc, vnode, dentry->d_name.name);
+               }
+
+               afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
+               afs_vnode_commit_status(&fc, vnode, fc.cb_break_2);
+               ihold(&vnode->vfs_inode);
+               d_instantiate(dentry, &vnode->vfs_inode);
+
+               mutex_unlock(&vnode->io_lock);
+               ret = afs_end_vnode_operation(&fc);
+               if (ret < 0)
+                       goto error_key;
+       }
 
-       ihold(&vnode->vfs_inode);
-       d_instantiate(dentry, &vnode->vfs_inode);
        key_put(key);
        _leave(" = 0");
        return 0;
 
-link_error:
+error_key:
        key_put(key);
 error:
        d_drop(dentry);
@@ -1017,20 +1091,21 @@ error:
 static int afs_symlink(struct inode *dir, struct dentry *dentry,
                       const char *content)
 {
-       struct afs_file_status status;
-       struct afs_server *server;
-       struct afs_vnode *dvnode, *vnode;
-       struct afs_fid fid;
-       struct inode *inode;
+       struct afs_fs_cursor fc;
+       struct afs_file_status newstatus;
+       struct afs_vnode *dvnode = AFS_FS_I(dir);
+       struct afs_fid newfid;
        struct key *key;
        int ret;
 
-       dvnode = AFS_FS_I(dir);
-
        _enter("{%x:%u},{%pd},%s",
               dvnode->fid.vid, dvnode->fid.vnode, dentry,
               content);
 
+       ret = -ENAMETOOLONG;
+       if (dentry->d_name.len >= AFSNAMEMAX)
+               goto error;
+
        ret = -EINVAL;
        if (strlen(content) >= AFSPATHMAX)
                goto error;
@@ -1041,39 +1116,27 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
                goto error;
        }
 
-       ret = afs_vnode_symlink(dvnode, key, dentry->d_name.name, content,
-                               &fid, &status, &server);
-       if (ret < 0)
-               goto create_error;
+       ret = -ERESTARTSYS;
+       if (afs_begin_vnode_operation(&fc, dvnode, key)) {
+               while (afs_select_fileserver(&fc)) {
+                       fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
+                       afs_fs_symlink(&fc, dentry->d_name.name, content,
+                                      &newfid, &newstatus);
+               }
 
-       inode = afs_iget(dir->i_sb, key, &fid, &status, NULL);
-       if (IS_ERR(inode)) {
-               /* ENOMEM at a really inconvenient time - just abandon the new
-                * directory on the server */
-               ret = PTR_ERR(inode);
-               goto iget_error;
+               afs_check_for_remote_deletion(&fc, fc.vnode);
+               afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
+               afs_vnode_new_inode(&fc, dentry, &newfid, &newstatus, NULL);
+               ret = afs_end_vnode_operation(&fc);
+               if (ret < 0)
+                       goto error_key;
        }
 
-       /* apply the status report we've got for the new vnode */
-       vnode = AFS_FS_I(inode);
-       spin_lock(&vnode->lock);
-       vnode->update_cnt++;
-       spin_unlock(&vnode->lock);
-       afs_vnode_finalise_status_update(vnode, server);
-       afs_put_server(server);
-
-       d_instantiate(dentry, inode);
-       if (d_unhashed(dentry)) {
-               _debug("not hashed");
-               d_rehash(dentry);
-       }
        key_put(key);
        _leave(" = 0");
        return 0;
 
-iget_error:
-       afs_put_server(server);
-create_error:
+error_key:
        key_put(key);
 error:
        d_drop(dentry);
@@ -1088,6 +1151,7 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
                      struct inode *new_dir, struct dentry *new_dentry,
                      unsigned int flags)
 {
+       struct afs_fs_cursor fc;
        struct afs_vnode *orig_dvnode, *new_dvnode, *vnode;
        struct key *key;
        int ret;
@@ -1111,16 +1175,35 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
                goto error;
        }
 
-       ret = afs_vnode_rename(orig_dvnode, new_dvnode, key,
-                              old_dentry->d_name.name,
-                              new_dentry->d_name.name);
-       if (ret < 0)
-               goto rename_error;
+       ret = -ERESTARTSYS;
+       if (afs_begin_vnode_operation(&fc, orig_dvnode, key)) {
+               if (orig_dvnode != new_dvnode) {
+                       if (mutex_lock_interruptible_nested(&new_dvnode->io_lock, 1) < 0) {
+                               afs_end_vnode_operation(&fc);
+                               return -ERESTARTSYS;
+                       }
+               }
+               while (afs_select_fileserver(&fc)) {
+                       fc.cb_break = orig_dvnode->cb_break + orig_dvnode->cb_s_break;
+                       fc.cb_break_2 = new_dvnode->cb_break + new_dvnode->cb_s_break;
+                       afs_fs_rename(&fc, old_dentry->d_name.name,
+                                     new_dvnode, new_dentry->d_name.name);
+               }
+
+               afs_vnode_commit_status(&fc, orig_dvnode, fc.cb_break);
+               afs_vnode_commit_status(&fc, new_dvnode, fc.cb_break_2);
+               if (orig_dvnode != new_dvnode)
+                       mutex_unlock(&new_dvnode->io_lock);
+               ret = afs_end_vnode_operation(&fc);
+               if (ret < 0)
+                       goto error_key;
+       }
+
        key_put(key);
        _leave(" = 0");
        return 0;
 
-rename_error:
+error_key:
        key_put(key);
 error:
        d_drop(new_dentry);
index 510cba15fa560549de1227be97baa0770d783762..a39192ced99e6c35449ec72c321f3ae687aa142d 100644 (file)
 #include <linux/task_io_accounting_ops.h>
 #include "internal.h"
 
+static int afs_file_mmap(struct file *file, struct vm_area_struct *vma);
 static int afs_readpage(struct file *file, struct page *page);
 static void afs_invalidatepage(struct page *page, unsigned int offset,
                               unsigned int length);
 static int afs_releasepage(struct page *page, gfp_t gfp_flags);
-static int afs_launder_page(struct page *page);
 
 static int afs_readpages(struct file *filp, struct address_space *mapping,
                         struct list_head *pages, unsigned nr_pages);
@@ -35,7 +35,7 @@ const struct file_operations afs_file_operations = {
        .llseek         = generic_file_llseek,
        .read_iter      = generic_file_read_iter,
        .write_iter     = afs_file_write,
-       .mmap           = generic_file_readonly_mmap,
+       .mmap           = afs_file_mmap,
        .splice_read    = generic_file_splice_read,
        .fsync          = afs_fsync,
        .lock           = afs_lock,
@@ -62,12 +62,63 @@ const struct address_space_operations afs_fs_aops = {
        .writepages     = afs_writepages,
 };
 
+static const struct vm_operations_struct afs_vm_ops = {
+       .fault          = filemap_fault,
+       .map_pages      = filemap_map_pages,
+       .page_mkwrite   = afs_page_mkwrite,
+};
+
+/*
+ * Discard a pin on a writeback key.
+ */
+void afs_put_wb_key(struct afs_wb_key *wbk)
+{
+       if (refcount_dec_and_test(&wbk->usage)) {
+               key_put(wbk->key);
+               kfree(wbk);
+       }
+}
+
+/*
+ * Cache key for writeback.
+ */
+int afs_cache_wb_key(struct afs_vnode *vnode, struct afs_file *af)
+{
+       struct afs_wb_key *wbk, *p;
+
+       wbk = kzalloc(sizeof(struct afs_wb_key), GFP_KERNEL);
+       if (!wbk)
+               return -ENOMEM;
+       refcount_set(&wbk->usage, 2);
+       wbk->key = af->key;
+
+       spin_lock(&vnode->wb_lock);
+       list_for_each_entry(p, &vnode->wb_keys, vnode_link) {
+               if (p->key == wbk->key)
+                       goto found;
+       }
+
+       key_get(wbk->key);
+       list_add_tail(&wbk->vnode_link, &vnode->wb_keys);
+       spin_unlock(&vnode->wb_lock);
+       af->wb = wbk;
+       return 0;
+
+found:
+       refcount_inc(&p->usage);
+       spin_unlock(&vnode->wb_lock);
+       af->wb = p;
+       kfree(wbk);
+       return 0;
+}
+
 /*
  * open an AFS file or directory and attach a key to it
  */
 int afs_open(struct inode *inode, struct file *file)
 {
        struct afs_vnode *vnode = AFS_FS_I(inode);
+       struct afs_file *af;
        struct key *key;
        int ret;
 
@@ -75,19 +126,38 @@ int afs_open(struct inode *inode, struct file *file)
 
        key = afs_request_key(vnode->volume->cell);
        if (IS_ERR(key)) {
-               _leave(" = %ld [key]", PTR_ERR(key));
-               return PTR_ERR(key);
+               ret = PTR_ERR(key);
+               goto error;
        }
 
-       ret = afs_validate(vnode, key);
-       if (ret < 0) {
-               _leave(" = %d [val]", ret);
-               return ret;
+       af = kzalloc(sizeof(*af), GFP_KERNEL);
+       if (!af) {
+               ret = -ENOMEM;
+               goto error_key;
        }
+       af->key = key;
+
+       ret = afs_validate(vnode, key);
+       if (ret < 0)
+               goto error_af;
 
-       file->private_data = key;
+       if (file->f_mode & FMODE_WRITE) {
+               ret = afs_cache_wb_key(vnode, af);
+               if (ret < 0)
+                       goto error_af;
+       }
+       
+       file->private_data = af;
        _leave(" = 0");
        return 0;
+
+error_af:
+       kfree(af);
+error_key:
+       key_put(key);
+error:
+       _leave(" = %d", ret);
+       return ret;
 }
 
 /*
@@ -96,10 +166,16 @@ int afs_open(struct inode *inode, struct file *file)
 int afs_release(struct inode *inode, struct file *file)
 {
        struct afs_vnode *vnode = AFS_FS_I(inode);
+       struct afs_file *af = file->private_data;
 
        _enter("{%x:%u},", vnode->fid.vid, vnode->fid.vnode);
 
-       key_put(file->private_data);
+       file->private_data = NULL;
+       if (af->wb)
+               afs_put_wb_key(af->wb);
+       key_put(af->key);
+       kfree(af);
+       afs_prune_wb_keys(vnode);
        _leave(" = 0");
        return 0;
 }
@@ -137,6 +213,37 @@ static void afs_file_readpage_read_complete(struct page *page,
 }
 #endif
 
+/*
+ * Fetch file data from the volume.
+ */
+int afs_fetch_data(struct afs_vnode *vnode, struct key *key, struct afs_read *desc)
+{
+       struct afs_fs_cursor fc;
+       int ret;
+
+       _enter("%s{%x:%u.%u},%x,,,",
+              vnode->volume->name,
+              vnode->fid.vid,
+              vnode->fid.vnode,
+              vnode->fid.unique,
+              key_serial(key));
+
+       ret = -ERESTARTSYS;
+       if (afs_begin_vnode_operation(&fc, vnode, key)) {
+               while (afs_select_fileserver(&fc)) {
+                       fc.cb_break = vnode->cb_break + vnode->cb_s_break;
+                       afs_fs_fetch_data(&fc, desc);
+               }
+
+               afs_check_for_remote_deletion(&fc, fc.vnode);
+               afs_vnode_commit_status(&fc, vnode, fc.cb_break);
+               ret = afs_end_vnode_operation(&fc);
+       }
+
+       _leave(" = %d", ret);
+       return ret;
+}
+
 /*
  * read page from file, directory or symlink, given a key to use
  */
@@ -199,8 +306,13 @@ int afs_page_filler(void *data, struct page *page)
 
                /* read the contents of the file from the server into the
                 * page */
-               ret = afs_vnode_fetch_data(vnode, key, req);
+               ret = afs_fetch_data(vnode, key, req);
                afs_put_read(req);
+
+               if (ret >= 0 && S_ISDIR(inode->i_mode) &&
+                   !afs_dir_check_page(inode, page))
+                       ret = -EIO;
+
                if (ret < 0) {
                        if (ret == -ENOENT) {
                                _debug("got NOENT from server"
@@ -259,12 +371,12 @@ static int afs_readpage(struct file *file, struct page *page)
        int ret;
 
        if (file) {
-               key = file->private_data;
+               key = afs_file_key(file);
                ASSERT(key != NULL);
                ret = afs_page_filler(key, page);
        } else {
                struct inode *inode = page->mapping->host;
-               key = afs_request_key(AFS_FS_S(inode->i_sb)->volume->cell);
+               key = afs_request_key(AFS_FS_S(inode->i_sb)->cell);
                if (IS_ERR(key)) {
                        ret = PTR_ERR(key);
                } else {
@@ -281,7 +393,7 @@ static int afs_readpage(struct file *file, struct page *page)
 static void afs_readpages_page_done(struct afs_call *call, struct afs_read *req)
 {
 #ifdef CONFIG_AFS_FSCACHE
-       struct afs_vnode *vnode = call->reply;
+       struct afs_vnode *vnode = call->reply[0];
 #endif
        struct page *page = req->pages[req->index];
 
@@ -310,7 +422,7 @@ static int afs_readpages_one(struct file *file, struct address_space *mapping,
        struct afs_read *req;
        struct list_head *p;
        struct page *first, *page;
-       struct key *key = file->private_data;
+       struct key *key = afs_file_key(file);
        pgoff_t index;
        int ret, n, i;
 
@@ -369,7 +481,7 @@ static int afs_readpages_one(struct file *file, struct address_space *mapping,
                return 0;
        }
 
-       ret = afs_vnode_fetch_data(vnode, key, req);
+       ret = afs_fetch_data(vnode, key, req);
        if (ret < 0)
                goto error;
 
@@ -406,7 +518,7 @@ error:
 static int afs_readpages(struct file *file, struct address_space *mapping,
                         struct list_head *pages, unsigned nr_pages)
 {
-       struct key *key = file->private_data;
+       struct key *key = afs_file_key(file);
        struct afs_vnode *vnode;
        int ret = 0;
 
@@ -463,16 +575,6 @@ static int afs_readpages(struct file *file, struct address_space *mapping,
        return ret;
 }
 
-/*
- * write back a dirty page
- */
-static int afs_launder_page(struct page *page)
-{
-       _enter("{%lu}", page->index);
-
-       return 0;
-}
-
 /*
  * invalidate part or all of a page
  * - release a page and clean up its private data if offset is 0 (indicating
@@ -481,7 +583,8 @@ static int afs_launder_page(struct page *page)
 static void afs_invalidatepage(struct page *page, unsigned int offset,
                               unsigned int length)
 {
-       struct afs_writeback *wb = (struct afs_writeback *) page_private(page);
+       struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
+       unsigned long priv;
 
        _enter("{%lu},%u,%u", page->index, offset, length);
 
@@ -498,13 +601,11 @@ static void afs_invalidatepage(struct page *page, unsigned int offset,
 #endif
 
                if (PagePrivate(page)) {
-                       if (wb && !PageWriteback(page)) {
-                               set_page_private(page, 0);
-                               afs_put_writeback(wb);
-                       }
-
-                       if (!page_private(page))
-                               ClearPagePrivate(page);
+                       priv = page_private(page);
+                       trace_afs_page_dirty(vnode, tracepoint_string("inval"),
+                                            page->index, priv);
+                       set_page_private(page, 0);
+                       ClearPagePrivate(page);
                }
        }
 
@@ -517,8 +618,8 @@ static void afs_invalidatepage(struct page *page, unsigned int offset,
  */
 static int afs_releasepage(struct page *page, gfp_t gfp_flags)
 {
-       struct afs_writeback *wb = (struct afs_writeback *) page_private(page);
        struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
+       unsigned long priv;
 
        _enter("{{%x:%u}[%lu],%lx},%x",
               vnode->fid.vid, vnode->fid.vnode, page->index, page->flags,
@@ -534,10 +635,10 @@ static int afs_releasepage(struct page *page, gfp_t gfp_flags)
 #endif
 
        if (PagePrivate(page)) {
-               if (wb) {
-                       set_page_private(page, 0);
-                       afs_put_writeback(wb);
-               }
+               priv = page_private(page);
+               trace_afs_page_dirty(vnode, tracepoint_string("rel"),
+                                    page->index, priv);
+               set_page_private(page, 0);
                ClearPagePrivate(page);
        }
 
@@ -545,3 +646,16 @@ static int afs_releasepage(struct page *page, gfp_t gfp_flags)
        _leave(" = T");
        return 1;
 }
+
+/*
+ * Handle setting up a memory mapping on an AFS file.
+ */
+static int afs_file_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       int ret;
+
+       ret = generic_file_mmap(file, vma);
+       if (ret == 0)
+               vma->vm_ops = &afs_vm_ops;
+       return ret;
+}
index 3191dff2c1569f83fa63ee1761fa7931d6d771ca..7571a5dfd5a35cbd674ddf24671029a8b6b522d1 100644 (file)
 #define AFS_LOCK_GRANTED       0
 #define AFS_LOCK_PENDING       1
 
+struct workqueue_struct *afs_lock_manager;
+
 static void afs_fl_copy_lock(struct file_lock *new, struct file_lock *fl);
 static void afs_fl_release_private(struct file_lock *fl);
 
-static struct workqueue_struct *afs_lock_manager;
-static DEFINE_MUTEX(afs_lock_manager_mutex);
-
 static const struct file_lock_operations afs_lock_ops = {
        .fl_copy_lock           = afs_fl_copy_lock,
        .fl_release_private     = afs_fl_release_private,
 };
 
-/*
- * initialise the lock manager thread if it isn't already running
- */
-static int afs_init_lock_manager(void)
-{
-       int ret;
-
-       ret = 0;
-       if (!afs_lock_manager) {
-               mutex_lock(&afs_lock_manager_mutex);
-               if (!afs_lock_manager) {
-                       afs_lock_manager = alloc_workqueue("kafs_lockd",
-                                                          WQ_MEM_RECLAIM, 0);
-                       if (!afs_lock_manager)
-                               ret = -ENOMEM;
-               }
-               mutex_unlock(&afs_lock_manager_mutex);
-       }
-       return ret;
-}
-
-/*
- * destroy the lock manager thread if it's running
- */
-void __exit afs_kill_lock_manager(void)
-{
-       if (afs_lock_manager)
-               destroy_workqueue(afs_lock_manager);
-}
-
 /*
  * if the callback is broken on this vnode, then the lock may now be available
  */
@@ -98,6 +67,100 @@ static void afs_grant_locks(struct afs_vnode *vnode, struct file_lock *fl)
        }
 }
 
+/*
+ * Get a lock on a file
+ */
+static int afs_set_lock(struct afs_vnode *vnode, struct key *key,
+                       afs_lock_type_t type)
+{
+       struct afs_fs_cursor fc;
+       int ret;
+
+       _enter("%s{%x:%u.%u},%x,%u",
+              vnode->volume->name,
+              vnode->fid.vid,
+              vnode->fid.vnode,
+              vnode->fid.unique,
+              key_serial(key), type);
+
+       ret = -ERESTARTSYS;
+       if (afs_begin_vnode_operation(&fc, vnode, key)) {
+               while (afs_select_fileserver(&fc)) {
+                       fc.cb_break = vnode->cb_break + vnode->cb_s_break;
+                       afs_fs_set_lock(&fc, type);
+               }
+
+               afs_check_for_remote_deletion(&fc, fc.vnode);
+               afs_vnode_commit_status(&fc, vnode, fc.cb_break);
+               ret = afs_end_vnode_operation(&fc);
+       }
+
+       _leave(" = %d", ret);
+       return ret;
+}
+
+/*
+ * Extend a lock on a file
+ */
+static int afs_extend_lock(struct afs_vnode *vnode, struct key *key)
+{
+       struct afs_fs_cursor fc;
+       int ret;
+
+       _enter("%s{%x:%u.%u},%x",
+              vnode->volume->name,
+              vnode->fid.vid,
+              vnode->fid.vnode,
+              vnode->fid.unique,
+              key_serial(key));
+
+       ret = -ERESTARTSYS;
+       if (afs_begin_vnode_operation(&fc, vnode, key)) {
+               while (afs_select_current_fileserver(&fc)) {
+                       fc.cb_break = vnode->cb_break + vnode->cb_s_break;
+                       afs_fs_extend_lock(&fc);
+               }
+
+               afs_check_for_remote_deletion(&fc, fc.vnode);
+               afs_vnode_commit_status(&fc, vnode, fc.cb_break);
+               ret = afs_end_vnode_operation(&fc);
+       }
+
+       _leave(" = %d", ret);
+       return ret;
+}
+
+/*
+ * Release a lock on a file
+ */
+static int afs_release_lock(struct afs_vnode *vnode, struct key *key)
+{
+       struct afs_fs_cursor fc;
+       int ret;
+
+       _enter("%s{%x:%u.%u},%x",
+              vnode->volume->name,
+              vnode->fid.vid,
+              vnode->fid.vnode,
+              vnode->fid.unique,
+              key_serial(key));
+
+       ret = -ERESTARTSYS;
+       if (afs_begin_vnode_operation(&fc, vnode, key)) {
+               while (afs_select_current_fileserver(&fc)) {
+                       fc.cb_break = vnode->cb_break + vnode->cb_s_break;
+                       afs_fs_release_lock(&fc);
+               }
+
+               afs_check_for_remote_deletion(&fc, fc.vnode);
+               afs_vnode_commit_status(&fc, vnode, fc.cb_break);
+               ret = afs_end_vnode_operation(&fc);
+       }
+
+       _leave(" = %d", ret);
+       return ret;
+}
+
 /*
  * do work for a lock, including:
  * - probing for a lock we're waiting on but didn't get immediately
@@ -122,7 +185,7 @@ void afs_lock_work(struct work_struct *work)
 
                /* attempt to release the server lock; if it fails, we just
                 * wait 5 minutes and it'll time out anyway */
-               ret = afs_vnode_release_lock(vnode, vnode->unlock_key);
+               ret = afs_release_lock(vnode, vnode->unlock_key);
                if (ret < 0)
                        printk(KERN_WARNING "AFS:"
                               " Failed to release lock on {%x:%x} error %d\n",
@@ -143,10 +206,10 @@ void afs_lock_work(struct work_struct *work)
                        BUG();
                fl = list_entry(vnode->granted_locks.next,
                                struct file_lock, fl_u.afs.link);
-               key = key_get(fl->fl_file->private_data);
+               key = key_get(afs_file_key(fl->fl_file));
                spin_unlock(&vnode->lock);
 
-               ret = afs_vnode_extend_lock(vnode, key);
+               ret = afs_extend_lock(vnode, key);
                clear_bit(AFS_VNODE_LOCKING, &vnode->flags);
                key_put(key);
                switch (ret) {
@@ -177,12 +240,12 @@ void afs_lock_work(struct work_struct *work)
                        BUG();
                fl = list_entry(vnode->pending_locks.next,
                                struct file_lock, fl_u.afs.link);
-               key = key_get(fl->fl_file->private_data);
+               key = key_get(afs_file_key(fl->fl_file));
                type = (fl->fl_type == F_RDLCK) ?
                        AFS_LOCK_READ : AFS_LOCK_WRITE;
                spin_unlock(&vnode->lock);
 
-               ret = afs_vnode_set_lock(vnode, key, type);
+               ret = afs_set_lock(vnode, key, type);
                clear_bit(AFS_VNODE_LOCKING, &vnode->flags);
                switch (ret) {
                case -EWOULDBLOCK:
@@ -213,7 +276,7 @@ void afs_lock_work(struct work_struct *work)
                                clear_bit(AFS_VNODE_READLOCKED, &vnode->flags);
                                clear_bit(AFS_VNODE_WRITELOCKED, &vnode->flags);
                                spin_unlock(&vnode->lock);
-                               afs_vnode_release_lock(vnode, key);
+                               afs_release_lock(vnode, key);
                                if (!list_empty(&vnode->pending_locks))
                                        afs_lock_may_be_available(vnode);
                        }
@@ -255,7 +318,7 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl)
        struct inode *inode = file_inode(file);
        struct afs_vnode *vnode = AFS_FS_I(inode);
        afs_lock_type_t type;
-       struct key *key = file->private_data;
+       struct key *key = afs_file_key(file);
        int ret;
 
        _enter("{%x:%u},%u", vnode->fid.vid, vnode->fid.vnode, fl->fl_type);
@@ -264,10 +327,6 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl)
        if (fl->fl_start != 0 || fl->fl_end != OFFSET_MAX)
                return -EINVAL;
 
-       ret = afs_init_lock_manager();
-       if (ret < 0)
-               return ret;
-
        fl->fl_ops = &afs_lock_ops;
        INIT_LIST_HEAD(&fl->fl_u.afs.link);
        fl->fl_u.afs.state = AFS_LOCK_PENDING;
@@ -278,7 +337,7 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl)
 
        /* make sure we've got a callback on this file and that our view of the
         * data version is up to date */
-       ret = afs_vnode_fetch_status(vnode, NULL, key);
+       ret = afs_validate(vnode, key);
        if (ret < 0)
                goto error;
 
@@ -315,7 +374,7 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl)
                set_bit(AFS_VNODE_LOCKING, &vnode->flags);
                spin_unlock(&vnode->lock);
 
-               ret = afs_vnode_set_lock(vnode, key, type);
+               ret = afs_set_lock(vnode, key, type);
                clear_bit(AFS_VNODE_LOCKING, &vnode->flags);
                switch (ret) {
                case 0:
@@ -418,7 +477,7 @@ given_lock:
        /* again, make sure we've got a callback on this file and, again, make
         * sure that our view of the data version is up to date (we ignore
         * errors incurred here and deal with the consequences elsewhere) */
-       afs_vnode_fetch_status(vnode, NULL, key);
+       afs_validate(vnode, key);
 
 error:
        spin_unlock(&inode->i_lock);
@@ -441,7 +500,7 @@ vfs_rejected_lock:
 static int afs_do_unlk(struct file *file, struct file_lock *fl)
 {
        struct afs_vnode *vnode = AFS_FS_I(file->f_mapping->host);
-       struct key *key = file->private_data;
+       struct key *key = afs_file_key(file);
        int ret;
 
        _enter("{%x:%u},%u", vnode->fid.vid, vnode->fid.vnode, fl->fl_type);
@@ -476,7 +535,7 @@ static int afs_do_unlk(struct file *file, struct file_lock *fl)
 static int afs_do_getlk(struct file *file, struct file_lock *fl)
 {
        struct afs_vnode *vnode = AFS_FS_I(file->f_mapping->host);
-       struct key *key = file->private_data;
+       struct key *key = afs_file_key(file);
        int ret, lock_count;
 
        _enter("");
@@ -490,7 +549,7 @@ static int afs_do_getlk(struct file *file, struct file_lock *fl)
        posix_test_lock(file, fl);
        if (fl->fl_type == F_UNLCK) {
                /* no local locks; consult the server */
-               ret = afs_vnode_fetch_status(vnode, NULL, key);
+               ret = afs_fetch_status(vnode, key);
                if (ret < 0)
                        goto error;
                lock_count = vnode->status.lock_count;
index 19f76ae36982df43be740c1bf73d396b1a81c77c..b90ef39ae914aa688580887a28d4aa433591da2c 100644 (file)
 #include "internal.h"
 #include "afs_fs.h"
 
+static const struct afs_fid afs_zero_fid;
+
 /*
  * We need somewhere to discard into in case the server helpfully returns more
  * than we asked for in FS.FetchData{,64}.
  */
 static u8 afs_discard_buffer[64];
 
+static inline void afs_use_fs_server(struct afs_call *call, struct afs_cb_interest *cbi)
+{
+       call->cbi = afs_get_cb_interest(cbi);
+}
+
 /*
  * decode an AFSFid block
  */
@@ -47,14 +54,18 @@ static void xdr_decode_AFSFetchStatus(const __be32 **_bp,
        const __be32 *bp = *_bp;
        umode_t mode;
        u64 data_version, size;
-       u32 changed = 0; /* becomes non-zero if ctime-type changes seen */
+       bool changed = false;
        kuid_t owner;
        kgid_t group;
 
+       if (vnode)
+               write_seqlock(&vnode->cb_lock);
+
 #define EXTRACT(DST)                           \
        do {                                    \
                u32 x = ntohl(*bp++);           \
-               changed |= DST - x;             \
+               if (DST != x)                   \
+                       changed |= true;        \
                DST = x;                        \
        } while (0)
 
@@ -70,8 +81,8 @@ static void xdr_decode_AFSFetchStatus(const __be32 **_bp,
        EXTRACT(status->caller_access); /* call ticket dependent */
        EXTRACT(status->anon_access);
        EXTRACT(status->mode);
-       EXTRACT(status->parent.vnode);
-       EXTRACT(status->parent.unique);
+       bp++; /* parent.vnode */
+       bp++; /* parent.unique */
        bp++; /* seg size */
        status->mtime_client = ntohl(*bp++);
        status->mtime_server = ntohl(*bp++);
@@ -95,7 +106,6 @@ static void xdr_decode_AFSFetchStatus(const __be32 **_bp,
               status->mtime_client, status->mtime_server);
 
        if (vnode) {
-               status->parent.vid = vnode->fid.vid;
                if (changed && !test_bit(AFS_VNODE_UNSET, &vnode->flags)) {
                        _debug("vnode changed");
                        i_size_write(&vnode->vfs_inode, size);
@@ -127,25 +137,47 @@ static void xdr_decode_AFSFetchStatus(const __be32 **_bp,
                        _debug("vnode modified %llx on {%x:%u}",
                               (unsigned long long) data_version,
                               vnode->fid.vid, vnode->fid.vnode);
-                       set_bit(AFS_VNODE_MODIFIED, &vnode->flags);
+                       set_bit(AFS_VNODE_DIR_MODIFIED, &vnode->flags);
                        set_bit(AFS_VNODE_ZAP_DATA, &vnode->flags);
                }
        } else if (store_version) {
                status->data_version = data_version;
        }
+
+       if (vnode)
+               write_sequnlock(&vnode->cb_lock);
 }
 
 /*
  * decode an AFSCallBack block
  */
-static void xdr_decode_AFSCallBack(const __be32 **_bp, struct afs_vnode *vnode)
+static void xdr_decode_AFSCallBack(struct afs_call *call,
+                                  struct afs_vnode *vnode,
+                                  const __be32 **_bp)
 {
+       struct afs_cb_interest *old, *cbi = call->cbi;
        const __be32 *bp = *_bp;
+       u32 cb_expiry;
+
+       write_seqlock(&vnode->cb_lock);
+
+       if (call->cb_break == (vnode->cb_break + cbi->server->cb_s_break)) {
+               vnode->cb_version       = ntohl(*bp++);
+               cb_expiry               = ntohl(*bp++);
+               vnode->cb_type          = ntohl(*bp++);
+               vnode->cb_expires_at    = cb_expiry + ktime_get_real_seconds();
+               old = vnode->cb_interest;
+               if (old != call->cbi) {
+                       vnode->cb_interest = cbi;
+                       cbi = old;
+               }
+               set_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
+       } else {
+               bp += 3;
+       }
 
-       vnode->cb_version       = ntohl(*bp++);
-       vnode->cb_expiry        = ntohl(*bp++);
-       vnode->cb_type          = ntohl(*bp++);
-       vnode->cb_expires       = vnode->cb_expiry + ktime_get_real_seconds();
+       write_sequnlock(&vnode->cb_lock);
+       call->cbi = cbi;
        *_bp = bp;
 }
 
@@ -243,22 +275,22 @@ static void xdr_decode_AFSFetchVolumeStatus(const __be32 **_bp,
  */
 static int afs_deliver_fs_fetch_status(struct afs_call *call)
 {
-       struct afs_vnode *vnode = call->reply;
+       struct afs_vnode *vnode = call->reply[0];
        const __be32 *bp;
        int ret;
 
-       _enter("");
-
        ret = afs_transfer_reply(call);
        if (ret < 0)
                return ret;
 
+       _enter("{%x:%u}", vnode->fid.vid, vnode->fid.vnode);
+
        /* unmarshall the reply once we've received all of it */
        bp = call->buffer;
        xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
-       xdr_decode_AFSCallBack(&bp, vnode);
-       if (call->reply2)
-               xdr_decode_AFSVolSync(&bp, call->reply2);
+       xdr_decode_AFSCallBack(call, vnode, &bp);
+       if (call->reply[1])
+               xdr_decode_AFSVolSync(&bp, call->reply[1]);
 
        _leave(" = 0 [done]");
        return 0;
@@ -269,35 +301,33 @@ static int afs_deliver_fs_fetch_status(struct afs_call *call)
  */
 static const struct afs_call_type afs_RXFSFetchStatus = {
        .name           = "FS.FetchStatus",
+       .op             = afs_FS_FetchStatus,
        .deliver        = afs_deliver_fs_fetch_status,
-       .abort_to_error = afs_abort_to_error,
        .destructor     = afs_flat_call_destructor,
 };
 
 /*
  * fetch the status information for a file
  */
-int afs_fs_fetch_file_status(struct afs_server *server,
-                            struct key *key,
-                            struct afs_vnode *vnode,
-                            struct afs_volsync *volsync,
-                            bool async)
+int afs_fs_fetch_file_status(struct afs_fs_cursor *fc, struct afs_volsync *volsync)
 {
+       struct afs_vnode *vnode = fc->vnode;
        struct afs_call *call;
+       struct afs_net *net = afs_v2net(vnode);
        __be32 *bp;
 
        _enter(",%x,{%x:%u},,",
-              key_serial(key), vnode->fid.vid, vnode->fid.vnode);
+              key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode);
 
-       call = afs_alloc_flat_call(&afs_RXFSFetchStatus, 16, (21 + 3 + 6) * 4);
-       if (!call)
+       call = afs_alloc_flat_call(net, &afs_RXFSFetchStatus, 16, (21 + 3 + 6) * 4);
+       if (!call) {
+               fc->ac.error = -ENOMEM;
                return -ENOMEM;
+       }
 
-       call->key = key;
-       call->reply = vnode;
-       call->reply2 = volsync;
-       call->service_id = FS_SERVICE;
-       call->port = htons(AFS_FS_PORT);
+       call->key = fc->key;
+       call->reply[0] = vnode;
+       call->reply[1] = volsync;
 
        /* marshall the parameters */
        bp = call->request;
@@ -306,7 +336,10 @@ int afs_fs_fetch_file_status(struct afs_server *server,
        bp[2] = htonl(vnode->fid.vnode);
        bp[3] = htonl(vnode->fid.unique);
 
-       return afs_make_call(&server->addr, call, GFP_NOFS, async);
+       call->cb_break = fc->cb_break;
+       afs_use_fs_server(call, fc->cbi);
+       trace_afs_make_fs_call(call, &vnode->fid);
+       return afs_make_call(&fc->ac, call, GFP_NOFS, false);
 }
 
 /*
@@ -314,8 +347,8 @@ int afs_fs_fetch_file_status(struct afs_server *server,
  */
 static int afs_deliver_fs_fetch_data(struct afs_call *call)
 {
-       struct afs_vnode *vnode = call->reply;
-       struct afs_read *req = call->reply3;
+       struct afs_vnode *vnode = call->reply[0];
+       struct afs_read *req = call->reply[2];
        const __be32 *bp;
        unsigned int size;
        void *buffer;
@@ -431,9 +464,9 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
 
                bp = call->buffer;
                xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
-               xdr_decode_AFSCallBack(&bp, vnode);
-               if (call->reply2)
-                       xdr_decode_AFSVolSync(&bp, call->reply2);
+               xdr_decode_AFSCallBack(call, vnode, &bp);
+               if (call->reply[1])
+                       xdr_decode_AFSVolSync(&bp, call->reply[1]);
 
                call->offset = 0;
                call->unmarshall++;
@@ -457,7 +490,7 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
 
 static void afs_fetch_data_destructor(struct afs_call *call)
 {
-       struct afs_read *req = call->reply3;
+       struct afs_read *req = call->reply[2];
 
        afs_put_read(req);
        afs_flat_call_destructor(call);
@@ -468,43 +501,38 @@ static void afs_fetch_data_destructor(struct afs_call *call)
  */
 static const struct afs_call_type afs_RXFSFetchData = {
        .name           = "FS.FetchData",
+       .op             = afs_FS_FetchData,
        .deliver        = afs_deliver_fs_fetch_data,
-       .abort_to_error = afs_abort_to_error,
        .destructor     = afs_fetch_data_destructor,
 };
 
 static const struct afs_call_type afs_RXFSFetchData64 = {
        .name           = "FS.FetchData64",
+       .op             = afs_FS_FetchData64,
        .deliver        = afs_deliver_fs_fetch_data,
-       .abort_to_error = afs_abort_to_error,
        .destructor     = afs_fetch_data_destructor,
 };
 
 /*
  * fetch data from a very large file
  */
-static int afs_fs_fetch_data64(struct afs_server *server,
-                              struct key *key,
-                              struct afs_vnode *vnode,
-                              struct afs_read *req,
-                              bool async)
+static int afs_fs_fetch_data64(struct afs_fs_cursor *fc, struct afs_read *req)
 {
+       struct afs_vnode *vnode = fc->vnode;
        struct afs_call *call;
+       struct afs_net *net = afs_v2net(vnode);
        __be32 *bp;
 
        _enter("");
 
-       call = afs_alloc_flat_call(&afs_RXFSFetchData64, 32, (21 + 3 + 6) * 4);
+       call = afs_alloc_flat_call(net, &afs_RXFSFetchData64, 32, (21 + 3 + 6) * 4);
        if (!call)
                return -ENOMEM;
 
-       call->key = key;
-       call->reply = vnode;
-       call->reply2 = NULL; /* volsync */
-       call->reply3 = req;
-       call->service_id = FS_SERVICE;
-       call->port = htons(AFS_FS_PORT);
-       call->operation_ID = FSFETCHDATA64;
+       call->key = fc->key;
+       call->reply[0] = vnode;
+       call->reply[1] = NULL; /* volsync */
+       call->reply[2] = req;
 
        /* marshall the parameters */
        bp = call->request;
@@ -518,39 +546,37 @@ static int afs_fs_fetch_data64(struct afs_server *server,
        bp[7] = htonl(lower_32_bits(req->len));
 
        atomic_inc(&req->usage);
-       return afs_make_call(&server->addr, call, GFP_NOFS, async);
+       call->cb_break = fc->cb_break;
+       afs_use_fs_server(call, fc->cbi);
+       trace_afs_make_fs_call(call, &vnode->fid);
+       return afs_make_call(&fc->ac, call, GFP_NOFS, false);
 }
 
 /*
  * fetch data from a file
  */
-int afs_fs_fetch_data(struct afs_server *server,
-                     struct key *key,
-                     struct afs_vnode *vnode,
-                     struct afs_read *req,
-                     bool async)
+int afs_fs_fetch_data(struct afs_fs_cursor *fc, struct afs_read *req)
 {
+       struct afs_vnode *vnode = fc->vnode;
        struct afs_call *call;
+       struct afs_net *net = afs_v2net(vnode);
        __be32 *bp;
 
        if (upper_32_bits(req->pos) ||
            upper_32_bits(req->len) ||
            upper_32_bits(req->pos + req->len))
-               return afs_fs_fetch_data64(server, key, vnode, req, async);
+               return afs_fs_fetch_data64(fc, req);
 
        _enter("");
 
-       call = afs_alloc_flat_call(&afs_RXFSFetchData, 24, (21 + 3 + 6) * 4);
+       call = afs_alloc_flat_call(net, &afs_RXFSFetchData, 24, (21 + 3 + 6) * 4);
        if (!call)
                return -ENOMEM;
 
-       call->key = key;
-       call->reply = vnode;
-       call->reply2 = NULL; /* volsync */
-       call->reply3 = req;
-       call->service_id = FS_SERVICE;
-       call->port = htons(AFS_FS_PORT);
-       call->operation_ID = FSFETCHDATA;
+       call->key = fc->key;
+       call->reply[0] = vnode;
+       call->reply[1] = NULL; /* volsync */
+       call->reply[2] = req;
 
        /* marshall the parameters */
        bp = call->request;
@@ -562,90 +588,10 @@ int afs_fs_fetch_data(struct afs_server *server,
        bp[5] = htonl(lower_32_bits(req->len));
 
        atomic_inc(&req->usage);
-       return afs_make_call(&server->addr, call, GFP_NOFS, async);
-}
-
-/*
- * deliver reply data to an FS.GiveUpCallBacks
- */
-static int afs_deliver_fs_give_up_callbacks(struct afs_call *call)
-{
-       _enter("");
-
-       /* shouldn't be any reply data */
-       return afs_extract_data(call, NULL, 0, false);
-}
-
-/*
- * FS.GiveUpCallBacks operation type
- */
-static const struct afs_call_type afs_RXFSGiveUpCallBacks = {
-       .name           = "FS.GiveUpCallBacks",
-       .deliver        = afs_deliver_fs_give_up_callbacks,
-       .abort_to_error = afs_abort_to_error,
-       .destructor     = afs_flat_call_destructor,
-};
-
-/*
- * give up a set of callbacks
- * - the callbacks are held in the server->cb_break ring
- */
-int afs_fs_give_up_callbacks(struct afs_server *server,
-                            bool async)
-{
-       struct afs_call *call;
-       size_t ncallbacks;
-       __be32 *bp, *tp;
-       int loop;
-
-       ncallbacks = CIRC_CNT(server->cb_break_head, server->cb_break_tail,
-                             ARRAY_SIZE(server->cb_break));
-
-       _enter("{%zu},", ncallbacks);
-
-       if (ncallbacks == 0)
-               return 0;
-       if (ncallbacks > AFSCBMAX)
-               ncallbacks = AFSCBMAX;
-
-       _debug("break %zu callbacks", ncallbacks);
-
-       call = afs_alloc_flat_call(&afs_RXFSGiveUpCallBacks,
-                                  12 + ncallbacks * 6 * 4, 0);
-       if (!call)
-               return -ENOMEM;
-
-       call->service_id = FS_SERVICE;
-       call->port = htons(AFS_FS_PORT);
-
-       /* marshall the parameters */
-       bp = call->request;
-       tp = bp + 2 + ncallbacks * 3;
-       *bp++ = htonl(FSGIVEUPCALLBACKS);
-       *bp++ = htonl(ncallbacks);
-       *tp++ = htonl(ncallbacks);
-
-       atomic_sub(ncallbacks, &server->cb_break_n);
-       for (loop = ncallbacks; loop > 0; loop--) {
-               struct afs_callback *cb =
-                       &server->cb_break[server->cb_break_tail];
-
-               *bp++ = htonl(cb->fid.vid);
-               *bp++ = htonl(cb->fid.vnode);
-               *bp++ = htonl(cb->fid.unique);
-               *tp++ = htonl(cb->version);
-               *tp++ = htonl(cb->expiry);
-               *tp++ = htonl(cb->type);
-               smp_mb();
-               server->cb_break_tail =
-                       (server->cb_break_tail + 1) &
-                       (ARRAY_SIZE(server->cb_break) - 1);
-       }
-
-       ASSERT(ncallbacks > 0);
-       wake_up_nr(&server->cb_break_waitq, ncallbacks);
-
-       return afs_make_call(&server->addr, call, GFP_NOFS, async);
+       call->cb_break = fc->cb_break;
+       afs_use_fs_server(call, fc->cbi);
+       trace_afs_make_fs_call(call, &vnode->fid);
+       return afs_make_call(&fc->ac, call, GFP_NOFS, false);
 }
 
 /*
@@ -653,7 +599,7 @@ int afs_fs_give_up_callbacks(struct afs_server *server,
  */
 static int afs_deliver_fs_create_vnode(struct afs_call *call)
 {
-       struct afs_vnode *vnode = call->reply;
+       struct afs_vnode *vnode = call->reply[0];
        const __be32 *bp;
        int ret;
 
@@ -665,11 +611,11 @@ static int afs_deliver_fs_create_vnode(struct afs_call *call)
 
        /* unmarshall the reply once we've received all of it */
        bp = call->buffer;
-       xdr_decode_AFSFid(&bp, call->reply2);
-       xdr_decode_AFSFetchStatus(&bp, call->reply3, NULL, NULL);
+       xdr_decode_AFSFid(&bp, call->reply[1]);
+       xdr_decode_AFSFetchStatus(&bp, call->reply[2], NULL, NULL);
        xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
-       xdr_decode_AFSCallBack_raw(&bp, call->reply4);
-       /* xdr_decode_AFSVolSync(&bp, call->replyX); */
+       xdr_decode_AFSCallBack_raw(&bp, call->reply[3]);
+       /* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
 
        _leave(" = 0 [done]");
        return 0;
@@ -678,27 +624,33 @@ static int afs_deliver_fs_create_vnode(struct afs_call *call)
 /*
  * FS.CreateFile and FS.MakeDir operation type
  */
-static const struct afs_call_type afs_RXFSCreateXXXX = {
-       .name           = "FS.CreateXXXX",
+static const struct afs_call_type afs_RXFSCreateFile = {
+       .name           = "FS.CreateFile",
+       .op             = afs_FS_CreateFile,
+       .deliver        = afs_deliver_fs_create_vnode,
+       .destructor     = afs_flat_call_destructor,
+};
+
+static const struct afs_call_type afs_RXFSMakeDir = {
+       .name           = "FS.MakeDir",
+       .op             = afs_FS_MakeDir,
        .deliver        = afs_deliver_fs_create_vnode,
-       .abort_to_error = afs_abort_to_error,
        .destructor     = afs_flat_call_destructor,
 };
 
 /*
  * create a file or make a directory
  */
-int afs_fs_create(struct afs_server *server,
-                 struct key *key,
-                 struct afs_vnode *vnode,
+int afs_fs_create(struct afs_fs_cursor *fc,
                  const char *name,
                  umode_t mode,
                  struct afs_fid *newfid,
                  struct afs_file_status *newstatus,
-                 struct afs_callback *newcb,
-                 bool async)
+                 struct afs_callback *newcb)
 {
+       struct afs_vnode *vnode = fc->vnode;
        struct afs_call *call;
+       struct afs_net *net = afs_v2net(vnode);
        size_t namesz, reqsz, padsz;
        __be32 *bp;
 
@@ -708,18 +660,17 @@ int afs_fs_create(struct afs_server *server,
        padsz = (4 - (namesz & 3)) & 3;
        reqsz = (5 * 4) + namesz + padsz + (6 * 4);
 
-       call = afs_alloc_flat_call(&afs_RXFSCreateXXXX, reqsz,
-                                  (3 + 21 + 21 + 3 + 6) * 4);
+       call = afs_alloc_flat_call(
+               net, S_ISDIR(mode) ? &afs_RXFSMakeDir : &afs_RXFSCreateFile,
+               reqsz, (3 + 21 + 21 + 3 + 6) * 4);
        if (!call)
                return -ENOMEM;
 
-       call->key = key;
-       call->reply = vnode;
-       call->reply2 = newfid;
-       call->reply3 = newstatus;
-       call->reply4 = newcb;
-       call->service_id = FS_SERVICE;
-       call->port = htons(AFS_FS_PORT);
+       call->key = fc->key;
+       call->reply[0] = vnode;
+       call->reply[1] = newfid;
+       call->reply[2] = newstatus;
+       call->reply[3] = newcb;
 
        /* marshall the parameters */
        bp = call->request;
@@ -741,7 +692,9 @@ int afs_fs_create(struct afs_server *server,
        *bp++ = htonl(mode & S_IALLUGO); /* unix mode */
        *bp++ = 0; /* segment size */
 
-       return afs_make_call(&server->addr, call, GFP_NOFS, async);
+       afs_use_fs_server(call, fc->cbi);
+       trace_afs_make_fs_call(call, &vnode->fid);
+       return afs_make_call(&fc->ac, call, GFP_NOFS, false);
 }
 
 /*
@@ -749,7 +702,7 @@ int afs_fs_create(struct afs_server *server,
  */
 static int afs_deliver_fs_remove(struct afs_call *call)
 {
-       struct afs_vnode *vnode = call->reply;
+       struct afs_vnode *vnode = call->reply[0];
        const __be32 *bp;
        int ret;
 
@@ -762,7 +715,7 @@ static int afs_deliver_fs_remove(struct afs_call *call)
        /* unmarshall the reply once we've received all of it */
        bp = call->buffer;
        xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
-       /* xdr_decode_AFSVolSync(&bp, call->replyX); */
+       /* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
 
        _leave(" = 0 [done]");
        return 0;
@@ -771,24 +724,28 @@ static int afs_deliver_fs_remove(struct afs_call *call)
 /*
  * FS.RemoveDir/FS.RemoveFile operation type
  */
-static const struct afs_call_type afs_RXFSRemoveXXXX = {
-       .name           = "FS.RemoveXXXX",
+static const struct afs_call_type afs_RXFSRemoveFile = {
+       .name           = "FS.RemoveFile",
+       .op             = afs_FS_RemoveFile,
+       .deliver        = afs_deliver_fs_remove,
+       .destructor     = afs_flat_call_destructor,
+};
+
+static const struct afs_call_type afs_RXFSRemoveDir = {
+       .name           = "FS.RemoveDir",
+       .op             = afs_FS_RemoveDir,
        .deliver        = afs_deliver_fs_remove,
-       .abort_to_error = afs_abort_to_error,
        .destructor     = afs_flat_call_destructor,
 };
 
 /*
  * remove a file or directory
  */
-int afs_fs_remove(struct afs_server *server,
-                 struct key *key,
-                 struct afs_vnode *vnode,
-                 const char *name,
-                 bool isdir,
-                 bool async)
+int afs_fs_remove(struct afs_fs_cursor *fc, const char *name, bool isdir)
 {
+       struct afs_vnode *vnode = fc->vnode;
        struct afs_call *call;
+       struct afs_net *net = afs_v2net(vnode);
        size_t namesz, reqsz, padsz;
        __be32 *bp;
 
@@ -798,14 +755,14 @@ int afs_fs_remove(struct afs_server *server,
        padsz = (4 - (namesz & 3)) & 3;
        reqsz = (5 * 4) + namesz + padsz;
 
-       call = afs_alloc_flat_call(&afs_RXFSRemoveXXXX, reqsz, (21 + 6) * 4);
+       call = afs_alloc_flat_call(
+               net, isdir ? &afs_RXFSRemoveDir : &afs_RXFSRemoveFile,
+               reqsz, (21 + 6) * 4);
        if (!call)
                return -ENOMEM;
 
-       call->key = key;
-       call->reply = vnode;
-       call->service_id = FS_SERVICE;
-       call->port = htons(AFS_FS_PORT);
+       call->key = fc->key;
+       call->reply[0] = vnode;
 
        /* marshall the parameters */
        bp = call->request;
@@ -821,7 +778,9 @@ int afs_fs_remove(struct afs_server *server,
                bp = (void *) bp + padsz;
        }
 
-       return afs_make_call(&server->addr, call, GFP_NOFS, async);
+       afs_use_fs_server(call, fc->cbi);
+       trace_afs_make_fs_call(call, &vnode->fid);
+       return afs_make_call(&fc->ac, call, GFP_NOFS, false);
 }
 
 /*
@@ -829,7 +788,7 @@ int afs_fs_remove(struct afs_server *server,
  */
 static int afs_deliver_fs_link(struct afs_call *call)
 {
-       struct afs_vnode *dvnode = call->reply, *vnode = call->reply2;
+       struct afs_vnode *dvnode = call->reply[0], *vnode = call->reply[1];
        const __be32 *bp;
        int ret;
 
@@ -843,7 +802,7 @@ static int afs_deliver_fs_link(struct afs_call *call)
        bp = call->buffer;
        xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
        xdr_decode_AFSFetchStatus(&bp, &dvnode->status, dvnode, NULL);
-       /* xdr_decode_AFSVolSync(&bp, call->replyX); */
+       /* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
 
        _leave(" = 0 [done]");
        return 0;
@@ -854,22 +813,20 @@ static int afs_deliver_fs_link(struct afs_call *call)
  */
 static const struct afs_call_type afs_RXFSLink = {
        .name           = "FS.Link",
+       .op             = afs_FS_Link,
        .deliver        = afs_deliver_fs_link,
-       .abort_to_error = afs_abort_to_error,
        .destructor     = afs_flat_call_destructor,
 };
 
 /*
  * make a hard link
  */
-int afs_fs_link(struct afs_server *server,
-               struct key *key,
-               struct afs_vnode *dvnode,
-               struct afs_vnode *vnode,
-               const char *name,
-               bool async)
+int afs_fs_link(struct afs_fs_cursor *fc, struct afs_vnode *vnode,
+               const char *name)
 {
+       struct afs_vnode *dvnode = fc->vnode;
        struct afs_call *call;
+       struct afs_net *net = afs_v2net(vnode);
        size_t namesz, reqsz, padsz;
        __be32 *bp;
 
@@ -879,15 +836,13 @@ int afs_fs_link(struct afs_server *server,
        padsz = (4 - (namesz & 3)) & 3;
        reqsz = (5 * 4) + namesz + padsz + (3 * 4);
 
-       call = afs_alloc_flat_call(&afs_RXFSLink, reqsz, (21 + 21 + 6) * 4);
+       call = afs_alloc_flat_call(net, &afs_RXFSLink, reqsz, (21 + 21 + 6) * 4);
        if (!call)
                return -ENOMEM;
 
-       call->key = key;
-       call->reply = dvnode;
-       call->reply2 = vnode;
-       call->service_id = FS_SERVICE;
-       call->port = htons(AFS_FS_PORT);
+       call->key = fc->key;
+       call->reply[0] = dvnode;
+       call->reply[1] = vnode;
 
        /* marshall the parameters */
        bp = call->request;
@@ -906,7 +861,9 @@ int afs_fs_link(struct afs_server *server,
        *bp++ = htonl(vnode->fid.vnode);
        *bp++ = htonl(vnode->fid.unique);
 
-       return afs_make_call(&server->addr, call, GFP_NOFS, async);
+       afs_use_fs_server(call, fc->cbi);
+       trace_afs_make_fs_call(call, &vnode->fid);
+       return afs_make_call(&fc->ac, call, GFP_NOFS, false);
 }
 
 /*
@@ -914,7 +871,7 @@ int afs_fs_link(struct afs_server *server,
  */
 static int afs_deliver_fs_symlink(struct afs_call *call)
 {
-       struct afs_vnode *vnode = call->reply;
+       struct afs_vnode *vnode = call->reply[0];
        const __be32 *bp;
        int ret;
 
@@ -926,10 +883,10 @@ static int afs_deliver_fs_symlink(struct afs_call *call)
 
        /* unmarshall the reply once we've received all of it */
        bp = call->buffer;
-       xdr_decode_AFSFid(&bp, call->reply2);
-       xdr_decode_AFSFetchStatus(&bp, call->reply3, NULL, NULL);
+       xdr_decode_AFSFid(&bp, call->reply[1]);
+       xdr_decode_AFSFetchStatus(&bp, call->reply[2], NULL, NULL);
        xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
-       /* xdr_decode_AFSVolSync(&bp, call->replyX); */
+       /* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
 
        _leave(" = 0 [done]");
        return 0;
@@ -940,24 +897,23 @@ static int afs_deliver_fs_symlink(struct afs_call *call)
  */
 static const struct afs_call_type afs_RXFSSymlink = {
        .name           = "FS.Symlink",
+       .op             = afs_FS_Symlink,
        .deliver        = afs_deliver_fs_symlink,
-       .abort_to_error = afs_abort_to_error,
        .destructor     = afs_flat_call_destructor,
 };
 
 /*
  * create a symbolic link
  */
-int afs_fs_symlink(struct afs_server *server,
-                  struct key *key,
-                  struct afs_vnode *vnode,
+int afs_fs_symlink(struct afs_fs_cursor *fc,
                   const char *name,
                   const char *contents,
                   struct afs_fid *newfid,
-                  struct afs_file_status *newstatus,
-                  bool async)
+                  struct afs_file_status *newstatus)
 {
+       struct afs_vnode *vnode = fc->vnode;
        struct afs_call *call;
+       struct afs_net *net = afs_v2net(vnode);
        size_t namesz, reqsz, padsz, c_namesz, c_padsz;
        __be32 *bp;
 
@@ -971,17 +927,15 @@ int afs_fs_symlink(struct afs_server *server,
 
        reqsz = (6 * 4) + namesz + padsz + c_namesz + c_padsz + (6 * 4);
 
-       call = afs_alloc_flat_call(&afs_RXFSSymlink, reqsz,
+       call = afs_alloc_flat_call(net, &afs_RXFSSymlink, reqsz,
                                   (3 + 21 + 21 + 6) * 4);
        if (!call)
                return -ENOMEM;
 
-       call->key = key;
-       call->reply = vnode;
-       call->reply2 = newfid;
-       call->reply3 = newstatus;
-       call->service_id = FS_SERVICE;
-       call->port = htons(AFS_FS_PORT);
+       call->key = fc->key;
+       call->reply[0] = vnode;
+       call->reply[1] = newfid;
+       call->reply[2] = newstatus;
 
        /* marshall the parameters */
        bp = call->request;
@@ -1010,7 +964,9 @@ int afs_fs_symlink(struct afs_server *server,
        *bp++ = htonl(S_IRWXUGO); /* unix mode */
        *bp++ = 0; /* segment size */
 
-       return afs_make_call(&server->addr, call, GFP_NOFS, async);
+       afs_use_fs_server(call, fc->cbi);
+       trace_afs_make_fs_call(call, &vnode->fid);
+       return afs_make_call(&fc->ac, call, GFP_NOFS, false);
 }
 
 /*
@@ -1018,7 +974,7 @@ int afs_fs_symlink(struct afs_server *server,
  */
 static int afs_deliver_fs_rename(struct afs_call *call)
 {
-       struct afs_vnode *orig_dvnode = call->reply, *new_dvnode = call->reply2;
+       struct afs_vnode *orig_dvnode = call->reply[0], *new_dvnode = call->reply[1];
        const __be32 *bp;
        int ret;
 
@@ -1034,7 +990,7 @@ static int afs_deliver_fs_rename(struct afs_call *call)
        if (new_dvnode != orig_dvnode)
                xdr_decode_AFSFetchStatus(&bp, &new_dvnode->status, new_dvnode,
                                          NULL);
-       /* xdr_decode_AFSVolSync(&bp, call->replyX); */
+       /* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
 
        _leave(" = 0 [done]");
        return 0;
@@ -1045,23 +1001,22 @@ static int afs_deliver_fs_rename(struct afs_call *call)
  */
 static const struct afs_call_type afs_RXFSRename = {
        .name           = "FS.Rename",
+       .op             = afs_FS_Rename,
        .deliver        = afs_deliver_fs_rename,
-       .abort_to_error = afs_abort_to_error,
        .destructor     = afs_flat_call_destructor,
 };
 
 /*
  * create a symbolic link
  */
-int afs_fs_rename(struct afs_server *server,
-                 struct key *key,
-                 struct afs_vnode *orig_dvnode,
+int afs_fs_rename(struct afs_fs_cursor *fc,
                  const char *orig_name,
                  struct afs_vnode *new_dvnode,
-                 const char *new_name,
-                 bool async)
+                 const char *new_name)
 {
+       struct afs_vnode *orig_dvnode = fc->vnode;
        struct afs_call *call;
+       struct afs_net *net = afs_v2net(orig_dvnode);
        size_t reqsz, o_namesz, o_padsz, n_namesz, n_padsz;
        __be32 *bp;
 
@@ -1078,15 +1033,13 @@ int afs_fs_rename(struct afs_server *server,
                (3 * 4) +
                4 + n_namesz + n_padsz;
 
-       call = afs_alloc_flat_call(&afs_RXFSRename, reqsz, (21 + 21 + 6) * 4);
+       call = afs_alloc_flat_call(net, &afs_RXFSRename, reqsz, (21 + 21 + 6) * 4);
        if (!call)
                return -ENOMEM;
 
-       call->key = key;
-       call->reply = orig_dvnode;
-       call->reply2 = new_dvnode;
-       call->service_id = FS_SERVICE;
-       call->port = htons(AFS_FS_PORT);
+       call->key = fc->key;
+       call->reply[0] = orig_dvnode;
+       call->reply[1] = new_dvnode;
 
        /* marshall the parameters */
        bp = call->request;
@@ -1113,7 +1066,9 @@ int afs_fs_rename(struct afs_server *server,
                bp = (void *) bp + n_padsz;
        }
 
-       return afs_make_call(&server->addr, call, GFP_NOFS, async);
+       afs_use_fs_server(call, fc->cbi);
+       trace_afs_make_fs_call(call, &orig_dvnode->fid);
+       return afs_make_call(&fc->ac, call, GFP_NOFS, false);
 }
 
 /*
@@ -1121,7 +1076,7 @@ int afs_fs_rename(struct afs_server *server,
  */
 static int afs_deliver_fs_store_data(struct afs_call *call)
 {
-       struct afs_vnode *vnode = call->reply;
+       struct afs_vnode *vnode = call->reply[0];
        const __be32 *bp;
        int ret;
 
@@ -1135,7 +1090,7 @@ static int afs_deliver_fs_store_data(struct afs_call *call)
        bp = call->buffer;
        xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode,
                                  &call->store_version);
-       /* xdr_decode_AFSVolSync(&bp, call->replyX); */
+       /* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
 
        afs_pages_written_back(vnode, call);
 
@@ -1148,47 +1103,44 @@ static int afs_deliver_fs_store_data(struct afs_call *call)
  */
 static const struct afs_call_type afs_RXFSStoreData = {
        .name           = "FS.StoreData",
+       .op             = afs_FS_StoreData,
        .deliver        = afs_deliver_fs_store_data,
-       .abort_to_error = afs_abort_to_error,
        .destructor     = afs_flat_call_destructor,
 };
 
 static const struct afs_call_type afs_RXFSStoreData64 = {
        .name           = "FS.StoreData64",
+       .op             = afs_FS_StoreData64,
        .deliver        = afs_deliver_fs_store_data,
-       .abort_to_error = afs_abort_to_error,
        .destructor     = afs_flat_call_destructor,
 };
 
 /*
  * store a set of pages to a very large file
  */
-static int afs_fs_store_data64(struct afs_server *server,
-                              struct afs_writeback *wb,
+static int afs_fs_store_data64(struct afs_fs_cursor *fc,
+                              struct address_space *mapping,
                               pgoff_t first, pgoff_t last,
                               unsigned offset, unsigned to,
-                              loff_t size, loff_t pos, loff_t i_size,
-                              bool async)
+                              loff_t size, loff_t pos, loff_t i_size)
 {
-       struct afs_vnode *vnode = wb->vnode;
+       struct afs_vnode *vnode = fc->vnode;
        struct afs_call *call;
+       struct afs_net *net = afs_v2net(vnode);
        __be32 *bp;
 
        _enter(",%x,{%x:%u},,",
-              key_serial(wb->key), vnode->fid.vid, vnode->fid.vnode);
+              key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode);
 
-       call = afs_alloc_flat_call(&afs_RXFSStoreData64,
+       call = afs_alloc_flat_call(net, &afs_RXFSStoreData64,
                                   (4 + 6 + 3 * 2) * 4,
                                   (21 + 6) * 4);
        if (!call)
                return -ENOMEM;
 
-       call->wb = wb;
-       call->key = wb->key;
-       call->reply = vnode;
-       call->service_id = FS_SERVICE;
-       call->port = htons(AFS_FS_PORT);
-       call->mapping = vnode->vfs_inode.i_mapping;
+       call->key = fc->key;
+       call->mapping = mapping;
+       call->reply[0] = vnode;
        call->first = first;
        call->last = last;
        call->first_offset = offset;
@@ -1217,24 +1169,25 @@ static int afs_fs_store_data64(struct afs_server *server,
        *bp++ = htonl(i_size >> 32);
        *bp++ = htonl((u32) i_size);
 
-       return afs_make_call(&server->addr, call, GFP_NOFS, async);
+       trace_afs_make_fs_call(call, &vnode->fid);
+       return afs_make_call(&fc->ac, call, GFP_NOFS, false);
 }
 
 /*
  * store a set of pages
  */
-int afs_fs_store_data(struct afs_server *server, struct afs_writeback *wb,
+int afs_fs_store_data(struct afs_fs_cursor *fc, struct address_space *mapping,
                      pgoff_t first, pgoff_t last,
-                     unsigned offset, unsigned to,
-                     bool async)
+                     unsigned offset, unsigned to)
 {
-       struct afs_vnode *vnode = wb->vnode;
+       struct afs_vnode *vnode = fc->vnode;
        struct afs_call *call;
+       struct afs_net *net = afs_v2net(vnode);
        loff_t size, pos, i_size;
        __be32 *bp;
 
        _enter(",%x,{%x:%u},,",
-              key_serial(wb->key), vnode->fid.vid, vnode->fid.vnode);
+              key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode);
 
        size = (loff_t)to - (loff_t)offset;
        if (first != last)
@@ -1251,21 +1204,18 @@ int afs_fs_store_data(struct afs_server *server, struct afs_writeback *wb,
               (unsigned long long) i_size);
 
        if (pos >> 32 || i_size >> 32 || size >> 32 || (pos + size) >> 32)
-               return afs_fs_store_data64(server, wb, first, last, offset, to,
-                                          size, pos, i_size, async);
+               return afs_fs_store_data64(fc, mapping, first, last, offset, to,
+                                          size, pos, i_size);
 
-       call = afs_alloc_flat_call(&afs_RXFSStoreData,
+       call = afs_alloc_flat_call(net, &afs_RXFSStoreData,
                                   (4 + 6 + 3) * 4,
                                   (21 + 6) * 4);
        if (!call)
                return -ENOMEM;
 
-       call->wb = wb;
-       call->key = wb->key;
-       call->reply = vnode;
-       call->service_id = FS_SERVICE;
-       call->port = htons(AFS_FS_PORT);
-       call->mapping = vnode->vfs_inode.i_mapping;
+       call->key = fc->key;
+       call->mapping = mapping;
+       call->reply[0] = vnode;
        call->first = first;
        call->last = last;
        call->first_offset = offset;
@@ -1291,7 +1241,9 @@ int afs_fs_store_data(struct afs_server *server, struct afs_writeback *wb,
        *bp++ = htonl(size);
        *bp++ = htonl(i_size);
 
-       return afs_make_call(&server->addr, call, GFP_NOFS, async);
+       afs_use_fs_server(call, fc->cbi);
+       trace_afs_make_fs_call(call, &vnode->fid);
+       return afs_make_call(&fc->ac, call, GFP_NOFS, false);
 }
 
 /*
@@ -1300,7 +1252,7 @@ int afs_fs_store_data(struct afs_server *server, struct afs_writeback *wb,
 static int afs_deliver_fs_store_status(struct afs_call *call)
 {
        afs_dataversion_t *store_version;
-       struct afs_vnode *vnode = call->reply;
+       struct afs_vnode *vnode = call->reply[0];
        const __be32 *bp;
        int ret;
 
@@ -1317,7 +1269,7 @@ static int afs_deliver_fs_store_status(struct afs_call *call)
 
        bp = call->buffer;
        xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, store_version);
-       /* xdr_decode_AFSVolSync(&bp, call->replyX); */
+       /* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
 
        _leave(" = 0 [done]");
        return 0;
@@ -1328,22 +1280,22 @@ static int afs_deliver_fs_store_status(struct afs_call *call)
  */
 static const struct afs_call_type afs_RXFSStoreStatus = {
        .name           = "FS.StoreStatus",
+       .op             = afs_FS_StoreStatus,
        .deliver        = afs_deliver_fs_store_status,
-       .abort_to_error = afs_abort_to_error,
        .destructor     = afs_flat_call_destructor,
 };
 
 static const struct afs_call_type afs_RXFSStoreData_as_Status = {
        .name           = "FS.StoreData",
+       .op             = afs_FS_StoreData,
        .deliver        = afs_deliver_fs_store_status,
-       .abort_to_error = afs_abort_to_error,
        .destructor     = afs_flat_call_destructor,
 };
 
 static const struct afs_call_type afs_RXFSStoreData64_as_Status = {
        .name           = "FS.StoreData64",
+       .op             = afs_FS_StoreData64,
        .deliver        = afs_deliver_fs_store_status,
-       .abort_to_error = afs_abort_to_error,
        .destructor     = afs_flat_call_destructor,
 };
 
@@ -1351,30 +1303,27 @@ static const struct afs_call_type afs_RXFSStoreData64_as_Status = {
  * set the attributes on a very large file, using FS.StoreData rather than
  * FS.StoreStatus so as to alter the file size also
  */
-static int afs_fs_setattr_size64(struct afs_server *server, struct key *key,
-                                struct afs_vnode *vnode, struct iattr *attr,
-                                bool async)
+static int afs_fs_setattr_size64(struct afs_fs_cursor *fc, struct iattr *attr)
 {
+       struct afs_vnode *vnode = fc->vnode;
        struct afs_call *call;
+       struct afs_net *net = afs_v2net(vnode);
        __be32 *bp;
 
        _enter(",%x,{%x:%u},,",
-              key_serial(key), vnode->fid.vid, vnode->fid.vnode);
+              key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode);
 
        ASSERT(attr->ia_valid & ATTR_SIZE);
 
-       call = afs_alloc_flat_call(&afs_RXFSStoreData64_as_Status,
+       call = afs_alloc_flat_call(net, &afs_RXFSStoreData64_as_Status,
                                   (4 + 6 + 3 * 2) * 4,
                                   (21 + 6) * 4);
        if (!call)
                return -ENOMEM;
 
-       call->key = key;
-       call->reply = vnode;
-       call->service_id = FS_SERVICE;
-       call->port = htons(AFS_FS_PORT);
+       call->key = fc->key;
+       call->reply[0] = vnode;
        call->store_version = vnode->status.data_version + 1;
-       call->operation_ID = FSSTOREDATA;
 
        /* marshall the parameters */
        bp = call->request;
@@ -1392,40 +1341,38 @@ static int afs_fs_setattr_size64(struct afs_server *server, struct key *key,
        *bp++ = htonl(attr->ia_size >> 32);     /* new file length */
        *bp++ = htonl((u32) attr->ia_size);
 
-       return afs_make_call(&server->addr, call, GFP_NOFS, async);
+       afs_use_fs_server(call, fc->cbi);
+       trace_afs_make_fs_call(call, &vnode->fid);
+       return afs_make_call(&fc->ac, call, GFP_NOFS, false);
 }
 
 /*
  * set the attributes on a file, using FS.StoreData rather than FS.StoreStatus
  * so as to alter the file size also
  */
-static int afs_fs_setattr_size(struct afs_server *server, struct key *key,
-                              struct afs_vnode *vnode, struct iattr *attr,
-                              bool async)
+static int afs_fs_setattr_size(struct afs_fs_cursor *fc, struct iattr *attr)
 {
+       struct afs_vnode *vnode = fc->vnode;
        struct afs_call *call;
+       struct afs_net *net = afs_v2net(vnode);
        __be32 *bp;
 
        _enter(",%x,{%x:%u},,",
-              key_serial(key), vnode->fid.vid, vnode->fid.vnode);
+              key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode);
 
        ASSERT(attr->ia_valid & ATTR_SIZE);
        if (attr->ia_size >> 32)
-               return afs_fs_setattr_size64(server, key, vnode, attr,
-                                            async);
+               return afs_fs_setattr_size64(fc, attr);
 
-       call = afs_alloc_flat_call(&afs_RXFSStoreData_as_Status,
+       call = afs_alloc_flat_call(net, &afs_RXFSStoreData_as_Status,
                                   (4 + 6 + 3) * 4,
                                   (21 + 6) * 4);
        if (!call)
                return -ENOMEM;
 
-       call->key = key;
-       call->reply = vnode;
-       call->service_id = FS_SERVICE;
-       call->port = htons(AFS_FS_PORT);
+       call->key = fc->key;
+       call->reply[0] = vnode;
        call->store_version = vnode->status.data_version + 1;
-       call->operation_ID = FSSTOREDATA;
 
        /* marshall the parameters */
        bp = call->request;
@@ -1440,38 +1387,36 @@ static int afs_fs_setattr_size(struct afs_server *server, struct key *key,
        *bp++ = 0;                              /* size of write */
        *bp++ = htonl(attr->ia_size);           /* new file length */
 
-       return afs_make_call(&server->addr, call, GFP_NOFS, async);
+       afs_use_fs_server(call, fc->cbi);
+       trace_afs_make_fs_call(call, &vnode->fid);
+       return afs_make_call(&fc->ac, call, GFP_NOFS, false);
 }
 
 /*
  * set the attributes on a file, using FS.StoreData if there's a change in file
  * size, and FS.StoreStatus otherwise
  */
-int afs_fs_setattr(struct afs_server *server, struct key *key,
-                  struct afs_vnode *vnode, struct iattr *attr,
-                  bool async)
+int afs_fs_setattr(struct afs_fs_cursor *fc, struct iattr *attr)
 {
+       struct afs_vnode *vnode = fc->vnode;
        struct afs_call *call;
+       struct afs_net *net = afs_v2net(vnode);
        __be32 *bp;
 
        if (attr->ia_valid & ATTR_SIZE)
-               return afs_fs_setattr_size(server, key, vnode, attr,
-                                          async);
+               return afs_fs_setattr_size(fc, attr);
 
        _enter(",%x,{%x:%u},,",
-              key_serial(key), vnode->fid.vid, vnode->fid.vnode);
+              key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode);
 
-       call = afs_alloc_flat_call(&afs_RXFSStoreStatus,
+       call = afs_alloc_flat_call(net, &afs_RXFSStoreStatus,
                                   (4 + 6) * 4,
                                   (21 + 6) * 4);
        if (!call)
                return -ENOMEM;
 
-       call->key = key;
-       call->reply = vnode;
-       call->service_id = FS_SERVICE;
-       call->port = htons(AFS_FS_PORT);
-       call->operation_ID = FSSTORESTATUS;
+       call->key = fc->key;
+       call->reply[0] = vnode;
 
        /* marshall the parameters */
        bp = call->request;
@@ -1482,7 +1427,9 @@ int afs_fs_setattr(struct afs_server *server, struct key *key,
 
        xdr_encode_AFS_StoreStatus(&bp, attr);
 
-       return afs_make_call(&server->addr, call, GFP_NOFS, async);
+       afs_use_fs_server(call, fc->cbi);
+       trace_afs_make_fs_call(call, &vnode->fid);
+       return afs_make_call(&fc->ac, call, GFP_NOFS, false);
 }
 
 /*
@@ -1510,7 +1457,7 @@ static int afs_deliver_fs_get_volume_status(struct afs_call *call)
                        return ret;
 
                bp = call->buffer;
-               xdr_decode_AFSFetchVolumeStatus(&bp, call->reply2);
+               xdr_decode_AFSFetchVolumeStatus(&bp, call->reply[1]);
                call->offset = 0;
                call->unmarshall++;
 
@@ -1531,13 +1478,13 @@ static int afs_deliver_fs_get_volume_status(struct afs_call *call)
        case 3:
                _debug("extract volname");
                if (call->count > 0) {
-                       ret = afs_extract_data(call, call->reply3,
+                       ret = afs_extract_data(call, call->reply[2],
                                               call->count, true);
                        if (ret < 0)
                                return ret;
                }
 
-               p = call->reply3;
+               p = call->reply[2];
                p[call->count] = 0;
                _debug("volname '%s'", p);
 
@@ -1578,13 +1525,13 @@ static int afs_deliver_fs_get_volume_status(struct afs_call *call)
        case 6:
                _debug("extract offline");
                if (call->count > 0) {
-                       ret = afs_extract_data(call, call->reply3,
+                       ret = afs_extract_data(call, call->reply[2],
                                               call->count, true);
                        if (ret < 0)
                                return ret;
                }
 
-               p = call->reply3;
+               p = call->reply[2];
                p[call->count] = 0;
                _debug("offline '%s'", p);
 
@@ -1625,13 +1572,13 @@ static int afs_deliver_fs_get_volume_status(struct afs_call *call)
        case 9:
                _debug("extract motd");
                if (call->count > 0) {
-                       ret = afs_extract_data(call, call->reply3,
+                       ret = afs_extract_data(call, call->reply[2],
                                               call->count, true);
                        if (ret < 0)
                                return ret;
                }
 
-               p = call->reply3;
+               p = call->reply[2];
                p[call->count] = 0;
                _debug("motd '%s'", p);
 
@@ -1662,8 +1609,8 @@ static int afs_deliver_fs_get_volume_status(struct afs_call *call)
  */
 static void afs_get_volume_status_call_destructor(struct afs_call *call)
 {
-       kfree(call->reply3);
-       call->reply3 = NULL;
+       kfree(call->reply[2]);
+       call->reply[2] = NULL;
        afs_flat_call_destructor(call);
 }
 
@@ -1672,21 +1619,20 @@ static void afs_get_volume_status_call_destructor(struct afs_call *call)
  */
 static const struct afs_call_type afs_RXFSGetVolumeStatus = {
        .name           = "FS.GetVolumeStatus",
+       .op             = afs_FS_GetVolumeStatus,
        .deliver        = afs_deliver_fs_get_volume_status,
-       .abort_to_error = afs_abort_to_error,
        .destructor     = afs_get_volume_status_call_destructor,
 };
 
 /*
  * fetch the status of a volume
  */
-int afs_fs_get_volume_status(struct afs_server *server,
-                            struct key *key,
-                            struct afs_vnode *vnode,
-                            struct afs_volume_status *vs,
-                            bool async)
+int afs_fs_get_volume_status(struct afs_fs_cursor *fc,
+                            struct afs_volume_status *vs)
 {
+       struct afs_vnode *vnode = fc->vnode;
        struct afs_call *call;
+       struct afs_net *net = afs_v2net(vnode);
        __be32 *bp;
        void *tmpbuf;
 
@@ -1696,25 +1642,25 @@ int afs_fs_get_volume_status(struct afs_server *server,
        if (!tmpbuf)
                return -ENOMEM;
 
-       call = afs_alloc_flat_call(&afs_RXFSGetVolumeStatus, 2 * 4, 12 * 4);
+       call = afs_alloc_flat_call(net, &afs_RXFSGetVolumeStatus, 2 * 4, 12 * 4);
        if (!call) {
                kfree(tmpbuf);
                return -ENOMEM;
        }
 
-       call->key = key;
-       call->reply = vnode;
-       call->reply2 = vs;
-       call->reply3 = tmpbuf;
-       call->service_id = FS_SERVICE;
-       call->port = htons(AFS_FS_PORT);
+       call->key = fc->key;
+       call->reply[0] = vnode;
+       call->reply[1] = vs;
+       call->reply[2] = tmpbuf;
 
        /* marshall the parameters */
        bp = call->request;
        bp[0] = htonl(FSGETVOLUMESTATUS);
        bp[1] = htonl(vnode->fid.vid);
 
-       return afs_make_call(&server->addr, call, GFP_NOFS, async);
+       afs_use_fs_server(call, fc->cbi);
+       trace_afs_make_fs_call(call, &vnode->fid);
+       return afs_make_call(&fc->ac, call, GFP_NOFS, false);
 }
 
 /*
@@ -1733,7 +1679,7 @@ static int afs_deliver_fs_xxxx_lock(struct afs_call *call)
 
        /* unmarshall the reply once we've received all of it */
        bp = call->buffer;
-       /* xdr_decode_AFSVolSync(&bp, call->replyX); */
+       /* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
 
        _leave(" = 0 [done]");
        return 0;
@@ -1744,8 +1690,8 @@ static int afs_deliver_fs_xxxx_lock(struct afs_call *call)
  */
 static const struct afs_call_type afs_RXFSSetLock = {
        .name           = "FS.SetLock",
+       .op             = afs_FS_SetLock,
        .deliver        = afs_deliver_fs_xxxx_lock,
-       .abort_to_error = afs_abort_to_error,
        .destructor     = afs_flat_call_destructor,
 };
 
@@ -1754,8 +1700,8 @@ static const struct afs_call_type afs_RXFSSetLock = {
  */
 static const struct afs_call_type afs_RXFSExtendLock = {
        .name           = "FS.ExtendLock",
+       .op             = afs_FS_ExtendLock,
        .deliver        = afs_deliver_fs_xxxx_lock,
-       .abort_to_error = afs_abort_to_error,
        .destructor     = afs_flat_call_destructor,
 };
 
@@ -1764,33 +1710,29 @@ static const struct afs_call_type afs_RXFSExtendLock = {
  */
 static const struct afs_call_type afs_RXFSReleaseLock = {
        .name           = "FS.ReleaseLock",
+       .op             = afs_FS_ReleaseLock,
        .deliver        = afs_deliver_fs_xxxx_lock,
-       .abort_to_error = afs_abort_to_error,
        .destructor     = afs_flat_call_destructor,
 };
 
 /*
- * get a lock on a file
+ * Set a lock on a file
  */
-int afs_fs_set_lock(struct afs_server *server,
-                   struct key *key,
-                   struct afs_vnode *vnode,
-                   afs_lock_type_t type,
-                   bool async)
+int afs_fs_set_lock(struct afs_fs_cursor *fc, afs_lock_type_t type)
 {
+       struct afs_vnode *vnode = fc->vnode;
        struct afs_call *call;
+       struct afs_net *net = afs_v2net(vnode);
        __be32 *bp;
 
        _enter("");
 
-       call = afs_alloc_flat_call(&afs_RXFSSetLock, 5 * 4, 6 * 4);
+       call = afs_alloc_flat_call(net, &afs_RXFSSetLock, 5 * 4, 6 * 4);
        if (!call)
                return -ENOMEM;
 
-       call->key = key;
-       call->reply = vnode;
-       call->service_id = FS_SERVICE;
-       call->port = htons(AFS_FS_PORT);
+       call->key = fc->key;
+       call->reply[0] = vnode;
 
        /* marshall the parameters */
        bp = call->request;
@@ -1800,30 +1742,29 @@ int afs_fs_set_lock(struct afs_server *server,
        *bp++ = htonl(vnode->fid.unique);
        *bp++ = htonl(type);
 
-       return afs_make_call(&server->addr, call, GFP_NOFS, async);
+       afs_use_fs_server(call, fc->cbi);
+       trace_afs_make_fs_call(call, &vnode->fid);
+       return afs_make_call(&fc->ac, call, GFP_NOFS, false);
 }
 
 /*
  * extend a lock on a file
  */
-int afs_fs_extend_lock(struct afs_server *server,
-                      struct key *key,
-                      struct afs_vnode *vnode,
-                      bool async)
+int afs_fs_extend_lock(struct afs_fs_cursor *fc)
 {
+       struct afs_vnode *vnode = fc->vnode;
        struct afs_call *call;
+       struct afs_net *net = afs_v2net(vnode);
        __be32 *bp;
 
        _enter("");
 
-       call = afs_alloc_flat_call(&afs_RXFSExtendLock, 4 * 4, 6 * 4);
+       call = afs_alloc_flat_call(net, &afs_RXFSExtendLock, 4 * 4, 6 * 4);
        if (!call)
                return -ENOMEM;
 
-       call->key = key;
-       call->reply = vnode;
-       call->service_id = FS_SERVICE;
-       call->port = htons(AFS_FS_PORT);
+       call->key = fc->key;
+       call->reply[0] = vnode;
 
        /* marshall the parameters */
        bp = call->request;
@@ -1832,30 +1773,29 @@ int afs_fs_extend_lock(struct afs_server *server,
        *bp++ = htonl(vnode->fid.vnode);
        *bp++ = htonl(vnode->fid.unique);
 
-       return afs_make_call(&server->addr, call, GFP_NOFS, async);
+       afs_use_fs_server(call, fc->cbi);
+       trace_afs_make_fs_call(call, &vnode->fid);
+       return afs_make_call(&fc->ac, call, GFP_NOFS, false);
 }
 
 /*
  * release a lock on a file
  */
-int afs_fs_release_lock(struct afs_server *server,
-                       struct key *key,
-                       struct afs_vnode *vnode,
-                       bool async)
+int afs_fs_release_lock(struct afs_fs_cursor *fc)
 {
+       struct afs_vnode *vnode = fc->vnode;
        struct afs_call *call;
+       struct afs_net *net = afs_v2net(vnode);
        __be32 *bp;
 
        _enter("");
 
-       call = afs_alloc_flat_call(&afs_RXFSReleaseLock, 4 * 4, 6 * 4);
+       call = afs_alloc_flat_call(net, &afs_RXFSReleaseLock, 4 * 4, 6 * 4);
        if (!call)
                return -ENOMEM;
 
-       call->key = key;
-       call->reply = vnode;
-       call->service_id = FS_SERVICE;
-       call->port = htons(AFS_FS_PORT);
+       call->key = fc->key;
+       call->reply[0] = vnode;
 
        /* marshall the parameters */
        bp = call->request;
@@ -1864,5 +1804,145 @@ int afs_fs_release_lock(struct afs_server *server,
        *bp++ = htonl(vnode->fid.vnode);
        *bp++ = htonl(vnode->fid.unique);
 
-       return afs_make_call(&server->addr, call, GFP_NOFS, async);
+       afs_use_fs_server(call, fc->cbi);
+       trace_afs_make_fs_call(call, &vnode->fid);
+       return afs_make_call(&fc->ac, call, GFP_NOFS, false);
+}
+
+/*
+ * Deliver reply data to an FS.GiveUpAllCallBacks operation.
+ */
+static int afs_deliver_fs_give_up_all_callbacks(struct afs_call *call)
+{
+       return afs_transfer_reply(call);
+}
+
+/*
+ * FS.GiveUpAllCallBacks operation type
+ */
+static const struct afs_call_type afs_RXFSGiveUpAllCallBacks = {
+       .name           = "FS.GiveUpAllCallBacks",
+       .op             = afs_FS_GiveUpAllCallBacks,
+       .deliver        = afs_deliver_fs_give_up_all_callbacks,
+       .destructor     = afs_flat_call_destructor,
+};
+
+/*
+ * Flush all the callbacks we have on a server.
+ */
+int afs_fs_give_up_all_callbacks(struct afs_net *net,
+                                struct afs_server *server,
+                                struct afs_addr_cursor *ac,
+                                struct key *key)
+{
+       struct afs_call *call;
+       __be32 *bp;
+
+       _enter("");
+
+       call = afs_alloc_flat_call(net, &afs_RXFSGiveUpAllCallBacks, 1 * 4, 0);
+       if (!call)
+               return -ENOMEM;
+
+       call->key = key;
+
+       /* marshall the parameters */
+       bp = call->request;
+       *bp++ = htonl(FSGIVEUPALLCALLBACKS);
+
+       /* Can't take a ref on server */
+       return afs_make_call(ac, call, GFP_NOFS, false);
+}
+
+/*
+ * Deliver reply data to an FS.GetCapabilities operation.
+ */
+static int afs_deliver_fs_get_capabilities(struct afs_call *call)
+{
+       u32 count;
+       int ret;
+
+       _enter("{%u,%zu/%u}", call->unmarshall, call->offset, call->count);
+
+again:
+       switch (call->unmarshall) {
+       case 0:
+               call->offset = 0;
+               call->unmarshall++;
+
+               /* Extract the capabilities word count */
+       case 1:
+               ret = afs_extract_data(call, &call->tmp,
+                                      1 * sizeof(__be32),
+                                      true);
+               if (ret < 0)
+                       return ret;
+
+               count = ntohl(call->tmp);
+
+               call->count = count;
+               call->count2 = count;
+               call->offset = 0;
+               call->unmarshall++;
+
+               /* Extract capabilities words */
+       case 2:
+               count = min(call->count, 16U);
+               ret = afs_extract_data(call, call->buffer,
+                                      count * sizeof(__be32),
+                                      call->count > 16);
+               if (ret < 0)
+                       return ret;
+
+               /* TODO: Examine capabilities */
+
+               call->count -= count;
+               if (call->count > 0)
+                       goto again;
+               call->offset = 0;
+               call->unmarshall++;
+               break;
+       }
+
+       _leave(" = 0 [done]");
+       return 0;
+}
+
+/*
+ * FS.GetCapabilities operation type
+ */
+static const struct afs_call_type afs_RXFSGetCapabilities = {
+       .name           = "FS.GetCapabilities",
+       .op             = afs_FS_GetCapabilities,
+       .deliver        = afs_deliver_fs_get_capabilities,
+       .destructor     = afs_flat_call_destructor,
+};
+
+/*
+ * Probe a fileserver for the capabilities that it supports.  This can
+ * return up to 196 words.
+ */
+int afs_fs_get_capabilities(struct afs_net *net,
+                           struct afs_server *server,
+                           struct afs_addr_cursor *ac,
+                           struct key *key)
+{
+       struct afs_call *call;
+       __be32 *bp;
+
+       _enter("");
+
+       call = afs_alloc_flat_call(net, &afs_RXFSGetCapabilities, 1 * 4, 16 * 4);
+       if (!call)
+               return -ENOMEM;
+
+       call->key = key;
+
+       /* marshall the parameters */
+       bp = call->request;
+       *bp++ = htonl(FSGETCAPABILITIES);
+
+       /* Can't take a ref on server */
+       trace_afs_make_fs_call(call, NULL);
+       return afs_make_call(ac, call, GFP_NOFS, false);
 }
index 342316a9e3e0cb47449f7482cce9f5e5bdfe211a..3415eb7484f6ba5653e3f08c8e3035ab29b2eaa3 100644 (file)
 #include <linux/namei.h>
 #include "internal.h"
 
-struct afs_iget_data {
-       struct afs_fid          fid;
-       struct afs_volume       *volume;        /* volume on which resides */
-};
-
 static const struct inode_operations afs_symlink_inode_operations = {
        .get_link       = page_get_link,
        .listxattr      = afs_listxattr,
@@ -39,6 +34,7 @@ static const struct inode_operations afs_symlink_inode_operations = {
 static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)
 {
        struct inode *inode = AFS_VNODE_TO_I(vnode);
+       bool changed;
 
        _debug("FS: ft=%d lk=%d sz=%llu ver=%Lu mod=%hu",
               vnode->status.type,
@@ -47,6 +43,8 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)
               vnode->status.data_version,
               vnode->status.mode);
 
+       read_seqlock_excl(&vnode->cb_lock);
+
        switch (vnode->status.type) {
        case AFS_FTYPE_FILE:
                inode->i_mode   = S_IFREG | vnode->status.mode;
@@ -63,9 +61,7 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)
                if ((vnode->status.mode & 0777) == 0644) {
                        inode->i_flags |= S_AUTOMOUNT;
 
-                       spin_lock(&vnode->lock);
                        set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
-                       spin_unlock(&vnode->lock);
 
                        inode->i_mode   = S_IFDIR | 0555;
                        inode->i_op     = &afs_mntpt_inode_operations;
@@ -78,13 +74,11 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)
                break;
        default:
                printk("kAFS: AFS vnode with undefined type\n");
+               read_sequnlock_excl(&vnode->cb_lock);
                return -EBADMSG;
        }
 
-#ifdef CONFIG_AFS_FSCACHE
-       if (vnode->status.size != inode->i_size)
-               fscache_attr_changed(vnode->cache);
-#endif
+       changed = (vnode->status.size != inode->i_size);
 
        set_nlink(inode, vnode->status.nlink);
        inode->i_uid            = vnode->status.owner;
@@ -97,13 +91,49 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)
        inode->i_generation     = vnode->fid.unique;
        inode->i_version        = vnode->status.data_version;
        inode->i_mapping->a_ops = &afs_fs_aops;
+
+       read_sequnlock_excl(&vnode->cb_lock);
+
+#ifdef CONFIG_AFS_FSCACHE
+       if (changed)
+               fscache_attr_changed(vnode->cache);
+#endif
        return 0;
 }
 
+/*
+ * Fetch file status from the volume.
+ */
+int afs_fetch_status(struct afs_vnode *vnode, struct key *key)
+{
+       struct afs_fs_cursor fc;
+       int ret;
+
+       _enter("%s,{%x:%u.%u,S=%lx}",
+              vnode->volume->name,
+              vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique,
+              vnode->flags);
+
+       ret = -ERESTARTSYS;
+       if (afs_begin_vnode_operation(&fc, vnode, key)) {
+               while (afs_select_fileserver(&fc)) {
+                       fc.cb_break = vnode->cb_break + vnode->cb_s_break;
+                       afs_fs_fetch_file_status(&fc, NULL);
+               }
+
+               afs_check_for_remote_deletion(&fc, fc.vnode);
+               afs_vnode_commit_status(&fc, vnode, fc.cb_break);
+               ret = afs_end_vnode_operation(&fc);
+       }
+
+       _leave(" = %d", ret);
+       return ret;
+}
+
 /*
  * iget5() comparator
  */
-static int afs_iget5_test(struct inode *inode, void *opaque)
+int afs_iget5_test(struct inode *inode, void *opaque)
 {
        struct afs_iget_data *data = opaque;
 
@@ -204,7 +234,7 @@ struct inode *afs_iget_autocell(struct inode *dir, const char *dev_name,
  */
 struct inode *afs_iget(struct super_block *sb, struct key *key,
                       struct afs_fid *fid, struct afs_file_status *status,
-                      struct afs_callback *cb)
+                      struct afs_callback *cb, struct afs_cb_interest *cbi)
 {
        struct afs_iget_data data = { .fid = *fid };
        struct afs_super_info *as;
@@ -237,8 +267,7 @@ struct inode *afs_iget(struct super_block *sb, struct key *key,
 
        if (!status) {
                /* it's a remotely extant inode */
-               set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
-               ret = afs_vnode_fetch_status(vnode, NULL, key);
+               ret = afs_fetch_status(vnode, key);
                if (ret < 0)
                        goto bad_inode;
        } else {
@@ -249,16 +278,17 @@ struct inode *afs_iget(struct super_block *sb, struct key *key,
                        /* it's a symlink we just created (the fileserver
                         * didn't give us a callback) */
                        vnode->cb_version = 0;
-                       vnode->cb_expiry = 0;
                        vnode->cb_type = 0;
-                       vnode->cb_expires = ktime_get_real_seconds();
+                       vnode->cb_expires_at = 0;
                } else {
                        vnode->cb_version = cb->version;
-                       vnode->cb_expiry = cb->expiry;
                        vnode->cb_type = cb->type;
-                       vnode->cb_expires = vnode->cb_expiry +
-                               ktime_get_real_seconds();
+                       vnode->cb_expires_at = cb->expiry;
+                       vnode->cb_interest = afs_get_cb_interest(cbi);
+                       set_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
                }
+
+               vnode->cb_expires_at += ktime_get_real_seconds();
        }
 
        /* set up caching before mapping the status, as map-status reads the
@@ -320,25 +350,34 @@ void afs_zap_data(struct afs_vnode *vnode)
  */
 int afs_validate(struct afs_vnode *vnode, struct key *key)
 {
+       time64_t now = ktime_get_real_seconds();
+       bool valid = false;
        int ret;
 
        _enter("{v={%x:%u} fl=%lx},%x",
               vnode->fid.vid, vnode->fid.vnode, vnode->flags,
               key_serial(key));
 
-       if (vnode->cb_promised &&
-           !test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) &&
-           !test_bit(AFS_VNODE_MODIFIED, &vnode->flags) &&
-           !test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) {
-               if (vnode->cb_expires < ktime_get_real_seconds() + 10) {
-                       _debug("callback expired");
-                       set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
-               } else {
-                       goto valid;
+       /* Quickly check the callback state.  Ideally, we'd use read_seqbegin
+        * here, but we have no way to pass the net namespace to the RCU
+        * cleanup for the server record.
+        */
+       read_seqlock_excl(&vnode->cb_lock);
+
+       if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
+               if (vnode->cb_s_break != vnode->cb_interest->server->cb_s_break) {
+                       vnode->cb_s_break = vnode->cb_interest->server->cb_s_break;
+               } else if (!test_bit(AFS_VNODE_DIR_MODIFIED, &vnode->flags) &&
+                          !test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags) &&
+                          vnode->cb_expires_at - 10 > now) {
+                               valid = true;
                }
+       } else if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
+               valid = true;
        }
 
-       if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
+       read_sequnlock_excl(&vnode->cb_lock);
+       if (valid)
                goto valid;
 
        mutex_lock(&vnode->validate_lock);
@@ -347,12 +386,16 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)
         * a new promise - note that if the (parent) directory's metadata was
         * changed then the security may be different and we may no longer have
         * access */
-       if (!vnode->cb_promised ||
-           test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) {
+       if (!test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
                _debug("not promised");
-               ret = afs_vnode_fetch_status(vnode, NULL, key);
-               if (ret < 0)
+               ret = afs_fetch_status(vnode, key);
+               if (ret < 0) {
+                       if (ret == -ENOENT) {
+                               set_bit(AFS_VNODE_DELETED, &vnode->flags);
+                               ret = -ESTALE;
+                       }
                        goto error_unlock;
+               }
                _debug("new promise [fl=%lx]", vnode->flags);
        }
 
@@ -367,7 +410,7 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)
        if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags))
                afs_zap_data(vnode);
 
-       clear_bit(AFS_VNODE_MODIFIED, &vnode->flags);
+       clear_bit(AFS_VNODE_DIR_MODIFIED, &vnode->flags);
        mutex_unlock(&vnode->validate_lock);
 valid:
        _leave(" = 0");
@@ -386,10 +429,17 @@ int afs_getattr(const struct path *path, struct kstat *stat,
                u32 request_mask, unsigned int query_flags)
 {
        struct inode *inode = d_inode(path->dentry);
+       struct afs_vnode *vnode = AFS_FS_I(inode);
+       int seq = 0;
 
        _enter("{ ino=%lu v=%u }", inode->i_ino, inode->i_generation);
 
-       generic_fillattr(inode, stat);
+       do {
+               read_seqbegin_or_lock(&vnode->cb_lock, &seq);
+               generic_fillattr(inode, stat);
+       } while (need_seqretry(&vnode->cb_lock, seq));
+
+       done_seqretry(&vnode->cb_lock, seq);
        return 0;
 }
 
@@ -411,18 +461,14 @@ int afs_drop_inode(struct inode *inode)
  */
 void afs_evict_inode(struct inode *inode)
 {
-       struct afs_permits *permits;
        struct afs_vnode *vnode;
 
        vnode = AFS_FS_I(inode);
 
-       _enter("{%x:%u.%d} v=%u x=%u t=%u }",
+       _enter("{%x:%u.%d}",
               vnode->fid.vid,
               vnode->fid.vnode,
-              vnode->fid.unique,
-              vnode->cb_version,
-              vnode->cb_expiry,
-              vnode->cb_type);
+              vnode->fid.unique);
 
        _debug("CLEAR INODE %p", inode);
 
@@ -431,31 +477,24 @@ void afs_evict_inode(struct inode *inode)
        truncate_inode_pages_final(&inode->i_data);
        clear_inode(inode);
 
-       afs_give_up_callback(vnode);
-
-       if (vnode->server) {
-               spin_lock(&vnode->server->fs_lock);
-               rb_erase(&vnode->server_rb, &vnode->server->fs_vnodes);
-               spin_unlock(&vnode->server->fs_lock);
-               afs_put_server(vnode->server);
-               vnode->server = NULL;
+       if (vnode->cb_interest) {
+               afs_put_cb_interest(afs_i2net(inode), vnode->cb_interest);
+               vnode->cb_interest = NULL;
        }
 
-       ASSERT(list_empty(&vnode->writebacks));
-       ASSERT(!vnode->cb_promised);
+       while (!list_empty(&vnode->wb_keys)) {
+               struct afs_wb_key *wbk = list_entry(vnode->wb_keys.next,
+                                                   struct afs_wb_key, vnode_link);
+               list_del(&wbk->vnode_link);
+               afs_put_wb_key(wbk);
+       }
 
 #ifdef CONFIG_AFS_FSCACHE
        fscache_relinquish_cookie(vnode->cache, 0);
        vnode->cache = NULL;
 #endif
 
-       mutex_lock(&vnode->permits_lock);
-       permits = vnode->permits;
-       RCU_INIT_POINTER(vnode->permits, NULL);
-       mutex_unlock(&vnode->permits_lock);
-       if (permits)
-               call_rcu(&permits->rcu, afs_zap_permits);
-
+       afs_put_permits(vnode->permit_cache);
        _leave("");
 }
 
@@ -464,6 +503,7 @@ void afs_evict_inode(struct inode *inode)
  */
 int afs_setattr(struct dentry *dentry, struct iattr *attr)
 {
+       struct afs_fs_cursor fc;
        struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry));
        struct key *key;
        int ret;
@@ -479,13 +519,11 @@ int afs_setattr(struct dentry *dentry, struct iattr *attr)
        }
 
        /* flush any dirty data outstanding on a regular file */
-       if (S_ISREG(vnode->vfs_inode.i_mode)) {
+       if (S_ISREG(vnode->vfs_inode.i_mode))
                filemap_write_and_wait(vnode->vfs_inode.i_mapping);
-               afs_writeback_all(vnode);
-       }
 
        if (attr->ia_valid & ATTR_FILE) {
-               key = attr->ia_file->private_data;
+               key = afs_file_key(attr->ia_file);
        } else {
                key = afs_request_key(vnode->volume->cell);
                if (IS_ERR(key)) {
@@ -494,7 +532,18 @@ int afs_setattr(struct dentry *dentry, struct iattr *attr)
                }
        }
 
-       ret = afs_vnode_setattr(vnode, key, attr);
+       ret = -ERESTARTSYS;
+       if (afs_begin_vnode_operation(&fc, vnode, key)) {
+               while (afs_select_fileserver(&fc)) {
+                       fc.cb_break = vnode->cb_break + vnode->cb_s_break;
+                       afs_fs_setattr(&fc, attr);
+               }
+
+               afs_check_for_remote_deletion(&fc, fc.vnode);
+               afs_vnode_commit_status(&fc, vnode, fc.cb_break);
+               ret = afs_end_vnode_operation(&fc);
+       }
+
        if (!(attr->ia_valid & ATTR_FILE))
                key_put(key);
 
index 3f03f7888302307de9c3c1c590a09ce3a5532d3f..bd8dcee7e066719439d5decb0d970e6142d167eb 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/fscache.h>
 #include <linux/backing-dev.h>
 #include <linux/uuid.h>
+#include <net/net_namespace.h>
 #include <net/af_rxrpc.h>
 
 #include "afs.h"
 struct pagevec;
 struct afs_call;
 
-typedef enum {
-       AFS_VL_NEW,                     /* new, uninitialised record */
-       AFS_VL_CREATING,                /* creating record */
-       AFS_VL_VALID,                   /* record is pending */
-       AFS_VL_NO_VOLUME,               /* no such volume available */
-       AFS_VL_UPDATING,                /* update in progress */
-       AFS_VL_VOLUME_DELETED,          /* volume was deleted */
-       AFS_VL_UNCERTAIN,               /* uncertain state (update failed) */
-} __attribute__((packed)) afs_vlocation_state_t;
-
 struct afs_mount_params {
        bool                    rwpath;         /* T if the parent should be considered R/W */
        bool                    force;          /* T to force cell type */
@@ -48,20 +39,43 @@ struct afs_mount_params {
        afs_voltype_t           type;           /* type of volume requested */
        int                     volnamesz;      /* size of volume name */
        const char              *volname;       /* name of volume to mount */
+       struct afs_net          *net;           /* Network namespace in effect */
        struct afs_cell         *cell;          /* cell in which to find volume */
        struct afs_volume       *volume;        /* volume record */
        struct key              *key;           /* key to use for secure mounting */
 };
 
+struct afs_iget_data {
+       struct afs_fid          fid;
+       struct afs_volume       *volume;        /* volume on which resides */
+};
+
 enum afs_call_state {
-       AFS_CALL_REQUESTING,    /* request is being sent for outgoing call */
-       AFS_CALL_AWAIT_REPLY,   /* awaiting reply to outgoing call */
-       AFS_CALL_AWAIT_OP_ID,   /* awaiting op ID on incoming call */
-       AFS_CALL_AWAIT_REQUEST, /* awaiting request data on incoming call */
-       AFS_CALL_REPLYING,      /* replying to incoming call */
-       AFS_CALL_AWAIT_ACK,     /* awaiting final ACK of incoming call */
-       AFS_CALL_COMPLETE,      /* Completed or failed */
+       AFS_CALL_CL_REQUESTING,         /* Client: Request is being sent */
+       AFS_CALL_CL_AWAIT_REPLY,        /* Client: Awaiting reply */
+       AFS_CALL_CL_PROC_REPLY,         /* Client: rxrpc call complete; processing reply */
+       AFS_CALL_SV_AWAIT_OP_ID,        /* Server: Awaiting op ID */
+       AFS_CALL_SV_AWAIT_REQUEST,      /* Server: Awaiting request data */
+       AFS_CALL_SV_REPLYING,           /* Server: Replying */
+       AFS_CALL_SV_AWAIT_ACK,          /* Server: Awaiting final ACK */
+       AFS_CALL_COMPLETE,              /* Completed or failed */
 };
+
+/*
+ * List of server addresses.
+ */
+struct afs_addr_list {
+       struct rcu_head         rcu;            /* Must be first */
+       refcount_t              usage;
+       u32                     version;        /* Version */
+       unsigned short          nr_addrs;
+       unsigned short          index;          /* Address currently in use */
+       unsigned short          nr_ipv4;        /* Number of IPv4 addresses */
+       unsigned long           probed;         /* Mask of servers that have been probed */
+       unsigned long           yfs;            /* Mask of servers that are YFS */
+       struct sockaddr_rxrpc   addrs[];
+};
+
 /*
  * a record of an in-progress RxRPC call
  */
@@ -72,25 +86,25 @@ struct afs_call {
        struct work_struct      work;           /* actual work processor */
        struct rxrpc_call       *rxcall;        /* RxRPC call handle */
        struct key              *key;           /* security for this call */
-       struct afs_server       *server;        /* server affected by incoming CM call */
+       struct afs_net          *net;           /* The network namespace */
+       struct afs_server       *cm_server;     /* Server affected by incoming CM call */
+       struct afs_cb_interest  *cbi;           /* Callback interest for server used */
        void                    *request;       /* request data (first part) */
-       struct address_space    *mapping;       /* page set */
-       struct afs_writeback    *wb;            /* writeback being performed */
+       struct address_space    *mapping;       /* Pages being written from */
        void                    *buffer;        /* reply receive buffer */
-       void                    *reply;         /* reply buffer (first part) */
-       void                    *reply2;        /* reply buffer (second part) */
-       void                    *reply3;        /* reply buffer (third part) */
-       void                    *reply4;        /* reply buffer (fourth part) */
+       void                    *reply[4];      /* Where to put the reply */
        pgoff_t                 first;          /* first page in mapping to deal with */
        pgoff_t                 last;           /* last page in mapping to deal with */
        size_t                  offset;         /* offset into received data store */
        atomic_t                usage;
        enum afs_call_state     state;
+       spinlock_t              state_lock;
        int                     error;          /* error code */
        u32                     abort_code;     /* Remote abort ID or 0 */
        unsigned                request_size;   /* size of request data */
        unsigned                reply_max;      /* maximum size of reply */
        unsigned                first_offset;   /* offset into mapping[first] */
+       unsigned int            cb_break;       /* cb_break + cb_s_break before the call */
        union {
                unsigned        last_to;        /* amount of mapping[last] */
                unsigned        count2;         /* count used in unmarshalling */
@@ -100,9 +114,9 @@ struct afs_call {
        bool                    send_pages;     /* T if data from mapping should be sent */
        bool                    need_attention; /* T if RxRPC poked us */
        bool                    async;          /* T if asynchronous */
+       bool                    ret_reply0;     /* T if should return reply[0] on success */
        bool                    upgrade;        /* T to request service upgrade */
-       u16                     service_id;     /* RxRPC service ID to call */
-       __be16                  port;           /* target UDP port */
+       u16                     service_id;     /* Actual service ID (after upgrade) */
        u32                     operation_ID;   /* operation ID for an incoming call */
        u32                     count;          /* count for use in unmarshalling */
        __be32                  tmp;            /* place to extract temporary data */
@@ -111,15 +125,13 @@ struct afs_call {
 
 struct afs_call_type {
        const char *name;
+       unsigned int op; /* Really enum afs_fs_operation */
 
        /* deliver request or reply data to an call
         * - returning an error will cause the call to be aborted
         */
        int (*deliver)(struct afs_call *call);
 
-       /* map an abort code to an error number */
-       int (*abort_to_error)(u32 abort_code);
-
        /* clean up a call */
        void (*destructor)(struct afs_call *call);
 
@@ -127,6 +139,30 @@ struct afs_call_type {
        void (*work)(struct work_struct *work);
 };
 
+/*
+ * Key available for writeback on a file.
+ */
+struct afs_wb_key {
+       refcount_t              usage;
+       struct key              *key;
+       struct list_head        vnode_link;     /* Link in vnode->wb_keys */
+};
+
+/*
+ * AFS open file information record.  Pointed to by file->private_data.
+ */
+struct afs_file {
+       struct key              *key;           /* The key this file was opened with */
+       struct afs_wb_key       *wb;            /* Writeback key record for this file */
+};
+
+static inline struct key *afs_file_key(struct file *file)
+{
+       struct afs_file *af = file->private_data;
+
+       return af->key;
+}
+
 /*
  * Record of an outstanding read operation on a vnode.
  */
@@ -142,39 +178,14 @@ struct afs_read {
        struct page             *pages[];
 };
 
-/*
- * record of an outstanding writeback on a vnode
- */
-struct afs_writeback {
-       struct list_head        link;           /* link in vnode->writebacks */
-       struct work_struct      writer;         /* work item to perform the writeback */
-       struct afs_vnode        *vnode;         /* vnode to which this write applies */
-       struct key              *key;           /* owner of this write */
-       wait_queue_head_t       waitq;          /* completion and ready wait queue */
-       pgoff_t                 first;          /* first page in batch */
-       pgoff_t                 point;          /* last page in current store op */
-       pgoff_t                 last;           /* last page in batch (inclusive) */
-       unsigned                offset_first;   /* offset into first page of start of write */
-       unsigned                to_last;        /* offset into last page of end of write */
-       int                     num_conflicts;  /* count of conflicting writes in list */
-       int                     usage;
-       bool                    conflicts;      /* T if has dependent conflicts */
-       enum {
-               AFS_WBACK_SYNCING,              /* synchronisation being performed */
-               AFS_WBACK_PENDING,              /* write pending */
-               AFS_WBACK_CONFLICTING,          /* conflicting writes posted */
-               AFS_WBACK_WRITING,              /* writing back */
-               AFS_WBACK_COMPLETE              /* the writeback record has been unlinked */
-       } state __attribute__((packed));
-};
-
 /*
  * AFS superblock private data
  * - there's one superblock per volume
  */
 struct afs_super_info {
+       struct afs_net          *net;           /* Network namespace */
+       struct afs_cell         *cell;          /* The cell in which the volume resides */
        struct afs_volume       *volume;        /* volume record */
-       char                    rwparent;       /* T if parent is R/W AFS volume */
 };
 
 static inline struct afs_super_info *AFS_FS_S(struct super_block *sb)
@@ -185,149 +196,238 @@ static inline struct afs_super_info *AFS_FS_S(struct super_block *sb)
 extern struct file_system_type afs_fs_type;
 
 /*
- * entry in the cached cell catalogue
+ * AFS network namespace record.
  */
-struct afs_cache_cell {
-       char            name[AFS_MAXCELLNAME];  /* cell name (padded with NULs) */
-       struct in_addr  vl_servers[15];         /* cached cell VL servers */
+struct afs_net {
+       struct afs_uuid         uuid;
+       bool                    live;           /* F if this namespace is being removed */
+
+       /* AF_RXRPC I/O stuff */
+       struct socket           *socket;
+       struct afs_call         *spare_incoming_call;
+       struct work_struct      charge_preallocation_work;
+       struct mutex            socket_mutex;
+       atomic_t                nr_outstanding_calls;
+       atomic_t                nr_superblocks;
+
+       /* Cell database */
+       struct rb_root          cells;
+       struct afs_cell         *ws_cell;
+       struct work_struct      cells_manager;
+       struct timer_list       cells_timer;
+       atomic_t                cells_outstanding;
+       seqlock_t               cells_lock;
+
+       spinlock_t              proc_cells_lock;
+       struct list_head        proc_cells;
+
+       /* Known servers.  Theoretically each fileserver can only be in one
+        * cell, but in practice, people create aliases and subsets and there's
+        * no easy way to distinguish them.
+        */
+       seqlock_t               fs_lock;        /* For fs_servers */
+       struct rb_root          fs_servers;     /* afs_server (by server UUID or address) */
+       struct list_head        fs_updates;     /* afs_server (by update_at) */
+       struct hlist_head       fs_proc;        /* procfs servers list */
+
+       struct hlist_head       fs_addresses4;  /* afs_server (by lowest IPv4 addr) */
+       struct hlist_head       fs_addresses6;  /* afs_server (by lowest IPv6 addr) */
+       seqlock_t               fs_addr_lock;   /* For fs_addresses[46] */
+
+       struct work_struct      fs_manager;
+       struct timer_list       fs_timer;
+       atomic_t                servers_outstanding;
+
+       /* File locking renewal management */
+       struct mutex            lock_manager_mutex;
+
+       /* Misc */
+       struct proc_dir_entry   *proc_afs;              /* /proc/net/afs directory */
+};
+
+extern struct afs_net __afs_net;// Dummy AFS network namespace; TODO: replace with real netns
+
+enum afs_cell_state {
+       AFS_CELL_UNSET,
+       AFS_CELL_ACTIVATING,
+       AFS_CELL_ACTIVE,
+       AFS_CELL_DEACTIVATING,
+       AFS_CELL_INACTIVE,
+       AFS_CELL_FAILED,
 };
 
 /*
- * AFS cell record
+ * AFS cell record.
+ *
+ * This is a tricky concept to get right as it is possible to create aliases
+ * simply by pointing AFSDB/SRV records for two names at the same set of VL
+ * servers; it is also possible to do things like setting up two sets of VL
+ * servers, one of which provides a superset of the volumes provided by the
+ * other (for internal/external division, for example).
+ *
+ * Cells only exist in the sense that (a) a cell's name maps to a set of VL
+ * servers and (b) a cell's name is used by the client to select the key to use
+ * for authentication and encryption.  The cell name is not typically used in
+ * the protocol.
+ *
+ * There is no easy way to determine if two cells are aliases or one is a
+ * subset of another.
  */
 struct afs_cell {
-       atomic_t                usage;
-       struct list_head        link;           /* main cell list link */
+       union {
+               struct rcu_head rcu;
+               struct rb_node  net_node;       /* Node in net->cells */
+       };
+       struct afs_net          *net;
        struct key              *anonymous_key; /* anonymous user key for this cell */
+       struct work_struct      manager;        /* Manager for init/deinit/dns */
        struct list_head        proc_link;      /* /proc cell list link */
 #ifdef CONFIG_AFS_FSCACHE
        struct fscache_cookie   *cache;         /* caching cookie */
 #endif
-
-       /* server record management */
-       rwlock_t                servers_lock;   /* active server list lock */
-       struct list_head        servers;        /* active server list */
-
-       /* volume location record management */
-       struct rw_semaphore     vl_sem;         /* volume management serialisation semaphore */
-       struct list_head        vl_list;        /* cell's active VL record list */
-       spinlock_t              vl_lock;        /* vl_list 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 */
+       time64_t                dns_expiry;     /* Time AFSDB/SRV record expires */
+       time64_t                last_inactive;  /* Time of last drop of usage count */
+       atomic_t                usage;
+       unsigned long           flags;
+#define AFS_CELL_FL_NOT_READY  0               /* The cell record is not ready for use */
+#define AFS_CELL_FL_NO_GC      1               /* The cell was added manually, don't auto-gc */
+#define AFS_CELL_FL_NOT_FOUND  2               /* Permanent DNS error */
+#define AFS_CELL_FL_DNS_FAIL   3               /* Failed to access DNS */
+#define AFS_CELL_FL_NO_LOOKUP_YET 4            /* Not completed first DNS lookup yet */
+       enum afs_cell_state     state;
+       short                   error;
+
+       /* Active fileserver interaction state. */
+       struct list_head        proc_volumes;   /* procfs volume list */
+       rwlock_t                proc_lock;
+
+       /* VL server list. */
+       rwlock_t                vl_addrs_lock;  /* Lock on vl_addrs */
+       struct afs_addr_list    __rcu *vl_addrs; /* List of VL servers */
+       u8                      name_len;       /* Length of name */
+       char                    name[64 + 1];   /* Cell name, case-flattened and NUL-padded */
 };
 
 /*
- * entry in the cached volume location catalogue
+ * Cached VLDB entry.
+ *
+ * This is pointed to by cell->vldb_entries, indexed by name.
  */
-struct afs_cache_vlocation {
-       /* volume name (lowercase, padded with NULs) */
-       uint8_t                 name[AFS_MAXVOLNAME + 1];
+struct afs_vldb_entry {
+       afs_volid_t             vid[3];         /* Volume IDs for R/W, R/O and Bak volumes */
 
-       uint8_t                 nservers;       /* number of entries used in servers[] */
-       uint8_t                 vidmask;        /* voltype mask for vid[] */
-       uint8_t                 srvtmask[8];    /* voltype masks for servers[] */
+       unsigned long           flags;
+#define AFS_VLDB_HAS_RW                0               /* - R/W volume exists */
+#define AFS_VLDB_HAS_RO                1               /* - R/O volume exists */
+#define AFS_VLDB_HAS_BAK       2               /* - Backup volume exists */
+#define AFS_VLDB_QUERY_VALID   3               /* - Record is valid */
+#define AFS_VLDB_QUERY_ERROR   4               /* - VL server returned error */
+
+       uuid_t                  fs_server[AFS_NMAXNSERVERS];
+       u8                      fs_mask[AFS_NMAXNSERVERS];
 #define AFS_VOL_VTM_RW 0x01 /* R/W version of the volume is available (on this server) */
 #define AFS_VOL_VTM_RO 0x02 /* R/O version of the volume is available (on this server) */
 #define AFS_VOL_VTM_BAK        0x04 /* backup version of the volume is available (on this server) */
-
-       afs_volid_t             vid[3];         /* volume IDs for R/W, R/O and Bak volumes */
-       struct in_addr          servers[8];     /* fileserver addresses */
-       time_t                  rtime;          /* last retrieval time */
+       short                   error;
+       u8                      nr_servers;     /* Number of server records */
+       u8                      name_len;
+       u8                      name[AFS_MAXVOLNAME + 1]; /* NUL-padded volume name */
 };
 
 /*
- * volume -> vnode hash table entry
+ * Record of fileserver with which we're actively communicating.
  */
-struct afs_cache_vhash {
-       afs_voltype_t           vtype;          /* which volume variation */
-       uint8_t                 hash_bucket;    /* which hash bucket this represents */
-} __attribute__((packed));
+struct afs_server {
+       struct rcu_head         rcu;
+       union {
+               uuid_t          uuid;           /* Server ID */
+               struct afs_uuid _uuid;
+       };
 
-/*
- * AFS volume location record
- */
-struct afs_vlocation {
+       struct afs_addr_list    __rcu *addresses;
+       struct rb_node          uuid_rb;        /* Link in net->servers */
+       struct hlist_node       addr4_link;     /* Link in net->fs_addresses4 */
+       struct hlist_node       addr6_link;     /* Link in net->fs_addresses6 */
+       struct hlist_node       proc_link;      /* Link in net->fs_proc */
+       struct afs_server       *gc_next;       /* Next server in manager's list */
+       time64_t                put_time;       /* Time at which last put */
+       time64_t                update_at;      /* Time at which to next update the record */
+       unsigned long           flags;
+#define AFS_SERVER_FL_NEW      0               /* New server, don't inc cb_s_break */
+#define AFS_SERVER_FL_NOT_READY        1               /* The record is not ready for use */
+#define AFS_SERVER_FL_NOT_FOUND        2               /* VL server says no such server */
+#define AFS_SERVER_FL_VL_FAIL  3               /* Failed to access VL server */
+#define AFS_SERVER_FL_UPDATING 4
+#define AFS_SERVER_FL_PROBED   5               /* The fileserver has been probed */
+#define AFS_SERVER_FL_PROBING  6               /* Fileserver is being probed */
        atomic_t                usage;
-       time64_t                time_of_death;  /* time at which put reduced usage to 0 */
-       struct list_head        link;           /* link in cell volume location list */
-       struct list_head        grave;          /* link in master graveyard list */
-       struct list_head        update;         /* link in master update list */
-       struct afs_cell         *cell;          /* cell to which volume belongs */
-#ifdef CONFIG_AFS_FSCACHE
-       struct fscache_cookie   *cache;         /* caching cookie */
-#endif
-       struct afs_cache_vlocation vldb;        /* volume information DB record */
-       struct afs_volume       *vols[3];       /* volume access record pointer (index by type) */
-       wait_queue_head_t       waitq;          /* status change waitqueue */
-       time64_t                update_at;      /* time at which record should be updated */
-       spinlock_t              lock;           /* access lock */
-       afs_vlocation_state_t   state;          /* volume location state */
-       unsigned short          upd_rej_cnt;    /* ENOMEDIUM count during update */
-       unsigned short          upd_busy_cnt;   /* EBUSY count during update */
-       bool                    valid;          /* T if valid */
+       u32                     addr_version;   /* Address list version */
+
+       /* file service access */
+       rwlock_t                fs_lock;        /* access lock */
+
+       /* callback promise management */
+       struct list_head        cb_interests;   /* List of superblocks using this server */
+       unsigned                cb_s_break;     /* Break-everything counter. */
+       rwlock_t                cb_break_lock;  /* Volume finding lock */
 };
 
 /*
- * AFS fileserver record
+ * Interest by a superblock on a server.
  */
-struct afs_server {
-       atomic_t                usage;
-       time64_t                time_of_death;  /* time at which put reduced usage to 0 */
-       struct in_addr          addr;           /* server address */
-       struct afs_cell         *cell;          /* cell in which server resides */
-       struct list_head        link;           /* link in cell's server list */
-       struct list_head        grave;          /* link in master graveyard list */
-       struct rb_node          master_rb;      /* link in master by-addr tree */
-       struct rw_semaphore     sem;            /* access lock */
+struct afs_cb_interest {
+       struct list_head        cb_link;        /* Link in server->cb_interests */
+       struct afs_server       *server;        /* Server on which this interest resides */
+       struct super_block      *sb;            /* Superblock on which inodes reside */
+       afs_volid_t             vid;            /* Volume ID to match */
+       refcount_t              usage;
+};
 
-       /* file service access */
-       struct rb_root          fs_vnodes;      /* vnodes backed by this server (ordered by FID) */
-       unsigned long           fs_act_jif;     /* time at which last activity occurred */
-       unsigned long           fs_dead_jif;    /* time at which no longer to be considered dead */
-       spinlock_t              fs_lock;        /* access lock */
-       int                     fs_state;       /* 0 or reason FS currently marked dead (-errno) */
+/*
+ * Replaceable server list.
+ */
+struct afs_server_entry {
+       struct afs_server       *server;
+       struct afs_cb_interest  *cb_interest;
+};
 
-       /* callback promise management */
-       struct rb_root          cb_promises;    /* vnode expiration list (ordered earliest first) */
-       struct delayed_work     cb_updater;     /* callback updater */
-       struct delayed_work     cb_break_work;  /* collected break dispatcher */
-       wait_queue_head_t       cb_break_waitq; /* space available in cb_break waitqueue */
-       spinlock_t              cb_lock;        /* access lock */
-       struct afs_callback     cb_break[64];   /* ring of callbacks awaiting breaking */
-       atomic_t                cb_break_n;     /* number of pending breaks */
-       u8                      cb_break_head;  /* head of callback breaking ring */
-       u8                      cb_break_tail;  /* tail of callback breaking ring */
+struct afs_server_list {
+       refcount_t              usage;
+       unsigned short          nr_servers;
+       unsigned short          index;          /* Server currently in use */
+       unsigned short          vnovol_mask;    /* Servers to be skipped due to VNOVOL */
+       unsigned int            seq;            /* Set to ->servers_seq when installed */
+       struct afs_server_entry servers[];
 };
 
 /*
- * AFS volume access record
+ * Live AFS volume management.
  */
 struct afs_volume {
+       afs_volid_t             vid;            /* volume ID */
        atomic_t                usage;
-       struct afs_cell         *cell;          /* cell to which belongs (unrefd ptr) */
-       struct afs_vlocation    *vlocation;     /* volume location */
+       time64_t                update_at;      /* Time at which to next update */
+       struct afs_cell         *cell;          /* Cell to which belongs (pins ref) */
+       struct list_head        proc_link;      /* Link in cell->vl_proc */
+       unsigned long           flags;
+#define AFS_VOLUME_NEEDS_UPDATE        0       /* - T if an update needs performing */
+#define AFS_VOLUME_UPDATING    1       /* - T if an update is in progress */
+#define AFS_VOLUME_WAIT                2       /* - T if users must wait for update */
+#define AFS_VOLUME_DELETED     3       /* - T if volume appears deleted */
+#define AFS_VOLUME_OFFLINE     4       /* - T if volume offline notice given */
+#define AFS_VOLUME_BUSY                5       /* - T if volume busy notice given */
 #ifdef CONFIG_AFS_FSCACHE
        struct fscache_cookie   *cache;         /* caching cookie */
 #endif
-       afs_volid_t             vid;            /* volume ID */
+       struct afs_server_list  *servers;       /* List of servers on which volume resides */
+       rwlock_t                servers_lock;   /* Lock for ->servers */
+       unsigned int            servers_seq;    /* Incremented each time ->servers changes */
+
        afs_voltype_t           type;           /* type of volume */
+       short                   error;
        char                    type_force;     /* force volume type (suppress R/O -> R/W) */
-       unsigned short          nservers;       /* number of server slots filled */
-       unsigned short          rjservers;      /* number of servers discarded due to -ENOMEDIUM */
-       struct afs_server       *servers[8];    /* servers on which volume resides (ordered) */
-       struct rw_semaphore     server_sem;     /* lock for accessing current server */
-};
-
-/*
- * vnode catalogue entry
- */
-struct afs_cache_vnode {
-       afs_vnodeid_t           vnode_id;       /* vnode ID */
-       unsigned                vnode_unique;   /* vnode ID uniquifier */
-       afs_dataversion_t       data_version;   /* data version */
+       u8                      name_len;
+       u8                      name[AFS_MAXVOLNAME + 1]; /* NUL-padded volume name */
 };
 
 /*
@@ -337,24 +437,20 @@ struct afs_vnode {
        struct inode            vfs_inode;      /* the VFS's inode record */
 
        struct afs_volume       *volume;        /* volume on which vnode resides */
-       struct afs_server       *server;        /* server currently supplying this file */
        struct afs_fid          fid;            /* the file identifier for this inode */
        struct afs_file_status  status;         /* AFS status info for this file */
 #ifdef CONFIG_AFS_FSCACHE
        struct fscache_cookie   *cache;         /* caching cookie */
 #endif
-       struct afs_permits      *permits;       /* cache of permits so far obtained */
-       struct mutex            permits_lock;   /* lock for altering permits list */
+       struct afs_permits      *permit_cache;  /* cache of permits so far obtained */
+       struct mutex            io_lock;        /* Lock for serialising I/O on this mutex */
        struct mutex            validate_lock;  /* lock for validating this vnode */
-       wait_queue_head_t       update_waitq;   /* status fetch waitqueue */
-       int                     update_cnt;     /* number of outstanding ops that will update the
-                                                * status */
-       spinlock_t              writeback_lock; /* lock for writebacks */
+       spinlock_t              wb_lock;        /* lock for wb_keys */
        spinlock_t              lock;           /* waitqueue/flags lock */
        unsigned long           flags;
-#define AFS_VNODE_CB_BROKEN    0               /* set if vnode's callback was broken */
+#define AFS_VNODE_CB_PROMISED  0               /* Set if vnode has a callback promise */
 #define AFS_VNODE_UNSET                1               /* set if vnode attributes not yet set */
-#define AFS_VNODE_MODIFIED     2               /* set if vnode's data modified */
+#define AFS_VNODE_DIR_MODIFIED 2               /* set if dir vnode's data modified */
 #define AFS_VNODE_ZAP_DATA     3               /* set if vnode's data should be invalidated */
 #define AFS_VNODE_DELETED      4               /* set if vnode deleted on server */
 #define AFS_VNODE_MOUNTPOINT   5               /* set if vnode is a mountpoint symlink */
@@ -365,24 +461,21 @@ struct afs_vnode {
 #define AFS_VNODE_AUTOCELL     10              /* set if Vnode is an auto mount point */
 #define AFS_VNODE_PSEUDODIR    11              /* set if Vnode is a pseudo directory */
 
-       long                    acl_order;      /* ACL check count (callback break count) */
-
-       struct list_head        writebacks;     /* alterations in pagecache that need writing */
+       struct list_head        wb_keys;        /* List of keys available for writeback */
        struct list_head        pending_locks;  /* locks waiting to be granted */
        struct list_head        granted_locks;  /* locks granted on this file */
        struct delayed_work     lock_work;      /* work to be done in locking */
        struct key              *unlock_key;    /* key to be used in unlocking */
 
        /* outstanding callback notification on this file */
-       struct rb_node          server_rb;      /* link in server->fs_vnodes */
-       struct rb_node          cb_promise;     /* link in server->cb_promises */
-       struct work_struct      cb_broken_work; /* work to be done on callback break */
-       time64_t                cb_expires;     /* time at which callback expires */
-       time64_t                cb_expires_at;  /* time used to order cb_promise */
+       struct afs_cb_interest  *cb_interest;   /* Server on which this resides */
+       unsigned int            cb_s_break;     /* Mass break counter on ->server */
+       unsigned int            cb_break;       /* Break counter on vnode */
+       seqlock_t               cb_lock;        /* Lock for ->cb_interest, ->status, ->cb_*break */
+
+       time64_t                cb_expires_at;  /* time at which callback expires */
        unsigned                cb_version;     /* callback version */
-       unsigned                cb_expiry;      /* callback expiry time */
        afs_callback_type_t     cb_type;        /* type of callback */
-       bool                    cb_promised;    /* true if promise still holds */
 };
 
 /*
@@ -390,16 +483,21 @@ struct afs_vnode {
  */
 struct afs_permit {
        struct key              *key;           /* RxRPC ticket holding a security context */
-       afs_access_t            access_mask;    /* access mask for this key */
+       afs_access_t            access;         /* CallerAccess value for this key */
 };
 
 /*
- * cache of security records from attempts to access a vnode
+ * Immutable cache of CallerAccess records from attempts to access vnodes.
+ * These may be shared between multiple vnodes.
  */
 struct afs_permits {
-       struct rcu_head         rcu;            /* disposal procedure */
-       int                     count;          /* number of records */
-       struct afs_permit       permits[0];     /* the permits so far examined */
+       struct rcu_head         rcu;
+       struct hlist_node       hash_node;      /* Link in hash */
+       unsigned long           h;              /* Hash value for this permit list */
+       refcount_t              usage;
+       unsigned short          nr_permits;     /* Number of records */
+       bool                    invalidated;    /* Invalidated due to key change */
+       struct afs_permit       permits[];      /* List of permits sorted by key pointer */
 };
 
 /*
@@ -411,28 +509,78 @@ struct afs_interface {
        unsigned        mtu;            /* MTU of interface */
 };
 
-struct afs_uuid {
-       __be32          time_low;                       /* low part of timestamp */
-       __be16          time_mid;                       /* mid part of timestamp */
-       __be16          time_hi_and_version;            /* high part of timestamp and version  */
-       __u8            clock_seq_hi_and_reserved;      /* clock seq hi and variant */
-       __u8            clock_seq_low;                  /* clock seq low */
-       __u8            node[6];                        /* spatially unique node ID (MAC addr) */
+/*
+ * Cursor for iterating over a server's address list.
+ */
+struct afs_addr_cursor {
+       struct afs_addr_list    *alist;         /* Current address list (pins ref) */
+       struct sockaddr_rxrpc   *addr;
+       u32                     abort_code;
+       unsigned short          start;          /* Starting point in alist->addrs[] */
+       unsigned short          index;          /* Wrapping offset from start to current addr */
+       short                   error;
+       bool                    begun;          /* T if we've begun iteration */
+       bool                    responded;      /* T if the current address responded */
+};
+
+/*
+ * Cursor for iterating over a set of fileservers.
+ */
+struct afs_fs_cursor {
+       struct afs_addr_cursor  ac;
+       struct afs_vnode        *vnode;
+       struct afs_server_list  *server_list;   /* Current server list (pins ref) */
+       struct afs_cb_interest  *cbi;           /* Server on which this resides (pins ref) */
+       struct key              *key;           /* Key for the server */
+       unsigned int            cb_break;       /* cb_break + cb_s_break before the call */
+       unsigned int            cb_break_2;     /* cb_break + cb_s_break (2nd vnode) */
+       unsigned char           start;          /* Initial index in server list */
+       unsigned char           index;          /* Number of servers tried beyond start */
+       unsigned short          flags;
+#define AFS_FS_CURSOR_STOP     0x0001          /* Set to cease iteration */
+#define AFS_FS_CURSOR_VBUSY    0x0002          /* Set if seen VBUSY */
+#define AFS_FS_CURSOR_VMOVED   0x0004          /* Set if seen VMOVED */
+#define AFS_FS_CURSOR_VNOVOL   0x0008          /* Set if seen VNOVOL */
+#define AFS_FS_CURSOR_CUR_ONLY 0x0010          /* Set if current server only (file lock held) */
+#define AFS_FS_CURSOR_NO_VSLEEP        0x0020          /* Set to prevent sleep on VBUSY, VOFFLINE, ... */
 };
 
+#include <trace/events/afs.h>
+
 /*****************************************************************************/
+/*
+ * addr_list.c
+ */
+static inline struct afs_addr_list *afs_get_addrlist(struct afs_addr_list *alist)
+{
+       if (alist)
+               refcount_inc(&alist->usage);
+       return alist;
+}
+extern struct afs_addr_list *afs_alloc_addrlist(unsigned int,
+                                               unsigned short,
+                                               unsigned short);
+extern void afs_put_addrlist(struct afs_addr_list *);
+extern struct afs_addr_list *afs_parse_text_addrs(const char *, size_t, char,
+                                                 unsigned short, unsigned short);
+extern struct afs_addr_list *afs_dns_query(struct afs_cell *, time64_t *);
+extern bool afs_iterate_addresses(struct afs_addr_cursor *);
+extern int afs_end_cursor(struct afs_addr_cursor *);
+extern int afs_set_vl_cursor(struct afs_addr_cursor *, struct afs_cell *);
+
+extern void afs_merge_fs_addr4(struct afs_addr_list *, __be32, u16);
+extern void afs_merge_fs_addr6(struct afs_addr_list *, __be32 *, u16);
+
 /*
  * cache.c
  */
 #ifdef CONFIG_AFS_FSCACHE
 extern struct fscache_netfs afs_cache_netfs;
 extern struct fscache_cookie_def afs_cell_cache_index_def;
-extern struct fscache_cookie_def afs_vlocation_cache_index_def;
 extern struct fscache_cookie_def afs_volume_cache_index_def;
 extern struct fscache_cookie_def afs_vnode_cache_index_def;
 #else
 #define afs_cell_cache_index_def       (*(struct fscache_cookie_def *) NULL)
-#define afs_vlocation_cache_index_def  (*(struct fscache_cookie_def *) NULL)
 #define afs_volume_cache_index_def     (*(struct fscache_cookie_def *) NULL)
 #define afs_vnode_cache_index_def      (*(struct fscache_cookie_def *) NULL)
 #endif
@@ -441,29 +589,31 @@ extern struct fscache_cookie_def afs_vnode_cache_index_def;
  * callback.c
  */
 extern void afs_init_callback_state(struct afs_server *);
-extern void afs_broken_callback_work(struct work_struct *);
-extern void afs_break_callbacks(struct afs_server *, size_t,
-                               struct afs_callback[]);
-extern void afs_discard_callback_on_delete(struct afs_vnode *);
-extern void afs_give_up_callback(struct afs_vnode *);
-extern void afs_dispatch_give_up_callbacks(struct work_struct *);
-extern void afs_flush_callback_breaks(struct afs_server *);
-extern int __init afs_callback_update_init(void);
-extern void afs_callback_update_kill(void);
+extern void afs_break_callback(struct afs_vnode *);
+extern void afs_break_callbacks(struct afs_server *, size_t,struct afs_callback[]);
+
+extern int afs_register_server_cb_interest(struct afs_vnode *, struct afs_server_entry *);
+extern void afs_put_cb_interest(struct afs_net *, struct afs_cb_interest *);
+extern void afs_clear_callback_interests(struct afs_net *, struct afs_server_list *);
+
+static inline struct afs_cb_interest *afs_get_cb_interest(struct afs_cb_interest *cbi)
+{
+       refcount_inc(&cbi->usage);
+       return cbi;
+}
 
 /*
  * cell.c
  */
-extern struct rw_semaphore afs_proc_cells_sem;
-extern struct list_head afs_proc_cells;
-
-#define afs_get_cell(C) do { atomic_inc(&(C)->usage); } while(0)
-extern int afs_cell_init(char *);
-extern struct afs_cell *afs_cell_create(const char *, unsigned, char *, bool);
-extern struct afs_cell *afs_cell_lookup(const char *, unsigned, bool);
-extern struct afs_cell *afs_grab_cell(struct afs_cell *);
-extern void afs_put_cell(struct afs_cell *);
-extern void afs_cell_purge(void);
+extern int afs_cell_init(struct afs_net *, const char *);
+extern struct afs_cell *afs_lookup_cell_rcu(struct afs_net *, const char *, unsigned);
+extern struct afs_cell *afs_lookup_cell(struct afs_net *, const char *, unsigned,
+                                       const char *, bool);
+extern struct afs_cell *afs_get_cell(struct afs_cell *);
+extern void afs_put_cell(struct afs_net *, struct afs_cell *);
+extern void afs_manage_cells(struct work_struct *);
+extern void afs_cells_timer(struct timer_list *);
+extern void __net_exit afs_cell_purge(struct afs_net *);
 
 /*
  * cmservice.c
@@ -473,6 +623,7 @@ extern bool afs_cm_incoming_call(struct afs_call *);
 /*
  * dir.c