]>
Commit | Line | Data |
---|---|---|
1 | /* RxRPC virtual connection handler | |
2 | * | |
3 | * Copyright (C) 2007 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 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
13 | ||
14 | #include <linux/module.h> | |
15 | #include <linux/slab.h> | |
16 | #include <linux/net.h> | |
17 | #include <linux/skbuff.h> | |
18 | #include <linux/crypto.h> | |
19 | #include <net/sock.h> | |
20 | #include <net/af_rxrpc.h> | |
21 | #include "ar-internal.h" | |
22 | ||
23 | /* | |
24 | * Time till a connection expires after last use (in seconds). | |
25 | */ | |
26 | unsigned int rxrpc_connection_expiry = 10 * 60; | |
27 | ||
28 | static void rxrpc_connection_reaper(struct work_struct *work); | |
29 | ||
30 | LIST_HEAD(rxrpc_connections); | |
31 | DEFINE_RWLOCK(rxrpc_connection_lock); | |
32 | static DECLARE_DELAYED_WORK(rxrpc_connection_reap, rxrpc_connection_reaper); | |
33 | ||
34 | /* | |
35 | * allocate a new connection | |
36 | */ | |
37 | static struct rxrpc_connection *rxrpc_alloc_connection(gfp_t gfp) | |
38 | { | |
39 | struct rxrpc_connection *conn; | |
40 | ||
41 | _enter(""); | |
42 | ||
43 | conn = kzalloc(sizeof(struct rxrpc_connection), gfp); | |
44 | if (conn) { | |
45 | spin_lock_init(&conn->channel_lock); | |
46 | init_waitqueue_head(&conn->channel_wq); | |
47 | INIT_WORK(&conn->processor, &rxrpc_process_connection); | |
48 | INIT_LIST_HEAD(&conn->link); | |
49 | conn->calls = RB_ROOT; | |
50 | skb_queue_head_init(&conn->rx_queue); | |
51 | conn->security = &rxrpc_no_security; | |
52 | rwlock_init(&conn->lock); | |
53 | spin_lock_init(&conn->state_lock); | |
54 | atomic_set(&conn->usage, 1); | |
55 | conn->debug_id = atomic_inc_return(&rxrpc_debug_id); | |
56 | atomic_set(&conn->avail_chans, RXRPC_MAXCALLS); | |
57 | conn->size_align = 4; | |
58 | conn->header_size = sizeof(struct rxrpc_wire_header); | |
59 | } | |
60 | ||
61 | _leave(" = %p{%d}", conn, conn ? conn->debug_id : 0); | |
62 | return conn; | |
63 | } | |
64 | ||
65 | /* | |
66 | * add a call to a connection's call-by-ID tree | |
67 | */ | |
68 | static void rxrpc_add_call_ID_to_conn(struct rxrpc_connection *conn, | |
69 | struct rxrpc_call *call) | |
70 | { | |
71 | struct rxrpc_call *xcall; | |
72 | struct rb_node *parent, **p; | |
73 | __be32 call_id; | |
74 | ||
75 | write_lock_bh(&conn->lock); | |
76 | ||
77 | call_id = call->call_id; | |
78 | p = &conn->calls.rb_node; | |
79 | parent = NULL; | |
80 | while (*p) { | |
81 | parent = *p; | |
82 | xcall = rb_entry(parent, struct rxrpc_call, conn_node); | |
83 | ||
84 | if (call_id < xcall->call_id) | |
85 | p = &(*p)->rb_left; | |
86 | else if (call_id > xcall->call_id) | |
87 | p = &(*p)->rb_right; | |
88 | else | |
89 | BUG(); | |
90 | } | |
91 | ||
92 | rb_link_node(&call->conn_node, parent, p); | |
93 | rb_insert_color(&call->conn_node, &conn->calls); | |
94 | ||
95 | write_unlock_bh(&conn->lock); | |
96 | } | |
97 | ||
98 | /* | |
99 | * Allocate a client connection. The caller must take care to clear any | |
100 | * padding bytes in *cp. | |
101 | */ | |
102 | static struct rxrpc_connection * | |
103 | rxrpc_alloc_client_connection(struct rxrpc_conn_parameters *cp, | |
104 | struct rxrpc_transport *trans, | |
105 | gfp_t gfp) | |
106 | { | |
107 | struct rxrpc_connection *conn; | |
108 | int ret; | |
109 | ||
110 | _enter(""); | |
111 | ||
112 | conn = rxrpc_alloc_connection(gfp); | |
113 | if (!conn) { | |
114 | _leave(" = -ENOMEM"); | |
115 | return ERR_PTR(-ENOMEM); | |
116 | } | |
117 | ||
118 | conn->params = *cp; | |
119 | conn->proto.local = cp->local; | |
120 | conn->proto.epoch = rxrpc_epoch; | |
121 | conn->proto.cid = 0; | |
122 | conn->proto.in_clientflag = 0; | |
123 | conn->proto.family = cp->peer->srx.transport.family; | |
124 | conn->out_clientflag = RXRPC_CLIENT_INITIATED; | |
125 | conn->state = RXRPC_CONN_CLIENT; | |
126 | ||
127 | switch (conn->proto.family) { | |
128 | case AF_INET: | |
129 | conn->proto.addr_size = sizeof(conn->proto.ipv4_addr); | |
130 | conn->proto.ipv4_addr = cp->peer->srx.transport.sin.sin_addr; | |
131 | conn->proto.port = cp->peer->srx.transport.sin.sin_port; | |
132 | break; | |
133 | } | |
134 | ||
135 | ret = rxrpc_get_client_connection_id(conn, gfp); | |
136 | if (ret < 0) | |
137 | goto error_0; | |
138 | ||
139 | ret = rxrpc_init_client_conn_security(conn); | |
140 | if (ret < 0) | |
141 | goto error_1; | |
142 | ||
143 | conn->security->prime_packet_security(conn); | |
144 | ||
145 | write_lock(&rxrpc_connection_lock); | |
146 | list_add_tail(&conn->link, &rxrpc_connections); | |
147 | write_unlock(&rxrpc_connection_lock); | |
148 | ||
149 | key_get(conn->params.key); | |
150 | conn->trans = trans; | |
151 | atomic_inc(&trans->usage); | |
152 | ||
153 | _leave(" = %p", conn); | |
154 | return conn; | |
155 | ||
156 | error_1: | |
157 | rxrpc_put_client_connection_id(conn); | |
158 | error_0: | |
159 | kfree(conn); | |
160 | _leave(" = %d", ret); | |
161 | return ERR_PTR(ret); | |
162 | } | |
163 | ||
164 | /* | |
165 | * find a connection for a call | |
166 | * - called in process context with IRQs enabled | |
167 | */ | |
168 | int rxrpc_connect_call(struct rxrpc_call *call, | |
169 | struct rxrpc_conn_parameters *cp, | |
170 | struct rxrpc_transport *trans, | |
171 | struct sockaddr_rxrpc *srx, | |
172 | gfp_t gfp) | |
173 | { | |
174 | struct rxrpc_connection *conn, *candidate = NULL; | |
175 | struct rxrpc_local *local = cp->local; | |
176 | struct rb_node *p, **pp, *parent; | |
177 | long diff; | |
178 | int chan; | |
179 | ||
180 | DECLARE_WAITQUEUE(myself, current); | |
181 | ||
182 | _enter("{%d,%lx},", call->debug_id, call->user_call_ID); | |
183 | ||
184 | cp->peer = trans->peer; | |
185 | rxrpc_get_peer(cp->peer); | |
186 | ||
187 | if (!cp->exclusive) { | |
188 | /* Search for a existing client connection unless this is going | |
189 | * to be a connection that's used exclusively for a single call. | |
190 | */ | |
191 | _debug("search 1"); | |
192 | spin_lock(&local->client_conns_lock); | |
193 | p = local->client_conns.rb_node; | |
194 | while (p) { | |
195 | conn = rb_entry(p, struct rxrpc_connection, client_node); | |
196 | ||
197 | #define cmp(X) ((long)conn->params.X - (long)cp->X) | |
198 | diff = (cmp(peer) ?: | |
199 | cmp(key) ?: | |
200 | cmp(security_level)); | |
201 | if (diff < 0) | |
202 | p = p->rb_left; | |
203 | else if (diff > 0) | |
204 | p = p->rb_right; | |
205 | else | |
206 | goto found_extant_conn; | |
207 | } | |
208 | spin_unlock(&local->client_conns_lock); | |
209 | } | |
210 | ||
211 | /* We didn't find a connection or we want an exclusive one. */ | |
212 | _debug("get new conn"); | |
213 | candidate = rxrpc_alloc_client_connection(cp, trans, gfp); | |
214 | if (!candidate) { | |
215 | _leave(" = -ENOMEM"); | |
216 | return -ENOMEM; | |
217 | } | |
218 | ||
219 | if (cp->exclusive) { | |
220 | /* Assign the call on an exclusive connection to channel 0 and | |
221 | * don't add the connection to the endpoint's shareable conn | |
222 | * lookup tree. | |
223 | */ | |
224 | _debug("exclusive chan 0"); | |
225 | conn = candidate; | |
226 | atomic_set(&conn->avail_chans, RXRPC_MAXCALLS - 1); | |
227 | spin_lock(&conn->channel_lock); | |
228 | chan = 0; | |
229 | goto found_channel; | |
230 | } | |
231 | ||
232 | /* We need to redo the search before attempting to add a new connection | |
233 | * lest we race with someone else adding a conflicting instance. | |
234 | */ | |
235 | _debug("search 2"); | |
236 | spin_lock(&local->client_conns_lock); | |
237 | ||
238 | pp = &local->client_conns.rb_node; | |
239 | parent = NULL; | |
240 | while (*pp) { | |
241 | parent = *pp; | |
242 | conn = rb_entry(parent, struct rxrpc_connection, client_node); | |
243 | ||
244 | diff = (cmp(peer) ?: | |
245 | cmp(key) ?: | |
246 | cmp(security_level)); | |
247 | if (diff < 0) | |
248 | pp = &(*pp)->rb_left; | |
249 | else if (diff > 0) | |
250 | pp = &(*pp)->rb_right; | |
251 | else | |
252 | goto found_extant_conn; | |
253 | } | |
254 | ||
255 | /* The second search also failed; simply add the new connection with | |
256 | * the new call in channel 0. Note that we need to take the channel | |
257 | * lock before dropping the client conn lock. | |
258 | */ | |
259 | _debug("new conn"); | |
260 | conn = candidate; | |
261 | candidate = NULL; | |
262 | ||
263 | rb_link_node(&conn->client_node, parent, pp); | |
264 | rb_insert_color(&conn->client_node, &local->client_conns); | |
265 | ||
266 | atomic_set(&conn->avail_chans, RXRPC_MAXCALLS - 1); | |
267 | spin_lock(&conn->channel_lock); | |
268 | spin_unlock(&local->client_conns_lock); | |
269 | chan = 0; | |
270 | ||
271 | found_channel: | |
272 | _debug("found chan"); | |
273 | call->conn = conn; | |
274 | call->channel = chan; | |
275 | call->epoch = conn->proto.epoch; | |
276 | call->cid = conn->proto.cid | chan; | |
277 | call->call_id = ++conn->call_counter; | |
278 | rcu_assign_pointer(conn->channels[chan], call); | |
279 | ||
280 | _net("CONNECT call %d on conn %d", call->debug_id, conn->debug_id); | |
281 | ||
282 | rxrpc_add_call_ID_to_conn(conn, call); | |
283 | spin_unlock(&conn->channel_lock); | |
284 | _leave(" = %p {u=%d}", conn, atomic_read(&conn->usage)); | |
285 | return 0; | |
286 | ||
287 | /* We found a suitable connection already in existence. Discard any | |
288 | * candidate we may have allocated, and try to get a channel on this | |
289 | * one. | |
290 | */ | |
291 | found_extant_conn: | |
292 | _debug("found conn"); | |
293 | rxrpc_get_connection(conn); | |
294 | spin_unlock(&local->client_conns_lock); | |
295 | ||
296 | rxrpc_put_connection(candidate); | |
297 | ||
298 | if (!atomic_add_unless(&conn->avail_chans, -1, 0)) { | |
299 | if (!gfpflags_allow_blocking(gfp)) { | |
300 | rxrpc_put_connection(conn); | |
301 | _leave(" = -EAGAIN"); | |
302 | return -EAGAIN; | |
303 | } | |
304 | ||
305 | add_wait_queue(&conn->channel_wq, &myself); | |
306 | for (;;) { | |
307 | set_current_state(TASK_INTERRUPTIBLE); | |
308 | if (atomic_add_unless(&conn->avail_chans, -1, 0)) | |
309 | break; | |
310 | if (signal_pending(current)) | |
311 | goto interrupted; | |
312 | schedule(); | |
313 | } | |
314 | remove_wait_queue(&conn->channel_wq, &myself); | |
315 | __set_current_state(TASK_RUNNING); | |
316 | } | |
317 | ||
318 | /* The connection allegedly now has a free channel and we can now | |
319 | * attach the call to it. | |
320 | */ | |
321 | spin_lock(&conn->channel_lock); | |
322 | ||
323 | for (chan = 0; chan < RXRPC_MAXCALLS; chan++) | |
324 | if (!conn->channels[chan]) | |
325 | goto found_channel; | |
326 | BUG(); | |
327 | ||
328 | interrupted: | |
329 | remove_wait_queue(&conn->channel_wq, &myself); | |
330 | __set_current_state(TASK_RUNNING); | |
331 | rxrpc_put_connection(conn); | |
332 | _leave(" = -ERESTARTSYS"); | |
333 | return -ERESTARTSYS; | |
334 | } | |
335 | ||
336 | /* | |
337 | * get a record of an incoming connection | |
338 | */ | |
339 | struct rxrpc_connection *rxrpc_incoming_connection(struct rxrpc_transport *trans, | |
340 | struct sk_buff *skb) | |
341 | { | |
342 | struct rxrpc_connection *conn, *candidate = NULL; | |
343 | struct rxrpc_skb_priv *sp = rxrpc_skb(skb); | |
344 | struct rb_node *p, **pp; | |
345 | const char *new = "old"; | |
346 | __be32 epoch; | |
347 | u32 cid; | |
348 | ||
349 | _enter(""); | |
350 | ||
351 | ASSERT(sp->hdr.flags & RXRPC_CLIENT_INITIATED); | |
352 | ||
353 | epoch = sp->hdr.epoch; | |
354 | cid = sp->hdr.cid & RXRPC_CIDMASK; | |
355 | ||
356 | /* search the connection list first */ | |
357 | read_lock_bh(&trans->conn_lock); | |
358 | ||
359 | p = trans->server_conns.rb_node; | |
360 | while (p) { | |
361 | conn = rb_entry(p, struct rxrpc_connection, service_node); | |
362 | ||
363 | _debug("maybe %x", conn->proto.cid); | |
364 | ||
365 | if (epoch < conn->proto.epoch) | |
366 | p = p->rb_left; | |
367 | else if (epoch > conn->proto.epoch) | |
368 | p = p->rb_right; | |
369 | else if (cid < conn->proto.cid) | |
370 | p = p->rb_left; | |
371 | else if (cid > conn->proto.cid) | |
372 | p = p->rb_right; | |
373 | else | |
374 | goto found_extant_connection; | |
375 | } | |
376 | read_unlock_bh(&trans->conn_lock); | |
377 | ||
378 | /* not yet present - create a candidate for a new record and then | |
379 | * redo the search */ | |
380 | candidate = rxrpc_alloc_connection(GFP_NOIO); | |
381 | if (!candidate) { | |
382 | _leave(" = -ENOMEM"); | |
383 | return ERR_PTR(-ENOMEM); | |
384 | } | |
385 | ||
386 | candidate->trans = trans; | |
387 | candidate->proto.local = trans->local; | |
388 | candidate->proto.epoch = sp->hdr.epoch; | |
389 | candidate->proto.cid = sp->hdr.cid & RXRPC_CIDMASK; | |
390 | candidate->proto.in_clientflag = RXRPC_CLIENT_INITIATED; | |
391 | candidate->params.local = trans->local; | |
392 | candidate->params.peer = trans->peer; | |
393 | candidate->params.service_id = sp->hdr.serviceId; | |
394 | candidate->security_ix = sp->hdr.securityIndex; | |
395 | candidate->out_clientflag = 0; | |
396 | candidate->state = RXRPC_CONN_SERVER; | |
397 | if (candidate->params.service_id) | |
398 | candidate->state = RXRPC_CONN_SERVER_UNSECURED; | |
399 | ||
400 | write_lock_bh(&trans->conn_lock); | |
401 | ||
402 | pp = &trans->server_conns.rb_node; | |
403 | p = NULL; | |
404 | while (*pp) { | |
405 | p = *pp; | |
406 | conn = rb_entry(p, struct rxrpc_connection, service_node); | |
407 | ||
408 | if (epoch < conn->proto.epoch) | |
409 | pp = &(*pp)->rb_left; | |
410 | else if (epoch > conn->proto.epoch) | |
411 | pp = &(*pp)->rb_right; | |
412 | else if (cid < conn->proto.cid) | |
413 | pp = &(*pp)->rb_left; | |
414 | else if (cid > conn->proto.cid) | |
415 | pp = &(*pp)->rb_right; | |
416 | else | |
417 | goto found_extant_second; | |
418 | } | |
419 | ||
420 | /* we can now add the new candidate to the list */ | |
421 | conn = candidate; | |
422 | candidate = NULL; | |
423 | rb_link_node(&conn->service_node, p, pp); | |
424 | rb_insert_color(&conn->service_node, &trans->server_conns); | |
425 | atomic_inc(&conn->trans->usage); | |
426 | ||
427 | write_unlock_bh(&trans->conn_lock); | |
428 | ||
429 | write_lock(&rxrpc_connection_lock); | |
430 | list_add_tail(&conn->link, &rxrpc_connections); | |
431 | write_unlock(&rxrpc_connection_lock); | |
432 | ||
433 | new = "new"; | |
434 | ||
435 | success: | |
436 | _net("CONNECTION %s %d {%x}", new, conn->debug_id, conn->proto.cid); | |
437 | ||
438 | _leave(" = %p {u=%d}", conn, atomic_read(&conn->usage)); | |
439 | return conn; | |
440 | ||
441 | /* we found the connection in the list immediately */ | |
442 | found_extant_connection: | |
443 | if (sp->hdr.securityIndex != conn->security_ix) { | |
444 | read_unlock_bh(&trans->conn_lock); | |
445 | goto security_mismatch; | |
446 | } | |
447 | rxrpc_get_connection(conn); | |
448 | read_unlock_bh(&trans->conn_lock); | |
449 | goto success; | |
450 | ||
451 | /* we found the connection on the second time through the list */ | |
452 | found_extant_second: | |
453 | if (sp->hdr.securityIndex != conn->security_ix) { | |
454 | write_unlock_bh(&trans->conn_lock); | |
455 | goto security_mismatch; | |
456 | } | |
457 | rxrpc_get_connection(conn); | |
458 | write_unlock_bh(&trans->conn_lock); | |
459 | kfree(candidate); | |
460 | goto success; | |
461 | ||
462 | security_mismatch: | |
463 | kfree(candidate); | |
464 | _leave(" = -EKEYREJECTED"); | |
465 | return ERR_PTR(-EKEYREJECTED); | |
466 | } | |
467 | ||
468 | /* | |
469 | * find a connection based on transport and RxRPC connection ID for an incoming | |
470 | * packet | |
471 | */ | |
472 | struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_transport *trans, | |
473 | struct sk_buff *skb) | |
474 | { | |
475 | struct rxrpc_connection *conn; | |
476 | struct rxrpc_skb_priv *sp = rxrpc_skb(skb); | |
477 | struct rb_node *p; | |
478 | u32 epoch, cid; | |
479 | ||
480 | _enter(",{%x,%x}", sp->hdr.cid, sp->hdr.flags); | |
481 | ||
482 | read_lock_bh(&trans->conn_lock); | |
483 | ||
484 | cid = sp->hdr.cid & RXRPC_CIDMASK; | |
485 | epoch = sp->hdr.epoch; | |
486 | ||
487 | if (sp->hdr.flags & RXRPC_CLIENT_INITIATED) { | |
488 | p = trans->server_conns.rb_node; | |
489 | while (p) { | |
490 | conn = rb_entry(p, struct rxrpc_connection, service_node); | |
491 | ||
492 | _debug("maybe %x", conn->proto.cid); | |
493 | ||
494 | if (epoch < conn->proto.epoch) | |
495 | p = p->rb_left; | |
496 | else if (epoch > conn->proto.epoch) | |
497 | p = p->rb_right; | |
498 | else if (cid < conn->proto.cid) | |
499 | p = p->rb_left; | |
500 | else if (cid > conn->proto.cid) | |
501 | p = p->rb_right; | |
502 | else | |
503 | goto found; | |
504 | } | |
505 | } else { | |
506 | conn = idr_find(&rxrpc_client_conn_ids, cid >> RXRPC_CIDSHIFT); | |
507 | if (conn && conn->proto.epoch == epoch) | |
508 | goto found; | |
509 | } | |
510 | ||
511 | read_unlock_bh(&trans->conn_lock); | |
512 | _leave(" = NULL"); | |
513 | return NULL; | |
514 | ||
515 | found: | |
516 | rxrpc_get_connection(conn); | |
517 | read_unlock_bh(&trans->conn_lock); | |
518 | _leave(" = %p", conn); | |
519 | return conn; | |
520 | } | |
521 | ||
522 | /* | |
523 | * Disconnect a call and clear any channel it occupies when that call | |
524 | * terminates. | |
525 | */ | |
526 | void rxrpc_disconnect_call(struct rxrpc_call *call) | |
527 | { | |
528 | struct rxrpc_connection *conn = call->conn; | |
529 | unsigned chan = call->channel; | |
530 | ||
531 | _enter("%d,%d", conn->debug_id, call->channel); | |
532 | ||
533 | if (conn->channels[chan] == call) { | |
534 | rcu_assign_pointer(conn->channels[chan], NULL); | |
535 | atomic_inc(&conn->avail_chans); | |
536 | wake_up(&conn->channel_wq); | |
537 | } | |
538 | } | |
539 | ||
540 | /* | |
541 | * release a virtual connection | |
542 | */ | |
543 | void rxrpc_put_connection(struct rxrpc_connection *conn) | |
544 | { | |
545 | if (!conn) | |
546 | return; | |
547 | ||
548 | _enter("%p{u=%d,d=%d}", | |
549 | conn, atomic_read(&conn->usage), conn->debug_id); | |
550 | ||
551 | ASSERTCMP(atomic_read(&conn->usage), >, 0); | |
552 | ||
553 | conn->put_time = ktime_get_seconds(); | |
554 | if (atomic_dec_and_test(&conn->usage)) { | |
555 | _debug("zombie"); | |
556 | rxrpc_queue_delayed_work(&rxrpc_connection_reap, 0); | |
557 | } | |
558 | ||
559 | _leave(""); | |
560 | } | |
561 | ||
562 | /* | |
563 | * destroy a virtual connection | |
564 | */ | |
565 | static void rxrpc_destroy_connection(struct rxrpc_connection *conn) | |
566 | { | |
567 | _enter("%p{%d}", conn, atomic_read(&conn->usage)); | |
568 | ||
569 | ASSERTCMP(atomic_read(&conn->usage), ==, 0); | |
570 | ||
571 | _net("DESTROY CONN %d", conn->debug_id); | |
572 | ||
573 | ASSERT(RB_EMPTY_ROOT(&conn->calls)); | |
574 | rxrpc_purge_queue(&conn->rx_queue); | |
575 | ||
576 | conn->security->clear(conn); | |
577 | key_put(conn->params.key); | |
578 | key_put(conn->server_key); | |
579 | ||
580 | rxrpc_put_transport(conn->trans); | |
581 | kfree(conn); | |
582 | _leave(""); | |
583 | } | |
584 | ||
585 | /* | |
586 | * reap dead connections | |
587 | */ | |
588 | static void rxrpc_connection_reaper(struct work_struct *work) | |
589 | { | |
590 | struct rxrpc_connection *conn, *_p; | |
591 | unsigned long now, earliest, reap_time; | |
592 | ||
593 | LIST_HEAD(graveyard); | |
594 | ||
595 | _enter(""); | |
596 | ||
597 | now = ktime_get_seconds(); | |
598 | earliest = ULONG_MAX; | |
599 | ||
600 | write_lock(&rxrpc_connection_lock); | |
601 | list_for_each_entry_safe(conn, _p, &rxrpc_connections, link) { | |
602 | _debug("reap CONN %d { u=%d,t=%ld }", | |
603 | conn->debug_id, atomic_read(&conn->usage), | |
604 | (long) now - (long) conn->put_time); | |
605 | ||
606 | if (likely(atomic_read(&conn->usage) > 0)) | |
607 | continue; | |
608 | ||
609 | if (rxrpc_conn_is_client(conn)) { | |
610 | struct rxrpc_local *local = conn->params.local; | |
611 | spin_lock(&local->client_conns_lock); | |
612 | reap_time = conn->put_time + rxrpc_connection_expiry; | |
613 | ||
614 | if (atomic_read(&conn->usage) > 0) { | |
615 | ; | |
616 | } else if (reap_time <= now) { | |
617 | list_move_tail(&conn->link, &graveyard); | |
618 | rxrpc_put_client_connection_id(conn); | |
619 | rb_erase(&conn->client_node, | |
620 | &local->client_conns); | |
621 | } else if (reap_time < earliest) { | |
622 | earliest = reap_time; | |
623 | } | |
624 | ||
625 | spin_unlock(&local->client_conns_lock); | |
626 | } else { | |
627 | write_lock_bh(&conn->trans->conn_lock); | |
628 | reap_time = conn->put_time + rxrpc_connection_expiry; | |
629 | ||
630 | if (atomic_read(&conn->usage) > 0) { | |
631 | ; | |
632 | } else if (reap_time <= now) { | |
633 | list_move_tail(&conn->link, &graveyard); | |
634 | rb_erase(&conn->service_node, | |
635 | &conn->trans->server_conns); | |
636 | } else if (reap_time < earliest) { | |
637 | earliest = reap_time; | |
638 | } | |
639 | ||
640 | write_unlock_bh(&conn->trans->conn_lock); | |
641 | } | |
642 | } | |
643 | write_unlock(&rxrpc_connection_lock); | |
644 | ||
645 | if (earliest != ULONG_MAX) { | |
646 | _debug("reschedule reaper %ld", (long) earliest - now); | |
647 | ASSERTCMP(earliest, >, now); | |
648 | rxrpc_queue_delayed_work(&rxrpc_connection_reap, | |
649 | (earliest - now) * HZ); | |
650 | } | |
651 | ||
652 | /* then destroy all those pulled out */ | |
653 | while (!list_empty(&graveyard)) { | |
654 | conn = list_entry(graveyard.next, struct rxrpc_connection, | |
655 | link); | |
656 | list_del_init(&conn->link); | |
657 | ||
658 | ASSERTCMP(atomic_read(&conn->usage), ==, 0); | |
659 | rxrpc_destroy_connection(conn); | |
660 | } | |
661 | ||
662 | _leave(""); | |
663 | } | |
664 | ||
665 | /* | |
666 | * preemptively destroy all the connection records rather than waiting for them | |
667 | * to time out | |
668 | */ | |
669 | void __exit rxrpc_destroy_all_connections(void) | |
670 | { | |
671 | _enter(""); | |
672 | ||
673 | rxrpc_connection_expiry = 0; | |
674 | cancel_delayed_work(&rxrpc_connection_reap); | |
675 | rxrpc_queue_delayed_work(&rxrpc_connection_reap, 0); | |
676 | ||
677 | _leave(""); | |
678 | } |