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 <wengzhe@xiaomi.com>
This commit is contained in:
Zhe Weng
2023-07-04 13:24:51 +08:00
committed by Xiang Xiao
parent 4b7604cf81
commit 075eb6a6d2
9 changed files with 115 additions and 62 deletions
+59 -30
View File
@@ -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 */
/* Copy the meta info into the I/O buffer chain, just before data.
* Layout: |datalen|ifindex|src_addr_size|src_addr|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);
offset = (dev->d_appdata - iob->io_data) - iob->io_offset;
/* Trim l3/l4 offset */
iob = iob_trimhead(iob, (dev->d_appdata - iob->io_data) -
iob->io_offset);
/* Add the new I/O buffer chain to the tail of the read-ahead queue */
ret = iob_tryadd_queue(iob, &conn->readahead);
offset -= src_addr_size;
ret = iob_trycopyin(iob, src_addr, src_addr_size, offset, true);
if (ret < 0)
{
nerr("ERROR: Failed to queue the I/O buffer chain: %d\n", ret);
iob_free_chain(iob);
buflen = 0;
goto errout;
}
#ifdef CONFIG_NET_UDP_NOTIFIER
else
offset -= sizeof(src_addr_size);
ret = iob_trycopyin(iob, &src_addr_size, sizeof(src_addr_size),
offset, true);
if (ret < 0)
{
ninfo("Buffered %d bytes\n", buflen);
/* Provided notification(s) that additional UDP read-ahead data is
* available.
*/
udp_readahead_signal(conn);
goto errout;
}
#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)
{
goto errout;
}
#endif
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
ninfo("Buffered %d bytes\n", buflen);
/* Provided notification(s) that additional UDP read-ahead data is
* available.
*/
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;
}
/****************************************************************************