diff options
Diffstat (limited to 'fs/afs/server.c')
-rw-r--r-- | fs/afs/server.c | 502 |
1 files changed, 502 insertions, 0 deletions
diff --git a/fs/afs/server.c b/fs/afs/server.c new file mode 100644 index 000000000000..62b093aa41c6 --- /dev/null +++ b/fs/afs/server.c | |||
@@ -0,0 +1,502 @@ | |||
1 | /* server.c: AFS server record management | ||
2 | * | ||
3 | * Copyright (C) 2002 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 License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/sched.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include <rxrpc/peer.h> | ||
15 | #include <rxrpc/connection.h> | ||
16 | #include "volume.h" | ||
17 | #include "cell.h" | ||
18 | #include "server.h" | ||
19 | #include "transport.h" | ||
20 | #include "vlclient.h" | ||
21 | #include "kafstimod.h" | ||
22 | #include "internal.h" | ||
23 | |||
24 | DEFINE_SPINLOCK(afs_server_peer_lock); | ||
25 | |||
26 | #define FS_SERVICE_ID 1 /* AFS Volume Location Service ID */ | ||
27 | #define VL_SERVICE_ID 52 /* AFS Volume Location Service ID */ | ||
28 | |||
29 | static void __afs_server_timeout(struct afs_timer *timer) | ||
30 | { | ||
31 | struct afs_server *server = | ||
32 | list_entry(timer, struct afs_server, timeout); | ||
33 | |||
34 | _debug("SERVER TIMEOUT [%p{u=%d}]", | ||
35 | server, atomic_read(&server->usage)); | ||
36 | |||
37 | afs_server_do_timeout(server); | ||
38 | } | ||
39 | |||
40 | static const struct afs_timer_ops afs_server_timer_ops = { | ||
41 | .timed_out = __afs_server_timeout, | ||
42 | }; | ||
43 | |||
44 | /*****************************************************************************/ | ||
45 | /* | ||
46 | * lookup a server record in a cell | ||
47 | * - TODO: search the cell's server list | ||
48 | */ | ||
49 | int afs_server_lookup(struct afs_cell *cell, const struct in_addr *addr, | ||
50 | struct afs_server **_server) | ||
51 | { | ||
52 | struct afs_server *server, *active, *zombie; | ||
53 | int loop; | ||
54 | |||
55 | _enter("%p,%08x,", cell, ntohl(addr->s_addr)); | ||
56 | |||
57 | /* allocate and initialise a server record */ | ||
58 | server = kmalloc(sizeof(struct afs_server), GFP_KERNEL); | ||
59 | if (!server) { | ||
60 | _leave(" = -ENOMEM"); | ||
61 | return -ENOMEM; | ||
62 | } | ||
63 | |||
64 | memset(server, 0, sizeof(struct afs_server)); | ||
65 | atomic_set(&server->usage, 1); | ||
66 | |||
67 | INIT_LIST_HEAD(&server->link); | ||
68 | init_rwsem(&server->sem); | ||
69 | INIT_LIST_HEAD(&server->fs_callq); | ||
70 | spin_lock_init(&server->fs_lock); | ||
71 | INIT_LIST_HEAD(&server->cb_promises); | ||
72 | spin_lock_init(&server->cb_lock); | ||
73 | |||
74 | for (loop = 0; loop < AFS_SERVER_CONN_LIST_SIZE; loop++) | ||
75 | server->fs_conn_cnt[loop] = 4; | ||
76 | |||
77 | memcpy(&server->addr, addr, sizeof(struct in_addr)); | ||
78 | server->addr.s_addr = addr->s_addr; | ||
79 | |||
80 | afs_timer_init(&server->timeout, &afs_server_timer_ops); | ||
81 | |||
82 | /* add to the cell */ | ||
83 | write_lock(&cell->sv_lock); | ||
84 | |||
85 | /* check the active list */ | ||
86 | list_for_each_entry(active, &cell->sv_list, link) { | ||
87 | if (active->addr.s_addr == addr->s_addr) | ||
88 | goto use_active_server; | ||
89 | } | ||
90 | |||
91 | /* check the inactive list */ | ||
92 | spin_lock(&cell->sv_gylock); | ||
93 | list_for_each_entry(zombie, &cell->sv_graveyard, link) { | ||
94 | if (zombie->addr.s_addr == addr->s_addr) | ||
95 | goto resurrect_server; | ||
96 | } | ||
97 | spin_unlock(&cell->sv_gylock); | ||
98 | |||
99 | afs_get_cell(cell); | ||
100 | server->cell = cell; | ||
101 | list_add_tail(&server->link, &cell->sv_list); | ||
102 | |||
103 | write_unlock(&cell->sv_lock); | ||
104 | |||
105 | *_server = server; | ||
106 | _leave(" = 0 (%p)", server); | ||
107 | return 0; | ||
108 | |||
109 | /* found a matching active server */ | ||
110 | use_active_server: | ||
111 | _debug("active server"); | ||
112 | afs_get_server(active); | ||
113 | write_unlock(&cell->sv_lock); | ||
114 | |||
115 | kfree(server); | ||
116 | |||
117 | *_server = active; | ||
118 | _leave(" = 0 (%p)", active); | ||
119 | return 0; | ||
120 | |||
121 | /* found a matching server in the graveyard, so resurrect it and | ||
122 | * dispose of the new record */ | ||
123 | resurrect_server: | ||
124 | _debug("resurrecting server"); | ||
125 | |||
126 | list_del(&zombie->link); | ||
127 | list_add_tail(&zombie->link, &cell->sv_list); | ||
128 | afs_get_server(zombie); | ||
129 | afs_kafstimod_del_timer(&zombie->timeout); | ||
130 | spin_unlock(&cell->sv_gylock); | ||
131 | write_unlock(&cell->sv_lock); | ||
132 | |||
133 | kfree(server); | ||
134 | |||
135 | *_server = zombie; | ||
136 | _leave(" = 0 (%p)", zombie); | ||
137 | return 0; | ||
138 | |||
139 | } /* end afs_server_lookup() */ | ||
140 | |||
141 | /*****************************************************************************/ | ||
142 | /* | ||
143 | * destroy a server record | ||
144 | * - removes from the cell list | ||
145 | */ | ||
146 | void afs_put_server(struct afs_server *server) | ||
147 | { | ||
148 | struct afs_cell *cell; | ||
149 | |||
150 | if (!server) | ||
151 | return; | ||
152 | |||
153 | _enter("%p", server); | ||
154 | |||
155 | cell = server->cell; | ||
156 | |||
157 | /* sanity check */ | ||
158 | BUG_ON(atomic_read(&server->usage) <= 0); | ||
159 | |||
160 | /* to prevent a race, the decrement and the dequeue must be effectively | ||
161 | * atomic */ | ||
162 | write_lock(&cell->sv_lock); | ||
163 | |||
164 | if (likely(!atomic_dec_and_test(&server->usage))) { | ||
165 | write_unlock(&cell->sv_lock); | ||
166 | _leave(""); | ||
167 | return; | ||
168 | } | ||
169 | |||
170 | spin_lock(&cell->sv_gylock); | ||
171 | list_del(&server->link); | ||
172 | list_add_tail(&server->link, &cell->sv_graveyard); | ||
173 | |||
174 | /* time out in 10 secs */ | ||
175 | afs_kafstimod_add_timer(&server->timeout, 10 * HZ); | ||
176 | |||
177 | spin_unlock(&cell->sv_gylock); | ||
178 | write_unlock(&cell->sv_lock); | ||
179 | |||
180 | _leave(" [killed]"); | ||
181 | } /* end afs_put_server() */ | ||
182 | |||
183 | /*****************************************************************************/ | ||
184 | /* | ||
185 | * timeout server record | ||
186 | * - removes from the cell's graveyard if the usage count is zero | ||
187 | */ | ||
188 | void afs_server_do_timeout(struct afs_server *server) | ||
189 | { | ||
190 | struct rxrpc_peer *peer; | ||
191 | struct afs_cell *cell; | ||
192 | int loop; | ||
193 | |||
194 | _enter("%p", server); | ||
195 | |||
196 | cell = server->cell; | ||
197 | |||
198 | BUG_ON(atomic_read(&server->usage) < 0); | ||
199 | |||
200 | /* remove from graveyard if still dead */ | ||
201 | spin_lock(&cell->vl_gylock); | ||
202 | if (atomic_read(&server->usage) == 0) | ||
203 | list_del_init(&server->link); | ||
204 | else | ||
205 | server = NULL; | ||
206 | spin_unlock(&cell->vl_gylock); | ||
207 | |||
208 | if (!server) { | ||
209 | _leave(""); | ||
210 | return; /* resurrected */ | ||
211 | } | ||
212 | |||
213 | /* we can now destroy it properly */ | ||
214 | afs_put_cell(cell); | ||
215 | |||
216 | /* uncross-point the structs under a global lock */ | ||
217 | spin_lock(&afs_server_peer_lock); | ||
218 | peer = server->peer; | ||
219 | if (peer) { | ||
220 | server->peer = NULL; | ||
221 | peer->user = NULL; | ||
222 | } | ||
223 | spin_unlock(&afs_server_peer_lock); | ||
224 | |||
225 | /* finish cleaning up the server */ | ||
226 | for (loop = AFS_SERVER_CONN_LIST_SIZE - 1; loop >= 0; loop--) | ||
227 | if (server->fs_conn[loop]) | ||
228 | rxrpc_put_connection(server->fs_conn[loop]); | ||
229 | |||
230 | if (server->vlserver) | ||
231 | rxrpc_put_connection(server->vlserver); | ||
232 | |||
233 | kfree(server); | ||
234 | |||
235 | _leave(" [destroyed]"); | ||
236 | } /* end afs_server_do_timeout() */ | ||
237 | |||
238 | /*****************************************************************************/ | ||
239 | /* | ||
240 | * get a callslot on a connection to the fileserver on the specified server | ||
241 | */ | ||
242 | int afs_server_request_callslot(struct afs_server *server, | ||
243 | struct afs_server_callslot *callslot) | ||
244 | { | ||
245 | struct afs_server_callslot *pcallslot; | ||
246 | struct rxrpc_connection *conn; | ||
247 | int nconn, ret; | ||
248 | |||
249 | _enter("%p,",server); | ||
250 | |||
251 | INIT_LIST_HEAD(&callslot->link); | ||
252 | callslot->task = current; | ||
253 | callslot->conn = NULL; | ||
254 | callslot->nconn = -1; | ||
255 | callslot->ready = 0; | ||
256 | |||
257 | ret = 0; | ||
258 | conn = NULL; | ||
259 | |||
260 | /* get hold of a callslot first */ | ||
261 | spin_lock(&server->fs_lock); | ||
262 | |||
263 | /* resurrect the server if it's death timeout has expired */ | ||
264 | if (server->fs_state) { | ||
265 | if (time_before(jiffies, server->fs_dead_jif)) { | ||
266 | ret = server->fs_state; | ||
267 | spin_unlock(&server->fs_lock); | ||
268 | _leave(" = %d [still dead]", ret); | ||
269 | return ret; | ||
270 | } | ||
271 | |||
272 | server->fs_state = 0; | ||
273 | } | ||
274 | |||
275 | /* try and find a connection that has spare callslots */ | ||
276 | for (nconn = 0; nconn < AFS_SERVER_CONN_LIST_SIZE; nconn++) { | ||
277 | if (server->fs_conn_cnt[nconn] > 0) { | ||
278 | server->fs_conn_cnt[nconn]--; | ||
279 | spin_unlock(&server->fs_lock); | ||
280 | callslot->nconn = nconn; | ||
281 | goto obtained_slot; | ||
282 | } | ||
283 | } | ||
284 | |||
285 | /* none were available - wait interruptibly for one to become | ||
286 | * available */ | ||
287 | set_current_state(TASK_INTERRUPTIBLE); | ||
288 | list_add_tail(&callslot->link, &server->fs_callq); | ||
289 | spin_unlock(&server->fs_lock); | ||
290 | |||
291 | while (!callslot->ready && !signal_pending(current)) { | ||
292 | schedule(); | ||
293 | set_current_state(TASK_INTERRUPTIBLE); | ||
294 | } | ||
295 | |||
296 | set_current_state(TASK_RUNNING); | ||
297 | |||
298 | /* even if we were interrupted we may still be queued */ | ||
299 | if (!callslot->ready) { | ||
300 | spin_lock(&server->fs_lock); | ||
301 | list_del_init(&callslot->link); | ||
302 | spin_unlock(&server->fs_lock); | ||
303 | } | ||
304 | |||
305 | nconn = callslot->nconn; | ||
306 | |||
307 | /* if interrupted, we must release any slot we also got before | ||
308 | * returning an error */ | ||
309 | if (signal_pending(current)) { | ||
310 | ret = -EINTR; | ||
311 | goto error_release; | ||
312 | } | ||
313 | |||
314 | /* if we were woken up with an error, then pass that error back to the | ||
315 | * called */ | ||
316 | if (nconn < 0) { | ||
317 | _leave(" = %d", callslot->errno); | ||
318 | return callslot->errno; | ||
319 | } | ||
320 | |||
321 | /* were we given a connection directly? */ | ||
322 | if (callslot->conn) { | ||
323 | /* yes - use it */ | ||
324 | _leave(" = 0 (nc=%d)", nconn); | ||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | /* got a callslot, but no connection */ | ||
329 | obtained_slot: | ||
330 | |||
331 | /* need to get hold of the RxRPC connection */ | ||
332 | down_write(&server->sem); | ||
333 | |||
334 | /* quick check to see if there's an outstanding error */ | ||
335 | ret = server->fs_state; | ||
336 | if (ret) | ||
337 | goto error_release_upw; | ||
338 | |||
339 | if (server->fs_conn[nconn]) { | ||
340 | /* reuse an existing connection */ | ||
341 | rxrpc_get_connection(server->fs_conn[nconn]); | ||
342 | callslot->conn = server->fs_conn[nconn]; | ||
343 | } | ||
344 | else { | ||
345 | /* create a new connection */ | ||
346 | ret = rxrpc_create_connection(afs_transport, | ||
347 | htons(7000), | ||
348 | server->addr.s_addr, | ||
349 | FS_SERVICE_ID, | ||
350 | NULL, | ||
351 | &server->fs_conn[nconn]); | ||
352 | |||
353 | if (ret < 0) | ||
354 | goto error_release_upw; | ||
355 | |||
356 | callslot->conn = server->fs_conn[0]; | ||
357 | rxrpc_get_connection(callslot->conn); | ||
358 | } | ||
359 | |||
360 | up_write(&server->sem); | ||
361 | |||
362 | _leave(" = 0"); | ||
363 | return 0; | ||
364 | |||
365 | /* handle an error occurring */ | ||
366 | error_release_upw: | ||
367 | up_write(&server->sem); | ||
368 | |||
369 | error_release: | ||
370 | /* either release the callslot or pass it along to another deserving | ||
371 | * task */ | ||
372 | spin_lock(&server->fs_lock); | ||
373 | |||
374 | if (nconn < 0) { | ||
375 | /* no callslot allocated */ | ||
376 | } | ||
377 | else if (list_empty(&server->fs_callq)) { | ||
378 | /* no one waiting */ | ||
379 | server->fs_conn_cnt[nconn]++; | ||
380 | spin_unlock(&server->fs_lock); | ||
381 | } | ||
382 | else { | ||
383 | /* someone's waiting - dequeue them and wake them up */ | ||
384 | pcallslot = list_entry(server->fs_callq.next, | ||
385 | struct afs_server_callslot, link); | ||
386 | list_del_init(&pcallslot->link); | ||
387 | |||
388 | pcallslot->errno = server->fs_state; | ||
389 | if (!pcallslot->errno) { | ||
390 | /* pass them out callslot details */ | ||
391 | callslot->conn = xchg(&pcallslot->conn, | ||
392 | callslot->conn); | ||
393 | pcallslot->nconn = nconn; | ||
394 | callslot->nconn = nconn = -1; | ||
395 | } | ||
396 | pcallslot->ready = 1; | ||
397 | wake_up_process(pcallslot->task); | ||
398 | spin_unlock(&server->fs_lock); | ||
399 | } | ||
400 | |||
401 | rxrpc_put_connection(callslot->conn); | ||
402 | callslot->conn = NULL; | ||
403 | |||
404 | _leave(" = %d", ret); | ||
405 | return ret; | ||
406 | |||
407 | } /* end afs_server_request_callslot() */ | ||
408 | |||
409 | /*****************************************************************************/ | ||
410 | /* | ||
411 | * release a callslot back to the server | ||
412 | * - transfers the RxRPC connection to the next pending callslot if possible | ||
413 | */ | ||
414 | void afs_server_release_callslot(struct afs_server *server, | ||
415 | struct afs_server_callslot *callslot) | ||
416 | { | ||
417 | struct afs_server_callslot *pcallslot; | ||
418 | |||
419 | _enter("{ad=%08x,cnt=%u},{%d}", | ||
420 | ntohl(server->addr.s_addr), | ||
421 | server->fs_conn_cnt[callslot->nconn], | ||
422 | callslot->nconn); | ||
423 | |||
424 | BUG_ON(callslot->nconn < 0); | ||
425 | |||
426 | spin_lock(&server->fs_lock); | ||
427 | |||
428 | if (list_empty(&server->fs_callq)) { | ||
429 | /* no one waiting */ | ||
430 | server->fs_conn_cnt[callslot->nconn]++; | ||
431 | spin_unlock(&server->fs_lock); | ||
432 | } | ||
433 | else { | ||
434 | /* someone's waiting - dequeue them and wake them up */ | ||
435 | pcallslot = list_entry(server->fs_callq.next, | ||
436 | struct afs_server_callslot, link); | ||
437 | list_del_init(&pcallslot->link); | ||
438 | |||
439 | pcallslot->errno = server->fs_state; | ||
440 | if (!pcallslot->errno) { | ||
441 | /* pass them out callslot details */ | ||
442 | callslot->conn = xchg(&pcallslot->conn, callslot->conn); | ||
443 | pcallslot->nconn = callslot->nconn; | ||
444 | callslot->nconn = -1; | ||
445 | } | ||
446 | |||
447 | pcallslot->ready = 1; | ||
448 | wake_up_process(pcallslot->task); | ||
449 | spin_unlock(&server->fs_lock); | ||
450 | } | ||
451 | |||
452 | rxrpc_put_connection(callslot->conn); | ||
453 | |||
454 | _leave(""); | ||
455 | } /* end afs_server_release_callslot() */ | ||
456 | |||
457 | /*****************************************************************************/ | ||
458 | /* | ||
459 | * get a handle to a connection to the vlserver (volume location) on the | ||
460 | * specified server | ||
461 | */ | ||
462 | int afs_server_get_vlconn(struct afs_server *server, | ||
463 | struct rxrpc_connection **_conn) | ||
464 | { | ||
465 | struct rxrpc_connection *conn; | ||
466 | int ret; | ||
467 | |||
468 | _enter("%p,", server); | ||
469 | |||
470 | ret = 0; | ||
471 | conn = NULL; | ||
472 | down_read(&server->sem); | ||
473 | |||
474 | if (server->vlserver) { | ||
475 | /* reuse an existing connection */ | ||
476 | rxrpc_get_connection(server->vlserver); | ||
477 | conn = server->vlserver; | ||
478 | up_read(&server->sem); | ||
479 | } | ||
480 | else { | ||
481 | /* create a new connection */ | ||
482 | up_read(&server->sem); | ||
483 | down_write(&server->sem); | ||
484 | if (!server->vlserver) { | ||
485 | ret = rxrpc_create_connection(afs_transport, | ||
486 | htons(7003), | ||
487 | server->addr.s_addr, | ||
488 | VL_SERVICE_ID, | ||
489 | NULL, | ||
490 | &server->vlserver); | ||
491 | } | ||
492 | if (ret == 0) { | ||
493 | rxrpc_get_connection(server->vlserver); | ||
494 | conn = server->vlserver; | ||
495 | } | ||
496 | up_write(&server->sem); | ||
497 | } | ||
498 | |||
499 | *_conn = conn; | ||
500 | _leave(" = %d", ret); | ||
501 | return ret; | ||
502 | } /* end afs_server_get_vlconn() */ | ||