blob: 44aff81dc6a74fa4cf924a32586e2c91d2f2febf [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* 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 */
Yan Burmanb593e482006-12-06 20:40:32 -080058 server = kzalloc(sizeof(struct afs_server), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -070059 if (!server) {
60 _leave(" = -ENOMEM");
61 return -ENOMEM;
62 }
63
Linus Torvalds1da177e2005-04-16 15:20:36 -070064 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
Akinobu Mitaf1166292006-06-26 00:24:46 -0700125 list_move_tail(&zombie->link, &cell->sv_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126 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 */
144void 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);
Akinobu Mitaf1166292006-06-26 00:24:46 -0700169 list_move_tail(&server->link, &cell->sv_graveyard);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170
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 */
185void 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 */
239int 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 */
411void 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 */
459int 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() */