net/tcp: Fix a deadlock condition that can occur when (1) all network logic runs on a single work queue, (1) TCP write buffering is enabled, and (2) we run out of IOBs. In this case, the TCP write buffering logic was blocking on iob_alloc() with the network locked. Since the network was locked, the device driver polls that would provide take the write buffer data and release the IOBs could not execute. This fixes the problem by unlocking the network lock while waiting for the IOBs.

This commit is contained in:
Gregory Nutt 2018-07-06 19:49:05 -06:00
parent 23a8af2069
commit 75cc19ebb4
3 changed files with 107 additions and 5 deletions

View File

@ -40,7 +40,6 @@
****************************************************************************/
#include <nuttx/net/netconfig.h>
#if defined(CONFIG_NET) && defined(CONFIG_NET_TCP) && defined(CONFIG_NET_TCP_WRITE_BUFFERS)
#if defined(CONFIG_DEBUG_FEATURES) && defined(CONFIG_NET_TCP_WRBUFFER_DEBUG)
/* Force debug output (from this file only) */
@ -59,8 +58,11 @@
#include <nuttx/net/net.h>
#include <nuttx/mm/iob.h>
#include "utils/utils.h"
#include "tcp/tcp.h"
#if defined(CONFIG_NET_TCP) && defined(CONFIG_NET_TCP_WRITE_BUFFERS)
/****************************************************************************
* Private Types
****************************************************************************/
@ -159,8 +161,29 @@ FAR struct tcp_wrbuffer_s *tcp_wrbuffer_alloc(void)
/* Now get the first I/O buffer for the write buffer structure */
wrb->wb_iob = iob_alloc(false);
if (!wrb->wb_iob)
wrb->wb_iob = iob_tryalloc(false);
if (wrb->wb_iob == NULL)
{
unsigned int count;
int ret;
/* There are no buffers available now. We will have to wait for one to
* become available. But let's not do that with the network locked.
*/
ret = net_breaklock(&count);
wrb->wb_iob = iob_alloc(false);
if (ret >= 0)
{
net_restorelock(count);
}
}
/* Did we get an IOB? We should always get one except under some really weird
* error conditions.
*/
if (wrb->wb_iob == NULL)
{
nerr("ERROR: Failed to allocate I/O buffer\n");
tcp_wrbuffer_release(wrb);
@ -284,4 +307,4 @@ int tcp_wrbuffer_test(void)
return ret;
}
#endif /* CONFIG_NET && CONFIG_NET_TCP && CONFIG_NET_TCP_WRITE_BUFFERS */
#endif /* CONFIG_NET_TCP && CONFIG_NET_TCP_WRITE_BUFFERS */

View File

@ -1,7 +1,7 @@
/****************************************************************************
* net/utils/net_lock.c
*
* Copyright (C) 2011-2012, 2014-2017 Gregory Nutt. All rights reserved.
* Copyright (C) 2011-2012, 2014-2018 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@ -204,6 +204,64 @@ void net_unlock(void)
#endif
}
/****************************************************************************
* Name: net_breaklock
*
* Description:
* Break the lock, return information needed to restore re-entrant lock
* state.
*
****************************************************************************/
int net_breaklock(FAR unsigned int *count)
{
irqstate_t flags;
pid_t me = getpid();
int ret = -EPERM;
DEBUGASSERT(count != NULL);
flags = spin_lock_irqsave(); /* No interrupts */
if (g_holder == me)
{
/* Return the lock setting */
*count = g_count;
/* Release the network lock */
g_holder = NO_HOLDER;
g_count = 0;
(void)nxsem_post(&g_netlock);
ret = OK;
}
spin_unlock_irqrestore(flags);
return ret;
}
/****************************************************************************
* Name: net_breaklock
*
* Description:
* Restore the locked state
*
****************************************************************************/
void net_restorelock(unsigned int count)
{
pid_t me = getpid();
DEBUGASSERT(g_holder != me);
/* Recover the network lock at the proper count */
_net_takesem();
g_holder = me;
g_count = count;
}
/****************************************************************************
* Name: net_timedwait
*

View File

@ -86,6 +86,27 @@ struct timeval; /* Forward reference */
void net_lockinitialize(void);
/****************************************************************************
* Name: net_breaklock
*
* Description:
* Break the lock, return information needed to restore re-entrant lock
* state.
*
****************************************************************************/
int net_breaklock(FAR unsigned int *count);
/****************************************************************************
* Name: net_breaklock
*
* Description:
* Restore the locked state
*
****************************************************************************/
void net_restorelock(unsigned int count);
/****************************************************************************
* Name: net_dsec2timeval
*