lwip - Fixed memory leak in k64f cyclic-buffer overflow

This was actually several bugs colluding together.

1. Confusion on the buffer-semaphore paradigm used led to misuse of the
tx semaphore and potential for odd behaviour.

2. Equality tests on tx_consume_index and tx_produce_index did not
handle overflow correctly. This would allow tx_consume_index to catch
up to tx_produce_index and trick the k64f_rx_reclaim function into
forgetting about a whole buffer of pbufs.

3. On top of all of that, the ENET_BUFFDESCRIPTOR_TX_READ_MASK was not
correctly read immediately after being set due to either a compiler
optimization or hardware delays. This caused k64f_low_level_output
to eagerly overrun existing buff-descriptors before they had been
completely sent. Adopting the counting-semaphore paradigm for 1 avoided
this concern.

As pointed out by @infinnovation, the overflow only occurs in the rare
case that the 120MHz CPU can actually generate packets faster than the
ENET hardware can transmit on a 100Mbps link.
pull/3135/head
Christopher Haster 2016-10-25 13:22:17 -05:00
parent d7c02a13b1
commit 2fd15f4f44
1 changed files with 8 additions and 11 deletions

View File

@ -102,24 +102,22 @@ static void update_read_buffer(uint8_t *buf)
*/
static void k64f_tx_reclaim(struct k64f_enetdata *k64f_enet)
{
uint8_t i = 0 ;
/* Get exclusive access */
sys_mutex_lock(&k64f_enet->TXLockMutex);
i = k64f_enet->tx_consume_index;
// Traverse all descriptors, looking for the ones modified by the uDMA
while((i != k64f_enet->tx_produce_index) && (!(g_handle.txBdDirty->control & ENET_BUFFDESCRIPTOR_TX_READY_MASK))) {
pbuf_free(tx_buff[i]);
while((k64f_enet->tx_consume_index != k64f_enet->tx_produce_index) &&
(!(g_handle.txBdDirty->control & ENET_BUFFDESCRIPTOR_TX_READY_MASK))) {
pbuf_free(tx_buff[k64f_enet->tx_consume_index % ENET_TX_RING_LEN]);
if (g_handle.txBdDirty->control & ENET_BUFFDESCRIPTOR_TX_WRAP_MASK)
g_handle.txBdDirty = g_handle.txBdBase;
else
g_handle.txBdDirty++;
i = (i + 1) % ENET_TX_RING_LEN;
k64f_enet->tx_consume_index += 1;
osSemaphoreRelease(k64f_enet->xTXDCountSem.id);
}
k64f_enet->tx_consume_index = i;
/* Restore access */
sys_mutex_unlock(&k64f_enet->TXLockMutex);
}
@ -526,15 +524,14 @@ static err_t k64f_low_level_output(struct netif *netif, struct pbuf *p)
/* Wait until a descriptor is available for the transfer. */
/* THIS WILL BLOCK UNTIL THERE ARE A DESCRIPTOR AVAILABLE */
while (g_handle.txBdCurrent->control & ENET_BUFFDESCRIPTOR_TX_READY_MASK)
osSemaphoreWait(k64f_enet->xTXDCountSem.id, osWaitForever);
osSemaphoreWait(k64f_enet->xTXDCountSem.id, osWaitForever);
/* Get exclusive access */
sys_mutex_lock(&k64f_enet->TXLockMutex);
/* Save the buffer so that it can be freed when transmit is done */
tx_buff[k64f_enet->tx_produce_index] = temp_pbuf;
k64f_enet->tx_produce_index = (k64f_enet->tx_produce_index + 1) % ENET_TX_RING_LEN;
tx_buff[k64f_enet->tx_produce_index % ENET_TX_RING_LEN] = temp_pbuf;
k64f_enet->tx_produce_index += 1;
/* Setup transfers */
g_handle.txBdCurrent->buffer = psend;