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:
parent
0406fff888
commit
fef255e5be
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
129
net/udp/udp.h
129
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 <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.
|
||||
*
|
||||
|
@ -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);
|
||||
|
821
net/udp/udp_psock_sendto_buffered.c
Normal file
821
net/udp/udp_psock_sendto_buffered.c
Normal 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 */
|
@ -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
217
net/udp/udp_wrbuffer.c
Normal 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 */
|
70
net/udp/udp_wrbuffer_dump.c
Normal file
70
net/udp/udp_wrbuffer_dump.c
Normal 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 */
|
Loading…
Reference in New Issue
Block a user