You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2023/07/18 02:51:51 UTC

[nuttx] branch master updated (b79671a336 -> 075eb6a6d2)

This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


    from b79671a336 risc-v/mpfs: emmcsd: fix csd read
     new 4b7604cf81 net: Rename tcp_dataconcat to net_iob_concat
     new 075eb6a6d2 net/udp: Change conn->readahead to I/O buffer chain

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 net/procfs/net_udp.c                               |  2 +-
 net/tcp/Kconfig                                    | 14 ----
 net/tcp/tcp.h                                      | 20 +-----
 net/tcp/tcp_callback.c                             | 44 ++----------
 net/tcp/tcp_input.c                                |  8 +--
 net/udp/udp.h                                      |  5 +-
 net/udp/udp_callback.c                             | 81 +++++++++++++++-------
 net/udp/udp_conn.c                                 |  4 +-
 net/udp/udp_ioctl.c                                |  6 +-
 net/udp/udp_netpoll.c                              |  2 +-
 net/udp/udp_notifier.c                             |  2 +-
 net/udp/udp_recvfrom.c                             | 62 ++++++++++++-----
 net/utils/CMakeLists.txt                           |  3 +-
 net/utils/Kconfig                                  | 14 ++++
 net/utils/Make.defs                                |  2 +-
 .../src/stm32_can.c => net/utils/net_iob_concat.c  | 59 ++++++++--------
 net/utils/utils.h                                  | 17 +++++
 17 files changed, 180 insertions(+), 165 deletions(-)
 copy boards/arm/stm32/nucleo-f303re/src/stm32_can.c => net/utils/net_iob_concat.c (69%)


[nuttx] 01/02: net: Rename tcp_dataconcat to net_iob_concat

Posted by xi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 4b7604cf8189520c559522898d0263ca77d19db6
Author: Zhe Weng <we...@xiaomi.com>
AuthorDate: Tue Jul 4 14:24:16 2023 +0800

    net: Rename tcp_dataconcat to net_iob_concat
    
    Allow other protocols like UDP to use concat logic.
    
    Signed-off-by: Zhe Weng <we...@xiaomi.com>
---
 net/tcp/Kconfig            | 14 ---------
 net/tcp/tcp.h              | 15 ----------
 net/tcp/tcp_callback.c     | 44 +++------------------------
 net/tcp/tcp_input.c        |  8 ++---
 net/utils/CMakeLists.txt   |  3 +-
 net/utils/Kconfig          | 14 +++++++++
 net/utils/Make.defs        |  2 +-
 net/utils/net_iob_concat.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++
 net/utils/utils.h          | 17 +++++++++++
 9 files changed, 116 insertions(+), 75 deletions(-)

diff --git a/net/tcp/Kconfig b/net/tcp/Kconfig
index d287271911..290cbb41bd 100644
--- a/net/tcp/Kconfig
+++ b/net/tcp/Kconfig
@@ -266,20 +266,6 @@ config NET_TCP_WRBUFFER_DUMP
 
 endif # NET_TCP_WRITE_BUFFERS
 
-config NET_TCP_RECV_PACK
-	bool "Enable TCP/IP receive data in a continuous poll"
-	default y
-	---help---
-		This option will enable TCP/IP receive data into a continuous iob chain.
-		Fragmentation of network data will intensify iob consumption, if
-		the device receives a message storm of fragmented packets, the iob
-		cache will not be effectively used, this is not allowed on iot devices
-		since the resources of such devices are limited. Of course, this
-		also takes some disadvantages: data needs to be copied.
-		This option will brings some balance on resource-constrained devices,
-		enable this config to reduce the consumption of iob, the received iob
-		buffers will be merged into the contiguous iob chain.
-
 config NET_TCPBACKLOG
 	bool "TCP/IP backlog support"
 	default n
diff --git a/net/tcp/tcp.h b/net/tcp/tcp.h
index adfb8313b5..301d5ea722 100644
--- a/net/tcp/tcp.h
+++ b/net/tcp/tcp.h
@@ -1373,21 +1373,6 @@ uint16_t tcp_datahandler(FAR struct net_driver_s *dev,
                          FAR struct tcp_conn_s *conn,
                          uint16_t offset);
 
-/****************************************************************************
- * Name: tcp_dataconcat
- *
- * Description:
- *   Concatenate iob_s chain iob2 to iob1, if CONFIG_NET_TCP_RECV_PACK is
- *   endabled, pack all data in the I/O buffer chain.
- *
- * Returned Value:
- *   The number of bytes actually buffered is returned.  This will be either
- *   zero or equal to iob1->io_pktlen.
- *
- ****************************************************************************/
-
-uint16_t tcp_dataconcat(FAR struct iob_s **iob1, FAR struct iob_s **iob2);
-
 /****************************************************************************
  * Name: tcp_backlogcreate
  *
diff --git a/net/tcp/tcp_callback.c b/net/tcp/tcp_callback.c
index d825ba07eb..89077bae8e 100644
--- a/net/tcp/tcp_callback.c
+++ b/net/tcp/tcp_callback.c
@@ -36,6 +36,7 @@
 
 #include "devif/devif.h"
 #include "tcp/tcp.h"
+#include "utils/utils.h"
 
 #ifdef NET_TCP_HAVE_STACK
 
@@ -143,7 +144,7 @@ static uint16_t tcp_ofoseg_data_event(FAR struct net_driver_s *dev,
           rcvseq = TCP_SEQ_ADD(rcvseq,
                                seg->data->io_pktlen);
           net_incr32(conn->rcvseq, seg->data->io_pktlen);
-          tcp_dataconcat(&conn->readahead, &seg->data);
+          net_iob_concat(&conn->readahead, &seg->data);
         }
       else if (TCP_SEQ_GT(rcvseq, seg->left))
         {
@@ -177,7 +178,7 @@ static uint16_t tcp_ofoseg_data_event(FAR struct net_driver_s *dev,
                   rcvseq = TCP_SEQ_ADD(rcvseq,
                                        seg->data->io_pktlen);
                   net_incr32(conn->rcvseq, seg->data->io_pktlen);
-                  tcp_dataconcat(&conn->readahead, &seg->data);
+                  net_iob_concat(&conn->readahead, &seg->data);
                 }
             }
         }
@@ -350,43 +351,6 @@ uint16_t tcp_callback(FAR struct net_driver_s *dev,
   return flags;
 }
 
-/****************************************************************************
- * Name: tcp_dataconcat
- *
- * Description:
- *   Concatenate iob_s chain iob2 to iob1, if CONFIG_NET_TCP_RECV_PACK is
- *   endabled, pack all data in the I/O buffer chain.
- *
- * Returned Value:
- *   The number of bytes actually buffered is returned.  This will be either
- *   zero or equal to iob->io_pktlen.
- *
- ****************************************************************************/
-
-uint16_t tcp_dataconcat(FAR struct iob_s **iob1, FAR struct iob_s **iob2)
-{
-  if (*iob1 == NULL)
-    {
-      *iob1 = *iob2;
-    }
-  else
-    {
-      iob_concat(*iob1, *iob2);
-    }
-
-  *iob2 = NULL;
-
-#ifdef CONFIG_NET_TCP_RECV_PACK
-  /* Merge an iob chain into a continuous space, thereby reducing iob
-   * consumption.
-   */
-
-  *iob1 = iob_pack(*iob1);
-#endif
-
-  return (*iob1)->io_pktlen;
-}
-
 /****************************************************************************
  * Name: tcp_datahandler
  *
@@ -438,7 +402,7 @@ uint16_t tcp_datahandler(FAR struct net_driver_s *dev,
 
   /* Concat the iob to readahead */
 
-  tcp_dataconcat(&conn->readahead, &iob);
+  net_iob_concat(&conn->readahead, &iob);
 
   /* Clear device buffer */
 
diff --git a/net/tcp/tcp_input.c b/net/tcp/tcp_input.c
index fd3a128239..dd34cd369c 100644
--- a/net/tcp/tcp_input.c
+++ b/net/tcp/tcp_input.c
@@ -315,7 +315,7 @@ static bool tcp_rebuild_ofosegs(FAR struct tcp_conn_s *conn,
 
           else if (ofoseg->left == seg->right)
             {
-              tcp_dataconcat(&seg->data, &ofoseg->data);
+              net_iob_concat(&seg->data, &ofoseg->data);
               seg->right = ofoseg->right;
             }
 
@@ -338,7 +338,7 @@ static bool tcp_rebuild_ofosegs(FAR struct tcp_conn_s *conn,
               ofoseg->data =
                 iob_trimhead(ofoseg->data,
                              TCP_SEQ_SUB(seg->right, ofoseg->left));
-              tcp_dataconcat(&seg->data, &ofoseg->data);
+              net_iob_concat(&seg->data, &ofoseg->data);
               seg->right = ofoseg->right;
             }
         }
@@ -355,7 +355,7 @@ static bool tcp_rebuild_ofosegs(FAR struct tcp_conn_s *conn,
 
           if (ofoseg->right == seg->left)
             {
-              tcp_dataconcat(&ofoseg->data, &seg->data);
+              net_iob_concat(&ofoseg->data, &seg->data);
               seg->data = ofoseg->data;
               seg->left = ofoseg->left;
               ofoseg->data = NULL;
@@ -390,7 +390,7 @@ static bool tcp_rebuild_ofosegs(FAR struct tcp_conn_s *conn,
               ofoseg->data =
                 iob_trimtail(ofoseg->data,
                              ofoseg->right - seg->left);
-              tcp_dataconcat(&ofoseg->data, &seg->data);
+              net_iob_concat(&ofoseg->data, &seg->data);
               seg->data = ofoseg->data;
               seg->left = ofoseg->left;
               ofoseg->data = NULL;
diff --git a/net/utils/CMakeLists.txt b/net/utils/CMakeLists.txt
index 307a348c19..9cbfd8cc89 100644
--- a/net/utils/CMakeLists.txt
+++ b/net/utils/CMakeLists.txt
@@ -29,7 +29,8 @@ set(SRCS
     net_incr32.c
     net_lock.c
     net_snoop.c
-    net_cmsg.c)
+    net_cmsg.c
+    net_iob_concat.c)
 
 # IPv6 utilities
 
diff --git a/net/utils/Kconfig b/net/utils/Kconfig
index 6e55134dd9..0bec1c2141 100644
--- a/net/utils/Kconfig
+++ b/net/utils/Kconfig
@@ -28,3 +28,17 @@ config NET_ARCH_CHKSUM
 config NET_SNOOP_BUFSIZE
 	int "Snoop buffer size for interrupt"
 	default 4096
+
+config NET_RECV_PACK
+	bool "Enable TCP/IP receive data in a continuous poll"
+	default y
+	---help---
+		This option will enable TCP/IP receive data into a continuous iob chain.
+		Fragmentation of network data will intensify iob consumption, if
+		the device receives a message storm of fragmented packets, the iob
+		cache will not be effectively used, this is not allowed on iot devices
+		since the resources of such devices are limited. Of course, this
+		also takes some disadvantages: data needs to be copied.
+		This option will brings some balance on resource-constrained devices,
+		enable this config to reduce the consumption of iob, the received iob
+		buffers will be merged into the contiguous iob chain.
diff --git a/net/utils/Make.defs b/net/utils/Make.defs
index 3782881c60..3ee0274495 100644
--- a/net/utils/Make.defs
+++ b/net/utils/Make.defs
@@ -22,7 +22,7 @@
 
 NET_CSRCS += net_dsec2tick.c net_dsec2timeval.c net_timeval2dsec.c
 NET_CSRCS += net_chksum.c net_ipchksum.c net_incr32.c net_lock.c net_snoop.c
-NET_CSRCS += net_cmsg.c
+NET_CSRCS += net_cmsg.c net_iob_concat.c
 
 # IPv6 utilities
 
diff --git a/net/utils/net_iob_concat.c b/net/utils/net_iob_concat.c
new file mode 100644
index 0000000000..c2e9b292f5
--- /dev/null
+++ b/net/utils/net_iob_concat.c
@@ -0,0 +1,74 @@
+/****************************************************************************
+ * net/utils/net_iob_concat.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+
+#include <nuttx/mm/iob.h>
+
+#ifdef CONFIG_MM_IOB
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: net_iob_concat
+ *
+ * Description:
+ *   Concatenate iob_s chain iob2 to iob1, if CONFIG_NET_RECV_PACK is
+ *   endabled, pack all data in the I/O buffer chain.
+ *
+ * Returned Value:
+ *   The number of bytes actually buffered is returned.  This will be either
+ *   zero or equal to iob->io_pktlen.
+ *
+ ****************************************************************************/
+
+uint16_t net_iob_concat(FAR struct iob_s **iob1, FAR struct iob_s **iob2)
+{
+  if (*iob1 == NULL)
+    {
+      *iob1 = *iob2;
+    }
+  else
+    {
+      iob_concat(*iob1, *iob2);
+    }
+
+  *iob2 = NULL;
+
+#ifdef CONFIG_NET_RECV_PACK
+  /* Merge an iob chain into a continuous space, thereby reducing iob
+   * consumption.
+   */
+
+  *iob1 = iob_pack(*iob1);
+#endif
+
+  return (*iob1)->io_pktlen;
+}
+
+#endif /* CONFIG_MM_IOB */
diff --git a/net/utils/utils.h b/net/utils/utils.h
index b08efad7ce..00eae795e5 100644
--- a/net/utils/utils.h
+++ b/net/utils/utils.h
@@ -192,6 +192,23 @@ uint8_t net_ipv6_mask2pref(FAR const uint16_t *mask);
 void net_ipv6_pref2mask(uint8_t preflen, net_ipv6addr_t mask);
 #endif
 
+/****************************************************************************
+ * Name: net_iob_concat
+ *
+ * Description:
+ *   Concatenate iob_s chain iob2 to iob1, if CONFIG_NET_RECV_PACK is
+ *   endabled, pack all data in the I/O buffer chain.
+ *
+ * Returned Value:
+ *   The number of bytes actually buffered is returned.  This will be either
+ *   zero or equal to iob1->io_pktlen.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MM_IOB
+uint16_t net_iob_concat(FAR struct iob_s **iob1, FAR struct iob_s **iob2);
+#endif
+
 /****************************************************************************
  * Name: net_chksum_adjust
  *


[nuttx] 02/02: net/udp: Change conn->readahead to I/O buffer chain

Posted by xi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 075eb6a6d254bda1d38d1d3459a7108e5c6c4eea
Author: Zhe Weng <we...@xiaomi.com>
AuthorDate: Tue Jul 4 13:24:51 2023 +0800

    net/udp: Change conn->readahead to I/O buffer chain
    
    When using IOB queue to store readahead data, we use one IOB for each
    UDP packet. Then if the packets are very small, like 10Bytes per packet,
    we'll use ~1600 IOBs just for 16KB recv buffer size, which is wasteful
    and dangerous. So change conn->readahead to a single IOB chain like TCP.
    
    Benefits:
    - Using memory and IOBs more efficiently (small packets are common in
      UDP)
    
    Side effects:
    - UDP recv buffer size may count the overhead
    - A little bit drop in performance (<1%, more seek & copy)
    
    Signed-off-by: Zhe Weng <we...@xiaomi.com>
---
 net/procfs/net_udp.c   |  2 +-
 net/tcp/tcp.h          |  5 ++--
 net/udp/udp.h          |  5 ++--
 net/udp/udp_callback.c | 81 ++++++++++++++++++++++++++++++++++----------------
 net/udp/udp_conn.c     |  4 +--
 net/udp/udp_ioctl.c    |  6 ++--
 net/udp/udp_netpoll.c  |  2 +-
 net/udp/udp_notifier.c |  2 +-
 net/udp/udp_recvfrom.c | 62 ++++++++++++++++++++++++++------------
 9 files changed, 111 insertions(+), 58 deletions(-)

diff --git a/net/procfs/net_udp.c b/net/procfs/net_udp.c
index deca5ffaeb..26ba034dbd 100644
--- a/net/procfs/net_udp.c
+++ b/net/procfs/net_udp.c
@@ -122,7 +122,7 @@ static ssize_t netprocfs_udpstats(FAR struct netprocfs_file_s *priv,
 #if CONFIG_NET_SEND_BUFSIZE > 0
                       udp_wrbuffer_inqueue_size(conn),
 #endif
-                      iob_get_queue_size(&conn->readahead));
+                      (conn->readahead) ? conn->readahead->io_pktlen : 0);
 
       len += snprintf(buffer + len, buflen - len,
                       " %*s:%-6" PRIu16 " %*s:%-6" PRIu16 "\n",
diff --git a/net/tcp/tcp.h b/net/tcp/tcp.h
index 301d5ea722..2907f63bd3 100644
--- a/net/tcp/tcp.h
+++ b/net/tcp/tcp.h
@@ -289,11 +289,10 @@ struct tcp_conn_s
 
   /* Read-ahead buffering.
    *
-   *   readahead - A singly linked list of type struct iob_s
-   *               where the TCP/IP read-ahead data is retained.
+   *   readahead - An IOB chain where the TCP/IP read-ahead data is retained.
    */
 
-  struct iob_s *readahead;   /* Read-ahead buffering */
+  FAR struct iob_s *readahead;   /* Read-ahead buffering */
 
 #ifdef CONFIG_NET_TCP_OUT_OF_ORDER
 
diff --git a/net/udp/udp.h b/net/udp/udp.h
index 2e325ccbe6..622897cd0b 100644
--- a/net/udp/udp.h
+++ b/net/udp/udp.h
@@ -127,11 +127,10 @@ struct udp_conn_s
 
   /* Read-ahead buffering.
    *
-   *   readahead - A singly linked list of type struct iob_qentry_s
-   *               where the UDP/IP read-ahead data is retained.
+   *   readahead - An IOB chain where the UDP/IP read-ahead data is retained.
    */
 
-  struct iob_queue_s readahead;   /* Read-ahead buffering */
+  FAR struct iob_s *readahead;   /* Read-ahead buffering */
 
 #ifdef CONFIG_NET_UDP_WRITE_BUFFERS
   /* Write buffering
diff --git a/net/udp/udp_callback.c b/net/udp/udp_callback.c
index 6ccf330cd2..309f494775 100644
--- a/net/udp/udp_callback.c
+++ b/net/udp/udp_callback.c
@@ -36,6 +36,7 @@
 
 #include "devif/devif.h"
 #include "udp/udp.h"
+#include "utils/utils.h"
 
 /****************************************************************************
  * Private Functions
@@ -71,11 +72,11 @@ static uint16_t udp_datahandler(FAR struct net_driver_s *dev,
 #endif
 
   uint8_t src_addr_size;
-  FAR void  *src_addr;
-  uint8_t offset = 0;
+  FAR void *src_addr;
+  int offset;
 
 #if CONFIG_NET_RECV_BUFSIZE > 0
-  if (iob_get_queue_size(&conn->readahead) > conn->rcvbufs)
+  if (conn->readahead && conn->readahead->io_pktlen > conn->rcvbufs)
     {
       netdev_iob_release(dev);
 #ifdef CONFIG_NET_STATISTICS
@@ -151,44 +152,72 @@ static uint16_t udp_datahandler(FAR struct net_driver_s *dev,
     }
 #endif /* CONFIG_NET_IPv4 */
 
-  /* Override the address info begin of io_data */
-
-#ifdef CONFIG_NETDEV_IFINDEX
-  iob->io_data[offset++] = dev->d_ifindex;
-#endif
-  iob->io_data[offset++] = src_addr_size;
-  memcpy(&iob->io_data[offset], src_addr, src_addr_size);
+  /* Copy the meta info into the I/O buffer chain, just before data.
+   * Layout: |datalen|ifindex|src_addr_size|src_addr|data|
+   */
 
-  /* Trim l3/l4 offset */
+  offset = (dev->d_appdata - iob->io_data) - iob->io_offset;
 
-  iob = iob_trimhead(iob, (dev->d_appdata - iob->io_data) -
-                          iob->io_offset);
+  offset -= src_addr_size;
+  ret = iob_trycopyin(iob, src_addr, src_addr_size, offset, true);
+  if (ret < 0)
+    {
+      goto errout;
+    }
 
-  /* Add the new I/O buffer chain to the tail of the read-ahead queue */
+  offset -= sizeof(src_addr_size);
+  ret = iob_trycopyin(iob, &src_addr_size, sizeof(src_addr_size),
+                      offset, true);
+  if (ret < 0)
+    {
+      goto errout;
+    }
 
-  ret = iob_tryadd_queue(iob, &conn->readahead);
+#ifdef CONFIG_NETDEV_IFINDEX
+  offset -= sizeof(dev->d_ifindex);
+  ret = iob_trycopyin(iob, &dev->d_ifindex, sizeof(dev->d_ifindex),
+                      offset, true);
   if (ret < 0)
     {
-      nerr("ERROR: Failed to queue the I/O buffer chain: %d\n", ret);
+      goto errout;
+    }
+#endif
 
-      iob_free_chain(iob);
-      buflen = 0;
+  offset -= sizeof(buflen);
+  ret = iob_trycopyin(iob, (FAR const uint8_t *)&buflen, sizeof(buflen),
+                      offset, true);
+  if (ret < 0)
+    {
+      goto errout;
     }
+
+  /* Trim l3/l4 offset, src_addr + 4Bytes should be less than header size. */
+
+  DEBUGASSERT(offset >= 0);
+  iob = iob_trimhead(iob, offset);
+
+  /* Concat the iob to readahead */
+
+  net_iob_concat(&conn->readahead, &iob);
+
 #ifdef CONFIG_NET_UDP_NOTIFIER
-  else
-    {
-      ninfo("Buffered %d bytes\n", buflen);
+  ninfo("Buffered %d bytes\n", buflen);
 
-      /* Provided notification(s) that additional UDP read-ahead data is
-       * available.
-       */
+  /* Provided notification(s) that additional UDP read-ahead data is
+   * available.
+   */
 
-      udp_readahead_signal(conn);
-    }
+  udp_readahead_signal(conn);
 #endif
 
   netdev_iob_clear(dev);
   return buflen;
+
+errout:
+  nerr("ERROR: Failed to queue the I/O buffer chain: %d\n", ret);
+
+  netdev_iob_release(dev);
+  return 0;
 }
 
 /****************************************************************************
diff --git a/net/udp/udp_conn.c b/net/udp/udp_conn.c
index b567789452..cc6c166fbe 100644
--- a/net/udp/udp_conn.c
+++ b/net/udp/udp_conn.c
@@ -691,9 +691,9 @@ void udp_free(FAR struct udp_conn_s *conn)
 
   dq_rem(&conn->sconn.node, &g_active_udp_connections);
 
-  /* Release any read-ahead buffers attached to the connection */
+  /* Release any read-ahead buffers attached to the connection, NULL is ok */
 
-  iob_free_queue(&conn->readahead);
+  iob_free_chain(conn->readahead);
 
 #ifdef CONFIG_NET_UDP_WRITE_BUFFERS
   /* Release any write buffers attached to the connection */
diff --git a/net/udp/udp_ioctl.c b/net/udp/udp_ioctl.c
index 2f29b9549c..5b5fdc17e2 100644
--- a/net/udp/udp_ioctl.c
+++ b/net/udp/udp_ioctl.c
@@ -64,10 +64,12 @@ int udp_ioctl(FAR struct udp_conn_s *conn, int cmd, unsigned long arg)
   switch (cmd)
     {
       case FIONREAD:
-        iob = iob_peek_queue(&conn->readahead);
+        iob = conn->readahead;
         if (iob)
           {
-            *(FAR int *)((uintptr_t)arg) = iob->io_pktlen;
+            uint16_t datalen;
+            iob_copyout((FAR uint8_t *)&datalen, iob, sizeof(datalen), 0);
+            *(FAR int *)((uintptr_t)arg) = datalen;
           }
         else
           {
diff --git a/net/udp/udp_netpoll.c b/net/udp/udp_netpoll.c
index e3e2c563c3..11f1b5cf6c 100644
--- a/net/udp/udp_netpoll.c
+++ b/net/udp/udp_netpoll.c
@@ -208,7 +208,7 @@ int udp_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds)
 
   /* Check for read data availability now */
 
-  if (!IOB_QEMPTY(&conn->readahead))
+  if (conn->readahead != NULL)
     {
       /* Normal data may be read without blocking. */
 
diff --git a/net/udp/udp_notifier.c b/net/udp/udp_notifier.c
index 21f16b0548..b1726ab512 100644
--- a/net/udp/udp_notifier.c
+++ b/net/udp/udp_notifier.c
@@ -76,7 +76,7 @@ int udp_readahead_notifier_setup(worker_t worker,
    * setting up the notification.
    */
 
-  if (conn->readahead.qh_head != NULL)
+  if (conn->readahead != NULL)
     {
       return 0;
     }
diff --git a/net/udp/udp_recvfrom.c b/net/udp/udp_recvfrom.c
index e96543ff89..82f08d2938 100644
--- a/net/udp/udp_recvfrom.c
+++ b/net/udp/udp_recvfrom.c
@@ -164,34 +164,56 @@ static inline void udp_readahead(struct udp_recvfrom_s *pstate)
 
   pstate->ir_recvlen = -1;
 
-  if ((iob = iob_peek_queue(&conn->readahead)) != NULL)
+  if ((iob = conn->readahead) != NULL)
     {
-      int recvlen = pstate->ir_msg->msg_iov->iov_len;
+      int recvlen;
+      int offset = 0;
+      uint16_t datalen;
       uint8_t src_addr_size;
-      uint8_t offset = 0;
-      FAR void *srcaddr;
       uint8_t ifindex;
+#ifdef CONFIG_NET_IPv6
+      uint8_t srcaddr[sizeof(struct sockaddr_in6)];
+#else
+      uint8_t srcaddr[sizeof(struct sockaddr_in)];
+#endif
 
-      /* Unflatten saved connection information */
+      /* Unflatten saved connection information
+       * Layout: |datalen|ifindex|src_addr_size|src_addr|data|
+       */
+
+      recvlen = iob_copyout((FAR uint8_t *)&datalen, iob,
+                            sizeof(datalen), offset);
+      offset += sizeof(datalen);
+      DEBUGASSERT(recvlen == sizeof(datalen));
 
 #ifdef CONFIG_NETDEV_IFINDEX
-      ifindex = iob->io_data[offset++];
+      recvlen = iob_copyout(&ifindex, iob, sizeof(ifindex), offset);
+      offset += sizeof(ifindex);
+      DEBUGASSERT(recvlen == sizeof(ifindex));
 #else
       ifindex = 1;
 #endif
-      src_addr_size = iob->io_data[offset++];
-      srcaddr = &iob->io_data[offset];
+      recvlen = iob_copyout(&src_addr_size, iob,
+                            sizeof(src_addr_size), offset);
+      offset += sizeof(src_addr_size);
+      DEBUGASSERT(recvlen == sizeof(src_addr_size));
+
+      recvlen = iob_copyout(srcaddr, iob, src_addr_size, offset);
+      offset += src_addr_size;
+      DEBUGASSERT(recvlen == src_addr_size);
 
       /* Copy to user */
 
-      recvlen = iob_copyout(pstate->ir_msg->msg_iov->iov_base,
-                            iob, recvlen, 0);
+      recvlen = iob_copyout(pstate->ir_msg->msg_iov->iov_base, iob,
+                            MIN(pstate->ir_msg->msg_iov->iov_len, datalen),
+                            offset);
 
       /* Update the accumulated size of the data read */
 
       pstate->ir_recvlen = recvlen;
 
-      ninfo("Received %d bytes (of %d)\n", recvlen, iob->io_pktlen);
+      ninfo("Received %d bytes (of %d, total %d)\n",
+            recvlen, datalen, iob->io_pktlen);
 
       if (pstate->ir_msg->msg_name)
         {
@@ -205,17 +227,19 @@ static inline void udp_readahead(struct udp_recvfrom_s *pstate)
 
       udp_recvpktinfo(pstate, srcaddr, ifindex);
 
-      /* Remove the I/O buffer chain from the head of the read-ahead
-       * buffer queue.
-       */
+      /* Remove the packet from the head of the I/O buffer chain. */
 
       if (!(pstate->ir_flags & MSG_PEEK))
         {
-          iob_remove_queue(&conn->readahead);
-
-          /* And free the I/O buffer chain */
-
-          iob_free_chain(iob);
+          if (offset + datalen >= iob->io_pktlen)
+            {
+              iob_free_chain(iob);
+              conn->readahead = NULL;
+            }
+          else
+            {
+              conn->readahead = iob_trimhead(iob, offset + datalen);
+            }
         }
     }
 }