This is an automated email from the ASF dual-hosted git repository.
xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new f19dbe6f6d3 net: support msg_iovlen > 1 in sendmsg/recvmsg using
universal iob helpers
f19dbe6f6d3 is described below
commit f19dbe6f6d31992491103c150215c8119d199e5d
Author: Sarthak Pandey <[email protected]>
AuthorDate: Fri Oct 16 14:48:16 2026 +0530
net: support msg_iovlen > 1 in sendmsg/recvmsg using universal iob helpers
- mm/iob: Introduce iob_copyin_iov, iob_trycopyin_iov, and iob_copyout_iov
to natively support scatter-gather iovec transfers in/out of IOB chains.
- Documentation: Document iob_copyin_iov, iob_trycopyin_iov, and
iob_copyout_iov in Documentation/os/memory/iob.rst.
- net/udp: Remove msg_iovlen != 1 check in psock_udp_recvfrom and
scatter data across multiple msg_iov buffers using iob_copyout_iov.
- net/can: Support multiple iovec elements in can_sendmsg and
can_sendmsg_buffered, validating against MTU and gathering into stack
buffer
or IOB chains.
- net/can: Remove msg_iovlen != 1 limitation in can_recvmsg and
scatter CAN frames into msg_iov buffers via iob_copyout_iov, preserving
MSG_CONFIRM check.
- net/local: Support multiple iovec buffers in local_recvmsg for stream
sockets, atomically receiving across iovecs in one call under lc_recvlock.
- net: Return standard -EMSGSIZE across all protocols when msg_iovlen == 0
per POSIX.1-2017 specification.
Fixes #15571
Assisted-by: Google Antigravity:gemini-3.8-flash-high
Signed-off-by: Sarthak Pandey <[email protected]>
---
Documentation/os/memory/iob.rst | 26 +++++++
include/nuttx/mm/iob.h | 42 +++++++++++
mm/iob/iob_copyin.c | 90 ++++++++++++++++++++++
mm/iob/iob_copyout.c | 41 ++++++++++
net/can/can_recvmsg.c | 49 +++++++-----
net/can/can_sendmsg.c | 56 ++++++++++++--
net/can/can_sendmsg_buffered.c | 40 +++++++---
net/inet/inet_sockif.c | 17 ++++-
net/local/local.h | 1 +
net/local/local_conn.c | 2 +
net/local/local_recvmsg.c | 163 +++++++++++++++++++++++++++++++++-------
net/udp/udp_recvfrom.c | 32 +++-----
12 files changed, 475 insertions(+), 84 deletions(-)
diff --git a/Documentation/os/memory/iob.rst b/Documentation/os/memory/iob.rst
index 54c7f266dba..e238ea157e8 100644
--- a/Documentation/os/memory/iob.rst
+++ b/Documentation/os/memory/iob.rst
@@ -167,8 +167,11 @@ Public Function Prototypes
- :c:func:`iob_free_queue_qentry()`
- :c:func:`iob_get_queue_size()`
- :c:func:`iob_copyin()`
+ - :c:func:`iob_copyin_iov()`
- :c:func:`iob_trycopyin()`
+ - :c:func:`iob_trycopyin_iov()`
- :c:func:`iob_copyout()`
+ - :c:func:`iob_copyout_iov()`
- :c:func:`iob_clone()`
- :c:func:`iob_clone_partial()`
- :c:func:`iob_concat()`
@@ -255,6 +258,14 @@ Public Function Prototypes
the I/O buffer chain, starting at ``offset``, extending the chain
as necessary.
+.. c:function:: int iob_copyin_iov(FAR struct iob_s *iob, \
+ FAR const struct iovec *iov, unsigned int iovlen, \
+ int offset, bool throttled);
+
+ Copy data from an ``iov`` array of user buffers into the I/O
+ buffer chain, starting at ``offset``, extending the chain as
+ necessary.
+
.. c:function:: int iob_trycopyin(FAR struct iob_s *iob, FAR const uint8_t
*src, \
unsigned int len, unsigned int offset, bool throttled);
@@ -262,6 +273,14 @@ Public Function Prototypes
the I/O buffer chain, starting at ``offset``, extending the chain
as necessary BUT without waiting if buffers are not available.
+.. c:function:: int iob_trycopyin_iov(FAR struct iob_s *iob, \
+ FAR const struct iovec *iov, unsigned int iovlen, \
+ int offset, bool throttled);
+
+ Copy data from an ``iov`` array of user buffers into the I/O
+ buffer chain, starting at ``offset``, extending the chain as
+ necessary BUT without waiting if buffers are not available.
+
.. c:function:: int iob_copyout(FAR uint8_t *dest, FAR const struct iob_s
*iob, \
unsigned int len, unsigned int offset);
@@ -269,6 +288,13 @@ Public Function Prototypes
buffer starting at ``offset`` in the I/O buffer, returning that
actual number of bytes copied out.
+.. c:function:: int iob_copyout_iov(FAR const struct iovec *iov, unsigned int
iovlen, \
+ FAR const struct iob_s *iob, unsigned int len, \
+ int offset);
+
+ Copy data ``len`` bytes of data from the I/O buffer chain
+ starting at ``offset`` into the user ``iov`` array of buffers.
+
.. c:function:: int iob_clone(FAR struct iob_s *iob1, FAR struct iob_s *iob2, \
bool throttled, bool block);
diff --git a/include/nuttx/mm/iob.h b/include/nuttx/mm/iob.h
index 125dd2627ea..4ae84f308a6 100644
--- a/include/nuttx/mm/iob.h
+++ b/include/nuttx/mm/iob.h
@@ -43,6 +43,8 @@
#ifdef CONFIG_MM_IOB
+struct iovec; /* Forward declaration for scatter-gather I/O */
+
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
@@ -543,6 +545,46 @@ int iob_trycopyin(FAR struct iob_s *iob, FAR const uint8_t
*src,
int iob_copyout(FAR uint8_t *dest, FAR const struct iob_s *iob,
unsigned int len, int offset);
+/****************************************************************************
+ * Name: iob_copyin_iov
+ *
+ * Description:
+ * Copy data from an 'iov' array of user buffers into the I/O buffer chain,
+ * starting at 'offset', extending the chain as necessary.
+ *
+ ****************************************************************************/
+
+int iob_copyin_iov(FAR struct iob_s *iob,
+ FAR const struct iovec *iov, unsigned int iovlen,
+ int offset, bool throttled);
+
+/****************************************************************************
+ * Name: iob_trycopyin_iov
+ *
+ * Description:
+ * Copy data from an 'iov' array of user buffers into the I/O buffer chain,
+ * starting at 'offset', extending the chain as necessary BUT without
+ * waiting if buffers are not available.
+ *
+ ****************************************************************************/
+
+int iob_trycopyin_iov(FAR struct iob_s *iob,
+ FAR const struct iovec *iov, unsigned int iovlen,
+ int offset, bool throttled);
+
+/****************************************************************************
+ * Name: iob_copyout_iov
+ *
+ * Description:
+ * Copy data 'len' bytes of data from the I/O buffer chain starting at
+ * 'offset' into the user 'iov' array of buffers.
+ *
+ ****************************************************************************/
+
+int iob_copyout_iov(FAR const struct iovec *iov, unsigned int iovlen,
+ FAR const struct iob_s *iob, unsigned int len,
+ int offset);
+
/****************************************************************************
* Name: iob_tailroom
*
diff --git a/mm/iob/iob_copyin.c b/mm/iob/iob_copyin.c
index 61a38219ace..b7e060ba786 100644
--- a/mm/iob/iob_copyin.c
+++ b/mm/iob/iob_copyin.c
@@ -27,9 +27,11 @@
#include <nuttx/config.h>
#include <stdint.h>
+#include <limits.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
+#include <sys/uio.h>
#include <nuttx/debug.h>
#include <nuttx/mm/iob.h>
@@ -211,6 +213,61 @@ static int iob_copyin_internal(FAR struct iob_s *iob, FAR
const uint8_t *src,
return total;
}
+/****************************************************************************
+ * Name: iob_copyin_iov_internal
+ *
+ * Description:
+ * Iterate through an array of 'iov' buffers and copy each into the I/O
+ * buffer chain, updating the offset for each segment.
+ *
+ ****************************************************************************/
+
+static int iob_copyin_iov_internal(FAR struct iob_s *iob,
+ FAR const struct iovec *iov,
+ unsigned int iovlen, int offset,
+ bool throttled, bool block)
+{
+ int total = 0;
+ int ret;
+ unsigned int i;
+
+ if (iob == NULL || iov == NULL || iovlen == 0)
+ {
+ return 0;
+ }
+
+ for (i = 0; i < iovlen; i++)
+ {
+ if (iov[i].iov_len == 0)
+ {
+ continue;
+ }
+
+ if (iov[i].iov_base == NULL)
+ {
+ return -EINVAL;
+ }
+
+ if (iov[i].iov_len > INT_MAX - total ||
+ (offset >= 0 && iov[i].iov_len > INT_MAX - offset))
+ {
+ return -EINVAL;
+ }
+
+ ret = iob_copyin_internal(iob, (FAR const uint8_t *)iov[i].iov_base,
+ iov[i].iov_len, offset, throttled, block);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ offset += iov[i].iov_len;
+ total += iov[i].iov_len;
+ }
+
+ return total;
+}
+
/****************************************************************************
* Public Functions
****************************************************************************/
@@ -245,3 +302,36 @@ int iob_trycopyin(FAR struct iob_s *iob, FAR const uint8_t
*src,
{
return iob_copyin_internal(iob, src, len, offset, throttled, false);
}
+
+/****************************************************************************
+ * Name: iob_copyin_iov
+ *
+ * Description:
+ * Copy data from an 'iov' array of user buffers into the I/O buffer chain,
+ * starting at 'offset', extending the chain as necessary.
+ *
+ ****************************************************************************/
+
+int iob_copyin_iov(FAR struct iob_s *iob,
+ FAR const struct iovec *iov, unsigned int iovlen,
+ int offset, bool throttled)
+{
+ return iob_copyin_iov_internal(iob, iov, iovlen, offset, throttled, true);
+}
+
+/****************************************************************************
+ * Name: iob_trycopyin_iov
+ *
+ * Description:
+ * Copy data from an 'iov' array of user buffers into the I/O buffer chain,
+ * starting at 'offset', extending the chain as necessary BUT without
+ * waiting if buffers are not available.
+ *
+ ****************************************************************************/
+
+int iob_trycopyin_iov(FAR struct iob_s *iob,
+ FAR const struct iovec *iov, unsigned int iovlen,
+ int offset, bool throttled)
+{
+ return iob_copyin_iov_internal(iob, iov, iovlen, offset, throttled, false);
+}
diff --git a/mm/iob/iob_copyout.c b/mm/iob/iob_copyout.c
index 91a41d3a025..68808e3d7a5 100644
--- a/mm/iob/iob_copyout.c
+++ b/mm/iob/iob_copyout.c
@@ -28,6 +28,7 @@
#include <stdint.h>
#include <sys/param.h>
+#include <sys/uio.h>
#include <string.h>
#include <assert.h>
@@ -111,3 +112,43 @@ int iob_copyout(FAR uint8_t *dest, FAR const struct iob_s
*iob,
return len - remaining;
}
+
+/****************************************************************************
+ * Name: iob_copyout_iov
+ *
+ * Description:
+ * Copy data 'len' bytes of data from the I/O buffer chain starting at
+ * 'offset' into the user 'iov' array of buffers.
+ *
+ ****************************************************************************/
+
+int iob_copyout_iov(FAR const struct iovec *iov, unsigned int iovlen,
+ FAR const struct iob_s *iob, unsigned int len,
+ int offset)
+{
+ int total = 0;
+ int copied;
+ unsigned int i;
+
+ if (iov == NULL || iovlen == 0 || iob == NULL || len == 0)
+ {
+ return 0;
+ }
+
+ for (i = 0; i < iovlen && total < len; i++)
+ {
+ size_t chunk = MIN(iov[i].iov_len, len - total);
+
+ if (chunk > 0 && iov[i].iov_base != NULL)
+ {
+ copied = iob_copyout(iov[i].iov_base, iob, chunk, offset);
+ if (copied > 0)
+ {
+ offset += copied;
+ total += copied;
+ }
+ }
+ }
+
+ return total;
+}
diff --git a/net/can/can_recvmsg.c b/net/can/can_recvmsg.c
index f7b1f626c4e..8e1aca93bc1 100644
--- a/net/can/can_recvmsg.c
+++ b/net/can/can_recvmsg.c
@@ -156,24 +156,23 @@ static size_t can_recvfrom_newdata(FAR struct
net_driver_s *dev,
unsigned int offset;
ssize_t recvlen;
-#ifdef CONFIG_NET_TIMESTAMP
- cmsg_store_timestamp(pstate->pr_msg, &dev->d_iob->io_time,
- pstate->pr_conn->sconn.s_options);
-#endif
-
if (dev->d_iob->io_conn == &pstate->pr_conn->sconn)
{
pstate->pr_msg->msg_flags |= MSG_CONFIRM;
}
- recvlen = MIN(pstate->pr_msg->msg_iov->iov_len, dev->d_len);
+#ifdef CONFIG_NET_TIMESTAMP
+ cmsg_store_timestamp(pstate->pr_msg, &dev->d_iob->io_time,
+ pstate->pr_conn->sconn.s_options);
+#endif
/* Copy the new packet data into the user buffer */
- offset = (dev->d_appdata - dev->d_iob->io_data) - dev->d_iob->io_offset;
-
- recvlen = iob_copyout(pstate->pr_msg->msg_iov->iov_base,
- dev->d_iob, recvlen, offset);
+ offset = (dev->d_appdata - dev->d_iob->io_data) -
+ dev->d_iob->io_offset;
+ recvlen = iob_copyout_iov(pstate->pr_msg->msg_iov,
+ pstate->pr_msg->msg_iovlen,
+ dev->d_iob, dev->d_len, offset);
if (recvlen <= 0)
{
return 0;
@@ -256,7 +255,9 @@ static inline int can_readahead(struct can_recvfrom_s
*pstate)
{
FAR struct can_conn_s *conn = pstate->pr_conn;
FAR struct iob_s *iob;
- int recvlen;
+ size_t total_recv = 0;
+ size_t i;
+ bool has_buf = false;
/* Check there is any CAN data already buffered in a read-ahead
* buffer.
@@ -264,7 +265,16 @@ static inline int can_readahead(struct can_recvfrom_s
*pstate)
pstate->pr_recvlen = -ENODATA;
- if (pstate->pr_msg->msg_iov->iov_len > 0 &&
+ for (i = 0; i < pstate->pr_msg->msg_iovlen; i++)
+ {
+ if (pstate->pr_msg->msg_iov[i].iov_len > 0)
+ {
+ has_buf = true;
+ break;
+ }
+ }
+
+ if (has_buf &&
(iob = iob_remove_queue(&conn->readahead)) != NULL)
{
DEBUGASSERT(iob->io_pktlen > 0);
@@ -283,16 +293,17 @@ static inline int can_readahead(struct can_recvfrom_s
*pstate)
* the user buffer.
*/
- recvlen = iob_copyout(pstate->pr_msg->msg_iov->iov_base,
- iob, pstate->pr_msg->msg_iov->iov_len, 0);
+ total_recv = iob_copyout_iov(pstate->pr_msg->msg_iov,
+ pstate->pr_msg->msg_iovlen,
+ iob, iob->io_pktlen, 0);
/* Update the accumulated size of the data read */
- pstate->pr_recvlen = recvlen;
+ pstate->pr_recvlen = total_recv;
/* The rest of a frame larger than the user buffer is discarded */
- if (iob->io_pktlen > pstate->pr_msg->msg_iov->iov_len)
+ if (iob->io_pktlen > total_recv)
{
pstate->pr_msg->msg_flags |= MSG_TRUNC;
}
@@ -308,7 +319,7 @@ static inline int can_readahead(struct can_recvfrom_s
*pstate)
iob_free_chain(iob);
- return recvlen;
+ return total_recv;
}
return 0;
@@ -434,9 +445,9 @@ ssize_t can_recvmsg(FAR struct socket *psock, FAR struct
msghdr *msg,
return -ENOSYS;
}
- if (msg->msg_iovlen != 1)
+ if (msg->msg_iovlen == 0)
{
- return -ENOTSUP;
+ return -EMSGSIZE;
}
conn_lock(&conn->sconn);
diff --git a/net/can/can_sendmsg.c b/net/can/can_sendmsg.c
index c40b8eaebe7..58008fefcf0 100644
--- a/net/can/can_sendmsg.c
+++ b/net/can/can_sendmsg.c
@@ -111,9 +111,10 @@ static uint32_t psock_send_eventhandler(FAR struct
net_driver_s *dev,
{
/* Copy the packet data into the device packet buffer and send it */
- int ret = devif_send(dev, pstate->snd_buffer,
- pstate->snd_buflen + pstate->pr_msglen, 0);
+ int ret;
+ ret = devif_send(dev, pstate->snd_buffer,
+ pstate->snd_buflen + pstate->pr_msglen, 0);
dev->d_len = dev->d_sndlen - pstate->pr_msglen;
if (ret <= 0)
{
@@ -176,7 +177,14 @@ ssize_t can_sendmsg(FAR struct socket *psock, FAR const
struct msghdr *msg,
FAR struct net_driver_s *dev;
FAR struct can_conn_s *conn;
struct send_s state;
+ size_t total_len = 0;
+ size_t i;
int ret = OK;
+#if defined(CONFIG_NET_CANPROTO_OPTIONS) && defined(CONFIG_NET_CAN_CANFD)
+ uint8_t buf[CANFD_MTU];
+#else
+ uint8_t buf[CAN_MTU];
+#endif
/* Verify that the sockfd corresponds to valid, allocated socket */
@@ -206,11 +214,30 @@ ssize_t can_sendmsg(FAR struct socket *psock, FAR const
struct msghdr *msg,
return -ENODEV;
}
+ if (msg->msg_iovlen == 0)
+ {
+ return -EMSGSIZE;
+ }
+
+ for (i = 0; i < msg->msg_iovlen; i++)
+ {
+ if (msg->msg_iov[i].iov_len > sizeof(buf) - total_len)
+ {
+ return -EINVAL;
+ }
+
+ if (msg->msg_iov[i].iov_len > 0 && msg->msg_iov[i].iov_base == NULL)
+ {
+ return -EINVAL;
+ }
+
+ total_len += msg->msg_iov[i].iov_len;
+ }
+
#if defined(CONFIG_NET_CANPROTO_OPTIONS) && defined(CONFIG_NET_CAN_CANFD)
if (_SO_GETOPT(conn->sconn.s_options, CAN_RAW_FD_FRAMES))
{
- if (msg->msg_iov->iov_len != CANFD_MTU &&
- msg->msg_iov->iov_len != CAN_MTU)
+ if (total_len != CANFD_MTU && total_len != CAN_MTU)
{
return -EINVAL;
}
@@ -218,12 +245,27 @@ ssize_t can_sendmsg(FAR struct socket *psock, FAR const
struct msghdr *msg,
else
#endif
{
- if (msg->msg_iov->iov_len != CAN_MTU)
+ if (total_len != CAN_MTU)
{
return -EINVAL;
}
}
+ if (msg->msg_iovlen > 1)
+ {
+ size_t offset = 0;
+
+ for (i = 0; i < msg->msg_iovlen; i++)
+ {
+ if (msg->msg_iov[i].iov_len > 0)
+ {
+ memcpy(buf + offset, msg->msg_iov[i].iov_base,
+ msg->msg_iov[i].iov_len);
+ offset += msg->msg_iov[i].iov_len;
+ }
+ }
+ }
+
/* Perform the send operation */
/* Initialize the state structure. This is done with the network locked
@@ -234,8 +276,8 @@ ssize_t can_sendmsg(FAR struct socket *psock, FAR const
struct msghdr *msg,
memset(&state, 0, sizeof(struct send_s));
nxsem_init(&state.snd_sem, 0, 0); /* Doesn't really fail */
- state.snd_buflen = msg->msg_iov->iov_len; /* bytes to send */
- state.snd_buffer = msg->msg_iov->iov_base; /* Buffer to send from */
+ state.snd_buflen = total_len;
+ state.snd_buffer = (msg->msg_iovlen == 1) ? msg->msg_iov[0].iov_base : buf;
state.snd_conn = conn;
#ifdef CONFIG_NET_CAN_RAW_TX_DEADLINE
diff --git a/net/can/can_sendmsg_buffered.c b/net/can/can_sendmsg_buffered.c
index 9b2a6138d4d..bc631aedb06 100644
--- a/net/can/can_sendmsg_buffered.c
+++ b/net/can/can_sendmsg_buffered.c
@@ -189,6 +189,8 @@ ssize_t can_sendmsg(FAR struct socket *psock, FAR const
struct msghdr *msg,
FAR struct net_driver_s *dev;
FAR struct can_conn_s *conn;
FAR struct iob_s *wb_iob;
+ size_t total_len = 0;
+ size_t i;
bool empty;
bool nonblock;
int ret = OK;
@@ -214,11 +216,25 @@ ssize_t can_sendmsg(FAR struct socket *psock, FAR const
struct msghdr *msg,
return -ENODEV;
}
+ if (msg->msg_iovlen == 0)
+ {
+ return -EMSGSIZE;
+ }
+
+ for (i = 0; i < msg->msg_iovlen; i++)
+ {
+ if (msg->msg_iov[i].iov_len > CANFD_MTU - total_len)
+ {
+ return -EINVAL;
+ }
+
+ total_len += msg->msg_iov[i].iov_len;
+ }
+
#if defined(CONFIG_NET_CANPROTO_OPTIONS) && defined(CONFIG_NET_CAN_CANFD)
if (_SO_GETOPT(conn->sconn.s_options, CAN_RAW_FD_FRAMES))
{
- if (msg->msg_iov->iov_len != CANFD_MTU &&
- msg->msg_iov->iov_len != CAN_MTU)
+ if (total_len != CANFD_MTU && total_len != CAN_MTU)
{
return -EINVAL;
}
@@ -226,7 +242,7 @@ ssize_t can_sendmsg(FAR struct socket *psock, FAR const
struct msghdr *msg,
else
#endif
{
- if (msg->msg_iov->iov_len != CAN_MTU)
+ if (total_len != CAN_MTU)
{
return -EINVAL;
}
@@ -250,7 +266,7 @@ ssize_t can_sendmsg(FAR struct socket *psock, FAR const
struct msghdr *msg,
*/
while (!IOB_QEMPTY(&conn->write_q) &&
- (iob_get_queue_size(&conn->write_q) + msg->msg_iov->iov_len) >
+ (iob_get_queue_size(&conn->write_q) + total_len) >
conn->sndbufs)
{
/* send buffer size exceeds the send limit */
@@ -310,8 +326,8 @@ ssize_t can_sendmsg(FAR struct socket *psock, FAR const
struct msghdr *msg,
if (nonblock)
{
- ret = iob_trycopyin(wb_iob, (FAR uint8_t *)msg->msg_iov->iov_base,
- msg->msg_iov->iov_len, 0, false);
+ ret = iob_trycopyin_iov(wb_iob, msg->msg_iov, msg->msg_iovlen, 0,
+ false);
}
else
{
@@ -321,14 +337,18 @@ ssize_t can_sendmsg(FAR struct socket *psock, FAR const
struct msghdr *msg,
*/
conn_dev_unlock(&conn->sconn, dev);
- ret = iob_copyin(wb_iob, (FAR uint8_t *)msg->msg_iov->iov_base,
- msg->msg_iov->iov_len, 0, false);
+ ret = iob_copyin_iov(wb_iob, msg->msg_iov, msg->msg_iovlen, 0, false);
conn_dev_lock(&conn->sconn, dev);
}
- if (ret < 0)
+ if (ret < 0 || ret != total_len)
{
nerr("ERROR: Failed to copy data into write buffer\n");
+ if (ret >= 0)
+ {
+ ret = -ENOMEM;
+ }
+
goto errout_with_wrb;
}
@@ -392,7 +412,7 @@ ssize_t can_sendmsg(FAR struct socket *psock, FAR const
struct msghdr *msg,
conn_dev_unlock(&conn->sconn, dev);
- return msg->msg_iov->iov_len;
+ return total_len;
errout_with_wrb:
iob_free_chain(wb_iob);
diff --git a/net/inet/inet_sockif.c b/net/inet/inet_sockif.c
index 1099a2246c6..abd0934a26c 100644
--- a/net/inet/inet_sockif.c
+++ b/net/inet/inet_sockif.c
@@ -1840,16 +1840,24 @@ static ssize_t inet_sendto(FAR struct socket *psock,
FAR const void *buf,
static ssize_t inet_sendmsg(FAR struct socket *psock,
FAR const struct msghdr *msg, int flags)
{
- FAR void *buf = msg->msg_iov->iov_base;
- size_t len = msg->msg_iov->iov_len;
FAR const struct sockaddr *to = msg->msg_name;
socklen_t tolen = msg->msg_namelen;
FAR const struct iovec *iov;
FAR const struct iovec *end;
+ FAR void *buf;
+ size_t len;
int ret;
+ if (msg->msg_iovlen == 0)
+ {
+ return -EMSGSIZE;
+ }
+
if (msg->msg_iovlen == 1)
{
+ buf = msg->msg_iov->iov_base;
+ len = msg->msg_iov->iov_len;
+
return to ? inet_sendto(psock, buf, len, flags, to, tolen) :
inet_send(psock, buf, len, flags);
}
@@ -2176,6 +2184,11 @@ static ssize_t inet_recvmsg(FAR struct socket *psock,
{
ssize_t ret;
+ if (msg->msg_iovlen == 0)
+ {
+ return -EMSGSIZE;
+ }
+
/* If a 'from' address has been provided, verify that it is large
* enough to hold this address family.
*/
diff --git a/net/local/local.h b/net/local/local.h
index 38cc04e2e99..fb273069c08 100644
--- a/net/local/local.h
+++ b/net/local/local.h
@@ -141,6 +141,7 @@ struct local_conn_s
#endif /* CONFIG_NET_LOCAL_SCM */
mutex_t lc_sendlock; /* Make sending multi-thread safe */
+ mutex_t lc_recvlock; /* Make receiving multi-thread safe */
mutex_t lc_polllock; /* Lock for net poll */
#ifdef CONFIG_NET_LOCAL_STREAM
diff --git a/net/local/local_conn.c b/net/local/local_conn.c
index df9674f31f7..05eacc34eb1 100644
--- a/net/local/local_conn.c
+++ b/net/local/local_conn.c
@@ -175,6 +175,7 @@ FAR struct local_conn_s *local_alloc(void)
*/
nxmutex_init(&conn->lc_sendlock);
+ nxmutex_init(&conn->lc_recvlock);
nxmutex_init(&conn->lc_polllock);
nxrmutex_init(&conn->lc_conn.s_lock);
@@ -354,6 +355,7 @@ void local_free(FAR struct local_conn_s *conn)
/* Destroy sem associated with the connection */
nxmutex_destroy(&conn->lc_sendlock);
+ nxmutex_destroy(&conn->lc_recvlock);
nxmutex_destroy(&conn->lc_polllock);
nxrmutex_destroy(&conn->lc_conn.s_lock);
diff --git a/net/local/local_recvmsg.c b/net/local/local_recvmsg.c
index ce8abadd86c..6ef5cba6bbc 100644
--- a/net/local/local_recvmsg.c
+++ b/net/local/local_recvmsg.c
@@ -209,11 +209,8 @@ out:
*
* Input Parameters:
* psock A pointer to a NuttX-specific, internal socket structure
- * buf Buffer to receive data
- * len Length of buffer
+ * msg Buffer to receive the message
* flags Receive flags
- * from Address of source (may be NULL)
- * fromlen The length of the address structure
*
* Returned Value:
* On success, returns the number of characters received. If no data is
@@ -225,27 +222,54 @@ out:
#ifdef CONFIG_NET_LOCAL_STREAM
static inline ssize_t
-psock_stream_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len,
- int flags, FAR struct sockaddr *from,
- FAR socklen_t *fromlen)
+psock_stream_recvfrom(FAR struct socket *psock, FAR struct msghdr *msg,
+ int flags)
{
FAR struct local_conn_s *conn = psock->s_conn;
- size_t readlen = len;
- int ret;
+ FAR struct sockaddr *from = msg->msg_name;
+ FAR socklen_t *fromlen = &msg->msg_namelen;
+ size_t peek_offset = 0;
+ ssize_t total_recv = 0;
+ size_t iov_recv;
+ size_t readlen;
+ ssize_t ret = 0;
+ int i;
+
+ /* Make sure that receive is multi-thread safe */
+
+ if ((flags & MSG_DONTWAIT) != 0 ||
+ _SS_ISNONBLOCK(conn->lc_conn.s_flags))
+ {
+ ret = nxmutex_trylock(&conn->lc_recvlock);
+ if (ret < 0)
+ {
+ return -EAGAIN;
+ }
+ }
+ else
+ {
+ ret = nxmutex_lock(&conn->lc_recvlock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ }
/* Verify that this is a connected peer socket */
if (conn->lc_state != LOCAL_STATE_CONNECTED)
{
nerr("ERROR: not connected\n");
- return -ENOTCONN;
+ ret = -ENOTCONN;
+ goto errout_with_lock;
}
/* Check shutdown state */
if (conn->lc_infile.f_inode == NULL)
{
- return 0;
+ ret = 0;
+ goto errout_with_lock;
}
/* If it is non-blocking mode, the data in fifo is 0 and
@@ -255,38 +279,118 @@ psock_stream_recvfrom(FAR struct socket *psock, FAR void
*buf, size_t len,
if (flags & MSG_DONTWAIT)
{
int data_len = 0;
+
ret = file_ioctl(&conn->lc_infile, FIONREAD, &data_len);
if (ret < 0)
{
- return ret;
+ goto errout_with_lock;
}
if (data_len == 0)
{
- return -EAGAIN;
+ ret = -EAGAIN;
+ goto errout_with_lock;
}
}
- /* Read the packet */
+ /* Read into user iovec buffers */
- ret = psock_fifo_read(psock, buf, 0, &readlen, flags, true);
- if (ret < 0)
+ for (i = 0; i < msg->msg_iovlen; i++)
{
- return ret;
+ if (msg->msg_iov[i].iov_len == 0 ||
+ msg->msg_iov[i].iov_base == NULL)
+ {
+ continue;
+ }
+
+ /* If data has already been received and MSG_WAITALL is not
+ * set, check if more data is available in the FIFO. If not,
+ * do not block waiting for more data in subsequent iovs.
+ */
+
+ if (total_recv > 0 && !(flags & MSG_WAITALL))
+ {
+ int data_len = 0;
+ int ioret;
+
+ ioret = file_ioctl(&conn->lc_infile, FIONREAD, &data_len);
+ if (ioret < 0 || data_len == 0)
+ {
+ break;
+ }
+ }
+
+ iov_recv = 0;
+ while (iov_recv < msg->msg_iov[i].iov_len)
+ {
+ readlen = msg->msg_iov[i].iov_len - iov_recv;
+ ret = psock_fifo_read(psock,
+ (FAR uint8_t *)msg->msg_iov[i].iov_base +
+ iov_recv,
+ peek_offset, &readlen, flags, true);
+ if (ret < 0)
+ {
+ break;
+ }
+
+ if (readlen == 0)
+ {
+ /* EOF */
+
+ break;
+ }
+
+ iov_recv += readlen;
+ total_recv += readlen;
+ if (flags & MSG_PEEK)
+ {
+ peek_offset += readlen;
+ }
+
+ /* If MSG_WAITALL is not set and we read less than requested,
+ * return what we have so far without blocking further.
+ */
+
+ if (!(flags & MSG_WAITALL))
+ {
+ break;
+ }
+ }
+
+ /* If an error occurred or EOF was reached, or if partial data was
+ * received without MSG_WAITALL, stop processing subsequent iovecs.
+ */
+
+ if (ret < 0 || readlen == 0 ||
+ (!(flags & MSG_WAITALL) && iov_recv < msg->msg_iov[i].iov_len))
+ {
+ break;
+ }
+ }
+
+ /* If no data was received and an error occurred, return the error
+ * without overwriting it with local_getaddr().
+ */
+
+ if (total_recv == 0 && ret < 0)
+ {
+ goto errout_with_lock;
}
/* Return the address family */
- if (from)
+ if (from != NULL)
{
ret = local_getaddr(conn, from, fromlen);
if (ret < 0)
{
- return ret;
+ goto errout_with_lock;
}
}
- return readlen;
+errout_with_lock:
+ nxmutex_unlock(&conn->lc_recvlock);
+ return total_recv > 0 ? total_recv : ret;
}
#endif /* CONFIG_NET_LOCAL_STREAM */
@@ -540,22 +644,23 @@ ssize_t local_recvmsg(FAR struct socket *psock, FAR
struct msghdr *msg,
{
FAR socklen_t *fromlen = &msg->msg_namelen;
FAR struct sockaddr *from = msg->msg_name;
- FAR void *buf = msg->msg_iov->iov_base;
- size_t len = msg->msg_iov->iov_len;
+ FAR void *buf;
+ ssize_t len;
- if (msg->msg_iovlen != 1)
+ if (msg->msg_iovlen == 0)
{
- return -ENOTSUP;
+ return -EMSGSIZE;
}
- DEBUGASSERT(buf);
+ buf = msg->msg_iov->iov_base;
+ len = msg->msg_iov->iov_len;
/* Check for a stream socket */
#ifdef CONFIG_NET_LOCAL_STREAM
if (psock->s_type == SOCK_STREAM)
{
- len = psock_stream_recvfrom(psock, buf, len, flags, from, fromlen);
+ len = psock_stream_recvfrom(psock, msg, flags);
}
else
#endif
@@ -563,6 +668,12 @@ ssize_t local_recvmsg(FAR struct socket *psock, FAR struct
msghdr *msg,
#ifdef CONFIG_NET_LOCAL_DGRAM
if (psock->s_type == SOCK_DGRAM)
{
+ if (msg->msg_iovlen != 1)
+ {
+ return -ENOTSUP;
+ }
+
+ DEBUGASSERT(buf);
len = psock_dgram_recvfrom(psock, buf, len, flags, from, fromlen);
}
else
diff --git a/net/udp/udp_recvfrom.c b/net/udp/udp_recvfrom.c
index aa29d4cc33f..17ed2a4035a 100644
--- a/net/udp/udp_recvfrom.c
+++ b/net/udp/udp_recvfrom.c
@@ -130,23 +130,15 @@ static inline size_t udp_recvfrom_newdata(FAR struct
net_driver_s *dev,
FAR struct udp_recvfrom_s *pstate)
{
size_t recvlen;
-
- /* Get the length of the data to return */
-
- if (dev->d_len > pstate->ir_msg->msg_iov->iov_len)
- {
- recvlen = pstate->ir_msg->msg_iov->iov_len;
- }
- else
- {
- recvlen = dev->d_len;
- }
+ int offset;
/* Copy the new appdata into the user buffer */
- recvlen = iob_copyout(pstate->ir_msg->msg_iov->iov_base, dev->d_iob,
- recvlen, dev->d_appdata - dev->d_iob->io_data -
- dev->d_iob->io_offset);
+ offset = dev->d_appdata - dev->d_iob->io_data - dev->d_iob->io_offset;
+
+ recvlen = iob_copyout_iov(pstate->ir_msg->msg_iov,
+ pstate->ir_msg->msg_iovlen,
+ dev->d_iob, dev->d_len, offset);
/* Update the size of the data read */
@@ -211,11 +203,11 @@ static inline void udp_readahead(struct udp_recvfrom_s
*pstate)
conn->sconn.s_options);
#endif
- /* Copy to user */
+ /* Copy to user buffer(s) */
- recvlen = iob_copyout(pstate->ir_msg->msg_iov->iov_base, iob,
- MIN(pstate->ir_msg->msg_iov->iov_len, datalen),
- offset);
+ recvlen = iob_copyout_iov(pstate->ir_msg->msg_iov,
+ pstate->ir_msg->msg_iovlen,
+ iob, datalen, offset);
/* Update the accumulated size of the data read */
@@ -659,9 +651,9 @@ ssize_t psock_udp_recvfrom(FAR struct socket *psock, FAR
struct msghdr *msg,
/* Perform the UDP recvfrom() operation */
- if (msg->msg_iovlen != 1)
+ if (msg->msg_iovlen == 0)
{
- return -ENOTSUP;
+ return -EMSGSIZE;
}
/* Initialize the state structure. This is done with the network locked