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