1 /* server.c: AFS server record management
3 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
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.
12 #include <linux/sched.h>
13 #include <linux/slab.h>
14 #include <rxrpc/peer.h>
15 #include <rxrpc/connection.h>
19 #include "transport.h"
21 #include "kafstimod.h"
24 DEFINE_SPINLOCK(afs_server_peer_lock
);
26 #define FS_SERVICE_ID 1 /* AFS Volume Location Service ID */
27 #define VL_SERVICE_ID 52 /* AFS Volume Location Service ID */
29 static void __afs_server_timeout(struct afs_timer
*timer
)
31 struct afs_server
*server
=
32 list_entry(timer
, struct afs_server
, timeout
);
34 _debug("SERVER TIMEOUT [%p{u=%d}]",
35 server
, atomic_read(&server
->usage
));
37 afs_server_do_timeout(server
);
40 static const struct afs_timer_ops afs_server_timer_ops
= {
41 .timed_out
= __afs_server_timeout
,
44 /*****************************************************************************/
46 * lookup a server record in a cell
47 * - TODO: search the cell's server list
49 int afs_server_lookup(struct afs_cell
*cell
, const struct in_addr
*addr
,
50 struct afs_server
**_server
)
52 struct afs_server
*server
, *active
, *zombie
;
55 _enter("%p,%08x,", cell
, ntohl(addr
->s_addr
));
57 /* allocate and initialise a server record */
58 server
= kmalloc(sizeof(struct afs_server
), GFP_KERNEL
);
64 memset(server
, 0, sizeof(struct afs_server
));
65 atomic_set(&server
->usage
, 1);
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
);
74 for (loop
= 0; loop
< AFS_SERVER_CONN_LIST_SIZE
; loop
++)
75 server
->fs_conn_cnt
[loop
] = 4;
77 memcpy(&server
->addr
, addr
, sizeof(struct in_addr
));
78 server
->addr
.s_addr
= addr
->s_addr
;
80 afs_timer_init(&server
->timeout
, &afs_server_timer_ops
);
83 write_lock(&cell
->sv_lock
);
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
;
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
;
97 spin_unlock(&cell
->sv_gylock
);
101 list_add_tail(&server
->link
, &cell
->sv_list
);
103 write_unlock(&cell
->sv_lock
);
106 _leave(" = 0 (%p)", server
);
109 /* found a matching active server */
111 _debug("active server");
112 afs_get_server(active
);
113 write_unlock(&cell
->sv_lock
);
118 _leave(" = 0 (%p)", active
);
121 /* found a matching server in the graveyard, so resurrect it and
122 * dispose of the new record */
124 _debug("resurrecting server");
126 list_move_tail(&zombie
->link
, &cell
->sv_list
);
127 afs_get_server(zombie
);
128 afs_kafstimod_del_timer(&zombie
->timeout
);
129 spin_unlock(&cell
->sv_gylock
);
130 write_unlock(&cell
->sv_lock
);
135 _leave(" = 0 (%p)", zombie
);
138 } /* end afs_server_lookup() */
140 /*****************************************************************************/
142 * destroy a server record
143 * - removes from the cell list
145 void afs_put_server(struct afs_server
*server
)
147 struct afs_cell
*cell
;
152 _enter("%p", server
);
157 BUG_ON(atomic_read(&server
->usage
) <= 0);
159 /* to prevent a race, the decrement and the dequeue must be effectively
161 write_lock(&cell
->sv_lock
);
163 if (likely(!atomic_dec_and_test(&server
->usage
))) {
164 write_unlock(&cell
->sv_lock
);
169 spin_lock(&cell
->sv_gylock
);
170 list_move_tail(&server
->link
, &cell
->sv_graveyard
);
172 /* time out in 10 secs */
173 afs_kafstimod_add_timer(&server
->timeout
, 10 * HZ
);
175 spin_unlock(&cell
->sv_gylock
);
176 write_unlock(&cell
->sv_lock
);
179 } /* end afs_put_server() */
181 /*****************************************************************************/
183 * timeout server record
184 * - removes from the cell's graveyard if the usage count is zero
186 void afs_server_do_timeout(struct afs_server
*server
)
188 struct rxrpc_peer
*peer
;
189 struct afs_cell
*cell
;
192 _enter("%p", server
);
196 BUG_ON(atomic_read(&server
->usage
) < 0);
198 /* remove from graveyard if still dead */
199 spin_lock(&cell
->vl_gylock
);
200 if (atomic_read(&server
->usage
) == 0)
201 list_del_init(&server
->link
);
204 spin_unlock(&cell
->vl_gylock
);
208 return; /* resurrected */
211 /* we can now destroy it properly */
214 /* uncross-point the structs under a global lock */
215 spin_lock(&afs_server_peer_lock
);
221 spin_unlock(&afs_server_peer_lock
);
223 /* finish cleaning up the server */
224 for (loop
= AFS_SERVER_CONN_LIST_SIZE
- 1; loop
>= 0; loop
--)
225 if (server
->fs_conn
[loop
])
226 rxrpc_put_connection(server
->fs_conn
[loop
]);
228 if (server
->vlserver
)
229 rxrpc_put_connection(server
->vlserver
);
233 _leave(" [destroyed]");
234 } /* end afs_server_do_timeout() */
236 /*****************************************************************************/
238 * get a callslot on a connection to the fileserver on the specified server
240 int afs_server_request_callslot(struct afs_server
*server
,
241 struct afs_server_callslot
*callslot
)
243 struct afs_server_callslot
*pcallslot
;
244 struct rxrpc_connection
*conn
;
247 _enter("%p,",server
);
249 INIT_LIST_HEAD(&callslot
->link
);
250 callslot
->task
= current
;
251 callslot
->conn
= NULL
;
252 callslot
->nconn
= -1;
258 /* get hold of a callslot first */
259 spin_lock(&server
->fs_lock
);
261 /* resurrect the server if it's death timeout has expired */
262 if (server
->fs_state
) {
263 if (time_before(jiffies
, server
->fs_dead_jif
)) {
264 ret
= server
->fs_state
;
265 spin_unlock(&server
->fs_lock
);
266 _leave(" = %d [still dead]", ret
);
270 server
->fs_state
= 0;
273 /* try and find a connection that has spare callslots */
274 for (nconn
= 0; nconn
< AFS_SERVER_CONN_LIST_SIZE
; nconn
++) {
275 if (server
->fs_conn_cnt
[nconn
] > 0) {
276 server
->fs_conn_cnt
[nconn
]--;
277 spin_unlock(&server
->fs_lock
);
278 callslot
->nconn
= nconn
;
283 /* none were available - wait interruptibly for one to become
285 set_current_state(TASK_INTERRUPTIBLE
);
286 list_add_tail(&callslot
->link
, &server
->fs_callq
);
287 spin_unlock(&server
->fs_lock
);
289 while (!callslot
->ready
&& !signal_pending(current
)) {
291 set_current_state(TASK_INTERRUPTIBLE
);
294 set_current_state(TASK_RUNNING
);
296 /* even if we were interrupted we may still be queued */
297 if (!callslot
->ready
) {
298 spin_lock(&server
->fs_lock
);
299 list_del_init(&callslot
->link
);
300 spin_unlock(&server
->fs_lock
);
303 nconn
= callslot
->nconn
;
305 /* if interrupted, we must release any slot we also got before
306 * returning an error */
307 if (signal_pending(current
)) {
312 /* if we were woken up with an error, then pass that error back to the
315 _leave(" = %d", callslot
->errno
);
316 return callslot
->errno
;
319 /* were we given a connection directly? */
320 if (callslot
->conn
) {
322 _leave(" = 0 (nc=%d)", nconn
);
326 /* got a callslot, but no connection */
329 /* need to get hold of the RxRPC connection */
330 down_write(&server
->sem
);
332 /* quick check to see if there's an outstanding error */
333 ret
= server
->fs_state
;
335 goto error_release_upw
;
337 if (server
->fs_conn
[nconn
]) {
338 /* reuse an existing connection */
339 rxrpc_get_connection(server
->fs_conn
[nconn
]);
340 callslot
->conn
= server
->fs_conn
[nconn
];
343 /* create a new connection */
344 ret
= rxrpc_create_connection(afs_transport
,
349 &server
->fs_conn
[nconn
]);
352 goto error_release_upw
;
354 callslot
->conn
= server
->fs_conn
[0];
355 rxrpc_get_connection(callslot
->conn
);
358 up_write(&server
->sem
);
363 /* handle an error occurring */
365 up_write(&server
->sem
);
368 /* either release the callslot or pass it along to another deserving
370 spin_lock(&server
->fs_lock
);
373 /* no callslot allocated */
375 else if (list_empty(&server
->fs_callq
)) {
377 server
->fs_conn_cnt
[nconn
]++;
378 spin_unlock(&server
->fs_lock
);
381 /* someone's waiting - dequeue them and wake them up */
382 pcallslot
= list_entry(server
->fs_callq
.next
,
383 struct afs_server_callslot
, link
);
384 list_del_init(&pcallslot
->link
);
386 pcallslot
->errno
= server
->fs_state
;
387 if (!pcallslot
->errno
) {
388 /* pass them out callslot details */
389 callslot
->conn
= xchg(&pcallslot
->conn
,
391 pcallslot
->nconn
= nconn
;
392 callslot
->nconn
= nconn
= -1;
394 pcallslot
->ready
= 1;
395 wake_up_process(pcallslot
->task
);
396 spin_unlock(&server
->fs_lock
);
399 rxrpc_put_connection(callslot
->conn
);
400 callslot
->conn
= NULL
;
402 _leave(" = %d", ret
);
405 } /* end afs_server_request_callslot() */
407 /*****************************************************************************/
409 * release a callslot back to the server
410 * - transfers the RxRPC connection to the next pending callslot if possible
412 void afs_server_release_callslot(struct afs_server
*server
,
413 struct afs_server_callslot
*callslot
)
415 struct afs_server_callslot
*pcallslot
;
417 _enter("{ad=%08x,cnt=%u},{%d}",
418 ntohl(server
->addr
.s_addr
),
419 server
->fs_conn_cnt
[callslot
->nconn
],
422 BUG_ON(callslot
->nconn
< 0);
424 spin_lock(&server
->fs_lock
);
426 if (list_empty(&server
->fs_callq
)) {
428 server
->fs_conn_cnt
[callslot
->nconn
]++;
429 spin_unlock(&server
->fs_lock
);
432 /* someone's waiting - dequeue them and wake them up */
433 pcallslot
= list_entry(server
->fs_callq
.next
,
434 struct afs_server_callslot
, link
);
435 list_del_init(&pcallslot
->link
);
437 pcallslot
->errno
= server
->fs_state
;
438 if (!pcallslot
->errno
) {
439 /* pass them out callslot details */
440 callslot
->conn
= xchg(&pcallslot
->conn
, callslot
->conn
);
441 pcallslot
->nconn
= callslot
->nconn
;
442 callslot
->nconn
= -1;
445 pcallslot
->ready
= 1;
446 wake_up_process(pcallslot
->task
);
447 spin_unlock(&server
->fs_lock
);
450 rxrpc_put_connection(callslot
->conn
);
453 } /* end afs_server_release_callslot() */
455 /*****************************************************************************/
457 * get a handle to a connection to the vlserver (volume location) on the
460 int afs_server_get_vlconn(struct afs_server
*server
,
461 struct rxrpc_connection
**_conn
)
463 struct rxrpc_connection
*conn
;
466 _enter("%p,", server
);
470 down_read(&server
->sem
);
472 if (server
->vlserver
) {
473 /* reuse an existing connection */
474 rxrpc_get_connection(server
->vlserver
);
475 conn
= server
->vlserver
;
476 up_read(&server
->sem
);
479 /* create a new connection */
480 up_read(&server
->sem
);
481 down_write(&server
->sem
);
482 if (!server
->vlserver
) {
483 ret
= rxrpc_create_connection(afs_transport
,
491 rxrpc_get_connection(server
->vlserver
);
492 conn
= server
->vlserver
;
494 up_write(&server
->sem
);
498 _leave(" = %d", ret
);
500 } /* end afs_server_get_vlconn() */