kafs-objs := \
$(afs-cache-y) \
+ addr_list.o \
callback.o \
cell.o \
cmservice.o \
--- /dev/null
+/* 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;
+}
* 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>
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);
if (excl) {
ret = -EEXIST;
} else {
- ASSERTCMP(atomic_read(&cursor->usage), >=, 1);
afs_get_cell(cursor);
ret = 0;
}
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);
}
*/
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("");
}
ASSERTCMP(atomic_read(&cell->usage), ==, 0);
+ afs_put_addrlist(cell->vl_addrs);
key_put(cell->anonymous_key);
kfree(cell);
afs_dec_cells_outstanding(net);
}
+/*
+ * 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.
*/
/*
* 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,
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);
}
/*
/*
* 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,
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,
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("");
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);
}
/*
/*
* 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,
*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);
}
/*
/*
* 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,
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);
}
/*
/*
* 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,
*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);
}
/*
/*
* 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,
*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);
}
/*
/*
* 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,
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);
}
/*
/*
* 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,
*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)
(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,
*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);
}
/*
* 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)
{
*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)
{
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,
*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)
{
__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);
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);
}
/*
/*
* 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,
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);
}
/*
/*
* 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,
*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)
*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)
*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);
}
/*
* 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)
{
*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);
}
AFS_CALL_COMPLETE, /* Completed or failed */
};
+/*
+ * 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
*/
#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 */
};
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 */
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
*/
/*
* 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 *);
/*
* 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
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);
/*
* 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);
/*
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
*/
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;
}
/*
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;
}
/*
*/
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();
}
/*
}
/* 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);
/*
* 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];
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)
_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;
}
/*
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);
/* 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;
}
_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) {
*/
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);
}
* 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,
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,
*bp = htonl(voltype);
/* initiate the call */
- return afs_make_call(addr, call, GFP_KERNEL, async);
+ return afs_make_call(ac, call, GFP_KERNEL, async);
}
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:
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;
}
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:
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) {
}
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;
}
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);
*/
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);
/* 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;
}
/*
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,
/* 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;
}
/*
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,
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;
}
/*
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,
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;
}
/*
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,
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);
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;
}
/*
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,
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;
}
/*
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,
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);
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;
}
/*
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,
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;
}
/*
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,
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;
}
/*
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,
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));
}
/*
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,
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));
}
/*
*/
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",
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));
}
/*
*/
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",
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));
}
_leave(" [destroyed]");
}
+/*
+ * 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;
/* 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
}
}
+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
&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:
*/
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:
*/
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;
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 */
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;
}