Blob Blame History Raw
From: David Herrmann <dh.herrmann@gmail.com>
Date: Sat, 18 Apr 2015 13:04:42 +0200
Subject: [PATCH] kdbus: skip acquiring an active reference in poll()

During poll(), we currently acquire an active reference to the connection
in question to verify it's still active. If it's not active, anymore, we
return POLLHUP.

This works fine, but requires an atomic_inc() to acquire the active
reference. However, all we need is a guarantee that the connection is
active right now, and a guarantee we're called again once this changes.
This is as simple as adding the waitqueue first, then checking the
active-state afterwards. kdbus_conn_disconnect() guarantees to wake us up
_after_ deactivating the connection, thus providing the required barrier
implicitly (in case someone is actually polling / waiting on the
connection).

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Acked-by: Daniel Mack <daniel@zonque.org>
---
 ipc/kdbus/handle.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/ipc/kdbus/handle.c b/ipc/kdbus/handle.c
index a3e01383a6f6..6230c7ef4347 100644
--- a/ipc/kdbus/handle.c
+++ b/ipc/kdbus/handle.c
@@ -610,7 +610,6 @@ static unsigned int kdbus_handle_poll(struct file *file,
 	struct kdbus_handle *handle = file->private_data;
 	enum kdbus_handle_type type;
 	unsigned int mask = POLLOUT | POLLWRNORM;
-	int ret;
 
 	/*
 	 * This pairs with smp_wmb() during handle setup. It guarantees that
@@ -626,18 +625,21 @@ static unsigned int kdbus_handle_poll(struct file *file,
 	if (type != KDBUS_HANDLE_CONNECTED)
 		return POLLERR | POLLHUP;
 
-	ret = kdbus_conn_acquire(handle->conn);
-	if (ret < 0)
-		return POLLERR | POLLHUP;
-
 	poll_wait(file, &handle->conn->wait, wait);
 
+	/*
+	 * Verify the connection hasn't been deactivated _after_ adding the
+	 * wait-queue. This guarantees, that if the connection is deactivated
+	 * after we checked it, the waitqueue is signaled and we're called
+	 * again.
+	 */
+	if (!kdbus_conn_active(handle->conn))
+		return POLLERR | POLLHUP;
+
 	if (!list_empty(&handle->conn->queue.msg_list) ||
 	    atomic_read(&handle->conn->lost_count) > 0)
 		mask |= POLLIN | POLLRDNORM;
 
-	kdbus_conn_release(handle->conn);
-
 	return mask;
 }