Blame view

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