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