From 99a1f0dc65dcf39cc7a730e51ad2cbfdeef456d0 Mon Sep 17 00:00:00 2001 From: chenrun1 Date: Tue, 22 Oct 2024 15:02:39 +0800 Subject: [PATCH] inet_sockif:support multiple iov recv Summary: Implement multiple iovlen recvmsg in inet_sockif, only TCP mode support at current. Signed-off-by: chenrun1 --- net/inet/inet_sockif.c | 5 -- net/tcp/tcp_recvfrom.c | 174 ++++++++++++++++++++++++++++------------- net/udp/udp_recvfrom.c | 9 ++- 3 files changed, 125 insertions(+), 63 deletions(-) diff --git a/net/inet/inet_sockif.c b/net/inet/inet_sockif.c index b217a67ec35f7..3c30486018d15 100644 --- a/net/inet/inet_sockif.c +++ b/net/inet/inet_sockif.c @@ -2273,11 +2273,6 @@ static ssize_t inet_recvmsg(FAR struct socket *psock, { ssize_t ret; - if (msg->msg_iovlen != 1) - { - return -ENOTSUP; - } - /* If a 'from' address has been provided, verify that it is large * enough to hold this address family. */ diff --git a/net/tcp/tcp_recvfrom.c b/net/tcp/tcp_recvfrom.c index c401b42ccb11a..fa5a2d82ebf04 100644 --- a/net/tcp/tcp_recvfrom.c +++ b/net/tcp/tcp_recvfrom.c @@ -658,53 +658,52 @@ static void tcp_notify_recvcpu(FAR struct tcp_conn_s *conn) #endif /* CONFIG_NETDEV_RSS */ /**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: psock_tcp_recvfrom + * Name: tcp_recvfrom_one * * Description: - * Perform the recvfrom operation for a TCP/IP SOCK_STREAM + * This function attempts to receive data from the specified TCP connection + * 'conn' and stores it in the provided buffer 'buf'. It handles data that + * may already be in the read-ahead buffer and manages blocking or + * non-blocking operations based on connection flags. + * It also monitors connection state, handles connection timeouts, and + * updates the receive window when necessary. * * Input Parameters: - * psock Pointer to the socket structure for the SOCK_DRAM socket - * msg Receive info and buffer for receive data - * flags Receive flags + * conn - The TCP connection from which data is to be received. + * buf - The buffer to store the received data. + * len - The maximum number of bytes to receive. + * from - Socket address structure to store the source address + * (if provided). + * fromlen - Length of the address structure. + * flags - Flags indicating specific receive options + * (e.g., non-blocking). * * Returned Value: - * On success, returns the number of characters received. On error, - * -errno is returned (see recvfrom for list of errnos). + * Returns the number of bytes received, or a negative error code in + * case of failure (e.g., -ENOTCONN if not connected). * * Assumptions: + * conn, buf, and from are non-NULL pointers. * ****************************************************************************/ -ssize_t psock_tcp_recvfrom(FAR struct socket *psock, FAR struct msghdr *msg, - int flags) +static ssize_t tcp_recvfrom_one(FAR struct tcp_conn_s *conn, FAR void *buf, + size_t len, FAR struct sockaddr *from, + FAR socklen_t *fromlen, int flags) { - FAR struct sockaddr *from = msg->msg_name; - FAR socklen_t *fromlen = &msg->msg_namelen; - FAR void *buf = msg->msg_iov->iov_base; - size_t len = msg->msg_iov->iov_len; - struct tcp_recvfrom_s state; - FAR struct tcp_conn_s *conn; - struct tcp_callback_s info; - int ret; - - net_lock(); + struct tcp_recvfrom_s state; + struct tcp_callback_s info; + ssize_t ret; - conn = psock->s_conn; - - /* Initialize the state structure. This is done with the network locked - * because we don't want anything to happen until we are ready. + /* Initialize the state structure. This is done with the network + * locked because we don't want anything to happen until we are ready. */ tcp_recvfrom_initialize(conn, buf, len, from, fromlen, &state, flags); - /* Handle any any TCP data already buffered in a read-ahead buffer. NOTE - * that there may be read-ahead data to be retrieved even after the - * socket has been disconnected. + /* Handle any any TCP data already buffered in a read-ahead buffer. + * NOTE that there may be read-ahead data to be retrieved even after + * the socket has been disconnected. */ tcp_readahead(&state); @@ -722,9 +721,9 @@ ssize_t psock_tcp_recvfrom(FAR struct socket *psock, FAR struct msghdr *msg, if (!_SS_ISCONNECTED(conn->sconn.s_flags)) { /* Was any data transferred from the readahead buffer after we were - * disconnected? If so, then return the number of bytes received. We - * will wait to return end disconnection indications the next time that - * recvfrom() is called. + * disconnected? If so, then return the number of bytes received. + * We will wait to return end disconnection indications the next + * time that recvfrom() is called. * * If no data was received (i.e., ret == 0 -- it will not be * negative) and the connection was gracefully closed by the remote @@ -735,8 +734,8 @@ ssize_t psock_tcp_recvfrom(FAR struct socket *psock, FAR struct msghdr *msg, if (ret <= 0 && !_SS_ISCLOSED(conn->sconn.s_flags)) { /* Nothing was previously received from the read-ahead buffers. - * The SOCK_STREAM must be (re-)connected in order to receive any - * additional data. + * The SOCK_STREAM must be (re-)connected in order to receive + * any additional data. */ ret = -ENOTCONN; @@ -744,13 +743,14 @@ ssize_t psock_tcp_recvfrom(FAR struct socket *psock, FAR struct msghdr *msg, } /* In general, this implementation will not support non-blocking socket - * operations... except in a few cases: Here for TCP receive with read- - * ahead enabled. If this socket is configured as non-blocking then - * return EAGAIN if no data was obtained from the read-ahead buffers. + * operations... except in a few cases: Here for TCP receive with + * read-ahead enabled. If this socket is configured as non-blocking + * then return EAGAIN if no data was obtained from the read-ahead + * buffers. */ else if (_SS_ISNONBLOCK(conn->sconn.s_flags) || - (flags & MSG_DONTWAIT) != 0) + (flags & MSG_DONTWAIT) != 0) { /* Return the number of bytes read from the read-ahead buffer if * something was received (already in 'ret'); EAGAIN if not. @@ -765,8 +765,9 @@ ssize_t psock_tcp_recvfrom(FAR struct socket *psock, FAR struct msghdr *msg, } /* It is okay to block if we need to. If there is space to receive - * anything more, then we will wait to receive the data. Otherwise return - * the number of bytes read from the read-ahead buffer (already in 'ret'). + * anything more, then we will wait to receive the data. Otherwise + * return the number of bytes read from the read-ahead buffer + * (already in 'ret'). */ else @@ -775,11 +776,11 @@ ssize_t psock_tcp_recvfrom(FAR struct socket *psock, FAR struct msghdr *msg, * incoming TCP/IP data. Just a few more conditions to check: * * 1) Make sure that there is buffer space to receive additional data - * (state.ir_buflen > 0). This could be zero, for example, we filled - * the user buffer with data from the read-ahead buffers. And - * 2) then we not want to wait if we already obtained some data from the - * read-ahead buffer. In that case, return now with what we have (don't - * want for more because there may be no timeout). + * (state.ir_buflen > 0). This could be zero, for example, we + * filled the user buffer with data from the read-ahead buffers. And + * 2) then we not want to wait if we already obtained some data from + * the read-ahead buffer. In that case, return now with what we + * have (don't want for more because there may be no timeout). * 3) If however MSG_WAITALL flag is set, block here till all requested * data are received (or there is a timeout / error). */ @@ -806,13 +807,13 @@ ssize_t psock_tcp_recvfrom(FAR struct socket *psock, FAR struct msghdr *msg, info.tc_sem = &state.ir_sem; tls_cleanup_push(tls_get_info(), tcp_callback_cleanup, &info); - /* Wait for either the receive to complete or for an error/timeout - * to occur. net_sem_timedwait will also terminate if a signal is - * received. + /* Wait for either the receive to complete or for an + * error/timeout to occur. net_sem_timedwait will also + * terminate if a signal is received. */ ret = net_sem_timedwait(&state.ir_sem, - _SO_TIMEOUT(conn->sconn.s_rcvtimeo)); + _SO_TIMEOUT(conn->sconn.s_rcvtimeo)); tls_cleanup_pop(tls_get_info(), 0); if (ret == -ETIMEDOUT) { @@ -831,10 +832,9 @@ ssize_t psock_tcp_recvfrom(FAR struct socket *psock, FAR struct msghdr *msg, } /* Receive additional data from read-ahead buffer, send the ACK timely. - * * Revisit: Because IOBs are system-wide resources, consuming the read - * ahead buffer would update recv window of all connections in the system, - * not only this particular connection. + * ahead buffer would update recv window of all connections in the + * system, not only this particular connection. */ if (tcp_should_send_recvwindow(conn)) @@ -843,10 +843,74 @@ ssize_t psock_tcp_recvfrom(FAR struct socket *psock, FAR struct msghdr *msg, } tcp_notify_recvcpu(conn); + tcp_recvfrom_uninitialize(&state); + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: psock_tcp_recvfrom + * + * Description: + * Perform the recvfrom operation for a TCP/IP SOCK_STREAM + * + * Input Parameters: + * psock Pointer to the socket structure for the SOCK_DRAM socket + * msg Receive info and buffer for receive data + * flags Receive flags + * + * Returned Value: + * On success, returns the number of characters received. On error, + * -errno is returned (see recvfrom for list of errnos). + * + * Assumptions: + * + ****************************************************************************/ + +ssize_t psock_tcp_recvfrom(FAR struct socket *psock, FAR struct msghdr *msg, + int flags) +{ + FAR struct sockaddr *from = msg->msg_name; + FAR socklen_t *fromlen = &msg->msg_namelen; + FAR struct tcp_conn_s *conn; + ssize_t nrecv = 0; + ssize_t ret = 0; + int i; + + net_lock(); + + conn = psock->s_conn; + for (i = 0; i < msg->msg_iovlen; i++) + { + FAR void *buf = msg->msg_iov[i].iov_base; + size_t len = msg->msg_iov[i].iov_len; + + ret = tcp_recvfrom_one(conn, buf, len, from, fromlen, flags); + if (ret <= 0) + { + break; + } + + nrecv += ret; + + /* User has not set MSG_WAITALL: If the first buffer is full + * when received for the first time, then check the next buffer, + * otherwise return the received data. + * User sets MSG_WAITALL: Ensure that each iov is filled before + * returning + */ + + if (!(flags & MSG_WAITALL) && ret < msg->msg_iov[i].iov_len) + { + break; + } + } net_unlock(); - tcp_recvfrom_uninitialize(&state); - return (ssize_t)ret; + return nrecv ? nrecv : ret; } #endif /* CONFIG_NET_TCP */ diff --git a/net/udp/udp_recvfrom.c b/net/udp/udp_recvfrom.c index 0854a20c436bd..b7fcca74ec5b9 100644 --- a/net/udp/udp_recvfrom.c +++ b/net/udp/udp_recvfrom.c @@ -647,8 +647,6 @@ static void udp_notify_recvcpu(FAR struct udp_conn_s *conn) conn->rcvcpu = cpu; } - - return; } #else # define udp_notify_recvcpu(c) @@ -683,10 +681,15 @@ ssize_t psock_udp_recvfrom(FAR struct socket *psock, FAR struct msghdr *msg, FAR struct net_driver_s *dev; struct udp_callback_s info; struct udp_recvfrom_s state; - int ret; + ssize_t ret; /* Perform the UDP recvfrom() operation */ + if (msg->msg_iovlen != 1) + { + return -ENOTSUP; + } + /* Initialize the state structure. This is done with the network locked * because we don't want anything to happen until we are ready. */