uio api tweaks

* Make readv/writev implementations update struct uio
  This can simplify partial result handling.

* change the error number on the overflow from EOVERFLOW to EINVAL
  to match NetBSD

* add a commented out uio_offset field. I used "#if 0" here as
  C comments can't nest.

* add a few helper functions

Note on uio_copyfrom/uio_copyto:
although i'm not quite happy with the "offset" functionality,
it's necessary to simplify the adaptation of some drivers like
drivers/serial/serial.c, which (ab)uses the user-supplied buffer
as a line-buffer.
This commit is contained in:
YAMAMOTO Takashi
2024-11-21 18:08:31 +09:00
committed by Xiang Xiao
parent 2749510413
commit 30ad31e9d7
8 changed files with 293 additions and 64 deletions
+20 -8
View File
@@ -50,8 +50,7 @@
*
****************************************************************************/
static ssize_t file_readv_compat(FAR struct file *filep,
FAR const struct uio *uio)
static ssize_t file_readv_compat(FAR struct file *filep, FAR struct uio *uio)
{
FAR const struct iovec *iov = uio->uio_iov;
int iovcnt = uio->uio_iovcnt;
@@ -102,6 +101,11 @@ static ssize_t file_readv_compat(FAR struct file *filep,
remaining -= nread;
}
if (ntotal >= 0)
{
uio_advance(uio, ntotal);
}
return ntotal;
}
@@ -130,7 +134,7 @@ static ssize_t file_readv_compat(FAR struct file *filep,
*
****************************************************************************/
ssize_t file_readv(FAR struct file *filep, FAR const struct uio *uio)
ssize_t file_readv(FAR struct file *filep, FAR struct uio *uio)
{
FAR struct inode *inode;
ssize_t ret = -EBADF;
@@ -204,11 +208,16 @@ ssize_t file_read(FAR struct file *filep, FAR void *buf, size_t nbytes)
{
struct iovec iov;
struct uio uio;
ssize_t ret;
iov.iov_base = buf;
iov.iov_len = nbytes;
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
ret = uio_init(&uio, &iov, 1);
if (ret != 0)
{
return ret;
}
return file_readv(filep, &uio);
}
@@ -251,9 +260,12 @@ ssize_t nx_readv(int fd, FAR const struct iovec *iov, int iovcnt)
/* Then let file_readv do all of the work. */
uio.uio_iov = iov;
uio.uio_iovcnt = iovcnt;
ret = file_readv(filep, &uio);
ret = uio_init(&uio, iov, iovcnt);
if (ret == 0)
{
ret = file_readv(filep, &uio);
}
fs_putfilep(filep);
return ret;
}
+167 -6
View File
@@ -35,19 +35,19 @@
#include <errno.h>
/****************************************************************************
* Public Functions
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: uio_total_len
* Name: uio_calc_resid
*
* Description:
* Return the total length of data in bytes.
* Or -EOVERFLOW.
* Return the remaining length of data in bytes.
* Or -EINVAL.
*
****************************************************************************/
ssize_t uio_total_len(FAR const struct uio *uio)
ssize_t uio_calc_resid(FAR const struct uio *uio)
{
const struct iovec *iov = uio->uio_iov;
int iovcnt = uio->uio_iovcnt;
@@ -58,7 +58,7 @@ ssize_t uio_total_len(FAR const struct uio *uio)
{
if (SSIZE_MAX - len < iov[i].iov_len)
{
return -EOVERFLOW;
return -EINVAL;
}
len += iov[i].iov_len;
@@ -66,3 +66,164 @@ ssize_t uio_total_len(FAR const struct uio *uio)
return len;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: uio_advance
*
* Description:
* Advance the pointer/offset in uio by the specified amount.
*
****************************************************************************/
void uio_advance(FAR struct uio *uio, size_t sz)
{
FAR const struct iovec *iov = uio->uio_iov;
int iovcnt = uio->uio_iovcnt;
size_t offset_in_iov = uio->uio_offset_in_iov;
DEBUGASSERT(sz <= SSIZE_MAX);
DEBUGASSERT(uio->uio_resid <= SSIZE_MAX);
DEBUGASSERT(sz <= uio->uio_resid);
DEBUGASSERT(uio->uio_offset_in_iov + uio->uio_resid ==
uio_calc_resid(uio));
uio->uio_resid -= sz;
while (iovcnt > 0)
{
DEBUGASSERT(offset_in_iov <= iov->iov_len);
if (sz < iov->iov_len - offset_in_iov)
{
offset_in_iov += sz;
break;
}
sz -= iov->iov_len - offset_in_iov;
iov++;
iovcnt--;
offset_in_iov = 0;
}
uio->uio_iov = iov;
uio->uio_iovcnt = iovcnt;
uio->uio_offset_in_iov = offset_in_iov;
DEBUGASSERT(uio->uio_offset_in_iov + uio->uio_resid ==
uio_calc_resid(uio));
}
/****************************************************************************
* Name: uio_init
*
* Description:
* Initialize the uio structure with reasonable default values.
*
****************************************************************************/
int uio_init(FAR struct uio *uio, FAR const struct iovec *iov, int iovcnt)
{
ssize_t resid;
memset(uio, 0, sizeof(*uio));
uio->uio_iov = iov;
uio->uio_iovcnt = iovcnt;
resid = uio_calc_resid(uio);
if (resid < 0)
{
return -EINVAL;
}
uio->uio_resid = resid;
return 0;
}
/****************************************************************************
* Name: uio_copyfrom
*
* Description:
* Copy data from the linear buffer to uio.
*
****************************************************************************/
void uio_copyfrom(FAR struct uio *uio, size_t offset, FAR const void *buf,
size_t len)
{
FAR const struct iovec *iov = uio->uio_iov;
DEBUGASSERT(uio->uio_resid >= 0);
DEBUGASSERT(uio->uio_resid <= SSIZE_MAX);
DEBUGASSERT(len <= uio->uio_resid);
DEBUGASSERT(offset <= uio->uio_resid - len);
DEBUGASSERT(SSIZE_MAX - offset >= uio->uio_offset_in_iov);
DEBUGASSERT(uio->uio_offset_in_iov + uio->uio_resid ==
uio_calc_resid(uio));
offset += uio->uio_offset_in_iov;
while (offset > iov->iov_len)
{
offset -= iov->iov_len;
iov++;
}
while (len > 0)
{
size_t blen = len;
if (blen > iov->iov_len - offset)
{
blen = iov->iov_len - offset;
}
memcpy((FAR uint8_t *)iov->iov_base + offset, buf, blen);
len -= blen;
buf = (const uint8_t *)buf + blen;
iov++;
offset = 0;
}
}
/****************************************************************************
* Name: uio_copyto
*
* Description:
* Copy data to the linear buffer from uio.
*
****************************************************************************/
void uio_copyto(FAR struct uio *uio, size_t offset, FAR void *buf,
size_t len)
{
FAR const struct iovec *iov = uio->uio_iov;
DEBUGASSERT(uio->uio_resid >= 0);
DEBUGASSERT(uio->uio_resid <= SSIZE_MAX);
DEBUGASSERT(len <= uio->uio_resid);
DEBUGASSERT(offset <= uio->uio_resid - len);
DEBUGASSERT(SSIZE_MAX - offset >= uio->uio_offset_in_iov);
DEBUGASSERT(uio->uio_offset_in_iov + uio->uio_resid ==
uio_calc_resid(uio));
offset += uio->uio_offset_in_iov;
while (offset > iov->iov_len)
{
offset -= iov->iov_len;
iov++;
}
while (len > 0)
{
size_t blen = len;
if (blen > iov->iov_len - offset)
{
blen = iov->iov_len - offset;
}
memcpy(buf, (FAR const uint8_t *)iov->iov_base + offset, blen);
len -= blen;
buf = (uint8_t *)buf + blen;
iov++;
offset = 0;
}
}
+20 -7
View File
@@ -51,7 +51,7 @@
****************************************************************************/
static ssize_t file_writev_compat(FAR struct file *filep,
FAR const struct uio *uio)
FAR struct uio *uio)
{
FAR const struct iovec *iov = uio->uio_iov;
int iovcnt = uio->uio_iovcnt;
@@ -102,6 +102,11 @@ static ssize_t file_writev_compat(FAR struct file *filep,
remaining -= nwritten;
}
if (ntotal >= 0)
{
uio_advance(uio, ntotal);
}
return ntotal;
}
@@ -133,7 +138,7 @@ static ssize_t file_writev_compat(FAR struct file *filep,
*
****************************************************************************/
ssize_t file_writev(FAR struct file *filep, FAR const struct uio *uio)
ssize_t file_writev(FAR struct file *filep, FAR struct uio *uio)
{
FAR struct inode *inode;
ssize_t ret = -EBADF;
@@ -202,11 +207,16 @@ ssize_t file_write(FAR struct file *filep, FAR const void *buf,
{
struct iovec iov;
struct uio uio;
ssize_t ret;
iov.iov_base = (FAR void *)buf;
iov.iov_len = nbytes;
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
ret = uio_init(&uio, &iov, 1);
if (ret != 0)
{
return ret;
}
return file_writev(filep, &uio);
}
@@ -252,9 +262,12 @@ ssize_t nx_writev(int fd, FAR const struct iovec *iov, int iovcnt)
* index. Note that file_writev() will return the errno on failure.
*/
uio.uio_iov = iov;
uio.uio_iovcnt = iovcnt;
ret = file_writev(filep, &uio);
ret = uio_init(&uio, iov, iovcnt);
if (ret == 0)
{
ret = file_writev(filep, &uio);
}
fs_putfilep(filep);
}