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 +#include +#include +#include +#include +#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 #include #include #include @@ -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 = ':'; + struct afs_addr_list *alist; - if (strchr(vllist, ',') || !strchr(vllist, '.')) - delim = ','; + alist = afs_parse_text_addrs(vllist, strlen(vllist), ':', + VL_SERVICE, AFS_VL_PORT); + if (IS_ERR(alist)) { + ret = PTR_ERR(alist); + goto parse_failed; + } - 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; - } - - cell->vl_naddrs++; - if (!*vllist) - break; - vllist++; - - } while (cell->vl_naddrs < AFS_CELL_MAX_ADDRS && vllist); - - /* 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: - /* 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; + case -EAGAIN: + case -ECONNREFUSED: + default: + set_bit(AFS_CELL_FL_DNS_FAIL, &cell->flags); + cell->dns_expiry = ktime_get_real_seconds() + 10; + break; } - cell->vl_naddrs++; - if (!*vllist) - break; - vllist++; + cell->error = -EDESTADDRREQ; + } else { + clear_bit(AFS_CELL_FL_DNS_FAIL, &cell->flags); + clear_bit(AFS_CELL_FL_NOT_FOUND, &cell->flags); - } while (cell->vl_naddrs < AFS_CELL_MAX_ADDRS); + /* 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; - 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 (old) + afs_put_addrlist(old); + } + + 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); @@ -514,6 +452,15 @@ void afs_cells_timer(struct timer_list *timer) 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. */ 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 @@ -70,6 +70,17 @@ enum afs_call_state { 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 */ @@ -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,7 +495,48 @@ struct afs_interface { unsigned mtu; /* MTU of interface */ }; +/* + * Cursor for iterating over a server's address list. + */ +struct afs_addr_cursor { + struct afs_addr_list *alist; /* Current address list (pins ref) */ + struct sockaddr_rxrpc *addr; + unsigned short start; /* Starting point in alist->addrs[] */ + unsigned short index; /* Wrapping offset from start to current addr */ + short error; + bool begun; /* T if we've begun iteration */ + bool responded; /* T if the current address responded */ +}; + +/* + * Cursor for iterating over a set of fileservers. + */ +struct afs_fs_cursor { + struct afs_addr_cursor ac; + struct afs_server *server; /* Current server (pins ref) */ +}; + /*****************************************************************************/ +/* + * addr_list.c + */ +static inline struct afs_addr_list *afs_get_addrlist(struct afs_addr_list *alist) +{ + if (alist) + refcount_inc(&alist->usage); + return alist; +} +extern struct afs_addr_list *afs_alloc_addrlist(unsigned int, + unsigned short, + unsigned short); +extern void afs_put_addrlist(struct afs_addr_list *); +extern struct afs_addr_list *afs_parse_text_addrs(const char *, size_t, char, + unsigned short, unsigned short); +extern struct afs_addr_list *afs_dns_query(struct afs_cell *, time64_t *); +extern bool afs_iterate_addresses(struct afs_addr_cursor *); +extern int afs_end_cursor(struct afs_addr_cursor *); +extern int afs_set_vl_cursor(struct afs_addr_cursor *, struct afs_cell *); + /* * cache.c */ @@ -521,17 +572,11 @@ static inline struct afs_cb_interest *afs_get_cb_interest(struct afs_cb_interest /* * cell.c */ - static inline struct afs_cell *afs_get_cell(struct afs_cell *cell) -{ - if (cell) - atomic_inc(&cell->usage); - return cell; -} - extern int afs_cell_init(struct afs_net *, const char *); extern struct afs_cell *afs_lookup_cell_rcu(struct afs_net *, const char *, unsigned); extern struct afs_cell *afs_lookup_cell(struct afs_net *, const char *, unsigned, const char *, bool); +extern struct afs_cell *afs_get_cell(struct afs_cell *); extern void afs_put_cell(struct afs_net *, struct afs_cell *); extern void afs_manage_cells(struct work_struct *); extern void afs_cells_timer(struct timer_list *); @@ -574,40 +619,41 @@ extern int afs_flock(struct file *, int, struct file_lock *); /* * fsclient.c */ -extern int afs_fs_fetch_file_status(struct afs_server *, struct key *, +extern int afs_fs_fetch_file_status(struct afs_fs_cursor *, struct key *, struct afs_vnode *, struct afs_volsync *, bool); extern int afs_fs_give_up_callbacks(struct afs_net *, struct afs_server *, bool); -extern int afs_fs_fetch_data(struct afs_server *, struct key *, +extern int afs_fs_fetch_data(struct afs_fs_cursor *, struct key *, struct afs_vnode *, struct afs_read *, bool); -extern int afs_fs_create(struct afs_server *, struct key *, +extern int afs_fs_create(struct afs_fs_cursor *, struct key *, struct afs_vnode *, const char *, umode_t, struct afs_fid *, struct afs_file_status *, struct afs_callback *, bool); -extern int afs_fs_remove(struct afs_server *, struct key *, +extern int afs_fs_remove(struct afs_fs_cursor *, struct key *, struct afs_vnode *, const char *, bool, bool); -extern int afs_fs_link(struct afs_server *, struct key *, struct afs_vnode *, +extern int afs_fs_link(struct afs_fs_cursor *, struct key *, struct afs_vnode *, struct afs_vnode *, const char *, bool); -extern int afs_fs_symlink(struct afs_server *, struct key *, +extern int afs_fs_symlink(struct afs_fs_cursor *, struct key *, struct afs_vnode *, const char *, const char *, struct afs_fid *, struct afs_file_status *, bool); -extern int afs_fs_rename(struct afs_server *, struct key *, +extern int afs_fs_rename(struct afs_fs_cursor *, struct key *, struct afs_vnode *, const char *, struct afs_vnode *, const char *, bool); -extern int afs_fs_store_data(struct afs_server *, struct afs_writeback *, +extern int afs_fs_store_data(struct afs_fs_cursor *, struct afs_writeback *, pgoff_t, pgoff_t, unsigned, unsigned, bool); -extern int afs_fs_setattr(struct afs_server *, struct key *, +extern int afs_fs_setattr(struct afs_fs_cursor *, struct key *, struct afs_vnode *, struct iattr *, bool); -extern int afs_fs_get_volume_status(struct afs_server *, struct key *, +extern int afs_fs_get_volume_status(struct afs_fs_cursor *, struct key *, struct afs_vnode *, struct afs_volume_status *, bool); -extern int afs_fs_set_lock(struct afs_server *, struct key *, +extern int afs_fs_set_lock(struct afs_fs_cursor *, struct key *, struct afs_vnode *, afs_lock_type_t, bool); -extern int afs_fs_extend_lock(struct afs_server *, struct key *, +extern int afs_fs_extend_lock(struct afs_fs_cursor *, struct key *, struct afs_vnode *, bool); -extern int afs_fs_release_lock(struct afs_server *, struct key *, +extern int afs_fs_release_lock(struct afs_fs_cursor *, struct key *, struct afs_vnode *, bool); -extern int afs_fs_give_up_all_callbacks(struct afs_server *, struct key *, bool); +extern int afs_fs_give_up_all_callbacks(struct afs_server *, struct afs_addr_cursor *, + struct key *, bool); /* * inode.c @@ -697,7 +743,7 @@ extern void __net_exit afs_close_socket(struct afs_net *); extern void afs_charge_preallocation(struct work_struct *); extern void afs_put_call(struct afs_call *); extern int afs_queue_call_work(struct afs_call *); -extern long afs_make_call(struct sockaddr_rxrpc *, struct afs_call *, gfp_t, bool); +extern long afs_make_call(struct afs_addr_cursor *, struct afs_call *, gfp_t, bool); extern struct afs_call *afs_alloc_flat_call(struct afs_net *, const struct afs_call_type *, size_t, size_t); @@ -751,13 +797,11 @@ extern void __exit afs_fs_exit(void); /* * vlclient.c */ -extern int afs_vl_get_entry_by_name(struct afs_net *, - struct sockaddr_rxrpc *, struct key *, - const char *, struct afs_cache_vlocation *, - bool); -extern int afs_vl_get_entry_by_id(struct afs_net *, - struct sockaddr_rxrpc *, struct key *, - afs_volid_t, afs_voltype_t, +extern int afs_vl_get_entry_by_name(struct afs_net *, struct afs_addr_cursor *, + struct key *, const char *, + struct afs_cache_vlocation *, bool); +extern int afs_vl_get_entry_by_id(struct afs_net *, struct afs_addr_cursor *, + struct key *, afs_volid_t, afs_voltype_t, struct afs_cache_vlocation *, bool); /* @@ -828,9 +872,11 @@ static inline struct afs_volume *afs_get_volume(struct afs_volume *volume) extern void afs_put_volume(struct afs_cell *, struct afs_volume *); extern struct afs_volume *afs_volume_lookup(struct afs_mount_params *); -extern struct afs_server *afs_volume_pick_fileserver(struct afs_vnode *); -extern int afs_volume_release_fileserver(struct afs_vnode *, - struct afs_server *, int); +extern void afs_init_fs_cursor(struct afs_fs_cursor *, struct afs_vnode *); +extern int afs_set_fs_cursor(struct afs_fs_cursor *, struct afs_vnode *); +extern bool afs_volume_pick_fileserver(struct afs_fs_cursor *, struct afs_vnode *); +extern bool afs_iterate_fs_cursor(struct afs_fs_cursor *, struct afs_vnode *); +extern int afs_end_fs_cursor(struct afs_fs_cursor *, struct afs_net *); /* * write.c diff --git a/fs/afs/proc.c b/fs/afs/proc.c index 08565429615d..9cf9ce88a8dd 100644 --- a/fs/afs/proc.c +++ b/fs/afs/proc.c @@ -514,23 +514,23 @@ static int afs_proc_cell_vlservers_open(struct inode *inode, struct file *file) */ static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos) { + struct afs_addr_list *alist; struct afs_cell *cell = m->private; loff_t pos = *_pos; - _enter("cell=%p pos=%Ld", cell, *_pos); + rcu_read_lock(); - /* lock the list against modification */ - down_read(&cell->vl_sem); + alist = rcu_dereference(cell->vl_addrs); /* allow for the header line */ if (!pos) return (void *) 1; pos--; - if (pos >= cell->vl_naddrs) + if (!alist || pos >= alist->nr_addrs) return NULL; - return &cell->vl_addrs[pos]; + return alist->addrs + pos; } /* @@ -539,17 +539,18 @@ static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos) static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v, loff_t *_pos) { + struct afs_addr_list *alist; struct afs_cell *cell = p->private; loff_t pos; - _enter("cell=%p{nad=%u} pos=%Ld", cell, cell->vl_naddrs, *_pos); + alist = rcu_dereference(cell->vl_addrs); pos = *_pos; (*_pos)++; - if (pos >= cell->vl_naddrs) + if (!alist || pos >= alist->nr_addrs) return NULL; - return &cell->vl_addrs[pos]; + return alist->addrs + pos; } /* @@ -557,9 +558,7 @@ static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v, */ static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v) { - struct afs_cell *cell = p->private; - - up_read(&cell->vl_sem); + rcu_read_unlock(); } /* @@ -658,7 +657,7 @@ static int afs_proc_cell_servers_show(struct seq_file *m, void *v) } /* display one cell per line on subsequent lines */ - sprintf(ipaddr, "%pISp", &server->addr.transport); + sprintf(ipaddr, "%pISp", &server->addrs->addrs[0].transport); seq_printf(m, "%3d %-15s %5d\n", atomic_read(&server->usage), ipaddr, server->fs_state); diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index ac1e25f957b1..5ddfb7c4cf78 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -321,9 +321,10 @@ static int afs_send_pages(struct afs_call *call, struct msghdr *msg) /* * initiate a call */ -long afs_make_call(struct sockaddr_rxrpc *srx, struct afs_call *call, +long afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, gfp_t gfp, bool async) { + struct sockaddr_rxrpc *srx = ac->addr; struct rxrpc_call *rxcall; struct msghdr msg; struct kvec iov[1]; diff --git a/fs/afs/server.c b/fs/afs/server.c index 4e66608fc805..9ca174b24f5b 100644 --- a/fs/afs/server.c +++ b/fs/afs/server.c @@ -56,7 +56,9 @@ static int afs_install_server(struct afs_server *server) p = *pp; _debug("- consider %p", p); xserver = rb_entry(p, struct afs_server, master_rb); - diff = memcmp(&server->addr, &xserver->addr, sizeof(server->addr)); + diff = memcmp(&server->addrs->addrs[0], + &xserver->addrs->addrs[0], + sizeof(sizeof(server->addrs->addrs[0]))); if (diff < 0) pp = &(*pp)->rb_left; else if (diff > 0) @@ -85,25 +87,38 @@ static struct afs_server *afs_alloc_server(struct afs_cell *cell, _enter(""); server = kzalloc(sizeof(struct afs_server), GFP_KERNEL); - if (server) { - atomic_set(&server->usage, 1); - server->net = cell->net; - server->cell = cell; + if (!server) + goto enomem; + server->addrs = kzalloc(sizeof(struct afs_addr_list) + + sizeof(struct sockaddr_rxrpc), + GFP_KERNEL); + if (!server->addrs) + goto enomem_server; - 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); + atomic_set(&server->usage, 1); + server->net = cell->net; + server->cell = cell; - server->addr = *addr; - afs_inc_servers_outstanding(cell->net); - _leave(" = %p{%d}", server, atomic_read(&server->usage)); - } else { - _leave(" = NULL [nomem]"); - } + INIT_LIST_HEAD(&server->link); + INIT_LIST_HEAD(&server->grave); + init_rwsem(&server->sem); + spin_lock_init(&server->fs_lock); + INIT_LIST_HEAD(&server->cb_interests); + rwlock_init(&server->cb_break_lock); + + refcount_set(&server->addrs->usage, 1); + server->addrs->nr_addrs = 1; + server->addrs->addrs[0] = *addr; + afs_inc_servers_outstanding(cell->net); + + _leave(" = %p{%d}", server, atomic_read(&server->usage)); return server; + +enomem_server: + kfree(server); +enomem: + _leave(" = NULL [nomem]"); + return NULL; } /* @@ -120,7 +135,7 @@ struct afs_server *afs_lookup_server(struct afs_cell *cell, read_lock(&cell->servers_lock); list_for_each_entry(server, &cell->servers, link) { - if (memcmp(&server->addr, addr, sizeof(*addr)) == 0) + if (memcmp(&server->addrs->addrs[0], addr, sizeof(*addr)) == 0) goto found_server_quickly; } read_unlock(&cell->servers_lock); @@ -135,7 +150,7 @@ struct afs_server *afs_lookup_server(struct afs_cell *cell, /* check the cell's server list again */ list_for_each_entry(server, &cell->servers, link) { - if (memcmp(&server->addr, addr, sizeof(*addr)) == 0) + if (memcmp(&server->addrs->addrs[0], addr, sizeof(*addr)) == 0) goto found_server; } @@ -204,7 +219,7 @@ struct afs_server *afs_find_server(struct afs_net *net, _debug("- consider %p", p); - diff = memcmp(srx, &server->addr, sizeof(*srx)); + diff = memcmp(srx, &server->addrs->addrs[0], sizeof(*srx)); if (diff < 0) { p = p->rb_left; } else if (diff > 0) { @@ -269,10 +284,19 @@ void afs_put_server(struct afs_net *net, struct afs_server *server) */ static void afs_destroy_server(struct afs_net *net, struct afs_server *server) { + struct afs_addr_list *alist = server->addrs; + struct afs_addr_cursor ac = { + .alist = alist, + .addr = &alist->addrs[0], + .start = alist->index, + .index = alist->index, + .error = 0, + }; _enter("%p", server); - afs_fs_give_up_all_callbacks(server, NULL, false); + afs_fs_give_up_all_callbacks(server, &ac, NULL, false); afs_put_cell(net, server->cell); + afs_put_addrlist(server->addrs); kfree(server); afs_dec_servers_outstanding(net); } diff --git a/fs/afs/vlclient.c b/fs/afs/vlclient.c index aa79fe3f168b..1d1e7df77dd5 100644 --- a/fs/afs/vlclient.c +++ b/fs/afs/vlclient.c @@ -114,7 +114,7 @@ static const struct afs_call_type afs_RXVLGetEntryById = { * dispatch a get volume entry by name operation */ int afs_vl_get_entry_by_name(struct afs_net *net, - struct sockaddr_rxrpc *addr, + struct afs_addr_cursor *ac, struct key *key, const char *volname, struct afs_cache_vlocation *entry, @@ -146,14 +146,14 @@ int afs_vl_get_entry_by_name(struct afs_net *net, memset((void *) bp + volnamesz, 0, padsz); /* initiate the call */ - return afs_make_call(addr, call, GFP_KERNEL, async); + return afs_make_call(ac, call, GFP_KERNEL, async); } /* * dispatch a get volume entry by ID operation */ int afs_vl_get_entry_by_id(struct afs_net *net, - struct sockaddr_rxrpc *addr, + struct afs_addr_cursor *ac, struct key *key, afs_volid_t volid, afs_voltype_t voltype, @@ -179,5 +179,5 @@ int afs_vl_get_entry_by_id(struct afs_net *net, *bp = htonl(voltype); /* initiate the call */ - return afs_make_call(addr, call, GFP_KERNEL, async); + return afs_make_call(ac, call, GFP_KERNEL, async); } diff --git a/fs/afs/vlocation.c b/fs/afs/vlocation.c index ec5ab8dc9bc8..52c31ad0ef60 100644 --- a/fs/afs/vlocation.c +++ b/fs/afs/vlocation.c @@ -29,22 +29,25 @@ static int afs_vlocation_access_vl_by_name(struct afs_vlocation *vl, struct key *key, struct afs_cache_vlocation *vldb) { - struct afs_cell *cell = vl->cell; - int count, ret; + struct afs_addr_cursor ac; + int ret; - _enter("%s,%s", cell->name, vl->vldb.name); + _enter("%s,%s", vl->cell->name, vl->vldb.name); + + ret = afs_set_vl_cursor(&ac, vl->cell); + if (ret < 0) + return ret; down_write(&vl->cell->vl_sem); + ret = -ENOMEDIUM; - for (count = cell->vl_naddrs; count > 0; count--) { - struct sockaddr_rxrpc *addr = &cell->vl_addrs[cell->vl_curr_svix]; - - _debug("CellServ[%hu]: %pIS", cell->vl_curr_svix, &addr->transport); + while (afs_iterate_addresses(&ac)) { + _debug("CellServ[%hu]: %pIS", ac.index, &ac.addr->transport); /* attempt to access the VL server */ - ret = afs_vl_get_entry_by_name(cell->net, addr, key, - vl->vldb.name, vldb, false); - switch (ret) { + ac.error = afs_vl_get_entry_by_name(vl->cell->net, &ac, key, + vl->vldb.name, vldb, false); + switch (ac.error) { case 0: goto out; case -ENOMEM: @@ -52,26 +55,24 @@ static int afs_vlocation_access_vl_by_name(struct afs_vlocation *vl, case -ENETUNREACH: case -EHOSTUNREACH: case -ECONNREFUSED: - if (ret == -ENOMEM || ret == -ENONET) + if (ac.error == -ENOMEM || ac.error == -ENONET) goto out; - goto rotate; + break; case -ENOMEDIUM: case -EKEYREJECTED: case -EKEYEXPIRED: + ac.responded = true; goto out; default: - ret = -EIO; - goto rotate; + ac.responded = true; + ac.error = -EIO; + break; } - - /* rotate the server records upon lookup failure */ - rotate: - cell->vl_curr_svix++; - cell->vl_curr_svix %= cell->vl_naddrs; } out: up_write(&vl->cell->vl_sem); + ret = afs_end_cursor(&ac); _leave(" = %d", ret); return ret; } @@ -86,22 +87,24 @@ static int afs_vlocation_access_vl_by_id(struct afs_vlocation *vl, afs_voltype_t voltype, struct afs_cache_vlocation *vldb) { - struct afs_cell *cell = vl->cell; - int count, ret; + struct afs_addr_cursor ac; + int ret; - _enter("%s,%x,%d,", cell->name, volid, voltype); + _enter("%s,%x,%d,", vl->cell->name, volid, voltype); + + ret = afs_set_vl_cursor(&ac, vl->cell); + if (ret < 0) + return ret; down_write(&vl->cell->vl_sem); ret = -ENOMEDIUM; - for (count = cell->vl_naddrs; count > 0; count--) { - struct sockaddr_rxrpc *addr = &cell->vl_addrs[cell->vl_curr_svix]; - - _debug("CellServ[%hu]: %pIS", cell->vl_curr_svix, &addr->transport); + while (afs_iterate_addresses(&ac)) { + _debug("CellServ[%hu]: %pIS", ac.index, &ac.addr->transport); /* attempt to access the VL server */ - ret = afs_vl_get_entry_by_id(cell->net, addr, key, volid, - voltype, vldb, false); - switch (ret) { + ac.error = afs_vl_get_entry_by_id(vl->cell->net, &ac, key, volid, + voltype, vldb, false); + switch (ac.error) { case 0: goto out; case -ENOMEM: @@ -109,10 +112,11 @@ static int afs_vlocation_access_vl_by_id(struct afs_vlocation *vl, case -ENETUNREACH: case -EHOSTUNREACH: case -ECONNREFUSED: - if (ret == -ENOMEM || ret == -ENONET) + if (ac.error == -ENOMEM || ac.error == -ENONET) goto out; goto rotate; case -EBUSY: + ac.responded = true; vl->upd_busy_cnt++; if (vl->upd_busy_cnt <= 3) { if (vl->upd_busy_cnt > 1) { @@ -124,30 +128,31 @@ static int afs_vlocation_access_vl_by_id(struct afs_vlocation *vl, } break; case -ENOMEDIUM: + ac.responded = true; vl->upd_rej_cnt++; goto rotate; default: - ret = -EIO; + ac.responded = true; + ac.error = -EIO; goto rotate; } /* rotate the server records upon lookup failure */ rotate: - cell->vl_curr_svix++; - cell->vl_curr_svix %= cell->vl_naddrs; vl->upd_busy_cnt = 0; } out: - if (ret < 0 && vl->upd_rej_cnt > 0) { + if (ac.error < 0 && vl->upd_rej_cnt > 0) { printk(KERN_NOTICE "kAFS:" " Active volume no longer valid '%s'\n", vl->vldb.name); vl->valid = 0; - ret = -ENOMEDIUM; + ac.error = -ENOMEDIUM; } up_write(&vl->cell->vl_sem); + ret = afs_end_cursor(&ac); _leave(" = %d", ret); return ret; } diff --git a/fs/afs/vnode.c b/fs/afs/vnode.c index 622e1100099b..9c7333eb01c2 100644 --- a/fs/afs/vnode.c +++ b/fs/afs/vnode.c @@ -44,30 +44,26 @@ static void afs_vnode_deleted_remotely(struct afs_vnode *vnode) void afs_vnode_finalise_status_update(struct afs_vnode *vnode, struct afs_server *server) { - struct afs_server *oldserver = NULL; - - _enter("%p,%p", vnode, server); - spin_lock(&vnode->lock); vnode->update_cnt--; ASSERTCMP(vnode->update_cnt, >=, 0); spin_unlock(&vnode->lock); wake_up_all(&vnode->update_waitq); - afs_put_server(afs_v2net(vnode), oldserver); _leave(""); } /* * finish off updating the recorded status of a file after an operation failed */ -static void afs_vnode_status_update_failed(struct afs_vnode *vnode, int ret) +static void afs_vnode_status_update_failed(struct afs_fs_cursor *fc, + struct afs_vnode *vnode) { - _enter("{%x:%u},%d", vnode->fid.vid, vnode->fid.vnode, ret); + _enter("{%x:%u},%d", vnode->fid.vid, vnode->fid.vnode, fc->ac.error); spin_lock(&vnode->lock); - if (ret == -ENOENT) { + if (fc->ac.error == -ENOENT) { /* the file was deleted on the server */ _debug("got NOENT from server - marking file deleted"); afs_vnode_deleted_remotely(vnode); @@ -90,9 +86,8 @@ static void afs_vnode_status_update_failed(struct afs_vnode *vnode, int ret) */ int afs_vnode_fetch_status(struct afs_vnode *vnode, struct key *key, bool force) { - struct afs_server *server; + struct afs_fs_cursor fc; unsigned int cb_break = 0; - int ret; DECLARE_WAITQUEUE(myself, current); @@ -172,43 +167,37 @@ get_anyway: /* merge AFS status fetches and clear outstanding callback on this * vnode */ + afs_init_fs_cursor(&fc, vnode); do { /* pick a server to query */ - server = afs_volume_pick_fileserver(vnode); - if (IS_ERR(server)) + if (!afs_volume_pick_fileserver(&fc, vnode)) goto no_server; - _debug("USING SERVER: %p{%pIS}", - server, &server->addr.transport); + fc.ac.error = afs_fs_fetch_file_status(&fc, key, vnode, NULL, false); - ret = afs_fs_fetch_file_status(server, key, vnode, NULL, - false); - - } while (!afs_volume_release_fileserver(vnode, server, ret)); + } while (afs_iterate_fs_cursor(&fc, vnode)); /* adjust the flags */ - if (ret == 0) { + if (fc.ac.error == 0) { _debug("adjust"); afs_cache_permit(vnode, key, cb_break); - afs_vnode_finalise_status_update(vnode, server); - afs_put_server(afs_v2net(vnode), server); + afs_vnode_finalise_status_update(vnode, fc.server); } else { - _debug("failed [%d]", ret); - afs_vnode_status_update_failed(vnode, ret); + _debug("failed [%d]", fc.ac.error); + afs_vnode_status_update_failed(&fc, vnode); } +out: + afs_end_fs_cursor(&fc, afs_v2net(vnode)); ASSERTCMP(vnode->update_cnt, >=, 0); - - _leave(" = %d [cnt %d]", ret, vnode->update_cnt); - return ret; + _leave(" = %d [cnt %d]", fc.ac.error, vnode->update_cnt); + return fc.ac.error; no_server: spin_lock(&vnode->lock); vnode->update_cnt--; - ASSERTCMP(vnode->update_cnt, >=, 0); spin_unlock(&vnode->lock); - _leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt); - return PTR_ERR(server); + goto out; } /* @@ -218,8 +207,7 @@ no_server: int afs_vnode_fetch_data(struct afs_vnode *vnode, struct key *key, struct afs_read *desc) { - struct afs_server *server; - int ret; + struct afs_fs_cursor fc; _enter("%s{%x:%u.%u},%x,,,", vnode->volume->vlocation->vldb.name, @@ -235,36 +223,31 @@ int afs_vnode_fetch_data(struct afs_vnode *vnode, struct key *key, /* merge in AFS status fetches and clear outstanding callback on this * vnode */ + afs_init_fs_cursor(&fc, vnode); do { /* pick a server to query */ - server = afs_volume_pick_fileserver(vnode); - if (IS_ERR(server)) + if (!afs_volume_pick_fileserver(&fc, vnode)) goto no_server; - _debug("USING SERVER: %pIS\n", &server->addr.transport); + fc.ac.error = afs_fs_fetch_data(&fc, key, vnode, desc, false); - ret = afs_fs_fetch_data(server, key, vnode, desc, - false); - - } while (!afs_volume_release_fileserver(vnode, server, ret)); + } while (afs_iterate_fs_cursor(&fc, vnode)); /* adjust the flags */ - if (ret == 0) { - afs_vnode_finalise_status_update(vnode, server); - afs_put_server(afs_v2net(vnode), server); - } else { - afs_vnode_status_update_failed(vnode, ret); - } + if (fc.ac.error == 0) + afs_vnode_finalise_status_update(vnode, fc.server); + else + afs_vnode_status_update_failed(&fc, vnode); - _leave(" = %d", ret); - return ret; +out: + return afs_end_fs_cursor(&fc, afs_v2net(vnode)); no_server: spin_lock(&vnode->lock); vnode->update_cnt--; ASSERTCMP(vnode->update_cnt, >=, 0); spin_unlock(&vnode->lock); - return PTR_ERR(server); + goto out; } /* @@ -275,8 +258,7 @@ int afs_vnode_create(struct afs_vnode *vnode, struct key *key, struct afs_file_status *newstatus, struct afs_callback *newcb, struct afs_server **_server) { - struct afs_server *server; - int ret; + struct afs_fs_cursor fc; _enter("%s{%x:%u.%u},%x,%s,,", vnode->volume->vlocation->vldb.name, @@ -291,38 +273,36 @@ int afs_vnode_create(struct afs_vnode *vnode, struct key *key, vnode->update_cnt++; spin_unlock(&vnode->lock); + afs_init_fs_cursor(&fc, vnode); do { /* pick a server to query */ - server = afs_volume_pick_fileserver(vnode); - if (IS_ERR(server)) + if (!afs_volume_pick_fileserver(&fc, vnode)) goto no_server; - _debug("USING SERVER: %pIS\n", &server->addr.transport); + fc.ac.error = afs_fs_create(&fc, key, vnode, name, mode, newfid, + newstatus, newcb, false); - ret = afs_fs_create(server, key, vnode, name, mode, newfid, - newstatus, newcb, false); - - } while (!afs_volume_release_fileserver(vnode, server, ret)); + } while (afs_iterate_fs_cursor(&fc, vnode)); /* adjust the flags */ - if (ret == 0) { - afs_vnode_finalise_status_update(vnode, server); - *_server = server; + if (fc.ac.error == 0) { + afs_vnode_finalise_status_update(vnode, fc.server); + *_server = fc.server; + fc.server = NULL; } else { - afs_vnode_status_update_failed(vnode, ret); + afs_vnode_status_update_failed(&fc, vnode); *_server = NULL; } - _leave(" = %d [cnt %d]", ret, vnode->update_cnt); - return ret; +out: + return afs_end_fs_cursor(&fc, afs_v2net(vnode)); no_server: spin_lock(&vnode->lock); vnode->update_cnt--; ASSERTCMP(vnode->update_cnt, >=, 0); spin_unlock(&vnode->lock); - _leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt); - return PTR_ERR(server); + goto out; } /* @@ -331,8 +311,7 @@ no_server: int afs_vnode_remove(struct afs_vnode *vnode, struct key *key, const char *name, bool isdir) { - struct afs_server *server; - int ret; + struct afs_fs_cursor fc; _enter("%s{%x:%u.%u},%x,%s", vnode->volume->vlocation->vldb.name, @@ -347,37 +326,31 @@ int afs_vnode_remove(struct afs_vnode *vnode, struct key *key, const char *name, vnode->update_cnt++; spin_unlock(&vnode->lock); + afs_init_fs_cursor(&fc, vnode); do { /* pick a server to query */ - server = afs_volume_pick_fileserver(vnode); - if (IS_ERR(server)) + if (!afs_volume_pick_fileserver(&fc, vnode)) goto no_server; - _debug("USING SERVER: %pIS\n", &server->addr.transport); + fc.ac.error = afs_fs_remove(&fc, key, vnode, name, isdir, false); - ret = afs_fs_remove(server, key, vnode, name, isdir, - false); - - } while (!afs_volume_release_fileserver(vnode, server, ret)); + } while (afs_iterate_fs_cursor(&fc, vnode)); /* adjust the flags */ - if (ret == 0) { - afs_vnode_finalise_status_update(vnode, server); - afs_put_server(afs_v2net(vnode), server); - } else { - afs_vnode_status_update_failed(vnode, ret); - } + if (fc.ac.error == 0) + afs_vnode_finalise_status_update(vnode, fc.server); + else + afs_vnode_status_update_failed(&fc, vnode); - _leave(" = %d [cnt %d]", ret, vnode->update_cnt); - return ret; +out: + return afs_end_fs_cursor(&fc, afs_v2net(vnode)); no_server: spin_lock(&vnode->lock); vnode->update_cnt--; ASSERTCMP(vnode->update_cnt, >=, 0); spin_unlock(&vnode->lock); - _leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt); - return PTR_ERR(server); + goto out; } /* @@ -386,8 +359,7 @@ no_server: int afs_vnode_link(struct afs_vnode *dvnode, struct afs_vnode *vnode, struct key *key, const char *name) { - struct afs_server *server; - int ret; + struct afs_fs_cursor fc; _enter("%s{%x:%u.%u},%s{%x:%u.%u},%x,%s", dvnode->volume->vlocation->vldb.name, @@ -409,31 +381,27 @@ int afs_vnode_link(struct afs_vnode *dvnode, struct afs_vnode *vnode, dvnode->update_cnt++; spin_unlock(&dvnode->lock); + afs_init_fs_cursor(&fc, vnode); do { /* pick a server to query */ - server = afs_volume_pick_fileserver(dvnode); - if (IS_ERR(server)) + if (!afs_volume_pick_fileserver(&fc, dvnode)) goto no_server; - _debug("USING SERVER: %pIS\n", &server->addr.transport); + fc.ac.error = afs_fs_link(&fc, key, dvnode, vnode, name, false); - ret = afs_fs_link(server, key, dvnode, vnode, name, - false); - - } while (!afs_volume_release_fileserver(dvnode, server, ret)); + } while (afs_iterate_fs_cursor(&fc, dvnode)); /* adjust the flags */ - if (ret == 0) { - afs_vnode_finalise_status_update(vnode, server); - afs_vnode_finalise_status_update(dvnode, server); - afs_put_server(afs_v2net(dvnode), server); + if (fc.ac.error == 0) { + afs_vnode_finalise_status_update(vnode, fc.server); + afs_vnode_finalise_status_update(dvnode, fc.server); } else { - afs_vnode_status_update_failed(vnode, ret); - afs_vnode_status_update_failed(dvnode, ret); + afs_vnode_status_update_failed(&fc, vnode); + afs_vnode_status_update_failed(&fc, dvnode); } - _leave(" = %d [cnt %d]", ret, vnode->update_cnt); - return ret; +out: + return afs_end_fs_cursor(&fc, afs_v2net(vnode)); no_server: spin_lock(&vnode->lock); @@ -444,8 +412,7 @@ no_server: dvnode->update_cnt--; ASSERTCMP(dvnode->update_cnt, >=, 0); spin_unlock(&dvnode->lock); - _leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt); - return PTR_ERR(server); + goto out; } /* @@ -457,8 +424,7 @@ int afs_vnode_symlink(struct afs_vnode *vnode, struct key *key, struct afs_file_status *newstatus, struct afs_server **_server) { - struct afs_server *server; - int ret; + struct afs_fs_cursor fc; _enter("%s{%x:%u.%u},%x,%s,%s,,,", vnode->volume->vlocation->vldb.name, @@ -473,38 +439,37 @@ int afs_vnode_symlink(struct afs_vnode *vnode, struct key *key, vnode->update_cnt++; spin_unlock(&vnode->lock); + afs_init_fs_cursor(&fc, vnode); do { /* pick a server to query */ - server = afs_volume_pick_fileserver(vnode); - if (IS_ERR(server)) + if (!afs_volume_pick_fileserver(&fc, vnode)) goto no_server; - _debug("USING SERVER: %pIS\n", &server->addr.transport); + fc.ac.error = afs_fs_symlink(&fc, key, vnode, name, content, + newfid, newstatus, false); - ret = afs_fs_symlink(server, key, vnode, name, content, - newfid, newstatus, false); - - } while (!afs_volume_release_fileserver(vnode, server, ret)); + } while (afs_iterate_fs_cursor(&fc, vnode)); /* adjust the flags */ - if (ret == 0) { - afs_vnode_finalise_status_update(vnode, server); - *_server = server; + if (fc.ac.error == 0) { + afs_vnode_finalise_status_update(vnode, fc.server); + *_server = fc.server; + fc.server = NULL; } else { - afs_vnode_status_update_failed(vnode, ret); + afs_vnode_status_update_failed(&fc, vnode); *_server = NULL; } - _leave(" = %d [cnt %d]", ret, vnode->update_cnt); - return ret; +out: + return afs_end_fs_cursor(&fc, afs_v2net(vnode)); no_server: spin_lock(&vnode->lock); vnode->update_cnt--; ASSERTCMP(vnode->update_cnt, >=, 0); spin_unlock(&vnode->lock); - _leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt); - return PTR_ERR(server); + *_server = NULL; + goto out; } /* @@ -516,8 +481,7 @@ int afs_vnode_rename(struct afs_vnode *orig_dvnode, const char *orig_name, const char *new_name) { - struct afs_server *server; - int ret; + struct afs_fs_cursor fc; _enter("%s{%x:%u.%u},%s{%u,%u,%u},%x,%s,%s", orig_dvnode->volume->vlocation->vldb.name, @@ -543,33 +507,30 @@ int afs_vnode_rename(struct afs_vnode *orig_dvnode, spin_unlock(&new_dvnode->lock); } + afs_init_fs_cursor(&fc, orig_dvnode); do { /* pick a server to query */ - server = afs_volume_pick_fileserver(orig_dvnode); - if (IS_ERR(server)) + if (!afs_volume_pick_fileserver(&fc, orig_dvnode)) goto no_server; - _debug("USING SERVER: %pIS\n", &server->addr.transport); + fc.ac.error = afs_fs_rename(&fc, key, orig_dvnode, orig_name, + new_dvnode, new_name, false); - ret = afs_fs_rename(server, key, orig_dvnode, orig_name, - new_dvnode, new_name, false); - - } while (!afs_volume_release_fileserver(orig_dvnode, server, ret)); + } while (afs_iterate_fs_cursor(&fc, orig_dvnode)); /* adjust the flags */ - if (ret == 0) { - afs_vnode_finalise_status_update(orig_dvnode, server); + if (fc.ac.error == 0) { + afs_vnode_finalise_status_update(orig_dvnode, fc.server); if (new_dvnode != orig_dvnode) - afs_vnode_finalise_status_update(new_dvnode, server); - afs_put_server(afs_v2net(orig_dvnode), server); + afs_vnode_finalise_status_update(new_dvnode, fc.server); } else { - afs_vnode_status_update_failed(orig_dvnode, ret); + afs_vnode_status_update_failed(&fc, orig_dvnode); if (new_dvnode != orig_dvnode) - afs_vnode_status_update_failed(new_dvnode, ret); + afs_vnode_status_update_failed(&fc, new_dvnode); } - _leave(" = %d [cnt %d]", ret, orig_dvnode->update_cnt); - return ret; +out: + return afs_end_fs_cursor(&fc, afs_v2net(orig_dvnode)); no_server: spin_lock(&orig_dvnode->lock); @@ -582,8 +543,7 @@ no_server: ASSERTCMP(new_dvnode->update_cnt, >=, 0); spin_unlock(&new_dvnode->lock); } - _leave(" = %ld [cnt %d]", PTR_ERR(server), orig_dvnode->update_cnt); - return PTR_ERR(server); + goto out; } /* @@ -592,9 +552,8 @@ no_server: int afs_vnode_store_data(struct afs_writeback *wb, pgoff_t first, pgoff_t last, unsigned offset, unsigned to) { - struct afs_server *server; + struct afs_fs_cursor fc; struct afs_vnode *vnode = wb->vnode; - int ret; _enter("%s{%x:%u.%u},%x,%lx,%lx,%x,%x", vnode->volume->vlocation->vldb.name, @@ -609,36 +568,33 @@ int afs_vnode_store_data(struct afs_writeback *wb, pgoff_t first, pgoff_t last, vnode->update_cnt++; spin_unlock(&vnode->lock); + afs_init_fs_cursor(&fc, vnode); do { /* pick a server to query */ - server = afs_volume_pick_fileserver(vnode); - if (IS_ERR(server)) + if (!afs_volume_pick_fileserver(&fc, vnode)) goto no_server; - _debug("USING SERVER: %pIS\n", &server->addr.transport); + fc.ac.error = afs_fs_store_data(&fc, wb, first, last, offset, to, + false); - ret = afs_fs_store_data(server, wb, first, last, offset, to, - false); - - } while (!afs_volume_release_fileserver(vnode, server, ret)); + } while (afs_iterate_fs_cursor(&fc, vnode)); /* adjust the flags */ - if (ret == 0) { - afs_vnode_finalise_status_update(vnode, server); - afs_put_server(afs_v2net(vnode), server); + if (fc.ac.error == 0) { + afs_vnode_finalise_status_update(vnode, fc.server); } else { - afs_vnode_status_update_failed(vnode, ret); + afs_vnode_status_update_failed(&fc, vnode); } - _leave(" = %d", ret); - return ret; +out: + return afs_end_fs_cursor(&fc, afs_v2net(vnode)); no_server: spin_lock(&vnode->lock); vnode->update_cnt--; ASSERTCMP(vnode->update_cnt, >=, 0); spin_unlock(&vnode->lock); - return PTR_ERR(server); + goto out; } /* @@ -647,8 +603,7 @@ no_server: int afs_vnode_setattr(struct afs_vnode *vnode, struct key *key, struct iattr *attr) { - struct afs_server *server; - int ret; + struct afs_fs_cursor fc; _enter("%s{%x:%u.%u},%x", vnode->volume->vlocation->vldb.name, @@ -662,35 +617,32 @@ int afs_vnode_setattr(struct afs_vnode *vnode, struct key *key, vnode->update_cnt++; spin_unlock(&vnode->lock); + afs_init_fs_cursor(&fc, vnode); do { /* pick a server to query */ - server = afs_volume_pick_fileserver(vnode); - if (IS_ERR(server)) + if (!afs_volume_pick_fileserver(&fc, vnode)) goto no_server; - _debug("USING SERVER: %pIS\n", &server->addr.transport); + fc.ac.error = afs_fs_setattr(&fc, key, vnode, attr, false); - ret = afs_fs_setattr(server, key, vnode, attr, false); - - } while (!afs_volume_release_fileserver(vnode, server, ret)); + } while (afs_iterate_fs_cursor(&fc, vnode)); /* adjust the flags */ - if (ret == 0) { - afs_vnode_finalise_status_update(vnode, server); - afs_put_server(afs_v2net(vnode), server); + if (fc.ac.error == 0) { + afs_vnode_finalise_status_update(vnode, fc.server); } else { - afs_vnode_status_update_failed(vnode, ret); + afs_vnode_status_update_failed(&fc, vnode); } - _leave(" = %d", ret); - return ret; +out: + return afs_end_fs_cursor(&fc, afs_v2net(vnode)); no_server: spin_lock(&vnode->lock); vnode->update_cnt--; ASSERTCMP(vnode->update_cnt, >=, 0); spin_unlock(&vnode->lock); - return PTR_ERR(server); + goto out; } /* @@ -699,8 +651,7 @@ no_server: int afs_vnode_get_volume_status(struct afs_vnode *vnode, struct key *key, struct afs_volume_status *vs) { - struct afs_server *server; - int ret; + struct afs_fs_cursor fc; _enter("%s{%x:%u.%u},%x,", vnode->volume->vlocation->vldb.name, @@ -709,27 +660,17 @@ int afs_vnode_get_volume_status(struct afs_vnode *vnode, struct key *key, vnode->fid.unique, key_serial(key)); + afs_init_fs_cursor(&fc, vnode); do { /* pick a server to query */ - server = afs_volume_pick_fileserver(vnode); - if (IS_ERR(server)) - goto no_server; + if (!afs_volume_pick_fileserver(&fc, vnode)) + break; - _debug("USING SERVER: %pIS\n", &server->addr.transport); + fc.ac.error = afs_fs_get_volume_status(&fc, key, vnode, vs, false); - ret = afs_fs_get_volume_status(server, key, vnode, vs, 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)); } /* @@ -738,8 +679,7 @@ no_server: int afs_vnode_set_lock(struct afs_vnode *vnode, struct key *key, afs_lock_type_t type) { - struct afs_server *server; - int ret; + struct afs_fs_cursor fc; _enter("%s{%x:%u.%u},%x,%u", vnode->volume->vlocation->vldb.name, @@ -748,27 +688,17 @@ int afs_vnode_set_lock(struct afs_vnode *vnode, struct key *key, vnode->fid.unique, key_serial(key), type); + afs_init_fs_cursor(&fc, vnode); do { /* pick a server to query */ - server = afs_volume_pick_fileserver(vnode); - if (IS_ERR(server)) - goto no_server; + if (!afs_volume_pick_fileserver(&fc, vnode)) + break; - _debug("USING SERVER: %pIS\n", &server->addr.transport); + fc.ac.error = afs_fs_set_lock(&fc, key, vnode, type, false); - ret = afs_fs_set_lock(server, key, vnode, type, false); + } while (afs_iterate_fs_cursor(&fc, vnode)); - } while (!afs_volume_release_fileserver(vnode, server, ret)); - - /* adjust the flags */ - if (ret == 0) - afs_put_server(afs_v2net(vnode), server); - - _leave(" = %d", ret); - return ret; - -no_server: - return PTR_ERR(server); + return afs_end_fs_cursor(&fc, afs_v2net(vnode)); } /* @@ -776,7 +706,7 @@ no_server: */ int afs_vnode_extend_lock(struct afs_vnode *vnode, struct key *key) { - struct afs_server *server; + struct afs_fs_cursor fc; int ret; _enter("%s{%x:%u.%u},%x", @@ -786,27 +716,13 @@ int afs_vnode_extend_lock(struct afs_vnode *vnode, struct key *key) vnode->fid.unique, key_serial(key)); - do { - /* pick a server to query */ - server = afs_volume_pick_fileserver(vnode); - if (IS_ERR(server)) - goto no_server; + ret = afs_set_fs_cursor(&fc, vnode); + if (ret < 0) + return ret; - _debug("USING SERVER: %pIS\n", &server->addr.transport); + fc.ac.error = afs_fs_extend_lock(&fc, key, vnode, false); - ret = afs_fs_extend_lock(server, key, vnode, false); - - } while (!afs_volume_release_fileserver(vnode, server, ret)); - - /* adjust the flags */ - if (ret == 0) - afs_put_server(afs_v2net(vnode), server); - - _leave(" = %d", ret); - return ret; - -no_server: - return PTR_ERR(server); + return afs_end_fs_cursor(&fc, afs_v2net(vnode)); } /* @@ -814,7 +730,7 @@ no_server: */ int afs_vnode_release_lock(struct afs_vnode *vnode, struct key *key) { - struct afs_server *server; + struct afs_fs_cursor fc; int ret; _enter("%s{%x:%u.%u},%x", @@ -824,25 +740,11 @@ int afs_vnode_release_lock(struct afs_vnode *vnode, struct key *key) vnode->fid.unique, key_serial(key)); - do { - /* pick a server to query */ - server = afs_volume_pick_fileserver(vnode); - if (IS_ERR(server)) - goto no_server; + ret = afs_set_fs_cursor(&fc, vnode); + if (ret < 0) + return ret; - _debug("USING SERVER: %pIS\n", &server->addr.transport); + fc.ac.error = afs_fs_release_lock(&fc, key, vnode, false); - ret = afs_fs_release_lock(server, key, vnode, false); - - } while (!afs_volume_release_fileserver(vnode, server, ret)); - - /* adjust the flags */ - if (ret == 0) - afs_put_server(afs_v2net(vnode), server); - - _leave(" = %d", ret); - return ret; - -no_server: - return PTR_ERR(server); + return afs_end_fs_cursor(&fc, afs_v2net(vnode)); } diff --git a/fs/afs/volume.c b/fs/afs/volume.c index 4f6fd10094c6..d282cd0ff268 100644 --- a/fs/afs/volume.c +++ b/fs/afs/volume.c @@ -209,11 +209,45 @@ void afs_put_volume(struct afs_cell *cell, struct afs_volume *volume) _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; @@ -223,19 +257,18 @@ struct afs_server *afs_volume_pick_fileserver(struct afs_vnode *vnode) /* stick with the server we're already using if we can */ if (vnode->cb_interest && vnode->cb_interest->server->fs_state == 0) { - afs_get_server(vnode->cb_interest->server); - _leave(" = %p [current]", vnode->cb_interest->server); - return vnode->cb_interest->server; + fc->server = afs_get_server(vnode->cb_interest->server); + goto set_server; } down_read(&volume->server_sem); /* handle the no-server case */ if (volume->nservers == 0) { - ret = volume->rjservers ? -ENOMEDIUM : -ESTALE; + fc->ac.error = volume->rjservers ? -ENOMEDIUM : -ESTALE; up_read(&volume->server_sem); - _leave(" = %d [no servers]", ret); - return ERR_PTR(ret); + _leave(" = f [no servers %d]", fc->ac.error); + return false; } /* basically, just search the list for the first live server and use @@ -280,13 +313,15 @@ struct afs_server *afs_volume_pick_fileserver(struct afs_vnode *vnode) } } +error: + fc->ac.error = ret; + /* no available servers * - TODO: handle the no active servers case better */ -error: up_read(&volume->server_sem); - _leave(" = %d", ret); - return ERR_PTR(ret); + _leave(" = f [%d]", fc->ac.error); + return false; picked_server: /* Found an apparently healthy server. We need to register an interest @@ -296,37 +331,41 @@ picked_server: &volume->cb_interests[loop], server); if (ret < 0) goto error; - - afs_get_server(server); + + fc->server = afs_get_server(server); up_read(&volume->server_sem); - _leave(" = %p (picked %pIS)", - server, &server->addr.transport); - return server; +set_server: + fc->ac.alist = afs_get_addrlist(fc->server->addrs); + fc->ac.addr = &fc->ac.alist->addrs[0]; + _debug("USING SERVER: %pIS\n", &fc->ac.addr->transport); + _leave(" = t (picked %pIS)", &fc->ac.addr->transport); + return true; } /* * release a server after use * - releases the ref on the server struct that was acquired by picking * - records result of using a particular server to access a volume - * - return 0 to try again, 1 if okay or to issue error - * - the caller must release the server struct if result was 0 + * - return true to try again, false if okay or to issue error + * - the caller must release the server struct if result was false */ -int afs_volume_release_fileserver(struct afs_vnode *vnode, - struct afs_server *server, - int result) +bool afs_iterate_fs_cursor(struct afs_fs_cursor *fc, + struct afs_vnode *vnode) { struct afs_volume *volume = vnode->volume; + struct afs_server *server = fc->server; unsigned loop; _enter("%s,%pIS,%d", - volume->vlocation->vldb.name, &server->addr.transport, result); + volume->vlocation->vldb.name, &fc->ac.addr->transport, + fc->ac.error); - switch (result) { + switch (fc->ac.error) { /* success */ case 0: server->fs_state = 0; - _leave(""); - return 1; + _leave(" = f"); + return false; /* the fileserver denied all knowledge of the volume */ case -ENOMEDIUM: @@ -363,8 +402,9 @@ int afs_volume_release_fileserver(struct afs_vnode *vnode, */ up_write(&volume->server_sem); afs_put_server(afs_v2net(vnode), server); - _leave(" [completely rejected]"); - return 1; + fc->server = NULL; + _leave(" = f [completely rejected]"); + return false; /* problem reaching the server */ case -ENETUNREACH: @@ -378,8 +418,8 @@ int afs_volume_release_fileserver(struct afs_vnode *vnode, */ spin_lock(&server->fs_lock); if (!server->fs_state) { - server->fs_state = result; - printk("kAFS: SERVER DEAD state=%d\n", result); + server->fs_state = fc->ac.error; + printk("kAFS: SERVER DEAD state=%d\n", fc->ac.error); } spin_unlock(&server->fs_lock); goto try_next_server; @@ -390,8 +430,9 @@ int afs_volume_release_fileserver(struct afs_vnode *vnode, case -ENONET: /* tell the caller to accept the result */ afs_put_server(afs_v2net(vnode), server); - _leave(" [local failure]"); - return 1; + fc->server = NULL; + _leave(" = f [local failure]"); + return false; } /* tell the caller to loop around and try the next server */ @@ -399,6 +440,16 @@ try_next_server_upw: up_write(&volume->server_sem); try_next_server: afs_put_server(afs_v2net(vnode), server); - _leave(" [try next server]"); - return 0; + _leave(" = t [try next server]"); + return true; +} + +/* + * Clean up a fileserver cursor. + */ +int afs_end_fs_cursor(struct afs_fs_cursor *fc, struct afs_net *net) +{ + afs_end_cursor(&fc->ac); + afs_put_server(net, fc->server); + return fc->ac.error; }