summaryrefslogtreecommitdiff
path: root/fs/afs
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2017-11-02 18:27:50 +0300
committerDavid Howells <dhowells@redhat.com>2017-11-13 18:38:18 +0300
commit8b2a464ced77fe35be72ab7d38152a9439daf8d3 (patch)
treeec478c8adebb6344513581b60935c0cf2b0ff937 /fs/afs
parent989782dcdc91a5e6d5999c7a52a84a60a0811e56 (diff)
downloadlinux-8b2a464ced77fe35be72ab7d38152a9439daf8d3.tar.xz
afs: Add an address list concept
Add an RCU replaceable address list structure to hold a list of server addresses. The list also holds the To this end: (1) A cell's VL server address list can be loaded directly via insmod or echo to /proc/fs/afs/cells or dynamically from a DNS query for AFSDB or SRV records. (2) Anyone wanting to use a cell's VL server address must wait until the cell record comes online and has tried to obtain some addresses. (3) An FS server's address list, for the moment, has a single entry that is the key to the server list. This will change in the future when a server is instead keyed on its UUID and the VL.GetAddrsU operation is used. (4) An 'address cursor' concept is introduced to handle iteration through the address list. This is passed to the afs_make_call() as, in the future, stuff (such as abort code) that doesn't outlast the call will be returned in it. In the future, we might want to annotate the list with information about how each address fares. We might then want to propagate such annotations over address list replacement. Whilst we're at it, we allow IPv6 addresses to be specified in colon-delimited lists by enclosing them in square brackets. Signed-off-by: David Howells <dhowells@redhat.com>
Diffstat (limited to 'fs/afs')
-rw-r--r--fs/afs/Makefile1
-rw-r--r--fs/afs/addr_list.c308
-rw-r--r--fs/afs/cell.c169
-rw-r--r--fs/afs/fsclient.c121
-rw-r--r--fs/afs/internal.h120
-rw-r--r--fs/afs/proc.c23
-rw-r--r--fs/afs/rxrpc.c3
-rw-r--r--fs/afs/server.c70
-rw-r--r--fs/afs/vlclient.c8
-rw-r--r--fs/afs/vlocation.c75
-rw-r--r--fs/afs/vnode.c392
-rw-r--r--fs/afs/volume.c115
12 files changed, 844 insertions, 561 deletions
diff --git a/fs/afs/Makefile b/fs/afs/Makefile
index 641148208e90..849383986d3b 100644
--- a/fs/afs/Makefile
+++ b/fs/afs/Makefile
@@ -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 \
diff --git a/fs/afs/addr_list.c b/fs/afs/addr_list.c
new file mode 100644
index 000000000000..ecb9c72aebd2
--- /dev/null
+++ b/fs/afs/addr_list.c
@@ -0,0 +1,308 @@
+/* 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)))
+
+/*
+ * 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;
+}
+
+/*
+ * 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;
+}
diff --git a/fs/afs/cell.c b/fs/afs/cell.c
index e83103e8a6fb..a0e08d3a108c 100644
--- a/fs/afs/cell.c
+++ b/fs/afs/cell.c
@@ -9,7 +9,6 @@
* 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>
@@ -152,68 +151,33 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net,
init_rwsem(&cell->vl_sem);
INIT_LIST_HEAD(&cell->vl_list);
spin_lock_init(&cell->vl_lock);
- seqlock_init(&cell->vl_addrs_lock);
- cell->flags = (1 << AFS_CELL_FL_NOT_READY);
-
- for (i = 0; i < AFS_CELL_MAX_ADDRS; i++) {
- struct sockaddr_rxrpc *srx = &cell->vl_addrs[i];
- srx->srx_family = AF_RXRPC;
- srx->srx_service = VL_SERVICE;
- srx->transport_type = SOCK_DGRAM;
- srx->transport.sin6.sin6_family = AF_INET6;
- srx->transport.sin6.sin6_port = htons(AFS_VL_PORT);
- }
+ cell->flags = ((1 << AFS_CELL_FL_NOT_READY) |
+ (1 << AFS_CELL_FL_NO_LOOKUP_YET));
+ rwlock_init(&cell->vl_addrs_lock);
/* Fill in the VL server list if we were given a list of addresses to
* use.
*/
if (vllist) {
- char delim = ':';
-
- if (strchr(vllist, ',') || !strchr(vllist, '.'))
- delim = ',';
-
- do {
- struct sockaddr_rxrpc *srx = &cell->vl_addrs[cell->vl_naddrs];
-
- if (in4_pton(vllist, -1,
- (u8 *)&srx->transport.sin6.sin6_addr.s6_addr32[3],
- delim, &vllist)) {
- srx->transport_len = sizeof(struct sockaddr_in6);
- 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(vllist, -1,
- srx->transport.sin6.sin6_addr.s6_addr,
- delim, &vllist)) {
- srx->transport_len = sizeof(struct sockaddr_in6);
- srx->transport.sin6.sin6_family = AF_INET6;
- } else {
- goto bad_address;
- }
+ struct afs_addr_list *alist;
- cell->vl_naddrs++;
- if (!*vllist)
- break;
- vllist++;
-
- } while (cell->vl_naddrs < AFS_CELL_MAX_ADDRS && vllist);
+ alist = afs_parse_text_addrs(vllist, strlen(vllist), ':',
+ VL_SERVICE, AFS_VL_PORT);
+ if (IS_ERR(alist)) {
+ ret = PTR_ERR(alist);
+ goto parse_failed;
+ }
- /* Disable DNS refresh for manually-specified cells */
+ rcu_assign_pointer(cell->vl_addrs, alist);
cell->dns_expiry = TIME64_MAX;
- } else {
- /* We're going to need to 'refresh' this cell's VL server list
- * from the DNS before we can use it.
- */
- cell->dns_expiry = S64_MIN;
}
_leave(" = %p", cell);
return cell;
-bad_address:
- printk(KERN_ERR "kAFS: bad VL server IP address\n");
- ret = -EINVAL;
+parse_failed:
+ if (ret == -EINVAL)
+ printk(KERN_ERR "kAFS: bad VL server IP address\n");
kfree(cell);
_leave(" = %d", ret);
return ERR_PTR(ret);
@@ -325,7 +289,6 @@ cell_already_exists:
if (excl) {
ret = -EEXIST;
} else {
- ASSERTCMP(atomic_read(&cursor->usage), >=, 1);
afs_get_cell(cursor);
ret = 0;
}
@@ -333,8 +296,10 @@ cell_already_exists:
kfree(candidate);
if (ret == 0)
goto wait_for_cell;
+ goto error_noput;
error:
afs_put_cell(net, cell);
+error_noput:
_leave(" = %d [error]", ret);
return ERR_PTR(ret);
}
@@ -396,78 +361,50 @@ int afs_cell_init(struct afs_net *net, const char *rootcell)
*/
static void afs_update_cell(struct afs_cell *cell)
{
+ struct afs_addr_list *alist, *old;
time64_t now, expiry;
- char *vllist = NULL;
- int ret;
_enter("%s", cell->name);
- ret = dns_query("afsdb", cell->name, cell->name_len,
- "ipv4", &vllist, &expiry);
- _debug("query %d", ret);
- switch (ret) {
- case 0 ... INT_MAX:
- clear_bit(AFS_CELL_FL_DNS_FAIL, &cell->flags);
- clear_bit(AFS_CELL_FL_NOT_FOUND, &cell->flags);
- goto parse_dns_data;
+ 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 -ENODATA:
- clear_bit(AFS_CELL_FL_DNS_FAIL, &cell->flags);
- set_bit(AFS_CELL_FL_NOT_FOUND, &cell->flags);
- cell->dns_expiry = ktime_get_real_seconds() + 61;
- cell->error = -EDESTADDRREQ;
- goto out;
+ case -EAGAIN:
+ case -ECONNREFUSED:
+ default:
+ set_bit(AFS_CELL_FL_DNS_FAIL, &cell->flags);
+ cell->dns_expiry = ktime_get_real_seconds() + 10;
+ break;
+ }
- case -EAGAIN:
- case -ECONNREFUSED:
- default:
- /* Unable to query DNS. */
- set_bit(AFS_CELL_FL_DNS_FAIL, &cell->flags);
- cell->dns_expiry = ktime_get_real_seconds() + 10;
cell->error = -EDESTADDRREQ;
- goto out;
- }
-
-parse_dns_data:
- write_seqlock(&cell->vl_addrs_lock);
-
- ret = -EINVAL;
- do {
- struct sockaddr_rxrpc *srx = &cell->vl_addrs[cell->vl_naddrs];
-
- if (in4_pton(vllist, -1,
- (u8 *)&srx->transport.sin6.sin6_addr.s6_addr32[3],
- ',', (const char **)&vllist)) {
- srx->transport_len = sizeof(struct sockaddr_in6);
- 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(vllist, -1,
- srx->transport.sin6.sin6_addr.s6_addr,
- ',', (const char **)&vllist)) {
- srx->transport_len = sizeof(struct sockaddr_in6);
- srx->transport.sin6.sin6_family = AF_INET6;
- } else {
- goto bad_address;
- }
+ } else {
+ clear_bit(AFS_CELL_FL_DNS_FAIL, &cell->flags);
+ clear_bit(AFS_CELL_FL_NOT_FOUND, &cell->flags);
- cell->vl_naddrs++;
- if (!*vllist)
- break;
- vllist++;
+ /* 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;
- } while (cell->vl_naddrs < AFS_CELL_MAX_ADDRS);
+ if (old)
+ afs_put_addrlist(old);
+ }
- if (cell->vl_naddrs < AFS_CELL_MAX_ADDRS)
- memset(cell->vl_addrs + cell->vl_naddrs, 0,
- (AFS_CELL_MAX_ADDRS - cell->vl_naddrs) * sizeof(cell->vl_addrs[0]));
+ if (test_and_clear_bit(AFS_CELL_FL_NO_LOOKUP_YET, &cell->flags))
+ wake_up_bit(&cell->flags, AFS_CELL_FL_NO_LOOKUP_YET);
now = ktime_get_real_seconds();
- cell->dns_expiry = expiry;
- afs_set_cell_timer(cell->net, expiry - now);
-bad_address:
- write_sequnlock(&cell->vl_addrs_lock);
-out:
+ afs_set_cell_timer(cell->net, cell->dns_expiry - now);
_leave("");
}
@@ -482,6 +419,7 @@ static void afs_cell_destroy(struct rcu_head *rcu)
ASSERTCMP(atomic_read(&cell->usage), ==, 0);
+ afs_put_addrlist(cell->vl_addrs);
key_put(cell->anonymous_key);
kfree(cell);
@@ -515,6 +453,15 @@ void afs_cells_timer(struct timer_list *timer)
}
/*
+ * Get a reference on a cell record.
+ */
+struct afs_cell *afs_get_cell(struct afs_cell *cell)
+{
+ atomic_inc(&cell->usage);
+ return cell;
+}
+
+/*
* Drop a reference on a cell record.
*/
void afs_put_cell(struct afs_net *net, struct afs_cell *cell)
diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c
index 680c02d510f7..6614d0a78daa 100644
--- a/fs/afs/fsclient.c
+++ b/fs/afs/fsclient.c
@@ -297,7 +297,7 @@ static const struct afs_call_type afs_RXFSFetchStatus = {
/*
* fetch the status information for a file
*/
-int afs_fs_fetch_file_status(struct afs_server *server,
+int afs_fs_fetch_file_status(struct afs_fs_cursor *fc,
struct key *key,
struct afs_vnode *vnode,
struct afs_volsync *volsync,
@@ -325,9 +325,9 @@ int afs_fs_fetch_file_status(struct afs_server *server,
bp[2] = htonl(vnode->fid.vnode);
bp[3] = htonl(vnode->fid.unique);
- call->cb_break = vnode->cb_break + server->cb_s_break;
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ call->cb_break = vnode->cb_break + fc->server->cb_s_break;
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
@@ -502,7 +502,7 @@ static const struct afs_call_type afs_RXFSFetchData64 = {
/*
* fetch data from a very large file
*/
-static int afs_fs_fetch_data64(struct afs_server *server,
+static int afs_fs_fetch_data64(struct afs_fs_cursor *fc,
struct key *key,
struct afs_vnode *vnode,
struct afs_read *req,
@@ -536,15 +536,15 @@ static int afs_fs_fetch_data64(struct afs_server *server,
bp[7] = htonl(lower_32_bits(req->len));
atomic_inc(&req->usage);
- call->cb_break = vnode->cb_break + server->cb_s_break;
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ call->cb_break = vnode->cb_break + fc->server->cb_s_break;
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
* fetch data from a file
*/
-int afs_fs_fetch_data(struct afs_server *server,
+int afs_fs_fetch_data(struct afs_fs_cursor *fc,
struct key *key,
struct afs_vnode *vnode,
struct afs_read *req,
@@ -557,7 +557,7 @@ int afs_fs_fetch_data(struct afs_server *server,
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, key, vnode, req, async);
_enter("");
@@ -581,9 +581,9 @@ int afs_fs_fetch_data(struct afs_server *server,
bp[5] = htonl(lower_32_bits(req->len));
atomic_inc(&req->usage);
- call->cb_break = vnode->cb_break + server->cb_s_break;
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ call->cb_break = vnode->cb_break + fc->server->cb_s_break;
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
@@ -625,7 +625,7 @@ static const struct afs_call_type afs_RXFSCreateXXXX = {
/*
* create a file or make a directory
*/
-int afs_fs_create(struct afs_server *server,
+int afs_fs_create(struct afs_fs_cursor *fc,
struct key *key,
struct afs_vnode *vnode,
const char *name,
@@ -677,8 +677,8 @@ int afs_fs_create(struct afs_server *server,
*bp++ = htonl(mode & S_IALLUGO); /* unix mode */
*bp++ = 0; /* segment size */
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
@@ -717,7 +717,7 @@ static const struct afs_call_type afs_RXFSRemoveXXXX = {
/*
* remove a file or directory
*/
-int afs_fs_remove(struct afs_server *server,
+int afs_fs_remove(struct afs_fs_cursor *fc,
struct key *key,
struct afs_vnode *vnode,
const char *name,
@@ -756,8 +756,8 @@ int afs_fs_remove(struct afs_server *server,
bp = (void *) bp + padsz;
}
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
@@ -797,7 +797,7 @@ static const struct afs_call_type afs_RXFSLink = {
/*
* make a hard link
*/
-int afs_fs_link(struct afs_server *server,
+int afs_fs_link(struct afs_fs_cursor *fc,
struct key *key,
struct afs_vnode *dvnode,
struct afs_vnode *vnode,
@@ -840,8 +840,8 @@ int afs_fs_link(struct afs_server *server,
*bp++ = htonl(vnode->fid.vnode);
*bp++ = htonl(vnode->fid.unique);
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
@@ -882,7 +882,7 @@ static const struct afs_call_type afs_RXFSSymlink = {
/*
* create a symbolic link
*/
-int afs_fs_symlink(struct afs_server *server,
+int afs_fs_symlink(struct afs_fs_cursor *fc,
struct key *key,
struct afs_vnode *vnode,
const char *name,
@@ -943,8 +943,8 @@ int afs_fs_symlink(struct afs_server *server,
*bp++ = htonl(S_IRWXUGO); /* unix mode */
*bp++ = 0; /* segment size */
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
@@ -986,7 +986,7 @@ static const struct afs_call_type afs_RXFSRename = {
/*
* create a symbolic link
*/
-int afs_fs_rename(struct afs_server *server,
+int afs_fs_rename(struct afs_fs_cursor *fc,
struct key *key,
struct afs_vnode *orig_dvnode,
const char *orig_name,
@@ -1045,8 +1045,8 @@ int afs_fs_rename(struct afs_server *server,
bp = (void *) bp + n_padsz;
}
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
@@ -1094,7 +1094,7 @@ static const struct afs_call_type afs_RXFSStoreData64 = {
/*
* store a set of pages to a very large file
*/
-static int afs_fs_store_data64(struct afs_server *server,
+static int afs_fs_store_data64(struct afs_fs_cursor *fc,
struct afs_writeback *wb,
pgoff_t first, pgoff_t last,
unsigned offset, unsigned to,
@@ -1147,14 +1147,14 @@ static int afs_fs_store_data64(struct afs_server *server,
*bp++ = htonl(i_size >> 32);
*bp++ = htonl((u32) i_size);
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
* 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 afs_writeback *wb,
pgoff_t first, pgoff_t last,
unsigned offset, unsigned to,
bool async)
@@ -1183,7 +1183,7 @@ 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,
+ return afs_fs_store_data64(fc, wb, first, last, offset, to,
size, pos, i_size, async);
call = afs_alloc_flat_call(net, &afs_RXFSStoreData,
@@ -1221,8 +1221,8 @@ int afs_fs_store_data(struct afs_server *server, struct afs_writeback *wb,
*bp++ = htonl(size);
*bp++ = htonl(i_size);
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
@@ -1279,7 +1279,7 @@ 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,
+static int afs_fs_setattr_size64(struct afs_fs_cursor *fc, struct key *key,
struct afs_vnode *vnode, struct iattr *attr,
bool async)
{
@@ -1319,15 +1319,15 @@ 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);
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
* 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,
+static int afs_fs_setattr_size(struct afs_fs_cursor *fc, struct key *key,
struct afs_vnode *vnode, struct iattr *attr,
bool async)
{
@@ -1340,8 +1340,7 @@ static int afs_fs_setattr_size(struct afs_server *server, struct key *key,
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, key, vnode, attr, async);
call = afs_alloc_flat_call(net, &afs_RXFSStoreData_as_Status,
(4 + 6 + 3) * 4,
@@ -1367,15 +1366,15 @@ 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 */
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
* 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,
+int afs_fs_setattr(struct afs_fs_cursor *fc, struct key *key,
struct afs_vnode *vnode, struct iattr *attr,
bool async)
{
@@ -1384,8 +1383,7 @@ int afs_fs_setattr(struct afs_server *server, struct key *key,
__be32 *bp;
if (attr->ia_valid & ATTR_SIZE)
- return afs_fs_setattr_size(server, key, vnode, attr,
- async);
+ return afs_fs_setattr_size(fc, key, vnode, attr, async);
_enter(",%x,{%x:%u},,",
key_serial(key), vnode->fid.vid, vnode->fid.vnode);
@@ -1409,8 +1407,8 @@ int afs_fs_setattr(struct afs_server *server, struct key *key,
xdr_encode_AFS_StoreStatus(&bp, attr);
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
@@ -1607,7 +1605,7 @@ static const struct afs_call_type afs_RXFSGetVolumeStatus = {
/*
* fetch the status of a volume
*/
-int afs_fs_get_volume_status(struct afs_server *server,
+int afs_fs_get_volume_status(struct afs_fs_cursor *fc,
struct key *key,
struct afs_vnode *vnode,
struct afs_volume_status *vs,
@@ -1640,8 +1638,8 @@ int afs_fs_get_volume_status(struct afs_server *server,
bp[0] = htonl(FSGETVOLUMESTATUS);
bp[1] = htonl(vnode->fid.vid);
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
@@ -1696,7 +1694,7 @@ static const struct afs_call_type afs_RXFSReleaseLock = {
/*
* get a lock on a file
*/
-int afs_fs_set_lock(struct afs_server *server,
+int afs_fs_set_lock(struct afs_fs_cursor *fc,
struct key *key,
struct afs_vnode *vnode,
afs_lock_type_t type,
@@ -1723,14 +1721,14 @@ int afs_fs_set_lock(struct afs_server *server,
*bp++ = htonl(vnode->fid.unique);
*bp++ = htonl(type);
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
* extend a lock on a file
*/
-int afs_fs_extend_lock(struct afs_server *server,
+int afs_fs_extend_lock(struct afs_fs_cursor *fc,
struct key *key,
struct afs_vnode *vnode,
bool async)
@@ -1755,14 +1753,14 @@ int afs_fs_extend_lock(struct afs_server *server,
*bp++ = htonl(vnode->fid.vnode);
*bp++ = htonl(vnode->fid.unique);
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
* release a lock on a file
*/
-int afs_fs_release_lock(struct afs_server *server,
+int afs_fs_release_lock(struct afs_fs_cursor *fc,
struct key *key,
struct afs_vnode *vnode,
bool async)
@@ -1787,8 +1785,8 @@ int afs_fs_release_lock(struct afs_server *server,
*bp++ = htonl(vnode->fid.vnode);
*bp++ = htonl(vnode->fid.unique);
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
@@ -1812,6 +1810,7 @@ static const struct afs_call_type afs_RXFSGiveUpAllCallBacks = {
* Flush all the callbacks we have on a server.
*/
int afs_fs_give_up_all_callbacks(struct afs_server *server,
+ struct afs_addr_cursor *ac,
struct key *key,
bool async)
{
@@ -1831,5 +1830,5 @@ int afs_fs_give_up_all_callbacks(struct afs_server *server,
*bp++ = htonl(FSGIVEUPALLCALLBACKS);
/* Can't take a ref on server */
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ return afs_make_call(ac, call, GFP_NOFS, async);
}
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 51e3825b5ffb..df52bf18a263 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -71,6 +71,17 @@ enum afs_call_state {
};
/*
+ * List of server addresses.
+ */
+struct afs_addr_list {
+ struct rcu_head rcu; /* Must be first */
+ refcount_t usage;
+ unsigned short nr_addrs;
+ unsigned short index; /* Address currently in use */
+ struct sockaddr_rxrpc addrs[];
+};
+
+/*
* a record of an in-progress RxRPC call
*/
struct afs_call {
@@ -283,16 +294,15 @@ struct afs_cell {
#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;
spinlock_t vl_lock; /* vl_list lock */
/* VLDB server list. */
- seqlock_t vl_addrs_lock;
- unsigned short vl_naddrs; /* number of VL servers in addr list */
- unsigned short vl_curr_svix; /* current server index */
- struct sockaddr_rxrpc vl_addrs[AFS_CELL_MAX_ADDRS]; /* cell VL server addresses */
+ 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 */
};
@@ -343,7 +353,7 @@ struct afs_vlocation {
struct afs_server {
atomic_t usage;
time64_t time_of_death; /* time at which put reduced usage to 0 */
- struct sockaddr_rxrpc addr; /* server address */
+ struct afs_addr_list __rcu *addrs; /* List of addresses for this server */
struct afs_net *net; /* Network namespace in which the server resides */
struct afs_cell *cell; /* cell in which server resides */
struct list_head link; /* link in cell's server list */
@@ -485,8 +495,49 @@ struct afs_interface {
unsigned mtu; /* MTU of interface */
};
+/*
+ * 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;
+ 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_server *server; /* Current server (pins ref) */
+};
+
/*****************************************************************************/
/*
+ * 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 *);
+
+/*
* cache.c
*/
#ifdef CONFIG_AFS_FSCACHE
@@ -521,17 +572,11 @@ static inline struct afs_cb_interest *afs_get_cb_interest(struct afs_cb_interest
/*
* cell.c
*/
- static inline struct afs_cell *afs_get_cell(struct afs_cell *cell)
-{
- if (cell)
- atomic_inc(&cell->usage);
- return cell;
-}
-
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 *);
@@ -574,40 +619,41 @@ extern int afs_flock(struct file *, int, struct file_lock *);
/*
* fsclient.c
*/
-extern int afs_fs_fetch_file_status(struct afs_server *, struct key *,
+extern int afs_fs_fetch_file_status(struct afs_fs_cursor *, struct key *,
struct afs_vnode *, struct afs_volsync *,
bool);
extern int afs_fs_give_up_callbacks(struct afs_net *, struct afs_server *, bool);
-extern int afs_fs_fetch_data(struct afs_server *, struct key *,
+extern int afs_fs_fetch_data(struct afs_fs_cursor *, struct key *,
struct afs_vnode *, struct afs_read *, bool);
-extern int afs_fs_create(struct afs_server *, struct key *,
+extern int afs_fs_create(struct afs_fs_cursor *, struct key *,
struct afs_vnode *, const char *, umode_t,
struct afs_fid *, struct afs_file_status *,
struct afs_callback *, bool);
-extern int afs_fs_remove(struct afs_server *, struct key *,
+extern int afs_fs_remove(struct afs_fs_cursor *, struct key *,
struct afs_vnode *, const char *, bool, bool);
-extern int afs_fs_link(struct afs_server *, struct key *, struct afs_vnode *,
+extern int afs_fs_link(struct afs_fs_cursor *, struct key *, struct afs_vnode *,
struct afs_vnode *, const char *, bool);
-extern int afs_fs_symlink(struct afs_server *, struct key *,
+extern int afs_fs_symlink(struct afs_fs_cursor *, struct key *,
struct afs_vnode *, const char *, const char *,
struct afs_fid *, struct afs_file_status *, bool);
-extern int afs_fs_rename(struct afs_server *, struct key *,
+extern int afs_fs_rename(struct afs_fs_cursor *, struct key *,
struct afs_vnode *, const char *,
struct afs_vnode *, const char *, bool);
-extern int afs_fs_store_data(struct afs_server *, struct afs_writeback *,
+extern int afs_fs_store_data(struct afs_fs_cursor *, struct afs_writeback *,
pgoff_t, pgoff_t, unsigned, unsigned, bool);
-extern int afs_fs_setattr(struct afs_server *, struct key *,
+extern int afs_fs_setattr(struct afs_fs_cursor *, struct key *,
struct afs_vnode *, struct iattr *, bool);
-extern int afs_fs_get_volume_status(struct afs_server *, struct key *,
+extern int afs_fs_get_volume_status(struct afs_fs_cursor *, struct key *,
struct afs_vnode *,
struct afs_volume_status *, bool);
-extern int afs_fs_set_lock(struct afs_server *, struct key *,
+extern int afs_fs_set_lock(struct afs_fs_cursor *, struct key *,
struct afs_vnode *, afs_lock_type_t, bool);
-extern int afs_fs_extend_lock(struct afs_server *, struct key *,
+extern int afs_fs_extend_lock(struct afs_fs_cursor *, struct key *,
struct afs_vnode *, bool);
-extern int afs_fs_release_lock(struct afs_server *, struct key *,
+extern int afs_fs_release_lock(struct afs_fs_cursor *, struct key *,
struct afs_vnode *, bool);
-extern int afs_fs_give_up_all_callbacks(struct afs_server *, struct key *, bool);
+extern int afs_fs_give_up_all_callbacks(struct afs_server *, struct afs_addr_cursor *,
+ struct key *, bool);
/*
* inode.c
@@ -697,7 +743,7 @@ extern void __net_exit afs_close_socket(struct afs_net *);
extern void afs_charge_preallocation(struct work_struct *);
extern void afs_put_call(struct afs_call *);
extern int afs_queue_call_work(struct afs_call *);
-extern long afs_make_call(struct sockaddr_rxrpc *, struct afs_call *, gfp_t, bool);
+extern long afs_make_call(struct afs_addr_cursor *, struct afs_call *, gfp_t, bool);
extern struct afs_call *afs_alloc_flat_call(struct afs_net *,
const struct afs_call_type *,
size_t, size_t);
@@ -751,13 +797,11 @@ extern void __exit afs_fs_exit(void);
/*
* vlclient.c
*/
-extern int afs_vl_get_entry_by_name(struct afs_net *,
- struct sockaddr_rxrpc *, struct key *,
- const char *, struct afs_cache_vlocation *,
- bool);
-extern int afs_vl_get_entry_by_id(struct afs_net *,
- struct sockaddr_rxrpc *, struct key *,
- afs_volid_t, afs_voltype_t,
+extern int afs_vl_get_entry_by_name(struct afs_net *, struct afs_addr_cursor *,
+ struct key *, const char *,
+ struct afs_cache_vlocation *, bool);
+extern int afs_vl_get_entry_by_id(struct afs_net *, struct afs_addr_cursor *,
+ struct key *, afs_volid_t, afs_voltype_t,
struct afs_cache_vlocation *, bool);
/*
@@ -828,9 +872,11 @@ static inline struct afs_volume *afs_get_volume(struct afs_volume *volume)
extern void afs_put_volume(struct afs_cell *, struct afs_volume *);
extern struct afs_volume *afs_volume_lookup(struct afs_mount_params *);
-extern struct afs_server *afs_volume_pick_fileserver(struct afs_vnode *);
-extern int afs_volume_release_fileserver(struct afs_vnode *,
- struct afs_server *, int);
+extern void afs_init_fs_cursor(struct afs_fs_cursor *, struct afs_vnode *);
+extern int afs_set_fs_cursor(struct afs_fs_cursor *, struct afs_vnode *);
+extern bool afs_volume_pick_fileserver(struct afs_fs_cursor *, struct afs_vnode *);
+extern bool afs_iterate_fs_cursor(struct afs_fs_cursor *, struct afs_vnode *);
+extern int afs_end_fs_cursor(struct afs_fs_cursor *, struct afs_net *);
/*
* write.c
diff --git a/fs/afs/proc.c b/fs/afs/proc.c
index 08565429615d..9cf9ce88a8dd 100644
--- a/fs/afs/proc.c
+++ b/fs/afs/proc.c
@@ -514,23 +514,23 @@ static int afs_proc_cell_vlservers_open(struct inode *inode, struct file *file)
*/
static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos)
{
+ struct afs_addr_list *alist;
struct afs_cell *cell = m->private;
loff_t pos = *_pos;
- _enter("cell=%p pos=%Ld", cell, *_pos);
+ rcu_read_lock();
- /* lock the list against modification */
- down_read(&cell->vl_sem);
+ alist = rcu_dereference(cell->vl_addrs);
/* allow for the header line */
if (!pos)
return (void *) 1;
pos--;
- if (pos >= cell->vl_naddrs)
+ if (!alist || pos >= alist->nr_addrs)
return NULL;
- return &cell->vl_addrs[pos];
+ return alist->addrs + pos;
}
/*
@@ -539,17 +539,18 @@ static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos)
static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v,
loff_t *_pos)
{
+ struct afs_addr_list *alist;
struct afs_cell *cell = p->private;
loff_t pos;
- _enter("cell=%p{nad=%u} pos=%Ld", cell, cell->vl_naddrs, *_pos);
+ alist = rcu_dereference(cell->vl_addrs);
pos = *_pos;
(*_pos)++;
- if (pos >= cell->vl_naddrs)
+ if (!alist || pos >= alist->nr_addrs)
return NULL;
- return &cell->vl_addrs[pos];
+ return alist->addrs + pos;
}
/*
@@ -557,9 +558,7 @@ static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v,
*/
static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v)
{
- struct afs_cell *cell = p->private;
-
- up_read(&cell->vl_sem);
+ rcu_read_unlock();
}
/*
@@ -658,7 +657,7 @@ static int afs_proc_cell_servers_show(struct seq_file *m, void *v)
}
/* display one cell per line on subsequent lines */
- sprintf(ipaddr, "%pISp", &server->addr.transport);
+ sprintf(ipaddr, "%pISp", &server->addrs->addrs[0].transport);
seq_printf(m, "%3d %-15s %5d\n",
atomic_read(&server->usage), ipaddr, server->fs_state);
diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c
index ac1e25f957b1..5ddfb7c4cf78 100644
--- a/fs/afs/rxrpc.c
+++ b/fs/afs/rxrpc.c
@@ -321,9 +321,10 @@ static int afs_send_pages(struct afs_call *call, struct msghdr *msg)
/*
* initiate a call
*/
-long afs_make_call(struct sockaddr_rxrpc *srx, struct afs_call *call,
+long afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call,
gfp_t gfp, bool async)
{
+ struct sockaddr_rxrpc *srx = ac->addr;
struct rxrpc_call *rxcall;
struct msghdr msg;
struct kvec iov[1];
diff --git a/fs/afs/server.c b/fs/afs/server.c
index 4e66608fc805..9ca174b24f5b 100644
--- a/fs/afs/server.c
+++ b/fs/afs/server.c
@@ -56,7 +56,9 @@ static int afs_install_server(struct afs_server *server)
p = *pp;
_debug("- consider %p", p);
xserver = rb_entry(p, struct afs_server, master_rb);
- diff = memcmp(&server->addr, &xserver->addr, sizeof(server->addr));
+ diff = memcmp(&server->addrs->addrs[0],
+ &xserver->addrs->addrs[0],
+ sizeof(sizeof(server->addrs->addrs[0])));
if (diff < 0)
pp = &(*pp)->rb_left;
else if (diff > 0)
@@ -85,25 +87,38 @@ static struct afs_server *afs_alloc_server(struct afs_cell *cell,
_enter("");
server = kzalloc(sizeof(struct afs_server), GFP_KERNEL);
- if (server) {
- atomic_set(&server->usage, 1);
- server->net = cell->net;
- server->cell = cell;
-
- INIT_LIST_HEAD(&server->link);
- INIT_LIST_HEAD(&server->grave);
- init_rwsem(&server->sem);
- spin_lock_init(&server->fs_lock);
- INIT_LIST_HEAD(&server->cb_interests);
- rwlock_init(&server->cb_break_lock);
-
- server->addr = *addr;
- afs_inc_servers_outstanding(cell->net);
- _leave(" = %p{%d}", server, atomic_read(&server->usage));
- } else {
- _leave(" = NULL [nomem]");
- }
+ if (!server)
+ goto enomem;
+ server->addrs = kzalloc(sizeof(struct afs_addr_list) +
+ sizeof(struct sockaddr_rxrpc),
+ GFP_KERNEL);
+ if (!server->addrs)
+ goto enomem_server;
+
+ atomic_set(&server->usage, 1);
+ server->net = cell->net;
+ server->cell = cell;
+
+ INIT_LIST_HEAD(&server->link);
+ INIT_LIST_HEAD(&server->grave);
+ init_rwsem(&server->sem);
+ spin_lock_init(&server->fs_lock);
+ INIT_LIST_HEAD(&server->cb_interests);
+ rwlock_init(&server->cb_break_lock);
+
+ refcount_set(&server->addrs->usage, 1);
+ server->addrs->nr_addrs = 1;
+ server->addrs->addrs[0] = *addr;
+ afs_inc_servers_outstanding(cell->net);
+
+ _leave(" = %p{%d}", server, atomic_read(&server->usage));
return server;
+
+enomem_server:
+ kfree(server);
+enomem:
+ _leave(" = NULL [nomem]");
+ return NULL;
}
/*
@@ -120,7 +135,7 @@ struct afs_server *afs_lookup_server(struct afs_cell *cell,
read_lock(&cell->servers_lock);
list_for_each_entry(server, &cell->servers, link) {
- if (memcmp(&server->addr, addr, sizeof(*addr)) == 0)
+ if (memcmp(&server->addrs->addrs[0], addr, sizeof(*addr)) == 0)
goto found_server_quickly;
}
read_unlock(&cell->servers_lock);
@@ -135,7 +150,7 @@ struct afs_server *afs_lookup_server(struct afs_cell *cell,
/* check the cell's server list again */
list_for_each_entry(server, &cell->servers, link) {
- if (memcmp(&server->addr, addr, sizeof(*addr)) == 0)
+ if (memcmp(&server->addrs->addrs[0], addr, sizeof(*addr)) == 0)
goto found_server;
}
@@ -204,7 +219,7 @@ struct afs_server *afs_find_server(struct afs_net *net,
_debug("- consider %p", p);
- diff = memcmp(srx, &server->addr, sizeof(*srx));
+ diff = memcmp(srx, &server->addrs->addrs[0], sizeof(*srx));
if (diff < 0) {
p = p->rb_left;
} else if (diff > 0) {
@@ -269,10 +284,19 @@ void afs_put_server(struct afs_net *net, struct afs_server *server)
*/
static void afs_destroy_server(struct afs_net *net, struct afs_server *server)
{
+ struct afs_addr_list *alist = server->addrs;
+ struct afs_addr_cursor ac = {
+ .alist = alist,
+ .addr = &alist->addrs[0],
+ .start = alist->index,
+ .index = alist->index,
+ .error = 0,
+ };
_enter("%p", server);
- afs_fs_give_up_all_callbacks(server, NULL, false);
+ afs_fs_give_up_all_callbacks(server, &ac, NULL, false);
afs_put_cell(net, server->cell);
+ afs_put_addrlist(server->addrs);
kfree(server);
afs_dec_servers_outstanding(net);
}
diff --git a/fs/afs/vlclient.c b/fs/afs/vlclient.c
index aa79fe3f168b..1d1e7df77dd5 100644
--- a/fs/afs/vlclient.c
+++ b/fs/afs/vlclient.c
@@ -114,7 +114,7 @@ static const struct afs_call_type afs_RXVLGetEntryById = {
* dispatch a get volume entry by name operation
*/
int afs_vl_get_entry_by_name(struct afs_net *net,
- struct sockaddr_rxrpc *addr,
+ struct afs_addr_cursor *ac,
struct key *key,
const char *volname,
struct afs_cache_vlocation *entry,
@@ -146,14 +146,14 @@ int afs_vl_get_entry_by_name(struct afs_net *net,
memset((void *) bp + volnamesz, 0, padsz);
/* initiate the call */
- return afs_make_call(addr, call, GFP_KERNEL, async);
+ return afs_make_call(ac, call, GFP_KERNEL, async);
}
/*
* dispatch a get volume entry by ID operation
*/
int afs_vl_get_entry_by_id(struct afs_net *net,
- struct sockaddr_rxrpc *addr,
+ struct afs_addr_cursor *ac,
struct key *key,
afs_volid_t volid,
afs_voltype_t voltype,
@@ -179,5 +179,5 @@ int afs_vl_get_entry_by_id(struct afs_net *net,
*bp = htonl(voltype);
/* initiate the call */
- return afs_make_call(addr, call, GFP_KERNEL, async);
+ return afs_make_call(ac, call, GFP_KERNEL, async);
}
diff --git a/fs/afs/vlocation.c b/fs/afs/vlocation.c
index ec5ab8dc9bc8..52c31ad0ef60 100644
--- a/fs/afs/vlocation.c
+++ b/fs/afs/vlocation.c
@@ -29,22 +29,25 @@ static int afs_vlocation_access_vl_by_name(struct afs_vlocation *vl,
struct key *key,
struct afs_cache_vlocation *vldb)
{
- struct afs_cell *cell = vl->cell;
- int count, ret;
+ struct afs_addr_cursor ac;
+ int ret;
- _enter("%s,%s", cell->name, vl->vldb.name);
+ _enter("%s,%s", vl->cell->name, vl->vldb.name);
+
+ ret = afs_set_vl_cursor(&ac, vl->cell);
+ if (ret < 0)
+ return ret;
down_write(&vl->cell->vl_sem);
+
ret = -ENOMEDIUM;
- for (count = cell->vl_naddrs; count > 0; count--) {
- struct sockaddr_rxrpc *addr = &cell->vl_addrs[cell->vl_curr_svix];
-
- _debug("CellServ[%hu]: %pIS", cell->vl_curr_svix, &addr->transport);
+ while (afs_iterate_addresses(&ac)) {
+ _debug("CellServ[%hu]: %pIS", ac.index, &ac.addr->transport);
/* attempt to access the VL server */
- ret = afs_vl_get_entry_by_name(cell->net, addr, key,
- vl->vldb.name, vldb, false);
- switch (ret) {
+ ac.error = afs_vl_get_entry_by_name(vl->cell->net, &ac, key,
+ vl->vldb.name, vldb, false);
+ switch (ac.error) {
case 0:
goto out;
case -ENOMEM:
@@ -52,26 +55,24 @@ static int afs_vlocation_access_vl_by_name(struct afs_vlocation *vl,
case -ENETUNREACH:
case -EHOSTUNREACH:
case -ECONNREFUSED:
- if (ret == -ENOMEM || ret == -ENONET)
+ if (ac.error == -ENOMEM || ac.error == -ENONET)
goto out;
- goto rotate;
+ break;
case -ENOMEDIUM:
case -EKEYREJECTED:
case -EKEYEXPIRED:
+ ac.responded = true;
goto out;
default:
- ret = -EIO;
- goto rotate;
+ ac.responded = true;
+ ac.error = -EIO;
+ break;
}
-
- /* rotate the server records upon lookup failure */
- rotate:
- cell->vl_curr_svix++;
- cell->vl_curr_svix %= cell->vl_naddrs;
}
out:
up_write(&vl->cell->vl_sem);
+ ret = afs_end_cursor(&ac);
_leave(" = %d", ret);
return ret;
}
@@ -86,22 +87,24 @@ static int afs_vlocation_access_vl_by_id(struct afs_vlocation *vl,
afs_voltype_t voltype,
struct afs_cache_vlocation *vldb)
{
- struct afs_cell *cell = vl->cell;
- int count, ret;
+ struct afs_addr_cursor ac;
+ int ret;
- _enter("%s,%x,%d,", cell->name, volid, voltype);
+ _enter("%s,%x,%d,", vl->cell->name, volid, voltype);
+
+ ret = afs_set_vl_cursor(&ac, vl->cell);
+ if (ret < 0)
+ return ret;
down_write(&vl->cell->vl_sem);
ret = -ENOMEDIUM;
- for (count = cell->vl_naddrs; count > 0; count--) {
- struct sockaddr_rxrpc *addr = &cell->vl_addrs[cell->vl_curr_svix];
-
- _debug("CellServ[%hu]: %pIS", cell->vl_curr_svix, &addr->transport);
+ while (afs_iterate_addresses(&ac)) {
+ _debug("CellServ[%hu]: %pIS", ac.index, &ac.addr->transport);
/* attempt to access the VL server */
- ret = afs_vl_get_entry_by_id(cell->net, addr, key, volid,
- voltype, vldb, false);
- switch (ret) {
+ ac.error = afs_vl_get_entry_by_id(vl->cell->net, &ac, key, volid,
+ voltype, vldb, false);
+ switch (ac.error) {
case 0:
goto out;
case -ENOMEM:
@@ -109,10 +112,11 @@ static int afs_vlocation_access_vl_by_id(struct afs_vlocation *vl,
case -ENETUNREACH:
case -EHOSTUNREACH:
case -ECONNREFUSED:
- if (ret == -ENOMEM || ret == -ENONET)
+ if (ac.error == -ENOMEM || ac.error == -ENONET)
goto out;
goto rotate;
case -EBUSY:
+ ac.responded = true;
vl->upd_busy_cnt++;
if (vl->upd_busy_cnt <= 3) {
if (vl->upd_busy_cnt > 1) {
@@ -124,30 +128,31 @@ static int afs_vlocation_access_vl_by_id(struct afs_vlocation *vl,
}
break;
case -ENOMEDIUM:
+ ac.responded = true;
vl->upd_rej_cnt++;
goto rotate;
default:
- ret = -EIO;
+ ac.responded = true;
+ ac.error = -EIO;
goto rotate;
}
/* rotate the server records upon lookup failure */
rotate:
- cell->vl_curr_svix++;
- cell->vl_curr_svix %= cell->vl_naddrs;
vl->upd_busy_cnt = 0;
}
out:
- if (ret < 0 && vl->upd_rej_cnt > 0) {
+ if (ac.error < 0 && vl->upd_rej_cnt > 0) {
printk(KERN_NOTICE "kAFS:"
" Active volume no longer valid '%s'\n",
vl->vldb.name);
vl->valid = 0;
- ret = -ENOMEDIUM;
+ ac.error = -ENOMEDIUM;
}
up_write(&vl->cell->vl_sem);
+ ret = afs_end_cursor(&ac);
_leave(" = %d", ret);
return ret;
}
diff --git a/fs/afs/vnode.c b/fs/afs/vnode.c
index 622e1100099b..9c7333eb01c2 100644
--- a/fs/afs/vnode.c
+++ b/fs/afs/vnode.c
@@ -44,30 +44,26 @@ static void afs_vnode_deleted_remotely(struct afs_vnode *vnode)
void afs_vnode_finalise_status_update(struct afs_vnode *vnode,
struct afs_server *server)
{
- struct afs_server *oldserver = NULL;
-
- _enter("%p,%p", vnode, server);
-
spin_lock(&vnode->lock);
vnode->update_cnt--;
ASSERTCMP(vnode->update_cnt, >=, 0);
spin_unlock(&vnode->lock);
wake_up_all(&vnode->update_waitq);
- afs_put_server(afs_v2net(vnode), oldserver);
_leave("");
}
/*
* finish off updating the recorded status of a file after an operation failed
*/
-static void afs_vnode_status_update_failed(struct afs_vnode *vnode, int ret)
+static void afs_vnode_status_update_failed(struct afs_fs_cursor *fc,
+ struct afs_vnode *vnode)
{
- _enter("{%x:%u},%d", vnode->fid.vid, vnode->fid.vnode, ret);
+ _enter("{%x:%u},%d", vnode->fid.vid, vnode->fid.vnode, fc->ac.error);
spin_lock(&vnode->lock);
- if (ret == -ENOENT) {
+ if (fc->ac.error == -ENOENT) {
/* the file was deleted on the server */
_debug("got NOENT from server - marking file deleted");
afs_vnode_deleted_remotely(vnode);
@@ -90,9 +86,8 @@ static void afs_vnode_status_update_failed(struct afs_vnode *vnode, int ret)
*/
int afs_vnode_fetch_status(struct afs_vnode *vnode, struct key *key, bool force)
{
- struct afs_server *server;
+ struct afs_fs_cursor fc;
unsigned int cb_break = 0;
- int ret;
DECLARE_WAITQUEUE(myself, current);
@@ -172,43 +167,37 @@ get_anyway:
/* merge AFS status fetches and clear outstanding callback on this
* vnode */
+ afs_init_fs_cursor(&fc, vnode);
do {
/* pick a server to query */
- server = afs_volume_pick_fileserver(vnode);
- if (IS_ERR(server))
+ if (!afs_volume_pick_fileserver(&fc, vnode))
goto no_server;
- _debug("USING SERVER: %p{%pIS}",
- server, &server->addr.transport);
-
- ret = afs_fs_fetch_file_status(server, key, vnode, NULL,
- false);
+ fc.ac.error = afs_fs_fetch_file_status(&fc, key, vnode, NULL, false);
- } while (!afs_volume_release_fileserver(vnode, server, ret));
+ } while (afs_iterate_fs_cursor(&fc, vnode));
/* adjust the flags */
- if (ret == 0) {
+ if (fc.ac.error == 0) {
_debug("adjust");
afs_cache_permit(vnode, key, cb_break);
- afs_vnode_finalise_status_update(vnode, server);
- afs_put_server(afs_v2net(vnode), server);
+ afs_vnode_finalise_status_update(vnode, fc.server);
} else {
- _debug("failed [%d]", ret);
- afs_vnode_status_update_failed(vnode, ret);
+ _debug("failed [%d]", fc.ac.error);
+ afs_vnode_status_update_failed(&fc, vnode);
}
+out:
+ afs_end_fs_cursor(&fc, afs_v2net(vnode));
ASSERTCMP(vnode->update_cnt, >=, 0);
-
- _leave(" = %d [cnt %d]", ret, vnode->update_cnt);
- return ret;
+ _leave(" = %d [cnt %d]", fc.ac.error, vnode->update_cnt);
+ return fc.ac.error;
no_server:
spin_lock(&vnode->lock);
vnode->update_cnt--;
- ASSERTCMP(vnode->update_cnt, >=, 0);
spin_unlock(&vnode->lock);
- _leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
- return PTR_ERR(server);
+ goto out;
}
/*
@@ -218,8 +207,7 @@ no_server:
int afs_vnode_fetch_data(struct afs_vnode *vnode, struct key *key,
struct afs_read *desc)
{
- struct afs_server *server;
- int ret;
+ struct afs_fs_cursor fc;
_enter("%s{%x:%u.%u},%x,,,",
vnode->volume->vlocation->vldb.name,
@@ -235,36 +223,31 @@ int afs_vnode_fetch_data(struct afs_vnode *vnode, struct key *key,
/* merge in AFS status fetches and clear outstanding callback on this
* vnode */
+ afs_init_fs_cursor(&fc, vnode);
do {
/* pick a server to query */
- server = afs_volume_pick_fileserver(vnode);
- if (IS_ERR(server))
+ if (!afs_volume_pick_fileserver(&fc, vnode))
goto no_server;
- _debug("USING SERVER: %pIS\n", &server->addr.transport);
-
- ret = afs_fs_fetch_data(server, key, vnode, desc,
- false);
+ fc.ac.error = afs_fs_fetch_data(&fc, key, vnode, desc, false);
- } while (!afs_volume_release_fileserver(vnode, server, ret));
+ } while (afs_iterate_fs_cursor(&fc, vnode));
/* adjust the flags */
- if (ret == 0) {
- afs_vnode_finalise_status_update(vnode, server);
- afs_put_server(afs_v2net(vnode), server);
- } else {
- afs_vnode_status_update_failed(vnode, ret);
- }
+ if (fc.ac.error == 0)
+ afs_vnode_finalise_status_update(vnode, fc.server);
+ else
+ afs_vnode_status_update_failed(&fc, vnode);
- _leave(" = %d", ret);
- return ret;
+out:
+ return afs_end_fs_cursor(&fc, afs_v2net(vnode));
no_server:
spin_lock(&vnode->lock);
vnode->update_cnt--;
ASSERTCMP(vnode->update_cnt, >=, 0);
spin_unlock(&vnode->lock);
- return PTR_ERR(server);
+ goto out;
}
/*
@@ -275,8 +258,7 @@ int afs_vnode_create(struct afs_vnode *vnode, struct key *key,
struct afs_file_status *newstatus,
struct afs_callback *newcb, struct afs_server **_server)
{
- struct afs_server *server;
- int ret;
+ struct afs_fs_cursor fc;
_enter("%s{%x:%u.%u},%x,%s,,",
vnode->volume->vlocation->vldb.name,
@@ -291,38 +273,36 @@ int afs_vnode_create(struct afs_vnode *vnode, struct key *key,
vnode->update_cnt++;
spin_unlock(&vnode->lock);
+ afs_init_fs_cursor(&fc, vnode);
do {
/* pick a server to query */
- server = afs_volume_pick_fileserver(vnode);
- if (IS_ERR(server))
+ if (!afs_volume_pick_fileserver(&fc, vnode))
goto no_server;
- _debug("USING SERVER: %pIS\n", &server->addr.transport);
-
- ret = afs_fs_create(server, key, vnode, name, mode, newfid,
- newstatus, newcb, false);
+ fc.ac.error = afs_fs_create(&fc, key, vnode, name, mode, newfid,
+ newstatus, newcb, false);
- } while (!afs_volume_release_fileserver(vnode, server, ret));
+ } while (afs_iterate_fs_cursor(&fc, vnode));
/* adjust the flags */
- if (ret == 0) {
- afs_vnode_finalise_status_update(vnode, server);
- *_server = server;
+ if (fc.ac.error == 0) {
+ afs_vnode_finalise_status_update(vnode, fc.server);
+ *_server = fc.server;
+ fc.server = NULL;
} else {
- afs_vnode_status_update_failed(vnode, ret);
+ afs_vnode_status_update_failed(&fc, vnode);
*_server = NULL;
}
- _leave(" = %d [cnt %d]", ret, vnode->update_cnt);
- return ret;
+out:
+ return afs_end_fs_cursor(&fc, afs_v2net(vnode));
no_server:
spin_lock(&vnode->lock);
vnode->update_cnt--;
ASSERTCMP(vnode->update_cnt, >=, 0);
spin_unlock(&vnode->lock);
- _leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
- return PTR_ERR(server);
+ goto out;
}
/*
@@ -331,8 +311,7 @@ no_server:
int afs_vnode_remove(struct afs_vnode *vnode, struct key *key, const char *name,
bool isdir)
{
- struct afs_server *server;
- int ret;
+ struct afs_fs_cursor fc;
_enter("%s{%x:%u.%u},%x,%s",
vnode->volume->vlocation->vldb.name,
@@ -347,37 +326,31 @@ int afs_vnode_remove(struct afs_vnode *vnode, struct key *key, const char *name,
vnode->update_cnt++;
spin_unlock(&vnode->lock);
+ afs_init_fs_cursor(&fc, vnode);
do {
/* pick a server to query */
- server = afs_volume_pick_fileserver(vnode);
- if (IS_ERR(server))
+ if (!afs_volume_pick_fileserver(&fc, vnode))
goto no_server;
- _debug("USING SERVER: %pIS\n", &server->addr.transport);
-
- ret = afs_fs_remove(server, key, vnode, name, isdir,
- false);
+ fc.ac.error = afs_fs_remove(&fc, key, vnode, name, isdir, false);
- } while (!afs_volume_release_fileserver(vnode, server, ret));
+ } while (afs_iterate_fs_cursor(&fc, vnode));
/* adjust the flags */
- if (ret == 0) {
- afs_vnode_finalise_status_update(vnode, server);
- afs_put_server(afs_v2net(vnode), server);
- } else {
- afs_vnode_status_update_failed(vnode, ret);
- }
+ if (fc.ac.error == 0)
+ afs_vnode_finalise_status_update(vnode, fc.server);
+ else
+ afs_vnode_status_update_failed(&fc, vnode);
- _leave(" = %d [cnt %d]", ret, vnode->update_cnt);
- return ret;
+out:
+ return afs_end_fs_cursor(&fc, afs_v2net(vnode));
no_server:
spin_lock(&vnode->lock);
vnode->update_cnt--;
ASSERTCMP(vnode->update_cnt, >=, 0);
spin_unlock(&vnode->lock);
- _leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
- return PTR_ERR(server);
+ goto out;
}
/*
@@ -386,8 +359,7 @@ no_server:
int afs_vnode_link(struct afs_vnode *dvnode, struct afs_vnode *vnode,
struct key *key, const char *name)
{
- struct afs_server *server;
- int ret;
+ struct afs_fs_cursor fc;
_enter("%s{%x:%u.%u},%s{%x:%u.%u},%x,%s",
dvnode->volume->vlocation->vldb.name,
@@ -409,31 +381,27 @@ int afs_vnode_link(struct afs_vnode *dvnode, struct afs_vnode *vnode,
dvnode->update_cnt++;
spin_unlock(&dvnode->lock);
+ afs_init_fs_cursor(&fc, vnode);
do {
/* pick a server to query */
- server = afs_volume_pick_fileserver(dvnode);
- if (IS_ERR(server))
+ if (!afs_volume_pick_fileserver(&fc, dvnode))
goto no_server;
- _debug("USING SERVER: %pIS\n", &server->addr.transport);
+ fc.ac.error = afs_fs_link(&fc, key, dvnode, vnode, name, false);
- ret = afs_fs_link(server, key, dvnode, vnode, name,
- false);
-
- } while (!afs_volume_release_fileserver(dvnode, server, ret));
+ } while (afs_iterate_fs_cursor(&fc, dvnode));
/* adjust the flags */
- if (ret == 0) {
- afs_vnode_finalise_status_update(vnode, server);
- afs_vnode_finalise_status_update(dvnode, server);
- afs_put_server(afs_v2net(dvnode), server);
+ if (fc.ac.error == 0) {
+ afs_vnode_finalise_status_update(vnode, fc.server);
+ afs_vnode_finalise_status_update(dvnode, fc.server);
} else {
- afs_vnode_status_update_failed(vnode, ret);
- afs_vnode_status_update_failed(dvnode, ret);
+ afs_vnode_status_update_failed(&fc, vnode);
+ afs_vnode_status_update_failed(&fc, dvnode);
}
- _leave(" = %d [cnt %d]", ret, vnode->update_cnt);
- return ret;
+out:
+ return afs_end_fs_cursor(&fc, afs_v2net(vnode));
no_server:
spin_lock(&vnode->lock);
@@ -444,8 +412,7 @@ no_server:
dvnode->update_cnt--;
ASSERTCMP(dvnode->update_cnt, >=, 0);
spin_unlock(&dvnode->lock);
- _leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
- return PTR_ERR(server);
+ goto out;
}
/*
@@ -457,8 +424,7 @@ int afs_vnode_symlink(struct afs_vnode *vnode, struct key *key,
struct afs_file_status *newstatus,
struct afs_server **_server)
{
- struct afs_server *server;
- int ret;
+ struct afs_fs_cursor fc;
_enter("%s{%x:%u.%u},%x,%s,%s,,,",
vnode->volume->vlocation->vldb.name,
@@ -473,38 +439,37 @@ int afs_vnode_symlink(struct afs_vnode *vnode, struct key *key,
vnode->update_cnt++;
spin_unlock(&vnode->lock);
+ afs_init_fs_cursor(&fc, vnode);
do {
/* pick a server to query */
- server = afs_volume_pick_fileserver(vnode);
- if (IS_ERR(server))
+ if (!afs_volume_pick_fileserver(&fc, vnode))
goto no_server;
- _debug("USING SERVER: %pIS\n", &server->addr.transport);
+ fc.ac.error = afs_fs_symlink(&fc, key, vnode, name, content,
+ newfid, newstatus, false);
- ret = afs_fs_symlink(server, key, vnode, name, content,
- newfid, newstatus, false);
-
- } while (!afs_volume_release_fileserver(vnode, server, ret));
+ } while (afs_iterate_fs_cursor(&fc, vnode));
/* adjust the flags */
- if (ret == 0) {
- afs_vnode_finalise_status_update(vnode, server);
- *_server = server;
+ if (fc.ac.error == 0) {
+ afs_vnode_finalise_status_update(vnode, fc.server);
+ *_server = fc.server;
+ fc.server = NULL;
} else {
- afs_vnode_status_update_failed(vnode, ret);
+ afs_vnode_status_update_failed(&fc, vnode);
*_server = NULL;
}
- _leave(" = %d [cnt %d]", ret, vnode->update_cnt);
- return ret;
+out:
+ return afs_end_fs_cursor(&fc, afs_v2net(vnode));
no_server:
spin_lock(&vnode->lock);
vnode->update_cnt--;
ASSERTCMP(vnode->update_cnt, >=, 0);
spin_unlock(&vnode->lock);
- _leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
- return PTR_ERR(server);
+ *_server = NULL;
+ goto out;
}
/*
@@ -516,8 +481,7 @@ int afs_vnode_rename(struct afs_vnode *orig_dvnode,
const char *orig_name,
const char *new_name)
{
- struct afs_server *server;
- int ret;
+ struct afs_fs_cursor fc;
_enter("%s{%x:%u.%u},%s{%u,%u,%u},%x,%s,%s",
orig_dvnode->volume->vlocation->vldb.name,
@@ -543,33 +507,30 @@ int afs_vnode_rename(struct afs_vnode *orig_dvnode,
spin_unlock(&new_dvnode->lock);
}
+ afs_init_fs_cursor(&fc, orig_dvnode);
do {
/* pick a server to query */
- server = afs_volume_pick_fileserver(orig_dvnode);
- if (IS_ERR(server))
+ if (!afs_volume_pick_fileserver(&fc, orig_dvnode))
goto no_server;
- _debug("USING SERVER: %pIS\n", &server->addr.transport);
-
- ret = afs_fs_rename(server, key, orig_dvnode, orig_name,
- new_dvnode, new_name, false);
+ fc.ac.error = afs_fs_rename(&fc, key, orig_dvnode, orig_name,
+ new_dvnode, new_name, false);
- } while (!afs_volume_release_fileserver(orig_dvnode, server, ret));
+ } while (afs_iterate_fs_cursor(&fc, orig_dvnode));
/* adjust the flags */
- if (ret == 0) {
- afs_vnode_finalise_status_update(orig_dvnode, server);
+ if (fc.ac.error == 0) {
+ afs_vnode_finalise_status_update(orig_dvnode, fc.server);
if (new_dvnode != orig_dvnode)
- afs_vnode_finalise_status_update(new_dvnode, server);
- afs_put_server(afs_v2net(orig_dvnode), server);
+ afs_vnode_finalise_status_update(new_dvnode, fc.server);
} else {
- afs_vnode_status_update_failed(orig_dvnode, ret);
+ afs_vnode_status_update_failed(&fc, orig_dvnode);
if (new_dvnode != orig_dvnode)
- afs_vnode_status_update_failed(new_dvnode, ret);
+ afs_vnode_status_update_failed(&fc, new_dvnode);
}
- _leave(" = %d [cnt %d]", ret, orig_dvnode->update_cnt);
- return ret;
+out:
+ return afs_end_fs_cursor(&fc, afs_v2net(orig_dvnode));
no_server:
spin_lock(&orig_dvnode->lock);
@@ -582,8 +543,7 @@ no_server:
ASSERTCMP(new_dvnode->update_cnt, >=, 0);
spin_unlock(&new_dvnode->lock);
}
- _leave(" = %ld [cnt %d]", PTR_ERR(server), orig_dvnode->update_cnt);
- return PTR_ERR(server);
+ goto out;
}
/*
@@ -592,9 +552,8 @@ no_server:
int afs_vnode_store_data(struct afs_writeback *wb, pgoff_t first, pgoff_t last,
unsigned offset, unsigned to)
{
- struct afs_server *server;
+ struct afs_fs_cursor fc;
struct afs_vnode *vnode = wb->vnode;
- int ret;
_enter("%s{%x:%u.%u},%x,%lx,%lx,%x,%x",
vnode->volume->vlocation->vldb.name,
@@ -609,36 +568,33 @@ int afs_vnode_store_data(struct afs_writeback *wb, pgoff_t first, pgoff_t last,
vnode->update_cnt++;
spin_unlock(&vnode->lock);
+ afs_init_fs_cursor(&fc, vnode);
do {
/* pick a server to query */
- server = afs_volume_pick_fileserver(vnode);
- if (IS_ERR(server))
+ if (!afs_volume_pick_fileserver(&fc, vnode))
goto no_server;
- _debug("USING SERVER: %pIS\n", &server->addr.transport);
-
- ret = afs_fs_store_data(server, wb, first, last, offset, to,
- false);
+ fc.ac.error = afs_fs_store_data(&fc, wb, first, last, offset, to,
+ false);
- } while (!afs_volume_release_fileserver(vnode, server, ret));
+ } while (afs_iterate_fs_cursor(&fc, vnode));
/* adjust the flags */
- if (ret == 0) {
- afs_vnode_finalise_status_update(vnode, server);
- afs_put_server(afs_v2net(vnode), server);
+ if (fc.ac.error == 0) {
+ afs_vnode_finalise_status_update(vnode, fc.server);
} else {
- afs_vnode_status_update_failed(vnode, ret);
+ afs_vnode_status_update_failed(&fc, vnode);
}
- _leave(" = %d", ret);
- return ret;
+out:
+ return afs_end_fs_cursor(&fc, afs_v2net(vnode));
no_server:
spin_lock(&vnode->lock);
vnode->update_cnt--;
ASSERTCMP(vnode->update_cnt, >=, 0);
spin_unlock(&vnode->lock);
- return PTR_ERR(server);
+ goto out;
}
/*
@@ -647,8 +603,7 @@ no_server:
int afs_vnode_setattr(struct afs_vnode *vnode, struct key *key,
struct iattr *attr)
{
- struct afs_server *server;
- int ret;
+ struct afs_fs_cursor fc;
_enter("%s{%x:%u.%u},%x",
vnode->volume->vlocation->vldb.name,
@@ -662,35 +617,32 @@ int afs_vnode_setattr(struct afs_vnode *vnode, struct key *key,
vnode->update_cnt++;
spin_unlock(&vnode->lock);
+ afs_init_fs_cursor(&fc, vnode);
do {
/* pick a server to query */
- server = afs_volume_pick_fileserver(vnode);
- if (IS_ERR(server))
+ if (!afs_volume_pick_fileserver(&fc, vnode))
goto no_server;
- _debug("USING SERVER: %pIS\n", &server->addr.transport);
-
- ret = afs_fs_setattr(server, key, vnode, attr, false);
+ fc.ac.error = afs_fs_setattr(&fc, key, vnode, attr, false);
- } while (!afs_volume_release_fileserver(vnode, server, ret));
+ } while (afs_iterate_fs_cursor(&fc, vnode));
/* adjust the flags */
- if (ret == 0) {
- afs_vnode_finalise_status_update(vnode, server);
- afs_put_server(afs_v2net(vnode), server);
+ if (fc.ac.error == 0) {
+ afs_vnode_finalise_status_update(vnode, fc.server);
} else {
- afs_vnode_status_update_failed(vnode, ret);
+ afs_vnode_status_update_failed(&fc, vnode);
}
- _leave(" = %d", ret);
- return ret;
+out:
+ return afs_end_fs_cursor(&fc, afs_v2net(vnode));
no_server:
spin_lock(&vnode->lock);
vnode->update_cnt--;
ASSERTCMP(vnode->update_cnt, >=, 0);
spin_unlock(&vnode->lock);
- return PTR_ERR(server);
+ goto out;
}
/*
@@ -699,8 +651,7 @@ no_server:
int afs_vnode_get_volume_status(struct afs_vnode *vnode, struct key *key,
struct afs_volume_status *vs)
{
- struct afs_server *server;
- int ret;
+ struct afs_fs_cursor fc;
_enter("%s{%x:%u.%u},%x,",
vnode->volume->vlocation->vldb.name,
@@ -709,27 +660,17 @@ int afs_vnode_get_volume_status(struct afs_vnode *vnode, struct key *key,
vnode->fid.unique,
key_serial(key));
+ afs_init_fs_cursor(&fc, vnode);
do {
/* pick a server to query */
- server = afs_volume_pick_fileserver(vnode);
- if (IS_ERR(server))
- goto no_server;
-
- _debug("USING SERVER: %pIS\n", &server->addr.transport);
+ if (!afs_volume_pick_fileserver(&fc, vnode))
+ break;
- ret = afs_fs_get_volume_status(server, key, vnode, vs, false);
+ fc.ac.error = afs_fs_get_volume_status(&fc, key, vnode, vs, false);
- } while (!afs_volume_release_fileserver(vnode, server, ret));
+ } while (afs_iterate_fs_cursor(&fc, vnode));
- /* adjust the flags */
- if (ret == 0)
- afs_put_server(afs_v2net(vnode), server);
-
- _leave(" = %d", ret);
- return ret;
-
-no_server:
- return PTR_ERR(server);
+ return afs_end_fs_cursor(&fc, afs_v2net(vnode));
}
/*
@@ -738,8 +679,7 @@ no_server:
int afs_vnode_set_lock(struct afs_vnode *vnode, struct key *key,
afs_lock_type_t type)
{
- struct afs_server *server;
- int ret;
+ struct afs_fs_cursor fc;
_enter("%s{%x:%u.%u},%x,%u",
vnode->volume->vlocation->vldb.name,
@@ -748,27 +688,17 @@ int afs_vnode_set_lock(struct afs_vnode *vnode, struct key *key,
vnode->fid.unique,
key_serial(key), type);
+ afs_init_fs_cursor(&fc, vnode);
do {
/* pick a server to query */
- server = afs_volume_pick_fileserver(vnode);
- if (IS_ERR(server))
- goto no_server;
+ if (!afs_volume_pick_fileserver(&fc, vnode))
+ break;
- _debug("USING SERVER: %pIS\n", &server->addr.transport);
+ fc.ac.error = afs_fs_set_lock(&fc, key, vnode, type, false);
- ret = afs_fs_set_lock(server, key, vnode, type, false);
+ } while (afs_iterate_fs_cursor(&fc, vnode));
- } while (!afs_volume_release_fileserver(vnode, server, ret));
-
- /* adjust the flags */
- if (ret == 0)
- afs_put_server(afs_v2net(vnode), server);
-
- _leave(" = %d", ret);
- return ret;
-
-no_server:
- return PTR_ERR(server);
+ return afs_end_fs_cursor(&fc, afs_v2net(vnode));
}
/*
@@ -776,7 +706,7 @@ no_server:
*/
int afs_vnode_extend_lock(struct afs_vnode *vnode, struct key *key)
{
- struct afs_server *server;
+ struct afs_fs_cursor fc;
int ret;
_enter("%s{%x:%u.%u},%x",
@@ -786,27 +716,13 @@ int afs_vnode_extend_lock(struct afs_vnode *vnode, struct key *key)
vnode->fid.unique,
key_serial(key));
- do {
- /* pick a server to query */
- server = afs_volume_pick_fileserver(vnode);
- if (IS_ERR(server))
- goto no_server;
-
- _debug("USING SERVER: %pIS\n", &server->addr.transport);
+ ret = afs_set_fs_cursor(&fc, vnode);
+ if (ret < 0)
+ return ret;
- ret = afs_fs_extend_lock(server, key, vnode, false);
+ fc.ac.error = afs_fs_extend_lock(&fc, key, vnode, false);
- } while (!afs_volume_release_fileserver(vnode, server, ret));
-
- /* adjust the flags */
- if (ret == 0)
- afs_put_server(afs_v2net(vnode), server);
-
- _leave(" = %d", ret);
- return ret;
-
-no_server:
- return PTR_ERR(server);
+ return afs_end_fs_cursor(&fc, afs_v2net(vnode));
}
/*
@@ -814,7 +730,7 @@ no_server:
*/
int afs_vnode_release_lock(struct afs_vnode *vnode, struct key *key)
{
- struct afs_server *server;
+ struct afs_fs_cursor fc;
int ret;
_enter("%s{%x:%u.%u},%x",
@@ -824,25 +740,11 @@ int afs_vnode_release_lock(struct afs_vnode *vnode, struct key *key)
vnode->fid.unique,
key_serial(key));
- do {
- /* pick a server to query */
- server = afs_volume_pick_fileserver(vnode);
- if (IS_ERR(server))
- goto no_server;
-
- _debug("USING SERVER: %pIS\n", &server->addr.transport);
+ ret = afs_set_fs_cursor(&fc, vnode);
+ if (ret < 0)
+ return ret;
- ret = afs_fs_release_lock(server, key, vnode, false);
+ fc.ac.error = afs_fs_release_lock(&fc, key, vnode, false);
- } while (!afs_volume_release_fileserver(vnode, server, ret));
-
- /* adjust the flags */
- if (ret == 0)
- afs_put_server(afs_v2net(vnode), server);
-
- _leave(" = %d", ret);
- return ret;
-
-no_server:
- return PTR_ERR(server);
+ return afs_end_fs_cursor(&fc, afs_v2net(vnode));
}
diff --git a/fs/afs/volume.c b/fs/afs/volume.c
index 4f6fd10094c6..d282cd0ff268 100644
--- a/fs/afs/volume.c
+++ b/fs/afs/volume.c
@@ -210,10 +210,44 @@ void afs_put_volume(struct afs_cell *cell, struct afs_volume *volume)
}
/*
+ * Initialise a filesystem server cursor for iterating over FS servers.
+ */
+void afs_init_fs_cursor(struct afs_fs_cursor *fc, struct afs_vnode *vnode)
+{
+ fc->ac.alist = NULL;
+ fc->ac.addr = NULL;
+ fc->ac.start = 0;
+ fc->ac.index = 0;
+ fc->ac.error = 0;
+ fc->server = NULL;
+}
+
+/*
+ * Set a filesystem server cursor for using a specific FS server.
+ */
+int afs_set_fs_cursor(struct afs_fs_cursor *fc, struct afs_vnode *vnode)
+{
+ afs_init_fs_cursor(fc, vnode);
+
+ read_seqlock_excl(&vnode->cb_lock);
+ if (vnode->cb_interest) {
+ if (vnode->cb_interest->server->fs_state == 0)
+ fc->server = afs_get_server(vnode->cb_interest->server);
+ else
+ fc->ac.error = vnode->cb_interest->server->fs_state;
+ } else {
+ fc->ac.error = -ESTALE;
+ }
+ read_sequnlock_excl(&vnode->cb_lock);
+
+ return fc->ac.error;
+}
+
+/*
* pick a server to use to try accessing this volume
* - returns with an elevated usage count on the server chosen
*/
-struct afs_server *afs_volume_pick_fileserver(struct afs_vnode *vnode)
+bool afs_volume_pick_fileserver(struct afs_fs_cursor *fc, struct afs_vnode *vnode)
{
struct afs_volume *volume = vnode->volume;
struct afs_server *server;
@@ -223,19 +257,18 @@ struct afs_server *afs_volume_pick_fileserver(struct afs_vnode *vnode)
/* stick with the server we're already using if we can */
if (vnode->cb_interest && vnode->cb_interest->server->fs_state == 0) {
- afs_get_server(vnode->cb_interest->server);
- _leave(" = %p [current]", vnode->cb_interest->server);
- return vnode->cb_interest->server;
+ fc->server = afs_get_server(vnode->cb_interest->server);
+ goto set_server;
}
down_read(&volume->server_sem);
/* handle the no-server case */
if (volume->nservers == 0) {
- ret = volume->rjservers ? -ENOMEDIUM : -ESTALE;
+ fc->ac.error = volume->rjservers ? -ENOMEDIUM : -ESTALE;
up_read(&volume->server_sem);
- _leave(" = %d [no servers]", ret);
- return ERR_PTR(ret);
+ _leave(" = f [no servers %d]", fc->ac.error);
+ return false;
}
/* basically, just search the list for the first live server and use
@@ -280,13 +313,15 @@ struct afs_server *afs_volume_pick_fileserver(struct afs_vnode *vnode)
}
}
+error:
+ fc->ac.error = ret;
+
/* no available servers
* - TODO: handle the no active servers case better
*/
-error:
up_read(&volume->server_sem);
- _leave(" = %d", ret);
- return ERR_PTR(ret);
+ _leave(" = f [%d]", fc->ac.error);
+ return false;
picked_server:
/* Found an apparently healthy server. We need to register an interest
@@ -296,37 +331,41 @@ picked_server:
&volume->cb_interests[loop], server);
if (ret < 0)
goto error;
-
- afs_get_server(server);
+
+ fc->server = afs_get_server(server);
up_read(&volume->server_sem);
- _leave(" = %p (picked %pIS)",
- server, &server->addr.transport);
- return server;
+set_server:
+ fc->ac.alist = afs_get_addrlist(fc->server->addrs);
+ fc->ac.addr = &fc->ac.alist->addrs[0];
+ _debug("USING SERVER: %pIS\n", &fc->ac.addr->transport);
+ _leave(" = t (picked %pIS)", &fc->ac.addr->transport);
+ return true;
}
/*
* release a server after use
* - releases the ref on the server struct that was acquired by picking
* - records result of using a particular server to access a volume
- * - return 0 to try again, 1 if okay or to issue error
- * - the caller must release the server struct if result was 0
+ * - return true to try again, false if okay or to issue error
+ * - the caller must release the server struct if result was false
*/
-int afs_volume_release_fileserver(struct afs_vnode *vnode,
- struct afs_server *server,
- int result)
+bool afs_iterate_fs_cursor(struct afs_fs_cursor *fc,
+ struct afs_vnode *vnode)
{
struct afs_volume *volume = vnode->volume;
+ struct afs_server *server = fc->server;
unsigned loop;
_enter("%s,%pIS,%d",
- volume->vlocation->vldb.name, &server->addr.transport, result);
+ volume->vlocation->vldb.name, &fc->ac.addr->transport,
+ fc->ac.error);
- switch (result) {
+ switch (fc->ac.error) {
/* success */
case 0:
server->fs_state = 0;
- _leave("");
- return 1;
+ _leave(" = f");
+ return false;
/* the fileserver denied all knowledge of the volume */
case -ENOMEDIUM:
@@ -363,8 +402,9 @@ int afs_volume_release_fileserver(struct afs_vnode *vnode,
*/
up_write(&volume->server_sem);
afs_put_server(afs_v2net(vnode), server);
- _leave(" [completely rejected]");
- return 1;
+ fc->server = NULL;
+ _leave(" = f [completely rejected]");
+ return false;
/* problem reaching the server */
case -ENETUNREACH:
@@ -378,8 +418,8 @@ int afs_volume_release_fileserver(struct afs_vnode *vnode,
*/
spin_lock(&server->fs_lock);
if (!server->fs_state) {
- server->fs_state = result;
- printk("kAFS: SERVER DEAD state=%d\n", result);
+ server->fs_state = fc->ac.error;
+ printk("kAFS: SERVER DEAD state=%d\n", fc->ac.error);
}
spin_unlock(&server->fs_lock);
goto try_next_server;
@@ -390,8 +430,9 @@ int afs_volume_release_fileserver(struct afs_vnode *vnode,
case -ENONET:
/* tell the caller to accept the result */
afs_put_server(afs_v2net(vnode), server);
- _leave(" [local failure]");
- return 1;
+ fc->server = NULL;
+ _leave(" = f [local failure]");
+ return false;
}
/* tell the caller to loop around and try the next server */
@@ -399,6 +440,16 @@ try_next_server_upw:
up_write(&volume->server_sem);
try_next_server:
afs_put_server(afs_v2net(vnode), server);
- _leave(" [try next server]");
- return 0;
+ _leave(" = t [try next server]");
+ return true;
+}
+
+/*
+ * Clean up a fileserver cursor.
+ */
+int afs_end_fs_cursor(struct afs_fs_cursor *fc, struct afs_net *net)
+{
+ afs_end_cursor(&fc->ac);
+ afs_put_server(net, fc->server);
+ return fc->ac.error;
}