Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/net/rxrpc/ar-transport.c
15109 views
1
/* RxRPC point-to-point transport session management
2
*
3
* Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
4
* Written by David Howells ([email protected])
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
#include <linux/module.h>
13
#include <linux/net.h>
14
#include <linux/skbuff.h>
15
#include <linux/slab.h>
16
#include <net/sock.h>
17
#include <net/af_rxrpc.h>
18
#include "ar-internal.h"
19
20
static void rxrpc_transport_reaper(struct work_struct *work);
21
22
static LIST_HEAD(rxrpc_transports);
23
static DEFINE_RWLOCK(rxrpc_transport_lock);
24
static unsigned long rxrpc_transport_timeout = 3600 * 24;
25
static DECLARE_DELAYED_WORK(rxrpc_transport_reap, rxrpc_transport_reaper);
26
27
/*
28
* allocate a new transport session manager
29
*/
30
static struct rxrpc_transport *rxrpc_alloc_transport(struct rxrpc_local *local,
31
struct rxrpc_peer *peer,
32
gfp_t gfp)
33
{
34
struct rxrpc_transport *trans;
35
36
_enter("");
37
38
trans = kzalloc(sizeof(struct rxrpc_transport), gfp);
39
if (trans) {
40
trans->local = local;
41
trans->peer = peer;
42
INIT_LIST_HEAD(&trans->link);
43
trans->bundles = RB_ROOT;
44
trans->client_conns = RB_ROOT;
45
trans->server_conns = RB_ROOT;
46
skb_queue_head_init(&trans->error_queue);
47
spin_lock_init(&trans->client_lock);
48
rwlock_init(&trans->conn_lock);
49
atomic_set(&trans->usage, 1);
50
trans->debug_id = atomic_inc_return(&rxrpc_debug_id);
51
52
if (peer->srx.transport.family == AF_INET) {
53
switch (peer->srx.transport_type) {
54
case SOCK_DGRAM:
55
INIT_WORK(&trans->error_handler,
56
rxrpc_UDP_error_handler);
57
break;
58
default:
59
BUG();
60
break;
61
}
62
} else {
63
BUG();
64
}
65
}
66
67
_leave(" = %p", trans);
68
return trans;
69
}
70
71
/*
72
* obtain a transport session for the nominated endpoints
73
*/
74
struct rxrpc_transport *rxrpc_get_transport(struct rxrpc_local *local,
75
struct rxrpc_peer *peer,
76
gfp_t gfp)
77
{
78
struct rxrpc_transport *trans, *candidate;
79
const char *new = "old";
80
int usage;
81
82
_enter("{%pI4+%hu},{%pI4+%hu},",
83
&local->srx.transport.sin.sin_addr,
84
ntohs(local->srx.transport.sin.sin_port),
85
&peer->srx.transport.sin.sin_addr,
86
ntohs(peer->srx.transport.sin.sin_port));
87
88
/* search the transport list first */
89
read_lock_bh(&rxrpc_transport_lock);
90
list_for_each_entry(trans, &rxrpc_transports, link) {
91
if (trans->local == local && trans->peer == peer)
92
goto found_extant_transport;
93
}
94
read_unlock_bh(&rxrpc_transport_lock);
95
96
/* not yet present - create a candidate for a new record and then
97
* redo the search */
98
candidate = rxrpc_alloc_transport(local, peer, gfp);
99
if (!candidate) {
100
_leave(" = -ENOMEM");
101
return ERR_PTR(-ENOMEM);
102
}
103
104
write_lock_bh(&rxrpc_transport_lock);
105
106
list_for_each_entry(trans, &rxrpc_transports, link) {
107
if (trans->local == local && trans->peer == peer)
108
goto found_extant_second;
109
}
110
111
/* we can now add the new candidate to the list */
112
trans = candidate;
113
candidate = NULL;
114
usage = atomic_read(&trans->usage);
115
116
rxrpc_get_local(trans->local);
117
atomic_inc(&trans->peer->usage);
118
list_add_tail(&trans->link, &rxrpc_transports);
119
write_unlock_bh(&rxrpc_transport_lock);
120
new = "new";
121
122
success:
123
_net("TRANSPORT %s %d local %d -> peer %d",
124
new,
125
trans->debug_id,
126
trans->local->debug_id,
127
trans->peer->debug_id);
128
129
_leave(" = %p {u=%d}", trans, usage);
130
return trans;
131
132
/* we found the transport in the list immediately */
133
found_extant_transport:
134
usage = atomic_inc_return(&trans->usage);
135
read_unlock_bh(&rxrpc_transport_lock);
136
goto success;
137
138
/* we found the transport on the second time through the list */
139
found_extant_second:
140
usage = atomic_inc_return(&trans->usage);
141
write_unlock_bh(&rxrpc_transport_lock);
142
kfree(candidate);
143
goto success;
144
}
145
146
/*
147
* find the transport connecting two endpoints
148
*/
149
struct rxrpc_transport *rxrpc_find_transport(struct rxrpc_local *local,
150
struct rxrpc_peer *peer)
151
{
152
struct rxrpc_transport *trans;
153
154
_enter("{%pI4+%hu},{%pI4+%hu},",
155
&local->srx.transport.sin.sin_addr,
156
ntohs(local->srx.transport.sin.sin_port),
157
&peer->srx.transport.sin.sin_addr,
158
ntohs(peer->srx.transport.sin.sin_port));
159
160
/* search the transport list */
161
read_lock_bh(&rxrpc_transport_lock);
162
163
list_for_each_entry(trans, &rxrpc_transports, link) {
164
if (trans->local == local && trans->peer == peer)
165
goto found_extant_transport;
166
}
167
168
read_unlock_bh(&rxrpc_transport_lock);
169
_leave(" = NULL");
170
return NULL;
171
172
found_extant_transport:
173
atomic_inc(&trans->usage);
174
read_unlock_bh(&rxrpc_transport_lock);
175
_leave(" = %p", trans);
176
return trans;
177
}
178
179
/*
180
* release a transport session
181
*/
182
void rxrpc_put_transport(struct rxrpc_transport *trans)
183
{
184
_enter("%p{u=%d}", trans, atomic_read(&trans->usage));
185
186
ASSERTCMP(atomic_read(&trans->usage), >, 0);
187
188
trans->put_time = get_seconds();
189
if (unlikely(atomic_dec_and_test(&trans->usage))) {
190
_debug("zombie");
191
/* let the reaper determine the timeout to avoid a race with
192
* overextending the timeout if the reaper is running at the
193
* same time */
194
rxrpc_queue_delayed_work(&rxrpc_transport_reap, 0);
195
}
196
_leave("");
197
}
198
199
/*
200
* clean up a transport session
201
*/
202
static void rxrpc_cleanup_transport(struct rxrpc_transport *trans)
203
{
204
_net("DESTROY TRANS %d", trans->debug_id);
205
206
rxrpc_purge_queue(&trans->error_queue);
207
208
rxrpc_put_local(trans->local);
209
rxrpc_put_peer(trans->peer);
210
kfree(trans);
211
}
212
213
/*
214
* reap dead transports that have passed their expiry date
215
*/
216
static void rxrpc_transport_reaper(struct work_struct *work)
217
{
218
struct rxrpc_transport *trans, *_p;
219
unsigned long now, earliest, reap_time;
220
221
LIST_HEAD(graveyard);
222
223
_enter("");
224
225
now = get_seconds();
226
earliest = ULONG_MAX;
227
228
/* extract all the transports that have been dead too long */
229
write_lock_bh(&rxrpc_transport_lock);
230
list_for_each_entry_safe(trans, _p, &rxrpc_transports, link) {
231
_debug("reap TRANS %d { u=%d t=%ld }",
232
trans->debug_id, atomic_read(&trans->usage),
233
(long) now - (long) trans->put_time);
234
235
if (likely(atomic_read(&trans->usage) > 0))
236
continue;
237
238
reap_time = trans->put_time + rxrpc_transport_timeout;
239
if (reap_time <= now)
240
list_move_tail(&trans->link, &graveyard);
241
else if (reap_time < earliest)
242
earliest = reap_time;
243
}
244
write_unlock_bh(&rxrpc_transport_lock);
245
246
if (earliest != ULONG_MAX) {
247
_debug("reschedule reaper %ld", (long) earliest - now);
248
ASSERTCMP(earliest, >, now);
249
rxrpc_queue_delayed_work(&rxrpc_transport_reap,
250
(earliest - now) * HZ);
251
}
252
253
/* then destroy all those pulled out */
254
while (!list_empty(&graveyard)) {
255
trans = list_entry(graveyard.next, struct rxrpc_transport,
256
link);
257
list_del_init(&trans->link);
258
259
ASSERTCMP(atomic_read(&trans->usage), ==, 0);
260
rxrpc_cleanup_transport(trans);
261
}
262
263
_leave("");
264
}
265
266
/*
267
* preemptively destroy all the transport session records rather than waiting
268
* for them to time out
269
*/
270
void __exit rxrpc_destroy_all_transports(void)
271
{
272
_enter("");
273
274
rxrpc_transport_timeout = 0;
275
cancel_delayed_work(&rxrpc_transport_reap);
276
rxrpc_queue_delayed_work(&rxrpc_transport_reap, 0);
277
278
_leave("");
279
}
280
281