diff options
author | David Howells <dhowells@redhat.com> | 2017-11-02 11:27:50 -0400 |
---|---|---|
committer | David Howells <dhowells@redhat.com> | 2017-11-13 10:38:19 -0500 |
commit | 9cc6fc50f7bc69ac28bee45eed13cbc65a86210f (patch) | |
tree | 81f5ea54ddb8e9df3bf1fa768f9421d6aeec98e5 /fs/afs/rotate.c | |
parent | 8b2a464ced77fe35be72ab7d38152a9439daf8d3 (diff) |
afs: Move server rotation code into its own file
Move server rotation code into its own file.
Signed-off-by: David Howells <dhowells@redhat.com>
Diffstat (limited to 'fs/afs/rotate.c')
-rw-r--r-- | fs/afs/rotate.c | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/fs/afs/rotate.c b/fs/afs/rotate.c new file mode 100644 index 000000000000..c7975b3ba59a --- /dev/null +++ b/fs/afs/rotate.c | |||
@@ -0,0 +1,254 @@ | |||
1 | /* Handle fileserver selection and rotation. | ||
2 | * | ||
3 | * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public Licence | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the Licence, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include "internal.h" | ||
15 | |||
16 | /* | ||
17 | * Initialise a filesystem server cursor for iterating over FS servers. | ||
18 | */ | ||
19 | void afs_init_fs_cursor(struct afs_fs_cursor *fc, struct afs_vnode *vnode) | ||
20 | { | ||
21 | memset(fc, 0, sizeof(*fc)); | ||
22 | } | ||
23 | |||
24 | /* | ||
25 | * Set a filesystem server cursor for using a specific FS server. | ||
26 | */ | ||
27 | int afs_set_fs_cursor(struct afs_fs_cursor *fc, struct afs_vnode *vnode) | ||
28 | { | ||
29 | afs_init_fs_cursor(fc, vnode); | ||
30 | |||
31 | read_seqlock_excl(&vnode->cb_lock); | ||
32 | if (vnode->cb_interest) { | ||
33 | if (vnode->cb_interest->server->fs_state == 0) | ||
34 | fc->server = afs_get_server(vnode->cb_interest->server); | ||
35 | else | ||
36 | fc->ac.error = vnode->cb_interest->server->fs_state; | ||
37 | } else { | ||
38 | fc->ac.error = -ESTALE; | ||
39 | } | ||
40 | read_sequnlock_excl(&vnode->cb_lock); | ||
41 | |||
42 | return fc->ac.error; | ||
43 | } | ||
44 | |||
45 | /* | ||
46 | * pick a server to use to try accessing this volume | ||
47 | * - returns with an elevated usage count on the server chosen | ||
48 | */ | ||
49 | bool afs_volume_pick_fileserver(struct afs_fs_cursor *fc, struct afs_vnode *vnode) | ||
50 | { | ||
51 | struct afs_volume *volume = vnode->volume; | ||
52 | struct afs_server *server; | ||
53 | int ret, state, loop; | ||
54 | |||
55 | _enter("%s", volume->vlocation->vldb.name); | ||
56 | |||
57 | /* stick with the server we're already using if we can */ | ||
58 | if (vnode->cb_interest && vnode->cb_interest->server->fs_state == 0) { | ||
59 | fc->server = afs_get_server(vnode->cb_interest->server); | ||
60 | goto set_server; | ||
61 | } | ||
62 | |||
63 | down_read(&volume->server_sem); | ||
64 | |||
65 | /* handle the no-server case */ | ||
66 | if (volume->nservers == 0) { | ||
67 | fc->ac.error = volume->rjservers ? -ENOMEDIUM : -ESTALE; | ||
68 | up_read(&volume->server_sem); | ||
69 | _leave(" = f [no servers %d]", fc->ac.error); | ||
70 | return false; | ||
71 | } | ||
72 | |||
73 | /* basically, just search the list for the first live server and use | ||
74 | * that */ | ||
75 | ret = 0; | ||
76 | for (loop = 0; loop < volume->nservers; loop++) { | ||
77 | server = volume->servers[loop]; | ||
78 | state = server->fs_state; | ||
79 | |||
80 | _debug("consider %d [%d]", loop, state); | ||
81 | |||
82 | switch (state) { | ||
83 | case 0: | ||
84 | goto picked_server; | ||
85 | |||
86 | case -ENETUNREACH: | ||
87 | if (ret == 0) | ||
88 | ret = state; | ||
89 | break; | ||
90 | |||
91 | case -EHOSTUNREACH: | ||
92 | if (ret == 0 || | ||
93 | ret == -ENETUNREACH) | ||
94 | ret = state; | ||
95 | break; | ||
96 | |||
97 | case -ECONNREFUSED: | ||
98 | if (ret == 0 || | ||
99 | ret == -ENETUNREACH || | ||
100 | ret == -EHOSTUNREACH) | ||
101 | ret = state; | ||
102 | break; | ||
103 | |||
104 | default: | ||
105 | case -EREMOTEIO: | ||
106 | if (ret == 0 || | ||
107 | ret == -ENETUNREACH || | ||
108 | ret == -EHOSTUNREACH || | ||
109 | ret == -ECONNREFUSED) | ||
110 | ret = state; | ||
111 | break; | ||
112 | } | ||
113 | } | ||
114 | |||
115 | error: | ||
116 | fc->ac.error = ret; | ||
117 | |||
118 | /* no available servers | ||
119 | * - TODO: handle the no active servers case better | ||
120 | */ | ||
121 | up_read(&volume->server_sem); | ||
122 | _leave(" = f [%d]", fc->ac.error); | ||
123 | return false; | ||
124 | |||
125 | picked_server: | ||
126 | /* Found an apparently healthy server. We need to register an interest | ||
127 | * in receiving callbacks before we talk to it. | ||
128 | */ | ||
129 | ret = afs_register_server_cb_interest(vnode, | ||
130 | &volume->cb_interests[loop], server); | ||
131 | if (ret < 0) | ||
132 | goto error; | ||
133 | |||
134 | fc->server = afs_get_server(server); | ||
135 | up_read(&volume->server_sem); | ||
136 | set_server: | ||
137 | fc->ac.alist = afs_get_addrlist(fc->server->addrs); | ||
138 | fc->ac.addr = &fc->ac.alist->addrs[0]; | ||
139 | _debug("USING SERVER: %pIS\n", &fc->ac.addr->transport); | ||
140 | _leave(" = t (picked %pIS)", &fc->ac.addr->transport); | ||
141 | return true; | ||
142 | } | ||
143 | |||
144 | /* | ||
145 | * release a server after use | ||
146 | * - releases the ref on the server struct that was acquired by picking | ||
147 | * - records result of using a particular server to access a volume | ||
148 | * - return true to try again, false if okay or to issue error | ||
149 | * - the caller must release the server struct if result was false | ||
150 | */ | ||
151 | bool afs_iterate_fs_cursor(struct afs_fs_cursor *fc, | ||
152 | struct afs_vnode *vnode) | ||
153 | { | ||
154 | struct afs_volume *volume = vnode->volume; | ||
155 | struct afs_server *server = fc->server; | ||
156 | unsigned loop; | ||
157 | |||
158 | _enter("%s,%pIS,%d", | ||
159 | volume->vlocation->vldb.name, &fc->ac.addr->transport, | ||
160 | fc->ac.error); | ||
161 | |||
162 | switch (fc->ac.error) { | ||
163 | /* success */ | ||
164 | case 0: | ||
165 | server->fs_state = 0; | ||
166 | _leave(" = f"); | ||
167 | return false; | ||
168 | |||
169 | /* the fileserver denied all knowledge of the volume */ | ||
170 | case -ENOMEDIUM: | ||
171 | down_write(&volume->server_sem); | ||
172 | |||
173 | /* firstly, find where the server is in the active list (if it | ||
174 | * is) */ | ||
175 | for (loop = 0; loop < volume->nservers; loop++) | ||
176 | if (volume->servers[loop] == server) | ||
177 | goto present; | ||
178 | |||
179 | /* no longer there - may have been discarded by another op */ | ||
180 | goto try_next_server_upw; | ||
181 | |||
182 | present: | ||
183 | volume->nservers--; | ||
184 | memmove(&volume->servers[loop], | ||
185 | &volume->servers[loop + 1], | ||
186 | sizeof(volume->servers[loop]) * | ||
187 | (volume->nservers - loop)); | ||
188 | volume->servers[volume->nservers] = NULL; | ||
189 | afs_put_server(afs_v2net(vnode), server); | ||
190 | volume->rjservers++; | ||
191 | |||
192 | if (volume->nservers > 0) | ||
193 | /* another server might acknowledge its existence */ | ||
194 | goto try_next_server_upw; | ||
195 | |||
196 | /* handle the case where all the fileservers have rejected the | ||
197 | * volume | ||
198 | * - TODO: try asking the fileservers for volume information | ||
199 | * - TODO: contact the VL server again to see if the volume is | ||
200 | * no longer registered | ||
201 | */ | ||
202 | up_write(&volume->server_sem); | ||
203 | afs_put_server(afs_v2net(vnode), server); | ||
204 | fc->server = NULL; | ||
205 | _leave(" = f [completely rejected]"); | ||
206 | return false; | ||
207 | |||
208 | /* problem reaching the server */ | ||
209 | case -ENETUNREACH: | ||
210 | case -EHOSTUNREACH: | ||
211 | case -ECONNREFUSED: | ||
212 | case -ETIME: | ||
213 | case -ETIMEDOUT: | ||
214 | case -EREMOTEIO: | ||
215 | /* mark the server as dead | ||
216 | * TODO: vary dead timeout depending on error | ||
217 | */ | ||
218 | spin_lock(&server->fs_lock); | ||
219 | if (!server->fs_state) { | ||
220 | server->fs_state = fc->ac.error; | ||
221 | printk("kAFS: SERVER DEAD state=%d\n", fc->ac.error); | ||
222 | } | ||
223 | spin_unlock(&server->fs_lock); | ||
224 | goto try_next_server; | ||
225 | |||
226 | /* miscellaneous error */ | ||
227 | default: | ||
228 | case -ENOMEM: | ||
229 | case -ENONET: | ||
230 | /* tell the caller to accept the result */ | ||
231 | afs_put_server(afs_v2net(vnode), server); | ||
232 | fc->server = NULL; | ||
233 | _leave(" = f [local failure]"); | ||
234 | return false; | ||
235 | } | ||
236 | |||
237 | /* tell the caller to loop around and try the next server */ | ||
238 | try_next_server_upw: | ||
239 | up_write(&volume->server_sem); | ||
240 | try_next_server: | ||
241 | afs_put_server(afs_v2net(vnode), server); | ||
242 | _leave(" = t [try next server]"); | ||
243 | return true; | ||
244 | } | ||
245 | |||
246 | /* | ||
247 | * Clean up a fileserver cursor. | ||
248 | */ | ||
249 | int afs_end_fs_cursor(struct afs_fs_cursor *fc, struct afs_net *net) | ||
250 | { | ||
251 | afs_end_cursor(&fc->ac); | ||
252 | afs_put_server(net, fc->server); | ||
253 | return fc->ac.error; | ||
254 | } | ||