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
+10 -9
View File
@@ -40,10 +40,8 @@
* Private Function Prototypes * Private Function Prototypes
****************************************************************************/ ****************************************************************************/
static ssize_t loop_readv(FAR struct file *filep, static ssize_t loop_readv(FAR struct file *filep, FAR struct uio *uio);
FAR const struct uio *uio); static ssize_t loop_writev(FAR struct file *filep, FAR struct uio *uio);
static ssize_t loop_writev(FAR struct file *filep,
FAR const struct uio *uio);
static int loop_ioctl(FAR struct file *filep, int cmd, static int loop_ioctl(FAR struct file *filep, int cmd,
unsigned long arg); unsigned long arg);
@@ -74,8 +72,7 @@ static const struct file_operations g_loop_fops =
* Name: loop_readv * Name: loop_readv
****************************************************************************/ ****************************************************************************/
static ssize_t loop_readv(FAR struct file *filep, static ssize_t loop_readv(FAR struct file *filep, FAR struct uio *uio)
FAR const struct uio *uio)
{ {
return 0; /* Return EOF */ return 0; /* Return EOF */
} }
@@ -84,10 +81,14 @@ static ssize_t loop_readv(FAR struct file *filep,
* Name: loop_writev * Name: loop_writev
****************************************************************************/ ****************************************************************************/
static ssize_t loop_writev(FAR struct file *filep, static ssize_t loop_writev(FAR struct file *filep, FAR struct uio *uio)
FAR const struct uio *uio)
{ {
return uio_total_len(uio); /* Say that everything was written */ /* Say that everything was written */
size_t ret = uio->uio_resid;
uio_advance(uio, ret);
return ret;
} }
/**************************************************************************** /****************************************************************************
+10 -9
View File
@@ -40,10 +40,8 @@
* Private Function Prototypes * Private Function Prototypes
****************************************************************************/ ****************************************************************************/
static ssize_t devnull_readv(FAR struct file *filep, static ssize_t devnull_readv(FAR struct file *filep, FAR struct uio *uio);
FAR const struct uio *uio); static ssize_t devnull_writev(FAR struct file *filep, FAR struct uio *uio);
static ssize_t devnull_writev(FAR struct file *filep,
FAR const struct uio *uio);
static int devnull_poll(FAR struct file *filep, FAR struct pollfd *fds, static int devnull_poll(FAR struct file *filep, FAR struct pollfd *fds,
bool setup); bool setup);
@@ -74,8 +72,7 @@ static const struct file_operations g_devnull_fops =
* Name: devnull_readv * Name: devnull_readv
****************************************************************************/ ****************************************************************************/
static ssize_t devnull_readv(FAR struct file *filep, static ssize_t devnull_readv(FAR struct file *filep, FAR struct uio *uio)
FAR const struct uio *uio)
{ {
UNUSED(filep); UNUSED(filep);
UNUSED(uio); UNUSED(uio);
@@ -87,12 +84,16 @@ static ssize_t devnull_readv(FAR struct file *filep,
* Name: devnull_writev * Name: devnull_writev
****************************************************************************/ ****************************************************************************/
static ssize_t devnull_writev(FAR struct file *filep, static ssize_t devnull_writev(FAR struct file *filep, FAR struct uio *uio)
FAR const struct uio *uio)
{ {
UNUSED(filep); UNUSED(filep);
return uio_total_len(uio); /* Say that everything was written */ /* Say that everything was written */
size_t ret = uio->uio_resid;
uio_advance(uio, ret);
return ret;
} }
/**************************************************************************** /****************************************************************************
+12 -15
View File
@@ -40,10 +40,8 @@
* Private Function Prototypes * Private Function Prototypes
****************************************************************************/ ****************************************************************************/
static ssize_t devzero_readv(FAR struct file *filep, static ssize_t devzero_readv(FAR struct file *filep, FAR struct uio *uio);
FAR const struct uio *uio); static ssize_t devzero_writev(FAR struct file *filep, FAR struct uio *uio);
static ssize_t devzero_writev(FAR struct file *filep,
FAR const struct uio *uio);
static int devzero_poll(FAR struct file *filep, FAR struct pollfd *fds, static int devzero_poll(FAR struct file *filep, FAR struct pollfd *fds,
bool setup); bool setup);
@@ -74,26 +72,22 @@ static const struct file_operations g_devzero_fops =
* Name: devzero_readv * Name: devzero_readv
****************************************************************************/ ****************************************************************************/
static ssize_t devzero_readv(FAR struct file *filep, static ssize_t devzero_readv(FAR struct file *filep, FAR struct uio *uio)
FAR const struct uio *uio)
{ {
ssize_t total = uio_total_len(uio); size_t total = uio->uio_resid;
FAR const struct iovec *iov = uio->uio_iov; FAR const struct iovec *iov = uio->uio_iov;
int iovcnt = uio->uio_iovcnt; int iovcnt = uio->uio_iovcnt;
int i; int i;
UNUSED(filep); UNUSED(filep);
if (total < 0)
{
return total;
}
for (i = 0; i < iovcnt; i++) for (i = 0; i < iovcnt; i++)
{ {
memset(iov[i].iov_base, 0, iov[i].iov_len); memset(iov[i].iov_base, 0, iov[i].iov_len);
} }
uio_advance(uio, total);
return total; return total;
} }
@@ -101,12 +95,15 @@ static ssize_t devzero_readv(FAR struct file *filep,
* Name: devzero_writev * Name: devzero_writev
****************************************************************************/ ****************************************************************************/
static ssize_t devzero_writev(FAR struct file *filep, static ssize_t devzero_writev(FAR struct file *filep, FAR struct uio *uio)
FAR const struct uio *uio)
{ {
size_t total;
UNUSED(filep); UNUSED(filep);
return uio_total_len(uio); total = uio->uio_resid;
uio_advance(uio, total);
return total;
} }
/**************************************************************************** /****************************************************************************
+20 -8
View File
@@ -50,8 +50,7 @@
* *
****************************************************************************/ ****************************************************************************/
static ssize_t file_readv_compat(FAR struct file *filep, static ssize_t file_readv_compat(FAR struct file *filep, FAR struct uio *uio)
FAR const struct uio *uio)
{ {
FAR const struct iovec *iov = uio->uio_iov; FAR const struct iovec *iov = uio->uio_iov;
int iovcnt = uio->uio_iovcnt; int iovcnt = uio->uio_iovcnt;
@@ -102,6 +101,11 @@ static ssize_t file_readv_compat(FAR struct file *filep,
remaining -= nread; remaining -= nread;
} }
if (ntotal >= 0)
{
uio_advance(uio, ntotal);
}
return 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; FAR struct inode *inode;
ssize_t ret = -EBADF; 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 iovec iov;
struct uio uio; struct uio uio;
ssize_t ret;
iov.iov_base = buf; iov.iov_base = buf;
iov.iov_len = nbytes; iov.iov_len = nbytes;
uio.uio_iov = &iov; ret = uio_init(&uio, &iov, 1);
uio.uio_iovcnt = 1; if (ret != 0)
{
return ret;
}
return file_readv(filep, &uio); 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. */ /* Then let file_readv do all of the work. */
uio.uio_iov = iov; ret = uio_init(&uio, iov, iovcnt);
uio.uio_iovcnt = iovcnt; if (ret == 0)
ret = file_readv(filep, &uio); {
ret = file_readv(filep, &uio);
}
fs_putfilep(filep); fs_putfilep(filep);
return ret; return ret;
} }
+167 -6
View File
@@ -35,19 +35,19 @@
#include <errno.h> #include <errno.h>
/**************************************************************************** /****************************************************************************
* Public Functions * Private Functions
****************************************************************************/ ****************************************************************************/
/**************************************************************************** /****************************************************************************
* Name: uio_total_len * Name: uio_calc_resid
* *
* Description: * Description:
* Return the total length of data in bytes. * Return the remaining length of data in bytes.
* Or -EOVERFLOW. * 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; const struct iovec *iov = uio->uio_iov;
int iovcnt = uio->uio_iovcnt; 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) if (SSIZE_MAX - len < iov[i].iov_len)
{ {
return -EOVERFLOW; return -EINVAL;
} }
len += iov[i].iov_len; len += iov[i].iov_len;
@@ -66,3 +66,164 @@ ssize_t uio_total_len(FAR const struct uio *uio)
return len; 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, 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; FAR const struct iovec *iov = uio->uio_iov;
int iovcnt = uio->uio_iovcnt; int iovcnt = uio->uio_iovcnt;
@@ -102,6 +102,11 @@ static ssize_t file_writev_compat(FAR struct file *filep,
remaining -= nwritten; remaining -= nwritten;
} }
if (ntotal >= 0)
{
uio_advance(uio, ntotal);
}
return 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; FAR struct inode *inode;
ssize_t ret = -EBADF; ssize_t ret = -EBADF;
@@ -202,11 +207,16 @@ ssize_t file_write(FAR struct file *filep, FAR const void *buf,
{ {
struct iovec iov; struct iovec iov;
struct uio uio; struct uio uio;
ssize_t ret;
iov.iov_base = (FAR void *)buf; iov.iov_base = (FAR void *)buf;
iov.iov_len = nbytes; iov.iov_len = nbytes;
uio.uio_iov = &iov; ret = uio_init(&uio, &iov, 1);
uio.uio_iovcnt = 1; if (ret != 0)
{
return ret;
}
return file_writev(filep, &uio); 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. * index. Note that file_writev() will return the errno on failure.
*/ */
uio.uio_iov = iov; ret = uio_init(&uio, iov, iovcnt);
uio.uio_iovcnt = iovcnt; if (ret == 0)
ret = file_writev(filep, &uio); {
ret = file_writev(filep, &uio);
}
fs_putfilep(filep); fs_putfilep(filep);
} }
+6 -6
View File
@@ -240,8 +240,8 @@ struct file_operations
CODE int (*poll)(FAR struct file *filep, FAR struct pollfd *fds, CODE int (*poll)(FAR struct file *filep, FAR struct pollfd *fds,
bool setup); bool setup);
CODE ssize_t (*readv)(FAR struct file *filep, FAR const struct uio *uio); CODE ssize_t (*readv)(FAR struct file *filep, FAR struct uio *uio);
CODE ssize_t (*writev)(FAR struct file *filep, FAR const struct uio *uio); CODE ssize_t (*writev)(FAR struct file *filep, FAR struct uio *uio);
/* The two structures need not be common after this point */ /* The two structures need not be common after this point */
@@ -311,8 +311,8 @@ struct mountpt_operations
CODE int (*truncate)(FAR struct file *filep, off_t length); CODE int (*truncate)(FAR struct file *filep, off_t length);
CODE int (*poll)(FAR struct file *filep, FAR struct pollfd *fds, CODE int (*poll)(FAR struct file *filep, FAR struct pollfd *fds,
bool setup); bool setup);
CODE ssize_t (*readv)(FAR struct file *filep, FAR const struct uio *uio); CODE ssize_t (*readv)(FAR struct file *filep, FAR struct uio *uio);
CODE ssize_t (*writev)(FAR struct file *filep, FAR const struct uio *uio); CODE ssize_t (*writev)(FAR struct file *filep, FAR struct uio *uio);
/* The two structures need not be common after this point. The following /* The two structures need not be common after this point. The following
* are extended methods needed to deal with the unique needs of mounted * are extended methods needed to deal with the unique needs of mounted
@@ -1419,7 +1419,7 @@ int close_mtddriver(FAR struct inode *pinode);
****************************************************************************/ ****************************************************************************/
ssize_t file_read(FAR struct file *filep, FAR void *buf, size_t nbytes); ssize_t file_read(FAR struct file *filep, FAR void *buf, size_t nbytes);
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);
/**************************************************************************** /****************************************************************************
* Name: nx_read * Name: nx_read
@@ -1473,7 +1473,7 @@ ssize_t nx_readv(int fd, FAR const struct iovec *iov, int iovcnt);
ssize_t file_write(FAR struct file *filep, FAR const void *buf, ssize_t file_write(FAR struct file *filep, FAR const void *buf,
size_t nbytes); size_t nbytes);
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);
/**************************************************************************** /****************************************************************************
* Name: nx_write * Name: nx_write
+48 -4
View File
@@ -45,6 +45,12 @@ struct uio
{ {
FAR const struct iovec *uio_iov; FAR const struct iovec *uio_iov;
int uio_iovcnt; int uio_iovcnt;
size_t uio_resid; /* the remaining bytes in the request */
size_t uio_offset_in_iov; /* offset in uio_iov[0].iov_base */
#if 0 /* notyet; planned for pread/pwrite */
off_t uio_offset; /* offset in the file */
#endif
}; };
/**************************************************************************** /****************************************************************************
@@ -52,14 +58,52 @@ struct uio
****************************************************************************/ ****************************************************************************/
/**************************************************************************** /****************************************************************************
* Name: uio_total_len * Name: uio_advance
* *
* Description: * Description:
* Return the total length of data in bytes. * Advance the pointer/offset in uio by the specified amount.
* Or -EOVERFLOW.
* *
****************************************************************************/ ****************************************************************************/
ssize_t uio_total_len(FAR const struct uio *uio); void uio_advance(FAR struct uio *uio, size_t sz);
/****************************************************************************
* Name: uio_init
*
* Description:
* Initialize the uio structure with reasonable default values.
*
* Return Value:
* 0 on success. A negative error number on an error.
*
* -EINVAL: The total size of the given iovec is too large.
* (Note: NetBSD's readv returns EINVAL in that case.
* I (yamamoto) couldn't find the specification in POSIX.)
*
****************************************************************************/
int uio_init(FAR struct uio *uio, FAR const struct iovec *iov, int iovcnt);
/****************************************************************************
* Name: uio_copyto
*
* Description:
* Copy data to the linear buffer from uio.
*
****************************************************************************/
void uio_copyfrom(FAR struct uio *uio, size_t offset, FAR const void *buf,
size_t len);
/****************************************************************************
* 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);
#endif /* __INCLUDE_NUTTX_FS_UIO_H */ #endif /* __INCLUDE_NUTTX_FS_UIO_H */