summaryrefslogtreecommitdiff
path: root/fs/afs/volume.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2017-11-02 15:27:50 +0000
committerDavid Howells <dhowells@redhat.com>2017-11-13 15:38:18 +0000
commit8b2a464ced77fe35be72ab7d38152a9439daf8d3 (patch)
treeec478c8adebb6344513581b60935c0cf2b0ff937 /fs/afs/volume.c
parent989782dcdc91a5e6d5999c7a52a84a60a0811e56 (diff)
downloadlwn-8b2a464ced77fe35be72ab7d38152a9439daf8d3.tar.gz
lwn-8b2a464ced77fe35be72ab7d38152a9439daf8d3.zip
afs: Add an address list concept
Add an RCU replaceable address list structure to hold a list of server addresses. The list also holds the To this end: (1) A cell's VL server address list can be loaded directly via insmod or echo to /proc/fs/afs/cells or dynamically from a DNS query for AFSDB or SRV records. (2) Anyone wanting to use a cell's VL server address must wait until the cell record comes online and has tried to obtain some addresses. (3) An FS server's address list, for the moment, has a single entry that is the key to the server list. This will change in the future when a server is instead keyed on its UUID and the VL.GetAddrsU operation is used. (4) An 'address cursor' concept is introduced to handle iteration through the address list. This is passed to the afs_make_call() as, in the future, stuff (such as abort code) that doesn't outlast the call will be returned in it. In the future, we might want to annotate the list with information about how each address fares. We might then want to propagate such annotations over address list replacement. Whilst we're at it, we allow IPv6 addresses to be specified in colon-delimited lists by enclosing them in square brackets. Signed-off-by: David Howells <dhowells@redhat.com>
Diffstat (limited to 'fs/afs/volume.c')
-rw-r--r--fs/afs/volume.c115
1 files changed, 83 insertions, 32 deletions
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
@@ -210,10 +210,44 @@ void afs_put_volume(struct afs_cell *cell, struct afs_volume *volume)
}
/*
+ * Initialise a filesystem server cursor for iterating over FS servers.
+ */
+void afs_init_fs_cursor(struct afs_fs_cursor *fc, struct afs_vnode *vnode)
+{
+ fc->ac.alist = NULL;
+ fc->ac.addr = NULL;
+ fc->ac.start = 0;
+ fc->ac.index = 0;
+ fc->ac.error = 0;
+ fc->server = NULL;
+}
+
+/*
+ * Set a filesystem server cursor for using a specific FS server.
+ */
+int afs_set_fs_cursor(struct afs_fs_cursor *fc, struct afs_vnode *vnode)
+{
+ afs_init_fs_cursor(fc, vnode);
+
+ read_seqlock_excl(&vnode->cb_lock);
+ if (vnode->cb_interest) {
+ if (vnode->cb_interest->server->fs_state == 0)
+ fc->server = afs_get_server(vnode->cb_interest->server);
+ else
+ fc->ac.error = vnode->cb_interest->server->fs_state;
+ } else {
+ fc->ac.error = -ESTALE;
+ }
+ read_sequnlock_excl(&vnode->cb_lock);
+
+ return fc->ac.error;
+}
+
+/*
* pick a server to use to try accessing this volume
* - returns with an elevated usage count on the server chosen
*/
-struct afs_server *afs_volume_pick_fileserver(struct afs_vnode *vnode)
+bool afs_volume_pick_fileserver(struct afs_fs_cursor *fc, struct afs_vnode *vnode)
{
struct afs_volume *volume = vnode->volume;
struct afs_server *server;
@@ -223,19 +257,18 @@ struct afs_server *afs_volume_pick_fileserver(struct afs_vnode *vnode)
/* stick with the server we're already using if we can */
if (vnode->cb_interest && vnode->cb_interest->server->fs_state == 0) {
- afs_get_server(vnode->cb_interest->server);
- _leave(" = %p [current]", vnode->cb_interest->server);
- return vnode->cb_interest->server;
+ fc->server = afs_get_server(vnode->cb_interest->server);
+ goto set_server;
}
down_read(&volume->server_sem);
/* handle the no-server case */
if (volume->nservers == 0) {
- ret = volume->rjservers ? -ENOMEDIUM : -ESTALE;
+ fc->ac.error = volume->rjservers ? -ENOMEDIUM : -ESTALE;
up_read(&volume->server_sem);
- _leave(" = %d [no servers]", ret);
- return ERR_PTR(ret);
+ _leave(" = f [no servers %d]", fc->ac.error);
+ return false;
}
/* basically, just search the list for the first live server and use
@@ -280,13 +313,15 @@ struct afs_server *afs_volume_pick_fileserver(struct afs_vnode *vnode)
}
}
+error:
+ fc->ac.error = ret;
+
/* no available servers
* - TODO: handle the no active servers case better
*/
-error:
up_read(&volume->server_sem);
- _leave(" = %d", ret);
- return ERR_PTR(ret);
+ _leave(" = f [%d]", fc->ac.error);
+ return false;
picked_server:
/* Found an apparently healthy server. We need to register an interest
@@ -296,37 +331,41 @@ picked_server:
&volume->cb_interests[loop], server);
if (ret < 0)
goto error;
-
- afs_get_server(server);
+
+ fc->server = afs_get_server(server);
up_read(&volume->server_sem);
- _leave(" = %p (picked %pIS)",
- server, &server->addr.transport);
- return server;
+set_server:
+ fc->ac.alist = afs_get_addrlist(fc->server->addrs);
+ fc->ac.addr = &fc->ac.alist->addrs[0];
+ _debug("USING SERVER: %pIS\n", &fc->ac.addr->transport);
+ _leave(" = t (picked %pIS)", &fc->ac.addr->transport);
+ return true;
}
/*
* release a server after use
* - releases the ref on the server struct that was acquired by picking
* - records result of using a particular server to access a volume
- * - return 0 to try again, 1 if okay or to issue error
- * - the caller must release the server struct if result was 0
+ * - return true to try again, false if okay or to issue error
+ * - the caller must release the server struct if result was false
*/
-int afs_volume_release_fileserver(struct afs_vnode *vnode,
- struct afs_server *server,
- int result)
+bool afs_iterate_fs_cursor(struct afs_fs_cursor *fc,
+ struct afs_vnode *vnode)
{
struct afs_volume *volume = vnode->volume;
+ struct afs_server *server = fc->server;
unsigned loop;
_enter("%s,%pIS,%d",
- volume->vlocation->vldb.name, &server->addr.transport, result);
+ volume->vlocation->vldb.name, &fc->ac.addr->transport,
+ fc->ac.error);
- switch (result) {
+ switch (fc->ac.error) {
/* success */
case 0:
server->fs_state = 0;
- _leave("");
- return 1;
+ _leave(" = f");
+ return false;
/* the fileserver denied all knowledge of the volume */
case -ENOMEDIUM:
@@ -363,8 +402,9 @@ int afs_volume_release_fileserver(struct afs_vnode *vnode,
*/
up_write(&volume->server_sem);
afs_put_server(afs_v2net(vnode), server);
- _leave(" [completely rejected]");
- return 1;
+ fc->server = NULL;
+ _leave(" = f [completely rejected]");
+ return false;
/* problem reaching the server */
case -ENETUNREACH:
@@ -378,8 +418,8 @@ int afs_volume_release_fileserver(struct afs_vnode *vnode,
*/
spin_lock(&server->fs_lock);
if (!server->fs_state) {
- server->fs_state = result;
- printk("kAFS: SERVER DEAD state=%d\n", result);
+ server->fs_state = fc->ac.error;
+ printk("kAFS: SERVER DEAD state=%d\n", fc->ac.error);
}
spin_unlock(&server->fs_lock);
goto try_next_server;
@@ -390,8 +430,9 @@ int afs_volume_release_fileserver(struct afs_vnode *vnode,
case -ENONET:
/* tell the caller to accept the result */
afs_put_server(afs_v2net(vnode), server);
- _leave(" [local failure]");
- return 1;
+ fc->server = NULL;
+ _leave(" = f [local failure]");
+ return false;
}
/* tell the caller to loop around and try the next server */
@@ -399,6 +440,16 @@ try_next_server_upw:
up_write(&volume->server_sem);
try_next_server:
afs_put_server(afs_v2net(vnode), server);
- _leave(" [try next server]");
- return 0;
+ _leave(" = t [try next server]");
+ return true;
+}
+
+/*
+ * Clean up a fileserver cursor.
+ */
+int afs_end_fs_cursor(struct afs_fs_cursor *fc, struct afs_net *net)
+{
+ afs_end_cursor(&fc->ac);
+ afs_put_server(net, fc->server);
+ return fc->ac.error;
}