diff options
author | Hannes Frederic Sowa <hannes@stressinduktion.org> | 2015-05-21 10:59:59 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-05-25 00:06:58 -0400 |
commit | 869e7c62486ec0e170a9771acaa251d1a33b5871 (patch) | |
tree | c9086093021e0d0e1d7573f437cb2f7ffdf36273 /net/unix | |
parent | be12a1fe298e8be04d5215364f94654dff81b0bc (diff) |
net: af_unix: implement stream sendpage support
This patch implements sendpage support for AF_UNIX SOCK_STREAM
sockets. This is also required for a complete splice implementation.
The implementation is a bit tricky because we append to already existing
skbs and so have to hold unix_sk->readlock to protect the reading side
from either advancing UNIXCB.consumed or freeing the skb at the socket
receive tail.
Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Acked-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/unix')
-rw-r--r-- | net/unix/af_unix.c | 99 |
1 files changed, 98 insertions, 1 deletions
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 941b3d26e3bf..7762c0b46721 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c | |||
@@ -518,6 +518,8 @@ static int unix_ioctl(struct socket *, unsigned int, unsigned long); | |||
518 | static int unix_shutdown(struct socket *, int); | 518 | static int unix_shutdown(struct socket *, int); |
519 | static int unix_stream_sendmsg(struct socket *, struct msghdr *, size_t); | 519 | static int unix_stream_sendmsg(struct socket *, struct msghdr *, size_t); |
520 | static int unix_stream_recvmsg(struct socket *, struct msghdr *, size_t, int); | 520 | static int unix_stream_recvmsg(struct socket *, struct msghdr *, size_t, int); |
521 | static ssize_t unix_stream_sendpage(struct socket *, struct page *, int offset, | ||
522 | size_t size, int flags); | ||
521 | static int unix_dgram_sendmsg(struct socket *, struct msghdr *, size_t); | 523 | static int unix_dgram_sendmsg(struct socket *, struct msghdr *, size_t); |
522 | static int unix_dgram_recvmsg(struct socket *, struct msghdr *, size_t, int); | 524 | static int unix_dgram_recvmsg(struct socket *, struct msghdr *, size_t, int); |
523 | static int unix_dgram_connect(struct socket *, struct sockaddr *, | 525 | static int unix_dgram_connect(struct socket *, struct sockaddr *, |
@@ -558,7 +560,7 @@ static const struct proto_ops unix_stream_ops = { | |||
558 | .sendmsg = unix_stream_sendmsg, | 560 | .sendmsg = unix_stream_sendmsg, |
559 | .recvmsg = unix_stream_recvmsg, | 561 | .recvmsg = unix_stream_recvmsg, |
560 | .mmap = sock_no_mmap, | 562 | .mmap = sock_no_mmap, |
561 | .sendpage = sock_no_sendpage, | 563 | .sendpage = unix_stream_sendpage, |
562 | .set_peek_off = unix_set_peek_off, | 564 | .set_peek_off = unix_set_peek_off, |
563 | }; | 565 | }; |
564 | 566 | ||
@@ -1720,6 +1722,101 @@ out_err: | |||
1720 | return sent ? : err; | 1722 | return sent ? : err; |
1721 | } | 1723 | } |
1722 | 1724 | ||
1725 | static ssize_t unix_stream_sendpage(struct socket *socket, struct page *page, | ||
1726 | int offset, size_t size, int flags) | ||
1727 | { | ||
1728 | int err = 0; | ||
1729 | bool send_sigpipe = true; | ||
1730 | struct sock *other, *sk = socket->sk; | ||
1731 | struct sk_buff *skb, *newskb = NULL, *tail = NULL; | ||
1732 | |||
1733 | if (flags & MSG_OOB) | ||
1734 | return -EOPNOTSUPP; | ||
1735 | |||
1736 | other = unix_peer(sk); | ||
1737 | if (!other || sk->sk_state != TCP_ESTABLISHED) | ||
1738 | return -ENOTCONN; | ||
1739 | |||
1740 | if (false) { | ||
1741 | alloc_skb: | ||
1742 | unix_state_unlock(other); | ||
1743 | mutex_unlock(&unix_sk(other)->readlock); | ||
1744 | newskb = sock_alloc_send_pskb(sk, 0, 0, flags & MSG_DONTWAIT, | ||
1745 | &err, 0); | ||
1746 | if (!newskb) | ||
1747 | return err; | ||
1748 | } | ||
1749 | |||
1750 | /* we must acquire readlock as we modify already present | ||
1751 | * skbs in the sk_receive_queue and mess with skb->len | ||
1752 | */ | ||
1753 | err = mutex_lock_interruptible(&unix_sk(other)->readlock); | ||
1754 | if (err) { | ||
1755 | err = flags & MSG_DONTWAIT ? -EAGAIN : -ERESTARTSYS; | ||
1756 | send_sigpipe = false; | ||
1757 | goto err; | ||
1758 | } | ||
1759 | |||
1760 | if (sk->sk_shutdown & SEND_SHUTDOWN) { | ||
1761 | err = -EPIPE; | ||
1762 | goto err_unlock; | ||
1763 | } | ||
1764 | |||
1765 | unix_state_lock(other); | ||
1766 | |||
1767 | if (sock_flag(other, SOCK_DEAD) || | ||
1768 | other->sk_shutdown & RCV_SHUTDOWN) { | ||
1769 | err = -EPIPE; | ||
1770 | goto err_state_unlock; | ||
1771 | } | ||
1772 | |||
1773 | skb = skb_peek_tail(&other->sk_receive_queue); | ||
1774 | if (tail && tail == skb) { | ||
1775 | skb = newskb; | ||
1776 | } else if (!skb) { | ||
1777 | if (newskb) | ||
1778 | skb = newskb; | ||
1779 | else | ||
1780 | goto alloc_skb; | ||
1781 | } else if (newskb) { | ||
1782 | /* this is fast path, we don't necessarily need to | ||
1783 | * call to kfree_skb even though with newskb == NULL | ||
1784 | * this - does no harm | ||
1785 | */ | ||
1786 | consume_skb(newskb); | ||
1787 | } | ||
1788 | |||
1789 | if (skb_append_pagefrags(skb, page, offset, size)) { | ||
1790 | tail = skb; | ||
1791 | goto alloc_skb; | ||
1792 | } | ||
1793 | |||
1794 | skb->len += size; | ||
1795 | skb->data_len += size; | ||
1796 | skb->truesize += size; | ||
1797 | atomic_add(size, &sk->sk_wmem_alloc); | ||
1798 | |||
1799 | if (newskb) | ||
1800 | __skb_queue_tail(&other->sk_receive_queue, newskb); | ||
1801 | |||
1802 | unix_state_unlock(other); | ||
1803 | mutex_unlock(&unix_sk(other)->readlock); | ||
1804 | |||
1805 | other->sk_data_ready(other); | ||
1806 | |||
1807 | return size; | ||
1808 | |||
1809 | err_state_unlock: | ||
1810 | unix_state_unlock(other); | ||
1811 | err_unlock: | ||
1812 | mutex_unlock(&unix_sk(other)->readlock); | ||
1813 | err: | ||
1814 | kfree_skb(newskb); | ||
1815 | if (send_sigpipe && !(flags & MSG_NOSIGNAL)) | ||
1816 | send_sig(SIGPIPE, current, 0); | ||
1817 | return err; | ||
1818 | } | ||
1819 | |||
1723 | static int unix_seqpacket_sendmsg(struct socket *sock, struct msghdr *msg, | 1820 | static int unix_seqpacket_sendmsg(struct socket *sock, struct msghdr *msg, |
1724 | size_t len) | 1821 | size_t len) |
1725 | { | 1822 | { |