[PATCH] Fix RECVFILE with non-blocking sockets and add OEM recvfile call.

Jeremy Allison jra at samba.org
Thu May 8 15:28:08 MDT 2014


Hi all,

Here are two patches to fix non-blocking RECVFILE
for OEMs who use a modified kernel
that allows splice to move data directly
from socket to file instead of going
via a pipe.

The first patch is the one that Jones
tested to ensure that the default
implementation of sys_recvfile
copes correctly with a non-blocking
socket. This one just needs second
team review.

The second one is the patch that
NetGear uses in their boxes in
conjunction with their custom
kernel splice code that allows
direct recvfile from socket fd
to file fd. This was was Signed-off-by
Justin Maggard <jmaggard at netgear.com>
(Justin has sent in the GPL assignment
form) and reviewed by me and just needs
a second Team reviewer. They want
to push it upstream to prevent them
from having to maintain it as an
out-of-tree patch.

It doesn't compile by default, but
needs:

#define HAVE_LINUX_SPLICE_FROM_SOCKET

set inside local.h.

Please review and push if you're
happy !

Cheers,

	Jeremy.
-------------- next part --------------
From c0c818caa761898bd4cec55a39c7603a4d640e5a Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 15 Apr 2014 12:43:06 -0700
Subject: [PATCH 1/2] s3: smbd : default_sys_recvfile() and spice() recvfile
 were not written to cope with non-blocking sockets.

When either the socket read() or splice() return
-1 EWOULDBLOCK or EAGAIN, if no bytes have been
processed yet return -1 and let the caller set
blocking and retry. If bytes have been processed,
just return them as a short read and let the
caller retry with the remaining needed reads.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/lib/recvfile.c | 46 ++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 44 insertions(+), 2 deletions(-)

diff --git a/source3/lib/recvfile.c b/source3/lib/recvfile.c
index 500a7e4..bffe07f 100644
--- a/source3/lib/recvfile.c
+++ b/source3/lib/recvfile.c
@@ -75,8 +75,33 @@ static ssize_t default_sys_recvfile(int fromfd,
 		ssize_t read_ret;
 		size_t toread = MIN(bufsize,count - total);
 
-		/* Read from socket - ignore EINTR. */
-		read_ret = sys_read(fromfd, buffer, toread);
+		/*
+		 * Read from socket - ignore EINTR.
+		 * Can't use sys_read() as that also
+		 * ignores EAGAIN and EWOULDBLOCK.
+		 */
+		do {
+			read_ret = read(fromfd, buffer, toread);
+		} while (read_ret == -1 && errno == EINTR);
+
+#if defined(EWOULDBLOCK)
+		if (read_ret == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+#else
+		if (read_ret == -1 && (errno == EAGAIN)) {
+#endif
+			/*
+			 * fromfd socket is in non-blocking mode.
+			 * If we already read some and wrote
+			 * it successfully, return that.
+			 * Only return -1 if this is the first read
+			 * attempt. Caller will handle both cases.
+			 */
+			if (total_written != 0) {
+				return total_written;
+			}
+			return -1;
+		}
+
 		if (read_ret <= 0) {
 			/* EOF or socket error. */
 			return -1;
@@ -184,6 +209,23 @@ ssize_t sys_recvfile(int fromfd,
 				return default_sys_recvfile(fromfd, tofd,
 							    offset, count);
 			}
+#if defined(EWOULDBLOCK)
+			if (errno == EAGAIN || errno == EWOULDBLOCK) {
+#else
+			if (errno == EAGAIN) {
+#endif
+				/*
+				 * fromfd socket is in non-blocking mode.
+				 * If we already read some and wrote
+				 * it successfully, return that.
+				 * Only return -1 if this is the first read
+				 * attempt. Caller will handle both cases.
+				 */
+				if (total_written != 0) {
+					return total_written;
+				}
+				return -1;
+			}
 			break;
 		}
 
-- 
1.9.1.423.g4596e3a


From baeb4fb12a570fb88fde9d38e6a790ba88b86704 Mon Sep 17 00:00:00 2001
From: Justin Maggard <jmaggard at netgear.com>
Date: Wed, 7 May 2014 13:47:39 -0700
Subject: [PATCH 2/2] recvfile: add support for splice from socket to file

Many Linux platforms have modified kernels that can splice directly
from socket to file, without using an intermediate pipe.

Signed-off-by: Justin Maggard <jmaggard at netgear.com>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
 source3/lib/recvfile.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 81 insertions(+), 1 deletion(-)

diff --git a/source3/lib/recvfile.c b/source3/lib/recvfile.c
index bffe07f..53112dc 100644
--- a/source3/lib/recvfile.c
+++ b/source3/lib/recvfile.c
@@ -145,7 +145,87 @@ static ssize_t default_sys_recvfile(int fromfd,
 	return (ssize_t)total_written;
 }
 
-#if defined(HAVE_LINUX_SPLICE)
+#if defined(HAVE_LINUX_SPLICE_FROM_SOCKET)
+
+/*
+ * For platforms that support direct splice-from-socket-to-file
+ */
+ssize_t sys_recvfile(int fromfd,
+			int tofd,
+			off_t offset,
+			size_t count)
+{
+	size_t total_written = 0;
+	loff_t splice_offset = offset;
+	static bool has_splice_from_socket = true;
+
+	if (!has_splice_from_socket)
+		return default_sys_recvfile(fromfd, tofd, offset, count);
+
+	DEBUG(10,("%s: from = %d, to = %d, "
+		"offset=%.0f, count = %lu\n",
+		__func__,
+		fromfd, tofd, (double)offset,
+		(unsigned long)count));
+
+	if (!count)
+		return 0;
+
+	while (count > 0) {
+		int nwritten;
+
+		nwritten = splice(fromfd, NULL, tofd, &splice_offset, count,
+				  SPLICE_F_MOVE);
+		if (nwritten < 0) {
+			/* If platform does not support direct splice-from-
+			 * socket, fall back.
+			 */
+			if (errno == EBADF) {
+				DEBUG(5,("%s: splice from socket unavailable. "
+					"Falls back to default_sys_recvfile.\n",
+					__func__));
+				has_splice_from_socket = false;
+				return default_sys_recvfile(fromfd, tofd,
+							    offset, count);
+			}
+#if defined(EWOULDBLOCK)
+			if (errno == EAGAIN || errno == EWOULDBLOCK) {
+#else
+			if (errno == EAGAIN) {
+#endif
+				/*
+				 * fromfd socket is in non-blocking mode.
+				 * If we already read some and wrote
+				 * it successfully, return that.
+				 * Only return -1 if this is the first read
+				 * attempt. Caller will handle both cases.
+				 */
+				if (total_written != 0) {
+					return total_written;
+				}
+				return -1;
+			}
+			break;
+		}
+
+		count -= nwritten;
+		offset += nwritten;
+		total_written += nwritten;
+	}
+
+	if (count) {
+		int saved_errno = errno;
+		if (drain_socket(fromfd, count) != count) {
+			/* socket is dead. */
+			return -1;
+		}
+		errno = saved_errno;
+	}
+
+	return total_written;
+}
+
+#elif defined(HAVE_LINUX_SPLICE)
 
 /*
  * Try and use the Linux system call to do this.
-- 
1.9.1.423.g4596e3a



More information about the samba-technical mailing list