]> err.no Git - linux-2.6/blob - net/rxrpc/ar-local.c
Merge branch 'zero-len' of git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik...
[linux-2.6] / net / rxrpc / ar-local.c
1 /* AF_RXRPC local endpoint management
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
12 #include <linux/module.h>
13 #include <linux/net.h>
14 #include <linux/skbuff.h>
15 #include <net/sock.h>
16 #include <net/af_rxrpc.h>
17 #include "ar-internal.h"
18
19 static LIST_HEAD(rxrpc_locals);
20 DEFINE_RWLOCK(rxrpc_local_lock);
21 static DECLARE_RWSEM(rxrpc_local_sem);
22 static DECLARE_WAIT_QUEUE_HEAD(rxrpc_local_wq);
23
24 static void rxrpc_destroy_local(struct work_struct *work);
25
26 /*
27  * allocate a new local
28  */
29 static
30 struct rxrpc_local *rxrpc_alloc_local(struct sockaddr_rxrpc *srx)
31 {
32         struct rxrpc_local *local;
33
34         local = kzalloc(sizeof(struct rxrpc_local), GFP_KERNEL);
35         if (local) {
36                 INIT_WORK(&local->destroyer, &rxrpc_destroy_local);
37                 INIT_WORK(&local->acceptor, &rxrpc_accept_incoming_calls);
38                 INIT_WORK(&local->rejecter, &rxrpc_reject_packets);
39                 INIT_LIST_HEAD(&local->services);
40                 INIT_LIST_HEAD(&local->link);
41                 init_rwsem(&local->defrag_sem);
42                 skb_queue_head_init(&local->accept_queue);
43                 skb_queue_head_init(&local->reject_queue);
44                 spin_lock_init(&local->lock);
45                 rwlock_init(&local->services_lock);
46                 atomic_set(&local->usage, 1);
47                 local->debug_id = atomic_inc_return(&rxrpc_debug_id);
48                 memcpy(&local->srx, srx, sizeof(*srx));
49         }
50
51         _leave(" = %p", local);
52         return local;
53 }
54
55 /*
56  * create the local socket
57  * - must be called with rxrpc_local_sem writelocked
58  */
59 static int rxrpc_create_local(struct rxrpc_local *local)
60 {
61         struct sock *sock;
62         int ret, opt;
63
64         _enter("%p{%d}", local, local->srx.transport_type);
65
66         /* create a socket to represent the local endpoint */
67         ret = sock_create_kern(PF_INET, local->srx.transport_type, IPPROTO_UDP,
68                                &local->socket);
69         if (ret < 0) {
70                 _leave(" = %d [socket]", ret);
71                 return ret;
72         }
73
74         /* if a local address was supplied then bind it */
75         if (local->srx.transport_len > sizeof(sa_family_t)) {
76                 _debug("bind");
77                 ret = kernel_bind(local->socket,
78                                   (struct sockaddr *) &local->srx.transport,
79                                   local->srx.transport_len);
80                 if (ret < 0) {
81                         _debug("bind failed");
82                         goto error;
83                 }
84         }
85
86         /* we want to receive ICMP errors */
87         opt = 1;
88         ret = kernel_setsockopt(local->socket, SOL_IP, IP_RECVERR,
89                                 (char *) &opt, sizeof(opt));
90         if (ret < 0) {
91                 _debug("setsockopt failed");
92                 goto error;
93         }
94
95         /* we want to set the don't fragment bit */
96         opt = IP_PMTUDISC_DO;
97         ret = kernel_setsockopt(local->socket, SOL_IP, IP_MTU_DISCOVER,
98                                 (char *) &opt, sizeof(opt));
99         if (ret < 0) {
100                 _debug("setsockopt failed");
101                 goto error;
102         }
103
104         write_lock_bh(&rxrpc_local_lock);
105         list_add(&local->link, &rxrpc_locals);
106         write_unlock_bh(&rxrpc_local_lock);
107
108         /* set the socket up */
109         sock = local->socket->sk;
110         sock->sk_user_data      = local;
111         sock->sk_data_ready     = rxrpc_data_ready;
112         sock->sk_error_report   = rxrpc_UDP_error_report;
113         _leave(" = 0");
114         return 0;
115
116 error:
117         kernel_sock_shutdown(local->socket, SHUT_RDWR);
118         local->socket->sk->sk_user_data = NULL;
119         sock_release(local->socket);
120         local->socket = NULL;
121
122         _leave(" = %d", ret);
123         return ret;
124 }
125
126 /*
127  * create a new local endpoint using the specified UDP address
128  */
129 struct rxrpc_local *rxrpc_lookup_local(struct sockaddr_rxrpc *srx)
130 {
131         struct rxrpc_local *local;
132         int ret;
133
134         _enter("{%d,%u,%u.%u.%u.%u+%hu}",
135                srx->transport_type,
136                srx->transport.family,
137                NIPQUAD(srx->transport.sin.sin_addr),
138                ntohs(srx->transport.sin.sin_port));
139
140         down_write(&rxrpc_local_sem);
141
142         /* see if we have a suitable local local endpoint already */
143         read_lock_bh(&rxrpc_local_lock);
144
145         list_for_each_entry(local, &rxrpc_locals, link) {
146                 _debug("CMP {%d,%u,%u.%u.%u.%u+%hu}",
147                        local->srx.transport_type,
148                        local->srx.transport.family,
149                        NIPQUAD(local->srx.transport.sin.sin_addr),
150                        ntohs(local->srx.transport.sin.sin_port));
151
152                 if (local->srx.transport_type != srx->transport_type ||
153                     local->srx.transport.family != srx->transport.family)
154                         continue;
155
156                 switch (srx->transport.family) {
157                 case AF_INET:
158                         if (local->srx.transport.sin.sin_port !=
159                             srx->transport.sin.sin_port)
160                                 continue;
161                         if (memcmp(&local->srx.transport.sin.sin_addr,
162                                    &srx->transport.sin.sin_addr,
163                                    sizeof(struct in_addr)) != 0)
164                                 continue;
165                         goto found_local;
166
167                 default:
168                         BUG();
169                 }
170         }
171
172         read_unlock_bh(&rxrpc_local_lock);
173
174         /* we didn't find one, so we need to create one */
175         local = rxrpc_alloc_local(srx);
176         if (!local) {
177                 up_write(&rxrpc_local_sem);
178                 return ERR_PTR(-ENOMEM);
179         }
180
181         ret = rxrpc_create_local(local);
182         if (ret < 0) {
183                 up_write(&rxrpc_local_sem);
184                 kfree(local);
185                 _leave(" = %d", ret);
186                 return ERR_PTR(ret);
187         }
188
189         up_write(&rxrpc_local_sem);
190
191         _net("LOCAL new %d {%d,%u,%u.%u.%u.%u+%hu}",
192              local->debug_id,
193              local->srx.transport_type,
194              local->srx.transport.family,
195              NIPQUAD(local->srx.transport.sin.sin_addr),
196              ntohs(local->srx.transport.sin.sin_port));
197
198         _leave(" = %p [new]", local);
199         return local;
200
201 found_local:
202         rxrpc_get_local(local);
203         read_unlock_bh(&rxrpc_local_lock);
204         up_write(&rxrpc_local_sem);
205
206         _net("LOCAL old %d {%d,%u,%u.%u.%u.%u+%hu}",
207              local->debug_id,
208              local->srx.transport_type,
209              local->srx.transport.family,
210              NIPQUAD(local->srx.transport.sin.sin_addr),
211              ntohs(local->srx.transport.sin.sin_port));
212
213         _leave(" = %p [reuse]", local);
214         return local;
215 }
216
217 /*
218  * release a local endpoint
219  */
220 void rxrpc_put_local(struct rxrpc_local *local)
221 {
222         _enter("%p{u=%d}", local, atomic_read(&local->usage));
223
224         ASSERTCMP(atomic_read(&local->usage), >, 0);
225
226         /* to prevent a race, the decrement and the dequeue must be effectively
227          * atomic */
228         write_lock_bh(&rxrpc_local_lock);
229         if (unlikely(atomic_dec_and_test(&local->usage))) {
230                 _debug("destroy local");
231                 rxrpc_queue_work(&local->destroyer);
232         }
233         write_unlock_bh(&rxrpc_local_lock);
234         _leave("");
235 }
236
237 /*
238  * destroy a local endpoint
239  */
240 static void rxrpc_destroy_local(struct work_struct *work)
241 {
242         struct rxrpc_local *local =
243                 container_of(work, struct rxrpc_local, destroyer);
244
245         _enter("%p{%d}", local, atomic_read(&local->usage));
246
247         down_write(&rxrpc_local_sem);
248
249         write_lock_bh(&rxrpc_local_lock);
250         if (atomic_read(&local->usage) > 0) {
251                 write_unlock_bh(&rxrpc_local_lock);
252                 up_read(&rxrpc_local_sem);
253                 _leave(" [resurrected]");
254                 return;
255         }
256
257         list_del(&local->link);
258         local->socket->sk->sk_user_data = NULL;
259         write_unlock_bh(&rxrpc_local_lock);
260
261         downgrade_write(&rxrpc_local_sem);
262
263         ASSERT(list_empty(&local->services));
264         ASSERT(!work_pending(&local->acceptor));
265         ASSERT(!work_pending(&local->rejecter));
266
267         /* finish cleaning up the local descriptor */
268         rxrpc_purge_queue(&local->accept_queue);
269         rxrpc_purge_queue(&local->reject_queue);
270         kernel_sock_shutdown(local->socket, SHUT_RDWR);
271         sock_release(local->socket);
272
273         up_read(&rxrpc_local_sem);
274
275         _net("DESTROY LOCAL %d", local->debug_id);
276         kfree(local);
277
278         if (list_empty(&rxrpc_locals))
279                 wake_up_all(&rxrpc_local_wq);
280
281         _leave("");
282 }
283
284 /*
285  * preemptively destroy all local local endpoint rather than waiting for
286  * them to be destroyed
287  */
288 void __exit rxrpc_destroy_all_locals(void)
289 {
290         DECLARE_WAITQUEUE(myself,current);
291
292         _enter("");
293
294         /* we simply have to wait for them to go away */
295         if (!list_empty(&rxrpc_locals)) {
296                 set_current_state(TASK_UNINTERRUPTIBLE);
297                 add_wait_queue(&rxrpc_local_wq, &myself);
298
299                 while (!list_empty(&rxrpc_locals)) {
300                         schedule();
301                         set_current_state(TASK_UNINTERRUPTIBLE);
302                 }
303
304                 remove_wait_queue(&rxrpc_local_wq, &myself);
305                 set_current_state(TASK_RUNNING);
306         }
307
308         _leave("");
309 }