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.c647
1 files changed, 236 insertions, 411 deletions
diff --git a/fs/afs/server.c b/fs/afs/server.c
index 44aff81dc6a7..96bb23b476a2 100644
--- a/fs/afs/server.c
+++ b/fs/afs/server.c
@@ -1,6 +1,6 @@
1/* server.c: AFS server record management 1/* AFS server record management
2 * 2 *
3 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. 3 * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com) 4 * Written by David Howells (dhowells@redhat.com)
5 * 5 *
6 * This program is free software; you can redistribute it and/or 6 * This program is free software; you can redistribute it and/or
@@ -11,489 +11,314 @@
11 11
12#include <linux/sched.h> 12#include <linux/sched.h>
13#include <linux/slab.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" 14#include "internal.h"
23 15
24DEFINE_SPINLOCK(afs_server_peer_lock); 16unsigned afs_server_timeout = 10; /* server timeout in seconds */
25 17
26#define FS_SERVICE_ID 1 /* AFS Volume Location Service ID */ 18static void afs_reap_server(struct work_struct *);
27#define VL_SERVICE_ID 52 /* AFS Volume Location Service ID */
28 19
29static void __afs_server_timeout(struct afs_timer *timer) 20/* tree of all the servers, indexed by IP address */
21static struct rb_root afs_servers = RB_ROOT;
22static DEFINE_RWLOCK(afs_servers_lock);
23
24/* LRU list of all the servers not currently in use */
25static LIST_HEAD(afs_server_graveyard);
26static DEFINE_SPINLOCK(afs_server_graveyard_lock);
27static DECLARE_DELAYED_WORK(afs_server_reaper, afs_reap_server);
28
29/*
30 * install a server record in the master tree
31 */
32static int afs_install_server(struct afs_server *server)
30{ 33{
31 struct afs_server *server = 34 struct afs_server *xserver;
32 list_entry(timer, struct afs_server, timeout); 35 struct rb_node **pp, *p;
36 int ret;
33 37
34 _debug("SERVER TIMEOUT [%p{u=%d}]", 38 _enter("%p", server);
35 server, atomic_read(&server->usage));
36 39
37 afs_server_do_timeout(server); 40 write_lock(&afs_servers_lock);
38} 41
42 ret = -EEXIST;
43 pp = &afs_servers.rb_node;
44 p = NULL;
45 while (*pp) {
46 p = *pp;
47 _debug("- consider %p", p);
48 xserver = rb_entry(p, struct afs_server, master_rb);
49 if (server->addr.s_addr < xserver->addr.s_addr)
50 pp = &(*pp)->rb_left;
51 else if (server->addr.s_addr > xserver->addr.s_addr)
52 pp = &(*pp)->rb_right;
53 else
54 goto error;
55 }
39 56
40static const struct afs_timer_ops afs_server_timer_ops = { 57 rb_link_node(&server->master_rb, p, pp);
41 .timed_out = __afs_server_timeout, 58 rb_insert_color(&server->master_rb, &afs_servers);
42}; 59 ret = 0;
60
61error:
62 write_unlock(&afs_servers_lock);
63 return ret;
64}
43 65
44/*****************************************************************************/
45/* 66/*
46 * lookup a server record in a cell 67 * allocate a new server record
47 * - TODO: search the cell's server list
48 */ 68 */
49int afs_server_lookup(struct afs_cell *cell, const struct in_addr *addr, 69static struct afs_server *afs_alloc_server(struct afs_cell *cell,
50 struct afs_server **_server) 70 const struct in_addr *addr)
51{ 71{
52 struct afs_server *server, *active, *zombie; 72 struct afs_server *server;
53 int loop;
54 73
55 _enter("%p,%08x,", cell, ntohl(addr->s_addr)); 74 _enter("");
56 75
57 /* allocate and initialise a server record */
58 server = kzalloc(sizeof(struct afs_server), GFP_KERNEL); 76 server = kzalloc(sizeof(struct afs_server), GFP_KERNEL);
59 if (!server) { 77 if (server) {
60 _leave(" = -ENOMEM"); 78 atomic_set(&server->usage, 1);
61 return -ENOMEM; 79 server->cell = cell;
80
81 INIT_LIST_HEAD(&server->link);
82 INIT_LIST_HEAD(&server->grave);
83 init_rwsem(&server->sem);
84 spin_lock_init(&server->fs_lock);
85 server->fs_vnodes = RB_ROOT;
86 server->cb_promises = RB_ROOT;
87 spin_lock_init(&server->cb_lock);
88 init_waitqueue_head(&server->cb_break_waitq);
89 INIT_DELAYED_WORK(&server->cb_break_work,
90 afs_dispatch_give_up_callbacks);
91
92 memcpy(&server->addr, addr, sizeof(struct in_addr));
93 server->addr.s_addr = addr->s_addr;
62 } 94 }
63 95
64 atomic_set(&server->usage, 1); 96 _leave(" = %p{%d}", server, atomic_read(&server->usage));
65 97 return server;
66 INIT_LIST_HEAD(&server->link); 98}
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 99
76 memcpy(&server->addr, addr, sizeof(struct in_addr)); 100/*
77 server->addr.s_addr = addr->s_addr; 101 * get an FS-server record for a cell
102 */
103struct afs_server *afs_lookup_server(struct afs_cell *cell,
104 const struct in_addr *addr)
105{
106 struct afs_server *server, *candidate;
78 107
79 afs_timer_init(&server->timeout, &afs_server_timer_ops); 108 _enter("%p,"NIPQUAD_FMT, cell, NIPQUAD(addr->s_addr));
80 109
81 /* add to the cell */ 110 /* quick scan of the list to see if we already have the server */
82 write_lock(&cell->sv_lock); 111 read_lock(&cell->servers_lock);
83 112
84 /* check the active list */ 113 list_for_each_entry(server, &cell->servers, link) {
85 list_for_each_entry(active, &cell->sv_list, link) { 114 if (server->addr.s_addr == addr->s_addr)
86 if (active->addr.s_addr == addr->s_addr) 115 goto found_server_quickly;
87 goto use_active_server;
88 } 116 }
117 read_unlock(&cell->servers_lock);
89 118
90 /* check the inactive list */ 119 candidate = afs_alloc_server(cell, addr);
91 spin_lock(&cell->sv_gylock); 120 if (!candidate) {
92 list_for_each_entry(zombie, &cell->sv_graveyard, link) { 121 _leave(" = -ENOMEM");
93 if (zombie->addr.s_addr == addr->s_addr) 122 return ERR_PTR(-ENOMEM);
94 goto resurrect_server;
95 } 123 }
96 spin_unlock(&cell->sv_gylock);
97 124
98 afs_get_cell(cell); 125 write_lock(&cell->servers_lock);
99 server->cell = cell;
100 list_add_tail(&server->link, &cell->sv_list);
101 126
102 write_unlock(&cell->sv_lock); 127 /* check the cell's server list again */
128 list_for_each_entry(server, &cell->servers, link) {
129 if (server->addr.s_addr == addr->s_addr)
130 goto found_server;
131 }
103 132
104 *_server = server; 133 _debug("new");
105 _leave(" = 0 (%p)", server); 134 server = candidate;
106 return 0; 135 if (afs_install_server(server) < 0)
136 goto server_in_two_cells;
107 137
108 /* found a matching active server */ 138 afs_get_cell(cell);
109 use_active_server: 139 list_add_tail(&server->link, &cell->servers);
110 _debug("active server"); 140
111 afs_get_server(active); 141 write_unlock(&cell->servers_lock);
112 write_unlock(&cell->sv_lock); 142 _leave(" = %p{%d}", server, atomic_read(&server->usage));
143 return server;
144
145 /* found a matching server quickly */
146found_server_quickly:
147 _debug("found quickly");
148 afs_get_server(server);
149 read_unlock(&cell->servers_lock);
150no_longer_unused:
151 if (!list_empty(&server->grave)) {
152 spin_lock(&afs_server_graveyard_lock);
153 list_del_init(&server->grave);
154 spin_unlock(&afs_server_graveyard_lock);
155 }
156 _leave(" = %p{%d}", server, atomic_read(&server->usage));
157 return server;
158
159 /* found a matching server on the second pass */
160found_server:
161 _debug("found");
162 afs_get_server(server);
163 write_unlock(&cell->servers_lock);
164 kfree(candidate);
165 goto no_longer_unused;
166
167 /* found a server that seems to be in two cells */
168server_in_two_cells:
169 write_unlock(&cell->servers_lock);
170 kfree(candidate);
171 printk(KERN_NOTICE "kAFS:"
172 " Server "NIPQUAD_FMT" appears to be in two cells\n",
173 NIPQUAD(*addr));
174 _leave(" = -EEXIST");
175 return ERR_PTR(-EEXIST);
176}
113 177
114 kfree(server); 178/*
179 * look up a server by its IP address
180 */
181struct afs_server *afs_find_server(const struct in_addr *_addr)
182{
183 struct afs_server *server = NULL;
184 struct rb_node *p;
185 struct in_addr addr = *_addr;
115 186
116 *_server = active; 187 _enter(NIPQUAD_FMT, NIPQUAD(addr.s_addr));
117 _leave(" = 0 (%p)", active);
118 return 0;
119 188
120 /* found a matching server in the graveyard, so resurrect it and 189 read_lock(&afs_servers_lock);
121 * dispose of the new record */
122 resurrect_server:
123 _debug("resurrecting server");
124 190
125 list_move_tail(&zombie->link, &cell->sv_list); 191 p = afs_servers.rb_node;
126 afs_get_server(zombie); 192 while (p) {
127 afs_kafstimod_del_timer(&zombie->timeout); 193 server = rb_entry(p, struct afs_server, master_rb);
128 spin_unlock(&cell->sv_gylock);
129 write_unlock(&cell->sv_lock);
130 194
131 kfree(server); 195 _debug("- consider %p", p);
132 196
133 *_server = zombie; 197 if (addr.s_addr < server->addr.s_addr) {
134 _leave(" = 0 (%p)", zombie); 198 p = p->rb_left;
135 return 0; 199 } else if (addr.s_addr > server->addr.s_addr) {
200 p = p->rb_right;
201 } else {
202 afs_get_server(server);
203 goto found;
204 }
205 }
136 206
137} /* end afs_server_lookup() */ 207 server = NULL;
208found:
209 read_unlock(&afs_servers_lock);
210 ASSERTIFCMP(server, server->addr.s_addr, ==, addr.s_addr);
211 _leave(" = %p", server);
212 return server;
213}
138 214
139/*****************************************************************************/
140/* 215/*
141 * destroy a server record 216 * destroy a server record
142 * - removes from the cell list 217 * - removes from the cell list
143 */ 218 */
144void afs_put_server(struct afs_server *server) 219void afs_put_server(struct afs_server *server)
145{ 220{
146 struct afs_cell *cell;
147
148 if (!server) 221 if (!server)
149 return; 222 return;
150 223
151 _enter("%p", server); 224 _enter("%p{%d}", server, atomic_read(&server->usage));
152
153 cell = server->cell;
154 225
155 /* sanity check */ 226 _debug("PUT SERVER %d", atomic_read(&server->usage));
156 BUG_ON(atomic_read(&server->usage) <= 0);
157 227
158 /* to prevent a race, the decrement and the dequeue must be effectively 228 ASSERTCMP(atomic_read(&server->usage), >, 0);
159 * atomic */
160 write_lock(&cell->sv_lock);
161 229
162 if (likely(!atomic_dec_and_test(&server->usage))) { 230 if (likely(!atomic_dec_and_test(&server->usage))) {
163 write_unlock(&cell->sv_lock);
164 _leave(""); 231 _leave("");
165 return; 232 return;
166 } 233 }
167 234
168 spin_lock(&cell->sv_gylock); 235 afs_flush_callback_breaks(server);
169 list_move_tail(&server->link, &cell->sv_graveyard);
170 236
171 /* time out in 10 secs */ 237 spin_lock(&afs_server_graveyard_lock);
172 afs_kafstimod_add_timer(&server->timeout, 10 * HZ); 238 if (atomic_read(&server->usage) == 0) {
173 239 list_move_tail(&server->grave, &afs_server_graveyard);
174 spin_unlock(&cell->sv_gylock); 240 server->time_of_death = get_seconds();
175 write_unlock(&cell->sv_lock); 241 schedule_delayed_work(&afs_server_reaper,
176 242 afs_server_timeout * HZ);
177 _leave(" [killed]"); 243 }
178} /* end afs_put_server() */ 244 spin_unlock(&afs_server_graveyard_lock);
245 _leave(" [dead]");
246}
179 247
180/*****************************************************************************/
181/* 248/*
182 * timeout server record 249 * destroy a dead server
183 * - removes from the cell's graveyard if the usage count is zero
184 */ 250 */
185void afs_server_do_timeout(struct afs_server *server) 251static void afs_destroy_server(struct afs_server *server)
186{ 252{
187 struct rxrpc_peer *peer;
188 struct afs_cell *cell;
189 int loop;
190
191 _enter("%p", server); 253 _enter("%p", server);
192 254
193 cell = server->cell; 255 ASSERTCMP(server->fs_vnodes.rb_node, ==, NULL);
194 256 ASSERTCMP(server->cb_promises.rb_node, ==, NULL);
195 BUG_ON(atomic_read(&server->usage) < 0); 257 ASSERTCMP(server->cb_break_head, ==, server->cb_break_tail);
196 258 ASSERTCMP(atomic_read(&server->cb_break_n), ==, 0);
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 259
260 afs_put_cell(server->cell);
230 kfree(server); 261 kfree(server);
262}
231 263
232 _leave(" [destroyed]");
233} /* end afs_server_do_timeout() */
234
235/*****************************************************************************/
236/* 264/*
237 * get a callslot on a connection to the fileserver on the specified server 265 * reap dead server records
238 */ 266 */
239int afs_server_request_callslot(struct afs_server *server, 267static void afs_reap_server(struct work_struct *work)
240 struct afs_server_callslot *callslot)
241{ 268{
242 struct afs_server_callslot *pcallslot; 269 LIST_HEAD(corpses);
243 struct rxrpc_connection *conn; 270 struct afs_server *server;
244 int nconn, ret; 271 unsigned long delay, expiry;
245 272 time_t now;
246 _enter("%p,",server); 273
247 274 now = get_seconds();
248 INIT_LIST_HEAD(&callslot->link); 275 spin_lock(&afs_server_graveyard_lock);
249 callslot->task = current; 276
250 callslot->conn = NULL; 277 while (!list_empty(&afs_server_graveyard)) {
251 callslot->nconn = -1; 278 server = list_entry(afs_server_graveyard.next,
252 callslot->ready = 0; 279 struct afs_server, grave);
253 280
254 ret = 0; 281 /* the queue is ordered most dead first */
255 conn = NULL; 282 expiry = server->time_of_death + afs_server_timeout;
256 283 if (expiry > now) {
257 /* get hold of a callslot first */ 284 delay = (expiry - now) * HZ;
258 spin_lock(&server->fs_lock); 285 if (!schedule_delayed_work(&afs_server_reaper, delay)) {
259 286 cancel_delayed_work(&afs_server_reaper);
260 /* resurrect the server if it's death timeout has expired */ 287 schedule_delayed_work(&afs_server_reaper,
261 if (server->fs_state) { 288 delay);
262 if (time_before(jiffies, server->fs_dead_jif)) { 289 }
263 ret = server->fs_state; 290 break;
264 spin_unlock(&server->fs_lock);
265 _leave(" = %d [still dead]", ret);
266 return ret;
267 } 291 }
268 292
269 server->fs_state = 0; 293 write_lock(&server->cell->servers_lock);
270 } 294 write_lock(&afs_servers_lock);
271 295 if (atomic_read(&server->usage) > 0) {
272 /* try and find a connection that has spare callslots */ 296 list_del_init(&server->grave);
273 for (nconn = 0; nconn < AFS_SERVER_CONN_LIST_SIZE; nconn++) { 297 } else {
274 if (server->fs_conn_cnt[nconn] > 0) { 298 list_move_tail(&server->grave, &corpses);
275 server->fs_conn_cnt[nconn]--; 299 list_del_init(&server->link);
276 spin_unlock(&server->fs_lock); 300 rb_erase(&server->master_rb, &afs_servers);
277 callslot->nconn = nconn;
278 goto obtained_slot;
279 } 301 }
302 write_unlock(&afs_servers_lock);
303 write_unlock(&server->cell->servers_lock);
280 } 304 }
281 305
282 /* none were available - wait interruptibly for one to become 306 spin_unlock(&afs_server_graveyard_lock);
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 307
304 /* if interrupted, we must release any slot we also got before 308 /* now reap the corpses we've extracted */
305 * returning an error */ 309 while (!list_empty(&corpses)) {
306 if (signal_pending(current)) { 310 server = list_entry(corpses.next, struct afs_server, grave);
307 ret = -EINTR; 311 list_del(&server->grave);
308 goto error_release; 312 afs_destroy_server(server);
309 } 313 }
314}
310 315
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/* 316/*
456 * get a handle to a connection to the vlserver (volume location) on the 317 * discard all the server records for rmmod
457 * specified server
458 */ 318 */
459int afs_server_get_vlconn(struct afs_server *server, 319void __exit afs_purge_servers(void)
460 struct rxrpc_connection **_conn)
461{ 320{
462 struct rxrpc_connection *conn; 321 afs_server_timeout = 0;
463 int ret; 322 cancel_delayed_work(&afs_server_reaper);
464 323 schedule_delayed_work(&afs_server_reaper, 0);
465 _enter("%p,", server); 324}
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() */