diff --git a/net/udp/udp.h b/net/udp/udp.h index 0dd327cec7adf..06fb5ce5b6d2d 100644 --- a/net/udp/udp.h +++ b/net/udp/udp.h @@ -447,6 +447,27 @@ struct udp_wrbuffer_s; FAR struct udp_wrbuffer_s *udp_wrbuffer_alloc(void); #endif /* CONFIG_NET_UDP_WRITE_BUFFERS */ +/**************************************************************************** + * Name: udp_wrbuffer_tryalloc + * + * Description: + * Try to 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 be sent if the socket is non-blocking. Returns + * immediately if allocation fails. + * + * Input parameters: + * None + * + * Assumptions: + * Called from user logic with the network locked. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_UDP_WRITE_BUFFERS +FAR struct udp_wrbuffer_s *udp_wrbuffer_tryalloc(void); +#endif /* CONFIG_NET_UDP_WRITE_BUFFERS */ + /**************************************************************************** * Name: udp_wrbuffer_release * diff --git a/net/udp/udp_psock_sendto_buffered.c b/net/udp/udp_psock_sendto_buffered.c index 73d8f446c0c4b..ecb7b86963b7d 100644 --- a/net/udp/udp_psock_sendto_buffered.c +++ b/net/udp/udp_psock_sendto_buffered.c @@ -700,18 +700,27 @@ ssize_t psock_udp_sendto(FAR struct socket *psock, FAR const void *buf, if (len > 0) { + net_lock(); + /* Allocate a write buffer. Careful, the network will be momentarily * unlocked here. */ - net_lock(); - wrb = udp_wrbuffer_alloc(); + if (_SS_ISNONBLOCK(psock->s_flags)) + { + wrb = udp_wrbuffer_tryalloc(); + } + else + { + wrb = udp_wrbuffer_alloc(); + } + if (wrb == NULL) { /* A buffer allocation error occurred */ nerr("ERROR: Failed to allocate write buffer\n"); - ret = -ENOMEM; + ret = _SS_ISNONBLOCK(psock->s_flags) ? -EAGAIN : -ENOMEM; goto errout_with_lock; } diff --git a/net/udp/udp_wrbuffer.c b/net/udp/udp_wrbuffer.c index 399da0de7ea0a..f364473e46ebd 100644 --- a/net/udp/udp_wrbuffer.c +++ b/net/udp/udp_wrbuffer.c @@ -169,6 +169,62 @@ FAR struct udp_wrbuffer_s *udp_wrbuffer_alloc(void) return wrb; } +/**************************************************************************** + * Name: udp_wrbuffer_tryalloc + * + * Description: + * Try to allocate a TCP 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 be sent on a non-blocking socket. Returns + * immediately if the allocation failed. + * + * Input parameters: + * None + * + * Assumptions: + * Called from user logic with the network locked. Will return if no buffer + * is available. + * + ****************************************************************************/ + +FAR struct udp_wrbuffer_s *udp_wrbuffer_tryalloc(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 IOB. In order to + * avoid deadlocks, we will need to free the IOB first, then the write + * buffer + */ + + if (nxsem_trywait(&g_wrbuffer.sem) != OK) + { + return NULL; + } + + /* 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_tryalloc(false, IOBUSER_NET_UDP_WRITEBUFFER); + 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 *