[PATCH linux dev-4.13 1/2] fsi: sbefifo: Unpack !sbefifo_xfr_rsp_pending(client) test

Andrew Jeffery andrew at aj.id.au
Mon May 14 16:18:08 AEST 2018


Currently a read() on a non-blocking SBEFIFO file-descriptor will return
EAGAIN despite the entire response having been read out of the device on
a prior read. This is broken if userspace performs partial reads due to
undersizing its destination buffer relative to the (unknowable) response
size.

This change doesn't fix the bug, rather lays the ground-work for the fix
by reworking the EAGAIN handling in sbefifo_read_common().

Signed-off-by: Andrew Jeffery <andrew at aj.id.au>
---
 drivers/fsi/fsi-sbefifo.c | 30 ++++++++++++++++++++++++------
 1 file changed, 24 insertions(+), 6 deletions(-)

diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c
index cc9b9e36ac72..afb5ff48aa3b 100644
--- a/drivers/fsi/fsi-sbefifo.c
+++ b/drivers/fsi/fsi-sbefifo.c
@@ -89,6 +89,7 @@ struct sbefifo_xfr {
 	struct list_head client;
 	struct list_head xfrs;
 	unsigned long flags;
+#define SBEFIFO_XFR_INIT		0
 #define SBEFIFO_XFR_WRITE_DONE		1
 #define SBEFIFO_XFR_RESP_PENDING	2
 #define SBEFIFO_XFR_COMPLETE		3
@@ -292,16 +293,21 @@ static struct sbefifo_xfr *sbefifo_enq_xfr(struct sbefifo_client *client)
 	return xfr;
 }
 
-static bool sbefifo_xfr_rsp_pending(struct sbefifo_client *client)
+static bool sbefifo_xfr_test(struct sbefifo_client *client, uint8_t idx)
 {
 	struct sbefifo_xfr *xfr = list_first_entry_or_null(&client->xfrs,
 							   struct sbefifo_xfr,
 							   client);
 
-	if (xfr && test_bit(SBEFIFO_XFR_RESP_PENDING, &xfr->flags))
-		return true;
+	if (!xfr)
+		return false;
 
-	return false;
+	/* No flags set indicates no action has been attempted on the xfr */
+	if (!idx)
+		return !xfr->flags;
+
+	/* Otherwise some bit will have been set, so test it */
+	return test_bit(idx, &xfr->flags);
 }
 
 static struct sbefifo_client *sbefifo_new_client(struct sbefifo *sbefifo)
@@ -626,8 +632,20 @@ static ssize_t sbefifo_read_common(struct sbefifo_client *client,
 	if ((len >> 2) << 2 != len)
 		return -EINVAL;
 
-	if ((client->f_flags & O_NONBLOCK) && !sbefifo_xfr_rsp_pending(client))
-		return -EAGAIN;
+	if (client->f_flags & O_NONBLOCK) {
+		/* Performing a read before completing a write */
+		if (sbefifo_xfr_test(client, SBEFIFO_XFR_INIT))
+			return -EAGAIN;
+
+		if (sbefifo_xfr_test(client, SBEFIFO_XFR_WRITE_DONE))
+			return -EAGAIN;
+
+		if (sbefifo_xfr_test(client, SBEFIFO_XFR_COMPLETE))
+			return -EAGAIN;
+
+		if (sbefifo_xfr_test(client, SBEFIFO_XFR_CANCEL))
+			return -EAGAIN;
+	}
 
 	sbefifo_get_client(client);
 	if (wait_event_interruptible(sbefifo->wait,
-- 
2.17.0



More information about the openbmc mailing list