diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2010-10-31 01:36:23 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-11-08 16:50:09 -0500 |
commit | 5456f09aaf88731e16dbcea7522cb330b6846415 (patch) | |
tree | eac95420f5da87dc95342aa23d9a97653544863d /net/unix | |
parent | 67426b756c4d52c511c4b22b269accea171692a8 (diff) |
af_unix: fix unix_dgram_poll() behavior for EPOLLOUT event
Alban Crequy reported a problem with connected dgram af_unix sockets and
provided a test program. epoll() would miss to send an EPOLLOUT event
when a thread unqueues a packet from the other peer, making its receive
queue not full.
This is because unix_dgram_poll() fails to call sock_poll_wait(file,
&unix_sk(other)->peer_wait, wait);
if the socket is not writeable at the time epoll_ctl(ADD) is called.
We must call sock_poll_wait(), regardless of 'writable' status, so that
epoll can be notified later of states changes.
Misc: avoids testing twice (sk->sk_shutdown & RCV_SHUTDOWN)
Reported-by: Alban Crequy <alban.crequy@collabora.co.uk>
Cc: Davide Libenzi <davidel@xmailserver.org>
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Acked-by: Davide Libenzi <davidel@xmailserver.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/unix')
-rw-r--r-- | net/unix/af_unix.c | 24 |
1 files changed, 9 insertions, 15 deletions
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index f33c5958dbb2..e8898758dd31 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c | |||
@@ -2074,13 +2074,12 @@ static unsigned int unix_dgram_poll(struct file *file, struct socket *sock, | |||
2074 | if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue)) | 2074 | if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue)) |
2075 | mask |= POLLERR; | 2075 | mask |= POLLERR; |
2076 | if (sk->sk_shutdown & RCV_SHUTDOWN) | 2076 | if (sk->sk_shutdown & RCV_SHUTDOWN) |
2077 | mask |= POLLRDHUP; | 2077 | mask |= POLLRDHUP | POLLIN | POLLRDNORM; |
2078 | if (sk->sk_shutdown == SHUTDOWN_MASK) | 2078 | if (sk->sk_shutdown == SHUTDOWN_MASK) |
2079 | mask |= POLLHUP; | 2079 | mask |= POLLHUP; |
2080 | 2080 | ||
2081 | /* readable? */ | 2081 | /* readable? */ |
2082 | if (!skb_queue_empty(&sk->sk_receive_queue) || | 2082 | if (!skb_queue_empty(&sk->sk_receive_queue)) |
2083 | (sk->sk_shutdown & RCV_SHUTDOWN)) | ||
2084 | mask |= POLLIN | POLLRDNORM; | 2083 | mask |= POLLIN | POLLRDNORM; |
2085 | 2084 | ||
2086 | /* Connection-based need to check for termination and startup */ | 2085 | /* Connection-based need to check for termination and startup */ |
@@ -2092,20 +2091,15 @@ static unsigned int unix_dgram_poll(struct file *file, struct socket *sock, | |||
2092 | return mask; | 2091 | return mask; |
2093 | } | 2092 | } |
2094 | 2093 | ||
2095 | /* writable? */ | ||
2096 | writable = unix_writable(sk); | 2094 | writable = unix_writable(sk); |
2097 | if (writable) { | 2095 | other = unix_peer_get(sk); |
2098 | other = unix_peer_get(sk); | 2096 | if (other) { |
2099 | if (other) { | 2097 | if (unix_peer(other) != sk) { |
2100 | if (unix_peer(other) != sk) { | 2098 | sock_poll_wait(file, &unix_sk(other)->peer_wait, wait); |
2101 | sock_poll_wait(file, &unix_sk(other)->peer_wait, | 2099 | if (unix_recvq_full(other)) |
2102 | wait); | 2100 | writable = 0; |
2103 | if (unix_recvq_full(other)) | ||
2104 | writable = 0; | ||
2105 | } | ||
2106 | |||
2107 | sock_put(other); | ||
2108 | } | 2101 | } |
2102 | sock_put(other); | ||
2109 | } | 2103 | } |
2110 | 2104 | ||
2111 | if (writable) | 2105 | if (writable) |