Skip to content

Commit

Permalink
signal: fix deadlock when sigdeliver call enter_critical_section
Browse files Browse the repository at this point in the history
cpu0                                 cpu1:

user_main
signest_test
sched_unlock
nxsched_merge_pending
nxsched_add_readytorun
up_cpu_pause
			             arm_sigdeliver
				     enter_critical_section

Reason:
In the SMP, cpu0 is already in the critical section and waiting for cpu1 to enter the suspended state.
However, when cpu1 executes arm_sigdeliver, it is in the irq-disabled state but not in the critical section.
At this point, cpu1 is unable to respond to interrupts and
is continuously attempting to enter the critical section, resulting in a deadlock.

Resolve:
adjust the logic, do not entering the critical section when interrupt-disabled.

test:
We can use qemu for testing.

compiling
make distclean -j20; ./tools/configure.sh -l qemu-armv8a:nsh_smp ;make -j20
running
qemu-system-aarch64 -cpu cortex-a53 -smp 4 -nographic -machine virt,virtualization=on,gic-version=3 -net none -chardev stdio,id=con,mux=on -serial chardev:con -mon chardev=con,mode=readline -kernel ./nuttx

Signed-off-by: hujun5 <[email protected]>
  • Loading branch information
hujun260 committed May 20, 2024
1 parent 7044e38 commit cb183d1
Show file tree
Hide file tree
Showing 10 changed files with 69 additions and 56 deletions.
12 changes: 6 additions & 6 deletions arch/arm/src/armv6-m/arm_sigdeliver.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@ void arm_sigdeliver(void)
*/

int16_t saved_irqcount;

enter_critical_section();
#endif

board_autoled_on(LED_SIGNAL);
Expand All @@ -81,17 +79,16 @@ void arm_sigdeliver(void)
*/

saved_irqcount = rtcb->irqcount;
DEBUGASSERT(saved_irqcount >= 1);
DEBUGASSERT(saved_irqcount >= 0);

/* Now we need call leave_critical_section() repeatedly to get the irqcount
* to zero, freeing all global spinlocks that enforce the critical section.
*/

do
while (rtcb->irqcount > 0)
{
leave_critical_section((uint16_t)regs[REG_PRIMASK]);
}
while (rtcb->irqcount > 0);
#endif /* CONFIG_SMP */

#ifndef CONFIG_SUPPRESS_INTERRUPTS
Expand Down Expand Up @@ -122,7 +119,7 @@ void arm_sigdeliver(void)
*/

DEBUGASSERT(rtcb->irqcount == 0);
while (rtcb->irqcount < saved_irqcount)
while (rtcb->irqcount < saved_irqcount + 1)
{
enter_critical_section();
}
Expand All @@ -135,6 +132,9 @@ void arm_sigdeliver(void)
if (!sq_empty(&rtcb->sigpendactionq) &&
(rtcb->flags & TCB_FLAG_SIGNAL_ACTION) == 0)
{
#ifdef CONFIG_SMP
leave_critical_section((uint16_t)regs[REG_PRIMASK]);
#endif
goto retry;
}

Expand Down
12 changes: 6 additions & 6 deletions arch/arm/src/armv7-a/arm_sigdeliver.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@ void arm_sigdeliver(void)
*/

int16_t saved_irqcount;

enter_critical_section();
#endif

board_autoled_on(LED_SIGNAL);
Expand All @@ -81,17 +79,16 @@ void arm_sigdeliver(void)
*/

saved_irqcount = rtcb->irqcount;
DEBUGASSERT(saved_irqcount >= 1);
DEBUGASSERT(saved_irqcount >= 0);

/* Now we need call leave_critical_section() repeatedly to get the irqcount
* to zero, freeing all global spinlocks that enforce the critical section.
*/

do
while (rtcb->irqcount > 0)
{
leave_critical_section(regs[REG_CPSR]);
}
while (rtcb->irqcount > 0);
#endif /* CONFIG_SMP */

#ifndef CONFIG_SUPPRESS_INTERRUPTS
Expand Down Expand Up @@ -122,7 +119,7 @@ void arm_sigdeliver(void)
*/

DEBUGASSERT(rtcb->irqcount == 0);
while (rtcb->irqcount < saved_irqcount)
while (rtcb->irqcount < saved_irqcount + 1)
{
enter_critical_section();
}
Expand All @@ -135,6 +132,9 @@ void arm_sigdeliver(void)
if (!sq_empty(&rtcb->sigpendactionq) &&
(rtcb->flags & TCB_FLAG_SIGNAL_ACTION) == 0)
{
#ifdef CONFIG_SMP
leave_critical_section(regs[REG_CPSR]);
#endif
goto retry;
}

Expand Down
16 changes: 10 additions & 6 deletions arch/arm/src/armv7-m/arm_sigdeliver.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@ void arm_sigdeliver(void)
*/

int16_t saved_irqcount;

enter_critical_section();
#endif

board_autoled_on(LED_SIGNAL);
Expand All @@ -81,21 +79,20 @@ void arm_sigdeliver(void)
*/

saved_irqcount = rtcb->irqcount;
DEBUGASSERT(saved_irqcount >= 1);
DEBUGASSERT(saved_irqcount >= 0);

/* Now we need call leave_critical_section() repeatedly to get the irqcount
* to zero, freeing all global spinlocks that enforce the critical section.
*/

do
while (rtcb->irqcount > 0)
{
#ifdef CONFIG_ARMV7M_USEBASEPRI
leave_critical_section((uint8_t)regs[REG_BASEPRI]);
#else
leave_critical_section((uint16_t)regs[REG_PRIMASK]);
#endif
}
while (rtcb->irqcount > 0);
#endif /* CONFIG_SMP */

#ifndef CONFIG_SUPPRESS_INTERRUPTS
Expand Down Expand Up @@ -126,7 +123,7 @@ void arm_sigdeliver(void)
*/

DEBUGASSERT(rtcb->irqcount == 0);
while (rtcb->irqcount < saved_irqcount)
while (rtcb->irqcount < saved_irqcount + 1)
{
enter_critical_section();
}
Expand All @@ -139,6 +136,13 @@ void arm_sigdeliver(void)
if (!sq_empty(&rtcb->sigpendactionq) &&
(rtcb->flags & TCB_FLAG_SIGNAL_ACTION) == 0)
{
#ifdef CONFIG_SMP
# ifdef CONFIG_ARMV7M_USEBASEPRI
leave_critical_section((uint8_t)regs[REG_BASEPRI]);
# else
leave_critical_section((uint16_t)regs[REG_PRIMASK]);
# endif
#endif
goto retry;
}

Expand Down
12 changes: 6 additions & 6 deletions arch/arm/src/armv7-r/arm_sigdeliver.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@ void arm_sigdeliver(void)
*/

int16_t saved_irqcount;

enter_critical_section();
#endif

board_autoled_on(LED_SIGNAL);
Expand All @@ -81,17 +79,16 @@ void arm_sigdeliver(void)
*/

saved_irqcount = rtcb->irqcount;
DEBUGASSERT(saved_irqcount >= 1);
DEBUGASSERT(saved_irqcount >= 0);

/* Now we need call leave_critical_section() repeatedly to get the irqcount
* to zero, freeing all global spinlocks that enforce the critical section.
*/

do
while (rtcb->irqcount > 0)
{
leave_critical_section(regs[REG_CPSR]);
}
while (rtcb->irqcount > 0);
#endif /* CONFIG_SMP */

#ifndef CONFIG_SUPPRESS_INTERRUPTS
Expand Down Expand Up @@ -119,7 +116,7 @@ void arm_sigdeliver(void)
*/

DEBUGASSERT(rtcb->irqcount == 0);
while (rtcb->irqcount < saved_irqcount)
while (rtcb->irqcount < saved_irqcount + 1)
{
enter_critical_section();
}
Expand All @@ -132,6 +129,9 @@ void arm_sigdeliver(void)
if (!sq_empty(&rtcb->sigpendactionq) &&
(rtcb->flags & TCB_FLAG_SIGNAL_ACTION) == 0)
{
#ifdef CONFIG_SMP
leave_critical_section(regs[REG_CPSR]);
#endif
goto retry;
}

Expand Down
16 changes: 10 additions & 6 deletions arch/arm/src/armv8-m/arm_sigdeliver.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@ void arm_sigdeliver(void)
*/

int16_t saved_irqcount;

enter_critical_section();
#endif

board_autoled_on(LED_SIGNAL);
Expand All @@ -81,21 +79,20 @@ void arm_sigdeliver(void)
*/

saved_irqcount = rtcb->irqcount;
DEBUGASSERT(saved_irqcount >= 1);
DEBUGASSERT(saved_irqcount >= 0);

/* Now we need call leave_critical_section() repeatedly to get the irqcount
* to zero, freeing all global spinlocks that enforce the critical section.
*/

do
while (rtcb->irqcount > 0)
{
#ifdef CONFIG_ARMV8M_USEBASEPRI
leave_critical_section((uint8_t)regs[REG_BASEPRI]);
#else
leave_critical_section((uint16_t)regs[REG_PRIMASK]);
#endif
}
while (rtcb->irqcount > 0);
#endif /* CONFIG_SMP */

#ifndef CONFIG_SUPPRESS_INTERRUPTS
Expand Down Expand Up @@ -126,7 +123,7 @@ void arm_sigdeliver(void)
*/

DEBUGASSERT(rtcb->irqcount == 0);
while (rtcb->irqcount < saved_irqcount)
while (rtcb->irqcount < saved_irqcount + 1)
{
enter_critical_section();
}
Expand All @@ -139,6 +136,13 @@ void arm_sigdeliver(void)
if (!sq_empty(&rtcb->sigpendactionq) &&
(rtcb->flags & TCB_FLAG_SIGNAL_ACTION) == 0)
{
#ifdef CONFIG_SMP
# ifdef CONFIG_ARMV8M_USEBASEPRI
leave_critical_section((uint8_t)regs[REG_BASEPRI]);
# else
leave_critical_section((uint16_t)regs[REG_PRIMASK]);
# endif
#endif
goto retry;
}

Expand Down
12 changes: 8 additions & 4 deletions arch/arm/src/armv8-r/arm_sigdeliver.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,16 @@ void arm_sigdeliver(void)
*/

saved_irqcount = rtcb->irqcount;
DEBUGASSERT(saved_irqcount >= 1);
DEBUGASSERT(saved_irqcount >= 0);

/* Now we need call leave_critical_section() repeatedly to get the irqcount
* to zero, freeing all global spinlocks that enforce the critical section.
*/

do
while (rtcb->irqcount > 0)
{
leave_critical_section(regs[REG_CPSR]);
}
while (rtcb->irqcount > 0);
#endif /* CONFIG_SMP */

#ifndef CONFIG_SUPPRESS_INTERRUPTS
Expand Down Expand Up @@ -117,7 +116,7 @@ void arm_sigdeliver(void)
*/

DEBUGASSERT(rtcb->irqcount == 0);
while (rtcb->irqcount < saved_irqcount)
while (rtcb->irqcount < saved_irqcount + 1)
{
enter_critical_section();
}
Expand All @@ -130,6 +129,9 @@ void arm_sigdeliver(void)
if (!sq_empty(&rtcb->sigpendactionq) &&
(rtcb->flags & TCB_FLAG_SIGNAL_ACTION) == 0)
{
#ifdef CONFIG_SMP
leave_critical_section(regs[REG_CPSR]);
#endif
goto retry;
}

Expand All @@ -149,6 +151,8 @@ void arm_sigdeliver(void)

board_autoled_off(LED_SIGNAL);
#ifdef CONFIG_SMP
rtcb->irqcount++;
leave_critical_section(regs[REG_CPSR]);
rtcb->irqcount--;
#endif
arm_fullcontextrestore(regs);
Expand Down
11 changes: 6 additions & 5 deletions arch/arm64/src/common/arm64_sigdeliver.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ void arm64_sigdeliver(void)
struct regs_context *pctx =
(struct regs_context *)rtcb->xcp.saved_reg;
flags = (pctx->spsr & SPSR_DAIF_MASK);
enter_critical_section();
#endif

sinfo("rtcb=%p sigdeliver=%p sigpendactionq.head=%p\n",
Expand All @@ -86,17 +85,16 @@ void arm64_sigdeliver(void)
*/

saved_irqcount = rtcb->irqcount;
DEBUGASSERT(saved_irqcount >= 1);
DEBUGASSERT(saved_irqcount >= 0);

/* Now we need call leave_critical_section() repeatedly to get the irqcount
* to zero, freeing all global spinlocks that enforce the critical section.
*/

do
while (rtcb->irqcount > 0)
{
leave_critical_section(flags);
}
while (rtcb->irqcount > 0);
#endif /* CONFIG_SMP */

#ifndef CONFIG_SUPPRESS_INTERRUPTS
Expand Down Expand Up @@ -127,7 +125,7 @@ void arm64_sigdeliver(void)
*/

DEBUGASSERT(rtcb->irqcount == 0);
while (rtcb->irqcount < saved_irqcount)
while (rtcb->irqcount < saved_irqcount + 1)
{
enter_critical_section();
}
Expand All @@ -140,6 +138,9 @@ void arm64_sigdeliver(void)
if (!sq_empty(&rtcb->sigpendactionq) &&
(rtcb->flags & TCB_FLAG_SIGNAL_ACTION) == 0)
{
#ifdef CONFIG_SMP
leave_critical_section(flags);
#endif
goto retry;
}

Expand Down
12 changes: 6 additions & 6 deletions arch/risc-v/src/common/riscv_sigdeliver.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@ void riscv_sigdeliver(void)
*/

int16_t saved_irqcount;

enter_critical_section();
#endif

board_autoled_on(LED_SIGNAL);
Expand All @@ -82,17 +80,16 @@ void riscv_sigdeliver(void)
*/

saved_irqcount = rtcb->irqcount;
DEBUGASSERT(saved_irqcount >= 1);
DEBUGASSERT(saved_irqcount >= 0);

/* Now we need call leave_critical_section() repeatedly to get the irqcount
* to zero, freeing all global spinlocks that enforce the critical section.
*/

do
while (rtcb->irqcount > 0)
{
leave_critical_section(regs[REG_INT_CTX]);
}
while (rtcb->irqcount > 0);
#endif /* CONFIG_SMP */

#ifndef CONFIG_SUPPRESS_INTERRUPTS
Expand Down Expand Up @@ -121,7 +118,7 @@ void riscv_sigdeliver(void)
*/

DEBUGASSERT(rtcb->irqcount == 0);
while (rtcb->irqcount < saved_irqcount)
while (rtcb->irqcount < saved_irqcount + 1)
{
enter_critical_section();
}
Expand All @@ -134,6 +131,9 @@ void riscv_sigdeliver(void)
if (!sq_empty(&rtcb->sigpendactionq) &&
(rtcb->flags & TCB_FLAG_SIGNAL_ACTION) == 0)
{
#ifdef CONFIG_SMP
leave_critical_section(regs[REG_INT_CTX]);
#endif
goto retry;
}

Expand Down
Loading

0 comments on commit cb183d1

Please sign in to comment.