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 641148208e90..849383986d3b 100644
--- a/fs/afs/Makefile
+++ b/fs/afs/Makefile
@@ -7,6 +7,7 @@ afs-cache-$(CONFIG_AFS_FSCACHE) := cache.o
kafs-objs := \
$(afs-cache-y) \
+ addr_list.o \
callback.o \
cell.o \
cmservice.o \
diff --git a/fs/afs/addr_list.c b/fs/afs/addr_list.c
new file mode 100644
index 000000000000..ecb9c72aebd2
--- /dev/null
+++ b/fs/afs/addr_list.c
@@ -0,0 +1,308 @@
+/* Server address list management
+ *
+ * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/dns_resolver.h>
+#include <linux/inet.h>
+#include <keys/rxrpc-type.h>
+#include "internal.h"
+#include "afs_fs.h"
+
+#define AFS_MAX_ADDRESSES \
+ ((unsigned int)((PAGE_SIZE - sizeof(struct afs_addr_list)) / \
+ sizeof(struct sockaddr_rxrpc)))
+
+/*
+ * Release an address list.
+ */
+void afs_put_addrlist(struct afs_addr_list *alist)
+{
+ if (alist && refcount_dec_and_test(&alist->usage))
+ call_rcu(&alist->rcu, (rcu_callback_t)kfree);
+}
+
+/*
+ * Allocate an address list.
+ */
+struct afs_addr_list *afs_alloc_addrlist(unsigned int nr,
+ unsigned short service,
+ unsigned short port)
+{
+ struct afs_addr_list *alist;
+ unsigned int i;
+
+ _enter("%u,%u,%u", nr, service, port);
+
+ alist = kzalloc(sizeof(*alist) + sizeof(alist->addrs[0]) * nr,
+ GFP_KERNEL);
+ if (!alist)
+ return NULL;
+
+ refcount_set(&alist->usage, 1);
+
+ for (i = 0; i < nr; i++) {
+ struct sockaddr_rxrpc *srx = &alist->addrs[i];
+ srx->srx_family = AF_RXRPC;
+ srx->srx_service = service;
+ srx->transport_type = SOCK_DGRAM;
+ srx->transport_len = sizeof(srx->transport.sin6);
+ srx->transport.sin6.sin6_family = AF_INET6;
+ srx->transport.sin6.sin6_port = htons(port);
+ }
+
+ return alist;
+}
+
+/*
+ * Parse a text string consisting of delimited addresses.
+ */
+struct afs_addr_list *afs_parse_text_addrs(const char *text, size_t len,
+ char delim,
+ unsigned short service,
+ unsigned short port)
+{
+ struct afs_addr_list *alist;
+ const char *p, *end = text + len;
+ unsigned int nr = 0;
+
+ _enter("%*.*s,%c", (int)len, (int)len, text, delim);
+
+ if (!len)
+ return ERR_PTR(-EDESTADDRREQ);
+
+ if (delim == ':' && (memchr(text, ',', len) || !memchr(text, '.', len)))
+ delim = ',';
+
+ /* Count the addresses */
+ p = text;
+ do {
+ if (!*p)
+ return ERR_PTR(-EINVAL);
+ if (*p == delim)
+ continue;
+ nr++;
+ if (*p == '[') {
+ p++;
+ if (p == end)
+ return ERR_PTR(-EINVAL);
+ p = memchr(p, ']', end - p);
+ if (!p)
+ return ERR_PTR(-EINVAL);
+ p++;
+ if (p >= end)
+ break;
+ }
+
+ p = memchr(p, delim, end - p);
+ if (!p)
+ break;
+ p++;
+ } while (p < end);
+
+ _debug("%u/%u addresses", nr, AFS_MAX_ADDRESSES);
+ if (nr > AFS_MAX_ADDRESSES)
+ nr = AFS_MAX_ADDRESSES;
+
+ alist = afs_alloc_addrlist(nr, service, port);
+ if (!alist)
+ return ERR_PTR(-ENOMEM);
+
+ /* Extract the addresses */
+ p = text;
+ do {
+ struct sockaddr_rxrpc *srx = &alist->addrs[alist->nr_addrs];
+ char tdelim = delim;
+
+ if (*p == delim) {
+ p++;
+ continue;
+ }
+
+ if (*p == '[') {
+ p++;
+ tdelim = ']';
+ }
+
+ if (in4_pton(p, end - p,
+ (u8 *)&srx->transport.sin6.sin6_addr.s6_addr32[3],
+ tdelim, &p)) {
+ srx->transport.sin6.sin6_addr.s6_addr32[0] = 0;
+ srx->transport.sin6.sin6_addr.s6_addr32[1] = 0;
+ srx->transport.sin6.sin6_addr.s6_addr32[2] = htonl(0xffff);
+ } else if (in6_pton(p, end - p,
+ srx->transport.sin6.sin6_addr.s6_addr,
+ tdelim, &p)) {
+ /* Nothing to do */
+ } else {
+ goto bad_address;
+ }
+
+ if (tdelim == ']') {
+ if (p == end || *p != ']')
+ goto bad_address;
+ p++;
+ }
+
+ if (p < end) {
+ if (*p == '+') {
+ /* Port number specification "+1234" */
+ unsigned int xport = 0;
+ p++;
+ if (p >= end || !isdigit(*p))
+ goto bad_address;
+ do {
+ xport *= 10;
+ xport += *p - '0';
+ if (xport > 65535)
+ goto bad_address;
+ p++;
+ } while (p < end && isdigit(*p));
+ srx->transport.sin6.sin6_port = htons(xport);
+ } else if (*p == delim) {
+ p++;
+ } else {
+ goto bad_address;
+ }
+ }
+
+ alist->nr_addrs++;
+ } while (p < end && alist->nr_addrs < AFS_MAX_ADDRESSES);
+
+ _leave(" = [nr %u]", alist->nr_addrs);
+ return alist;
+
+bad_address:
+ kfree(alist);
+ return ERR_PTR(-EINVAL);
+}
+
+/*
+ * Compare old and new address lists to see if there's been any change.
+ * - How to do this in better than O(Nlog(N)) time?
+ * - We don't really want to sort the address list, but would rather take the
+ * list as we got it so as not to undo record rotation by the DNS server.
+ */
+#if 0
+static int afs_cmp_addr_list(const struct afs_addr_list *a1,
+ const struct afs_addr_list *a2)
+{
+}
+#endif
+
+/*
+ * Perform a DNS query for VL servers and build a up an address list.
+ */
+struct afs_addr_list *afs_dns_query(struct afs_cell *cell, time64_t *_expiry)
+{
+ struct afs_addr_list *alist;
+ char *vllist = NULL;
+ int ret;
+
+ _enter("%s", cell->name);
+
+ ret = dns_query("afsdb", cell->name, cell->name_len,
+ "ipv4", &vllist, _expiry);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ alist = afs_parse_text_addrs(vllist, strlen(vllist), ',',
+ VL_SERVICE, AFS_VL_PORT);
+ if (IS_ERR(alist)) {
+ kfree(vllist);
+ if (alist != ERR_PTR(-ENOMEM))
+ pr_err("Failed to parse DNS data\n");
+ return alist;
+ }
+
+ kfree(vllist);
+ return alist;
+}
+
+/*
+ * Get an address to try.
+ */
+bool afs_iterate_addresses(struct afs_addr_cursor *ac)
+{
+ _enter("%hu+%hd", ac->start, (short)ac->index);
+
+ if (!ac->alist)
+ return false;
+
+ if (ac->begun) {
+ ac->index++;
+ if (ac->index == ac->alist->nr_addrs)
+ ac->index = 0;
+
+ if (ac->index == ac->start) {
+ ac->error = -EDESTADDRREQ;
+ return false;
+ }
+ }
+
+ ac->begun = true;
+ ac->responded = false;
+ ac->addr = &ac->alist->addrs[ac->index];
+ return true;
+}
+
+/*
+ * Release an address list cursor.
+ */
+int afs_end_cursor(struct afs_addr_cursor *ac)
+{
+ if (ac->responded && ac->index != ac->start)
+ WRITE_ONCE(ac->alist->index, ac->index);
+
+ afs_put_addrlist(ac->alist);
+ ac->alist = NULL;
+ return ac->error;
+}
+
+/*
+ * Set the address cursor for iterating over VL servers.
+ */
+int afs_set_vl_cursor(struct afs_addr_cursor *ac, struct afs_cell *cell)
+{
+ struct afs_addr_list *alist;
+ int ret;
+
+ if (!rcu_access_pointer(cell->vl_addrs)) {
+ ret = wait_on_bit(&cell->flags, AFS_CELL_FL_NO_LOOKUP_YET,
+ TASK_INTERRUPTIBLE);
+ if (ret < 0)
+ return ret;
+
+ if (!rcu_access_pointer(cell->vl_addrs) &&
+ ktime_get_real_seconds() < cell->dns_expiry)
+ return cell->error;
+ }
+
+ read_lock(&cell->vl_addrs_lock);
+ alist = rcu_dereference_protected(cell->vl_addrs,
+ lockdep_is_held(&cell->vl_addrs_lock));
+ if (alist->nr_addrs > 0)
+ afs_get_addrlist(alist);
+ else
+ alist = NULL;
+ read_unlock(&cell->vl_addrs_lock);
+
+ if (!alist)
+ return -EDESTADDRREQ;
+
+ ac->alist = alist;
+ ac->addr = NULL;
+ ac->start = READ_ONCE(alist->index);
+ ac->index = ac->start;
+ ac->error = 0;
+ ac->begun = false;
+ return 0;
+}
diff --git a/fs/afs/cell.c b/fs/afs/cell.c
index e83103e8a6fb..a0e08d3a108c 100644
--- a/fs/afs/cell.c
+++ b/fs/afs/cell.c
@@ -9,7 +9,6 @@
* 2 of the License, or (at your option) any later version.
*/
-#include <linux/module.h>
#include <linux/slab.h>
#include <linux/key.h>
#include <linux/ctype.h>
@@ -152,68 +151,33 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net,
init_rwsem(&cell->vl_sem);
INIT_LIST_HEAD(&cell->vl_list);
spin_lock_init(&cell->vl_lock);
- seqlock_init(&cell->vl_addrs_lock);
- cell->flags = (1 << AFS_CELL_FL_NOT_READY);
-
- for (i = 0; i < AFS_CELL_MAX_ADDRS; i++) {
- struct sockaddr_rxrpc *srx = &cell->vl_addrs[i];
- srx->srx_family = AF_RXRPC;
- srx->srx_service = VL_SERVICE;
- srx->transport_type = SOCK_DGRAM;
- srx->transport.sin6.sin6_family = AF_INET6;
- srx->transport.sin6.sin6_port = htons(AFS_VL_PORT);
- }
+ cell->flags = ((1 << AFS_CELL_FL_NOT_READY) |
+ (1 << AFS_CELL_FL_NO_LOOKUP_YET));
+ rwlock_init(&cell->vl_addrs_lock);
/* Fill in the VL server list if we were given a list of addresses to
* use.
*/
if (vllist) {
- char delim = ':';
-
- if (strchr(vllist, ',') || !strchr(vllist, '.'))
- delim = ',';
-
- do {
- struct sockaddr_rxrpc *srx = &cell->vl_addrs[cell->vl_naddrs];
-
- if (in4_pton(vllist, -1,
- (u8 *)&srx->transport.sin6.sin6_addr.s6_addr32[3],
- delim, &vllist)) {
- srx->transport_len = sizeof(struct sockaddr_in6);
- srx->transport.sin6.sin6_addr.s6_addr32[0] = 0;
- srx->transport.sin6.sin6_addr.s6_addr32[1] = 0;
- srx->transport.sin6.sin6_addr.s6_addr32[2] = htonl(0xffff);
- } else if (in6_pton(vllist, -1,
- srx->transport.sin6.sin6_addr.s6_addr,
- delim, &vllist)) {
- srx->transport_len = sizeof(struct sockaddr_in6);
- srx->transport.sin6.sin6_family = AF_INET6;
- } else {
- goto bad_address;
- }
+ struct afs_addr_list *alist;
- cell->vl_naddrs++;
- if (!*vllist)
- break;
- vllist++;
-
- } while (cell->vl_naddrs < AFS_CELL_MAX_ADDRS && vllist);
+ alist = afs_parse_text_addrs(vllist, strlen(vllist), ':',
+ VL_SERVICE, AFS_VL_PORT);
+ if (IS_ERR(alist)) {
+ ret = PTR_ERR(alist);
+ goto parse_failed;
+ }
- /* Disable DNS refresh for manually-specified cells */
+ rcu_assign_pointer(cell->vl_addrs, alist);
cell->dns_expiry = TIME64_MAX;
- } else {
- /* We're going to need to 'refresh' this cell's VL server list
- * from the DNS before we can use it.
- */
- cell->dns_expiry = S64_MIN;
}
_leave(" = %p", cell);
return cell;
-bad_address:
- printk(KERN_ERR "kAFS: bad VL server IP address\n");
- ret = -EINVAL;
+parse_failed:
+ if (ret == -EINVAL)
+ printk(KERN_ERR "kAFS: bad VL server IP address\n");
kfree(cell);
_leave(" = %d", ret);
return ERR_PTR(ret);
@@ -325,7 +289,6 @@ cell_already_exists:
if (excl) {
ret = -EEXIST;
} else {
- ASSERTCMP(atomic_read(&cursor->usage), >=, 1);
afs_get_cell(cursor);
ret = 0;
}
@@ -333,8 +296,10 @@ cell_already_exists:
kfree(candidate);
if (ret == 0)
goto wait_for_cell;
+ goto error_noput;
error:
afs_put_cell(net, cell);
+error_noput:
_leave(" = %d [error]", ret);
return ERR_PTR(ret);
}
@@ -396,78 +361,50 @@ int afs_cell_init(struct afs_net *net, const char *rootcell)
*/
static void afs_update_cell(struct afs_cell *cell)
{
+ struct afs_addr_list *alist, *old;
time64_t now, expiry;
- char *vllist = NULL;
- int ret;
_enter("%s", cell->name);
- ret = dns_query("afsdb", cell->name, cell->name_len,
- "ipv4", &vllist, &expiry);
- _debug("query %d", ret);
- switch (ret) {
- case 0 ... INT_MAX:
- clear_bit(AFS_CELL_FL_DNS_FAIL, &cell->flags);
- clear_bit(AFS_CELL_FL_NOT_FOUND, &cell->flags);
- goto parse_dns_data;
+ alist = afs_dns_query(cell, &expiry);
+ if (IS_ERR(alist)) {
+ switch (PTR_ERR(alist)) {
+ case -ENODATA:
+ /* The DNS said that the cell does not exist */
+ set_bit(AFS_CELL_FL_NOT_FOUND, &cell->flags);
+ clear_bit(AFS_CELL_FL_DNS_FAIL, &cell->flags);
+ cell->dns_expiry = ktime_get_real_seconds() + 61;
+ break;
- case -ENODATA:
- clear_bit(AFS_CELL_FL_DNS_FAIL, &cell->flags);
- set_bit(AFS_CELL_FL_NOT_FOUND, &cell->flags);
- cell->dns_expiry = ktime_get_real_seconds() + 61;
- cell->error = -EDESTADDRREQ;
- goto out;
+ case -EAGAIN:
+ case -ECONNREFUSED:
+ default:
+ set_bit(AFS_CELL_FL_DNS_FAIL, &cell->flags);
+ cell->dns_expiry = ktime_get_real_seconds() + 10;
+ break;
+ }
- case -EAGAIN:
- case -ECONNREFUSED:
- default:
- /* Unable to query DNS. */
- set_bit(AFS_CELL_FL_DNS_FAIL, &cell->flags);
- cell->dns_expiry = ktime_get_real_seconds() + 10;
cell->error = -EDESTADDRREQ;
- goto out;
- }
-
-parse_dns_data:
- write_seqlock(&cell->vl_addrs_lock);
-
- ret = -EINVAL;
- do {
- struct sockaddr_rxrpc *srx = &cell->vl_addrs[cell->vl_naddrs];
-
- if (in4_pton(vllist, -1,
- (u8 *)&srx->transport.sin6.sin6_addr.s6_addr32[3],
- ',', (const char **)&vllist)) {
- srx->transport_len = sizeof(struct sockaddr_in6);
- srx->transport.sin6.sin6_addr.s6_addr32[0] = 0;
- srx->transport.sin6.sin6_addr.s6_addr32[1] = 0;
- srx->transport.sin6.sin6_addr.s6_addr32[2] = htonl(0xffff);
- } else if (in6_pton(vllist, -1,
- srx->transport.sin6.sin6_addr.s6_addr,
- ',', (const char **)&vllist)) {
- srx->transport_len = sizeof(struct sockaddr_in6);
- srx->transport.sin6.sin6_family = AF_INET6;
- } else {
- goto bad_address;
- }
+ } else {
+ clear_bit(AFS_CELL_FL_DNS_FAIL, &cell->flags);
+ clear_bit(AFS_CELL_FL_NOT_FOUND, &cell->flags);
- cell->vl_naddrs++;
- if (!*vllist)
- break;
- vllist++;
+ /* Exclusion on changing vl_addrs is achieved by a
+ * non-reentrant work item.
+ */
+ old = rcu_dereference_protected(cell->vl_addrs, true);
+ rcu_assign_pointer(cell->vl_addrs, alist);
+ cell->dns_expiry = expiry;
- } while (cell->vl_naddrs < AFS_CELL_MAX_ADDRS);
+ if (old)
+ afs_put_addrlist(old);
+ }
- if (cell->vl_naddrs < AFS_CELL_MAX_ADDRS)
- memset(cell->vl_addrs + cell->vl_naddrs, 0,
- (AFS_CELL_MAX_ADDRS - cell->vl_naddrs) * sizeof(cell->vl_addrs[0]));
+ if (test_and_clear_bit(AFS_CELL_FL_NO_LOOKUP_YET, &cell->flags))
+ wake_up_bit(&cell->flags, AFS_CELL_FL_NO_LOOKUP_YET);
now = ktime_get_real_seconds();
- cell->dns_expiry = expiry;
- afs_set_cell_timer(cell->net, expiry - now);
-bad_address:
- write_sequnlock(&cell->vl_addrs_lock);
-out:
+ afs_set_cell_timer(cell->net, cell->dns_expiry - now);
_leave("");
}
@@ -482,6 +419,7 @@ static void afs_cell_destroy(struct rcu_head *rcu)
ASSERTCMP(atomic_read(&cell->usage), ==, 0);
+ afs_put_addrlist(cell->vl_addrs);
key_put(cell->anonymous_key);
kfree(cell);
@@ -515,6 +453,15 @@ void afs_cells_timer(struct timer_list *timer)
}
/*
+ * Get a reference on a cell record.
+ */
+struct afs_cell *afs_get_cell(struct afs_cell *cell)
+{
+ atomic_inc(&cell->usage);
+ return cell;
+}
+
+/*
* Drop a reference on a cell record.
*/
void afs_put_cell(struct afs_net *net, struct afs_cell *cell)
diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c
index 680c02d510f7..6614d0a78daa 100644
--- a/fs/afs/fsclient.c
+++ b/fs/afs/fsclient.c
@@ -297,7 +297,7 @@ static const struct afs_call_type afs_RXFSFetchStatus = {
/*
* fetch the status information for a file
*/
-int afs_fs_fetch_file_status(struct afs_server *server,
+int afs_fs_fetch_file_status(struct afs_fs_cursor *fc,
struct key *key,
struct afs_vnode *vnode,
struct afs_volsync *volsync,
@@ -325,9 +325,9 @@ int afs_fs_fetch_file_status(struct afs_server *server,
bp[2] = htonl(vnode->fid.vnode);
bp[3] = htonl(vnode->fid.unique);
- call->cb_break = vnode->cb_break + server->cb_s_break;
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ call->cb_break = vnode->cb_break + fc->server->cb_s_break;
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
@@ -502,7 +502,7 @@ static const struct afs_call_type afs_RXFSFetchData64 = {
/*
* fetch data from a very large file
*/
-static int afs_fs_fetch_data64(struct afs_server *server,
+static int afs_fs_fetch_data64(struct afs_fs_cursor *fc,
struct key *key,
struct afs_vnode *vnode,
struct afs_read *req,
@@ -536,15 +536,15 @@ static int afs_fs_fetch_data64(struct afs_server *server,
bp[7] = htonl(lower_32_bits(req->len));
atomic_inc(&req->usage);
- call->cb_break = vnode->cb_break + server->cb_s_break;
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ call->cb_break = vnode->cb_break + fc->server->cb_s_break;
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
* fetch data from a file
*/
-int afs_fs_fetch_data(struct afs_server *server,
+int afs_fs_fetch_data(struct afs_fs_cursor *fc,
struct key *key,
struct afs_vnode *vnode,
struct afs_read *req,
@@ -557,7 +557,7 @@ int afs_fs_fetch_data(struct afs_server *server,
if (upper_32_bits(req->pos) ||
upper_32_bits(req->len) ||
upper_32_bits(req->pos + req->len))
- return afs_fs_fetch_data64(server, key, vnode, req, async);
+ return afs_fs_fetch_data64(fc, key, vnode, req, async);
_enter("");
@@ -581,9 +581,9 @@ int afs_fs_fetch_data(struct afs_server *server,
bp[5] = htonl(lower_32_bits(req->len));
atomic_inc(&req->usage);
- call->cb_break = vnode->cb_break + server->cb_s_break;
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ call->cb_break = vnode->cb_break + fc->server->cb_s_break;
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
@@ -625,7 +625,7 @@ static const struct afs_call_type afs_RXFSCreateXXXX = {
/*
* create a file or make a directory
*/
-int afs_fs_create(struct afs_server *server,
+int afs_fs_create(struct afs_fs_cursor *fc,
struct key *key,
struct afs_vnode *vnode,
const char *name,
@@ -677,8 +677,8 @@ int afs_fs_create(struct afs_server *server,
*bp++ = htonl(mode & S_IALLUGO); /* unix mode */
*bp++ = 0; /* segment size */
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
@@ -717,7 +717,7 @@ static const struct afs_call_type afs_RXFSRemoveXXXX = {
/*
* remove a file or directory
*/
-int afs_fs_remove(struct afs_server *server,
+int afs_fs_remove(struct afs_fs_cursor *fc,
struct key *key,
struct afs_vnode *vnode,
const char *name,
@@ -756,8 +756,8 @@ int afs_fs_remove(struct afs_server *server,
bp = (void *) bp + padsz;
}
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
@@ -797,7 +797,7 @@ static const struct afs_call_type afs_RXFSLink = {
/*
* make a hard link
*/
-int afs_fs_link(struct afs_server *server,
+int afs_fs_link(struct afs_fs_cursor *fc,
struct key *key,
struct afs_vnode *dvnode,
struct afs_vnode *vnode,
@@ -840,8 +840,8 @@ int afs_fs_link(struct afs_server *server,
*bp++ = htonl(vnode->fid.vnode);
*bp++ = htonl(vnode->fid.unique);
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
@@ -882,7 +882,7 @@ static const struct afs_call_type afs_RXFSSymlink = {
/*
* create a symbolic link
*/
-int afs_fs_symlink(struct afs_server *server,
+int afs_fs_symlink(struct afs_fs_cursor *fc,
struct key *key,
struct afs_vnode *vnode,
const char *name,
@@ -943,8 +943,8 @@ int afs_fs_symlink(struct afs_server *server,
*bp++ = htonl(S_IRWXUGO); /* unix mode */
*bp++ = 0; /* segment size */
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
@@ -986,7 +986,7 @@ static const struct afs_call_type afs_RXFSRename = {
/*
* create a symbolic link
*/
-int afs_fs_rename(struct afs_server *server,
+int afs_fs_rename(struct afs_fs_cursor *fc,
struct key *key,
struct afs_vnode *orig_dvnode,
const char *orig_name,
@@ -1045,8 +1045,8 @@ int afs_fs_rename(struct afs_server *server,
bp = (void *) bp + n_padsz;
}
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
@@ -1094,7 +1094,7 @@ static const struct afs_call_type afs_RXFSStoreData64 = {
/*
* store a set of pages to a very large file
*/
-static int afs_fs_store_data64(struct afs_server *server,
+static int afs_fs_store_data64(struct afs_fs_cursor *fc,
struct afs_writeback *wb,
pgoff_t first, pgoff_t last,
unsigned offset, unsigned to,
@@ -1147,14 +1147,14 @@ static int afs_fs_store_data64(struct afs_server *server,
*bp++ = htonl(i_size >> 32);
*bp++ = htonl((u32) i_size);
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
* store a set of pages
*/
-int afs_fs_store_data(struct afs_server *server, struct afs_writeback *wb,
+int afs_fs_store_data(struct afs_fs_cursor *fc, struct afs_writeback *wb,
pgoff_t first, pgoff_t last,
unsigned offset, unsigned to,
bool async)
@@ -1183,7 +1183,7 @@ int afs_fs_store_data(struct afs_server *server, struct afs_writeback *wb,
(unsigned long long) i_size);
if (pos >> 32 || i_size >> 32 || size >> 32 || (pos + size) >> 32)
- return afs_fs_store_data64(server, wb, first, last, offset, to,
+ return afs_fs_store_data64(fc, wb, first, last, offset, to,
size, pos, i_size, async);
call = afs_alloc_flat_call(net, &afs_RXFSStoreData,
@@ -1221,8 +1221,8 @@ int afs_fs_store_data(struct afs_server *server, struct afs_writeback *wb,
*bp++ = htonl(size);
*bp++ = htonl(i_size);
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
@@ -1279,7 +1279,7 @@ static const struct afs_call_type afs_RXFSStoreData64_as_Status = {
* set the attributes on a very large file, using FS.StoreData rather than
* FS.StoreStatus so as to alter the file size also
*/
-static int afs_fs_setattr_size64(struct afs_server *server, struct key *key,
+static int afs_fs_setattr_size64(struct afs_fs_cursor *fc, struct key *key,
struct afs_vnode *vnode, struct iattr *attr,
bool async)
{
@@ -1319,15 +1319,15 @@ static int afs_fs_setattr_size64(struct afs_server *server, struct key *key,
*bp++ = htonl(attr->ia_size >> 32); /* new file length */
*bp++ = htonl((u32) attr->ia_size);
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
* set the attributes on a file, using FS.StoreData rather than FS.StoreStatus
* so as to alter the file size also
*/
-static int afs_fs_setattr_size(struct afs_server *server, struct key *key,
+static int afs_fs_setattr_size(struct afs_fs_cursor *fc, struct key *key,
struct afs_vnode *vnode, struct iattr *attr,
bool async)
{
@@ -1340,8 +1340,7 @@ static int afs_fs_setattr_size(struct afs_server *server, struct key *key,
ASSERT(attr->ia_valid & ATTR_SIZE);
if (attr->ia_size >> 32)
- return afs_fs_setattr_size64(server, key, vnode, attr,
- async);
+ return afs_fs_setattr_size64(fc, key, vnode, attr, async);
call = afs_alloc_flat_call(net, &afs_RXFSStoreData_as_Status,
(4 + 6 + 3) * 4,
@@ -1367,15 +1366,15 @@ static int afs_fs_setattr_size(struct afs_server *server, struct key *key,
*bp++ = 0; /* size of write */
*bp++ = htonl(attr->ia_size); /* new file length */
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
* set the attributes on a file, using FS.StoreData if there's a change in file
* size, and FS.StoreStatus otherwise
*/
-int afs_fs_setattr(struct afs_server *server, struct key *key,
+int afs_fs_setattr(struct afs_fs_cursor *fc, struct key *key,
struct afs_vnode *vnode, struct iattr *attr,
bool async)
{
@@ -1384,8 +1383,7 @@ int afs_fs_setattr(struct afs_server *server, struct key *key,
__be32 *bp;
if (attr->ia_valid & ATTR_SIZE)
- return afs_fs_setattr_size(server, key, vnode, attr,
- async);
+ return afs_fs_setattr_size(fc, key, vnode, attr, async);
_enter(",%x,{%x:%u},,",
key_serial(key), vnode->fid.vid, vnode->fid.vnode);
@@ -1409,8 +1407,8 @@ int afs_fs_setattr(struct afs_server *server, struct key *key,
xdr_encode_AFS_StoreStatus(&bp, attr);
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
@@ -1607,7 +1605,7 @@ static const struct afs_call_type afs_RXFSGetVolumeStatus = {
/*
* fetch the status of a volume
*/
-int afs_fs_get_volume_status(struct afs_server *server,
+int afs_fs_get_volume_status(struct afs_fs_cursor *fc,
struct key *key,
struct afs_vnode *vnode,
struct afs_volume_status *vs,
@@ -1640,8 +1638,8 @@ int afs_fs_get_volume_status(struct afs_server *server,
bp[0] = htonl(FSGETVOLUMESTATUS);
bp[1] = htonl(vnode->fid.vid);
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
@@ -1696,7 +1694,7 @@ static const struct afs_call_type afs_RXFSReleaseLock = {
/*
* get a lock on a file
*/
-int afs_fs_set_lock(struct afs_server *server,
+int afs_fs_set_lock(struct afs_fs_cursor *fc,
struct key *key,
struct afs_vnode *vnode,
afs_lock_type_t type,
@@ -1723,14 +1721,14 @@ int afs_fs_set_lock(struct afs_server *server,
*bp++ = htonl(vnode->fid.unique);
*bp++ = htonl(type);
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
* extend a lock on a file
*/
-int afs_fs_extend_lock(struct afs_server *server,
+int afs_fs_extend_lock(struct afs_fs_cursor *fc,
struct key *key,
struct afs_vnode *vnode,
bool async)
@@ -1755,14 +1753,14 @@ int afs_fs_extend_lock(struct afs_server *server,
*bp++ = htonl(vnode->fid.vnode);
*bp++ = htonl(vnode->fid.unique);
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
* release a lock on a file
*/
-int afs_fs_release_lock(struct afs_server *server,
+int afs_fs_release_lock(struct afs_fs_cursor *fc,
struct key *key,
struct afs_vnode *vnode,
bool async)
@@ -1787,8 +1785,8 @@ int afs_fs_release_lock(struct afs_server *server,
*bp++ = htonl(vnode->fid.vnode);
*bp++ = htonl(vnode->fid.unique);
- afs_use_fs_server(call, server);
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ afs_use_fs_server(call, fc->server);
+ return afs_make_call(&fc->ac, call, GFP_NOFS, async);
}
/*
@@ -1812,6 +1810,7 @@ static const struct afs_call_type afs_RXFSGiveUpAllCallBacks = {
* Flush all the callbacks we have on a server.
*/
int afs_fs_give_up_all_callbacks(struct afs_server *server,
+ struct afs_addr_cursor *ac,
struct key *key,
bool async)
{
@@ -1831,5 +1830,5 @@ int afs_fs_give_up_all_callbacks(struct afs_server *server,
*bp++ = htonl(FSGIVEUPALLCALLBACKS);
/* Can't take a ref on server */
- return afs_make_call(&server->addr, call, GFP_NOFS, async);
+ return afs_make_call(ac, call, GFP_NOFS, async);
}
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 51e3825b5ffb..df52bf18a263 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -71,6 +71,17 @@ enum afs_call_state {
};
/*
+ * List of server addresses.
+ */
+struct afs_addr_list {
+ struct rcu_head rcu; /* Must be first */
+ refcount_t usage;
+ unsigned short nr_addrs;
+ unsigned short index; /* Address currently in use */
+ struct sockaddr_rxrpc addrs[];
+};
+
+/*
* a record of an in-progress RxRPC call
*/
struct afs_call {
@@ -283,16 +294,15 @@ struct afs_cell {
#define AFS_CELL_FL_NO_GC 1 /* The cell was added manually, don't auto-gc */
#define AFS_CELL_FL_NOT_FOUND 2 /* Permanent DNS error */
#define AFS_CELL_FL_DNS_FAIL 3 /* Failed to access DNS */
+#define AFS_CELL_FL_NO_LOOKUP_YET 4 /* Not completed first DNS lookup yet */
enum afs_cell_state state;
short error;
spinlock_t vl_lock; /* vl_list lock */
/* VLDB server list. */
- seqlock_t vl_addrs_lock;
- unsigned short vl_naddrs; /* number of VL servers in addr list */
- unsigned short vl_curr_svix; /* current server index */
- struct sockaddr_rxrpc vl_addrs[AFS_CELL_MAX_ADDRS]; /* cell VL server addresses */
+ rwlock_t vl_addrs_lock; /* Lock on vl_addrs */
+ struct afs_addr_list __rcu *vl_addrs; /* List of VL servers */
u8 name_len; /* Length of name */
char name[64 + 1]; /* Cell name, case-flattened and NUL-padded */
};
@@ -343,7 +353,7 @@ struct afs_vlocation {
struct afs_server {
atomic_t usage;
time64_t time_of_death; /* time at which put reduced usage to 0 */
- struct sockaddr_rxrpc addr; /* server address */
+ struct afs_addr_list __rcu *addrs; /* List of addresses for this server */
struct afs_net *net; /* Network namespace in which the server resides */
struct afs_cell *cell; /* cell in which server resides */
struct list_head link; /* link in cell's server list */
@@ -485,8 +495,49 @@ struct afs_interface {
unsigned mtu; /* MTU of interface */
};
+/*
+ * Cursor for iterating over a server's address list.
+ */
+struct afs_addr_cursor {
+ struct afs_addr_list *alist; /* Current address list (pins ref) */
+ struct sockaddr_rxrpc *addr;
+ unsigned short start; /* Starting point in alist->addrs[] */
+ unsigned short index; /* Wrapping offset from start to current addr */
+ short error;
+ bool begun; /* T if we've begun iteration */
+ bool responded; /* T if the current address responded */
+};
+
+/*
+ * Cursor for iterating over a set of fileservers.
+ */
+struct afs_fs_cursor {
+ struct afs_addr_cursor ac;
+ struct afs_server *server; /* Current server (pins ref) */
+};
+
/*****************************************************************************/
/*
+ * addr_list.c
+ */
+static inline struct afs_addr_list *afs_get_addrlist(struct afs_addr_list *alist)
+{
+ if (alist)
+ refcount_inc(&alist->usage);
+ return alist;
+}
+extern struct afs_addr_list *afs_alloc_addrlist(unsigned int,
+ unsigned short,
+ unsigned short);
+extern void afs_put_addrlist(struct afs_addr_list *);
+extern struct afs_addr_list *afs_parse_text_addrs(const char *, size_t, char,
+ unsigned short, unsigned short);
+extern struct afs_addr_list *afs_dns_query(struct afs_cell *, time64_t *);