diff options
author | Rusty Russell <rusty@rustcorp.com.au> | 2007-10-21 21:03:30 -0400 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2007-10-23 01:49:52 -0400 |
commit | cc6d4fbcef328acdc9fa7023e69f39f753f72fe1 (patch) | |
tree | 860672e7da1a3516e36dd40f962552451ef0bcf2 | |
parent | 4614a3a3b638dfd7a67d0237944f6a76331af61d (diff) |
Introduce "hcall" pointer to indicate pending hypercall.
Currently we look at the "trapnum" to see if the Guest wants a
hypercall. But once the hypercall is done we have to reset trapnum to
a bogus value, otherwise if we exit to userspace and return, we'd run
the same hypercall twice (that was a nasty bug to find!).
This has two main effects:
1) When Jes's patch changes the hypercall args to be a generic "struct
hcall_args" we simply change the type of "lg->hcall". It's set by
arch code, so if it has to copy args or something it can do so, and
point "hcall" into lg->arch somewhere.
2) Async hypercalls only get run when an actual hypercall is pending.
This simplfies the code a little and is a more logical semantic.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
-rw-r--r-- | drivers/lguest/core.c | 8 | ||||
-rw-r--r-- | drivers/lguest/hypercalls.c | 48 | ||||
-rw-r--r-- | drivers/lguest/lg.h | 3 | ||||
-rw-r--r-- | drivers/lguest/x86/core.c | 13 |
4 files changed, 34 insertions, 38 deletions
diff --git a/drivers/lguest/core.c b/drivers/lguest/core.c index 06869a2d3b40..02556bae9e9f 100644 --- a/drivers/lguest/core.c +++ b/drivers/lguest/core.c | |||
@@ -198,10 +198,10 @@ int run_guest(struct lguest *lg, unsigned long __user *user) | |||
198 | { | 198 | { |
199 | /* We stop running once the Guest is dead. */ | 199 | /* We stop running once the Guest is dead. */ |
200 | while (!lg->dead) { | 200 | while (!lg->dead) { |
201 | /* First we run any hypercalls the Guest wants done: either in | 201 | /* First we run any hypercalls the Guest wants done. */ |
202 | * the hypercall ring in "struct lguest_data", or directly by | 202 | if (lg->hcall) |
203 | * using int 31 (LGUEST_TRAP_ENTRY). */ | 203 | do_hypercalls(lg); |
204 | do_hypercalls(lg); | 204 | |
205 | /* It's possible the Guest did a SEND_DMA hypercall to the | 205 | /* It's possible the Guest did a SEND_DMA hypercall to the |
206 | * Launcher, in which case we return from the read() now. */ | 206 | * Launcher, in which case we return from the read() now. */ |
207 | if (lg->dma_is_pending) { | 207 | if (lg->dma_is_pending) { |
diff --git a/drivers/lguest/hypercalls.c b/drivers/lguest/hypercalls.c index 8bde20934f91..0175a9f03347 100644 --- a/drivers/lguest/hypercalls.c +++ b/drivers/lguest/hypercalls.c | |||
@@ -241,19 +241,6 @@ static void initialize(struct lguest *lg) | |||
241 | * is one other way we can do things for the Guest, as we see in | 241 | * is one other way we can do things for the Guest, as we see in |
242 | * emulate_insn(). */ | 242 | * emulate_insn(). */ |
243 | 243 | ||
244 | /*H:110 Tricky point: we mark the hypercall as "done" once we've done it. | ||
245 | * Normally we don't need to do this: the Guest will run again and update the | ||
246 | * trap number before we come back around the run_guest() loop to | ||
247 | * do_hypercalls(). | ||
248 | * | ||
249 | * However, if we are signalled or the Guest sends DMA to the Launcher, that | ||
250 | * loop will exit without running the Guest. When it comes back it would try | ||
251 | * to re-run the hypercall. */ | ||
252 | static void clear_hcall(struct lguest *lg) | ||
253 | { | ||
254 | lg->regs->trapnum = 255; | ||
255 | } | ||
256 | |||
257 | /*H:100 | 244 | /*H:100 |
258 | * Hypercalls | 245 | * Hypercalls |
259 | * | 246 | * |
@@ -262,16 +249,12 @@ static void clear_hcall(struct lguest *lg) | |||
262 | */ | 249 | */ |
263 | void do_hypercalls(struct lguest *lg) | 250 | void do_hypercalls(struct lguest *lg) |
264 | { | 251 | { |
265 | /* Not initialized yet? */ | 252 | /* Not initialized yet? This hypercall must do it. */ |
266 | if (unlikely(!lg->lguest_data)) { | 253 | if (unlikely(!lg->lguest_data)) { |
267 | /* Did the Guest make a hypercall? We might have come back for | 254 | /* Set up the "struct lguest_data" */ |
268 | * some other reason (an interrupt, a different trap). */ | 255 | initialize(lg); |
269 | if (lg->regs->trapnum == LGUEST_TRAP_ENTRY) { | 256 | /* Hcall is done. */ |
270 | /* Set up the "struct lguest_data" */ | 257 | lg->hcall = NULL; |
271 | initialize(lg); | ||
272 | /* The hypercall is done. */ | ||
273 | clear_hcall(lg); | ||
274 | } | ||
275 | return; | 258 | return; |
276 | } | 259 | } |
277 | 260 | ||
@@ -281,12 +264,21 @@ void do_hypercalls(struct lguest *lg) | |||
281 | do_async_hcalls(lg); | 264 | do_async_hcalls(lg); |
282 | 265 | ||
283 | /* If we stopped reading the hypercall ring because the Guest did a | 266 | /* If we stopped reading the hypercall ring because the Guest did a |
284 | * SEND_DMA to the Launcher, we want to return now. Otherwise if the | 267 | * SEND_DMA to the Launcher, we want to return now. Otherwise we do |
285 | * Guest asked us to do a hypercall, we do it. */ | 268 | * the hypercall. */ |
286 | if (!lg->dma_is_pending && lg->regs->trapnum == LGUEST_TRAP_ENTRY) { | 269 | if (!lg->dma_is_pending) { |
287 | do_hcall(lg, lg->regs); | 270 | do_hcall(lg, lg->hcall); |
288 | /* The hypercall is done. */ | 271 | /* Tricky point: we reset the hcall pointer to mark the |
289 | clear_hcall(lg); | 272 | * hypercall as "done". We use the hcall pointer rather than |
273 | * the trap number to indicate a hypercall is pending. | ||
274 | * Normally it doesn't matter: the Guest will run again and | ||
275 | * update the trap number before we come back here. | ||
276 | * | ||
277 | * However, if we are signalled or the Guest sends DMA to the | ||
278 | * Launcher, the run_guest() loop will exit without running the | ||
279 | * Guest. When it comes back it would try to re-run the | ||
280 | * hypercall. */ | ||
281 | lg->hcall = NULL; | ||
290 | } | 282 | } |
291 | } | 283 | } |
292 | 284 | ||
diff --git a/drivers/lguest/lg.h b/drivers/lguest/lg.h index 203d3100c3b4..662994b776cc 100644 --- a/drivers/lguest/lg.h +++ b/drivers/lguest/lg.h | |||
@@ -106,6 +106,9 @@ struct lguest | |||
106 | u32 esp1; | 106 | u32 esp1; |
107 | u8 ss1; | 107 | u8 ss1; |
108 | 108 | ||
109 | /* If a hypercall was asked for, this points to the arguments. */ | ||
110 | struct lguest_regs *hcall; | ||
111 | |||
109 | /* Do we need to stop what we're doing and return to userspace? */ | 112 | /* Do we need to stop what we're doing and return to userspace? */ |
110 | int break_out; | 113 | int break_out; |
111 | wait_queue_head_t break_wq; | 114 | wait_queue_head_t break_wq; |
diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c index e2f46b16ce31..0cc251cbc72a 100644 --- a/drivers/lguest/x86/core.c +++ b/drivers/lguest/x86/core.c | |||
@@ -316,13 +316,14 @@ void lguest_arch_handle_trap(struct lguest *lg) | |||
316 | return; | 316 | return; |
317 | break; | 317 | break; |
318 | case 32 ... 255: | 318 | case 32 ... 255: |
319 | /* These values mean a real interrupt occurred, in | 319 | /* These values mean a real interrupt occurred, in which case |
320 | * which case the Host handler has already been run. | 320 | * the Host handler has already been run. We just do a |
321 | * We just do a friendly check if another process | 321 | * friendly check if another process should now be run, then |
322 | * should now be run, then fall through to loop | 322 | * return to run the Guest again */ |
323 | * around: */ | ||
324 | cond_resched(); | 323 | cond_resched(); |
325 | case LGUEST_TRAP_ENTRY: /* Handled before re-entering Guest */ | 324 | return; |
325 | case LGUEST_TRAP_ENTRY: | ||
326 | lg->hcall = lg->regs; | ||
326 | return; | 327 | return; |
327 | } | 328 | } |
328 | 329 | ||