Skip to content

Commit ac5f7db

Browse files
marcrittinghausunikraft-bot
authored andcommitted
plat/kvm/x86: Fix lost IRQs
There is a race condition in `ukplat_lcpu_halt_irq()`, where an IRQ may fire in between the short window after which interrupts are enabled and before the halt is done. This may halt the caller forever (or much longer until the next interrupt). As this issue is platform and architecture specific, this commit moves `ukplat_lcpu_halt_irq()` from the common code to the plat/arch-specific implementation. For x86 the race condition is avoided by ensuring that there are no instructions between `STI` and `HLT`. Signed-off-by: Marc Rittinghaus <[email protected]> Reviewed-by: Simon Kuenzer <[email protected]> Tested-by: Unikraft CI <[email protected]> GitHub-Pull-Request: unikraft#257
1 parent 5b8ca84 commit ac5f7db

File tree

6 files changed

+42
-9
lines changed

6 files changed

+42
-9
lines changed

plat/common/include/x86/irq.h

+6
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@
4949
asm volatile("sti" : : : "memory"); \
5050
})
5151

52+
#define __sti_hlt() \
53+
({ \
54+
asm volatile("sti; hlt" : : : "memory"); \
55+
})
56+
5257
#define __save_flags(x) \
5358
do { \
5459
unsigned long __f; \
@@ -83,6 +88,7 @@ static inline int irqs_disabled(void)
8388
#define local_save_flags(x) __save_flags(x)
8489
#define local_irq_disable() __cli()
8590
#define local_irq_enable() __sti()
91+
#define local_irq_enable_halt() __sti_hlt()
8692

8793
#define __MAX_IRQ 16
8894

plat/common/lcpu.c

-8
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,12 @@
3434
#include <uk/plat/common/cpu.h>
3535
#include <uk/plat/common/_time.h>
3636

37-
3837
void ukplat_lcpu_halt(void)
3938
{
4039
ukplat_lcpu_disable_irq();
4140
halt();
4241
}
4342

44-
void ukplat_lcpu_halt_irq(void)
45-
{
46-
ukplat_lcpu_enable_irq();
47-
halt();
48-
ukplat_lcpu_disable_irq();
49-
}
50-
5143
void ukplat_lcpu_halt_to(__snsec until)
5244
{
5345
unsigned long flags;

plat/kvm/arm/lcpu.c

+7
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ void ukplat_lcpu_disable_irq(void)
4343
local_irq_disable();
4444
}
4545

46+
void ukplat_lcpu_halt_irq(void)
47+
{
48+
ukplat_lcpu_enable_irq();
49+
halt();
50+
ukplat_lcpu_disable_irq();
51+
}
52+
4653
unsigned long ukplat_lcpu_save_irqf(void)
4754
{
4855
unsigned long flags;

plat/kvm/x86/lcpu.c

+15-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
#include <uk/plat/lcpu.h>
3636
#include <x86/irq.h>
3737

38-
3938
void ukplat_lcpu_enable_irq(void)
4039
{
4140
local_irq_enable();
@@ -46,6 +45,21 @@ void ukplat_lcpu_disable_irq(void)
4645
local_irq_disable();
4746
}
4847

48+
void ukplat_lcpu_halt_irq(void)
49+
{
50+
/*
51+
* We have to be careful when enabling interrupts before entering a
52+
* halt state. If we want to wait for an interrupt (e.g., a timer)
53+
* the interrupt may fire in the short window between sti and hlt and
54+
* we are going to halt forever. As sti only enables interrupts after
55+
* the following instruction, we can avoid the race condition by
56+
* ensuring that hlt immediately follows sti. There must be no
57+
* instruction in between.
58+
*/
59+
local_irq_enable_halt();
60+
local_irq_disable();
61+
}
62+
4963
unsigned long ukplat_lcpu_save_irqf(void)
5064
{
5165
unsigned long flags;

plat/linuxu/irq.c

+7
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,13 @@ void ukplat_lcpu_disable_irq(void)
7979
irq_enabled = 0;
8080
}
8181

82+
void ukplat_lcpu_halt_irq(void)
83+
{
84+
ukplat_lcpu_enable_irq();
85+
halt();
86+
ukplat_lcpu_disable_irq();
87+
}
88+
8289
int ukplat_lcpu_irqs_disabled(void)
8390
{
8491
return (irq_enabled == 0);

plat/xen/lcpu.c

+7
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ void ukplat_lcpu_disable_irq(void)
5151
local_irq_disable();
5252
}
5353

54+
void ukplat_lcpu_halt_irq(void)
55+
{
56+
ukplat_lcpu_enable_irq();
57+
halt();
58+
ukplat_lcpu_disable_irq();
59+
}
60+
5461
unsigned long ukplat_lcpu_save_irqf(void)
5562
{
5663
unsigned long flags;

0 commit comments

Comments
 (0)