diff --git a/include/nuttx/irq.h b/include/nuttx/irq.h index 07474bc154..0a6c0946f1 100644 --- a/include/nuttx/irq.h +++ b/include/nuttx/irq.h @@ -46,6 +46,9 @@ #ifndef __ASSEMBLY__ # include # include +# ifdef CONFIG_SMP +# include +# endif #endif /* Now include architecture-specific types */ @@ -170,6 +173,53 @@ int irqchain_detach(int irq, xcpt_t isr, FAR void *arg); # define irqchain_detach(irq, isr, arg) irq_detach(irq) #endif +/**************************************************************************** + * Name: irq_waitlock + * + * Description: + * Spin to get g_irq_waitlock, handling a known deadlock condition: + * + * A deadlock may occur if enter_critical_section is called from an + * interrupt handler. Suppose: + * + * - CPUn is in a critical section and has the g_cpu_irqlock spinlock. + * - CPUm takes an interrupt and attempts to enter the critical section. + * - It spins waiting on g_cpu_irqlock with interrupts disabled. + * - CPUn calls up_cpu_pause() to pause operation on CPUm. This will + * issue an inter-CPU interrupt to CPUm + * - But interrupts are disabled on CPUm so the up_cpu_pause() is never + * handled, causing the deadlock. + * + * This same deadlock can occur in the normal tasking case: + * + * - A task on CPUn enters a critical section and has the g_cpu_irqlock + * spinlock. + * - Another task on CPUm attempts to enter the critical section but has + * to wait, spinning to get g_cpu_irqlock with interrupts disabled. + * - The task on CPUn causes a new task to become ready-to-run and the + * scheduler selects CPUm. CPUm is requested to pause via a pause + * interrupt. + * - But the task on CPUm is also attempting to enter the critical + * section. Since it is spinning with interrupts disabled, CPUm cannot + * process the pending pause interrupt, causing the deadlock. + * + * This function detects this deadlock condition while spinning with \ + * interrupts disabled. + * + * Input Parameters: + * cpu - The index of CPU that is trying to enter the critical section. + * + * Returned Value: + * True: The g_cpu_irqlock spinlock has been taken. + * False: The g_cpu_irqlock spinlock has not been taken yet, but there is + * a pending pause interrupt request. + * + ****************************************************************************/ + +#ifdef CONFIG_SMP +bool irq_waitlock(int cpu); +#endif + /**************************************************************************** * Name: enter_critical_section * diff --git a/sched/irq/irq_csection.c b/sched/irq/irq_csection.c index c26e6d6dc3..34565daacf 100644 --- a/sched/irq/irq_csection.c +++ b/sched/irq/irq_csection.c @@ -105,7 +105,7 @@ volatile uint8_t g_cpu_nestcount[CONFIG_SMP_NCPUS]; ****************************************************************************/ #ifdef CONFIG_SMP -static inline bool irq_waitlock(int cpu) +bool irq_waitlock(int cpu) { #ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS FAR struct tcb_s *tcb = current_task(cpu);