diff options
| author | Arnaldo Carvalho de Melo <acme@mandriva.com> | 2005-09-22 07:29:08 -0400 |
|---|---|---|
| committer | Arnaldo Carvalho de Melo <acme@mandriva.com> | 2005-09-22 07:29:08 -0400 |
| commit | 8420e1b541fe92aee1d8d4d25d9e33eaca756a7b (patch) | |
| tree | c427c8cfe59bfae22eac2dc4c325aa947f7cb0eb /net/llc | |
| parent | d389424e00f9097cd24b3df4ca0ab7221f140eeb (diff) | |
[LLC]: fix llc_ui_recvmsg, making it behave like tcp_recvmsg
In fact it is an exact copy of the parts that makes sense to LLC :-)
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
Diffstat (limited to 'net/llc')
| -rw-r--r-- | net/llc/af_llc.c | 180 | ||||
| -rw-r--r-- | net/llc/llc_conn.c | 2 | ||||
| -rw-r--r-- | net/llc/llc_proc.c | 2 | ||||
| -rw-r--r-- | net/llc/llc_sap.c | 5 |
4 files changed, 153 insertions, 36 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 | /** |
diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c index e10ce5adb104..042b24a8ca4c 100644 --- a/net/llc/llc_conn.c +++ b/net/llc/llc_conn.c | |||
| @@ -120,8 +120,8 @@ int llc_conn_state_process(struct sock *sk, struct sk_buff *skb) | |||
| 120 | sk->sk_socket->state = SS_UNCONNECTED; | 120 | sk->sk_socket->state = SS_UNCONNECTED; |
| 121 | sk->sk_state = TCP_CLOSE; | 121 | sk->sk_state = TCP_CLOSE; |
| 122 | if (!sock_flag(sk, SOCK_DEAD)) { | 122 | if (!sock_flag(sk, SOCK_DEAD)) { |
| 123 | sk->sk_state_change(sk); | ||
| 124 | sock_set_flag(sk, SOCK_DEAD); | 123 | sock_set_flag(sk, SOCK_DEAD); |
| 124 | sk->sk_state_change(sk); | ||
| 125 | } | 125 | } |
| 126 | } | 126 | } |
| 127 | kfree_skb(skb); | 127 | kfree_skb(skb); |
diff --git a/net/llc/llc_proc.c b/net/llc/llc_proc.c index 36e8db3fa1a2..bd531cb235a7 100644 --- a/net/llc/llc_proc.c +++ b/net/llc/llc_proc.c | |||
| @@ -134,7 +134,7 @@ static int llc_seq_socket_show(struct seq_file *seq, void *v) | |||
| 134 | llc_ui_format_mac(seq, llc->daddr.mac); | 134 | llc_ui_format_mac(seq, llc->daddr.mac); |
| 135 | seq_printf(seq, "@%02X %8d %8d %2d %3d %4d\n", llc->daddr.lsap, | 135 | seq_printf(seq, "@%02X %8d %8d %2d %3d %4d\n", llc->daddr.lsap, |
| 136 | atomic_read(&sk->sk_wmem_alloc), | 136 | atomic_read(&sk->sk_wmem_alloc), |
| 137 | atomic_read(&sk->sk_rmem_alloc), | 137 | atomic_read(&sk->sk_rmem_alloc) - llc->copied_seq, |
| 138 | sk->sk_state, | 138 | sk->sk_state, |
| 139 | sk->sk_socket ? SOCK_INODE(sk->sk_socket)->i_uid : -1, | 139 | sk->sk_socket ? SOCK_INODE(sk->sk_socket)->i_uid : -1, |
| 140 | llc->link); | 140 | llc->link); |
diff --git a/net/llc/llc_sap.c b/net/llc/llc_sap.c index e6d538937f93..4029ceee9b91 100644 --- a/net/llc/llc_sap.c +++ b/net/llc/llc_sap.c | |||
| @@ -49,9 +49,12 @@ struct sk_buff *llc_alloc_frame(struct sock *sk, struct net_device *dev) | |||
| 49 | 49 | ||
| 50 | void llc_save_primitive(struct sock *sk, struct sk_buff* skb, u8 prim) | 50 | void llc_save_primitive(struct sock *sk, struct sk_buff* skb, u8 prim) |
| 51 | { | 51 | { |
| 52 | struct sockaddr_llc *addr = llc_ui_skb_cb(skb); | 52 | struct sockaddr_llc *addr; |
| 53 | 53 | ||
| 54 | if (skb->sk->sk_type == SOCK_STREAM) /* See UNIX98 */ | ||
| 55 | return; | ||
| 54 | /* save primitive for use by the user. */ | 56 | /* save primitive for use by the user. */ |
| 57 | addr = llc_ui_skb_cb(skb); | ||
| 55 | addr->sllc_family = sk->sk_family; | 58 | addr->sllc_family = sk->sk_family; |
| 56 | addr->sllc_arphrd = skb->dev->type; | 59 | addr->sllc_arphrd = skb->dev->type; |
| 57 | addr->sllc_test = prim == LLC_TEST_PRIM; | 60 | addr->sllc_test = prim == LLC_TEST_PRIM; |
