diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /net/atm/svc.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'net/atm/svc.c')
-rw-r--r-- | net/atm/svc.c | 674 |
1 files changed, 674 insertions, 0 deletions
diff --git a/net/atm/svc.c b/net/atm/svc.c new file mode 100644 index 000000000000..02f5374a51f2 --- /dev/null +++ b/net/atm/svc.c | |||
@@ -0,0 +1,674 @@ | |||
1 | /* net/atm/svc.c - ATM SVC sockets */ | ||
2 | |||
3 | /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ | ||
4 | |||
5 | |||
6 | #include <linux/string.h> | ||
7 | #include <linux/net.h> /* struct socket, struct proto_ops */ | ||
8 | #include <linux/errno.h> /* error codes */ | ||
9 | #include <linux/kernel.h> /* printk */ | ||
10 | #include <linux/skbuff.h> | ||
11 | #include <linux/wait.h> | ||
12 | #include <linux/sched.h> /* jiffies and HZ */ | ||
13 | #include <linux/fcntl.h> /* O_NONBLOCK */ | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/atm.h> /* ATM stuff */ | ||
16 | #include <linux/atmsap.h> | ||
17 | #include <linux/atmsvc.h> | ||
18 | #include <linux/atmdev.h> | ||
19 | #include <linux/bitops.h> | ||
20 | #include <net/sock.h> /* for sock_no_* */ | ||
21 | #include <asm/uaccess.h> | ||
22 | |||
23 | #include "resources.h" | ||
24 | #include "common.h" /* common for PVCs and SVCs */ | ||
25 | #include "signaling.h" | ||
26 | #include "addr.h" | ||
27 | |||
28 | |||
29 | #if 0 | ||
30 | #define DPRINTK(format,args...) printk(KERN_DEBUG format,##args) | ||
31 | #else | ||
32 | #define DPRINTK(format,args...) | ||
33 | #endif | ||
34 | |||
35 | |||
36 | static int svc_create(struct socket *sock,int protocol); | ||
37 | |||
38 | |||
39 | /* | ||
40 | * Note: since all this is still nicely synchronized with the signaling demon, | ||
41 | * there's no need to protect sleep loops with clis. If signaling is | ||
42 | * moved into the kernel, that would change. | ||
43 | */ | ||
44 | |||
45 | |||
46 | static int svc_shutdown(struct socket *sock,int how) | ||
47 | { | ||
48 | return 0; | ||
49 | } | ||
50 | |||
51 | |||
52 | static void svc_disconnect(struct atm_vcc *vcc) | ||
53 | { | ||
54 | DEFINE_WAIT(wait); | ||
55 | struct sk_buff *skb; | ||
56 | struct sock *sk = sk_atm(vcc); | ||
57 | |||
58 | DPRINTK("svc_disconnect %p\n",vcc); | ||
59 | if (test_bit(ATM_VF_REGIS,&vcc->flags)) { | ||
60 | prepare_to_wait(sk->sk_sleep, &wait, TASK_UNINTERRUPTIBLE); | ||
61 | sigd_enq(vcc,as_close,NULL,NULL,NULL); | ||
62 | while (!test_bit(ATM_VF_RELEASED,&vcc->flags) && sigd) { | ||
63 | schedule(); | ||
64 | prepare_to_wait(sk->sk_sleep, &wait, TASK_UNINTERRUPTIBLE); | ||
65 | } | ||
66 | finish_wait(sk->sk_sleep, &wait); | ||
67 | } | ||
68 | /* beware - socket is still in use by atmsigd until the last | ||
69 | as_indicate has been answered */ | ||
70 | while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) { | ||
71 | atm_return(vcc, skb->truesize); | ||
72 | DPRINTK("LISTEN REL\n"); | ||
73 | sigd_enq2(NULL,as_reject,vcc,NULL,NULL,&vcc->qos,0); | ||
74 | dev_kfree_skb(skb); | ||
75 | } | ||
76 | clear_bit(ATM_VF_REGIS, &vcc->flags); | ||
77 | /* ... may retry later */ | ||
78 | } | ||
79 | |||
80 | |||
81 | static int svc_release(struct socket *sock) | ||
82 | { | ||
83 | struct sock *sk = sock->sk; | ||
84 | struct atm_vcc *vcc; | ||
85 | |||
86 | if (sk) { | ||
87 | vcc = ATM_SD(sock); | ||
88 | DPRINTK("svc_release %p\n", vcc); | ||
89 | clear_bit(ATM_VF_READY, &vcc->flags); | ||
90 | /* VCC pointer is used as a reference, so we must not free it | ||
91 | (thereby subjecting it to re-use) before all pending connections | ||
92 | are closed */ | ||
93 | svc_disconnect(vcc); | ||
94 | vcc_release(sock); | ||
95 | } | ||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | |||
100 | static int svc_bind(struct socket *sock,struct sockaddr *sockaddr, | ||
101 | int sockaddr_len) | ||
102 | { | ||
103 | DEFINE_WAIT(wait); | ||
104 | struct sock *sk = sock->sk; | ||
105 | struct sockaddr_atmsvc *addr; | ||
106 | struct atm_vcc *vcc; | ||
107 | int error; | ||
108 | |||
109 | if (sockaddr_len != sizeof(struct sockaddr_atmsvc)) | ||
110 | return -EINVAL; | ||
111 | lock_sock(sk); | ||
112 | if (sock->state == SS_CONNECTED) { | ||
113 | error = -EISCONN; | ||
114 | goto out; | ||
115 | } | ||
116 | if (sock->state != SS_UNCONNECTED) { | ||
117 | error = -EINVAL; | ||
118 | goto out; | ||
119 | } | ||
120 | vcc = ATM_SD(sock); | ||
121 | if (test_bit(ATM_VF_SESSION, &vcc->flags)) { | ||
122 | error = -EINVAL; | ||
123 | goto out; | ||
124 | } | ||
125 | addr = (struct sockaddr_atmsvc *) sockaddr; | ||
126 | if (addr->sas_family != AF_ATMSVC) { | ||
127 | error = -EAFNOSUPPORT; | ||
128 | goto out; | ||
129 | } | ||
130 | clear_bit(ATM_VF_BOUND,&vcc->flags); | ||
131 | /* failing rebind will kill old binding */ | ||
132 | /* @@@ check memory (de)allocation on rebind */ | ||
133 | if (!test_bit(ATM_VF_HASQOS,&vcc->flags)) { | ||
134 | error = -EBADFD; | ||
135 | goto out; | ||
136 | } | ||
137 | vcc->local = *addr; | ||
138 | set_bit(ATM_VF_WAITING, &vcc->flags); | ||
139 | prepare_to_wait(sk->sk_sleep, &wait, TASK_UNINTERRUPTIBLE); | ||
140 | sigd_enq(vcc,as_bind,NULL,NULL,&vcc->local); | ||
141 | while (test_bit(ATM_VF_WAITING, &vcc->flags) && sigd) { | ||
142 | schedule(); | ||
143 | prepare_to_wait(sk->sk_sleep, &wait, TASK_UNINTERRUPTIBLE); | ||
144 | } | ||
145 | finish_wait(sk->sk_sleep, &wait); | ||
146 | clear_bit(ATM_VF_REGIS,&vcc->flags); /* doesn't count */ | ||
147 | if (!sigd) { | ||
148 | error = -EUNATCH; | ||
149 | goto out; | ||
150 | } | ||
151 | if (!sk->sk_err) | ||
152 | set_bit(ATM_VF_BOUND,&vcc->flags); | ||
153 | error = -sk->sk_err; | ||
154 | out: | ||
155 | release_sock(sk); | ||
156 | return error; | ||
157 | } | ||
158 | |||
159 | |||
160 | static int svc_connect(struct socket *sock,struct sockaddr *sockaddr, | ||
161 | int sockaddr_len,int flags) | ||
162 | { | ||
163 | DEFINE_WAIT(wait); | ||
164 | struct sock *sk = sock->sk; | ||
165 | struct sockaddr_atmsvc *addr; | ||
166 | struct atm_vcc *vcc = ATM_SD(sock); | ||
167 | int error; | ||
168 | |||
169 | DPRINTK("svc_connect %p\n",vcc); | ||
170 | lock_sock(sk); | ||
171 | if (sockaddr_len != sizeof(struct sockaddr_atmsvc)) { | ||
172 | error = -EINVAL; | ||
173 | goto out; | ||
174 | } | ||
175 | |||
176 | switch (sock->state) { | ||
177 | default: | ||
178 | error = -EINVAL; | ||
179 | goto out; | ||
180 | case SS_CONNECTED: | ||
181 | error = -EISCONN; | ||
182 | goto out; | ||
183 | case SS_CONNECTING: | ||
184 | if (test_bit(ATM_VF_WAITING, &vcc->flags)) { | ||
185 | error = -EALREADY; | ||
186 | goto out; | ||
187 | } | ||
188 | sock->state = SS_UNCONNECTED; | ||
189 | if (sk->sk_err) { | ||
190 | error = -sk->sk_err; | ||
191 | goto out; | ||
192 | } | ||
193 | break; | ||
194 | case SS_UNCONNECTED: | ||
195 | addr = (struct sockaddr_atmsvc *) sockaddr; | ||
196 | if (addr->sas_family != AF_ATMSVC) { | ||
197 | error = -EAFNOSUPPORT; | ||
198 | goto out; | ||
199 | } | ||
200 | if (!test_bit(ATM_VF_HASQOS, &vcc->flags)) { | ||
201 | error = -EBADFD; | ||
202 | goto out; | ||
203 | } | ||
204 | if (vcc->qos.txtp.traffic_class == ATM_ANYCLASS || | ||
205 | vcc->qos.rxtp.traffic_class == ATM_ANYCLASS) { | ||
206 | error = -EINVAL; | ||
207 | goto out; | ||
208 | } | ||
209 | if (!vcc->qos.txtp.traffic_class && | ||
210 | !vcc->qos.rxtp.traffic_class) { | ||
211 | error = -EINVAL; | ||
212 | goto out; | ||
213 | } | ||
214 | vcc->remote = *addr; | ||
215 | set_bit(ATM_VF_WAITING, &vcc->flags); | ||
216 | prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); | ||
217 | sigd_enq(vcc,as_connect,NULL,NULL,&vcc->remote); | ||
218 | if (flags & O_NONBLOCK) { | ||
219 | finish_wait(sk->sk_sleep, &wait); | ||
220 | sock->state = SS_CONNECTING; | ||
221 | error = -EINPROGRESS; | ||
222 | goto out; | ||
223 | } | ||
224 | error = 0; | ||
225 | while (test_bit(ATM_VF_WAITING, &vcc->flags) && sigd) { | ||
226 | schedule(); | ||
227 | if (!signal_pending(current)) { | ||
228 | prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); | ||
229 | continue; | ||
230 | } | ||
231 | DPRINTK("*ABORT*\n"); | ||
232 | /* | ||
233 | * This is tricky: | ||
234 | * Kernel ---close--> Demon | ||
235 | * Kernel <--close--- Demon | ||
236 | * or | ||
237 | * Kernel ---close--> Demon | ||
238 | * Kernel <--error--- Demon | ||
239 | * or | ||
240 | * Kernel ---close--> Demon | ||
241 | * Kernel <--okay---- Demon | ||
242 | * Kernel <--close--- Demon | ||
243 | */ | ||
244 | sigd_enq(vcc,as_close,NULL,NULL,NULL); | ||
245 | while (test_bit(ATM_VF_WAITING, &vcc->flags) && sigd) { | ||
246 | prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); | ||
247 | schedule(); | ||
248 | } | ||
249 | if (!sk->sk_err) | ||
250 | while (!test_bit(ATM_VF_RELEASED,&vcc->flags) | ||
251 | && sigd) { | ||
252 | prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); | ||
253 | schedule(); | ||
254 | } | ||
255 | clear_bit(ATM_VF_REGIS,&vcc->flags); | ||
256 | clear_bit(ATM_VF_RELEASED,&vcc->flags); | ||
257 | clear_bit(ATM_VF_CLOSE,&vcc->flags); | ||
258 | /* we're gone now but may connect later */ | ||
259 | error = -EINTR; | ||
260 | break; | ||
261 | } | ||
262 | finish_wait(sk->sk_sleep, &wait); | ||
263 | if (error) | ||
264 | goto out; | ||
265 | if (!sigd) { | ||
266 | error = -EUNATCH; | ||
267 | goto out; | ||
268 | } | ||
269 | if (sk->sk_err) { | ||
270 | error = -sk->sk_err; | ||
271 | goto out; | ||
272 | } | ||
273 | } | ||
274 | /* | ||
275 | * Not supported yet | ||
276 | * | ||
277 | * #ifndef CONFIG_SINGLE_SIGITF | ||
278 | */ | ||
279 | vcc->qos.txtp.max_pcr = SELECT_TOP_PCR(vcc->qos.txtp); | ||
280 | vcc->qos.txtp.pcr = 0; | ||
281 | vcc->qos.txtp.min_pcr = 0; | ||
282 | /* | ||
283 | * #endif | ||
284 | */ | ||
285 | if (!(error = vcc_connect(sock, vcc->itf, vcc->vpi, vcc->vci))) | ||
286 | sock->state = SS_CONNECTED; | ||
287 | else | ||
288 | (void) svc_disconnect(vcc); | ||
289 | out: | ||
290 | release_sock(sk); | ||
291 | return error; | ||
292 | } | ||
293 | |||
294 | |||
295 | static int svc_listen(struct socket *sock,int backlog) | ||
296 | { | ||
297 | DEFINE_WAIT(wait); | ||
298 | struct sock *sk = sock->sk; | ||
299 | struct atm_vcc *vcc = ATM_SD(sock); | ||
300 | int error; | ||
301 | |||
302 | DPRINTK("svc_listen %p\n",vcc); | ||
303 | lock_sock(sk); | ||
304 | /* let server handle listen on unbound sockets */ | ||
305 | if (test_bit(ATM_VF_SESSION,&vcc->flags)) { | ||
306 | error = -EINVAL; | ||
307 | goto out; | ||
308 | } | ||
309 | set_bit(ATM_VF_WAITING, &vcc->flags); | ||
310 | prepare_to_wait(sk->sk_sleep, &wait, TASK_UNINTERRUPTIBLE); | ||
311 | sigd_enq(vcc,as_listen,NULL,NULL,&vcc->local); | ||
312 | while (test_bit(ATM_VF_WAITING, &vcc->flags) && sigd) { | ||
313 | schedule(); | ||
314 | prepare_to_wait(sk->sk_sleep, &wait, TASK_UNINTERRUPTIBLE); | ||
315 | } | ||
316 | finish_wait(sk->sk_sleep, &wait); | ||
317 | if (!sigd) { | ||
318 | error = -EUNATCH; | ||
319 | goto out; | ||
320 | } | ||
321 | set_bit(ATM_VF_LISTEN,&vcc->flags); | ||
322 | sk->sk_max_ack_backlog = backlog > 0 ? backlog : ATM_BACKLOG_DEFAULT; | ||
323 | error = -sk->sk_err; | ||
324 | out: | ||
325 | release_sock(sk); | ||
326 | return error; | ||
327 | } | ||
328 | |||
329 | |||
330 | static int svc_accept(struct socket *sock,struct socket *newsock,int flags) | ||
331 | { | ||
332 | struct sock *sk = sock->sk; | ||
333 | struct sk_buff *skb; | ||
334 | struct atmsvc_msg *msg; | ||
335 | struct atm_vcc *old_vcc = ATM_SD(sock); | ||
336 | struct atm_vcc *new_vcc; | ||
337 | int error; | ||
338 | |||
339 | lock_sock(sk); | ||
340 | |||
341 | error = svc_create(newsock,0); | ||
342 | if (error) | ||
343 | goto out; | ||
344 | |||
345 | new_vcc = ATM_SD(newsock); | ||
346 | |||
347 | DPRINTK("svc_accept %p -> %p\n",old_vcc,new_vcc); | ||
348 | while (1) { | ||
349 | DEFINE_WAIT(wait); | ||
350 | |||
351 | prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); | ||
352 | while (!(skb = skb_dequeue(&sk->sk_receive_queue)) && | ||
353 | sigd) { | ||
354 | if (test_bit(ATM_VF_RELEASED,&old_vcc->flags)) break; | ||
355 | if (test_bit(ATM_VF_CLOSE,&old_vcc->flags)) { | ||
356 | error = -sk->sk_err; | ||
357 | break; | ||
358 | } | ||
359 | if (flags & O_NONBLOCK) { | ||
360 | error = -EAGAIN; | ||
361 | break; | ||
362 | } | ||
363 | release_sock(sk); | ||
364 | schedule(); | ||
365 | lock_sock(sk); | ||
366 | if (signal_pending(current)) { | ||
367 | error = -ERESTARTSYS; | ||
368 | break; | ||
369 | } | ||
370 | prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); | ||
371 | } | ||
372 | finish_wait(sk->sk_sleep, &wait); | ||
373 | if (error) | ||
374 | goto out; | ||
375 | if (!skb) { | ||
376 | error = -EUNATCH; | ||
377 | goto out; | ||
378 | } | ||
379 | msg = (struct atmsvc_msg *) skb->data; | ||
380 | new_vcc->qos = msg->qos; | ||
381 | set_bit(ATM_VF_HASQOS,&new_vcc->flags); | ||
382 | new_vcc->remote = msg->svc; | ||
383 | new_vcc->local = msg->local; | ||
384 | new_vcc->sap = msg->sap; | ||
385 | error = vcc_connect(newsock, msg->pvc.sap_addr.itf, | ||
386 | msg->pvc.sap_addr.vpi, msg->pvc.sap_addr.vci); | ||
387 | dev_kfree_skb(skb); | ||
388 | sk->sk_ack_backlog--; | ||
389 | if (error) { | ||
390 | sigd_enq2(NULL,as_reject,old_vcc,NULL,NULL, | ||
391 | &old_vcc->qos,error); | ||
392 | error = error == -EAGAIN ? -EBUSY : error; | ||
393 | goto out; | ||
394 | } | ||
395 | /* wait should be short, so we ignore the non-blocking flag */ | ||
396 | set_bit(ATM_VF_WAITING, &new_vcc->flags); | ||
397 | prepare_to_wait(sk_atm(new_vcc)->sk_sleep, &wait, TASK_UNINTERRUPTIBLE); | ||
398 | sigd_enq(new_vcc,as_accept,old_vcc,NULL,NULL); | ||
399 | while (test_bit(ATM_VF_WAITING, &new_vcc->flags) && sigd) { | ||
400 | release_sock(sk); | ||
401 | schedule(); | ||
402 | lock_sock(sk); | ||
403 | prepare_to_wait(sk_atm(new_vcc)->sk_sleep, &wait, TASK_UNINTERRUPTIBLE); | ||
404 | } | ||
405 | finish_wait(sk_atm(new_vcc)->sk_sleep, &wait); | ||
406 | if (!sigd) { | ||
407 | error = -EUNATCH; | ||
408 | goto out; | ||
409 | } | ||
410 | if (!sk_atm(new_vcc)->sk_err) | ||
411 | break; | ||
412 | if (sk_atm(new_vcc)->sk_err != ERESTARTSYS) { | ||
413 | error = -sk_atm(new_vcc)->sk_err; | ||
414 | goto out; | ||
415 | } | ||
416 | } | ||
417 | newsock->state = SS_CONNECTED; | ||
418 | out: | ||
419 | release_sock(sk); | ||
420 | return error; | ||
421 | } | ||
422 | |||
423 | |||
424 | static int svc_getname(struct socket *sock,struct sockaddr *sockaddr, | ||
425 | int *sockaddr_len,int peer) | ||
426 | { | ||
427 | struct sockaddr_atmsvc *addr; | ||
428 | |||
429 | *sockaddr_len = sizeof(struct sockaddr_atmsvc); | ||
430 | addr = (struct sockaddr_atmsvc *) sockaddr; | ||
431 | memcpy(addr,peer ? &ATM_SD(sock)->remote : &ATM_SD(sock)->local, | ||
432 | sizeof(struct sockaddr_atmsvc)); | ||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | |||
437 | int svc_change_qos(struct atm_vcc *vcc,struct atm_qos *qos) | ||
438 | { | ||
439 | struct sock *sk = sk_atm(vcc); | ||
440 | DEFINE_WAIT(wait); | ||
441 | |||
442 | set_bit(ATM_VF_WAITING, &vcc->flags); | ||
443 | prepare_to_wait(sk->sk_sleep, &wait, TASK_UNINTERRUPTIBLE); | ||
444 | sigd_enq2(vcc,as_modify,NULL,NULL,&vcc->local,qos,0); | ||
445 | while (test_bit(ATM_VF_WAITING, &vcc->flags) && | ||
446 | !test_bit(ATM_VF_RELEASED, &vcc->flags) && sigd) { | ||
447 | schedule(); | ||
448 | prepare_to_wait(sk->sk_sleep, &wait, TASK_UNINTERRUPTIBLE); | ||
449 | } | ||
450 | finish_wait(sk->sk_sleep, &wait); | ||
451 | if (!sigd) return -EUNATCH; | ||
452 | return -sk->sk_err; | ||
453 | } | ||
454 | |||
455 | |||
456 | static int svc_setsockopt(struct socket *sock, int level, int optname, | ||
457 | char __user *optval, int optlen) | ||
458 | { | ||
459 | struct sock *sk = sock->sk; | ||
460 | struct atm_vcc *vcc = ATM_SD(sock); | ||
461 | int value, error = 0; | ||
462 | |||
463 | lock_sock(sk); | ||
464 | switch (optname) { | ||
465 | case SO_ATMSAP: | ||
466 | if (level != SOL_ATM || optlen != sizeof(struct atm_sap)) { | ||
467 | error = -EINVAL; | ||
468 | goto out; | ||
469 | } | ||
470 | if (copy_from_user(&vcc->sap, optval, optlen)) { | ||
471 | error = -EFAULT; | ||
472 | goto out; | ||
473 | } | ||
474 | set_bit(ATM_VF_HASSAP, &vcc->flags); | ||
475 | break; | ||
476 | case SO_MULTIPOINT: | ||
477 | if (level != SOL_ATM || optlen != sizeof(int)) { | ||
478 | error = -EINVAL; | ||
479 | goto out; | ||
480 | } | ||
481 | if (get_user(value, (int __user *) optval)) { | ||
482 | error = -EFAULT; | ||
483 | goto out; | ||
484 | } | ||
485 | if (value == 1) { | ||
486 | set_bit(ATM_VF_SESSION, &vcc->flags); | ||
487 | } else if (value == 0) { | ||
488 | clear_bit(ATM_VF_SESSION, &vcc->flags); | ||
489 | } else { | ||
490 | error = -EINVAL; | ||
491 | } | ||
492 | break; | ||
493 | default: | ||
494 | error = vcc_setsockopt(sock, level, optname, | ||
495 | optval, optlen); | ||
496 | } | ||
497 | |||
498 | out: | ||
499 | release_sock(sk); | ||
500 | return error; | ||
501 | } | ||
502 | |||
503 | |||
504 | static int svc_getsockopt(struct socket *sock,int level,int optname, | ||
505 | char __user *optval,int __user *optlen) | ||
506 | { | ||
507 | struct sock *sk = sock->sk; | ||
508 | int error = 0, len; | ||
509 | |||
510 | lock_sock(sk); | ||
511 | if (!__SO_LEVEL_MATCH(optname, level) || optname != SO_ATMSAP) { | ||
512 | error = vcc_getsockopt(sock, level, optname, optval, optlen); | ||
513 | goto out; | ||
514 | } | ||
515 | if (get_user(len, optlen)) { | ||
516 | error = -EFAULT; | ||
517 | goto out; | ||
518 | } | ||
519 | if (len != sizeof(struct atm_sap)) { | ||
520 | error = -EINVAL; | ||
521 | goto out; | ||
522 | } | ||
523 | if (copy_to_user(optval, &ATM_SD(sock)->sap, sizeof(struct atm_sap))) { | ||
524 | error = -EFAULT; | ||
525 | goto out; | ||
526 | } | ||
527 | out: | ||
528 | release_sock(sk); | ||
529 | return error; | ||
530 | } | ||
531 | |||
532 | |||
533 | static int svc_addparty(struct socket *sock, struct sockaddr *sockaddr, | ||
534 | int sockaddr_len, int flags) | ||
535 | { | ||
536 | DEFINE_WAIT(wait); | ||
537 | struct sock *sk = sock->sk; | ||
538 | struct atm_vcc *vcc = ATM_SD(sock); | ||
539 | int error; | ||
540 | |||
541 | lock_sock(sk); | ||
542 | set_bit(ATM_VF_WAITING, &vcc->flags); | ||
543 | prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); | ||
544 | sigd_enq(vcc, as_addparty, NULL, NULL, | ||
545 | (struct sockaddr_atmsvc *) sockaddr); | ||
546 | if (flags & O_NONBLOCK) { | ||
547 | finish_wait(sk->sk_sleep, &wait); | ||
548 | error = -EINPROGRESS; | ||
549 | goto out; | ||
550 | } | ||
551 | DPRINTK("svc_addparty added wait queue\n"); | ||
552 | while (test_bit(ATM_VF_WAITING, &vcc->flags) && sigd) { | ||
553 | schedule(); | ||
554 | prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); | ||
555 | } | ||
556 | finish_wait(sk->sk_sleep, &wait); | ||
557 | error = xchg(&sk->sk_err_soft, 0); | ||
558 | out: | ||
559 | release_sock(sk); | ||
560 | return error; | ||
561 | } | ||
562 | |||
563 | |||
564 | static int svc_dropparty(struct socket *sock, int ep_ref) | ||
565 | { | ||
566 | DEFINE_WAIT(wait); | ||
567 | struct sock *sk = sock->sk; | ||
568 | struct atm_vcc *vcc = ATM_SD(sock); | ||
569 | int error; | ||
570 | |||
571 | lock_sock(sk); | ||
572 | set_bit(ATM_VF_WAITING, &vcc->flags); | ||
573 | prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); | ||
574 | sigd_enq2(vcc, as_dropparty, NULL, NULL, NULL, NULL, ep_ref); | ||
575 | while (test_bit(ATM_VF_WAITING, &vcc->flags) && sigd) { | ||
576 | schedule(); | ||
577 | prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); | ||
578 | } | ||
579 | finish_wait(sk->sk_sleep, &wait); | ||
580 | if (!sigd) { | ||
581 | error = -EUNATCH; | ||
582 | goto out; | ||
583 | } | ||
584 | error = xchg(&sk->sk_err_soft, 0); | ||
585 | out: | ||
586 | release_sock(sk); | ||
587 | return error; | ||
588 | } | ||
589 | |||
590 | |||
591 | static int svc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) | ||
592 | { | ||
593 | int error, ep_ref; | ||
594 | struct sockaddr_atmsvc sa; | ||
595 | struct atm_vcc *vcc = ATM_SD(sock); | ||
596 | |||
597 | switch (cmd) { | ||
598 | case ATM_ADDPARTY: | ||
599 | if (!test_bit(ATM_VF_SESSION, &vcc->flags)) | ||
600 | return -EINVAL; | ||
601 | if (copy_from_user(&sa, (void __user *) arg, sizeof(sa))) | ||
602 | return -EFAULT; | ||
603 | error = svc_addparty(sock, (struct sockaddr *) &sa, sizeof(sa), 0); | ||
604 | break; | ||
605 | case ATM_DROPPARTY: | ||
606 | if (!test_bit(ATM_VF_SESSION, &vcc->flags)) | ||
607 | return -EINVAL; | ||
608 | if (copy_from_user(&ep_ref, (void __user *) arg, sizeof(int))) | ||
609 | return -EFAULT; | ||
610 | error = svc_dropparty(sock, ep_ref); | ||
611 | break; | ||
612 | default: | ||
613 | error = vcc_ioctl(sock, cmd, arg); | ||
614 | } | ||
615 | |||
616 | return error; | ||
617 | } | ||
618 | |||
619 | static struct proto_ops svc_proto_ops = { | ||
620 | .family = PF_ATMSVC, | ||
621 | .owner = THIS_MODULE, | ||
622 | |||
623 | .release = svc_release, | ||
624 | .bind = svc_bind, | ||
625 | .connect = svc_connect, | ||
626 | .socketpair = sock_no_socketpair, | ||
627 | .accept = svc_accept, | ||
628 | .getname = svc_getname, | ||
629 | .poll = vcc_poll, | ||
630 | .ioctl = svc_ioctl, | ||
631 | .listen = svc_listen, | ||
632 | .shutdown = svc_shutdown, | ||
633 | .setsockopt = svc_setsockopt, | ||
634 | .getsockopt = svc_getsockopt, | ||
635 | .sendmsg = vcc_sendmsg, | ||
636 | .recvmsg = vcc_recvmsg, | ||
637 | .mmap = sock_no_mmap, | ||
638 | .sendpage = sock_no_sendpage, | ||
639 | }; | ||
640 | |||
641 | |||
642 | static int svc_create(struct socket *sock,int protocol) | ||
643 | { | ||
644 | int error; | ||
645 | |||
646 | sock->ops = &svc_proto_ops; | ||
647 | error = vcc_create(sock, protocol, AF_ATMSVC); | ||
648 | if (error) return error; | ||
649 | ATM_SD(sock)->local.sas_family = AF_ATMSVC; | ||
650 | ATM_SD(sock)->remote.sas_family = AF_ATMSVC; | ||
651 | return 0; | ||
652 | } | ||
653 | |||
654 | |||
655 | static struct net_proto_family svc_family_ops = { | ||
656 | .family = PF_ATMSVC, | ||
657 | .create = svc_create, | ||
658 | .owner = THIS_MODULE, | ||
659 | }; | ||
660 | |||
661 | |||
662 | /* | ||
663 | * Initialize the ATM SVC protocol family | ||
664 | */ | ||
665 | |||
666 | int __init atmsvc_init(void) | ||
667 | { | ||
668 | return sock_register(&svc_family_ops); | ||
669 | } | ||
670 | |||
671 | void atmsvc_exit(void) | ||
672 | { | ||
673 | sock_unregister(PF_ATMSVC); | ||
674 | } | ||