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:
parent
4b7604cf81
commit
075eb6a6d2
@ -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",
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -36,6 +36,7 @@
|
||||
|
||||
#include "devif/devif.h"
|
||||
#include "udp/udp.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
@ -72,10 +73,10 @@ static uint16_t udp_datahandler(FAR struct net_driver_s *dev,
|
||||
|
||||
uint8_t src_addr_size;
|
||||
FAR void *src_addr;
|
||||
uint8_t offset = 0;
|
||||
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,32 +152,55 @@ 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)
|
||||
{
|
||||
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
|
||||
@ -184,11 +208,16 @@ static uint16_t udp_datahandler(FAR struct net_driver_s *dev,
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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. */
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
||||
if (offset + datalen >= iob->io_pktlen)
|
||||
{
|
||||
iob_free_chain(iob);
|
||||
conn->readahead = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
conn->readahead = iob_trimhead(iob, offset + datalen);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user