SMP: irq_csection() has a bad assumption. It assumed that the state of certain variables. That was true on entry into the interrupt handler, but might change to the execution of logic within the interrupt handler.

This commit is contained in:
Gregory Nutt 2016-11-18 07:38:16 -06:00
parent 8e029f019b
commit 8602e8a8a9
1 changed files with 23 additions and 8 deletions

View File

@ -129,7 +129,7 @@ irqstate_t enter_critical_section(void)
/* We are in an interrupt handler. How can this happen? /* We are in an interrupt handler. How can this happen?
* *
* 1. We were not in a critical section when the interrupt * 1. We were not in a critical section when the interrupt
* occurred. In this case: * occurred. In this case, the interrupt was entered with:
* *
* g_cpu_irqlock = SP_UNLOCKED. * g_cpu_irqlock = SP_UNLOCKED.
* g_cpu_nestcount = 0 * g_cpu_nestcount = 0
@ -152,6 +152,11 @@ irqstate_t enter_critical_section(void)
* g_cpu_irqlock = SP_LOCKED. * g_cpu_irqlock = SP_LOCKED.
* g_cpu_nestcount > 0 * g_cpu_nestcount > 0
* The bit in g_cpu_irqset for this CPU hould be zero * The bit in g_cpu_irqset for this CPU hould be zero
*
* NOTE: However, the interrupt entry conditions can change due
* to previous processing by the interrupt handler that may
* instantiate a new thread that has irqcount > 0 and may then
* set the bit in g_cpu_irqset and g_cpu_irqlock = SP_LOCKED
*/ */
/* Handle nested calls to enter_critical_section() from the same /* Handle nested calls to enter_critical_section() from the same
@ -165,19 +170,29 @@ irqstate_t enter_critical_section(void)
g_cpu_nestcount[cpu] < UINT8_MAX); g_cpu_nestcount[cpu] < UINT8_MAX);
g_cpu_nestcount[cpu]++; g_cpu_nestcount[cpu]++;
} }
/* This is the first call to enter_critical_section from the
* interrupt handler.
*/
else else
{ {
/* First call to enter_critical_section. Test assumptions, then /* Make sure that the g_cpu_irqlock() was not already set
* wait until we have the lock. * by previous logic on this CPU that was executed by the
* interrupt handler.
*/ */
DEBUGASSERT((g_cpu_irqset & (1 << cpu)) == 0); if ((g_cpu_irqset & (1 << cpu)) == 0)
{
/* Wait until we can get the spinlock (meaning that we are
* no longer in the critical section).
*/
/* Wait until we can get the spinlock (meaning that we are no spin_lock(&g_cpu_irqlock);
* longer in the critical section). }
*/
/* In any event, the nesting count is now one */
spin_lock(&g_cpu_irqlock);
g_cpu_nestcount[cpu] = 1; g_cpu_nestcount[cpu] = 1;
} }
} }