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

Reply via email to