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
This commit is contained in:
parent
c33d6ef392
commit
0df28c9fa4
@ -11,6 +11,7 @@
|
|||||||
/* The following are so basic, all the lib files get them automatically. */
|
/* The following are so basic, all the lib files get them automatically. */
|
||||||
#include <minix/config.h> /* must be first */
|
#include <minix/config.h> /* must be first */
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
@ -41,4 +42,11 @@ void _loadname(const char *_name, message *_msgptr);
|
|||||||
int _len(const char *_s);
|
int _len(const char *_s);
|
||||||
void _begsig(int _dummy);
|
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 */
|
#endif /* _LIB_H */
|
||||||
|
@ -12,121 +12,150 @@
|
|||||||
#include <sys/uio.h>
|
#include <sys/uio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#define VECTORIO_READ 1
|
/*
|
||||||
#define VECTORIO_WRITE 2
|
* Create a single temporary buffer for the entire vector. For writes, also
|
||||||
|
* copy the actual data into the temporary buffer.
|
||||||
static ssize_t vectorio_buffer(int fildes, const struct iovec *iov,
|
*/
|
||||||
int iovcnt, int readwrite, ssize_t totallen)
|
ssize_t
|
||||||
|
_vectorio_setup(const struct iovec * iov, int iovcnt, char ** ptr, int op)
|
||||||
{
|
{
|
||||||
char *buffer;
|
char *buffer;
|
||||||
int iovidx, errno_saved;
|
ssize_t totallen, copied;
|
||||||
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)
|
|
||||||
{
|
|
||||||
int i;
|
int i;
|
||||||
ssize_t totallen;
|
|
||||||
|
|
||||||
/* parameter sanity checks */
|
/* Parameter sanity checks. */
|
||||||
if (iovcnt > IOV_MAX)
|
if (iovcnt < 0 || iovcnt > IOV_MAX) {
|
||||||
{
|
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
totallen = 0;
|
totallen = 0;
|
||||||
for (i = 0; i < iovcnt; i++)
|
for (i = 0; i < iovcnt; i++) {
|
||||||
{
|
/* Do not read/write anything in case of possible overflow. */
|
||||||
/* don't read/write anything in case of possible overflow */
|
if ((size_t)SSIZE_MAX - totallen < iov[i].iov_len) {
|
||||||
if ((ssize_t) (totallen + iov[i].iov_len) < totallen)
|
|
||||||
{
|
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
totallen += iov[i].iov_len;
|
totallen += iov[i].iov_len;
|
||||||
|
|
||||||
/* report on NULL pointers */
|
/* Report on NULL pointers. */
|
||||||
if (iov[i].iov_len && !iov[i].iov_base)
|
if (iov[i].iov_len > 0 && iov[i].iov_base == NULL) {
|
||||||
{
|
|
||||||
errno = EFAULT;
|
errno = EFAULT;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* anything to do? */
|
/* Anything to do? */
|
||||||
if (totallen == 0)
|
if (totallen == 0) {
|
||||||
|
*ptr = NULL;
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* there aught to be a system call here; instead we use an intermediate
|
* Clean up the temporary buffer created for the vector. For successful reads,
|
||||||
* buffer; this is preferred over multiple read/write calls because
|
* also copy out the retrieved buffer contents.
|
||||||
* this function has to be atomic
|
|
||||||
*/
|
*/
|
||||||
return vectorio_buffer(fildes, iov, iovcnt, readwrite, totallen);
|
void
|
||||||
}
|
_vectorio_cleanup(const struct iovec * iov, int iovcnt, char * buffer,
|
||||||
|
ssize_t r, int op)
|
||||||
ssize_t readv(int fildes, const struct iovec *iov, int iovcnt)
|
|
||||||
{
|
{
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t writev(int fildes, const struct iovec *iov, int iovcnt)
|
/* Free the temporary buffer. */
|
||||||
|
if (buffer != NULL)
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
errno = errno_saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user