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->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
* 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
@ -191,24 +206,9 @@ static void xbee_attnworker(FAR void *arg)
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)
{
while (priv->lower->poll(priv->lower))
while (priv->lower->poll(priv->lower) && iob != NULL)
{
DEBUGASSERT(iob->io_len <= CONFIG_IOB_BUFSIZE);
@ -264,14 +264,18 @@ static void xbee_attnworker(FAR void *arg)
* processing.
*/
iob->io_flink = iob_alloc(false);
iob->io_flink = iob_tryalloc(false);
iob = iob->io_flink;
iob->io_flink = NULL;
iob->io_len = 0;
iob->io_offset = 0;
iob->io_pktlen = 0;
}
if (iob != NULL)
{
iob->io_flink = NULL;
iob->io_len = 0;
iob->io_offset = 0;
iob->io_pktlen = 0;
}
}
else
{
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
@ -298,28 +305,31 @@ static void xbee_attnworker(FAR void *arg)
* 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;
}
else
{
previob = iobhead;
while (previob->io_flink != iob)
if (iobhead == 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)
{
wlwarn("Partial API frame clocked in. Dropping!\n");
}
if (iob->io_len > 0)
{
wlwarn("Partial API frame clocked in. Dropping!\n");
}
iob_free(iob);
iob_free(iob);
}
}
if (iobhead != NULL)
@ -665,11 +675,8 @@ static void xbee_process_apiframes(FAR struct xbee_priv_s *priv,
break;
case XBEE_APIFRAME_TXSTATUS:
{
wd_cancel(priv->reqdata_wd);
xbee_process_txstatus(priv, frame->io_data[frame->io_offset],
frame->io_data[frame->io_offset + 1]);
priv->txdone = true;
nxsem_post(&priv->txdone_sem);
}
break;
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);
/* 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
* 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.
*
* 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_len = 0;
iob->io_offset = 0;
@ -1132,11 +1152,18 @@ void xbee_send_apiframe(FAR struct xbee_priv_s *priv,
iobhead = iob;
i = 0;
while (i < framelen || priv->lower->poll(priv->lower))
while (i < framelen || (priv->lower->poll(priv->lower) && iob != NULL))
{
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
{
@ -1147,7 +1174,7 @@ void xbee_send_apiframe(FAR struct xbee_priv_s *priv,
* 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);
@ -1201,14 +1228,17 @@ void xbee_send_apiframe(FAR struct xbee_priv_s *priv,
* processing.
*/
iob->io_flink = iob_alloc(false);
iob->io_flink = iob_tryalloc(false);
iob = iob->io_flink;
iob->io_flink = NULL;
iob->io_len = 0;
iob->io_offset = 0;
iob->io_pktlen = 0;
}
if (iob != NULL)
{
iob->io_flink = NULL;
iob->io_len = 0;
iob->io_offset = 0;
iob->io_pktlen = 0;
}
}
else
{
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.
*/
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;
}
else
{
previob = iobhead;
while (previob->io_flink != iob)
if (iobhead == 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)
{
wlwarn("Partial API frame clocked in. Dropping!\n");
}
if (iob->io_len > 0)
{
wlwarn("Partial API frame clocked in. Dropping!\n");
}
iob_free(iob);
iob_free(iob);
}
}
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 */
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);