Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5456f09a authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller
Browse files

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: default avatarAlban Crequy <alban.crequy@collabora.co.uk>
Cc: Davide Libenzi <davidel@xmailserver.org>
Signed-off-by: default avatarEric Dumazet <eric.dumazet@gmail.com>
Acked-by: default avatarDavide Libenzi <davidel@xmailserver.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 67426b75
Loading
Loading
Loading
Loading
+9 −15
Original line number Diff line number Diff line
@@ -2074,13 +2074,12 @@ static unsigned int unix_dgram_poll(struct file *file, struct socket *sock,
	if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
		mask |= POLLERR;
	if (sk->sk_shutdown & RCV_SHUTDOWN)
		mask |= POLLRDHUP;
		mask |= POLLRDHUP | POLLIN | POLLRDNORM;
	if (sk->sk_shutdown == SHUTDOWN_MASK)
		mask |= POLLHUP;

	/* readable? */
	if (!skb_queue_empty(&sk->sk_receive_queue) ||
	    (sk->sk_shutdown & RCV_SHUTDOWN))
	if (!skb_queue_empty(&sk->sk_receive_queue))
		mask |= POLLIN | POLLRDNORM;

	/* Connection-based need to check for termination and startup */
@@ -2092,21 +2091,16 @@ static unsigned int unix_dgram_poll(struct file *file, struct socket *sock,
			return mask;
	}

	/* writable? */
	writable = unix_writable(sk);
	if (writable) {
	other = unix_peer_get(sk);
	if (other) {
		if (unix_peer(other) != sk) {
				sock_poll_wait(file, &unix_sk(other)->peer_wait,
					  wait);
			sock_poll_wait(file, &unix_sk(other)->peer_wait, wait);
			if (unix_recvq_full(other))
				writable = 0;
		}

		sock_put(other);
	}
	}

	if (writable)
		mask |= POLLOUT | POLLWRNORM | POLLWRBAND;