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