]> Zhao Yanbai Git Server - minix.git/commitdiff
libc: reorganize vector I/O wrappers 99/3299/1
authorDavid van Moolenbroek <david@minix3.org>
Sat, 16 Jan 2016 17:30:39 +0000 (17:30 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Mon, 22 Feb 2016 23:24:47 +0000 (23:24 +0000)
The reorganization allows other libc system call wrappers (namely,
sendmsg and recvmsg) to perform I/O vector coalescing as well.

Change-Id: I116b48a6db39439053280ee805e0dcbdaec667a3

minix/include/lib.h
minix/lib/libc/sys/vectorio.c

index 00a8eb586d3916836a1f2cb7b29ba1d320aa4676..81687ecdc62097be7295d7f5e9d34fdbc4080fcc 100644 (file)
@@ -11,6 +11,7 @@
 /* The following are so basic, all the lib files get them automatically. */
 #include <minix/config.h>      /* must be first */
 #include <sys/types.h>
+#include <sys/uio.h>
 #include <limits.h>
 #include <errno.h>
 
@@ -41,4 +42,11 @@ void _loadname(const char *_name, message *_msgptr);
 int _len(const char *_s);
 void _begsig(int _dummy);
 
+#define _VECTORIO_READ 1
+#define _VECTORIO_WRITE        2
+ssize_t _vectorio_setup(const struct iovec * iov, int iovcnt, char ** ptr,
+       int op);
+void _vectorio_cleanup(const struct iovec * iov, int iovcnt, char * buffer,
+       ssize_t r, int op);
+
 #endif /* _LIB_H */
index 6639ea98a6e395333b1d271042c5e80cb4c53ec5..c8480cfc2a4ad59d0ec2cb99fa4dac9ab9e14923 100644 (file)
 #include <sys/uio.h>
 #include <unistd.h>
 
-#define VECTORIO_READ  1
-#define VECTORIO_WRITE 2
-
-static ssize_t vectorio_buffer(int fildes, const struct iovec *iov, 
-       int iovcnt, int readwrite, ssize_t totallen)
+/*
+ * Create a single temporary buffer for the entire vector.  For writes, also
+ * copy the actual data into the temporary buffer.
+ */
+ssize_t
+_vectorio_setup(const struct iovec * iov, int iovcnt, char ** ptr, int op)
 {
        char *buffer;
-       int iovidx, errno_saved;
-       ssize_t copied, len, r;
-
-       /* allocate buffer */
-       buffer = (char *) malloc(totallen);
-       if (!buffer)
-               return -1;
-
-       /* perform the actual read/write for the entire buffer */
-       switch (readwrite)
-       {
-               case VECTORIO_READ:
-                       /* first read, then copy buffers (only part read) */
-                       r = read(fildes, buffer, totallen);
-
-                       copied = 0;
-                       iovidx = 0;
-                       while (copied < r)
-                       {
-                               assert(iovidx < iovcnt);
-                               len = iov[iovidx].iov_len;
-                               if (len > r - copied)
-                                       len = r - copied;
-                               memcpy(iov[iovidx++].iov_base, buffer + copied, len);
-                               copied += len;
-                       }
-                       assert(r < 0 || r == copied);
-                       break;
-
-               case VECTORIO_WRITE: 
-                       /* first copy buffers, then write */
-                       copied = 0;
-                       for (iovidx = 0; iovidx < iovcnt; iovidx++)
-                       {
-                               memcpy(buffer + copied, iov[iovidx].iov_base, 
-                                       iov[iovidx].iov_len);
-                               copied += iov[iovidx].iov_len;
-                       }
-                       assert(copied == totallen);
-
-                       r = write(fildes, buffer, totallen);
-                       break;
-
-               default:        
-                       assert(0);
-                       errno = EINVAL;
-                       r = -1;
-       }
-
-       /* free the buffer, keeping errno unchanged */
-       errno_saved = errno;
-       free(buffer);
-       errno = errno_saved;
-
-       return r;
-}
-
-static ssize_t vectorio(int fildes, const struct iovec *iov, 
-       int iovcnt, int readwrite)
-{
+       ssize_t totallen, copied;
        int i;
-       ssize_t totallen;
 
-       /* parameter sanity checks */
-       if (iovcnt > IOV_MAX)
-       {
+       /* Parameter sanity checks. */
+       if (iovcnt < 0 || iovcnt > IOV_MAX) {
                errno = EINVAL;
                return -1;
        }
 
        totallen = 0;
-       for (i = 0; i < iovcnt; i++)
-       {
-               /* don't read/write anything in case of possible overflow */
-               if ((ssize_t) (totallen + iov[i].iov_len) < totallen)
-               {
+       for (i = 0; i < iovcnt; i++) {
+               /* Do not read/write anything in case of possible overflow. */
+               if ((size_t)SSIZE_MAX - totallen < iov[i].iov_len) {
                        errno = EINVAL;
                        return -1;
                }
                totallen += iov[i].iov_len;
 
-               /* report on NULL pointers */
-               if (iov[i].iov_len && !iov[i].iov_base)
-               {
+               /* Report on NULL pointers. */
+               if (iov[i].iov_len > 0 && iov[i].iov_base == NULL) {
                        errno = EFAULT;
                        return -1;
                }
        }
 
-       /* anything to do? */
-       if (totallen == 0)
+       /* Anything to do? */
+       if (totallen == 0) {
+               *ptr = NULL;
                return 0;
+       }
 
-       /* 
-        * there aught to be a system call here; instead we use an intermediate 
-        * buffer; this is preferred over multiple read/write calls because 
-        * this function has to be atomic
-        */
-       return vectorio_buffer(fildes, iov, iovcnt, readwrite, totallen);
+       /* Allocate a temporary buffer. */
+       buffer = (char *)malloc(totallen);
+       if (buffer == NULL)
+               return -1;
+
+       /* For writes, copy over the buffer contents before the call. */
+       if (op == _VECTORIO_WRITE) {
+               copied = 0;
+               for (i = 0; i < iovcnt; i++) {
+                       memcpy(buffer + copied, iov[i].iov_base,
+                           iov[i].iov_len);
+                       copied += iov[i].iov_len;
+               }
+               assert(copied == totallen);
+       }
+
+       /* Return the temporary buffer and its size. */
+       *ptr = buffer;
+       return totallen;
 }
 
-ssize_t readv(int fildes, const struct iovec *iov, int iovcnt)
+/*
+ * Clean up the temporary buffer created for the vector.  For successful reads,
+ * also copy out the retrieved buffer contents.
+ */
+void
+_vectorio_cleanup(const struct iovec * iov, int iovcnt, char * buffer,
+       ssize_t r, int op)
 {
-       return vectorio(fildes, iov, iovcnt, VECTORIO_READ);    
+       int i, errno_saved;
+       ssize_t copied, len;
+
+       /* Make sure to retain the original errno value in case of failure. */
+       errno_saved = errno;
+
+       /*
+        * If this was for a read and the read call succeeded, copy out the
+        * resulting data.
+        */
+       if (op == _VECTORIO_READ && r > 0) {
+               assert(buffer != NULL);
+               copied = 0;
+               i = 0;
+               while (copied < r) {
+                       assert(i < iovcnt);
+                       len = iov[i].iov_len;
+                       if (len > r - copied)
+                               len = r - copied;
+                       memcpy(iov[i++].iov_base, buffer + copied, len);
+                       copied += len;
+               }
+               assert(r < 0 || r == copied);
+       }
+
+       /* Free the temporary buffer. */
+       if (buffer != NULL)
+               free(buffer);
+
+       errno = errno_saved;
 }
 
-ssize_t writev(int fildes, const struct iovec *iov, int iovcnt)
+/*
+ * Read a vector.
+ */
+ssize_t
+readv(int fd, const struct iovec * iov, int iovcnt)
 {
-       return vectorio(fildes, iov, iovcnt, VECTORIO_WRITE);   
+       char *ptr;
+       ssize_t r;
+
+       /*
+        * There ought to be just a readv system call here.  Instead, we use an
+        * intermediate buffer.  This approach is preferred over multiple read
+        * calls, because the actual I/O operation has to be atomic.
+        */
+       if ((r = _vectorio_setup(iov, iovcnt, &ptr, _VECTORIO_READ)) <= 0)
+               return r;
+
+       r = read(fd, ptr, r);
+
+       _vectorio_cleanup(iov, iovcnt, ptr, r, _VECTORIO_READ);
+
+       return r;
 }
 
+/*
+ * Write a vector.
+ */
+ssize_t
+writev(int fd, const struct iovec * iov, int iovcnt)
+{
+       char *ptr;
+       ssize_t r;
+
+       /*
+        * There ought to be just a writev system call here.  Instead, we use
+        * an intermediate buffer.  This approach is preferred over multiple
+        * write calls, because the actual I/O operation has to be atomic.
+        */
+       if ((r = _vectorio_setup(iov, iovcnt, &ptr, _VECTORIO_WRITE)) <= 0)
+               return r;
+
+       r = write(fd, ptr, r);
+
+       _vectorio_cleanup(iov, iovcnt, ptr, r, _VECTORIO_WRITE);
+
+       return r;
+}