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.
This commit is contained in:
Gregory Nutt 2018-01-22 18:32:02 -06:00
parent 0406fff888
commit fef255e5be
15 changed files with 1354 additions and 22 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 <gnutt@nuttx.org>
#
# 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

View File

@ -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 <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@ -45,6 +45,7 @@
#include <sys/types.h>
#include <queue.h>
#include <nuttx/clock.h>
#include <nuttx/net/ip.h>
#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.
*

View File

@ -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 <gnutt@nuttx.org>
*
* 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);

View File

@ -0,0 +1,821 @@
/****************************************************************************
* net/udp/udp_send_buffered.c
*
* Copyright (C) 2018 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* 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 <nuttx/config.h>
#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 <sys/types.h>
#include <sys/socket.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <debug.h>
#include <debug.h>
#include <arch/irq.h>
#include <nuttx/clock.h>
#include <nuttx/net/net.h>
#include <nuttx/mm/iob.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/arp.h>
#include <nuttx/net/udp.h>
#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 */

View File

@ -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 <gnutt@nuttx.org>
*
* 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 */

217
net/udp/udp_wrbuffer.c Normal file
View File

@ -0,0 +1,217 @@
/****************************************************************************
* net/udp/udp_wrbuffer.c
*
* Copyright (C) 2018 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* 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 <nuttx/net/netconfig.h>
#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 <queue.h>
#include <string.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/semaphore.h>
#include <nuttx/net/net.h>
#include <nuttx/mm/iob.h>
#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 */

View File

@ -0,0 +1,70 @@
/****************************************************************************
* net/udp/udp_wrbuffer_dump.c
*
* Copyright (C) 2018 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* 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 <nuttx/config.h>
#include <stdint.h>
#include <debug.h>
#include <nuttx/mm/iob.h>
#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 */