diff options
author | Rusty Russell <rusty@rustcorp.com.au> | 2009-06-13 00:27:02 -0400 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2009-06-12 08:57:02 -0400 |
commit | abd41f037e1a64543000ed73b42f616d04d92700 (patch) | |
tree | d9013e66f4d8fc66fc92ce0587f8d126e156b253 | |
parent | ebf9a5a99c1a464afe0b4dfa64416fc8b273bc5c (diff) |
lguest: fix race in halt code
When the Guest does the LHCALL_HALT hypercall, we go to sleep, expecting
that a timer or the Waker will wake_up_process() us.
But we do it in a stupid way, leaving a classic missing wakeup race.
So split maybe_do_interrupt() into interrupt_pending() and
try_deliver_interrupt(), and check maybe_do_interrupt() and the
"break_out" flag before calling schedule.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
-rw-r--r-- | drivers/lguest/core.c | 14 | ||||
-rw-r--r-- | drivers/lguest/interrupts_and_traps.c | 26 | ||||
-rw-r--r-- | drivers/lguest/lg.h | 3 |
3 files changed, 31 insertions, 12 deletions
diff --git a/drivers/lguest/core.c b/drivers/lguest/core.c index 4845fb3cf74..8ca1def5b14 100644 --- a/drivers/lguest/core.c +++ b/drivers/lguest/core.c | |||
@@ -188,6 +188,8 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user) | |||
188 | { | 188 | { |
189 | /* We stop running once the Guest is dead. */ | 189 | /* We stop running once the Guest is dead. */ |
190 | while (!cpu->lg->dead) { | 190 | while (!cpu->lg->dead) { |
191 | unsigned int irq; | ||
192 | |||
191 | /* First we run any hypercalls the Guest wants done. */ | 193 | /* First we run any hypercalls the Guest wants done. */ |
192 | if (cpu->hcall) | 194 | if (cpu->hcall) |
193 | do_hypercalls(cpu); | 195 | do_hypercalls(cpu); |
@@ -211,7 +213,9 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user) | |||
211 | /* Check if there are any interrupts which can be delivered now: | 213 | /* Check if there are any interrupts which can be delivered now: |
212 | * if so, this sets up the hander to be executed when we next | 214 | * if so, this sets up the hander to be executed when we next |
213 | * run the Guest. */ | 215 | * run the Guest. */ |
214 | maybe_do_interrupt(cpu); | 216 | irq = interrupt_pending(cpu); |
217 | if (irq < LGUEST_IRQS) | ||
218 | try_deliver_interrupt(cpu, irq); | ||
215 | 219 | ||
216 | /* All long-lived kernel loops need to check with this horrible | 220 | /* All long-lived kernel loops need to check with this horrible |
217 | * thing called the freezer. If the Host is trying to suspend, | 221 | * thing called the freezer. If the Host is trying to suspend, |
@@ -227,7 +231,13 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user) | |||
227 | * clock timer or LHREQ_BREAK from the Waker will wake us. */ | 231 | * clock timer or LHREQ_BREAK from the Waker will wake us. */ |
228 | if (cpu->halted) { | 232 | if (cpu->halted) { |
229 | set_current_state(TASK_INTERRUPTIBLE); | 233 | set_current_state(TASK_INTERRUPTIBLE); |
230 | schedule(); | 234 | /* Just before we sleep, make sure nothing snuck in |
235 | * which we should be doing. */ | ||
236 | if (interrupt_pending(cpu) < LGUEST_IRQS | ||
237 | || cpu->break_out) | ||
238 | set_current_state(TASK_RUNNING); | ||
239 | else | ||
240 | schedule(); | ||
231 | continue; | 241 | continue; |
232 | } | 242 | } |
233 | 243 | ||
diff --git a/drivers/lguest/interrupts_and_traps.c b/drivers/lguest/interrupts_and_traps.c index 9ea26ad88c9..a8c966fee1e 100644 --- a/drivers/lguest/interrupts_and_traps.c +++ b/drivers/lguest/interrupts_and_traps.c | |||
@@ -128,30 +128,38 @@ static void set_guest_interrupt(struct lg_cpu *cpu, u32 lo, u32 hi, | |||
128 | /*H:205 | 128 | /*H:205 |
129 | * Virtual Interrupts. | 129 | * Virtual Interrupts. |
130 | * | 130 | * |
131 | * maybe_do_interrupt() gets called before every entry to the Guest, to see if | 131 | * interrupt_pending() returns the first pending interrupt which isn't blocked |
132 | * we should divert the Guest to running an interrupt handler. */ | 132 | * by the Guest. It is called before every entry to the Guest, and just before |
133 | void maybe_do_interrupt(struct lg_cpu *cpu) | 133 | * we go to sleep when the Guest has halted itself. */ |
134 | unsigned int interrupt_pending(struct lg_cpu *cpu) | ||
134 | { | 135 | { |
135 | unsigned int irq; | 136 | unsigned int irq; |
136 | DECLARE_BITMAP(blk, LGUEST_IRQS); | 137 | DECLARE_BITMAP(blk, LGUEST_IRQS); |
137 | struct desc_struct *idt; | ||
138 | 138 | ||
139 | /* If the Guest hasn't even initialized yet, we can do nothing. */ | 139 | /* If the Guest hasn't even initialized yet, we can do nothing. */ |
140 | if (!cpu->lg->lguest_data) | 140 | if (!cpu->lg->lguest_data) |
141 | return; | 141 | return LGUEST_IRQS; |
142 | 142 | ||
143 | /* Take our "irqs_pending" array and remove any interrupts the Guest | 143 | /* Take our "irqs_pending" array and remove any interrupts the Guest |
144 | * wants blocked: the result ends up in "blk". */ | 144 | * wants blocked: the result ends up in "blk". */ |
145 | if (copy_from_user(&blk, cpu->lg->lguest_data->blocked_interrupts, | 145 | if (copy_from_user(&blk, cpu->lg->lguest_data->blocked_interrupts, |
146 | sizeof(blk))) | 146 | sizeof(blk))) |
147 | return; | 147 | return LGUEST_IRQS; |
148 | bitmap_andnot(blk, cpu->irqs_pending, blk, LGUEST_IRQS); | 148 | bitmap_andnot(blk, cpu->irqs_pending, blk, LGUEST_IRQS); |
149 | 149 | ||
150 | /* Find the first interrupt. */ | 150 | /* Find the first interrupt. */ |
151 | irq = find_first_bit(blk, LGUEST_IRQS); | 151 | irq = find_first_bit(blk, LGUEST_IRQS); |
152 | /* None? Nothing to do */ | 152 | |
153 | if (irq >= LGUEST_IRQS) | 153 | return irq; |
154 | return; | 154 | } |
155 | |||
156 | /* This actually diverts the Guest to running an interrupt handler, once an | ||
157 | * interrupt has been identified by interrupt_pending(). */ | ||
158 | void try_deliver_interrupt(struct lg_cpu *cpu, unsigned int irq) | ||
159 | { | ||
160 | struct desc_struct *idt; | ||
161 | |||
162 | BUG_ON(irq >= LGUEST_IRQS); | ||
155 | 163 | ||
156 | /* They may be in the middle of an iret, where they asked us never to | 164 | /* They may be in the middle of an iret, where they asked us never to |
157 | * deliver interrupts. */ | 165 | * deliver interrupts. */ |
diff --git a/drivers/lguest/lg.h b/drivers/lguest/lg.h index af92a176697..6743cf147d9 100644 --- a/drivers/lguest/lg.h +++ b/drivers/lguest/lg.h | |||
@@ -139,7 +139,8 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user); | |||
139 | #define pgd_pfn(x) (pgd_val(x) >> PAGE_SHIFT) | 139 | #define pgd_pfn(x) (pgd_val(x) >> PAGE_SHIFT) |
140 | 140 | ||
141 | /* interrupts_and_traps.c: */ | 141 | /* interrupts_and_traps.c: */ |
142 | void maybe_do_interrupt(struct lg_cpu *cpu); | 142 | unsigned int interrupt_pending(struct lg_cpu *cpu); |
143 | void try_deliver_interrupt(struct lg_cpu *cpu, unsigned int irq); | ||
143 | bool deliver_trap(struct lg_cpu *cpu, unsigned int num); | 144 | bool deliver_trap(struct lg_cpu *cpu, unsigned int num); |
144 | void load_guest_idt_entry(struct lg_cpu *cpu, unsigned int i, | 145 | void load_guest_idt_entry(struct lg_cpu *cpu, unsigned int i, |
145 | u32 low, u32 hi); | 146 | u32 low, u32 hi); |