From: David van Moolenbroek Date: Sat, 16 Jan 2016 17:30:39 +0000 (+0000) Subject: libc: reorganize vector I/O wrappers X-Git-Url: http://zhaoyanbai.com/repos/?a=commitdiff_plain;h=refs%2Fchanges%2F99%2F3299%2F1;p=minix.git libc: reorganize vector I/O wrappers The reorganization allows other libc system call wrappers (namely, sendmsg and recvmsg) to perform I/O vector coalescing as well. Change-Id: I116b48a6db39439053280ee805e0dcbdaec667a3 --- diff --git a/minix/include/lib.h b/minix/include/lib.h index 00a8eb586..81687ecdc 100644 --- a/minix/include/lib.h +++ b/minix/include/lib.h @@ -11,6 +11,7 @@ /* The following are so basic, all the lib files get them automatically. */ #include /* must be first */ #include +#include #include #include @@ -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 */ diff --git a/minix/lib/libc/sys/vectorio.c b/minix/lib/libc/sys/vectorio.c index 6639ea98a..c8480cfc2 100644 --- a/minix/lib/libc/sys/vectorio.c +++ b/minix/lib/libc/sys/vectorio.c @@ -12,121 +12,150 @@ #include #include -#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; +}