drivers/wireless/ieee802154/xbee: Fix logic to prevent deadlock scenario when there are no available IOBs

This commit is contained in:
Anthony Merlino 2018-12-21 21:17:29 +04:00
parent 210de35cb5
commit 77dca3d821
2 changed files with 115 additions and 68 deletions

View File

@ -166,6 +166,21 @@ static void xbee_attnworker(FAR void *arg)
DEBUGASSERT(priv); DEBUGASSERT(priv);
DEBUGASSERT(priv->spi); DEBUGASSERT(priv->spi);
/* Allocate an IOB for the incoming data. */
iob = iob_alloc(false);
iob->io_flink = NULL;
iob->io_len = 0;
iob->io_offset = 0;
iob->io_pktlen = 0;
/* Keep a reference to the first IOB. If we need to allocate more than
* one to hold each API frame, then we will still have this reference to
* the head of the list
*/
iobhead = iob;
/* NOTE: There is a helpful side-effect to trying to get the SPI Lock here /* NOTE: There is a helpful side-effect to trying to get the SPI Lock here
* even when there is a write going on. That is, if the SPI write are on a * even when there is a write going on. That is, if the SPI write are on a
* thread with lower priority, trying to get the lock here should boost the * thread with lower priority, trying to get the lock here should boost the
@ -191,24 +206,9 @@ static void xbee_attnworker(FAR void *arg)
priv->attn_latched = false; priv->attn_latched = false;
} }
/* Allocate an IOB for the incoming data. */
iob = iob_alloc(false);
iob->io_flink = NULL;
iob->io_len = 0;
iob->io_offset = 0;
iob->io_pktlen = 0;
/* Keep a reference to the first IOB. If we need to allocate more than
* one to hold each API frame, then we will still have this reference to
* the head of the list
*/
iobhead = iob;
if (priv->attn_latched) if (priv->attn_latched)
{ {
while (priv->lower->poll(priv->lower)) while (priv->lower->poll(priv->lower) && iob != NULL)
{ {
DEBUGASSERT(iob->io_len <= CONFIG_IOB_BUFSIZE); DEBUGASSERT(iob->io_len <= CONFIG_IOB_BUFSIZE);
@ -264,14 +264,18 @@ static void xbee_attnworker(FAR void *arg)
* processing. * processing.
*/ */
iob->io_flink = iob_alloc(false); iob->io_flink = iob_tryalloc(false);
iob = iob->io_flink; iob = iob->io_flink;
iob->io_flink = NULL; if (iob != NULL)
iob->io_len = 0; {
iob->io_offset = 0;
iob->io_pktlen = 0; iob->io_flink = NULL;
} iob->io_len = 0;
iob->io_offset = 0;
iob->io_pktlen = 0;
}
}
else else
{ {
wlwarn("invalid checksum on incoming API frame. Dropping!\n"); wlwarn("invalid checksum on incoming API frame. Dropping!\n");
@ -287,7 +291,10 @@ static void xbee_attnworker(FAR void *arg)
} }
} }
priv->attn_latched = false; if (!priv->lower->poll(priv->lower))
{
priv->attn_latched = false;
}
} }
/* The last IOB in the list (or the only one) may be able to be freed since /* The last IOB in the list (or the only one) may be able to be freed since
@ -298,28 +305,31 @@ static void xbee_attnworker(FAR void *arg)
* we can only drop it. * we can only drop it.
*/ */
if (iob->io_len < XBEE_APIFRAME_OVERHEAD || iob->io_len != rxframelen) if (iob != NULL)
{ {
if (iobhead == iob) if (iob->io_len < XBEE_APIFRAME_OVERHEAD || iob->io_len != rxframelen)
{ {
iobhead = NULL; if (iobhead == iob)
}
else
{
previob = iobhead;
while (previob->io_flink != iob)
{ {
previob = previob->io_flink; iobhead = NULL;
}
else
{
previob = iobhead;
while (previob->io_flink != iob)
{
previob = previob->io_flink;
}
previob->io_flink = NULL;
} }
previob->io_flink = NULL;
}
if (iob->io_len > 0) if (iob->io_len > 0)
{ {
wlwarn("Partial API frame clocked in. Dropping!\n"); wlwarn("Partial API frame clocked in. Dropping!\n");
} }
iob_free(iob); iob_free(iob);
}
} }
if (iobhead != NULL) if (iobhead != NULL)
@ -665,11 +675,8 @@ static void xbee_process_apiframes(FAR struct xbee_priv_s *priv,
break; break;
case XBEE_APIFRAME_TXSTATUS: case XBEE_APIFRAME_TXSTATUS:
{ {
wd_cancel(priv->reqdata_wd);
xbee_process_txstatus(priv, frame->io_data[frame->io_offset], xbee_process_txstatus(priv, frame->io_data[frame->io_offset],
frame->io_data[frame->io_offset + 1]); frame->io_data[frame->io_offset + 1]);
priv->txdone = true;
nxsem_post(&priv->txdone_sem);
} }
break; break;
case XBEE_APIFRAME_RX_EADDR: case XBEE_APIFRAME_RX_EADDR:
@ -841,6 +848,17 @@ static void xbee_process_txstatus(FAR struct xbee_priv_s *priv, uint8_t frameid,
} }
xbee_notify(priv, primitive); xbee_notify(priv, primitive);
/* If this is the frame we are currently waiting on, cancel the timeout, and
* notify the waiting thread that the transmit is done
*/
if (priv->frameid == frameid)
{
wd_cancel(priv->reqdata_wd);
priv->txdone = true;
nxsem_post(&priv->txdone_sem);
}
} }
/**************************************************************************** /****************************************************************************
@ -1116,9 +1134,11 @@ void xbee_send_apiframe(FAR struct xbee_priv_s *priv,
/* Allocate an IOB for the incoming data. The XBee supports full-duplex /* Allocate an IOB for the incoming data. The XBee supports full-duplex
* SPI communication. This means that the MISO data can become valid at any * SPI communication. This means that the MISO data can become valid at any
* time. This requires us to process incoming MISO data to see if it is valid. * time. This requires us to process incoming MISO data to see if it is valid.
*
* If we can't allocate an IOB, then we have to just drop the incoming data.
*/ */
iob = iob_alloc(false); iob = iob_tryalloc(false);
iob->io_flink = NULL; iob->io_flink = NULL;
iob->io_len = 0; iob->io_len = 0;
iob->io_offset = 0; iob->io_offset = 0;
@ -1132,11 +1152,18 @@ void xbee_send_apiframe(FAR struct xbee_priv_s *priv,
iobhead = iob; iobhead = iob;
i = 0; i = 0;
while (i < framelen || priv->lower->poll(priv->lower)) while (i < framelen || (priv->lower->poll(priv->lower) && iob != NULL))
{ {
if (i < framelen) if (i < framelen)
{ {
iob->io_data[iob->io_len] = SPI_SEND(priv->spi, frame[i++]); if (iob)
{
iob->io_data[iob->io_len] = SPI_SEND(priv->spi, frame[i++]);
}
else
{
SPI_SEND(priv->spi, frame[i++]);
}
} }
else else
{ {
@ -1147,7 +1174,7 @@ void xbee_send_apiframe(FAR struct xbee_priv_s *priv,
* data prior to that can be completely ignored. * data prior to that can be completely ignored.
*/ */
if (priv->attn_latched) if (priv->attn_latched && iob != NULL)
{ {
DEBUGASSERT(iob->io_len <= CONFIG_IOB_BUFSIZE); DEBUGASSERT(iob->io_len <= CONFIG_IOB_BUFSIZE);
@ -1201,14 +1228,17 @@ void xbee_send_apiframe(FAR struct xbee_priv_s *priv,
* processing. * processing.
*/ */
iob->io_flink = iob_alloc(false); iob->io_flink = iob_tryalloc(false);
iob = iob->io_flink; iob = iob->io_flink;
iob->io_flink = NULL; if (iob != NULL)
iob->io_len = 0; {
iob->io_offset = 0; iob->io_flink = NULL;
iob->io_pktlen = 0; iob->io_len = 0;
} iob->io_offset = 0;
iob->io_pktlen = 0;
}
}
else else
{ {
wlwarn("invalid checksum on incoming API frame. Dropping!\n"); wlwarn("invalid checksum on incoming API frame. Dropping!\n");
@ -1233,28 +1263,31 @@ void xbee_send_apiframe(FAR struct xbee_priv_s *priv,
* we can only drop it. * we can only drop it.
*/ */
if (iob->io_len < XBEE_APIFRAME_OVERHEAD || iob->io_len != rxframelen) if (iob != NULL)
{ {
if (iobhead == iob) if (iob->io_len < XBEE_APIFRAME_OVERHEAD || iob->io_len != rxframelen)
{ {
iobhead = NULL; if (iobhead == iob)
}
else
{
previob = iobhead;
while (previob->io_flink != iob)
{ {
previob = previob->io_flink; iobhead = NULL;
}
else
{
previob = iobhead;
while (previob->io_flink != iob)
{
previob = previob->io_flink;
}
previob->io_flink = NULL;
} }
previob->io_flink = NULL;
}
if (iob->io_len > 0) if (iob->io_len > 0)
{ {
wlwarn("Partial API frame clocked in. Dropping!\n"); wlwarn("Partial API frame clocked in. Dropping!\n");
} }
iob_free(iob); iob_free(iob);
}
} }
if (iobhead != NULL) if (iobhead != NULL)

View File

@ -380,6 +380,20 @@ int xbee_req_data(XBEEHANDLE xbee,
/* Wait for a transmit status to be received. Does not necessarily mean success */ /* Wait for a transmit status to be received. Does not necessarily mean success */
while (nxsem_wait(&priv->txdone_sem) < 0); while (nxsem_wait(&priv->txdone_sem) < 0);
/* If the transmit timeout has occured, and there are no IOBs available,
* we may be blocking the context needed to free the IOBs. We cannot receive
* the Tx status because it requires an IOB. Therefore, if we have hit the
* timeout, and there are no IOBs, let's move on assuming the transmit was
* a success
*/
if (!priv->txdone && iob_navail(false) <= 0)
{
wlwarn("Couldn't confirm TX. No IOBs\n");
break;
}
} }
while (!priv->txdone); while (!priv->txdone);