TCP: return from write when there is no buffer space
During a write, if there is no more buffer space for the user data, return the amount that was written instead of waiting until there is free space. If nothing has been written yet, then block as before. This solves a deadlock that occurs if the user data is too large to fit in the available buffer: the write thread will block before any data is added to the write queue, leaving no possibility that more buffers will free up when they are ACKed (since they have not yet been sent). The write thread will then block forever and hold all of the buffers.
This commit is contained in:
parent
e221777a7a
commit
5e3023bef1
@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
* net/iob/iob_copyin.c
|
||||
*
|
||||
* Copyright (C) 2014 Gregory Nutt. All rights reserved.
|
||||
* Copyright (C) 2014, 2016 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -89,17 +89,21 @@ typedef CODE struct iob_s *(*iob_alloc_t)(bool throttled);
|
||||
* Copy data 'len' bytes from a user buffer into the I/O buffer chain,
|
||||
* starting at 'offset', extending the chain as necessary.
|
||||
*
|
||||
* Returned Value:
|
||||
* The number of uncopied bytes left if >= 0 OR a negative error code.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int iob_copyin_internal(FAR struct iob_s *iob, FAR const uint8_t *src,
|
||||
unsigned int len, unsigned int offset,
|
||||
bool throttled, iob_alloc_t allocator)
|
||||
bool throttled, bool can_block)
|
||||
{
|
||||
FAR struct iob_s *head = iob;
|
||||
FAR struct iob_s *next;
|
||||
FAR uint8_t *dest;
|
||||
unsigned int ncopy;
|
||||
unsigned int avail;
|
||||
unsigned int total = len;
|
||||
|
||||
nllvdbg("iob=%p len=%u offset=%u\n", iob, len, offset);
|
||||
DEBUGASSERT(iob && src);
|
||||
@ -207,13 +211,25 @@ static int iob_copyin_internal(FAR struct iob_s *iob, FAR const uint8_t *src,
|
||||
|
||||
if (len > 0 && !next)
|
||||
{
|
||||
/* Yes.. allocate a new buffer */
|
||||
/* Yes.. allocate a new buffer.
|
||||
*
|
||||
* Copy as many bytes as possible. If we have successfully copied
|
||||
* any already don't block, otherwise block if we're allowed.
|
||||
*/
|
||||
|
||||
if (!can_block || len < total)
|
||||
{
|
||||
next = iob_tryalloc(throttled);
|
||||
}
|
||||
else
|
||||
{
|
||||
next = iob_alloc(throttled);
|
||||
}
|
||||
|
||||
next = allocator(throttled);
|
||||
if (next == NULL)
|
||||
{
|
||||
ndbg("ERROR: Failed to allocate I/O buffer\n");
|
||||
return -ENOMEM;
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Add the new, empty I/O buffer to the end of the buffer chain. */
|
||||
@ -226,7 +242,7 @@ static int iob_copyin_internal(FAR struct iob_s *iob, FAR const uint8_t *src,
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
return OK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -245,7 +261,7 @@ static int iob_copyin_internal(FAR struct iob_s *iob, FAR const uint8_t *src,
|
||||
int iob_copyin(FAR struct iob_s *iob, FAR const uint8_t *src,
|
||||
unsigned int len, unsigned int offset, bool throttled)
|
||||
{
|
||||
return iob_copyin_internal(iob, src, len, offset, throttled, iob_alloc);
|
||||
return len - iob_copyin_internal(iob, src, len, offset, throttled, true);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -261,6 +277,12 @@ int iob_copyin(FAR struct iob_s *iob, FAR const uint8_t *src,
|
||||
int iob_trycopyin(FAR struct iob_s *iob, FAR const uint8_t *src,
|
||||
unsigned int len, unsigned int offset, bool throttled)
|
||||
{
|
||||
return iob_copyin_internal(iob, src, len, offset, throttled, iob_tryalloc);
|
||||
if (iob_copyin_internal(iob, src, len, offset, throttled, false) == 0)
|
||||
{
|
||||
return OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1046,7 +1046,7 @@ ssize_t psock_tcp_send(FAR struct socket *psock, FAR const void *buf,
|
||||
|
||||
WRB_SEQNO(wrb) = (unsigned)-1;
|
||||
WRB_NRTX(wrb) = 0;
|
||||
WRB_COPYIN(wrb, (FAR uint8_t *)buf, len);
|
||||
result = WRB_COPYIN(wrb, (FAR uint8_t *)buf, len);
|
||||
|
||||
/* Dump I/O buffer chain */
|
||||
|
||||
@ -1065,7 +1065,6 @@ ssize_t psock_tcp_send(FAR struct socket *psock, FAR const void *buf,
|
||||
|
||||
send_txnotify(psock, conn);
|
||||
net_unlock(save);
|
||||
result = len;
|
||||
}
|
||||
|
||||
/* Set the socket state to idle */
|
||||
|
Loading…
Reference in New Issue
Block a user