Skip to content

Commit ed78646

Browse files
hujun260Alan Carvalho de Assis
authored andcommitted
signal: fix deadlock when sigdeliver call enter_critical_section
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 <hujun5@xiaomi.com>
1 parent b55ed92 commit ed78646

File tree

10 files changed

+69
-56
lines changed

10 files changed

+69
-56
lines changed

arch/arm/src/armv6-m/arm_sigdeliver.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@ void arm_sigdeliver(void)
6363
*/
6464

6565
int16_t saved_irqcount;
66-
67-
enter_critical_section();
6866
#endif
6967

7068
board_autoled_on(LED_SIGNAL);
@@ -81,17 +79,16 @@ void arm_sigdeliver(void)
8179
*/
8280

8381
saved_irqcount = rtcb->irqcount;
84-
DEBUGASSERT(saved_irqcount >= 1);
82+
DEBUGASSERT(saved_irqcount >= 0);
8583

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

90-
do
88+
while (rtcb->irqcount > 0)
9189
{
9290
leave_critical_section((uint16_t)regs[REG_PRIMASK]);
9391
}
94-
while (rtcb->irqcount > 0);
9592
#endif /* CONFIG_SMP */
9693

9794
#ifndef CONFIG_SUPPRESS_INTERRUPTS
@@ -122,7 +119,7 @@ void arm_sigdeliver(void)
122119
*/
123120

124121
DEBUGASSERT(rtcb->irqcount == 0);
125-
while (rtcb->irqcount < saved_irqcount)
122+
while (rtcb->irqcount < saved_irqcount + 1)
126123
{
127124
enter_critical_section();
128125
}
@@ -135,6 +132,9 @@ void arm_sigdeliver(void)
135132
if (!sq_empty(&rtcb->sigpendactionq) &&
136133
(rtcb->flags & TCB_FLAG_SIGNAL_ACTION) == 0)
137134
{
135+
#ifdef CONFIG_SMP
136+
leave_critical_section((uint16_t)regs[REG_PRIMASK]);
137+
#endif
138138
goto retry;
139139
}
140140

arch/arm/src/armv7-a/arm_sigdeliver.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@ void arm_sigdeliver(void)
6363
*/
6464

6565
int16_t saved_irqcount;
66-
67-
enter_critical_section();
6866
#endif
6967

7068
board_autoled_on(LED_SIGNAL);
@@ -81,17 +79,16 @@ void arm_sigdeliver(void)
8179
*/
8280

8381
saved_irqcount = rtcb->irqcount;
84-
DEBUGASSERT(saved_irqcount >= 1);
82+
DEBUGASSERT(saved_irqcount >= 0);
8583

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

90-
do
88+
while (rtcb->irqcount > 0)
9189
{
9290
leave_critical_section(regs[REG_CPSR]);
9391
}
94-
while (rtcb->irqcount > 0);
9592
#endif /* CONFIG_SMP */
9693

9794
#ifndef CONFIG_SUPPRESS_INTERRUPTS
@@ -122,7 +119,7 @@ void arm_sigdeliver(void)
122119
*/
123120

124121
DEBUGASSERT(rtcb->irqcount == 0);
125-
while (rtcb->irqcount < saved_irqcount)
122+
while (rtcb->irqcount < saved_irqcount + 1)
126123
{
127124
enter_critical_section();
128125
}
@@ -135,6 +132,9 @@ void arm_sigdeliver(void)
135132
if (!sq_empty(&rtcb->sigpendactionq) &&
136133
(rtcb->flags & TCB_FLAG_SIGNAL_ACTION) == 0)
137134
{
135+
#ifdef CONFIG_SMP
136+
leave_critical_section(regs[REG_CPSR]);
137+
#endif
138138
goto retry;
139139
}
140140

arch/arm/src/armv7-m/arm_sigdeliver.c

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@ void arm_sigdeliver(void)
6363
*/
6464

6565
int16_t saved_irqcount;
66-
67-
enter_critical_section();
6866
#endif
6967

7068
board_autoled_on(LED_SIGNAL);
@@ -81,21 +79,20 @@ void arm_sigdeliver(void)
8179
*/
8280

8381
saved_irqcount = rtcb->irqcount;
84-
DEBUGASSERT(saved_irqcount >= 1);
82+
DEBUGASSERT(saved_irqcount >= 0);
8583

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

90-
do
88+
while (rtcb->irqcount > 0)
9189
{
9290
#ifdef CONFIG_ARMV7M_USEBASEPRI
9391
leave_critical_section((uint8_t)regs[REG_BASEPRI]);
9492
#else
9593
leave_critical_section((uint16_t)regs[REG_PRIMASK]);
9694
#endif
9795
}
98-
while (rtcb->irqcount > 0);
9996
#endif /* CONFIG_SMP */
10097

10198
#ifndef CONFIG_SUPPRESS_INTERRUPTS
@@ -126,7 +123,7 @@ void arm_sigdeliver(void)
126123
*/
127124

128125
DEBUGASSERT(rtcb->irqcount == 0);
129-
while (rtcb->irqcount < saved_irqcount)
126+
while (rtcb->irqcount < saved_irqcount + 1)
130127
{
131128
enter_critical_section();
132129
}
@@ -139,6 +136,13 @@ void arm_sigdeliver(void)
139136
if (!sq_empty(&rtcb->sigpendactionq) &&
140137
(rtcb->flags & TCB_FLAG_SIGNAL_ACTION) == 0)
141138
{
139+
#ifdef CONFIG_SMP
140+
# ifdef CONFIG_ARMV7M_USEBASEPRI
141+
leave_critical_section((uint8_t)regs[REG_BASEPRI]);
142+
# else
143+
leave_critical_section((uint16_t)regs[REG_PRIMASK]);
144+
# endif
145+
#endif
142146
goto retry;
143147
}
144148

arch/arm/src/armv7-r/arm_sigdeliver.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@ void arm_sigdeliver(void)
6363
*/
6464

6565
int16_t saved_irqcount;
66-
67-
enter_critical_section();
6866
#endif
6967

7068
board_autoled_on(LED_SIGNAL);
@@ -81,17 +79,16 @@ void arm_sigdeliver(void)
8179
*/
8280

8381
saved_irqcount = rtcb->irqcount;
84-
DEBUGASSERT(saved_irqcount >= 1);
82+
DEBUGASSERT(saved_irqcount >= 0);
8583

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

90-
do
88+
while (rtcb->irqcount > 0)
9189
{
9290
leave_critical_section(regs[REG_CPSR]);
9391
}
94-
while (rtcb->irqcount > 0);
9592
#endif /* CONFIG_SMP */
9693

9794
#ifndef CONFIG_SUPPRESS_INTERRUPTS
@@ -119,7 +116,7 @@ void arm_sigdeliver(void)
119116
*/
120117

121118
DEBUGASSERT(rtcb->irqcount == 0);
122-
while (rtcb->irqcount < saved_irqcount)
119+
while (rtcb->irqcount < saved_irqcount + 1)
123120
{
124121
enter_critical_section();
125122
}
@@ -132,6 +129,9 @@ void arm_sigdeliver(void)
132129
if (!sq_empty(&rtcb->sigpendactionq) &&
133130
(rtcb->flags & TCB_FLAG_SIGNAL_ACTION) == 0)
134131
{
132+
#ifdef CONFIG_SMP
133+
leave_critical_section(regs[REG_CPSR]);
134+
#endif
135135
goto retry;
136136
}
137137

arch/arm/src/armv8-m/arm_sigdeliver.c

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@ void arm_sigdeliver(void)
6363
*/
6464

6565
int16_t saved_irqcount;
66-
67-
enter_critical_section();
6866
#endif
6967

7068
board_autoled_on(LED_SIGNAL);
@@ -81,21 +79,20 @@ void arm_sigdeliver(void)
8179
*/
8280

8381
saved_irqcount = rtcb->irqcount;
84-
DEBUGASSERT(saved_irqcount >= 1);
82+
DEBUGASSERT(saved_irqcount >= 0);
8583

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

90-
do
88+
while (rtcb->irqcount > 0)
9189
{
9290
#ifdef CONFIG_ARMV8M_USEBASEPRI
9391
leave_critical_section((uint8_t)regs[REG_BASEPRI]);
9492
#else
9593
leave_critical_section((uint16_t)regs[REG_PRIMASK]);
9694
#endif
9795
}
98-
while (rtcb->irqcount > 0);
9996
#endif /* CONFIG_SMP */
10097

10198
#ifndef CONFIG_SUPPRESS_INTERRUPTS
@@ -126,7 +123,7 @@ void arm_sigdeliver(void)
126123
*/
127124

128125
DEBUGASSERT(rtcb->irqcount == 0);
129-
while (rtcb->irqcount < saved_irqcount)
126+
while (rtcb->irqcount < saved_irqcount + 1)
130127
{
131128
enter_critical_section();
132129
}
@@ -139,6 +136,13 @@ void arm_sigdeliver(void)
139136
if (!sq_empty(&rtcb->sigpendactionq) &&
140137
(rtcb->flags & TCB_FLAG_SIGNAL_ACTION) == 0)
141138
{
139+
#ifdef CONFIG_SMP
140+
# ifdef CONFIG_ARMV8M_USEBASEPRI
141+
leave_critical_section((uint8_t)regs[REG_BASEPRI]);
142+
# else
143+
leave_critical_section((uint16_t)regs[REG_PRIMASK]);
144+
# endif
145+
#endif
142146
goto retry;
143147
}
144148

arch/arm/src/armv8-r/arm_sigdeliver.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,17 +79,16 @@ void arm_sigdeliver(void)
7979
*/
8080

8181
saved_irqcount = rtcb->irqcount;
82-
DEBUGASSERT(saved_irqcount >= 1);
82+
DEBUGASSERT(saved_irqcount >= 0);
8383

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

88-
do
88+
while (rtcb->irqcount > 0)
8989
{
9090
leave_critical_section(regs[REG_CPSR]);
9191
}
92-
while (rtcb->irqcount > 0);
9392
#endif /* CONFIG_SMP */
9493

9594
#ifndef CONFIG_SUPPRESS_INTERRUPTS
@@ -117,7 +116,7 @@ void arm_sigdeliver(void)
117116
*/
118117

119118
DEBUGASSERT(rtcb->irqcount == 0);
120-
while (rtcb->irqcount < saved_irqcount)
119+
while (rtcb->irqcount < saved_irqcount + 1)
121120
{
122121
enter_critical_section();
123122
}
@@ -130,6 +129,9 @@ void arm_sigdeliver(void)
130129
if (!sq_empty(&rtcb->sigpendactionq) &&
131130
(rtcb->flags & TCB_FLAG_SIGNAL_ACTION) == 0)
132131
{
132+
#ifdef CONFIG_SMP
133+
leave_critical_section(regs[REG_CPSR]);
134+
#endif
133135
goto retry;
134136
}
135137

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

150152
board_autoled_off(LED_SIGNAL);
151153
#ifdef CONFIG_SMP
154+
rtcb->irqcount++;
155+
leave_critical_section(regs[REG_CPSR]);
152156
rtcb->irqcount--;
153157
#endif
154158
arm_fullcontextrestore(regs);

arch/arm64/src/common/arm64_sigdeliver.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ void arm64_sigdeliver(void)
7171
struct regs_context *pctx =
7272
(struct regs_context *)rtcb->xcp.saved_reg;
7373
flags = (pctx->spsr & SPSR_DAIF_MASK);
74-
enter_critical_section();
7574
#endif
7675

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

8887
saved_irqcount = rtcb->irqcount;
89-
DEBUGASSERT(saved_irqcount >= 1);
88+
DEBUGASSERT(saved_irqcount >= 0);
9089

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

95-
do
94+
while (rtcb->irqcount > 0)
9695
{
9796
leave_critical_section(flags);
9897
}
99-
while (rtcb->irqcount > 0);
10098
#endif /* CONFIG_SMP */
10199

102100
#ifndef CONFIG_SUPPRESS_INTERRUPTS
@@ -127,7 +125,7 @@ void arm64_sigdeliver(void)
127125
*/
128126

129127
DEBUGASSERT(rtcb->irqcount == 0);
130-
while (rtcb->irqcount < saved_irqcount)
128+
while (rtcb->irqcount < saved_irqcount + 1)
131129
{
132130
enter_critical_section();
133131
}
@@ -140,6 +138,9 @@ void arm64_sigdeliver(void)
140138
if (!sq_empty(&rtcb->sigpendactionq) &&
141139
(rtcb->flags & TCB_FLAG_SIGNAL_ACTION) == 0)
142140
{
141+
#ifdef CONFIG_SMP
142+
leave_critical_section(flags);
143+
#endif
143144
goto retry;
144145
}
145146

arch/risc-v/src/common/riscv_sigdeliver.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,6 @@ void riscv_sigdeliver(void)
6464
*/
6565

6666
int16_t saved_irqcount;
67-
68-
enter_critical_section();
6967
#endif
7068

7169
board_autoled_on(LED_SIGNAL);
@@ -82,17 +80,16 @@ void riscv_sigdeliver(void)
8280
*/
8381

8482
saved_irqcount = rtcb->irqcount;
85-
DEBUGASSERT(saved_irqcount >= 1);
83+
DEBUGASSERT(saved_irqcount >= 0);
8684

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

91-
do
89+
while (rtcb->irqcount > 0)
9290
{
9391
leave_critical_section(regs[REG_INT_CTX]);
9492
}
95-
while (rtcb->irqcount > 0);
9693
#endif /* CONFIG_SMP */
9794

9895
#ifndef CONFIG_SUPPRESS_INTERRUPTS
@@ -121,7 +118,7 @@ void riscv_sigdeliver(void)
121118
*/
122119

123120
DEBUGASSERT(rtcb->irqcount == 0);
124-
while (rtcb->irqcount < saved_irqcount)
121+
while (rtcb->irqcount < saved_irqcount + 1)
125122
{
126123
enter_critical_section();
127124
}
@@ -134,6 +131,9 @@ void riscv_sigdeliver(void)
134131
if (!sq_empty(&rtcb->sigpendactionq) &&
135132
(rtcb->flags & TCB_FLAG_SIGNAL_ACTION) == 0)
136133
{
134+
#ifdef CONFIG_SMP
135+
leave_critical_section(regs[REG_INT_CTX]);
136+
#endif
137137
goto retry;
138138
}
139139

0 commit comments

Comments
 (0)