diff options
author | Rémi Denis-Courmont <remi.denis-courmont@nokia.com> | 2011-03-08 17:44:10 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-03-09 14:59:32 -0500 |
commit | f7ae8d59f66154df0424fd94035c89981fed3379 (patch) | |
tree | 829e2a3e2188a30b48afbd200675bf8d0a21cb0d /net/phonet | |
parent | 44c9ab16d29a50af6ed9ae084b75774570de512a (diff) |
Phonet: allocate sock from accept syscall rather than soft IRQ
This moves most of the accept logic to process context like other
socket stacks do. Then we can use a few more common socket helpers
and simplify a bit.
Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/phonet')
-rw-r--r-- | net/phonet/pep.c | 284 | ||||
-rw-r--r-- | net/phonet/socket.c | 10 |
2 files changed, 121 insertions, 173 deletions
diff --git a/net/phonet/pep.c b/net/phonet/pep.c index 610794a416e8..c0fab4cfcef7 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c | |||
@@ -42,7 +42,7 @@ | |||
42 | * TCP_ESTABLISHED connected pipe in enabled state | 42 | * TCP_ESTABLISHED connected pipe in enabled state |
43 | * | 43 | * |
44 | * pep_sock locking: | 44 | * pep_sock locking: |
45 | * - sk_state, ackq, hlist: sock lock needed | 45 | * - sk_state, hlist: sock lock needed |
46 | * - listener: read only | 46 | * - listener: read only |
47 | * - pipe_handle: read only | 47 | * - pipe_handle: read only |
48 | */ | 48 | */ |
@@ -202,11 +202,12 @@ static int pep_accept_conn(struct sock *sk, struct sk_buff *skb) | |||
202 | GFP_KERNEL); | 202 | GFP_KERNEL); |
203 | } | 203 | } |
204 | 204 | ||
205 | static int pep_reject_conn(struct sock *sk, struct sk_buff *skb, u8 code) | 205 | static int pep_reject_conn(struct sock *sk, struct sk_buff *skb, u8 code, |
206 | gfp_t priority) | ||
206 | { | 207 | { |
207 | static const u8 data[4] = { PAD, PAD, PAD, 0 /* sub-blocks */ }; | 208 | static const u8 data[4] = { PAD, PAD, PAD, 0 /* sub-blocks */ }; |
208 | WARN_ON(code == PN_PIPE_NO_ERROR); | 209 | WARN_ON(code == PN_PIPE_NO_ERROR); |
209 | return pep_reply(sk, skb, code, data, sizeof(data), GFP_ATOMIC); | 210 | return pep_reply(sk, skb, code, data, sizeof(data), priority); |
210 | } | 211 | } |
211 | 212 | ||
212 | /* Control requests are not sent by the pipe service and have a specific | 213 | /* Control requests are not sent by the pipe service and have a specific |
@@ -365,7 +366,7 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb) | |||
365 | 366 | ||
366 | switch (hdr->message_id) { | 367 | switch (hdr->message_id) { |
367 | case PNS_PEP_CONNECT_REQ: | 368 | case PNS_PEP_CONNECT_REQ: |
368 | pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE); | 369 | pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, GFP_ATOMIC); |
369 | break; | 370 | break; |
370 | 371 | ||
371 | case PNS_PEP_DISCONNECT_REQ: | 372 | case PNS_PEP_DISCONNECT_REQ: |
@@ -574,7 +575,6 @@ static int pep_connresp_rcv(struct sock *sk, struct sk_buff *skb) | |||
574 | 575 | ||
575 | sk->sk_state = TCP_SYN_RECV; | 576 | sk->sk_state = TCP_SYN_RECV; |
576 | sk->sk_backlog_rcv = pipe_do_rcv; | 577 | sk->sk_backlog_rcv = pipe_do_rcv; |
577 | sk->sk_destruct = pipe_destruct; | ||
578 | pn->rx_credits = 0; | 578 | pn->rx_credits = 0; |
579 | sk->sk_state_change(sk); | 579 | sk->sk_state_change(sk); |
580 | 580 | ||
@@ -582,96 +582,6 @@ static int pep_connresp_rcv(struct sock *sk, struct sk_buff *skb) | |||
582 | } | 582 | } |
583 | #endif | 583 | #endif |
584 | 584 | ||
585 | static int pep_connreq_rcv(struct sock *sk, struct sk_buff *skb) | ||
586 | { | ||
587 | struct sock *newsk; | ||
588 | struct pep_sock *newpn, *pn = pep_sk(sk); | ||
589 | struct pnpipehdr *hdr; | ||
590 | struct sockaddr_pn dst, src; | ||
591 | u16 peer_type; | ||
592 | u8 pipe_handle, enabled, n_sb; | ||
593 | u8 aligned = 0; | ||
594 | |||
595 | if (!pskb_pull(skb, sizeof(*hdr) + 4)) | ||
596 | return -EINVAL; | ||
597 | |||
598 | hdr = pnp_hdr(skb); | ||
599 | pipe_handle = hdr->pipe_handle; | ||
600 | switch (hdr->state_after_connect) { | ||
601 | case PN_PIPE_DISABLE: | ||
602 | enabled = 0; | ||
603 | break; | ||
604 | case PN_PIPE_ENABLE: | ||
605 | enabled = 1; | ||
606 | break; | ||
607 | default: | ||
608 | pep_reject_conn(sk, skb, PN_PIPE_ERR_INVALID_PARAM); | ||
609 | return -EINVAL; | ||
610 | } | ||
611 | peer_type = hdr->other_pep_type << 8; | ||
612 | |||
613 | /* Parse sub-blocks (options) */ | ||
614 | n_sb = hdr->data[4]; | ||
615 | while (n_sb > 0) { | ||
616 | u8 type, buf[1], len = sizeof(buf); | ||
617 | const u8 *data = pep_get_sb(skb, &type, &len, buf); | ||
618 | |||
619 | if (data == NULL) | ||
620 | return -EINVAL; | ||
621 | switch (type) { | ||
622 | case PN_PIPE_SB_CONNECT_REQ_PEP_SUB_TYPE: | ||
623 | if (len < 1) | ||
624 | return -EINVAL; | ||
625 | peer_type = (peer_type & 0xff00) | data[0]; | ||
626 | break; | ||
627 | case PN_PIPE_SB_ALIGNED_DATA: | ||
628 | aligned = data[0] != 0; | ||
629 | break; | ||
630 | } | ||
631 | n_sb--; | ||
632 | } | ||
633 | |||
634 | skb = skb_clone(skb, GFP_ATOMIC); | ||
635 | if (!skb) | ||
636 | return -ENOMEM; | ||
637 | |||
638 | /* Create a new to-be-accepted sock */ | ||
639 | newsk = sk_alloc(sock_net(sk), PF_PHONET, GFP_ATOMIC, sk->sk_prot); | ||
640 | if (!newsk) { | ||
641 | kfree_skb(skb); | ||
642 | return -ENOMEM; | ||
643 | } | ||
644 | sock_init_data(NULL, newsk); | ||
645 | newsk->sk_state = TCP_SYN_RECV; | ||
646 | newsk->sk_backlog_rcv = pipe_do_rcv; | ||
647 | newsk->sk_protocol = sk->sk_protocol; | ||
648 | newsk->sk_destruct = pipe_destruct; | ||
649 | |||
650 | newpn = pep_sk(newsk); | ||
651 | pn_skb_get_dst_sockaddr(skb, &dst); | ||
652 | pn_skb_get_src_sockaddr(skb, &src); | ||
653 | newpn->pn_sk.sobject = pn_sockaddr_get_object(&dst); | ||
654 | newpn->pn_sk.dobject = pn_sockaddr_get_object(&src); | ||
655 | newpn->pn_sk.resource = pn_sockaddr_get_resource(&dst); | ||
656 | skb_queue_head_init(&newpn->ctrlreq_queue); | ||
657 | newpn->pipe_handle = pipe_handle; | ||
658 | atomic_set(&newpn->tx_credits, 0); | ||
659 | newpn->peer_type = peer_type; | ||
660 | newpn->rx_credits = 0; | ||
661 | newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL; | ||
662 | newpn->init_enable = enabled; | ||
663 | newpn->aligned = aligned; | ||
664 | |||
665 | BUG_ON(!skb_queue_empty(&newsk->sk_receive_queue)); | ||
666 | skb_queue_head(&newsk->sk_receive_queue, skb); | ||
667 | if (!sock_flag(sk, SOCK_DEAD)) | ||
668 | sk->sk_data_ready(sk, 0); | ||
669 | |||
670 | sk_acceptq_added(sk); | ||
671 | sk_add_node(newsk, &pn->ackq); | ||
672 | return 0; | ||
673 | } | ||
674 | |||
675 | /* Listening sock must be locked */ | 585 | /* Listening sock must be locked */ |
676 | static struct sock *pep_find_pipe(const struct hlist_head *hlist, | 586 | static struct sock *pep_find_pipe(const struct hlist_head *hlist, |
677 | const struct sockaddr_pn *dst, | 587 | const struct sockaddr_pn *dst, |
@@ -726,22 +636,18 @@ static int pep_do_rcv(struct sock *sk, struct sk_buff *skb) | |||
726 | if (sknode) | 636 | if (sknode) |
727 | return sk_receive_skb(sknode, skb, 1); | 637 | return sk_receive_skb(sknode, skb, 1); |
728 | 638 | ||
729 | /* Look for a pipe handle pending accept */ | ||
730 | sknode = pep_find_pipe(&pn->ackq, &dst, pipe_handle); | ||
731 | if (sknode) { | ||
732 | sock_put(sknode); | ||
733 | if (net_ratelimit()) | ||
734 | printk(KERN_WARNING"Phonet unconnected PEP ignored"); | ||
735 | goto drop; | ||
736 | } | ||
737 | |||
738 | switch (hdr->message_id) { | 639 | switch (hdr->message_id) { |
739 | case PNS_PEP_CONNECT_REQ: | 640 | case PNS_PEP_CONNECT_REQ: |
740 | if (sk->sk_state == TCP_LISTEN && !sk_acceptq_is_full(sk)) | 641 | if (sk->sk_state != TCP_LISTEN || sk_acceptq_is_full(sk)) { |
741 | pep_connreq_rcv(sk, skb); | 642 | pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, |
742 | else | 643 | GFP_ATOMIC); |
743 | pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE); | 644 | break; |
744 | break; | 645 | } |
646 | skb_queue_head(&sk->sk_receive_queue, skb); | ||
647 | sk_acceptq_added(sk); | ||
648 | if (!sock_flag(sk, SOCK_DEAD)) | ||
649 | sk->sk_data_ready(sk, 0); | ||
650 | return NET_RX_SUCCESS; | ||
745 | 651 | ||
746 | #ifdef CONFIG_PHONET_PIPECTRLR | 652 | #ifdef CONFIG_PHONET_PIPECTRLR |
747 | case PNS_PEP_CONNECT_RESP: | 653 | case PNS_PEP_CONNECT_RESP: |
@@ -799,24 +705,16 @@ static void pep_sock_close(struct sock *sk, long timeout) | |||
799 | sk_common_release(sk); | 705 | sk_common_release(sk); |
800 | 706 | ||
801 | lock_sock(sk); | 707 | lock_sock(sk); |
802 | if (sk->sk_state == TCP_LISTEN) { | 708 | if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED)) { |
803 | /* Destroy the listen queue */ | ||
804 | struct sock *sknode; | ||
805 | struct hlist_node *p, *n; | ||
806 | |||
807 | sk_for_each_safe(sknode, p, n, &pn->ackq) | ||
808 | sk_del_node_init(sknode); | ||
809 | sk->sk_state = TCP_CLOSE; | ||
810 | } else if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED)) { | ||
811 | #ifndef CONFIG_PHONET_PIPECTRLR | 709 | #ifndef CONFIG_PHONET_PIPECTRLR |
812 | /* Forcefully remove dangling Phonet pipe */ | 710 | /* Forcefully remove dangling Phonet pipe */ |
813 | pipe_do_remove(sk); | 711 | pipe_do_remove(sk); |
814 | #else | 712 | #else |
815 | /* send pep disconnect request */ | 713 | /* send pep disconnect request */ |
816 | pipe_handler_request(sk, PNS_PEP_DISCONNECT_REQ, PAD, NULL, 0); | 714 | pipe_handler_request(sk, PNS_PEP_DISCONNECT_REQ, PAD, NULL, 0); |
817 | sk->sk_state = TCP_CLOSE; | ||
818 | #endif | 715 | #endif |
819 | } | 716 | } |
717 | sk->sk_state = TCP_CLOSE; | ||
820 | 718 | ||
821 | ifindex = pn->ifindex; | 719 | ifindex = pn->ifindex; |
822 | pn->ifindex = 0; | 720 | pn->ifindex = 0; |
@@ -827,69 +725,121 @@ static void pep_sock_close(struct sock *sk, long timeout) | |||
827 | sock_put(sk); | 725 | sock_put(sk); |
828 | } | 726 | } |
829 | 727 | ||
830 | static int pep_wait_connreq(struct sock *sk, int noblock) | 728 | static struct sock *pep_sock_accept(struct sock *sk, int flags, int *errp) |
831 | { | 729 | { |
832 | struct task_struct *tsk = current; | 730 | struct pep_sock *pn = pep_sk(sk), *newpn; |
833 | struct pep_sock *pn = pep_sk(sk); | 731 | struct sock *newsk = NULL; |
834 | long timeo = sock_rcvtimeo(sk, noblock); | 732 | struct sk_buff *skb; |
835 | 733 | struct pnpipehdr *hdr; | |
836 | for (;;) { | 734 | struct sockaddr_pn dst, src; |
837 | DEFINE_WAIT(wait); | 735 | int err; |
736 | u16 peer_type; | ||
737 | u8 pipe_handle, enabled, n_sb; | ||
738 | u8 aligned = 0; | ||
838 | 739 | ||
839 | if (sk->sk_state != TCP_LISTEN) | 740 | skb = skb_recv_datagram(sk, 0, flags & O_NONBLOCK, errp); |
840 | return -EINVAL; | 741 | if (!skb) |
841 | if (!hlist_empty(&pn->ackq)) | 742 | return NULL; |
842 | break; | ||
843 | if (!timeo) | ||
844 | return -EWOULDBLOCK; | ||
845 | if (signal_pending(tsk)) | ||
846 | return sock_intr_errno(timeo); | ||
847 | 743 | ||
848 | prepare_to_wait_exclusive(sk_sleep(sk), &wait, | 744 | lock_sock(sk); |
849 | TASK_INTERRUPTIBLE); | 745 | if (sk->sk_state != TCP_LISTEN) { |
850 | release_sock(sk); | 746 | err = -EINVAL; |
851 | timeo = schedule_timeout(timeo); | 747 | goto drop; |
852 | lock_sock(sk); | ||
853 | finish_wait(sk_sleep(sk), &wait); | ||
854 | } | 748 | } |
749 | sk_acceptq_removed(sk); | ||
855 | 750 | ||
856 | return 0; | 751 | err = -EPROTO; |
857 | } | 752 | if (!pskb_may_pull(skb, sizeof(*hdr) + 4)) |
753 | goto drop; | ||
858 | 754 | ||
859 | static struct sock *pep_sock_accept(struct sock *sk, int flags, int *errp) | 755 | hdr = pnp_hdr(skb); |
860 | { | 756 | pipe_handle = hdr->pipe_handle; |
861 | struct pep_sock *pn = pep_sk(sk); | 757 | switch (hdr->state_after_connect) { |
862 | struct sock *newsk = NULL; | 758 | case PN_PIPE_DISABLE: |
863 | struct sk_buff *oskb; | 759 | enabled = 0; |
864 | int err; | 760 | break; |
761 | case PN_PIPE_ENABLE: | ||
762 | enabled = 1; | ||
763 | break; | ||
764 | default: | ||
765 | pep_reject_conn(sk, skb, PN_PIPE_ERR_INVALID_PARAM, | ||
766 | GFP_KERNEL); | ||
767 | goto drop; | ||
768 | } | ||
769 | peer_type = hdr->other_pep_type << 8; | ||
865 | 770 | ||
866 | lock_sock(sk); | 771 | /* Parse sub-blocks (options) */ |
867 | err = pep_wait_connreq(sk, flags & O_NONBLOCK); | 772 | n_sb = hdr->data[4]; |
868 | if (err) | 773 | while (n_sb > 0) { |
869 | goto out; | 774 | u8 type, buf[1], len = sizeof(buf); |
775 | const u8 *data = pep_get_sb(skb, &type, &len, buf); | ||
870 | 776 | ||
871 | newsk = __sk_head(&pn->ackq); | 777 | if (data == NULL) |
778 | goto drop; | ||
779 | switch (type) { | ||
780 | case PN_PIPE_SB_CONNECT_REQ_PEP_SUB_TYPE: | ||
781 | if (len < 1) | ||
782 | goto drop; | ||
783 | peer_type = (peer_type & 0xff00) | data[0]; | ||
784 | break; | ||
785 | case PN_PIPE_SB_ALIGNED_DATA: | ||
786 | aligned = data[0] != 0; | ||
787 | break; | ||
788 | } | ||
789 | n_sb--; | ||
790 | } | ||
872 | 791 | ||
873 | oskb = skb_dequeue(&newsk->sk_receive_queue); | 792 | /* Check for duplicate pipe handle */ |
874 | err = pep_accept_conn(newsk, oskb); | 793 | newsk = pep_find_pipe(&pn->hlist, &dst, pipe_handle); |
875 | if (err) { | 794 | if (unlikely(newsk)) { |
876 | skb_queue_head(&newsk->sk_receive_queue, oskb); | 795 | __sock_put(newsk); |
877 | newsk = NULL; | 796 | newsk = NULL; |
878 | goto out; | 797 | pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, GFP_KERNEL); |
798 | goto drop; | ||
799 | } | ||
800 | |||
801 | /* Create a new to-be-accepted sock */ | ||
802 | newsk = sk_alloc(sock_net(sk), PF_PHONET, GFP_KERNEL, sk->sk_prot); | ||
803 | if (!newsk) { | ||
804 | pep_reject_conn(sk, skb, PN_PIPE_ERR_OVERLOAD, GFP_KERNEL); | ||
805 | err = -ENOBUFS; | ||
806 | goto drop; | ||
879 | } | 807 | } |
880 | kfree_skb(oskb); | ||
881 | 808 | ||
809 | sock_init_data(NULL, newsk); | ||
810 | newsk->sk_state = TCP_SYN_RECV; | ||
811 | newsk->sk_backlog_rcv = pipe_do_rcv; | ||
812 | newsk->sk_protocol = sk->sk_protocol; | ||
813 | newsk->sk_destruct = pipe_destruct; | ||
814 | |||
815 | newpn = pep_sk(newsk); | ||
816 | pn_skb_get_dst_sockaddr(skb, &dst); | ||
817 | pn_skb_get_src_sockaddr(skb, &src); | ||
818 | newpn->pn_sk.sobject = pn_sockaddr_get_object(&dst); | ||
819 | newpn->pn_sk.dobject = pn_sockaddr_get_object(&src); | ||
820 | newpn->pn_sk.resource = pn_sockaddr_get_resource(&dst); | ||
882 | sock_hold(sk); | 821 | sock_hold(sk); |
883 | pep_sk(newsk)->listener = sk; | 822 | newpn->listener = sk; |
823 | skb_queue_head_init(&newpn->ctrlreq_queue); | ||
824 | newpn->pipe_handle = pipe_handle; | ||
825 | atomic_set(&newpn->tx_credits, 0); | ||
826 | newpn->ifindex = 0; | ||
827 | newpn->peer_type = peer_type; | ||
828 | newpn->rx_credits = 0; | ||
829 | newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL; | ||
830 | newpn->init_enable = enabled; | ||
831 | newpn->aligned = aligned; | ||
884 | 832 | ||
885 | sock_hold(newsk); | 833 | err = pep_accept_conn(newsk, skb); |
886 | sk_del_node_init(newsk); | 834 | if (err) { |
887 | sk_acceptq_removed(sk); | 835 | sock_put(newsk); |
836 | newsk = NULL; | ||
837 | goto drop; | ||
838 | } | ||
888 | sk_add_node(newsk, &pn->hlist); | 839 | sk_add_node(newsk, &pn->hlist); |
889 | __sock_put(newsk); | 840 | drop: |
890 | |||
891 | out: | ||
892 | release_sock(sk); | 841 | release_sock(sk); |
842 | kfree_skb(skb); | ||
893 | *errp = err; | 843 | *errp = err; |
894 | return newsk; | 844 | return newsk; |
895 | } | 845 | } |
@@ -937,7 +887,7 @@ static int pep_init(struct sock *sk) | |||
937 | { | 887 | { |
938 | struct pep_sock *pn = pep_sk(sk); | 888 | struct pep_sock *pn = pep_sk(sk); |
939 | 889 | ||
940 | INIT_HLIST_HEAD(&pn->ackq); | 890 | sk->sk_destruct = pipe_destruct; |
941 | INIT_HLIST_HEAD(&pn->hlist); | 891 | INIT_HLIST_HEAD(&pn->hlist); |
942 | skb_queue_head_init(&pn->ctrlreq_queue); | 892 | skb_queue_head_init(&pn->ctrlreq_queue); |
943 | pn->pipe_handle = PN_PIPE_INVALID_HANDLE; | 893 | pn->pipe_handle = PN_PIPE_INVALID_HANDLE; |
diff --git a/net/phonet/socket.c b/net/phonet/socket.c index 65a03338769e..1eccfc35bcc0 100644 --- a/net/phonet/socket.c +++ b/net/phonet/socket.c | |||
@@ -327,6 +327,9 @@ static int pn_socket_accept(struct socket *sock, struct socket *newsock, | |||
327 | struct sock *newsk; | 327 | struct sock *newsk; |
328 | int err; | 328 | int err; |
329 | 329 | ||
330 | if (unlikely(sk->sk_state != TCP_LISTEN)) | ||
331 | return -EINVAL; | ||
332 | |||
330 | newsk = sk->sk_prot->accept(sk, flags, &err); | 333 | newsk = sk->sk_prot->accept(sk, flags, &err); |
331 | if (!newsk) | 334 | if (!newsk) |
332 | return err; | 335 | return err; |
@@ -363,13 +366,8 @@ static unsigned int pn_socket_poll(struct file *file, struct socket *sock, | |||
363 | 366 | ||
364 | poll_wait(file, sk_sleep(sk), wait); | 367 | poll_wait(file, sk_sleep(sk), wait); |
365 | 368 | ||
366 | switch (sk->sk_state) { | 369 | if (sk->sk_state == TCP_CLOSE) |
367 | case TCP_LISTEN: | ||
368 | return hlist_empty(&pn->ackq) ? 0 : POLLIN; | ||
369 | case TCP_CLOSE: | ||
370 | return POLLERR; | 370 | return POLLERR; |
371 | } | ||
372 | |||
373 | if (!skb_queue_empty(&sk->sk_receive_queue)) | 371 | if (!skb_queue_empty(&sk->sk_receive_queue)) |
374 | mask |= POLLIN | POLLRDNORM; | 372 | mask |= POLLIN | POLLRDNORM; |
375 | if (!skb_queue_empty(&pn->ctrlreq_queue)) | 373 | if (!skb_queue_empty(&pn->ctrlreq_queue)) |