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