diff options
Diffstat (limited to 'net/llc/af_llc.c')
-rw-r--r-- | net/llc/af_llc.c | 180 |
1 files changed, 147 insertions, 33 deletions
diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index 7aa51eb79b13..59d02cbbeb9e 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c | |||
@@ -648,53 +648,167 @@ out: | |||
648 | * llc_ui_recvmsg - copy received data to the socket user. | 648 | * llc_ui_recvmsg - copy received data to the socket user. |
649 | * @sock: Socket to copy data from. | 649 | * @sock: Socket to copy data from. |
650 | * @msg: Various user space related information. | 650 | * @msg: Various user space related information. |
651 | * @size: Size of user buffer. | 651 | * @len: Size of user buffer. |
652 | * @flags: User specified flags. | 652 | * @flags: User specified flags. |
653 | * | 653 | * |
654 | * Copy received data to the socket user. | 654 | * Copy received data to the socket user. |
655 | * Returns non-negative upon success, negative otherwise. | 655 | * Returns non-negative upon success, negative otherwise. |
656 | */ | 656 | */ |
657 | static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock, | 657 | static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock, |
658 | struct msghdr *msg, size_t size, int flags) | 658 | struct msghdr *msg, size_t len, int flags) |
659 | { | 659 | { |
660 | struct sock *sk = sock->sk; | ||
661 | struct sockaddr_llc *uaddr = (struct sockaddr_llc *)msg->msg_name; | 660 | struct sockaddr_llc *uaddr = (struct sockaddr_llc *)msg->msg_name; |
662 | struct sk_buff *skb; | 661 | const int nonblock = flags & MSG_DONTWAIT; |
662 | struct sk_buff *skb = NULL; | ||
663 | struct sock *sk = sock->sk; | ||
664 | struct llc_sock *llc = llc_sk(sk); | ||
663 | size_t copied = 0; | 665 | size_t copied = 0; |
664 | int rc = -ENOMEM; | 666 | u32 peek_seq = 0; |
665 | int noblock = flags & MSG_DONTWAIT; | 667 | u32 *seq; |
668 | unsigned long used; | ||
669 | int target; /* Read at least this many bytes */ | ||
670 | long timeo; | ||
666 | 671 | ||
667 | dprintk("%s: receiving in %02X from %02X\n", __FUNCTION__, | ||
668 | llc_sk(sk)->laddr.lsap, llc_sk(sk)->daddr.lsap); | ||
669 | lock_sock(sk); | 672 | lock_sock(sk); |
670 | if (skb_queue_empty(&sk->sk_receive_queue)) { | 673 | copied = -ENOTCONN; |
671 | rc = llc_wait_data(sk, sock_rcvtimeo(sk, noblock)); | 674 | if (sk->sk_state == TCP_LISTEN) |
672 | if (rc) | ||
673 | goto out; | ||
674 | } | ||
675 | skb = skb_dequeue(&sk->sk_receive_queue); | ||
676 | if (!skb) /* shutdown */ | ||
677 | goto out; | 675 | goto out; |
678 | copied = skb->len; | 676 | |
679 | if (copied > size) | 677 | timeo = sock_rcvtimeo(sk, nonblock); |
680 | copied = size; | 678 | |
681 | rc = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); | 679 | seq = &llc->copied_seq; |
682 | if (rc) | 680 | if (flags & MSG_PEEK) { |
683 | goto dgram_free; | 681 | peek_seq = llc->copied_seq; |
684 | if (skb->len > copied) { | 682 | seq = &peek_seq; |
685 | skb_pull(skb, copied); | 683 | } |
686 | skb_queue_head(&sk->sk_receive_queue, skb); | 684 | |
687 | } | 685 | target = sock_rcvlowat(sk, flags & MSG_WAITALL, len); |
688 | if (uaddr) | 686 | copied = 0; |
689 | memcpy(uaddr, llc_ui_skb_cb(skb), sizeof(*uaddr)); | 687 | |
690 | msg->msg_namelen = sizeof(*uaddr); | 688 | do { |
691 | if (!skb->next) { | 689 | u32 offset; |
692 | dgram_free: | 690 | |
693 | kfree_skb(skb); | 691 | /* |
694 | } | 692 | * We need to check signals first, to get correct SIGURG |
693 | * handling. FIXME: Need to check this doesn't impact 1003.1g | ||
694 | * and move it down to the bottom of the loop | ||
695 | */ | ||
696 | if (signal_pending(current)) { | ||
697 | if (copied) | ||
698 | break; | ||
699 | copied = timeo ? sock_intr_errno(timeo) : -EAGAIN; | ||
700 | break; | ||
701 | } | ||
702 | |||
703 | /* Next get a buffer. */ | ||
704 | |||
705 | skb = skb_peek(&sk->sk_receive_queue); | ||
706 | if (skb) { | ||
707 | offset = *seq; | ||
708 | goto found_ok_skb; | ||
709 | } | ||
710 | /* Well, if we have backlog, try to process it now yet. */ | ||
711 | |||
712 | if (copied >= target && !sk->sk_backlog.tail) | ||
713 | break; | ||
714 | |||
715 | if (copied) { | ||
716 | if (sk->sk_err || | ||
717 | sk->sk_state == TCP_CLOSE || | ||
718 | (sk->sk_shutdown & RCV_SHUTDOWN) || | ||
719 | !timeo || | ||
720 | (flags & MSG_PEEK)) | ||
721 | break; | ||
722 | } else { | ||
723 | if (sock_flag(sk, SOCK_DONE)) | ||
724 | break; | ||
725 | |||
726 | if (sk->sk_err) { | ||
727 | copied = sock_error(sk); | ||
728 | break; | ||
729 | } | ||
730 | if (sk->sk_shutdown & RCV_SHUTDOWN) | ||
731 | break; | ||
732 | |||
733 | if (sk->sk_state == TCP_CLOSE) { | ||
734 | if (!sock_flag(sk, SOCK_DONE)) { | ||
735 | /* | ||
736 | * This occurs when user tries to read | ||
737 | * from never connected socket. | ||
738 | */ | ||
739 | copied = -ENOTCONN; | ||
740 | break; | ||
741 | } | ||
742 | break; | ||
743 | } | ||
744 | if (!timeo) { | ||
745 | copied = -EAGAIN; | ||
746 | break; | ||
747 | } | ||
748 | } | ||
749 | |||
750 | if (copied >= target) { /* Do not sleep, just process backlog. */ | ||
751 | release_sock(sk); | ||
752 | lock_sock(sk); | ||
753 | } else | ||
754 | sk_wait_data(sk, &timeo); | ||
755 | |||
756 | if ((flags & MSG_PEEK) && peek_seq != llc->copied_seq) { | ||
757 | if (net_ratelimit()) | ||
758 | printk(KERN_DEBUG "LLC(%s:%d): Application " | ||
759 | "bug, race in MSG_PEEK.\n", | ||
760 | current->comm, current->pid); | ||
761 | peek_seq = llc->copied_seq; | ||
762 | } | ||
763 | continue; | ||
764 | found_ok_skb: | ||
765 | /* Ok so how much can we use? */ | ||
766 | used = skb->len - offset; | ||
767 | if (len < used) | ||
768 | used = len; | ||
769 | |||
770 | if (!(flags & MSG_TRUNC)) { | ||
771 | int rc = skb_copy_datagram_iovec(skb, offset, | ||
772 | msg->msg_iov, used); | ||
773 | if (rc) { | ||
774 | /* Exception. Bailout! */ | ||
775 | if (!copied) | ||
776 | copied = -EFAULT; | ||
777 | break; | ||
778 | } | ||
779 | } | ||
780 | |||
781 | *seq += used; | ||
782 | copied += used; | ||
783 | len -= used; | ||
784 | |||
785 | if (used + offset < skb->len) | ||
786 | continue; | ||
787 | |||
788 | if (!(flags & MSG_PEEK)) { | ||
789 | sk_eat_skb(sk, skb); | ||
790 | *seq = 0; | ||
791 | } | ||
792 | } while (len > 0); | ||
793 | |||
794 | /* | ||
795 | * According to UNIX98, msg_name/msg_namelen are ignored | ||
796 | * on connected socket. -ANK | ||
797 | * But... af_llc still doesn't have separate sets of methods for | ||
798 | * SOCK_DGRAM and SOCK_STREAM :-( So we have to do this test, will | ||
799 | * eventually fix this tho :-) -acme | ||
800 | */ | ||
801 | if (sk->sk_type == SOCK_DGRAM) | ||
802 | goto copy_uaddr; | ||
695 | out: | 803 | out: |
696 | release_sock(sk); | 804 | release_sock(sk); |
697 | return rc ? : copied; | 805 | return copied; |
806 | copy_uaddr: | ||
807 | if (uaddr != NULL && skb != NULL) { | ||
808 | memcpy(uaddr, llc_ui_skb_cb(skb), sizeof(*uaddr)); | ||
809 | msg->msg_namelen = sizeof(*uaddr); | ||
810 | } | ||
811 | goto out; | ||
698 | } | 812 | } |
699 | 813 | ||
700 | /** | 814 | /** |