leon, grcan: semaphore reset count required after flushing

It is also required to use semaphore release instead of flush when stopping
or on BUSOFF/AHBERR condition. Otherwise a task just about to wait
(taking the semaphore) could end up locked because the semaphore count is
still the same.

There was previously a scenario where the semaphore flush would not always make
semaphore obtain to return in case of BUSOFF, AHBERROR or grcan_stop. It has to
do with the rtems_semaphore_flush() not releasing the semaphore but just aborts
any _current_ waiter.
This commit is contained in:
Daniel Hellstrom
2017-04-11 12:30:02 +02:00
parent 9855690300
commit 9154155110

View File

@@ -492,15 +492,19 @@ static void grcan_hw_stop(struct grcan_priv *pDev)
static void grcan_sw_stop(struct grcan_priv *pDev)
{
/* Reset semaphores to the initial state and wakeing
* all threads waiting for an IRQ. The threads that
* get woken up must check for RTEMS_UNSATISFIED in
/*
* Release semaphores to wake all threads waiting for an IRQ.
* The threads that
* get woken up must check started state in
* order to determine that they should return to
* user space with error status.
*
* Entering into started mode again will reset the
* semaphore count.
*/
rtems_semaphore_flush(pDev->rx_sem);
rtems_semaphore_flush(pDev->tx_sem);
rtems_semaphore_flush(pDev->txempty_sem);
rtems_semaphore_release(pDev->rx_sem);
rtems_semaphore_release(pDev->tx_sem);
rtems_semaphore_release(pDev->txempty_sem);
}
static void grcan_hw_config(struct grcan_regs *regs, struct grcan_config *conf)
@@ -962,17 +966,14 @@ static int grcan_wait_rxdata(struct grcan_priv *pDev, int min)
/* Wait for IRQ to fire only if has been triggered */
if (wait) {
if (
rtems_semaphore_obtain(
pDev->rx_sem,
RTEMS_WAIT,
RTEMS_NO_TIMEOUT
) == RTEMS_UNSATISFIED
) {
DBGC(DBG_STATE, "UNSATISFIED\n");
/* Device driver has been closed or stopped, return with error status */
return state2err[pDev->started];
}
rtems_semaphore_obtain(pDev->rx_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
/*
* The semaphore is released either due to the expected IRQ
* condition or by BUSOFF, AHBERROR or another thread calling
* grcan_stop(). In either case, state2err[] has the correnct
* return value.
*/
return state2err[pDev->started];
}
return 0;
@@ -1054,13 +1055,8 @@ static int grcan_wait_txspace(struct grcan_priv *pDev, int min)
/* Wait for IRQ to fire only if it has been triggered */
if (wait) {
if (rtems_semaphore_obtain(pDev->tx_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) ==
RTEMS_UNSATISFIED) {
/* Device driver has flushed us, this may be due to another thread has
* closed the device, this is to avoid deadlock */
DBGC(DBG_STATE, "UNSATISFIED\n");
return state2err[pDev->started];
}
rtems_semaphore_obtain(pDev->tx_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
return state2err[pDev->started];
}
/* At this point the TxIRQ has been masked, we ned not to mask it */
@@ -1117,11 +1113,10 @@ static int grcan_tx_flush(struct grcan_priv *pDev)
break;
/* Wait for IRQ to wake us */
if (rtems_semaphore_obtain
(pDev->txempty_sem, RTEMS_WAIT,
RTEMS_NO_TIMEOUT) == RTEMS_UNSATISFIED) {
DBGC(DBG_STATE, "UNSATISFIED\n");
return state2err[pDev->started];
rtems_semaphore_obtain(pDev->txempty_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
state = pDev->started;
if (state != STATE_STARTED) {
return state2err[state];
}
}
return 0;
@@ -1565,6 +1560,13 @@ int grcan_start(void *d)
return -2;
}
/* Clear semaphore state. This is to avoid effects from previous
* bus-off/stop where semahpores where flushed() but the count remained.
*/
rtems_semaphore_obtain(pDev->rx_sem, RTEMS_NO_WAIT, 0);
rtems_semaphore_obtain(pDev->tx_sem, RTEMS_NO_WAIT, 0);
rtems_semaphore_obtain(pDev->txempty_sem, RTEMS_NO_WAIT, 0);
/* Read and write are now open... */
pDev->started = STATE_STARTED;
DBGC(DBG_STATE, "STOPPED|BUSOFF|AHBERR->STARTED\n");
@@ -1942,7 +1944,7 @@ static void grcan_interrupt(void *arg)
*/
SPIN_UNLOCK(&pDev->devlock, irqflags);
/* flush semaphores to wake blocked threads */
/* Release semaphores to wake blocked threads. */
grcan_sw_stop(pDev);
/*