aboutsummaryrefslogtreecommitdiffstats
path: root/net/unix/af_unix.c
diff options
context:
space:
mode:
authorRainer Weikusat <rweikusat@mobileactivedefense.com>2015-12-06 16:11:38 -0500
committerDavid S. Miller <davem@davemloft.net>2015-12-06 23:31:54 -0500
commit64874280889e7c0b2c9266705363627d4c92cf01 (patch)
tree2e7d955b8093dbd3cfc80a68ae1e4c648a34e74f /net/unix/af_unix.c
parentea3793ee29d3621faf857fa8ef5425e9ff9a756d (diff)
af_unix: fix unix_dgram_recvmsg entry locking
The current unix_dgram_recvsmg code acquires the u->readlock mutex in order to protect access to the peek offset prior to calling __skb_recv_datagram for actually receiving data. This implies that a blocking reader will go to sleep with this mutex held if there's presently no data to return to userspace. Two non-desirable side effects of this are that a later non-blocking read call on the same socket will block on the ->readlock mutex until the earlier blocking call releases it (or the readers is interrupted) and that later blocking read calls will wait longer than the effective socket read timeout says they should: The timeout will only start 'ticking' once such a reader hits the schedule_timeout in wait_for_more_packets (core.c) while the time it already had to wait until it could acquire the mutex is unaccounted for. The patch avoids both by using the __skb_try_recv_datagram and __skb_wait_for_more packets functions created by the first patch to implement a unix_dgram_recvmsg read loop which releases the readlock mutex prior to going to sleep and reacquires it as needed afterwards. Non-blocking readers will thus immediately return with -EAGAIN if there's no data available regardless of any concurrent blocking readers and all blocking readers will end up sleeping via schedule_timeout, thus honouring the configured socket receive timeout. Signed-off-by: Rainer Weikusat <rweikusat@mobileactivedefense.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/unix/af_unix.c')
-rw-r--r--net/unix/af_unix.c35
1 files changed, 20 insertions, 15 deletions
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 502e572af3fd..1c3c1f3a3ec4 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -2078,8 +2078,8 @@ static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg,
2078 struct scm_cookie scm; 2078 struct scm_cookie scm;
2079 struct sock *sk = sock->sk; 2079 struct sock *sk = sock->sk;
2080 struct unix_sock *u = unix_sk(sk); 2080 struct unix_sock *u = unix_sk(sk);
2081 int noblock = flags & MSG_DONTWAIT; 2081 struct sk_buff *skb, *last;
2082 struct sk_buff *skb; 2082 long timeo;
2083 int err; 2083 int err;
2084 int peeked, skip; 2084 int peeked, skip;
2085 2085
@@ -2087,26 +2087,32 @@ static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg,
2087 if (flags&MSG_OOB) 2087 if (flags&MSG_OOB)
2088 goto out; 2088 goto out;
2089 2089
2090 err = mutex_lock_interruptible(&u->readlock); 2090 timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
2091 if (unlikely(err)) {
2092 /* recvmsg() in non blocking mode is supposed to return -EAGAIN
2093 * sk_rcvtimeo is not honored by mutex_lock_interruptible()
2094 */
2095 err = noblock ? -EAGAIN : -ERESTARTSYS;
2096 goto out;
2097 }
2098 2091
2099 skip = sk_peek_offset(sk, flags); 2092 do {
2093 mutex_lock(&u->readlock);
2100 2094
2101 skb = __skb_recv_datagram(sk, flags, &peeked, &skip, &err); 2095 skip = sk_peek_offset(sk, flags);
2102 if (!skb) { 2096 skb = __skb_try_recv_datagram(sk, flags, &peeked, &skip, &err,
2097 &last);
2098 if (skb)
2099 break;
2100
2101 mutex_unlock(&u->readlock);
2102
2103 if (err != -EAGAIN)
2104 break;
2105 } while (timeo &&
2106 !__skb_wait_for_more_packets(sk, &err, &timeo, last));
2107
2108 if (!skb) { /* implies readlock unlocked */
2103 unix_state_lock(sk); 2109 unix_state_lock(sk);
2104 /* Signal EOF on disconnected non-blocking SEQPACKET socket. */ 2110 /* Signal EOF on disconnected non-blocking SEQPACKET socket. */
2105 if (sk->sk_type == SOCK_SEQPACKET && err == -EAGAIN && 2111 if (sk->sk_type == SOCK_SEQPACKET && err == -EAGAIN &&
2106 (sk->sk_shutdown & RCV_SHUTDOWN)) 2112 (sk->sk_shutdown & RCV_SHUTDOWN))
2107 err = 0; 2113 err = 0;
2108 unix_state_unlock(sk); 2114 unix_state_unlock(sk);
2109 goto out_unlock; 2115 goto out;
2110 } 2116 }
2111 2117
2112 if (wq_has_sleeper(&u->peer_wait)) 2118 if (wq_has_sleeper(&u->peer_wait))
@@ -2164,7 +2170,6 @@ static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg,
2164 2170
2165out_free: 2171out_free:
2166 skb_free_datagram(sk, skb); 2172 skb_free_datagram(sk, skb);
2167out_unlock:
2168 mutex_unlock(&u->readlock); 2173 mutex_unlock(&u->readlock);
2169out: 2174out:
2170 return err; 2175 return err;