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) 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 * Release semaphores to wake all threads waiting for an IRQ.
* get woken up must check for RTEMS_UNSATISFIED in * The threads that
* get woken up must check started state in
* order to determine that they should return to * order to determine that they should return to
* user space with error status. * user space with error status.
*
* Entering into started mode again will reset the
* semaphore count.
*/ */
rtems_semaphore_flush(pDev->rx_sem); rtems_semaphore_release(pDev->rx_sem);
rtems_semaphore_flush(pDev->tx_sem); rtems_semaphore_release(pDev->tx_sem);
rtems_semaphore_flush(pDev->txempty_sem); rtems_semaphore_release(pDev->txempty_sem);
} }
static void grcan_hw_config(struct grcan_regs *regs, struct grcan_config *conf) 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 */ /* Wait for IRQ to fire only if has been triggered */
if (wait) { if (wait) {
if ( rtems_semaphore_obtain(pDev->rx_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
rtems_semaphore_obtain( /*
pDev->rx_sem, * The semaphore is released either due to the expected IRQ
RTEMS_WAIT, * condition or by BUSOFF, AHBERROR or another thread calling
RTEMS_NO_TIMEOUT * grcan_stop(). In either case, state2err[] has the correnct
) == RTEMS_UNSATISFIED * return value.
) { */
DBGC(DBG_STATE, "UNSATISFIED\n"); return state2err[pDev->started];
/* Device driver has been closed or stopped, return with error status */
return state2err[pDev->started];
}
} }
return 0; 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 */ /* Wait for IRQ to fire only if it has been triggered */
if (wait) { if (wait) {
if (rtems_semaphore_obtain(pDev->tx_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) == rtems_semaphore_obtain(pDev->tx_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
RTEMS_UNSATISFIED) { return state2err[pDev->started];
/* 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];
}
} }
/* At this point the TxIRQ has been masked, we ned not to mask it */ /* 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; break;
/* Wait for IRQ to wake us */ /* Wait for IRQ to wake us */
if (rtems_semaphore_obtain rtems_semaphore_obtain(pDev->txempty_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
(pDev->txempty_sem, RTEMS_WAIT, state = pDev->started;
RTEMS_NO_TIMEOUT) == RTEMS_UNSATISFIED) { if (state != STATE_STARTED) {
DBGC(DBG_STATE, "UNSATISFIED\n"); return state2err[state];
return state2err[pDev->started];
} }
} }
return 0; return 0;
@@ -1565,6 +1560,13 @@ int grcan_start(void *d)
return -2; 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... */ /* Read and write are now open... */
pDev->started = STATE_STARTED; pDev->started = STATE_STARTED;
DBGC(DBG_STATE, "STOPPED|BUSOFF|AHBERR->STARTED\n"); DBGC(DBG_STATE, "STOPPED|BUSOFF|AHBERR->STARTED\n");
@@ -1942,7 +1944,7 @@ static void grcan_interrupt(void *arg)
*/ */
SPIN_UNLOCK(&pDev->devlock, irqflags); SPIN_UNLOCK(&pDev->devlock, irqflags);
/* flush semaphores to wake blocked threads */ /* Release semaphores to wake blocked threads. */
grcan_sw_stop(pDev); grcan_sw_stop(pDev);
/* /*