summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2017-11-02 11:27:50 -0400
committerDavid Howells <dhowells@redhat.com>2017-11-13 10:38:19 -0500
commit9cc6fc50f7bc69ac28bee45eed13cbc65a86210f (patch)
tree81f5ea54ddb8e9df3bf1fa768f9421d6aeec98e5
parent8b2a464ced77fe35be72ab7d38152a9439daf8d3 (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>
-rw-r--r--fs/afs/Makefile1
-rw-r--r--fs/afs/rotate.c254
-rw-r--r--fs/afs/volume.c250
3 files changed, 255 insertions, 250 deletions
diff --git a/fs/afs/Makefile b/fs/afs/Makefile
index 849383986d3b..192d476d7c76 100644
--- a/fs/afs/Makefile
+++ b/fs/afs/Makefile
@@ -20,6 +20,7 @@ kafs-objs := \
20 misc.o \ 20 misc.o \
21 mntpt.o \ 21 mntpt.o \
22 proc.o \ 22 proc.o \
23 rotate.o \
23 rxrpc.o \ 24 rxrpc.o \
24 security.o \ 25 security.o \
25 server.o \ 26 server.o \
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 */
19void 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 */
27int 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 */
49bool 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
115error:
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
125picked_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);
136set_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 */
151bool 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 */
238try_next_server_upw:
239 up_write(&volume->server_sem);
240try_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 */
249int 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}
diff --git a/fs/afs/volume.c b/fs/afs/volume.c
index d282cd0ff268..3c5ad1cc50f3 100644
--- a/fs/afs/volume.c
+++ b/fs/afs/volume.c
@@ -10,12 +10,7 @@
10 */ 10 */
11 11
12#include <linux/kernel.h> 12#include <linux/kernel.h>
13#include <linux/module.h>
14#include <linux/init.h>
15#include <linux/slab.h> 13#include <linux/slab.h>
16#include <linux/fs.h>
17#include <linux/pagemap.h>
18#include <linux/sched.h>
19#include "internal.h" 14#include "internal.h"
20 15
21static const char *afs_voltypes[] = { "R/W", "R/O", "BAK" }; 16static const char *afs_voltypes[] = { "R/W", "R/O", "BAK" };
@@ -208,248 +203,3 @@ void afs_put_volume(struct afs_cell *cell, struct afs_volume *volume)
208 203
209 _leave(" [destroyed]"); 204 _leave(" [destroyed]");
210} 205}
211
212/*
213 * Initialise a filesystem server cursor for iterating over FS servers.
214 */
215void afs_init_fs_cursor(struct afs_fs_cursor *fc, struct afs_vnode *vnode)
216{
217 fc->ac.alist = NULL;
218 fc->ac.addr = NULL;
219 fc->ac.start = 0;
220 fc->ac.index = 0;
221 fc->ac.error = 0;
222 fc->server = NULL;
223}
224
225/*
226 * Set a filesystem server cursor for using a specific FS server.
227 */
228int afs_set_fs_cursor(struct afs_fs_cursor *fc, struct afs_vnode *vnode)
229{
230 afs_init_fs_cursor(fc, vnode);
231
232 read_seqlock_excl(&vnode->cb_lock);
233 if (vnode->cb_interest) {
234 if (vnode->cb_interest->server->fs_state == 0)
235 fc->server = afs_get_server(vnode->cb_interest->server);
236 else
237 fc->ac.error = vnode->cb_interest->server->fs_state;
238 } else {
239 fc->ac.error = -ESTALE;
240 }
241 read_sequnlock_excl(&vnode->cb_lock);
242
243 return fc->ac.error;
244}
245
246/*
247 * pick a server to use to try accessing this volume
248 * - returns with an elevated usage count on the server chosen
249 */
250bool afs_volume_pick_fileserver(struct afs_fs_cursor *fc, struct afs_vnode *vnode)
251{
252 struct afs_volume *volume = vnode->volume;
253 struct afs_server *server;
254 int ret, state, loop;
255
256 _enter("%s", volume->vlocation->vldb.name);
257
258 /* stick with the server we're already using if we can */
259 if (vnode->cb_interest && vnode->cb_interest->server->fs_state == 0) {
260 fc->server = afs_get_server(vnode->cb_interest->server);
261 goto set_server;
262 }
263
264 down_read(&volume->server_sem);
265
266 /* handle the no-server case */
267 if (volume->nservers == 0) {
268 fc->ac.error = volume->rjservers ? -ENOMEDIUM : -ESTALE;
269 up_read(&volume->server_sem);
270 _leave(" = f [no servers %d]", fc->ac.error);
271 return false;
272 }
273
274 /* basically, just search the list for the first live server and use
275 * that */
276 ret = 0;
277 for (loop = 0; loop < volume->nservers; loop++) {
278 server = volume->servers[loop];
279 state = server->fs_state;
280
281 _debug("consider %d [%d]", loop, state);
282
283 switch (state) {
284 case 0:
285 goto picked_server;
286
287 case -ENETUNREACH:
288 if (ret == 0)
289 ret = state;
290 break;
291
292 case -EHOSTUNREACH:
293 if (ret == 0 ||
294 ret == -ENETUNREACH)
295 ret = state;
296 break;
297
298 case -ECONNREFUSED:
299 if (ret == 0 ||
300 ret == -ENETUNREACH ||
301 ret == -EHOSTUNREACH)
302 ret = state;
303 break;
304
305 default:
306 case -EREMOTEIO:
307 if (ret == 0 ||
308 ret == -ENETUNREACH ||
309 ret == -EHOSTUNREACH ||
310 ret == -ECONNREFUSED)
311 ret = state;
312 break;
313 }
314 }
315
316error:
317 fc->ac.error = ret;
318
319 /* no available servers
320 * - TODO: handle the no active servers case better
321 */
322 up_read(&volume->server_sem);
323 _leave(" = f [%d]", fc->ac.error);
324 return false;
325
326picked_server:
327 /* Found an apparently healthy server. We need to register an interest
328 * in receiving callbacks before we talk to it.
329 */
330 ret = afs_register_server_cb_interest(vnode,
331 &volume->cb_interests[loop], server);
332 if (ret < 0)
333 goto error;
334
335 fc->server = afs_get_server(server);
336 up_read(&volume->server_sem);
337set_server:
338 fc->ac.alist = afs_get_addrlist(fc->server->addrs);
339 fc->ac.addr = &fc->ac.alist->addrs[0];
340 _debug("USING SERVER: %pIS\n", &fc->ac.addr->transport);
341 _leave(" = t (picked %pIS)", &fc->ac.addr->transport);
342 return true;
343}
344
345/*
346 * release a server after use
347 * - releases the ref on the server struct that was acquired by picking
348 * - records result of using a particular server to access a volume
349 * - return true to try again, false if okay or to issue error
350 * - the caller must release the server struct if result was false
351 */
352bool afs_iterate_fs_cursor(struct afs_fs_cursor *fc,
353 struct afs_vnode *vnode)
354{
355 struct afs_volume *volume = vnode->volume;
356 struct afs_server *server = fc->server;
357 unsigned loop;
358
359 _enter("%s,%pIS,%d",
360 volume->vlocation->vldb.name, &fc->ac.addr->transport,
361 fc->ac.error);
362
363 switch (fc->ac.error) {
364 /* success */
365 case 0:
366 server->fs_state = 0;
367 _leave(" = f");
368 return false;
369
370 /* the fileserver denied all knowledge of the volume */
371 case -ENOMEDIUM:
372 down_write(&volume->server_sem);
373
374 /* firstly, find where the server is in the active list (if it
375 * is) */
376 for (loop = 0; loop < volume->nservers; loop++)
377 if (volume->servers[loop] == server)
378 goto present;
379
380 /* no longer there - may have been discarded by another op */
381 goto try_next_server_upw;
382
383 present:
384 volume->nservers--;
385 memmove(&volume->servers[loop],
386 &volume->servers[loop + 1],
387 sizeof(volume->servers[loop]) *
388 (volume->nservers - loop));
389 volume->servers[volume->nservers] = NULL;
390 afs_put_server(afs_v2net(vnode), server);
391 volume->rjservers++;
392
393 if (volume->nservers > 0)
394 /* another server might acknowledge its existence */
395 goto try_next_server_upw;
396
397 /* handle the case where all the fileservers have rejected the
398 * volume
399 * - TODO: try asking the fileservers for volume information
400 * - TODO: contact the VL server again to see if the volume is
401 * no longer registered
402 */
403 up_write(&volume->server_sem);
404 afs_put_server(afs_v2net(vnode), server);
405 fc->server = NULL;
406 _leave(" = f [completely rejected]");
407 return false;
408
409 /* problem reaching the server */
410 case -ENETUNREACH:
411 case -EHOSTUNREACH:
412 case -ECONNREFUSED:
413 case -ETIME:
414 case -ETIMEDOUT:
415 case -EREMOTEIO:
416 /* mark the server as dead
417 * TODO: vary dead timeout depending on error
418 */
419 spin_lock(&server->fs_lock);
420 if (!server->fs_state) {
421 server->fs_state = fc->ac.error;
422 printk("kAFS: SERVER DEAD state=%d\n", fc->ac.error);
423 }
424 spin_unlock(&server->fs_lock);
425 goto try_next_server;
426
427 /* miscellaneous error */
428 default:
429 case -ENOMEM:
430 case -ENONET:
431 /* tell the caller to accept the result */
432 afs_put_server(afs_v2net(vnode), server);
433 fc->server = NULL;
434 _leave(" = f [local failure]");
435 return false;
436 }
437
438 /* tell the caller to loop around and try the next server */
439try_next_server_upw:
440 up_write(&volume->server_sem);
441try_next_server:
442 afs_put_server(afs_v2net(vnode), server);
443 _leave(" = t [try next server]");
444 return true;
445}
446
447/*
448 * Clean up a fileserver cursor.
449 */
450int afs_end_fs_cursor(struct afs_fs_cursor *fc, struct afs_net *net)
451{
452 afs_end_cursor(&fc->ac);
453 afs_put_server(net, fc->server);
454 return fc->ac.error;
455}