From fef255e5be8f2956c8abb99f00aa076668336b66 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Mon, 22 Jan 2018 18:32:02 -0600 Subject: [PATCH] This commit adds an as-of-yet untested implemented of UDP write buffering. Squashed commit of the following: net/udp: Address most of the issues with UDP write buffering. There is a remaining issue with handling one network going down in a multi-network environment. None of this has been test but it is certainly ready for test. Hence, the feature is marked EXPERIMENTAL. net/udp: Some baby steps toward a corrected write buffering design. net/udp: Remove pesky write buffer macros. Eliminate trailing space at the end of lines. net/udp: A little more UDP write buffering logic. Still at least on big gaping hole in the design. net/udp: Undefined CONFIG_NET_SENDTO_TIMEOUT. net/udp: Crude, naive port of the TCP write buffering logic into UDP. This commit is certainly non-functional and is simply a starting point for the implementatin of UDP write buffering. net/udp: Rename udp/udp_psock_sendto.c udp/udp_psock_sendto_unbuffered.c. --- include/nuttx/net/net.h | 4 +- mm/iob/Kconfig | 14 +- net/Kconfig | 8 + net/inet/inet_sockif.c | 2 +- net/net_initialize.c | 4 + net/socket/socket.c | 2 +- net/tcp/Kconfig | 4 +- net/udp/Kconfig | 50 ++ net/udp/Make.defs | 19 +- net/udp/udp.h | 129 ++- net/udp/udp_conn.c | 21 +- net/udp/udp_psock_sendto_buffered.c | 821 ++++++++++++++++++ ...sendto.c => udp_psock_sendto_unbuffered.c} | 11 +- net/udp/udp_wrbuffer.c | 217 +++++ net/udp/udp_wrbuffer_dump.c | 70 ++ 15 files changed, 1354 insertions(+), 22 deletions(-) create mode 100644 net/udp/udp_psock_sendto_buffered.c rename net/udp/{udp_psock_sendto.c => udp_psock_sendto_unbuffered.c} (98%) create mode 100644 net/udp/udp_wrbuffer.c create mode 100644 net/udp/udp_wrbuffer_dump.c diff --git a/include/nuttx/net/net.h b/include/nuttx/net/net.h index fb4de5c473..9e1eec09db 100644 --- a/include/nuttx/net/net.h +++ b/include/nuttx/net/net.h @@ -208,8 +208,8 @@ struct socket FAR const struct sock_intf_s *s_sockif; -#ifdef CONFIG_NET_TCP_WRITE_BUFFERS - /* Callback instance for TCP send */ +#if defined(CONFIG_NET_TCP_WRITE_BUFFERS) || defined(CONFIG_NET_UDP_WRITE_BUFFERS) + /* Callback instance for TCP send() or UDP sendto() */ FAR struct devif_callback_s *s_sndcb; #endif diff --git a/mm/iob/Kconfig b/mm/iob/Kconfig index b64f90e3ce..62893db094 100644 --- a/mm/iob/Kconfig +++ b/mm/iob/Kconfig @@ -16,9 +16,9 @@ if MM_IOB config IOB_NBUFFERS int "Number of pre-allocated I/O buffers" - default 24 if (NET_TCP_WRITE_BUFFERS && !NET_TCP_READAHEAD) || (!NET_TCP_WRITE_BUFFERS && NET_TCP_READAHEAD) - default 36 if NET_TCP_WRITE_BUFFERS && NET_TCP_READAHEAD - default 8 if !NET_TCP_WRITE_BUFFERS && !NET_TCP_READAHEAD + default 24 if (NET_WRITE_BUFFERS && !NET_READAHEAD) || (!NET_WRITE_BUFFERS && NET_READAHEAD) + default 36 if NET_WRITE_BUFFERS && NET_READAHEAD + default 8 if !NET_WRITE_BUFFERS && !NET_READAHEAD ---help--- Each packet is represented by a series of small I/O buffers in a chain. This setting determines the number of preallocated I/O @@ -34,8 +34,8 @@ config IOB_BUFSIZE config IOB_NCHAINS int "Number of pre-allocated I/O buffer chain heads" - default 0 if !NET_TCP_READAHEAD && !NET_UDP_READAHEAD - default 8 if NET_TCP_READAHEAD || NET_UDP_READAHEAD + default 0 if !NET_READAHEAD && !NET_UDP_READAHEAD + default 8 if NET_READAHEAD || NET_UDP_READAHEAD ---help--- These tiny nodes are used as "containers" to support queueing of I/O buffer chains. This will limit the number of I/O transactions @@ -49,8 +49,8 @@ config IOB_NCHAINS config IOB_THROTTLE int "I/O buffer throttle value" - default 0 if !NET_TCP_WRITE_BUFFERS || !NET_TCP_READAHEAD - default 8 if NET_TCP_WRITE_BUFFERS && NET_TCP_READAHEAD + default 0 if !NET_WRITE_BUFFERS || !NET_READAHEAD + default 8 if NET_WRITE_BUFFERS && NET_READAHEAD ---help--- TCP write buffering and read-ahead buffer use the same pool of free I/O buffers. In order to prevent uncontrolled incoming TCP packets diff --git a/net/Kconfig b/net/Kconfig index 48163d26bf..3c4f969486 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -11,6 +11,14 @@ config ARCH_HAVE_PHY bool default n +config NET_WRITE_BUFFERS + bool + default n + +config NET_READAHEAD + bool + default n + config NET bool "Networking support" default n diff --git a/net/inet/inet_sockif.c b/net/inet/inet_sockif.c index 761a65bb1b..9ab4be7336 100644 --- a/net/inet/inet_sockif.c +++ b/net/inet/inet_sockif.c @@ -1146,7 +1146,7 @@ static ssize_t inet_sendto(FAR struct socket *psock, FAR const void *buf, #if defined(CONFIG_NET_6LOWPAN) /* Try 6LoWPAN UDP packet sendto() */ - nsent = psock_6lowpan_udp_sendto(psock, buf, len, flags, to, tolen); + nsent = psock_6lowpan_udp_sendto(psock, buf, len, flags, to, minlen); #ifdef NET_UDP_HAVE_STACK if (nsent < 0) diff --git a/net/net_initialize.c b/net/net_initialize.c index 4d0105acf7..0447f88dfc 100644 --- a/net/net_initialize.c +++ b/net/net_initialize.c @@ -172,6 +172,10 @@ void net_setup(void) /* Initialize the UDP connection structures */ udp_initialize(); + +#ifdef CONFIG_NET_UDP_WRITE_BUFFERS + udp_wrbuffer_initialize(); +#endif #endif #ifdef CONFIG_NET_IGMP diff --git a/net/socket/socket.c b/net/socket/socket.c index 539ef0b216..caaed28320 100644 --- a/net/socket/socket.c +++ b/net/socket/socket.c @@ -105,7 +105,7 @@ int psock_socket(int domain, int type, int protocol, FAR struct socket *psock) psock->s_domain = domain; psock->s_type = type; psock->s_conn = NULL; -#ifdef CONFIG_NET_TCP_WRITE_BUFFERS +#if defined(CONFIG_NET_TCP_WRITE_BUFFERS) || defined(CONFIG_NET_UDP_WRITE_BUFFERS) psock->s_sndcb = NULL; #endif diff --git a/net/tcp/Kconfig b/net/tcp/Kconfig index 77cebe81fb..c3647ae289 100644 --- a/net/tcp/Kconfig +++ b/net/tcp/Kconfig @@ -70,6 +70,7 @@ config NET_MAX_LISTENPORTS config NET_TCP_READAHEAD bool "Enable TCP/IP read-ahead buffering" default y + select NET_READAHEAD select MM_IOB ---help--- Read-ahead buffers allows buffering of TCP/IP packets when there is no @@ -91,6 +92,7 @@ endif # NET_TCP_READAHEAD config NET_TCP_WRITE_BUFFERS bool "Enable TCP/IP write buffering" default n + select NET_WRITE_BUFFERS select MM_IOB ---help--- Write buffers allows buffering of ongoing TCP/IP packets, providing @@ -105,7 +107,7 @@ config NET_TCP_NWRBCHAINS int "Number of pre-allocated I/O buffer chain heads" default 8 ---help--- - These tiny nodes are used as "containers" to support queueing of + These tiny nodes are used as "containers" to support queuing of TCP write buffers. This setting will limit the number of TCP write operations that can be "in-flight" at any give time. So a good choice for this value would be the same as the maximum number of diff --git a/net/udp/Kconfig b/net/udp/Kconfig index ba710b8629..16d40a72ef 100644 --- a/net/udp/Kconfig +++ b/net/udp/Kconfig @@ -44,7 +44,57 @@ config NET_BROADCAST config NET_UDP_READAHEAD bool "Enable UDP/IP read-ahead buffering" default y + select NET_READAHEAD select MM_IOB +config NET_UDP_WRITE_BUFFERS + bool "Enable UDP/IP write buffering" + default n + select NET_WRITE_BUFFERS + select MM_IOB + depends on EXPERIMENTAL + ---help--- + Write buffers allows buffering of ongoing UDP/IP packets, providing + for higher performance, streamed output. + + You might want to disable UDP/IP write buffering on a highly memory + memory constrained system where there are no performance issues. + +if NET_UDP_WRITE_BUFFERS + +config NET_UDP_NWRBCHAINS + int "Number of pre-allocated I/O buffer chain heads" + default 8 + ---help--- + These tiny nodes are used as "containers" to support queuing of + UDP write buffers. This setting will limit the number of UDP write + operations that can be "in-flight" at any give time. So a good + choice for this value would be the same as the maximum number of + UDP connections. + +config NET_UDP_WRBUFFER_DEBUG + bool "Force write buffer debug" + default n + depends on DEBUG_FEATURES + select IOB_DEBUG + ---help--- + This option will force debug output from UDP write buffer logic, + even without network debug output. This is not normally something + that would want to do but is convenient if you are debugging the + write buffer logic and do not want to get overloaded with other + network-related debug output. + +config NET_UDP_WRBUFFER_DUMP + bool "Force write buffer dump" + default n + depends on DEBUG_NET || NET_UDP_WRBUFFER_DEBUG + select IOB_DEBUG + ---help--- + Dump the contents of the write buffers. You do not want to do this + unless you really want to analyze the write buffer transfers in + detail. + +endif # NET_UDP_WRITE_BUFFERS + endif # NET_UDP && !NET_UDP_NO_STACK endmenu # UDP Networking diff --git a/net/udp/Make.defs b/net/udp/Make.defs index 4b3992911d..5c8918ea9a 100644 --- a/net/udp/Make.defs +++ b/net/udp/Make.defs @@ -1,7 +1,7 @@ ############################################################################ # net/udp/Make.defs # -# Copyright (C) 2014-2015 Gregory Nutt. All rights reserved. +# Copyright (C) 2014-2015, 2018 Gregory Nutt. All rights reserved. # Author: Gregory Nutt # # Redistribution and use in source and binary forms, with or without @@ -40,7 +40,13 @@ ifneq ($(CONFIG_NET_UDP_NO_STACK),y) # Socket layer -NET_CSRCS += udp_psock_send.c udp_psock_sendto.c +NET_CSRCS += udp_psock_send.c + +ifeq ($(CONFIG_NET_UDP_WRITE_BUFFERS),y) +SOCK_CSRCS += udp_psock_sendto_buffered.c +else +SOCK_CSRCS += udp_psock_sendto_unbuffered.c +endif ifneq ($(CONFIG_DISABLE_POLL),y) ifeq ($(CONFIG_NET_UDP_READAHEAD),y) @@ -53,6 +59,15 @@ endif NET_CSRCS += udp_conn.c udp_devpoll.c udp_send.c udp_input.c udp_finddev.c NET_CSRCS += udp_callback.c udp_ipselect.c +# UDP write buffering + +ifeq ($(CONFIG_NET_UDP_WRITE_BUFFERS),y) +NET_CSRCS += udp_wrbuffer.c +ifeq ($(CONFIG_DEBUG_FEATURES),y) +NET_CSRCS += udp_wrbuffer_dump.c +endif +endif + # Include UDP build support DEPPATH += --dep-path udp diff --git a/net/udp/udp.h b/net/udp/udp.h index cb6ca378f5..ee591ed078 100644 --- a/net/udp/udp.h +++ b/net/udp/udp.h @@ -1,7 +1,7 @@ /**************************************************************************** * net/udp/udp.h * - * Copyright (C) 2014-2015 Gregory Nutt. All rights reserved. + * Copyright (C) 2014-2015, 2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -45,6 +45,7 @@ #include #include +#include #include #ifdef CONFIG_NET_UDP_READAHEAD @@ -66,6 +67,17 @@ # define HAVE_UDP_POLL #endif +#ifdef CONFIG_NET_UDP_WRITE_BUFFERS +/* UDP write buffer dump macros */ + +# ifdef CONFIG_DEBUG_FEATURES +# define UDPWB_DUMP(msg,wrb,len,offset) \ + udp_wrbuffer_dump(msg,wrb,len,offset) +# else +# define UDPWB_DUMP(msg,wrb,len,offset) +# endif +#endif + /* Allocate a new UDP data callback */ #define udp_callback_alloc(dev,conn) \ @@ -102,11 +114,38 @@ struct udp_conn_s struct iob_queue_s readahead; /* Read-ahead buffering */ #endif +#ifdef CONFIG_NET_TCP_WRITE_BUFFERS + /* Write buffering + * + * write_q - The queue of unsent I/O buffers. The head of this + * list may be partially sent. FIFO ordering. + */ + + sq_queue_t write_q; /* Write buffering for UDP packets */ + FAR struct net_driver_s *dev; /* Last device */ +#endif + /* Defines the list of UDP callbacks */ FAR struct devif_callback_s *list; }; +/* This structure supports UDP write buffering. It is simply a container + * for a IOB list and associated destination address. + */ + +#ifdef CONFIG_NET_UDP_WRITE_BUFFERS +struct udp_wrbuffer_s +{ + sq_entry_t wb_node; /* Supports a singly linked list */ + union ip_addr_u wb_dest; /* Destination address */ +#ifdef CONFIG_NET_SOCKOPTS + systime_t wb_start; /* Start time for timeout calculation */ +#endif + struct iob_s *wb_iob; /* Head of the I/O buffer chain */ +}; +#endif + /**************************************************************************** * Public Data ****************************************************************************/ @@ -291,6 +330,92 @@ void udp_poll(FAR struct net_driver_s *dev, FAR struct udp_conn_s *conn); void udp_send(FAR struct net_driver_s *dev, FAR struct udp_conn_s *conn); +/**************************************************************************** + * Name: udp_wrbuffer_initialize + * + * Description: + * Initialize the list of free write buffers + * + * Assumptions: + * Called once early initialization. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_UDP_WRITE_BUFFERS +void udp_wrbuffer_initialize(void); +#endif /* CONFIG_NET_UDP_WRITE_BUFFERS */ + +/**************************************************************************** + * Name: udp_wrbuffer_alloc + * + * Description: + * Allocate a UDP write buffer by taking a pre-allocated buffer from + * the free list. This function is called from UDP logic when a buffer + * of UDP data is about to sent + * + * Input parameters: + * None + * + * Assumptions: + * Called from user logic with the network locked. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_UDP_WRITE_BUFFERS +struct udp_wrbuffer_s; + +FAR struct udp_wrbuffer_s *udp_wrbuffer_alloc(void); +#endif /* CONFIG_NET_UDP_WRITE_BUFFERS */ + +/**************************************************************************** + * Name: udp_wrbuffer_release + * + * Description: + * Release a UDP write buffer by returning the buffer to the free list. + * This function is called from user logic after it is consumed the + * buffered data. + * + * Assumptions: + * Called from network stack logic with the network stack locked + * + ****************************************************************************/ + +#ifdef CONFIG_NET_UDP_WRITE_BUFFERS +void udp_wrbuffer_release(FAR struct udp_wrbuffer_s *wrb); +#endif /* CONFIG_NET_UDP_WRITE_BUFFERS */ + +/**************************************************************************** + * Name: udp_wrbuffer_test + * + * Description: + * Check if there is room in the write buffer. Does not reserve any space. + * + * Assumptions: + * None. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_UDP_WRITE_BUFFERS +int udp_wrbuffer_test(void); +#endif /* CONFIG_NET_UDP_WRITE_BUFFERS */ + +/**************************************************************************** + * Name: udp_wrbuffer_dump + * + * Description: + * Dump the contents of a write buffer. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_UDP_WRITE_BUFFERS +#ifdef CONFIG_DEBUG_FEATURES +void udp_wrbuffer_dump(FAR const char *msg, FAR struct udp_wrbuffer_s *wrb, + unsigned int len, unsigned int offset); +#else +# define udp_wrbuffer_dump(msg,wrb) +#endif +#endif /* CONFIG_NET_UDP_WRITE_BUFFERS */ + /**************************************************************************** * Name: udp_ipv4_input * @@ -495,7 +620,7 @@ int udp_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds); * Teardown monitoring of events on an UDP/IP socket * * Input Parameters: - * psock - The TCP/IP socket of interest + * psock - The UDP/IP socket of interest * fds - The structure describing the events to be monitored, OR NULL if * this is a request to stop monitoring events. * diff --git a/net/udp/udp_conn.c b/net/udp/udp_conn.c index 97037c444f..61e67028f6 100644 --- a/net/udp/udp_conn.c +++ b/net/udp/udp_conn.c @@ -1,7 +1,8 @@ /**************************************************************************** * net/udp/udp_conn.c * - * Copyright (C) 2007-2009, 2011-2012, 2016 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2009, 2011-2012, 2016, 2018 Gregory Nutt. All rights + * reserved. * Author: Gregory Nutt * * Large parts of this file were leveraged from uIP logic: @@ -485,6 +486,11 @@ FAR struct udp_conn_s *udp_alloc(uint8_t domain) conn->lport = 0; conn->ttl = IP_TTL; +#ifdef CONFIG_NET_TCP_WRITE_BUFFERS + /* Initialize the write buffer lists */ + + sq_init(&conn->write_q); +#endif /* Enqueue the connection into the active list */ dq_addlast(&conn->node, &g_active_udp_connections); @@ -505,6 +511,10 @@ FAR struct udp_conn_s *udp_alloc(uint8_t domain) void udp_free(FAR struct udp_conn_s *conn) { +#ifdef CONFIG_NET_UDP_WRITE_BUFFERS + FAR struct udp_wrbuffer_s *wrbuffer; +#endif + /* The free list is protected by a semaphore (that behaves like a mutex). */ DEBUGASSERT(conn->crefs == 0); @@ -522,6 +532,15 @@ void udp_free(FAR struct udp_conn_s *conn) iob_free_queue(&conn->readahead); #endif +#ifdef CONFIG_NET_UDP_WRITE_BUFFERS + /* Release any write buffers attached to the connection */ + + while ((wrbuffer = (struct udp_wrbuffer_s *)sq_remfirst(&conn->write_q)) != NULL) + { + udp_wrbuffer_release(wrbuffer); + } +#endif + /* Free the connection */ dq_addlast(&conn->node, &g_free_udp_connections); diff --git a/net/udp/udp_psock_sendto_buffered.c b/net/udp/udp_psock_sendto_buffered.c new file mode 100644 index 0000000000..3f5e5305b4 --- /dev/null +++ b/net/udp/udp_psock_sendto_buffered.c @@ -0,0 +1,821 @@ +/**************************************************************************** + * net/udp/udp_send_buffered.c + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#if defined(CONFIG_NET) && defined(CONFIG_NET_UDP) && \ + defined(CONFIG_NET_UDP_WRITE_BUFFERS) + +#if defined(CONFIG_DEBUG_FEATURES) && defined(CONFIG_NET_UDP_WRBUFFER_DEBUG) +/* Force debug output (from this file only) */ + +# undef CONFIG_DEBUG_NET +# define CONFIG_DEBUG_NET 1 +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "netdev/netdev.h" +#include "socket/socket.h" +#include "inet/inet.h" +#include "arp/arp.h" +#include "icmpv6/icmpv6.h" +#include "neighbor/neighbor.h" +#include "udp/udp.h" +#include "devif/devif.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* If both IPv4 and IPv6 support are both enabled, then we will need to build + * in some additional domain selection support. + */ + +#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6) +# define NEED_IPDOMAIN_SUPPORT 1 +#endif + +#define UDPIPv4BUF ((struct udp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv4_HDRLEN]) +#define UDPIPv6BUF ((struct udp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN]) + +/* Debug */ + +#ifdef CONFIG_NET_UDP_WRBUFFER_DUMP +# define BUF_DUMP(msg,buf,len) lib_dumpbuffer(msg,buf,len) +#else +# define BUF_DUMP(msg,buf,len) +# undef UDPWB_DUMP +# define UDPWB_DUMP(msg,wrb,len,offset) +#endif + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static inline void sendto_netdev_down(FAR struct socket *psock, + FAR struct udp_conn_s *conn); + +#ifdef NEED_IPDOMAIN_SUPPORT +static inline void sendto_ipselect(FAR struct net_driver_s *dev, + FAR struct udp_conn_s *conn); +#endif +#ifdef CONFIG_NET_ETHERNET +static inline bool sendto_addrcheck(FAR struct udp_conn_s *conn, + FAR struct net_driver_s *dev); +#else +# define sendto_addrcheck(c,d) (true) +#endif +#ifdef CONFIG_NET_SOCKOPTS +static inline int sendto_timeout(FAR struct socket *psock, + FAR struct udp_conn_s *conn); +#endif +static int sendto_next_transfer(FAR struct socket *psock, + FAR struct udp_conn_s *conn); +static uint16_t sendto_eventhandler(FAR struct net_driver_s *dev, + FAR void *pvconn, FAR void *pvpriv, + uint16_t flags); +static inline void sendto_txnotify(FAR struct socket *psock, + FAR struct udp_conn_s *conn); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sendto_netdev_down + * + * Description: + * The network device is down. Free all write buffers. + * REVISIT: Should only free write buffers associated with the device + * that went down. + * + * Parameters: + * psock The socket structure + * conn The connection structure associated with the socket + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void sendto_netdev_down(FAR struct socket *psock, + FAR struct udp_conn_s *conn) +{ + FAR sq_entry_t *entry; + FAR sq_entry_t *next; + + /* Do not allow any further callbacks */ + + if (psock->s_sndcb != NULL) + { + psock->s_sndcb->flags = 0; + psock->s_sndcb->event = NULL; + } + + /* Free all queued write buffers */ + + for (entry = sq_peek(&conn->write_q); entry; entry = next) + { + next = sq_next(entry); + udp_wrbuffer_release((FAR struct udp_wrbuffer_s *)entry); + } + + /* Reset write buffering variables */ + + sq_init(&conn->write_q); +} + +/**************************************************************************** + * Name: sendto_ipselect + * + * Description: + * If both IPv4 and IPv6 support are enabled, then we will need to select + * which one to use when generating the outgoing packet. If only one + * domain is selected, then the setup is already in place and we need do + * nothing. + * + * Parameters: + * dev - The structure of the network driver that caused the event + * psock - Socket state structure + * + * Returned Value: + * None + * + * Assumptions: + * The network is locked + * + ****************************************************************************/ + +#ifdef NEED_IPDOMAIN_SUPPORT +static inline void sendto_ipselect(FAR struct net_driver_s *dev, + FAR struct udp_conn_s *conn) +{ + /* Which domain the socket support */ + + if (conn->domain == PF_INET) + { + /* Select the IPv4 domain */ + + udp_ipv4_select(dev); + } + else /* if (conn->domain == PF_INET6) */ + { + /* Select the IPv6 domain */ + + DEBUGASSERT(conn->domain == PF_INET6); + udp_ipv6_select(dev); + } +} +#endif + +/**************************************************************************** + * Name: sendto_addrcheck + * + * Description: + * Check if the destination IP address is in the IPv4 ARP or IPv6 Neighbor + * tables. If not, then the send won't actually make it out... it will be + * replaced with an ARP request (IPv4) or a Neighbor Solicitation (IPv6). + * + * NOTE 1: This could be an expensive check if there are a lot of + * entries in the ARP or Neighbor tables. + * + * NOTE 2: If we are actually harvesting IP addresses on incoming IP + * packets, then this check should not be necessary; the MAC mapping + * should already be in the ARP table in many cases (IPv4 only). + * + * NOTE 3: If CONFIG_NET_ARP_SEND then we can be assured that the IP + * address mapping is already in the ARP table. + * + * Parameters: + * conn - The UDP connection structure + * dev - Polling network device + * + * Returned Value: + * true - The Ethernet MAC address is in the ARP or Neighbor table (OR + * the network device is not Ethernet). + * + * Assumptions: + * The network is locked + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ETHERNET +static inline bool sendto_addrcheck(FAR struct udp_conn_s *conn, + FAR struct net_driver_s *dev) +{ + /* REVISIT: Could the MAC address not also be in a routing table? */ + + if (dev->d_lltype != NET_LL_ETHERNET) + { + return true; + } + +#ifdef CONFIG_NET_IPv4 +#ifdef CONFIG_NET_IPv6 + if (conn->domain == PF_INET) +#endif + { +#if !defined(CONFIG_NET_ARP_IPIN) && !defined(CONFIG_NET_ARP_SEND) + return (arp_find(conn->u.ipv4.raddr) != NULL); +#else + return true; +#endif + } +#endif /* CONFIG_NET_IPv4 */ + +#ifdef CONFIG_NET_IPv6 +#ifdef CONFIG_NET_IPv4 + else +#endif + { +#if !defined(CONFIG_NET_ICMPv6_NEIGHBOR) + return (neighbor_findentry(conn->u.ipv6.raddr) != NULL); +#else + return true; +#endif + } +#endif /* CONFIG_NET_IPv6 */ +} +#endif /* CONFIG_NET_ETHERNET */ + +/**************************************************************************** + * Name: sendto_timeout + * + * Description: + * Check for send timeout. + * + * Parameters: + * pstate - sendto state structure + * + * Returned Value: + * TRUE:timeout FALSE:no timeout + * + * Assumptions: + * The network is locked + * + ****************************************************************************/ + +#ifdef CONFIG_NET_SOCKOPTS +static inline int sendto_timeout(FAR struct socket *psock, + FAR struct udp_conn_s *conn) +{ + FAR struct udp_wrbuffer_s *wrb; + + /* Peek at the head of the write queue (without altering the write queue). */ + + wrb = (FAR struct udp_wrbuffer_s *)sq_peek(&conn->write_q); + if (wrb != NULL) + { + /* Check for a timeout configured via setsockopts(SO_SNDTIMEO). + * If none... we well let the send wait forever. + */ + + if (psock->s_sndtimeo != 0) + { + /* Check if the configured timeout has elapsed */ + + return net_timeo(wrb->wb_start, psock->s_sndtimeo); + } + } + + /* No timeout */ + + return FALSE; +} +#endif /* CONFIG_NET_SOCKOPTS */ + +/**************************************************************************** + * Name: sendto_next_transfer + * + * Description: + * Setup for the next packet transfer + * + * Parameters: + * psock - Socket state structure + * conn - The UDP connection structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static int sendto_next_transfer(FAR struct socket *psock, + FAR struct udp_conn_s *conn) +{ + FAR struct udp_wrbuffer_s *wrb; + FAR struct net_driver_s *dev; + int ret; + + /* Set the UDP "connection" to the destination address of the write buffer + * at the head of the queue. + */ + + wrb = (FAR struct udp_wrbuffer_s *)sq_peek(&conn->write_q); + DEBUGASSERT(wrb != NULL); + + ret = udp_connect(conn, (FAR const struct sockaddr *)wrb->wb_iob->io_data); + if (ret < 0) + { + nerr("ERROR: udp_connect failed: %d\n", ret); + return ret; + } + + /* Get the device that will handle the remote packet transfers. This + * should never be NULL. + */ + + dev = udp_find_raddr_device(conn); + if (dev == NULL) + { + nerr("ERROR: udp_find_raddr_device failed\n"); + return -ENETUNREACH; + } + + /* If this is not the same device that we used in the last call to + * udp_callback_alloc(), then we need to release and reallocate the old + * callback instance. + */ + + if (psock->s_sndcb != NULL && conn->dev != dev) + { + udp_callback_free(conn->dev, conn, psock->s_sndcb); + psock->s_sndcb = NULL; + } + + /* Allocate resources to receive a callback from this device if the + * callback is not already in place. + */ + + if (psock->s_sndcb == NULL) + { + psock->s_sndcb = udp_callback_alloc(dev, conn); + } + + /* Test if the callback has been allocated */ + + if (psock->s_sndcb == NULL) + { + /* A buffer allocation error occurred */ + + nerr("ERROR: Failed to allocate callback\n"); + return -ENOMEM; + } + + conn->dev = dev; + + /* Set up the callback in the connection */ + + psock->s_sndcb->flags = (UDP_POLL | NETDEV_DOWN); + psock->s_sndcb->priv = (FAR void *)psock; + psock->s_sndcb->event = sendto_eventhandler; + return OK; +} + +/**************************************************************************** + * Name: sendto_eventhandler + * + * Description: + * This function is called to perform the actual send operation when + * polled by the lower, device interfacing layer. + * + * Parameters: + * dev The structure of the network driver that caused the event + * conn The connection structure associated with the socket + * flags Set of events describing why the callback was invoked + * + * Returned Value: + * None + * + * Assumptions: + * The network is locked + * + ****************************************************************************/ + +static uint16_t sendto_eventhandler(FAR struct net_driver_s *dev, + FAR void *pvconn, FAR void *pvpriv, + uint16_t flags) +{ + FAR struct udp_conn_s *conn = (FAR struct udp_conn_s *)pvconn; + FAR struct socket *psock = (FAR struct socket *)pvpriv; + int ret; + + ninfo("flags: %04x\n", flags); + + /* Check if the network device has gone down */ + + if ((flags & NETDEV_DOWN) != 0) + { + ninfo("Device down: %04x\n", flags); + + /* Free write buffers and terminate polling */ + + sendto_netdev_down(psock, conn); + return flags; + } + + /* Check for a normal polling cycle and if the outgoing packet is + * available. It would not be available if it has been claimed by a send + * event serving a different thread -OR- if the output buffer currently + * contains unprocessed incoming data. In these cases we will just have + * to wait for the next polling cycle. + * + * And, of course, we can do nothing if we have no data in the write + * buffers to send. + */ + + if (dev->d_sndlen <= 0 && (flags & UDP_NEWDATA) != 0 && + (flags & UDP_POLL) != 0 && !sq_empty(&conn->write_q)) + { + /* Check if the destination IP address is in the ARP or Neighbor + * table. If not, then the send won't actually make it out... it + * will be replaced with an ARP request or Neighbor Solicitation. + */ + + if (sendto_addrcheck(conn, dev)) + { + FAR struct udp_wrbuffer_s *wrb; + FAR struct udp_wrbuffer_s *tmp; + size_t sndlen; + + /* Peek at the head of the write queue (but don't remove anything + * from the write queue yet). We know from the above test that + * the write_q is not empty. + */ + + wrb = (FAR struct udp_wrbuffer_s *)sq_peek(&conn->write_q); + DEBUGASSERT(wrb != NULL); + + /* Get the amount of data that we can send in the next packet. + * We will send either the remaining data in the buffer I/O + * buffer chain, or as much as will fit given the MSS and current + * window size. + */ + + sndlen = wrb->wb_iob->io_pktlen; + ninfo("wrb=%p sndlen=%u\n", wrb, sndlen); + +#ifdef NEED_IPDOMAIN_SUPPORT + /* If both IPv4 and IPv6 support are enabled, then we will need to + * select which one to use when generating the outgoing packet. + * If only one domain is selected, then the setup is already in + * place and we need do nothing. + */ + + sendto_ipselect(dev, conn); +#endif + /* Then set-up to send that amount of data with the offset + * corresponding to the size of the IP-dependent address structure. + */ + + devif_iob_send(dev, wrb->wb_iob, sndlen, 0); + + /* Remove and free the write buffer from the head of the write + * buffer queue. + */ + + do + { + tmp = (FAR struct udp_wrbuffer_s *)sq_remfirst(&conn->write_q); + DEBUGASSERT(tmp == wrb); + + udp_wrbuffer_release(tmp); + + /* Check if the write queue became empty */ + + if (sq_empty(&conn->write_q)) + { + /* Yes.. stifle any further callbacks until more write + * data is enqueued. + */ + + psock->s_sndcb->flags = 0; + psock->s_sndcb->priv = NULL; + psock->s_sndcb->event = NULL; + ret = OK; + } + else + { + /* Set up for the next packet transfer by setting the + * connection address to the address of the next packet + * now at the header of of the write buffer queue. + */ + + ret = sendto_next_transfer(psock, conn); + } + } + while (ret < 0); + + /* Only one data can be sent by low level driver at once, + * tell the caller stop polling the other connections. + */ + + flags &= ~UDP_POLL; + } + } + +#ifdef CONFIG_NET_SOCKOPTS + /* We cannot send the data now. Check for a timeout. */ + + else if (sendto_timeout(psock, conn)) + { + FAR struct udp_wrbuffer_s *wrb; + + do + { + /* Remove and free the write buffer from the head of the write + * buffer queue. Here we know that the write queue is not empty + * because the entry at the head of the queue just timed out! + */ + + wrb = (FAR struct udp_wrbuffer_s *)sq_remfirst(&conn->write_q); + DEBUGASSERT(wrb != NULL); + + udp_wrbuffer_release(wrb); + + /* Set up for the next packet transfer by setting the connection + * address to the address of the next packet now at the header of the + * write buffer queue. + */ + + ret = sendto_next_transfer(psock, conn); + } + while (ret < 0); + } +#endif /* CONFIG_NET_SOCKOPTS */ + + /* Continue waiting */ + + return flags; +} + +/**************************************************************************** + * Name: sendto_txnotify + * + * Description: + * Notify the appropriate device driver that we are have data ready to + * be send (UDP) + * + * Parameters: + * psock - Socket state structure + * conn - The UDP connection structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void sendto_txnotify(FAR struct socket *psock, + FAR struct udp_conn_s *conn) +{ +#ifdef CONFIG_NET_IPv4 +#ifdef CONFIG_NET_IPv6 + /* If both IPv4 and IPv6 support are enabled, then we will need to select + * the device driver using the appropriate IP domain. + */ + + if (psock->s_domain == PF_INET) +#endif + { + /* Notify the device driver that send data is available */ + + netdev_ipv4_txnotify(conn->u.ipv4.laddr, conn->u.ipv4.raddr); + } +#endif /* CONFIG_NET_IPv4 */ + +#ifdef CONFIG_NET_IPv6 +#ifdef CONFIG_NET_IPv4 + else /* if (psock->s_domain == PF_INET6) */ +#endif /* CONFIG_NET_IPv4 */ + { + /* Notify the device driver that send data is available */ + + DEBUGASSERT(psock->s_domain == PF_INET6); + netdev_ipv6_txnotify(conn->u.ipv6.laddr, conn->u.ipv6.raddr); + } +#endif /* CONFIG_NET_IPv6 */ +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: psock_udp_sendto + * + * Description: + * This function implements the UDP-specific logic of the standard + * sendto() socket operation. + * + * Input Parameters: + * psock A pointer to a NuttX-specific, internal socket structure + * buf Data to send + * len Length of data to send + * flags Send flags + * to Address of recipient + * tolen The length of the address structure + * + * NOTE: All input parameters were verified by sendto() before this + * function was called. + * + * Returned Value: + * On success, returns the number of characters sent. On error, + * a negated errno value is returned. See the description in + * net/socket/sendto.c for the list of appropriate return value. + * + ****************************************************************************/ + +ssize_t psock_udp_sendto(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags, FAR const struct sockaddr *to, + socklen_t tolen) +{ + FAR struct udp_conn_s *conn; + FAR struct udp_wrbuffer_s *wrb; + int ret = OK; + + /* Make sure that we have the IP address mapping */ + + conn = (FAR struct udp_conn_s *)psock->s_conn; + DEBUGASSERT(conn); + +#if defined(CONFIG_NET_ARP_SEND) || defined(CONFIG_NET_ICMPv6_NEIGHBOR) +#ifdef CONFIG_NET_ARP_SEND +#ifdef CONFIG_NET_ICMPv6_NEIGHBOR + if (psock->s_domain == PF_INET) +#endif + { + /* Make sure that the IP address mapping is in the ARP table */ + + ret = arp_send(conn->u.ipv4.raddr); + } +#endif /* CONFIG_NET_ARP_SEND */ + +#ifdef CONFIG_NET_ICMPv6_NEIGHBOR +#ifdef CONFIG_NET_ARP_SEND + else +#endif + { + /* Make sure that the IP address mapping is in the Neighbor Table */ + + ret = icmpv6_neighbor(conn->u.ipv6.raddr); + } +#endif /* CONFIG_NET_ICMPv6_NEIGHBOR */ + + /* Did we successfully get the address mapping? */ + + if (ret < 0) + { + nerr("ERROR: Not reachable\n"); + return -ENETUNREACH; + } +#endif /* CONFIG_NET_ARP_SEND || CONFIG_NET_ICMPv6_NEIGHBOR */ + + /* Dump the incoming buffer */ + + BUF_DUMP("psock_udp_send", buf, len); + + /* Set the socket state to sending */ + + psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_SEND); + + if (len > 0) + { + /* Allocate a write buffer. Careful, the network will be momentarily + * unlocked here. + */ + + net_lock(); + wrb = udp_wrbuffer_alloc(); + if (wrb == NULL) + { + /* A buffer allocation error occurred */ + + nerr("ERROR: Failed to allocate write buffer\n"); + ret = -ENOMEM; + goto errout_with_lock; + } + + /* Initialize the write buffer */ + + memcpy(&wrb->wb_dest, to, tolen); + wrb->wb_start = clock_systimer(); + + /* Copy the user data into the write buffer. We cannot wait for + * buffer space if the socket was opened non-blocking. + */ + + if (_SS_ISNONBLOCK(psock->s_flags)) + { + ret = iob_trycopyin(wrb->wb_iob, (FAR uint8_t *)buf, len, 0, false); + } + else + { + ret = iob_copyin(wrb->wb_iob, (FAR uint8_t *)buf, len, 0, false); + } + + if (ret < 0) + { + goto errout_with_wrb; + } + + /* Dump I/O buffer chain */ + + UDPWB_DUMP("I/O buffer chain", wrb, wrb->wb_iob->io_pktlen, 0); + + /* sendto_eventhandler() will send data in FIFO order from the + * conn->write_q + */ + + sq_addlast(&wrb->wb_node, &conn->write_q); + ninfo("Queued WRB=%p pktlen=%u write_q(%p,%p)\n", + wrb, wrb->wb_iob->io_pktlen, + conn->write_q.head, conn->write_q.tail); + + /* Set up for the next packet transfer by setting the connection + * address to the address of the next packet now at the header of the + * write buffer queue. + */ + + ret = sendto_next_transfer(psock, conn); + if (ret < 0) + { + /* REVISIT: An error here is not recoverable */ + + goto errout_with_lock; + } + + /* Notify the device driver of the availability of TX data */ + + sendto_txnotify(psock, conn); + net_unlock(); + } + + /* Set the socket state to idle */ + + psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_IDLE); + + /* Return the number of bytes that will be sent */ + + return len; + +errout_with_wrb: + udp_wrbuffer_release(wrb); + +errout_with_lock: + net_unlock(); + return ret; +} + +#endif /* CONFIG_NET && CONFIG_NET_UDP && CONFIG_NET_UDP_WRITE_BUFFERS */ diff --git a/net/udp/udp_psock_sendto.c b/net/udp/udp_psock_sendto_unbuffered.c similarity index 98% rename from net/udp/udp_psock_sendto.c rename to net/udp/udp_psock_sendto_unbuffered.c index ff362a310e..809ee2ce9e 100644 --- a/net/udp/udp_psock_sendto.c +++ b/net/udp/udp_psock_sendto_unbuffered.c @@ -1,7 +1,8 @@ /**************************************************************************** - * net/udp/udp_psock_sendto.c + * net/udp/udp_psock_sendto_unbuffered.c * - * Copyright (C) 2007-2009, 2011-2016 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2009, 2011-2016, 2018 Gregory Nutt. All rights + * reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -94,7 +95,7 @@ struct sendto_s ****************************************************************************/ /**************************************************************************** - * Name: send_timeout + * Name: sendto_timeout * * Description: * Check for send timeout. @@ -111,7 +112,7 @@ struct sendto_s ****************************************************************************/ #ifdef CONFIG_NET_SOCKOPTS -static inline int send_timeout(FAR struct sendto_s *pstate) +static inline int sendto_timeout(FAR struct sendto_s *pstate) { FAR struct socket *psock; @@ -235,7 +236,7 @@ static uint16_t sendto_eventhandler(FAR struct net_driver_s *dev, */ #ifdef CONFIG_NET_SOCKOPTS - if (send_timeout(pstate)) + if (sendto_timeout(pstate)) { /* Yes.. report the timeout */ diff --git a/net/udp/udp_wrbuffer.c b/net/udp/udp_wrbuffer.c new file mode 100644 index 0000000000..ae8fafd7ff --- /dev/null +++ b/net/udp/udp_wrbuffer.c @@ -0,0 +1,217 @@ +/**************************************************************************** + * net/udp/udp_wrbuffer.c + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#if defined(CONFIG_NET) && defined(CONFIG_NET_UDP) && defined(CONFIG_NET_UDP_WRITE_BUFFERS) + +#if defined(CONFIG_DEBUG_FEATURES) && defined(CONFIG_NET_UDP_WRBUFFER_DEBUG) +/* Force debug output (from this file only) */ + +# undef CONFIG_DEBUG_NET +# define CONFIG_DEBUG_NET 1 +#endif + +#include +#include +#include +#include + +#include +#include +#include + +#include "udp/udp.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Package all globals used by this logic into a structure */ + +struct wrbuffer_s +{ + /* The semaphore to protect the buffers */ + + sem_t sem; + + /* This is the list of available write buffers */ + + sq_queue_t freebuffers; + + /* These are the pre-allocated write buffers */ + + struct udp_wrbuffer_s buffers[CONFIG_NET_UDP_NWRBCHAINS]; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* This is the state of the global write buffer resource */ + +static struct wrbuffer_s g_wrbuffer; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: udp_wrbuffer_initialize + * + * Description: + * Initialize the list of free write buffers + * + * Assumptions: + * Called once early initialization. + * + ****************************************************************************/ + +void udp_wrbuffer_initialize(void) +{ + int i; + + sq_init(&g_wrbuffer.freebuffers); + + for (i = 0; i < CONFIG_NET_UDP_NWRBCHAINS; i++) + { + sq_addfirst(&g_wrbuffer.buffers[i].wb_node, &g_wrbuffer.freebuffers); + } + + nxsem_init(&g_wrbuffer.sem, 0, CONFIG_NET_UDP_NWRBCHAINS); +} + +/**************************************************************************** + * Name: udp_wrbuffer_alloc + * + * Description: + * Allocate a UDP write buffer by taking a pre-allocated buffer from + * the free list. This function is called from UDP logic when a buffer + * of UDP data is about to sent + * + * Input parameters: + * None + * + * Assumptions: + * Called from user logic with the network locked. + * + ****************************************************************************/ + +FAR struct udp_wrbuffer_s *udp_wrbuffer_alloc(void) +{ + FAR struct udp_wrbuffer_s *wrb; + + /* We need to allocate two things: (1) A write buffer structure and (2) + * at least one I/O buffer to start the chain. + * + * Allocate the write buffer structure first then the IOBG. In order to + * avoid deadlocks, we will need to free the IOB first, then the write + * buffer + */ + + DEBUGVERIFY(net_lockedwait(&g_wrbuffer.sem)); /* TODO: Handle EINTR. */ + + /* Now, we are guaranteed to have a write buffer structure reserved + * for us in the free list. + */ + + wrb = (FAR struct udp_wrbuffer_s *)sq_remfirst(&g_wrbuffer.freebuffers); + DEBUGASSERT(wrb); + memset(wrb, 0, sizeof(struct udp_wrbuffer_s)); + + /* Now get the first I/O buffer for the write buffer structure */ + + wrb->wb_iob = iob_alloc(false); + if (!wrb->wb_iob) + { + nerr("ERROR: Failed to allocate I/O buffer\n"); + udp_wrbuffer_release(wrb); + return NULL; + } + + return wrb; +} + +/**************************************************************************** + * Name: udp_wrbuffer_release + * + * Description: + * Release a UDP write buffer by returning the buffer to the free list. + * This function is called from user logic after it is consumed the + * buffered data. + * + * Assumptions: + * This function must be called with the network locked. + * + ****************************************************************************/ + +void udp_wrbuffer_release(FAR struct udp_wrbuffer_s *wrb) +{ + DEBUGASSERT(wrb && wrb->wb_iob); + + /* To avoid deadlocks, we must following this ordering: Release the I/O + * buffer chain first, then the write buffer structure. + */ + + iob_free_chain(wrb->wb_iob); + + /* Then free the write buffer structure */ + + sq_addlast(&wrb->wb_node, &g_wrbuffer.freebuffers); + nxsem_post(&g_wrbuffer.sem); +} + +/**************************************************************************** + * Name: udp_wrbuffer_test + * + * Description: + * Check if there is room in the write buffer. Does not reserve any space. + * + * Assumptions: + * None. + * + ****************************************************************************/ + +int udp_wrbuffer_test(void) +{ + int val = 0; + nxsem_getvalue(&g_wrbuffer.sem, &val); + return val > 0 ? OK : ERROR; +} + +#endif /* CONFIG_NET && CONFIG_NET_UDP && CONFIG_NET_UDP_WRITE_BUFFERS */ diff --git a/net/udp/udp_wrbuffer_dump.c b/net/udp/udp_wrbuffer_dump.c new file mode 100644 index 0000000000..e3e8df1f59 --- /dev/null +++ b/net/udp/udp_wrbuffer_dump.c @@ -0,0 +1,70 @@ +/**************************************************************************** + * net/udp/udp_wrbuffer_dump.c + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include + +#include "udp/udp.h" + +#ifdef CONFIG_DEBUG_FEATURES + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: udp_wrbuffer_dump + * + * Description: + * Dump the contents of a write buffer + * + ****************************************************************************/ + +void udp_wrbuffer_dump(FAR const char *msg, FAR struct udp_wrbuffer_s *wrb, + unsigned int len, unsigned int offset) +{ + syslog(LOG_DEBUG, "%s: wrb=%p pktlen=%d\n", msg, wrb, wrb->wb_iob->io_pktlen); + iob_dump("I/O Buffer Chain", wrb->wb_iob, len, offset); +} + +#endif /* CONFIG_DEBUG_FEATURES */