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