forked from Imagelibrary/rtems
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:
@@ -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);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
Reference in New Issue
Block a user