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/incubator-nuttx.git
commit 7d4502aca61d3e760f3af0868efe800e864f3bf2 Author: chao.an <[email protected]> AuthorDate: Mon Jul 19 21:45:46 2021 +0800 net/socket: add SO_SNDBUF support Signed-off-by: chao.an <[email protected]> --- include/nuttx/net/netconfig.h | 4 +++ net/Kconfig | 7 +++++ net/socket/setsockopt.c | 63 ++++++++++++++++++++++++++++++++++++++++++- net/tcp/tcp.h | 23 ++++++++++++++++ net/tcp/tcp_conn.c | 13 +++++++++ net/tcp/tcp_send_buffered.c | 58 +++++++++++++++++++++++++++++++++++++++ net/udp/udp.h | 23 ++++++++++++++++ net/udp/udp_conn.c | 13 +++++++++ net/udp/udp_sendto_buffered.c | 53 ++++++++++++++++++++++++++++++++++++ 9 files changed, 256 insertions(+), 1 deletion(-) diff --git a/include/nuttx/net/netconfig.h b/include/nuttx/net/netconfig.h index adeb7ed..af1c439 100644 --- a/include/nuttx/net/netconfig.h +++ b/include/nuttx/net/netconfig.h @@ -201,6 +201,10 @@ # define NET_LO_PKTSIZE CONFIG_NET_LOOPBACK_PKTSIZE #endif +#ifndef CONFIG_NET_SEND_BUFSIZE +#define CONFIG_NET_SEND_BUFSIZE 0 +#endif + /* Layer 3/4 Configuration Options ******************************************/ /* IP configuration options */ diff --git a/net/Kconfig b/net/Kconfig index 083e6e1..481fa65 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -123,6 +123,13 @@ config NET_RECV_BUFSIZE ---help--- This is the default value for receive buffer size. +config NET_SEND_BUFSIZE + int "Net Send buffer size" + depends on NET_TCP_WRITE_BUFFERS || NET_UDP_WRITE_BUFFERS + default 0 + ---help--- + This is the default value for send buffer size. + endmenu # Driver buffer configuration menu "Link layer support" diff --git a/net/socket/setsockopt.c b/net/socket/setsockopt.c index 6e424ca..e9f87ac 100644 --- a/net/socket/setsockopt.c +++ b/net/socket/setsockopt.c @@ -196,6 +196,68 @@ static int psock_socketlevel_option(FAR struct socket *psock, int option, return OK; } #endif + +#if CONFIG_NET_SEND_BUFSIZE > 0 + case SO_SNDBUF: /* Sets send buffer size */ + { + int buffersize; + + /* Verify that option is the size of an 'int'. Should also check + * that 'value' is properly aligned for an 'int' + */ + + if (value_len != sizeof(int)) + { + return -EINVAL; + } + + /* Get the value. Is the option being set or cleared? */ + + buffersize = *(FAR int *)value; + + if (buffersize < 0 || buffersize > INT_MAX) + { + return -EINVAL; + } + + net_lock(); + +#if defined(CONFIG_NET_TCP) && !defined(CONFIG_NET_TCP_NO_STACK) + if (psock->s_type == SOCK_STREAM) + { + FAR struct tcp_conn_s *conn; + + conn = (FAR struct tcp_conn_s *)psock->s_conn; + + /* Save the send buffer size */ + + conn->snd_bufs = buffersize; + } + else +#endif +#if defined(CONFIG_NET_UDP) && !defined(CONFIG_NET_UDP_NO_STACK) + if (psock->s_type == SOCK_DGRAM) + { + FAR struct udp_conn_s *conn; + + conn = (FAR struct udp_conn_s *)psock->s_conn; + + /* Save the send buffer size */ + + conn->sndbufs = buffersize; + } + else +#endif + { + net_unlock(); + return -ENOPROTOOPT; + } + + net_unlock(); + + return OK; + } +#endif } #ifdef CONFIG_NET_USRSOCK @@ -337,7 +399,6 @@ static int psock_socketlevel_option(FAR struct socket *psock, int option, /* The following are not yet implemented */ case SO_RCVLOWAT: /* Sets the minimum number of bytes to input */ - case SO_SNDBUF: /* Sets send buffer size */ case SO_SNDLOWAT: /* Sets the minimum number of bytes to output */ /* There options are only valid when used with getopt */ diff --git a/net/tcp/tcp.h b/net/tcp/tcp.h index 716e995..d80567c 100644 --- a/net/tcp/tcp.h +++ b/net/tcp/tcp.h @@ -31,6 +31,7 @@ #include <queue.h> #include <nuttx/clock.h> +#include <nuttx/semaphore.h> #include <nuttx/mm/iob.h> #include <nuttx/net/ip.h> @@ -204,6 +205,10 @@ struct tcp_conn_s #if CONFIG_NET_RECV_BUFSIZE > 0 int32_t rcv_bufs; /* Maximum amount of bytes queued in recv */ #endif +#if CONFIG_NET_SEND_BUFSIZE > 0 + int32_t snd_bufs; /* Maximum amount of bytes queued in send */ + sem_t snd_sem; /* Semaphore signals send completion */ +#endif #ifdef CONFIG_NET_TCP_WRITE_BUFFERS uint32_t tx_unacked; /* Number bytes sent but not yet ACKed */ #else @@ -1888,6 +1893,24 @@ int tcp_txdrain(FAR struct socket *psock, unsigned int timeout); int tcp_ioctl(FAR struct tcp_conn_s *conn, int cmd, FAR void *arg, size_t arglen); +/**************************************************************************** + * Name: tcp_sendbuffer_notify + * + * Description: + * Notify the send buffer semaphore + * + * Input Parameters: + * conn - The TCP connection of interest + * + * Assumptions: + * Called from user logic with the network locked. + * + ****************************************************************************/ + +#if CONFIG_NET_SEND_BUFSIZE > 0 +void tcp_sendbuffer_notify(FAR struct tcp_conn_s *conn); +#endif /* CONFIG_NET_SEND_BUFSIZE */ + #ifdef __cplusplus } #endif diff --git a/net/tcp/tcp_conn.c b/net/tcp/tcp_conn.c index f5ef61d..fbcfb52 100644 --- a/net/tcp/tcp_conn.c +++ b/net/tcp/tcp_conn.c @@ -676,6 +676,12 @@ FAR struct tcp_conn_s *tcp_alloc(uint8_t domain) #if CONFIG_NET_RECV_BUFSIZE > 0 conn->rcv_bufs = CONFIG_NET_RECV_BUFSIZE; #endif +#if CONFIG_NET_SEND_BUFSIZE > 0 + conn->snd_bufs = CONFIG_NET_SEND_BUFSIZE; + + nxsem_init(&conn->snd_sem, 0, 0); + nxsem_set_protocol(&conn->snd_sem, SEM_PRIO_NONE); +#endif } return conn; @@ -746,6 +752,13 @@ void tcp_free(FAR struct tcp_conn_s *conn) { tcp_wrbuffer_release(wrbuffer); } + +#if CONFIG_NET_SEND_BUFSIZE > 0 + /* Notify the send buffer available */ + + tcp_sendbuffer_notify(conn); +#endif /* CONFIG_NET_SEND_BUFSIZE */ + #endif #ifdef CONFIG_NET_TCPBACKLOG diff --git a/net/tcp/tcp_send_buffered.c b/net/tcp/tcp_send_buffered.c index 364f24f..2743dca 100644 --- a/net/tcp/tcp_send_buffered.c +++ b/net/tcp/tcp_send_buffered.c @@ -108,6 +108,7 @@ * ****************************************************************************/ +#if CONFIG_NET_SEND_BUFSIZE > 0 static uint32_t tcp_inqueue_wrb_size(FAR struct tcp_conn_s *conn) { FAR struct tcp_wrbuffer_s *wrb; @@ -131,6 +132,7 @@ static uint32_t tcp_inqueue_wrb_size(FAR struct tcp_conn_s *conn) return total; } +#endif /* CONFIG_NET_SEND_BUFSIZE */ /**************************************************************************** * Name: psock_insert_segment @@ -258,6 +260,12 @@ static inline void psock_lost_connection(FAR struct socket *psock, tcp_wrbuffer_release((FAR struct tcp_wrbuffer_s *)entry); } +#if CONFIG_NET_SEND_BUFSIZE > 0 + /* Notify the send buffer available */ + + tcp_sendbuffer_notify(conn); +#endif /* CONFIG_NET_SEND_BUFSIZE */ + /* Reset write buffering variables */ sq_init(&conn->unacked_q); @@ -741,6 +749,12 @@ static uint16_t psock_send_eventhandler(FAR struct net_driver_s *dev, } } +#if CONFIG_NET_SEND_BUFSIZE > 0 + /* Notify the send buffer available if wrbbuffer drained */ + + tcp_sendbuffer_notify(conn); +#endif /* CONFIG_NET_SEND_BUFSIZE */ + /* Check if the outgoing packet is available (it may have been claimed * by a sendto event serving a different thread). */ @@ -1098,6 +1112,23 @@ ssize_t psock_tcp_send(FAR struct socket *psock, FAR const void *buf, psock->s_sndcb->priv = (FAR void *)psock; psock->s_sndcb->event = psock_send_eventhandler; +#if CONFIG_NET_SEND_BUFSIZE > 0 + /* If the send buffer size exceeds the send limit, + * wait for the write buffer to be released + */ + + while (tcp_inqueue_wrb_size(conn) >= conn->snd_bufs) + { + if (nonblock) + { + ret = -EAGAIN; + goto errout_with_lock; + } + + net_lockedwait_uninterruptible(&conn->snd_sem); + } +#endif /* CONFIG_NET_SEND_BUFSIZE */ + /* Allocate a write buffer. Careful, the network will be momentarily * unlocked here. */ @@ -1355,4 +1386,31 @@ int psock_tcp_cansend(FAR struct socket *psock) return OK; } +/**************************************************************************** + * Name: tcp_sendbuffer_notify + * + * Description: + * Notify the send buffer semaphore + * + * Input Parameters: + * conn - The TCP connection of interest + * + * Assumptions: + * Called from user logic with the network locked. + * + ****************************************************************************/ + +#if CONFIG_NET_SEND_BUFSIZE > 0 +void tcp_sendbuffer_notify(FAR struct tcp_conn_s *conn) +{ + int val = 0; + + nxsem_get_value(&conn->snd_sem, &val); + if (val < 0) + { + nxsem_post(&conn->snd_sem); + } +} +#endif /* CONFIG_NET_SEND_BUFSIZE */ + #endif /* CONFIG_NET && CONFIG_NET_TCP && CONFIG_NET_TCP_WRITE_BUFFERS */ diff --git a/net/udp/udp.h b/net/udp/udp.h index 71a4b2e..fef9c9f 100644 --- a/net/udp/udp.h +++ b/net/udp/udp.h @@ -31,6 +31,7 @@ #include <sys/socket.h> #include <queue.h> +#include <nuttx/semaphore.h> #include <nuttx/net/ip.h> #include <nuttx/mm/iob.h> @@ -123,6 +124,10 @@ struct udp_conn_s #if CONFIG_NET_RECV_BUFSIZE > 0 int32_t rcvbufs; /* Maximum amount of bytes queued in recv */ #endif +#if CONFIG_NET_SEND_BUFSIZE > 0 + int32_t sndbufs; /* Maximum amount of bytes queued in send */ + sem_t sndsem; /* Semaphore signals send completion */ +#endif /* Read-ahead buffering. * @@ -889,6 +894,24 @@ int udp_txdrain(FAR struct socket *psock, unsigned int timeout); int udp_ioctl(FAR struct udp_conn_s *conn, int cmd, FAR void *arg, size_t arglen); +/**************************************************************************** + * Name: udp_sendbuffer_notify + * + * Description: + * Notify the send buffer semaphore + * + * Input Parameters: + * conn - The UDP connection of interest + * + * Assumptions: + * Called from user logic with the network locked. + * + ****************************************************************************/ + +#if CONFIG_NET_SEND_BUFSIZE > 0 +void udp_sendbuffer_notify(FAR struct udp_conn_s *conn); +#endif /* CONFIG_NET_SEND_BUFSIZE */ + #undef EXTERN #ifdef __cplusplus } diff --git a/net/udp/udp_conn.c b/net/udp/udp_conn.c index 1aba26c..acb4377 100644 --- a/net/udp/udp_conn.c +++ b/net/udp/udp_conn.c @@ -588,6 +588,12 @@ FAR struct udp_conn_s *udp_alloc(uint8_t domain) #if CONFIG_NET_RECV_BUFSIZE > 0 conn->rcvbufs = CONFIG_NET_RECV_BUFSIZE; #endif +#if CONFIG_NET_SEND_BUFSIZE > 0 + conn->sndbufs = CONFIG_NET_SEND_BUFSIZE; + + nxsem_init(&conn->sndsem, 0, 0); + nxsem_set_protocol(&conn->sndsem, SEM_PRIO_NONE); +#endif #ifdef CONFIG_NET_UDP_WRITE_BUFFERS /* Initialize the write buffer lists */ @@ -641,6 +647,13 @@ void udp_free(FAR struct udp_conn_s *conn) { udp_wrbuffer_release(wrbuffer); } + +#if CONFIG_NET_SEND_BUFSIZE > 0 + /* Notify the send buffer available */ + + udp_sendbuffer_notify(conn); +#endif /* CONFIG_NET_SEND_BUFSIZE */ + #endif /* Free the connection */ diff --git a/net/udp/udp_sendto_buffered.c b/net/udp/udp_sendto_buffered.c index 899b3d5..9f8840b 100644 --- a/net/udp/udp_sendto_buffered.c +++ b/net/udp/udp_sendto_buffered.c @@ -119,6 +119,7 @@ static uint16_t sendto_eventhandler(FAR struct net_driver_s *dev, * ****************************************************************************/ +#if CONFIG_NET_SEND_BUFSIZE > 0 static uint32_t udp_inqueue_wrb_size(FAR struct udp_conn_s *conn) { FAR struct udp_wrbuffer_s *wrb; @@ -136,6 +137,7 @@ static uint32_t udp_inqueue_wrb_size(FAR struct udp_conn_s *conn) return total; } +#endif /* CONFIG_NET_SEND_BUFSIZE */ /**************************************************************************** * Name: sendto_writebuffer_release @@ -202,6 +204,12 @@ static void sendto_writebuffer_release(FAR struct socket *psock, } } while (wrb != NULL && ret < 0); + +#if CONFIG_NET_SEND_BUFSIZE > 0 + /* Notify the send buffer available if wrbbuffer drained */ + + udp_sendbuffer_notify(conn); +#endif /* CONFIG_NET_SEND_BUFSIZE */ } /**************************************************************************** @@ -657,6 +665,23 @@ ssize_t psock_udp_sendto(FAR struct socket *psock, FAR const void *buf, { net_lock(); +#if CONFIG_NET_SEND_BUFSIZE > 0 + /* If the send buffer size exceeds the send limit, + * wait for the write buffer to be released + */ + + while (udp_inqueue_wrb_size(conn) + len > conn->sndbufs) + { + if (nonblock) + { + ret = -EAGAIN; + goto errout_with_lock; + } + + net_lockedwait_uninterruptible(&conn->sndsem); + } +#endif /* CONFIG_NET_SEND_BUFSIZE */ + /* Allocate a write buffer. Careful, the network will be momentarily * unlocked here. */ @@ -861,4 +886,32 @@ int psock_udp_cansend(FAR struct socket *psock) return OK; } + +/**************************************************************************** + * Name: udp_sendbuffer_notify + * + * Description: + * Notify the send buffer semaphore + * + * Input Parameters: + * conn - The UDP connection of interest + * + * Assumptions: + * Called from user logic with the network locked. + * + ****************************************************************************/ + +#if CONFIG_NET_SEND_BUFSIZE > 0 +void udp_sendbuffer_notify(FAR struct udp_conn_s *conn) +{ + int val = 0; + + nxsem_get_value(&conn->sndsem, &val); + if (val < 0) + { + nxsem_post(&conn->sndsem); + } +} +#endif /* CONFIG_NET_SEND_BUFSIZE */ + #endif /* CONFIG_NET && CONFIG_NET_UDP && CONFIG_NET_UDP_WRITE_BUFFERS */
