diff options
Diffstat (limited to 'net/econet/af_econet.c')
-rw-r--r-- | net/econet/af_econet.c | 140 |
1 files changed, 85 insertions, 55 deletions
diff --git a/net/econet/af_econet.c b/net/econet/af_econet.c index 0c4c83bb2a59..868265619dbb 100644 --- a/net/econet/af_econet.c +++ b/net/econet/af_econet.c | |||
@@ -42,6 +42,7 @@ | |||
42 | #include <linux/spinlock.h> | 42 | #include <linux/spinlock.h> |
43 | #include <linux/rcupdate.h> | 43 | #include <linux/rcupdate.h> |
44 | #include <linux/bitops.h> | 44 | #include <linux/bitops.h> |
45 | #include <linux/mutex.h> | ||
45 | 46 | ||
46 | #include <asm/uaccess.h> | 47 | #include <asm/uaccess.h> |
47 | #include <asm/system.h> | 48 | #include <asm/system.h> |
@@ -49,6 +50,7 @@ | |||
49 | static const struct proto_ops econet_ops; | 50 | static const struct proto_ops econet_ops; |
50 | static struct hlist_head econet_sklist; | 51 | static struct hlist_head econet_sklist; |
51 | static DEFINE_RWLOCK(econet_lock); | 52 | static DEFINE_RWLOCK(econet_lock); |
53 | static DEFINE_MUTEX(econet_mutex); | ||
52 | 54 | ||
53 | /* Since there are only 256 possible network numbers (or fewer, depends | 55 | /* Since there are only 256 possible network numbers (or fewer, depends |
54 | how you count) it makes sense to use a simple lookup table. */ | 56 | how you count) it makes sense to use a simple lookup table. */ |
@@ -124,6 +126,8 @@ static int econet_recvmsg(struct kiocb *iocb, struct socket *sock, | |||
124 | 126 | ||
125 | msg->msg_namelen = sizeof(struct sockaddr_ec); | 127 | msg->msg_namelen = sizeof(struct sockaddr_ec); |
126 | 128 | ||
129 | mutex_lock(&econet_mutex); | ||
130 | |||
127 | /* | 131 | /* |
128 | * Call the generic datagram receiver. This handles all sorts | 132 | * Call the generic datagram receiver. This handles all sorts |
129 | * of horrible races and re-entrancy so we can forget about it | 133 | * of horrible races and re-entrancy so we can forget about it |
@@ -174,6 +178,7 @@ static int econet_recvmsg(struct kiocb *iocb, struct socket *sock, | |||
174 | out_free: | 178 | out_free: |
175 | skb_free_datagram(sk, skb); | 179 | skb_free_datagram(sk, skb); |
176 | out: | 180 | out: |
181 | mutex_unlock(&econet_mutex); | ||
177 | return err; | 182 | return err; |
178 | } | 183 | } |
179 | 184 | ||
@@ -184,8 +189,8 @@ out: | |||
184 | static int econet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) | 189 | static int econet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) |
185 | { | 190 | { |
186 | struct sockaddr_ec *sec = (struct sockaddr_ec *)uaddr; | 191 | struct sockaddr_ec *sec = (struct sockaddr_ec *)uaddr; |
187 | struct sock *sk=sock->sk; | 192 | struct sock *sk; |
188 | struct econet_sock *eo = ec_sk(sk); | 193 | struct econet_sock *eo; |
189 | 194 | ||
190 | /* | 195 | /* |
191 | * Check legality | 196 | * Check legality |
@@ -195,11 +200,18 @@ static int econet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len | |||
195 | sec->sec_family != AF_ECONET) | 200 | sec->sec_family != AF_ECONET) |
196 | return -EINVAL; | 201 | return -EINVAL; |
197 | 202 | ||
203 | mutex_lock(&econet_mutex); | ||
204 | |||
205 | sk = sock->sk; | ||
206 | eo = ec_sk(sk); | ||
207 | |||
198 | eo->cb = sec->cb; | 208 | eo->cb = sec->cb; |
199 | eo->port = sec->port; | 209 | eo->port = sec->port; |
200 | eo->station = sec->addr.station; | 210 | eo->station = sec->addr.station; |
201 | eo->net = sec->addr.net; | 211 | eo->net = sec->addr.net; |
202 | 212 | ||
213 | mutex_unlock(&econet_mutex); | ||
214 | |||
203 | return 0; | 215 | return 0; |
204 | } | 216 | } |
205 | 217 | ||
@@ -284,6 +296,8 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock, | |||
284 | * Get and verify the address. | 296 | * Get and verify the address. |
285 | */ | 297 | */ |
286 | 298 | ||
299 | mutex_lock(&econet_mutex); | ||
300 | |||
287 | if (saddr == NULL) { | 301 | if (saddr == NULL) { |
288 | struct econet_sock *eo = ec_sk(sk); | 302 | struct econet_sock *eo = ec_sk(sk); |
289 | 303 | ||
@@ -292,8 +306,10 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock, | |||
292 | port = eo->port; | 306 | port = eo->port; |
293 | cb = eo->cb; | 307 | cb = eo->cb; |
294 | } else { | 308 | } else { |
295 | if (msg->msg_namelen < sizeof(struct sockaddr_ec)) | 309 | if (msg->msg_namelen < sizeof(struct sockaddr_ec)) { |
310 | mutex_unlock(&econet_mutex); | ||
296 | return -EINVAL; | 311 | return -EINVAL; |
312 | } | ||
297 | addr.station = saddr->addr.station; | 313 | addr.station = saddr->addr.station; |
298 | addr.net = saddr->addr.net; | 314 | addr.net = saddr->addr.net; |
299 | port = saddr->port; | 315 | port = saddr->port; |
@@ -304,19 +320,21 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock, | |||
304 | dev = net2dev_map[addr.net]; | 320 | dev = net2dev_map[addr.net]; |
305 | 321 | ||
306 | /* If not directly reachable, use some default */ | 322 | /* If not directly reachable, use some default */ |
307 | if (dev == NULL) | 323 | if (dev == NULL) { |
308 | { | ||
309 | dev = net2dev_map[0]; | 324 | dev = net2dev_map[0]; |
310 | /* No interfaces at all? */ | 325 | /* No interfaces at all? */ |
311 | if (dev == NULL) | 326 | if (dev == NULL) { |
327 | mutex_unlock(&econet_mutex); | ||
312 | return -ENETDOWN; | 328 | return -ENETDOWN; |
329 | } | ||
313 | } | 330 | } |
314 | 331 | ||
315 | if (len + 15 > dev->mtu) | 332 | if (len + 15 > dev->mtu) { |
333 | mutex_unlock(&econet_mutex); | ||
316 | return -EMSGSIZE; | 334 | return -EMSGSIZE; |
335 | } | ||
317 | 336 | ||
318 | if (dev->type == ARPHRD_ECONET) | 337 | if (dev->type == ARPHRD_ECONET) { |
319 | { | ||
320 | /* Real hardware Econet. We're not worthy etc. */ | 338 | /* Real hardware Econet. We're not worthy etc. */ |
321 | #ifdef CONFIG_ECONET_NATIVE | 339 | #ifdef CONFIG_ECONET_NATIVE |
322 | unsigned short proto = 0; | 340 | unsigned short proto = 0; |
@@ -374,6 +392,7 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock, | |||
374 | 392 | ||
375 | dev_queue_xmit(skb); | 393 | dev_queue_xmit(skb); |
376 | dev_put(dev); | 394 | dev_put(dev); |
395 | mutex_unlock(&econet_mutex); | ||
377 | return(len); | 396 | return(len); |
378 | 397 | ||
379 | out_free: | 398 | out_free: |
@@ -384,14 +403,18 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock, | |||
384 | #else | 403 | #else |
385 | err = -EPROTOTYPE; | 404 | err = -EPROTOTYPE; |
386 | #endif | 405 | #endif |
406 | mutex_unlock(&econet_mutex); | ||
407 | |||
387 | return err; | 408 | return err; |
388 | } | 409 | } |
389 | 410 | ||
390 | #ifdef CONFIG_ECONET_AUNUDP | 411 | #ifdef CONFIG_ECONET_AUNUDP |
391 | /* AUN virtual Econet. */ | 412 | /* AUN virtual Econet. */ |
392 | 413 | ||
393 | if (udpsock == NULL) | 414 | if (udpsock == NULL) { |
415 | mutex_unlock(&econet_mutex); | ||
394 | return -ENETDOWN; /* No socket - can't send */ | 416 | return -ENETDOWN; /* No socket - can't send */ |
417 | } | ||
395 | 418 | ||
396 | /* Make up a UDP datagram and hand it off to some higher intellect. */ | 419 | /* Make up a UDP datagram and hand it off to some higher intellect. */ |
397 | 420 | ||
@@ -438,8 +461,10 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock, | |||
438 | void __user *base = msg->msg_iov[i].iov_base; | 461 | void __user *base = msg->msg_iov[i].iov_base; |
439 | size_t len = msg->msg_iov[i].iov_len; | 462 | size_t len = msg->msg_iov[i].iov_len; |
440 | /* Check it now since we switch to KERNEL_DS later. */ | 463 | /* Check it now since we switch to KERNEL_DS later. */ |
441 | if (!access_ok(VERIFY_READ, base, len)) | 464 | if (!access_ok(VERIFY_READ, base, len)) { |
465 | mutex_unlock(&econet_mutex); | ||
442 | return -EFAULT; | 466 | return -EFAULT; |
467 | } | ||
443 | iov[i+1].iov_base = base; | 468 | iov[i+1].iov_base = base; |
444 | iov[i+1].iov_len = len; | 469 | iov[i+1].iov_len = len; |
445 | size += len; | 470 | size += len; |
@@ -447,8 +472,11 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock, | |||
447 | 472 | ||
448 | /* Get a skbuff (no data, just holds our cb information) */ | 473 | /* Get a skbuff (no data, just holds our cb information) */ |
449 | if ((skb = sock_alloc_send_skb(sk, 0, | 474 | if ((skb = sock_alloc_send_skb(sk, 0, |
450 | msg->msg_flags & MSG_DONTWAIT, &err)) == NULL) | 475 | msg->msg_flags & MSG_DONTWAIT, |
476 | &err)) == NULL) { | ||
477 | mutex_unlock(&econet_mutex); | ||
451 | return err; | 478 | return err; |
479 | } | ||
452 | 480 | ||
453 | eb = (struct ec_cb *)&skb->cb; | 481 | eb = (struct ec_cb *)&skb->cb; |
454 | 482 | ||
@@ -475,6 +503,8 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock, | |||
475 | #else | 503 | #else |
476 | err = -EPROTOTYPE; | 504 | err = -EPROTOTYPE; |
477 | #endif | 505 | #endif |
506 | mutex_unlock(&econet_mutex); | ||
507 | |||
478 | return err; | 508 | return err; |
479 | } | 509 | } |
480 | 510 | ||
@@ -485,18 +515,25 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock, | |||
485 | static int econet_getname(struct socket *sock, struct sockaddr *uaddr, | 515 | static int econet_getname(struct socket *sock, struct sockaddr *uaddr, |
486 | int *uaddr_len, int peer) | 516 | int *uaddr_len, int peer) |
487 | { | 517 | { |
488 | struct sock *sk = sock->sk; | 518 | struct sock *sk; |
489 | struct econet_sock *eo = ec_sk(sk); | 519 | struct econet_sock *eo; |
490 | struct sockaddr_ec *sec = (struct sockaddr_ec *)uaddr; | 520 | struct sockaddr_ec *sec = (struct sockaddr_ec *)uaddr; |
491 | 521 | ||
492 | if (peer) | 522 | if (peer) |
493 | return -EOPNOTSUPP; | 523 | return -EOPNOTSUPP; |
494 | 524 | ||
525 | mutex_lock(&econet_mutex); | ||
526 | |||
527 | sk = sock->sk; | ||
528 | eo = ec_sk(sk); | ||
529 | |||
495 | sec->sec_family = AF_ECONET; | 530 | sec->sec_family = AF_ECONET; |
496 | sec->port = eo->port; | 531 | sec->port = eo->port; |
497 | sec->addr.station = eo->station; | 532 | sec->addr.station = eo->station; |
498 | sec->addr.net = eo->net; | 533 | sec->addr.net = eo->net; |
499 | 534 | ||
535 | mutex_unlock(&econet_mutex); | ||
536 | |||
500 | *uaddr_len = sizeof(*sec); | 537 | *uaddr_len = sizeof(*sec); |
501 | return 0; | 538 | return 0; |
502 | } | 539 | } |
@@ -522,10 +559,13 @@ static void econet_destroy_timer(unsigned long data) | |||
522 | 559 | ||
523 | static int econet_release(struct socket *sock) | 560 | static int econet_release(struct socket *sock) |
524 | { | 561 | { |
525 | struct sock *sk = sock->sk; | 562 | struct sock *sk; |
526 | 563 | ||
564 | mutex_lock(&econet_mutex); | ||
565 | |||
566 | sk = sock->sk; | ||
527 | if (!sk) | 567 | if (!sk) |
528 | return 0; | 568 | goto out_unlock; |
529 | 569 | ||
530 | econet_remove_socket(&econet_sklist, sk); | 570 | econet_remove_socket(&econet_sklist, sk); |
531 | 571 | ||
@@ -549,10 +589,14 @@ static int econet_release(struct socket *sock) | |||
549 | sk->sk_timer.expires = jiffies + HZ; | 589 | sk->sk_timer.expires = jiffies + HZ; |
550 | sk->sk_timer.function = econet_destroy_timer; | 590 | sk->sk_timer.function = econet_destroy_timer; |
551 | add_timer(&sk->sk_timer); | 591 | add_timer(&sk->sk_timer); |
552 | return 0; | 592 | |
593 | goto out_unlock; | ||
553 | } | 594 | } |
554 | 595 | ||
555 | sk_free(sk); | 596 | sk_free(sk); |
597 | |||
598 | out_unlock: | ||
599 | mutex_unlock(&econet_mutex); | ||
556 | return 0; | 600 | return 0; |
557 | } | 601 | } |
558 | 602 | ||
@@ -608,6 +652,7 @@ static int ec_dev_ioctl(struct socket *sock, unsigned int cmd, void __user *arg) | |||
608 | struct ec_device *edev; | 652 | struct ec_device *edev; |
609 | struct net_device *dev; | 653 | struct net_device *dev; |
610 | struct sockaddr_ec *sec; | 654 | struct sockaddr_ec *sec; |
655 | int err; | ||
611 | 656 | ||
612 | /* | 657 | /* |
613 | * Fetch the caller's info block into kernel space | 658 | * Fetch the caller's info block into kernel space |
@@ -621,38 +666,35 @@ static int ec_dev_ioctl(struct socket *sock, unsigned int cmd, void __user *arg) | |||
621 | 666 | ||
622 | sec = (struct sockaddr_ec *)&ifr.ifr_addr; | 667 | sec = (struct sockaddr_ec *)&ifr.ifr_addr; |
623 | 668 | ||
624 | switch (cmd) | 669 | mutex_lock(&econet_mutex); |
625 | { | 670 | |
671 | err = 0; | ||
672 | switch (cmd) { | ||
626 | case SIOCSIFADDR: | 673 | case SIOCSIFADDR: |
627 | edev = dev->ec_ptr; | 674 | edev = dev->ec_ptr; |
628 | if (edev == NULL) | 675 | if (edev == NULL) { |
629 | { | ||
630 | /* Magic up a new one. */ | 676 | /* Magic up a new one. */ |
631 | edev = kmalloc(sizeof(struct ec_device), GFP_KERNEL); | 677 | edev = kmalloc(sizeof(struct ec_device), GFP_KERNEL); |
632 | if (edev == NULL) { | 678 | if (edev == NULL) { |
633 | printk("af_ec: memory squeeze.\n"); | 679 | err = -ENOMEM; |
634 | dev_put(dev); | 680 | break; |
635 | return -ENOMEM; | ||
636 | } | 681 | } |
637 | memset(edev, 0, sizeof(struct ec_device)); | 682 | memset(edev, 0, sizeof(struct ec_device)); |
638 | dev->ec_ptr = edev; | 683 | dev->ec_ptr = edev; |
639 | } | 684 | } else |
640 | else | ||
641 | net2dev_map[edev->net] = NULL; | 685 | net2dev_map[edev->net] = NULL; |
642 | edev->station = sec->addr.station; | 686 | edev->station = sec->addr.station; |
643 | edev->net = sec->addr.net; | 687 | edev->net = sec->addr.net; |
644 | net2dev_map[sec->addr.net] = dev; | 688 | net2dev_map[sec->addr.net] = dev; |
645 | if (!net2dev_map[0]) | 689 | if (!net2dev_map[0]) |
646 | net2dev_map[0] = dev; | 690 | net2dev_map[0] = dev; |
647 | dev_put(dev); | 691 | break; |
648 | return 0; | ||
649 | 692 | ||
650 | case SIOCGIFADDR: | 693 | case SIOCGIFADDR: |
651 | edev = dev->ec_ptr; | 694 | edev = dev->ec_ptr; |
652 | if (edev == NULL) | 695 | if (edev == NULL) { |
653 | { | 696 | err = -ENODEV; |
654 | dev_put(dev); | 697 | break; |
655 | return -ENODEV; | ||
656 | } | 698 | } |
657 | memset(sec, 0, sizeof(struct sockaddr_ec)); | 699 | memset(sec, 0, sizeof(struct sockaddr_ec)); |
658 | sec->addr.station = edev->station; | 700 | sec->addr.station = edev->station; |
@@ -660,12 +702,19 @@ static int ec_dev_ioctl(struct socket *sock, unsigned int cmd, void __user *arg) | |||
660 | sec->sec_family = AF_ECONET; | 702 | sec->sec_family = AF_ECONET; |
661 | dev_put(dev); | 703 | dev_put(dev); |
662 | if (copy_to_user(arg, &ifr, sizeof(struct ifreq))) | 704 | if (copy_to_user(arg, &ifr, sizeof(struct ifreq))) |
663 | return -EFAULT; | 705 | err = -EFAULT; |
664 | return 0; | 706 | break; |
707 | |||
708 | default: | ||
709 | err = -EINVAL; | ||
710 | break; | ||
665 | } | 711 | } |
666 | 712 | ||
713 | mutex_unlock(&econet_mutex); | ||
714 | |||
667 | dev_put(dev); | 715 | dev_put(dev); |
668 | return -EINVAL; | 716 | |
717 | return err; | ||
669 | } | 718 | } |
670 | 719 | ||
671 | /* | 720 | /* |
@@ -693,26 +742,13 @@ static int econet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg | |||
693 | return 0; | 742 | return 0; |
694 | } | 743 | } |
695 | 744 | ||
696 | #ifdef CONFIG_COMPAT | ||
697 | static int econet_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) | ||
698 | { | ||
699 | /* | ||
700 | * All ioctls provided by econet are standard. There is one gotcha, sockaddr_ec | ||
701 | * differs between 32bit and 64bit. Fortunately nobody in kernel uses portion | ||
702 | * of sockaddr which differs between 32bit and 64bit, so we do not need special | ||
703 | * handling. | ||
704 | */ | ||
705 | return -ENOIOCTLCMD; | ||
706 | } | ||
707 | #endif | ||
708 | |||
709 | static struct net_proto_family econet_family_ops = { | 745 | static struct net_proto_family econet_family_ops = { |
710 | .family = PF_ECONET, | 746 | .family = PF_ECONET, |
711 | .create = econet_create, | 747 | .create = econet_create, |
712 | .owner = THIS_MODULE, | 748 | .owner = THIS_MODULE, |
713 | }; | 749 | }; |
714 | 750 | ||
715 | static const struct proto_ops SOCKOPS_WRAPPED(econet_ops) = { | 751 | static const struct proto_ops econet_ops = { |
716 | .family = PF_ECONET, | 752 | .family = PF_ECONET, |
717 | .owner = THIS_MODULE, | 753 | .owner = THIS_MODULE, |
718 | .release = econet_release, | 754 | .release = econet_release, |
@@ -723,9 +759,6 @@ static const struct proto_ops SOCKOPS_WRAPPED(econet_ops) = { | |||
723 | .getname = econet_getname, | 759 | .getname = econet_getname, |
724 | .poll = datagram_poll, | 760 | .poll = datagram_poll, |
725 | .ioctl = econet_ioctl, | 761 | .ioctl = econet_ioctl, |
726 | #ifdef CONFIG_COMPAT | ||
727 | .compat_ioctl = econet_compat_ioctl, | ||
728 | #endif | ||
729 | .listen = sock_no_listen, | 762 | .listen = sock_no_listen, |
730 | .shutdown = sock_no_shutdown, | 763 | .shutdown = sock_no_shutdown, |
731 | .setsockopt = sock_no_setsockopt, | 764 | .setsockopt = sock_no_setsockopt, |
@@ -736,9 +769,6 @@ static const struct proto_ops SOCKOPS_WRAPPED(econet_ops) = { | |||
736 | .sendpage = sock_no_sendpage, | 769 | .sendpage = sock_no_sendpage, |
737 | }; | 770 | }; |
738 | 771 | ||
739 | #include <linux/smp_lock.h> | ||
740 | SOCKOPS_WRAP(econet, PF_ECONET); | ||
741 | |||
742 | #if defined(CONFIG_ECONET_AUNUDP) || defined(CONFIG_ECONET_NATIVE) | 772 | #if defined(CONFIG_ECONET_AUNUDP) || defined(CONFIG_ECONET_NATIVE) |
743 | /* | 773 | /* |
744 | * Find the listening socket, if any, for the given data. | 774 | * Find the listening socket, if any, for the given data. |