aboutsummaryrefslogtreecommitdiffstats
path: root/fs/afs/server.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/afs/server.c')
-rw-r--r--fs/afs/server.c502
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
24DEFINE_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
29static 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
40static 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 */
49int 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 */
146void 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 */
188void 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 */
242int 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 */
414void 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 */
462int 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() */