summaryrefslogtreecommitdiffstats
path: root/fs/afs/vlocation.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/afs/vlocation.c')
-rw-r--r--fs/afs/vlocation.c954
1 files changed, 954 insertions, 0 deletions
diff --git a/fs/afs/vlocation.c b/fs/afs/vlocation.c
new file mode 100644
index 00000000000..eced20618ec
--- /dev/null
+++ b/fs/afs/vlocation.c
@@ -0,0 +1,954 @@
+/* vlocation.c: volume location management
+ *
+ * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include "volume.h"
+#include "cell.h"
+#include "cmservice.h"
+#include "fsclient.h"
+#include "vlclient.h"
+#include "kafstimod.h"
+#include <rxrpc/connection.h>
+#include "internal.h"
+
+#define AFS_VLDB_TIMEOUT HZ*1000
+
+static void afs_vlocation_update_timer(struct afs_timer *timer);
+static void afs_vlocation_update_attend(struct afs_async_op *op);
+static void afs_vlocation_update_discard(struct afs_async_op *op);
+static void __afs_put_vlocation(struct afs_vlocation *vlocation);
+
+static void __afs_vlocation_timeout(struct afs_timer *timer)
+{
+ struct afs_vlocation *vlocation =
+ list_entry(timer, struct afs_vlocation, timeout);
+
+ _debug("VL TIMEOUT [%s{u=%d}]",
+ vlocation->vldb.name, atomic_read(&vlocation->usage));
+
+ afs_vlocation_do_timeout(vlocation);
+}
+
+static const struct afs_timer_ops afs_vlocation_timer_ops = {
+ .timed_out = __afs_vlocation_timeout,
+};
+
+static const struct afs_timer_ops afs_vlocation_update_timer_ops = {
+ .timed_out = afs_vlocation_update_timer,
+};
+
+static const struct afs_async_op_ops afs_vlocation_update_op_ops = {
+ .attend = afs_vlocation_update_attend,
+ .discard = afs_vlocation_update_discard,
+};
+
+static LIST_HEAD(afs_vlocation_update_pendq); /* queue of VLs awaiting update */
+static struct afs_vlocation *afs_vlocation_update; /* VL currently being updated */
+static DEFINE_SPINLOCK(afs_vlocation_update_lock); /* lock guarding update queue */
+
+#ifdef AFS_CACHING_SUPPORT
+static cachefs_match_val_t afs_vlocation_cache_match(void *target,
+ const void *entry);
+static void afs_vlocation_cache_update(void *source, void *entry);
+
+struct cachefs_index_def afs_vlocation_cache_index_def = {
+ .name = "vldb",
+ .data_size = sizeof(struct afs_cache_vlocation),
+ .keys[0] = { CACHEFS_INDEX_KEYS_ASCIIZ, 64 },
+ .match = afs_vlocation_cache_match,
+ .update = afs_vlocation_cache_update,
+};
+#endif
+
+/*****************************************************************************/
+/*
+ * iterate through the VL servers in a cell until one of them admits knowing
+ * about the volume in question
+ * - caller must have cell->vl_sem write-locked
+ */
+static int afs_vlocation_access_vl_by_name(struct afs_vlocation *vlocation,
+ const char *name,
+ unsigned namesz,
+ struct afs_cache_vlocation *vldb)
+{
+ struct afs_server *server = NULL;
+ struct afs_cell *cell = vlocation->cell;
+ int count, ret;
+
+ _enter("%s,%*.*s,%u", cell->name, namesz, namesz, name, namesz);
+
+ ret = -ENOMEDIUM;
+ for (count = cell->vl_naddrs; count > 0; count--) {
+ _debug("CellServ[%hu]: %08x",
+ cell->vl_curr_svix,
+ cell->vl_addrs[cell->vl_curr_svix].s_addr);
+
+ /* try and create a server */
+ ret = afs_server_lookup(cell,
+ &cell->vl_addrs[cell->vl_curr_svix],
+ &server);
+ switch (ret) {
+ case 0:
+ break;
+ case -ENOMEM:
+ case -ENONET:
+ goto out;
+ default:
+ goto rotate;
+ }
+
+ /* attempt to access the VL server */
+ ret = afs_rxvl_get_entry_by_name(server, name, namesz, vldb);
+ switch (ret) {
+ case 0:
+ afs_put_server(server);
+ goto out;
+ case -ENOMEM:
+ case -ENONET:
+ case -ENETUNREACH:
+ case -EHOSTUNREACH:
+ case -ECONNREFUSED:
+ down_write(&server->sem);
+ if (server->vlserver) {
+ rxrpc_put_connection(server->vlserver);
+ server->vlserver = NULL;
+ }
+ up_write(&server->sem);
+ afs_put_server(server);
+ if (ret == -ENOMEM || ret == -ENONET)
+ goto out;
+ goto rotate;
+ case -ENOMEDIUM:
+ afs_put_server(server);
+ goto out;
+ default:
+ afs_put_server(server);
+ ret = -ENOMEDIUM;
+ goto rotate;
+ }
+
+ /* rotate the server records upon lookup failure */
+ rotate:
+ cell->vl_curr_svix++;
+ cell->vl_curr_svix %= cell->vl_naddrs;
+ }
+
+ out:
+ _leave(" = %d", ret);
+ return ret;
+
+} /* end afs_vlocation_access_vl_by_name() */
+
+/*****************************************************************************/
+/*
+ * iterate through the VL servers in a cell until one of them admits knowing
+ * about the volume in question
+ * - caller must have cell->vl_sem write-locked
+ */
+static int afs_vlocation_access_vl_by_id(struct afs_vlocation *vlocation,
+ afs_volid_t volid,
+ afs_voltype_t voltype,
+ struct afs_cache_vlocation *vldb)
+{
+ struct afs_server *server = NULL;
+ struct afs_cell *cell = vlocation->cell;
+ int count, ret;
+
+ _enter("%s,%x,%d,", cell->name, volid, voltype);
+
+ ret = -ENOMEDIUM;
+ for (count = cell->vl_naddrs; count > 0; count--) {
+ _debug("CellServ[%hu]: %08x",
+ cell->vl_curr_svix,
+ cell->vl_addrs[cell->vl_curr_svix].s_addr);
+
+ /* try and create a server */
+ ret = afs_server_lookup(cell,
+ &cell->vl_addrs[cell->vl_curr_svix],
+ &server);
+ switch (ret) {
+ case 0:
+ break;
+ case -ENOMEM:
+ case -ENONET:
+ goto out;
+ default:
+ goto rotate;
+ }
+
+ /* attempt to access the VL server */
+ ret = afs_rxvl_get_entry_by_id(server, volid, voltype, vldb);
+ switch (ret) {
+ case 0:
+ afs_put_server(server);
+ goto out;
+ case -ENOMEM:
+ case -ENONET:
+ case -ENETUNREACH:
+ case -EHOSTUNREACH:
+ case -ECONNREFUSED:
+ down_write(&server->sem);
+ if (server->vlserver) {
+ rxrpc_put_connection(server->vlserver);
+ server->vlserver = NULL;
+ }
+ up_write(&server->sem);
+ afs_put_server(server);
+ if (ret == -ENOMEM || ret == -ENONET)
+ goto out;
+ goto rotate;
+ case -ENOMEDIUM:
+ afs_put_server(server);
+ goto out;
+ default:
+ afs_put_server(server);
+ ret = -ENOMEDIUM;
+ goto rotate;
+ }
+
+ /* rotate the server records upon lookup failure */
+ rotate:
+ cell->vl_curr_svix++;
+ cell->vl_curr_svix %= cell->vl_naddrs;
+ }
+
+ out:
+ _leave(" = %d", ret);
+ return ret;
+
+} /* end afs_vlocation_access_vl_by_id() */
+
+/*****************************************************************************/
+/*
+ * lookup volume location
+ * - caller must have cell->vol_sem write-locked
+ * - iterate through the VL servers in a cell until one of them admits knowing
+ * about the volume in question
+ * - lookup in the local cache if not able to find on the VL server
+ * - insert/update in the local cache if did get a VL response
+ */
+int afs_vlocation_lookup(struct afs_cell *cell,
+ const char *name,
+ unsigned namesz,
+ struct afs_vlocation **_vlocation)
+{
+ struct afs_cache_vlocation vldb;
+ struct afs_vlocation *vlocation;
+ afs_voltype_t voltype;
+ afs_volid_t vid;
+ int active = 0, ret;
+
+ _enter("{%s},%*.*s,%u,", cell->name, namesz, namesz, name, namesz);
+
+ if (namesz > sizeof(vlocation->vldb.name)) {
+ _leave(" = -ENAMETOOLONG");
+ return -ENAMETOOLONG;
+ }
+
+ /* search the cell's active list first */
+ list_for_each_entry(vlocation, &cell->vl_list, link) {
+ if (namesz < sizeof(vlocation->vldb.name) &&
+ vlocation->vldb.name[namesz] != '\0')
+ continue;
+
+ if (memcmp(vlocation->vldb.name, name, namesz) == 0)
+ goto found_in_memory;
+ }
+
+ /* search the cell's graveyard list second */
+ spin_lock(&cell->vl_gylock);
+ list_for_each_entry(vlocation, &cell->vl_graveyard, link) {
+ if (namesz < sizeof(vlocation->vldb.name) &&
+ vlocation->vldb.name[namesz] != '\0')
+ continue;
+
+ if (memcmp(vlocation->vldb.name, name, namesz) == 0)
+ goto found_in_graveyard;
+ }
+ spin_unlock(&cell->vl_gylock);
+
+ /* not in the cell's in-memory lists - create a new record */
+ vlocation = kmalloc(sizeof(struct afs_vlocation), GFP_KERNEL);
+ if (!vlocation)
+ return -ENOMEM;
+
+ memset(vlocation, 0, sizeof(struct afs_vlocation));
+ atomic_set(&vlocation->usage, 1);
+ INIT_LIST_HEAD(&vlocation->link);
+ rwlock_init(&vlocation->lock);
+ memcpy(vlocation->vldb.name, name, namesz);
+
+ afs_timer_init(&vlocation->timeout, &afs_vlocation_timer_ops);
+ afs_timer_init(&vlocation->upd_timer, &afs_vlocation_update_timer_ops);
+ afs_async_op_init(&vlocation->upd_op, &afs_vlocation_update_op_ops);
+
+ afs_get_cell(cell);
+ vlocation->cell = cell;
+
+ list_add_tail(&vlocation->link, &cell->vl_list);
+
+#ifdef AFS_CACHING_SUPPORT
+ /* we want to store it in the cache, plus it might already be
+ * encached */
+ cachefs_acquire_cookie(cell->cache,
+ &afs_volume_cache_index_def,
+ vlocation,
+ &vlocation->cache);
+
+ if (vlocation->valid)
+ goto found_in_cache;
+#endif
+
+ /* try to look up an unknown volume in the cell VL databases by name */
+ ret = afs_vlocation_access_vl_by_name(vlocation, name, namesz, &vldb);
+ if (ret < 0) {
+ printk("kAFS: failed to locate '%*.*s' in cell '%s'\n",
+ namesz, namesz, name, cell->name);
+ goto error;
+ }
+
+ goto found_on_vlserver;
+
+ found_in_graveyard:
+ /* found in the graveyard - resurrect */
+ _debug("found in graveyard");
+ atomic_inc(&vlocation->usage);
+ list_del(&vlocation->link);
+ list_add_tail(&vlocation->link, &cell->vl_list);
+ spin_unlock(&cell->vl_gylock);
+
+ afs_kafstimod_del_timer(&vlocation->timeout);
+ goto active;
+
+ found_in_memory:
+ /* found in memory - check to see if it's active */
+ _debug("found in memory");
+ atomic_inc(&vlocation->usage);
+
+ active:
+ active = 1;
+
+#ifdef AFS_CACHING_SUPPORT
+ found_in_cache:
+#endif
+ /* try to look up a cached volume in the cell VL databases by ID */
+ _debug("found in cache");
+
+ _debug("Locally Cached: %s %02x { %08x(%x) %08x(%x) %08x(%x) }",
+ vlocation->vldb.name,
+ vlocation->vldb.vidmask,
+ ntohl(vlocation->vldb.servers[0].s_addr),
+ vlocation->vldb.srvtmask[0],
+ ntohl(vlocation->vldb.servers[1].s_addr),
+ vlocation->vldb.srvtmask[1],
+ ntohl(vlocation->vldb.servers[2].s_addr),
+ vlocation->vldb.srvtmask[2]
+ );
+
+ _debug("Vids: %08x %08x %08x",
+ vlocation->vldb.vid[0],
+ vlocation->vldb.vid[1],
+ vlocation->vldb.vid[2]);
+
+ if (vlocation->vldb.vidmask & AFS_VOL_VTM_RW) {
+ vid = vlocation->vldb.vid[0];
+ voltype = AFSVL_RWVOL;
+ }
+ else if (vlocation->vldb.vidmask & AFS_VOL_VTM_RO) {
+ vid = vlocation->vldb.vid[1];
+ voltype = AFSVL_ROVOL;
+ }
+ else if (vlocation->vldb.vidmask & AFS_VOL_VTM_BAK) {
+ vid = vlocation->vldb.vid[2];
+ voltype = AFSVL_BACKVOL;
+ }
+ else {
+ BUG();
+ vid = 0;
+ voltype = 0;
+ }
+
+ ret = afs_vlocation_access_vl_by_id(vlocation, vid, voltype, &vldb);
+ switch (ret) {
+ /* net error */
+ default:
+ printk("kAFS: failed to volume '%*.*s' (%x) up in '%s': %d\n",
+ namesz, namesz, name, vid, cell->name, ret);
+ goto error;
+
+ /* pulled from local cache into memory */
+ case 0:
+ goto found_on_vlserver;
+
+ /* uh oh... looks like the volume got deleted */
+ case -ENOMEDIUM:
+ printk("kAFS: volume '%*.*s' (%x) does not exist '%s'\n",
+ namesz, namesz, name, vid, cell->name);
+
+ /* TODO: make existing record unavailable */
+ goto error;
+ }
+
+ found_on_vlserver:
+ _debug("Done VL Lookup: %*.*s %02x { %08x(%x) %08x(%x) %08x(%x) }",
+ namesz, namesz, name,
+ vldb.vidmask,
+ ntohl(vldb.servers[0].s_addr), vldb.srvtmask[0],
+ ntohl(vldb.servers[1].s_addr), vldb.srvtmask[1],
+ ntohl(vldb.servers[2].s_addr), vldb.srvtmask[2]
+ );
+
+ _debug("Vids: %08x %08x %08x", vldb.vid[0], vldb.vid[1], vldb.vid[2]);
+
+ if ((namesz < sizeof(vlocation->vldb.name) &&
+ vlocation->vldb.name[namesz] != '\0') ||
+ memcmp(vldb.name, name, namesz) != 0)
+ printk("kAFS: name of volume '%*.*s' changed to '%s' on server\n",
+ namesz, namesz, name, vldb.name);
+
+ memcpy(&vlocation->vldb, &vldb, sizeof(vlocation->vldb));
+
+ afs_kafstimod_add_timer(&vlocation->upd_timer, 10 * HZ);
+
+#ifdef AFS_CACHING_SUPPORT
+ /* update volume entry in local cache */
+ cachefs_update_cookie(vlocation->cache);
+#endif
+
+ *_vlocation = vlocation;
+ _leave(" = 0 (%p)",vlocation);
+ return 0;
+
+ error:
+ if (vlocation) {
+ if (active) {
+ __afs_put_vlocation(vlocation);
+ }
+ else {
+ list_del(&vlocation->link);
+#ifdef AFS_CACHING_SUPPORT
+ cachefs_relinquish_cookie(vlocation->cache, 0);
+#endif
+ afs_put_cell(vlocation->cell);
+ kfree(vlocation);
+ }
+ }
+
+ _leave(" = %d", ret);
+ return ret;
+} /* end afs_vlocation_lookup() */
+
+/*****************************************************************************/
+/*
+ * finish using a volume location record
+ * - caller must have cell->vol_sem write-locked
+ */
+static void __afs_put_vlocation(struct afs_vlocation *vlocation)
+{
+ struct afs_cell *cell;
+
+ if (!vlocation)
+ return;
+
+ _enter("%s", vlocation->vldb.name);
+
+ cell = vlocation->cell;
+
+ /* sanity check */
+ BUG_ON(atomic_read(&vlocation->usage) <= 0);
+
+ spin_lock(&cell->vl_gylock);
+ if (likely(!atomic_dec_and_test(&vlocation->usage))) {
+ spin_unlock(&cell->vl_gylock);
+ _leave("");
+ return;
+ }
+
+ /* move to graveyard queue */
+ list_del(&vlocation->link);
+ list_add_tail(&vlocation->link,&cell->vl_graveyard);
+
+ /* remove from pending timeout queue (refcounted if actually being
+ * updated) */
+ list_del_init(&vlocation->upd_op.link);
+
+ /* time out in 10 secs */
+ afs_kafstimod_del_timer(&vlocation->upd_timer);
+ afs_kafstimod_add_timer(&vlocation->timeout, 10 * HZ);
+
+ spin_unlock(&cell->vl_gylock);
+
+ _leave(" [killed]");
+} /* end __afs_put_vlocation() */
+
+/*****************************************************************************/
+/*
+ * finish using a volume location record
+ */
+void afs_put_vlocation(struct afs_vlocation *vlocation)
+{
+ if (vlocation) {
+ struct afs_cell *cell = vlocation->cell;
+
+ down_write(&cell->vl_sem);
+ __afs_put_vlocation(vlocation);
+ up_write(&cell->vl_sem);
+ }
+} /* end afs_put_vlocation() */
+
+/*****************************************************************************/
+/*
+ * timeout vlocation record
+ * - removes from the cell's graveyard if the usage count is zero
+ */
+void afs_vlocation_do_timeout(struct afs_vlocation *vlocation)
+{
+ struct afs_cell *cell;
+
+ _enter("%s", vlocation->vldb.name);
+
+ cell = vlocation->cell;
+
+ BUG_ON(atomic_read(&vlocation->usage) < 0);
+
+ /* remove from graveyard if still dead */
+ spin_lock(&cell->vl_gylock);
+ if (atomic_read(&vlocation->usage) == 0)
+ list_del_init(&vlocation->link);
+ else
+ vlocation = NULL;
+ spin_unlock(&cell->vl_gylock);
+
+ if (!vlocation) {
+ _leave("");
+ return; /* resurrected */
+ }
+
+ /* we can now destroy it properly */
+#ifdef AFS_CACHING_SUPPORT
+ cachefs_relinquish_cookie(vlocation->cache, 0);
+#endif
+ afs_put_cell(cell);
+
+ kfree(vlocation);
+
+ _leave(" [destroyed]");
+} /* end afs_vlocation_do_timeout() */
+
+/*****************************************************************************/
+/*
+ * send an update operation to the currently selected server
+ */
+static int afs_vlocation_update_begin(struct afs_vlocation *vlocation)
+{
+ afs_voltype_t voltype;
+ afs_volid_t vid;
+ int ret;
+
+ _enter("%s{ufs=%u ucs=%u}",
+ vlocation->vldb.name,
+ vlocation->upd_first_svix,
+ vlocation->upd_curr_svix);
+
+ /* try to look up a cached volume in the cell VL databases by ID */
+ if (vlocation->vldb.vidmask & AFS_VOL_VTM_RW) {
+ vid = vlocation->vldb.vid[0];
+ voltype = AFSVL_RWVOL;
+ }
+ else if (vlocation->vldb.vidmask & AFS_VOL_VTM_RO) {
+ vid = vlocation->vldb.vid[1];
+ voltype = AFSVL_ROVOL;
+ }
+ else if (vlocation->vldb.vidmask & AFS_VOL_VTM_BAK) {
+ vid = vlocation->vldb.vid[2];
+ voltype = AFSVL_BACKVOL;
+ }
+ else {
+ BUG();
+ vid = 0;
+ voltype = 0;
+ }
+
+ /* contact the chosen server */
+ ret = afs_server_lookup(
+ vlocation->cell,
+ &vlocation->cell->vl_addrs[vlocation->upd_curr_svix],
+ &vlocation->upd_op.server);
+
+ switch (ret) {
+ case 0:
+ break;
+ case -ENOMEM:
+ case -ENONET:
+ default:
+ _leave(" = %d", ret);
+ return ret;
+ }
+
+ /* initiate the update operation */
+ ret = afs_rxvl_get_entry_by_id_async(&vlocation->upd_op, vid, voltype);
+ if (ret < 0) {
+ _leave(" = %d", ret);
+ return ret;
+ }
+
+ _leave(" = %d", ret);
+ return ret;
+} /* end afs_vlocation_update_begin() */
+
+/*****************************************************************************/
+/*
+ * abandon updating a VL record
+ * - does not restart the update timer
+ */
+static void afs_vlocation_update_abandon(struct afs_vlocation *vlocation,
+ afs_vlocation_upd_t state,
+ int ret)
+{
+ _enter("%s,%u", vlocation->vldb.name, state);
+
+ if (ret < 0)
+ printk("kAFS: Abandoning VL update '%s': %d\n",
+ vlocation->vldb.name, ret);
+
+ /* discard the server record */
+ afs_put_server(vlocation->upd_op.server);
+ vlocation->upd_op.server = NULL;
+
+ spin_lock(&afs_vlocation_update_lock);
+ afs_vlocation_update = NULL;
+ vlocation->upd_state = state;
+
+ /* TODO: start updating next VL record on pending list */
+
+ spin_unlock(&afs_vlocation_update_lock);
+
+ _leave("");
+} /* end afs_vlocation_update_abandon() */
+
+/*****************************************************************************/
+/*
+ * handle periodic update timeouts and busy retry timeouts
+ * - called from kafstimod
+ */
+static void afs_vlocation_update_timer(struct afs_timer *timer)
+{
+ struct afs_vlocation *vlocation =
+ list_entry(timer, struct afs_vlocation, upd_timer);
+ int ret;
+
+ _enter("%s", vlocation->vldb.name);
+
+ /* only update if not in the graveyard (defend against putting too) */
+ spin_lock(&vlocation->cell->vl_gylock);
+
+ if (!atomic_read(&vlocation->usage))
+ goto out_unlock1;
+
+ spin_lock(&afs_vlocation_update_lock);
+
+ /* if we were woken up due to EBUSY sleep then restart immediately if
+ * possible or else jump to front of pending queue */
+ if (vlocation->upd_state == AFS_VLUPD_BUSYSLEEP) {
+ if (afs_vlocation_update) {
+ list_add(&vlocation->upd_op.link,
+ &afs_vlocation_update_pendq);
+ }
+ else {
+ afs_get_vlocation(vlocation);
+ afs_vlocation_update = vlocation;
+ vlocation->upd_state = AFS_VLUPD_INPROGRESS;
+ }
+ goto out_unlock2;
+ }
+
+ /* put on pending queue if there's already another update in progress */
+ if (afs_vlocation_update) {
+ vlocation->upd_state = AFS_VLUPD_PENDING;
+ list_add_tail(&vlocation->upd_op.link,
+ &afs_vlocation_update_pendq);
+ goto out_unlock2;
+ }
+
+ /* hold a ref on it while actually updating */
+ afs_get_vlocation(vlocation);
+ afs_vlocation_update = vlocation;
+ vlocation->upd_state = AFS_VLUPD_INPROGRESS;
+
+ spin_unlock(&afs_vlocation_update_lock);
+ spin_unlock(&vlocation->cell->vl_gylock);
+
+ /* okay... we can start the update */
+ _debug("BEGIN VL UPDATE [%s]", vlocation->vldb.name);
+ vlocation->upd_first_svix = vlocation->cell->vl_curr_svix;
+ vlocation->upd_curr_svix = vlocation->upd_first_svix;
+ vlocation->upd_rej_cnt = 0;
+ vlocation->upd_busy_cnt = 0;
+
+ ret = afs_vlocation_update_begin(vlocation);
+ if (ret < 0) {
+ afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, ret);
+ afs_kafstimod_add_timer(&vlocation->upd_timer,
+ AFS_VLDB_TIMEOUT);
+ afs_put_vlocation(vlocation);
+ }
+
+ _leave("");
+ return;
+
+ out_unlock2:
+ spin_unlock(&afs_vlocation_update_lock);
+ out_unlock1:
+ spin_unlock(&vlocation->cell->vl_gylock);
+ _leave("");
+ return;
+
+} /* end afs_vlocation_update_timer() */
+
+/*****************************************************************************/
+/*
+ * attend to an update operation upon which an event happened
+ * - called in kafsasyncd context
+ */
+static void afs_vlocation_update_attend(struct afs_async_op *op)
+{
+ struct afs_cache_vlocation vldb;
+ struct afs_vlocation *vlocation =
+ list_entry(op, struct afs_vlocation, upd_op);
+ unsigned tmp;
+ int ret;
+
+ _enter("%s", vlocation->vldb.name);
+
+ ret = afs_rxvl_get_entry_by_id_async2(op, &vldb);
+ switch (ret) {
+ case -EAGAIN:
+ _leave(" [unfinished]");
+ return;
+
+ case 0:
+ _debug("END VL UPDATE: %d\n", ret);
+ vlocation->valid = 1;
+
+ _debug("Done VL Lookup: %02x { %08x(%x) %08x(%x) %08x(%x) }",
+ vldb.vidmask,
+ ntohl(vldb.servers[0].s_addr), vldb.srvtmask[0],
+ ntohl(vldb.servers[1].s_addr), vldb.srvtmask[1],
+ ntohl(vldb.servers[2].s_addr), vldb.srvtmask[2]
+ );
+
+ _debug("Vids: %08x %08x %08x",
+ vldb.vid[0], vldb.vid[1], vldb.vid[2]);
+
+ afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, 0);
+
+ down_write(&vlocation->cell->vl_sem);
+
+ /* actually update the cache */
+ if (strncmp(vldb.name, vlocation->vldb.name,
+ sizeof(vlocation->vldb.name)) != 0)
+ printk("kAFS: name of volume '%s'"
+ " changed to '%s' on server\n",
+ vlocation->vldb.name, vldb.name);
+
+ memcpy(&vlocation->vldb, &vldb, sizeof(vlocation->vldb));
+
+#if 0
+ /* TODO update volume entry in local cache */
+#endif
+
+ up_write(&vlocation->cell->vl_sem);
+
+ if (ret < 0)
+ printk("kAFS: failed to update local cache: %d\n", ret);
+
+ afs_kafstimod_add_timer(&vlocation->upd_timer,
+ AFS_VLDB_TIMEOUT);
+ afs_put_vlocation(vlocation);
+ _leave(" [found]");
+ return;
+
+ case -ENOMEDIUM:
+ vlocation->upd_rej_cnt++;
+ goto try_next;
+
+ /* the server is locked - retry in a very short while */
+ case -EBUSY:
+ vlocation->upd_busy_cnt++;
+ if (vlocation->upd_busy_cnt > 3)
+ goto try_next; /* too many retries */
+
+ afs_vlocation_update_abandon(vlocation,
+ AFS_VLUPD_BUSYSLEEP, 0);
+ afs_kafstimod_add_timer(&vlocation->upd_timer, HZ / 2);
+ afs_put_vlocation(vlocation);
+ _leave(" [busy]");
+ return;
+
+ case -ENETUNREACH:
+ case -EHOSTUNREACH:
+ case -ECONNREFUSED:
+ case -EREMOTEIO:
+ /* record bad vlserver info in the cell too
+ * - TODO: use down_write_trylock() if available
+ */
+ if (vlocation->upd_curr_svix == vlocation->cell->vl_curr_svix)
+ vlocation->cell->vl_curr_svix =
+ vlocation->cell->vl_curr_svix %
+ vlocation->cell->vl_naddrs;
+
+ case -EBADRQC:
+ case -EINVAL:
+ case -EACCES:
+ case -EBADMSG:
+ goto try_next;
+
+ default:
+ goto abandon;
+ }
+
+ /* try contacting the next server */
+ try_next:
+ vlocation->upd_busy_cnt = 0;
+
+ /* discard the server record */
+ afs_put_server(vlocation->upd_op.server);
+ vlocation->upd_op.server = NULL;
+
+ tmp = vlocation->cell->vl_naddrs;
+ if (tmp == 0)
+ goto abandon;
+
+ vlocation->upd_curr_svix++;
+ if (vlocation->upd_curr_svix >= tmp)
+ vlocation->upd_curr_svix = 0;
+ if (vlocation->upd_first_svix >= tmp)
+ vlocation->upd_first_svix = tmp - 1;
+
+ /* move to the next server */
+ if (vlocation->upd_curr_svix != vlocation->upd_first_svix) {
+ afs_vlocation_update_begin(vlocation);
+ _leave(" [next]");
+ return;
+ }
+
+ /* run out of servers to try - was the volume rejected? */
+ if (vlocation->upd_rej_cnt > 0) {
+ printk("kAFS: Active volume no longer valid '%s'\n",
+ vlocation->vldb.name);
+ vlocation->valid = 0;
+ afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, 0);
+ afs_kafstimod_add_timer(&vlocation->upd_timer,
+ AFS_VLDB_TIMEOUT);
+ afs_put_vlocation(vlocation);
+ _leave(" [invalidated]");
+ return;
+ }
+
+ /* abandon the update */
+ abandon:
+ afs_vlocation_update_abandon(vlocation, AFS_VLUPD_SLEEP, ret);
+ afs_kafstimod_add_timer(&vlocation->upd_timer, HZ * 10);
+ afs_put_vlocation(vlocation);
+ _leave(" [abandoned]");
+
+} /* end afs_vlocation_update_attend() */
+
+/*****************************************************************************/
+/*
+ * deal with an update operation being discarded
+ * - called in kafsasyncd context when it's dying due to rmmod
+ * - the call has already been aborted and put()'d
+ */
+static void afs_vlocation_update_discard(struct afs_async_op *op)
+{
+ struct afs_vlocation *vlocation =
+ list_entry(op, struct afs_vlocation, upd_op);
+
+ _enter("%s", vlocation->vldb.name);
+
+ afs_put_server(op->server);
+ op->server = NULL;
+
+ afs_put_vlocation(vlocation);
+
+ _leave("");
+} /* end afs_vlocation_update_discard() */
+
+/*****************************************************************************/
+/*
+ * match a VLDB record stored in the cache
+ * - may also load target from entry
+ */
+#ifdef AFS_CACHING_SUPPORT
+static cachefs_match_val_t afs_vlocation_cache_match(void *target,
+ const void *entry)
+{
+ const struct afs_cache_vlocation *vldb = entry;
+ struct afs_vlocation *vlocation = target;
+
+ _enter("{%s},{%s}", vlocation->vldb.name, vldb->name);
+
+ if (strncmp(vlocation->vldb.name, vldb->name, sizeof(vldb->name)) == 0
+ ) {
+ if (!vlocation->valid ||
+ vlocation->vldb.rtime == vldb->rtime
+ ) {
+ vlocation->vldb = *vldb;
+ vlocation->valid = 1;
+ _leave(" = SUCCESS [c->m]");
+ return CACHEFS_MATCH_SUCCESS;
+ }
+ /* need to update cache if cached info differs */
+ else if (memcmp(&vlocation->vldb, vldb, sizeof(*vldb)) != 0) {
+ /* delete if VIDs for this name differ */
+ if (memcmp(&vlocation->vldb.vid,
+ &vldb->vid,
+ sizeof(vldb->vid)) != 0) {
+ _leave(" = DELETE");
+ return CACHEFS_MATCH_SUCCESS_DELETE;
+ }
+
+ _leave(" = UPDATE");
+ return CACHEFS_MATCH_SUCCESS_UPDATE;
+ }
+ else {
+ _leave(" = SUCCESS");
+ return CACHEFS_MATCH_SUCCESS;
+ }
+ }
+
+ _leave(" = FAILED");
+ return CACHEFS_MATCH_FAILED;
+} /* end afs_vlocation_cache_match() */
+#endif
+
+/*****************************************************************************/
+/*
+ * update a VLDB record stored in the cache
+ */
+#ifdef AFS_CACHING_SUPPORT
+static void afs_vlocation_cache_update(void *source, void *entry)
+{
+ struct afs_cache_vlocation *vldb = entry;
+ struct afs_vlocation *vlocation = source;
+
+ _enter("");
+
+ *vldb = vlocation->vldb;
+
+} /* end afs_vlocation_cache_update() */
+#endif