Skip to content

Commit

Permalink
inet_sockif:support multiple iov recv
Browse files Browse the repository at this point in the history
Summary:
  Implement multiple iovlen recvmsg in inet_sockif, only TCP mode
  support at current.

Signed-off-by: chenrun1 <[email protected]>
  • Loading branch information
crafcat7 authored and xiaoxiang781216 committed Jan 20, 2025
1 parent 87a7714 commit 99a1f0d
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 63 deletions.
5 changes: 0 additions & 5 deletions net/inet/inet_sockif.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
174 changes: 119 additions & 55 deletions net/tcp/tcp_recvfrom.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
Expand All @@ -735,22 +734,23 @@ 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;
}
}

/* 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.
Expand All @@ -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
Expand All @@ -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).
*/
Expand All @@ -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)
{
Expand All @@ -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))
Expand All @@ -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 */
9 changes: 6 additions & 3 deletions net/udp/udp_recvfrom.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.
*/
Expand Down

0 comments on commit 99a1f0d

Please sign in to comment.