diff options
-rw-r--r-- | fs/afs/Makefile | 1 | ||||
-rw-r--r-- | fs/afs/addr_list.c | 308 | ||||
-rw-r--r-- | fs/afs/cell.c | 169 | ||||
-rw-r--r-- | fs/afs/fsclient.c | 121 | ||||
-rw-r--r-- | fs/afs/internal.h | 120 | ||||
-rw-r--r-- | fs/afs/proc.c | 23 | ||||
-rw-r--r-- | fs/afs/rxrpc.c | 3 | ||||
-rw-r--r-- | fs/afs/server.c | 70 | ||||
-rw-r--r-- | fs/afs/vlclient.c | 8 | ||||
-rw-r--r-- | fs/afs/vlocation.c | 75 | ||||
-rw-r--r-- | fs/afs/vnode.c | 392 | ||||
-rw-r--r-- | fs/afs/volume.c | 115 |
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 *); |