blob: 2c2456ff2853d7c065778e4cd858d52a3c8c8843 [file] [log] [blame]
David Howells17926a72007-04-26 15:48:28 -07001/* 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
Joe Perches9b6d5392016-06-02 12:08:52 -070012#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13
David Howells17926a72007-04-26 15:48:28 -070014#include <linux/module.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090015#include <linux/slab.h>
David Howells17926a72007-04-26 15:48:28 -070016#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
David Howells5873c082014-02-07 18:58:44 +000023/*
24 * Time till a connection expires after last use (in seconds).
25 */
David Howellsdad8aff2016-03-09 23:22:56 +000026unsigned int rxrpc_connection_expiry = 10 * 60;
David Howells5873c082014-02-07 18:58:44 +000027
David Howells17926a72007-04-26 15:48:28 -070028static void rxrpc_connection_reaper(struct work_struct *work);
29
30LIST_HEAD(rxrpc_connections);
31DEFINE_RWLOCK(rxrpc_connection_lock);
David Howells17926a72007-04-26 15:48:28 -070032static DECLARE_DELAYED_WORK(rxrpc_connection_reap, rxrpc_connection_reaper);
33
34/*
David Howells17926a72007-04-26 15:48:28 -070035 * allocate a new connection
36 */
37static 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) {
David Howells999b69f2016-06-17 15:42:35 +010045 spin_lock_init(&conn->channel_lock);
46 init_waitqueue_head(&conn->channel_wq);
David Howells17926a72007-04-26 15:48:28 -070047 INIT_WORK(&conn->processor, &rxrpc_process_connection);
David Howells999b69f2016-06-17 15:42:35 +010048 INIT_LIST_HEAD(&conn->link);
David Howells17926a72007-04-26 15:48:28 -070049 conn->calls = RB_ROOT;
50 skb_queue_head_init(&conn->rx_queue);
David Howellse0e4d822016-04-07 17:23:58 +010051 conn->security = &rxrpc_no_security;
David Howells17926a72007-04-26 15:48:28 -070052 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);
David Howells999b69f2016-06-17 15:42:35 +010056 atomic_set(&conn->avail_chans, RXRPC_MAXCALLS);
David Howells17926a72007-04-26 15:48:28 -070057 conn->size_align = 4;
David Howells0d12f8a2016-03-04 15:53:46 +000058 conn->header_size = sizeof(struct rxrpc_wire_header);
David Howells17926a72007-04-26 15:48:28 -070059 }
60
Adrian Bunk16c61ad2007-06-15 15:15:43 -070061 _leave(" = %p{%d}", conn, conn ? conn->debug_id : 0);
David Howells17926a72007-04-26 15:48:28 -070062 return conn;
63}
64
65/*
David Howells17926a72007-04-26 15:48:28 -070066 * add a call to a connection's call-by-ID tree
67 */
68static 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;
David Howells88b99d02016-07-01 08:27:42 +010073 u32 call_id;
David Howells17926a72007-04-26 15:48:28 -070074
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/*
David Howells999b69f2016-06-17 15:42:35 +010099 * Allocate a client connection. The caller must take care to clear any
100 * padding bytes in *cp.
David Howells4a3388c2016-04-04 14:00:37 +0100101 */
102static struct rxrpc_connection *
David Howellsaa390bb2016-06-17 10:06:56 +0100103rxrpc_alloc_client_connection(struct rxrpc_conn_parameters *cp, gfp_t gfp)
David Howells4a3388c2016-04-04 14:00:37 +0100104{
105 struct rxrpc_connection *conn;
106 int ret;
107
108 _enter("");
109
110 conn = rxrpc_alloc_connection(gfp);
111 if (!conn) {
112 _leave(" = -ENOMEM");
113 return ERR_PTR(-ENOMEM);
114 }
115
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;
124
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
David Howells999b69f2016-06-17 15:42:35 +0100133 ret = rxrpc_get_client_connection_id(conn, gfp);
David Howells4a3388c2016-04-04 14:00:37 +0100134 if (ret < 0)
135 goto error_0;
136
137 ret = rxrpc_init_client_conn_security(conn);
138 if (ret < 0)
139 goto error_1;
140
141 conn->security->prime_packet_security(conn);
142
143 write_lock(&rxrpc_connection_lock);
144 list_add_tail(&conn->link, &rxrpc_connections);
145 write_unlock(&rxrpc_connection_lock);
146
David Howellsaa390bb2016-06-17 10:06:56 +0100147 /* We steal the caller's peer ref. */
148 cp->peer = NULL;
149 rxrpc_get_local(conn->params.local);
David Howells4a3388c2016-04-04 14:00:37 +0100150 key_get(conn->params.key);
151
152 _leave(" = %p", conn);
153 return conn;
154
155error_1:
156 rxrpc_put_client_connection_id(conn);
157error_0:
158 kfree(conn);
159 _leave(" = %d", ret);
160 return ERR_PTR(ret);
161}
162
163/*
David Howells17926a72007-04-26 15:48:28 -0700164 * find a connection for a call
165 * - called in process context with IRQs enabled
166 */
David Howells999b69f2016-06-17 15:42:35 +0100167int rxrpc_connect_call(struct rxrpc_call *call,
David Howells19ffa012016-04-04 14:00:36 +0100168 struct rxrpc_conn_parameters *cp,
David Howells999b69f2016-06-17 15:42:35 +0100169 struct sockaddr_rxrpc *srx,
David Howells17926a72007-04-26 15:48:28 -0700170 gfp_t gfp)
171{
David Howells999b69f2016-06-17 15:42:35 +0100172 struct rxrpc_connection *conn, *candidate = NULL;
173 struct rxrpc_local *local = cp->local;
174 struct rb_node *p, **pp, *parent;
175 long diff;
David Howells4a3388c2016-04-04 14:00:37 +0100176 int chan;
David Howells17926a72007-04-26 15:48:28 -0700177
178 DECLARE_WAITQUEUE(myself, current);
179
David Howells999b69f2016-06-17 15:42:35 +0100180 _enter("{%d,%lx},", call->debug_id, call->user_call_ID);
David Howells17926a72007-04-26 15:48:28 -0700181
David Howellsaa390bb2016-06-17 10:06:56 +0100182 cp->peer = rxrpc_lookup_peer(cp->local, srx, gfp);
183 if (!cp->peer)
184 return -ENOMEM;
David Howells17926a72007-04-26 15:48:28 -0700185
David Howells999b69f2016-06-17 15:42:35 +0100186 if (!cp->exclusive) {
187 /* Search for a existing client connection unless this is going
188 * to be a connection that's used exclusively for a single call.
189 */
190 _debug("search 1");
191 spin_lock(&local->client_conns_lock);
192 p = local->client_conns.rb_node;
193 while (p) {
194 conn = rb_entry(p, struct rxrpc_connection, client_node);
195
196#define cmp(X) ((long)conn->params.X - (long)cp->X)
197 diff = (cmp(peer) ?:
198 cmp(key) ?:
199 cmp(security_level));
200 if (diff < 0)
201 p = p->rb_left;
202 else if (diff > 0)
203 p = p->rb_right;
204 else
205 goto found_extant_conn;
David Howells17926a72007-04-26 15:48:28 -0700206 }
David Howells999b69f2016-06-17 15:42:35 +0100207 spin_unlock(&local->client_conns_lock);
David Howells17926a72007-04-26 15:48:28 -0700208 }
209
David Howells999b69f2016-06-17 15:42:35 +0100210 /* We didn't find a connection or we want an exclusive one. */
211 _debug("get new conn");
David Howellsaa390bb2016-06-17 10:06:56 +0100212 candidate = rxrpc_alloc_client_connection(cp, gfp);
David Howells999b69f2016-06-17 15:42:35 +0100213 if (!candidate) {
214 _leave(" = -ENOMEM");
215 return -ENOMEM;
216 }
217
218 if (cp->exclusive) {
219 /* Assign the call on an exclusive connection to channel 0 and
220 * don't add the connection to the endpoint's shareable conn
221 * lookup tree.
222 */
223 _debug("exclusive chan 0");
224 conn = candidate;
225 atomic_set(&conn->avail_chans, RXRPC_MAXCALLS - 1);
226 spin_lock(&conn->channel_lock);
227 chan = 0;
228 goto found_channel;
229 }
230
231 /* We need to redo the search before attempting to add a new connection
232 * lest we race with someone else adding a conflicting instance.
David Howells17926a72007-04-26 15:48:28 -0700233 */
David Howells999b69f2016-06-17 15:42:35 +0100234 _debug("search 2");
235 spin_lock(&local->client_conns_lock);
236
237 pp = &local->client_conns.rb_node;
238 parent = NULL;
239 while (*pp) {
240 parent = *pp;
241 conn = rb_entry(parent, struct rxrpc_connection, client_node);
242
243 diff = (cmp(peer) ?:
244 cmp(key) ?:
245 cmp(security_level));
246 if (diff < 0)
247 pp = &(*pp)->rb_left;
248 else if (diff > 0)
249 pp = &(*pp)->rb_right;
250 else
251 goto found_extant_conn;
252 }
253
254 /* The second search also failed; simply add the new connection with
255 * the new call in channel 0. Note that we need to take the channel
256 * lock before dropping the client conn lock.
257 */
258 _debug("new conn");
259 conn = candidate;
260 candidate = NULL;
261
262 rb_link_node(&conn->client_node, parent, pp);
263 rb_insert_color(&conn->client_node, &local->client_conns);
264
265 atomic_set(&conn->avail_chans, RXRPC_MAXCALLS - 1);
266 spin_lock(&conn->channel_lock);
267 spin_unlock(&local->client_conns_lock);
268 chan = 0;
269
270found_channel:
271 _debug("found chan");
272 call->conn = conn;
273 call->channel = chan;
274 call->epoch = conn->proto.epoch;
275 call->cid = conn->proto.cid | chan;
276 call->call_id = ++conn->call_counter;
277 rcu_assign_pointer(conn->channels[chan], call);
278
279 _net("CONNECT call %d on conn %d", call->debug_id, conn->debug_id);
280
281 rxrpc_add_call_ID_to_conn(conn, call);
282 spin_unlock(&conn->channel_lock);
David Howellsaa390bb2016-06-17 10:06:56 +0100283 rxrpc_put_peer(cp->peer);
284 cp->peer = NULL;
David Howells999b69f2016-06-17 15:42:35 +0100285 _leave(" = %p {u=%d}", conn, atomic_read(&conn->usage));
286 return 0;
287
288 /* We found a suitable connection already in existence. Discard any
289 * candidate we may have allocated, and try to get a channel on this
290 * one.
291 */
292found_extant_conn:
293 _debug("found conn");
294 rxrpc_get_connection(conn);
295 spin_unlock(&local->client_conns_lock);
296
297 rxrpc_put_connection(candidate);
298
299 if (!atomic_add_unless(&conn->avail_chans, -1, 0)) {
300 if (!gfpflags_allow_blocking(gfp)) {
301 rxrpc_put_connection(conn);
302 _leave(" = -EAGAIN");
303 return -EAGAIN;
304 }
305
306 add_wait_queue(&conn->channel_wq, &myself);
307 for (;;) {
308 set_current_state(TASK_INTERRUPTIBLE);
309 if (atomic_add_unless(&conn->avail_chans, -1, 0))
310 break;
311 if (signal_pending(current))
312 goto interrupted;
313 schedule();
314 }
315 remove_wait_queue(&conn->channel_wq, &myself);
316 __set_current_state(TASK_RUNNING);
317 }
318
319 /* The connection allegedly now has a free channel and we can now
320 * attach the call to it.
321 */
322 spin_lock(&conn->channel_lock);
323
David Howells17926a72007-04-26 15:48:28 -0700324 for (chan = 0; chan < RXRPC_MAXCALLS; chan++)
325 if (!conn->channels[chan])
326 goto found_channel;
327 BUG();
328
David Howells17926a72007-04-26 15:48:28 -0700329interrupted:
David Howells999b69f2016-06-17 15:42:35 +0100330 remove_wait_queue(&conn->channel_wq, &myself);
331 __set_current_state(TASK_RUNNING);
332 rxrpc_put_connection(conn);
David Howellsaa390bb2016-06-17 10:06:56 +0100333 rxrpc_put_peer(cp->peer);
334 cp->peer = NULL;
David Howells17926a72007-04-26 15:48:28 -0700335 _leave(" = -ERESTARTSYS");
336 return -ERESTARTSYS;
337}
338
339/*
340 * get a record of an incoming connection
341 */
David Howellsaa390bb2016-06-17 10:06:56 +0100342struct rxrpc_connection *rxrpc_incoming_connection(struct rxrpc_local *local,
343 struct rxrpc_peer *peer,
David Howells999b69f2016-06-17 15:42:35 +0100344 struct sk_buff *skb)
David Howells17926a72007-04-26 15:48:28 -0700345{
346 struct rxrpc_connection *conn, *candidate = NULL;
David Howells42886ff2016-06-16 13:31:07 +0100347 struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
David Howells17926a72007-04-26 15:48:28 -0700348 struct rb_node *p, **pp;
349 const char *new = "old";
David Howells88b99d02016-07-01 08:27:42 +0100350 u32 epoch, cid;
David Howells17926a72007-04-26 15:48:28 -0700351
352 _enter("");
353
David Howells42886ff2016-06-16 13:31:07 +0100354 ASSERT(sp->hdr.flags & RXRPC_CLIENT_INITIATED);
David Howells17926a72007-04-26 15:48:28 -0700355
David Howells42886ff2016-06-16 13:31:07 +0100356 epoch = sp->hdr.epoch;
357 cid = sp->hdr.cid & RXRPC_CIDMASK;
David Howells17926a72007-04-26 15:48:28 -0700358
359 /* search the connection list first */
David Howellsaa390bb2016-06-17 10:06:56 +0100360 read_lock_bh(&peer->conn_lock);
David Howells17926a72007-04-26 15:48:28 -0700361
David Howellsaa390bb2016-06-17 10:06:56 +0100362 p = peer->service_conns.rb_node;
David Howells17926a72007-04-26 15:48:28 -0700363 while (p) {
David Howells999b69f2016-06-17 15:42:35 +0100364 conn = rb_entry(p, struct rxrpc_connection, service_node);
David Howells17926a72007-04-26 15:48:28 -0700365
David Howells19ffa012016-04-04 14:00:36 +0100366 _debug("maybe %x", conn->proto.cid);
David Howells17926a72007-04-26 15:48:28 -0700367
David Howells19ffa012016-04-04 14:00:36 +0100368 if (epoch < conn->proto.epoch)
David Howells17926a72007-04-26 15:48:28 -0700369 p = p->rb_left;
David Howells19ffa012016-04-04 14:00:36 +0100370 else if (epoch > conn->proto.epoch)
David Howells17926a72007-04-26 15:48:28 -0700371 p = p->rb_right;
David Howells19ffa012016-04-04 14:00:36 +0100372 else if (cid < conn->proto.cid)
David Howells17926a72007-04-26 15:48:28 -0700373 p = p->rb_left;
David Howells19ffa012016-04-04 14:00:36 +0100374 else if (cid > conn->proto.cid)
David Howells17926a72007-04-26 15:48:28 -0700375 p = p->rb_right;
376 else
377 goto found_extant_connection;
378 }
David Howellsaa390bb2016-06-17 10:06:56 +0100379 read_unlock_bh(&peer->conn_lock);
David Howells17926a72007-04-26 15:48:28 -0700380
381 /* not yet present - create a candidate for a new record and then
382 * redo the search */
David Howells843099c2016-04-07 17:23:37 +0100383 candidate = rxrpc_alloc_connection(GFP_NOIO);
David Howells17926a72007-04-26 15:48:28 -0700384 if (!candidate) {
385 _leave(" = -ENOMEM");
386 return ERR_PTR(-ENOMEM);
387 }
388
David Howellsaa390bb2016-06-17 10:06:56 +0100389 candidate->proto.local = local;
David Howells42886ff2016-06-16 13:31:07 +0100390 candidate->proto.epoch = sp->hdr.epoch;
391 candidate->proto.cid = sp->hdr.cid & RXRPC_CIDMASK;
392 candidate->proto.in_clientflag = RXRPC_CLIENT_INITIATED;
David Howellsaa390bb2016-06-17 10:06:56 +0100393 candidate->params.local = local;
394 candidate->params.peer = peer;
David Howells42886ff2016-06-16 13:31:07 +0100395 candidate->params.service_id = sp->hdr.serviceId;
396 candidate->security_ix = sp->hdr.securityIndex;
397 candidate->out_clientflag = 0;
398 candidate->state = RXRPC_CONN_SERVER;
David Howells19ffa012016-04-04 14:00:36 +0100399 if (candidate->params.service_id)
David Howells42886ff2016-06-16 13:31:07 +0100400 candidate->state = RXRPC_CONN_SERVER_UNSECURED;
David Howells17926a72007-04-26 15:48:28 -0700401
David Howellsaa390bb2016-06-17 10:06:56 +0100402 write_lock_bh(&peer->conn_lock);
David Howells17926a72007-04-26 15:48:28 -0700403
David Howellsaa390bb2016-06-17 10:06:56 +0100404 pp = &peer->service_conns.rb_node;
David Howells17926a72007-04-26 15:48:28 -0700405 p = NULL;
406 while (*pp) {
407 p = *pp;
David Howells999b69f2016-06-17 15:42:35 +0100408 conn = rb_entry(p, struct rxrpc_connection, service_node);
David Howells17926a72007-04-26 15:48:28 -0700409
David Howells19ffa012016-04-04 14:00:36 +0100410 if (epoch < conn->proto.epoch)
David Howells17926a72007-04-26 15:48:28 -0700411 pp = &(*pp)->rb_left;
David Howells19ffa012016-04-04 14:00:36 +0100412 else if (epoch > conn->proto.epoch)
David Howells17926a72007-04-26 15:48:28 -0700413 pp = &(*pp)->rb_right;
David Howells19ffa012016-04-04 14:00:36 +0100414 else if (cid < conn->proto.cid)
David Howells17926a72007-04-26 15:48:28 -0700415 pp = &(*pp)->rb_left;
David Howells19ffa012016-04-04 14:00:36 +0100416 else if (cid > conn->proto.cid)
David Howells17926a72007-04-26 15:48:28 -0700417 pp = &(*pp)->rb_right;
418 else
419 goto found_extant_second;
420 }
421
422 /* we can now add the new candidate to the list */
423 conn = candidate;
424 candidate = NULL;
David Howells999b69f2016-06-17 15:42:35 +0100425 rb_link_node(&conn->service_node, p, pp);
David Howellsaa390bb2016-06-17 10:06:56 +0100426 rb_insert_color(&conn->service_node, &peer->service_conns);
427 rxrpc_get_peer(peer);
428 rxrpc_get_local(local);
David Howells17926a72007-04-26 15:48:28 -0700429
David Howellsaa390bb2016-06-17 10:06:56 +0100430 write_unlock_bh(&peer->conn_lock);
David Howells17926a72007-04-26 15:48:28 -0700431
David Howellsb3f57502016-06-21 16:10:03 +0100432 write_lock(&rxrpc_connection_lock);
David Howells17926a72007-04-26 15:48:28 -0700433 list_add_tail(&conn->link, &rxrpc_connections);
David Howellsb3f57502016-06-21 16:10:03 +0100434 write_unlock(&rxrpc_connection_lock);
David Howells17926a72007-04-26 15:48:28 -0700435
436 new = "new";
437
438success:
David Howells19ffa012016-04-04 14:00:36 +0100439 _net("CONNECTION %s %d {%x}", new, conn->debug_id, conn->proto.cid);
David Howells17926a72007-04-26 15:48:28 -0700440
441 _leave(" = %p {u=%d}", conn, atomic_read(&conn->usage));
442 return conn;
443
444 /* we found the connection in the list immediately */
445found_extant_connection:
David Howells42886ff2016-06-16 13:31:07 +0100446 if (sp->hdr.securityIndex != conn->security_ix) {
David Howellsaa390bb2016-06-17 10:06:56 +0100447 read_unlock_bh(&peer->conn_lock);
David Howells17926a72007-04-26 15:48:28 -0700448 goto security_mismatch;
449 }
David Howells5627cc82016-04-04 14:00:38 +0100450 rxrpc_get_connection(conn);
David Howellsaa390bb2016-06-17 10:06:56 +0100451 read_unlock_bh(&peer->conn_lock);
David Howells17926a72007-04-26 15:48:28 -0700452 goto success;
453
454 /* we found the connection on the second time through the list */
455found_extant_second:
David Howells42886ff2016-06-16 13:31:07 +0100456 if (sp->hdr.securityIndex != conn->security_ix) {
David Howellsaa390bb2016-06-17 10:06:56 +0100457 write_unlock_bh(&peer->conn_lock);
David Howells17926a72007-04-26 15:48:28 -0700458 goto security_mismatch;
459 }
David Howells5627cc82016-04-04 14:00:38 +0100460 rxrpc_get_connection(conn);
David Howellsaa390bb2016-06-17 10:06:56 +0100461 write_unlock_bh(&peer->conn_lock);
David Howells17926a72007-04-26 15:48:28 -0700462 kfree(candidate);
463 goto success;
464
465security_mismatch:
466 kfree(candidate);
467 _leave(" = -EKEYREJECTED");
468 return ERR_PTR(-EKEYREJECTED);
469}
470
471/*
472 * find a connection based on transport and RxRPC connection ID for an incoming
473 * packet
474 */
David Howellsaa390bb2016-06-17 10:06:56 +0100475struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_local *local,
476 struct rxrpc_peer *peer,
David Howells42886ff2016-06-16 13:31:07 +0100477 struct sk_buff *skb)
David Howells17926a72007-04-26 15:48:28 -0700478{
479 struct rxrpc_connection *conn;
David Howells42886ff2016-06-16 13:31:07 +0100480 struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
David Howells17926a72007-04-26 15:48:28 -0700481 struct rb_node *p;
David Howells0d12f8a2016-03-04 15:53:46 +0000482 u32 epoch, cid;
David Howells17926a72007-04-26 15:48:28 -0700483
David Howells42886ff2016-06-16 13:31:07 +0100484 _enter(",{%x,%x}", sp->hdr.cid, sp->hdr.flags);
David Howells17926a72007-04-26 15:48:28 -0700485
David Howellsaa390bb2016-06-17 10:06:56 +0100486 read_lock_bh(&peer->conn_lock);
David Howells17926a72007-04-26 15:48:28 -0700487
David Howells42886ff2016-06-16 13:31:07 +0100488 cid = sp->hdr.cid & RXRPC_CIDMASK;
489 epoch = sp->hdr.epoch;
David Howells17926a72007-04-26 15:48:28 -0700490
David Howells4a3388c2016-04-04 14:00:37 +0100491 if (sp->hdr.flags & RXRPC_CLIENT_INITIATED) {
David Howellsaa390bb2016-06-17 10:06:56 +0100492 p = peer->service_conns.rb_node;
David Howells4a3388c2016-04-04 14:00:37 +0100493 while (p) {
David Howells999b69f2016-06-17 15:42:35 +0100494 conn = rb_entry(p, struct rxrpc_connection, service_node);
David Howells17926a72007-04-26 15:48:28 -0700495
David Howells4a3388c2016-04-04 14:00:37 +0100496 _debug("maybe %x", conn->proto.cid);
David Howells17926a72007-04-26 15:48:28 -0700497
David Howells4a3388c2016-04-04 14:00:37 +0100498 if (epoch < conn->proto.epoch)
499 p = p->rb_left;
500 else if (epoch > conn->proto.epoch)
501 p = p->rb_right;
502 else if (cid < conn->proto.cid)
503 p = p->rb_left;
504 else if (cid > conn->proto.cid)
505 p = p->rb_right;
506 else
507 goto found;
508 }
509 } else {
510 conn = idr_find(&rxrpc_client_conn_ids, cid >> RXRPC_CIDSHIFT);
David Howells689f4c62016-06-30 11:34:30 +0100511 if (conn &&
512 conn->proto.epoch == epoch &&
513 conn->params.peer == peer)
David Howells17926a72007-04-26 15:48:28 -0700514 goto found;
515 }
516
David Howellsaa390bb2016-06-17 10:06:56 +0100517 read_unlock_bh(&peer->conn_lock);
David Howells17926a72007-04-26 15:48:28 -0700518 _leave(" = NULL");
519 return NULL;
520
521found:
David Howells5627cc82016-04-04 14:00:38 +0100522 rxrpc_get_connection(conn);
David Howellsaa390bb2016-06-17 10:06:56 +0100523 read_unlock_bh(&peer->conn_lock);
David Howells17926a72007-04-26 15:48:28 -0700524 _leave(" = %p", conn);
525 return conn;
526}
527
528/*
David Howells999b69f2016-06-17 15:42:35 +0100529 * Disconnect a call and clear any channel it occupies when that call
530 * terminates.
531 */
532void rxrpc_disconnect_call(struct rxrpc_call *call)
533{
534 struct rxrpc_connection *conn = call->conn;
535 unsigned chan = call->channel;
536
537 _enter("%d,%d", conn->debug_id, call->channel);
538
539 if (conn->channels[chan] == call) {
540 rcu_assign_pointer(conn->channels[chan], NULL);
541 atomic_inc(&conn->avail_chans);
542 wake_up(&conn->channel_wq);
543 }
544}
545
546/*
David Howells17926a72007-04-26 15:48:28 -0700547 * release a virtual connection
548 */
549void rxrpc_put_connection(struct rxrpc_connection *conn)
550{
David Howells999b69f2016-06-17 15:42:35 +0100551 if (!conn)
552 return;
553
David Howells17926a72007-04-26 15:48:28 -0700554 _enter("%p{u=%d,d=%d}",
555 conn, atomic_read(&conn->usage), conn->debug_id);
556
557 ASSERTCMP(atomic_read(&conn->usage), >, 0);
558
Ksenija Stanojevic22a3f9a2015-09-17 18:12:53 +0200559 conn->put_time = ktime_get_seconds();
David Howells17926a72007-04-26 15:48:28 -0700560 if (atomic_dec_and_test(&conn->usage)) {
561 _debug("zombie");
David Howells651350d2007-04-26 15:50:17 -0700562 rxrpc_queue_delayed_work(&rxrpc_connection_reap, 0);
David Howells17926a72007-04-26 15:48:28 -0700563 }
564
565 _leave("");
566}
567
568/*
569 * destroy a virtual connection
570 */
571static void rxrpc_destroy_connection(struct rxrpc_connection *conn)
572{
573 _enter("%p{%d}", conn, atomic_read(&conn->usage));
574
575 ASSERTCMP(atomic_read(&conn->usage), ==, 0);
576
577 _net("DESTROY CONN %d", conn->debug_id);
578
David Howells17926a72007-04-26 15:48:28 -0700579 ASSERT(RB_EMPTY_ROOT(&conn->calls));
580 rxrpc_purge_queue(&conn->rx_queue);
581
David Howellse0e4d822016-04-07 17:23:58 +0100582 conn->security->clear(conn);
David Howells19ffa012016-04-04 14:00:36 +0100583 key_put(conn->params.key);
David Howellse0e4d822016-04-07 17:23:58 +0100584 key_put(conn->server_key);
David Howellsaa390bb2016-06-17 10:06:56 +0100585 rxrpc_put_peer(conn->params.peer);
586 rxrpc_put_local(conn->params.local);
David Howellse0e4d822016-04-07 17:23:58 +0100587
David Howells17926a72007-04-26 15:48:28 -0700588 kfree(conn);
589 _leave("");
590}
591
592/*
593 * reap dead connections
594 */
Roel Kluin5eaa65b2008-12-10 15:18:31 -0800595static void rxrpc_connection_reaper(struct work_struct *work)
David Howells17926a72007-04-26 15:48:28 -0700596{
597 struct rxrpc_connection *conn, *_p;
David Howellsaa390bb2016-06-17 10:06:56 +0100598 struct rxrpc_peer *peer;
David Howells17926a72007-04-26 15:48:28 -0700599 unsigned long now, earliest, reap_time;
600
601 LIST_HEAD(graveyard);
602
603 _enter("");
604
Ksenija Stanojevic22a3f9a2015-09-17 18:12:53 +0200605 now = ktime_get_seconds();
David Howells17926a72007-04-26 15:48:28 -0700606 earliest = ULONG_MAX;
607
David Howellsb3f57502016-06-21 16:10:03 +0100608 write_lock(&rxrpc_connection_lock);
David Howells17926a72007-04-26 15:48:28 -0700609 list_for_each_entry_safe(conn, _p, &rxrpc_connections, link) {
610 _debug("reap CONN %d { u=%d,t=%ld }",
611 conn->debug_id, atomic_read(&conn->usage),
612 (long) now - (long) conn->put_time);
613
614 if (likely(atomic_read(&conn->usage) > 0))
615 continue;
616
David Howells999b69f2016-06-17 15:42:35 +0100617 if (rxrpc_conn_is_client(conn)) {
618 struct rxrpc_local *local = conn->params.local;
619 spin_lock(&local->client_conns_lock);
620 reap_time = conn->put_time + rxrpc_connection_expiry;
David Howells17926a72007-04-26 15:48:28 -0700621
David Howells999b69f2016-06-17 15:42:35 +0100622 if (atomic_read(&conn->usage) > 0) {
623 ;
624 } else if (reap_time <= now) {
625 list_move_tail(&conn->link, &graveyard);
David Howells4a3388c2016-04-04 14:00:37 +0100626 rxrpc_put_client_connection_id(conn);
David Howells999b69f2016-06-17 15:42:35 +0100627 rb_erase(&conn->client_node,
628 &local->client_conns);
629 } else if (reap_time < earliest) {
630 earliest = reap_time;
David Howells17926a72007-04-26 15:48:28 -0700631 }
632
David Howells999b69f2016-06-17 15:42:35 +0100633 spin_unlock(&local->client_conns_lock);
634 } else {
David Howellsaa390bb2016-06-17 10:06:56 +0100635 peer = conn->params.peer;
636 write_lock_bh(&peer->conn_lock);
David Howells999b69f2016-06-17 15:42:35 +0100637 reap_time = conn->put_time + rxrpc_connection_expiry;
David Howells17926a72007-04-26 15:48:28 -0700638
David Howells999b69f2016-06-17 15:42:35 +0100639 if (atomic_read(&conn->usage) > 0) {
640 ;
641 } else if (reap_time <= now) {
642 list_move_tail(&conn->link, &graveyard);
643 rb_erase(&conn->service_node,
David Howellsaa390bb2016-06-17 10:06:56 +0100644 &peer->service_conns);
David Howells999b69f2016-06-17 15:42:35 +0100645 } else if (reap_time < earliest) {
646 earliest = reap_time;
647 }
648
David Howellsaa390bb2016-06-17 10:06:56 +0100649 write_unlock_bh(&peer->conn_lock);
David Howells999b69f2016-06-17 15:42:35 +0100650 }
David Howells17926a72007-04-26 15:48:28 -0700651 }
David Howellsb3f57502016-06-21 16:10:03 +0100652 write_unlock(&rxrpc_connection_lock);
David Howells17926a72007-04-26 15:48:28 -0700653
654 if (earliest != ULONG_MAX) {
655 _debug("reschedule reaper %ld", (long) earliest - now);
656 ASSERTCMP(earliest, >, now);
David Howells651350d2007-04-26 15:50:17 -0700657 rxrpc_queue_delayed_work(&rxrpc_connection_reap,
658 (earliest - now) * HZ);
David Howells17926a72007-04-26 15:48:28 -0700659 }
660
661 /* then destroy all those pulled out */
662 while (!list_empty(&graveyard)) {
663 conn = list_entry(graveyard.next, struct rxrpc_connection,
664 link);
665 list_del_init(&conn->link);
666
667 ASSERTCMP(atomic_read(&conn->usage), ==, 0);
668 rxrpc_destroy_connection(conn);
669 }
670
671 _leave("");
672}
673
674/*
675 * preemptively destroy all the connection records rather than waiting for them
676 * to time out
677 */
678void __exit rxrpc_destroy_all_connections(void)
679{
680 _enter("");
681
David Howells5873c082014-02-07 18:58:44 +0000682 rxrpc_connection_expiry = 0;
David Howells17926a72007-04-26 15:48:28 -0700683 cancel_delayed_work(&rxrpc_connection_reap);
David Howells651350d2007-04-26 15:50:17 -0700684 rxrpc_queue_delayed_work(&rxrpc_connection_reap, 0);
David Howells17926a72007-04-26 15:48:28 -0700685
686 _leave("");
687}