You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2021/07/21 03:25:38 UTC

[incubator-nuttx] 02/02: net/socket: add SO_SNDBUF support

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 <an...@xiaomi.com>
AuthorDate: Mon Jul 19 21:45:46 2021 +0800

    net/socket: add SO_SNDBUF support
    
    Signed-off-by: chao.an <an...@xiaomi.com>
---
 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 */