Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ar-transport.c
Go to the documentation of this file.
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);
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,
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  */
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  */
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  */
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  */
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 }