summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 64114820..8493839 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 0000000..ecb9c72
--- /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 e83103e..a0e08d3 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 680c02d..6614d0a 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 51e3825..df52bf1 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 0856542..9cf9ce8 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 ac1e25f..5ddfb7c 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 4e66608..9ca174b 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 aa79fe3..1d1e7df 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 ec5ab8d..52c31ad 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 622e110..9c7333e 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 4f6fd10..d282cd0 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;
}
OpenPOWER on IntegriCloud