]>
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 | */ | |
c6d2b8d7 | 37 | struct rxrpc_connection *rxrpc_alloc_connection(gfp_t gfp) |
17926a79 DH |
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 | 49 | skb_queue_head_init(&conn->rx_queue); |
e0e4d82f | 50 | conn->security = &rxrpc_no_security; |
17926a79 DH |
51 | spin_lock_init(&conn->state_lock); |
52 | atomic_set(&conn->usage, 1); | |
53 | conn->debug_id = atomic_inc_return(&rxrpc_debug_id); | |
999b69f8 | 54 | atomic_set(&conn->avail_chans, RXRPC_MAXCALLS); |
17926a79 | 55 | conn->size_align = 4; |
0d12f8a4 | 56 | conn->header_size = sizeof(struct rxrpc_wire_header); |
17926a79 DH |
57 | } |
58 | ||
16c61add | 59 | _leave(" = %p{%d}", conn, conn ? conn->debug_id : 0); |
17926a79 DH |
60 | return conn; |
61 | } | |
62 | ||
17926a79 DH |
63 | /* |
64 | * find a connection based on transport and RxRPC connection ID for an incoming | |
65 | * packet | |
66 | */ | |
aa390bbe DH |
67 | struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_local *local, |
68 | struct rxrpc_peer *peer, | |
42886ffe | 69 | struct sk_buff *skb) |
17926a79 DH |
70 | { |
71 | struct rxrpc_connection *conn; | |
42886ffe | 72 | struct rxrpc_skb_priv *sp = rxrpc_skb(skb); |
17926a79 | 73 | struct rb_node *p; |
0d12f8a4 | 74 | u32 epoch, cid; |
17926a79 | 75 | |
42886ffe | 76 | _enter(",{%x,%x}", sp->hdr.cid, sp->hdr.flags); |
17926a79 | 77 | |
aa390bbe | 78 | read_lock_bh(&peer->conn_lock); |
17926a79 | 79 | |
42886ffe DH |
80 | cid = sp->hdr.cid & RXRPC_CIDMASK; |
81 | epoch = sp->hdr.epoch; | |
17926a79 | 82 | |
4a3388c8 | 83 | if (sp->hdr.flags & RXRPC_CLIENT_INITIATED) { |
aa390bbe | 84 | p = peer->service_conns.rb_node; |
4a3388c8 | 85 | while (p) { |
999b69f8 | 86 | conn = rb_entry(p, struct rxrpc_connection, service_node); |
4a3388c8 DH |
87 | |
88 | _debug("maybe %x", conn->proto.cid); | |
89 | ||
90 | if (epoch < conn->proto.epoch) | |
91 | p = p->rb_left; | |
92 | else if (epoch > conn->proto.epoch) | |
93 | p = p->rb_right; | |
94 | else if (cid < conn->proto.cid) | |
95 | p = p->rb_left; | |
96 | else if (cid > conn->proto.cid) | |
97 | p = p->rb_right; | |
98 | else | |
99 | goto found; | |
100 | } | |
101 | } else { | |
102 | conn = idr_find(&rxrpc_client_conn_ids, cid >> RXRPC_CIDSHIFT); | |
689f4c64 DH |
103 | if (conn && |
104 | conn->proto.epoch == epoch && | |
105 | conn->params.peer == peer) | |
17926a79 DH |
106 | goto found; |
107 | } | |
108 | ||
aa390bbe | 109 | read_unlock_bh(&peer->conn_lock); |
17926a79 DH |
110 | _leave(" = NULL"); |
111 | return NULL; | |
112 | ||
113 | found: | |
5627cc8b | 114 | rxrpc_get_connection(conn); |
aa390bbe | 115 | read_unlock_bh(&peer->conn_lock); |
17926a79 DH |
116 | _leave(" = %p", conn); |
117 | return conn; | |
118 | } | |
119 | ||
999b69f8 DH |
120 | /* |
121 | * Disconnect a call and clear any channel it occupies when that call | |
a1399f8b DH |
122 | * terminates. The caller must hold the channel_lock and must release the |
123 | * call's ref on the connection. | |
999b69f8 | 124 | */ |
a1399f8b | 125 | void __rxrpc_disconnect_call(struct rxrpc_call *call) |
999b69f8 DH |
126 | { |
127 | struct rxrpc_connection *conn = call->conn; | |
a1399f8b | 128 | struct rxrpc_channel *chan = &conn->channels[call->channel]; |
999b69f8 DH |
129 | |
130 | _enter("%d,%d", conn->debug_id, call->channel); | |
131 | ||
a1399f8b DH |
132 | if (rcu_access_pointer(chan->call) == call) { |
133 | /* Save the result of the call so that we can repeat it if necessary | |
134 | * through the channel, whilst disposing of the actual call record. | |
135 | */ | |
136 | chan->last_result = call->local_abort; | |
137 | smp_wmb(); | |
138 | chan->last_call = chan->call_id; | |
139 | chan->call_id = chan->call_counter; | |
e653cfe4 | 140 | |
a1399f8b | 141 | rcu_assign_pointer(chan->call, NULL); |
999b69f8 DH |
142 | atomic_inc(&conn->avail_chans); |
143 | wake_up(&conn->channel_wq); | |
144 | } | |
e653cfe4 | 145 | |
a1399f8b DH |
146 | _leave(""); |
147 | } | |
148 | ||
149 | /* | |
150 | * Disconnect a call and clear any channel it occupies when that call | |
151 | * terminates. | |
152 | */ | |
153 | void rxrpc_disconnect_call(struct rxrpc_call *call) | |
154 | { | |
155 | struct rxrpc_connection *conn = call->conn; | |
156 | ||
157 | spin_lock(&conn->channel_lock); | |
158 | __rxrpc_disconnect_call(call); | |
e653cfe4 DH |
159 | spin_unlock(&conn->channel_lock); |
160 | ||
161 | call->conn = NULL; | |
162 | rxrpc_put_connection(conn); | |
999b69f8 DH |
163 | } |
164 | ||
17926a79 DH |
165 | /* |
166 | * release a virtual connection | |
167 | */ | |
168 | void rxrpc_put_connection(struct rxrpc_connection *conn) | |
169 | { | |
999b69f8 DH |
170 | if (!conn) |
171 | return; | |
172 | ||
17926a79 DH |
173 | _enter("%p{u=%d,d=%d}", |
174 | conn, atomic_read(&conn->usage), conn->debug_id); | |
175 | ||
176 | ASSERTCMP(atomic_read(&conn->usage), >, 0); | |
177 | ||
22a3f9a2 | 178 | conn->put_time = ktime_get_seconds(); |
17926a79 DH |
179 | if (atomic_dec_and_test(&conn->usage)) { |
180 | _debug("zombie"); | |
651350d1 | 181 | rxrpc_queue_delayed_work(&rxrpc_connection_reap, 0); |
17926a79 DH |
182 | } |
183 | ||
184 | _leave(""); | |
185 | } | |
186 | ||
187 | /* | |
188 | * destroy a virtual connection | |
189 | */ | |
dee46364 | 190 | static void rxrpc_destroy_connection(struct rcu_head *rcu) |
17926a79 | 191 | { |
dee46364 DH |
192 | struct rxrpc_connection *conn = |
193 | container_of(rcu, struct rxrpc_connection, rcu); | |
194 | ||
195 | _enter("{%d,u=%d}", conn->debug_id, atomic_read(&conn->usage)); | |
17926a79 DH |
196 | |
197 | ASSERTCMP(atomic_read(&conn->usage), ==, 0); | |
198 | ||
199 | _net("DESTROY CONN %d", conn->debug_id); | |
200 | ||
17926a79 DH |
201 | rxrpc_purge_queue(&conn->rx_queue); |
202 | ||
e0e4d82f | 203 | conn->security->clear(conn); |
19ffa01c | 204 | key_put(conn->params.key); |
e0e4d82f | 205 | key_put(conn->server_key); |
aa390bbe DH |
206 | rxrpc_put_peer(conn->params.peer); |
207 | rxrpc_put_local(conn->params.local); | |
e0e4d82f | 208 | |
17926a79 DH |
209 | kfree(conn); |
210 | _leave(""); | |
211 | } | |
212 | ||
213 | /* | |
214 | * reap dead connections | |
215 | */ | |
5eaa65b2 | 216 | static void rxrpc_connection_reaper(struct work_struct *work) |
17926a79 DH |
217 | { |
218 | struct rxrpc_connection *conn, *_p; | |
aa390bbe | 219 | struct rxrpc_peer *peer; |
17926a79 DH |
220 | unsigned long now, earliest, reap_time; |
221 | ||
222 | LIST_HEAD(graveyard); | |
223 | ||
224 | _enter(""); | |
225 | ||
22a3f9a2 | 226 | now = ktime_get_seconds(); |
17926a79 DH |
227 | earliest = ULONG_MAX; |
228 | ||
b3f57504 | 229 | write_lock(&rxrpc_connection_lock); |
17926a79 DH |
230 | list_for_each_entry_safe(conn, _p, &rxrpc_connections, link) { |
231 | _debug("reap CONN %d { u=%d,t=%ld }", | |
232 | conn->debug_id, atomic_read(&conn->usage), | |
233 | (long) now - (long) conn->put_time); | |
234 | ||
235 | if (likely(atomic_read(&conn->usage) > 0)) | |
236 | continue; | |
237 | ||
999b69f8 DH |
238 | if (rxrpc_conn_is_client(conn)) { |
239 | struct rxrpc_local *local = conn->params.local; | |
240 | spin_lock(&local->client_conns_lock); | |
241 | reap_time = conn->put_time + rxrpc_connection_expiry; | |
17926a79 | 242 | |
999b69f8 DH |
243 | if (atomic_read(&conn->usage) > 0) { |
244 | ; | |
245 | } else if (reap_time <= now) { | |
246 | list_move_tail(&conn->link, &graveyard); | |
4a3388c8 | 247 | rxrpc_put_client_connection_id(conn); |
999b69f8 DH |
248 | rb_erase(&conn->client_node, |
249 | &local->client_conns); | |
250 | } else if (reap_time < earliest) { | |
251 | earliest = reap_time; | |
252 | } | |
253 | ||
254 | spin_unlock(&local->client_conns_lock); | |
255 | } else { | |
aa390bbe DH |
256 | peer = conn->params.peer; |
257 | write_lock_bh(&peer->conn_lock); | |
999b69f8 DH |
258 | reap_time = conn->put_time + rxrpc_connection_expiry; |
259 | ||
260 | if (atomic_read(&conn->usage) > 0) { | |
261 | ; | |
262 | } else if (reap_time <= now) { | |
263 | list_move_tail(&conn->link, &graveyard); | |
264 | rb_erase(&conn->service_node, | |
aa390bbe | 265 | &peer->service_conns); |
999b69f8 DH |
266 | } else if (reap_time < earliest) { |
267 | earliest = reap_time; | |
17926a79 DH |
268 | } |
269 | ||
aa390bbe | 270 | write_unlock_bh(&peer->conn_lock); |
17926a79 | 271 | } |
17926a79 | 272 | } |
b3f57504 | 273 | write_unlock(&rxrpc_connection_lock); |
17926a79 DH |
274 | |
275 | if (earliest != ULONG_MAX) { | |
276 | _debug("reschedule reaper %ld", (long) earliest - now); | |
277 | ASSERTCMP(earliest, >, now); | |
651350d1 DH |
278 | rxrpc_queue_delayed_work(&rxrpc_connection_reap, |
279 | (earliest - now) * HZ); | |
17926a79 DH |
280 | } |
281 | ||
282 | /* then destroy all those pulled out */ | |
283 | while (!list_empty(&graveyard)) { | |
284 | conn = list_entry(graveyard.next, struct rxrpc_connection, | |
285 | link); | |
286 | list_del_init(&conn->link); | |
287 | ||
288 | ASSERTCMP(atomic_read(&conn->usage), ==, 0); | |
dee46364 DH |
289 | skb_queue_purge(&conn->rx_queue); |
290 | call_rcu(&conn->rcu, rxrpc_destroy_connection); | |
17926a79 DH |
291 | } |
292 | ||
293 | _leave(""); | |
294 | } | |
295 | ||
296 | /* | |
297 | * preemptively destroy all the connection records rather than waiting for them | |
298 | * to time out | |
299 | */ | |
300 | void __exit rxrpc_destroy_all_connections(void) | |
301 | { | |
dee46364 DH |
302 | struct rxrpc_connection *conn, *_p; |
303 | bool leak = false; | |
304 | ||
17926a79 DH |
305 | _enter(""); |
306 | ||
5873c083 | 307 | rxrpc_connection_expiry = 0; |
17926a79 | 308 | cancel_delayed_work(&rxrpc_connection_reap); |
651350d1 | 309 | rxrpc_queue_delayed_work(&rxrpc_connection_reap, 0); |
dee46364 DH |
310 | flush_workqueue(rxrpc_workqueue); |
311 | ||
312 | write_lock(&rxrpc_connection_lock); | |
313 | list_for_each_entry_safe(conn, _p, &rxrpc_connections, link) { | |
314 | pr_err("AF_RXRPC: Leaked conn %p {%d}\n", | |
315 | conn, atomic_read(&conn->usage)); | |
316 | leak = true; | |
317 | } | |
318 | write_unlock(&rxrpc_connection_lock); | |
319 | BUG_ON(leak); | |
320 | ||
321 | /* Make sure the local and peer records pinned by any dying connections | |
322 | * are released. | |
323 | */ | |
324 | rcu_barrier(); | |
325 | rxrpc_destroy_client_conn_ids(); | |
17926a79 DH |
326 | |
327 | _leave(""); | |
328 | } |