mirror of https://gitee.com/openkylin/linux.git
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>
This commit is contained in:
parent
4614a3a3b6
commit
cc6d4fbcef
|
@ -198,10 +198,10 @@ int run_guest(struct lguest *lg, unsigned long __user *user)
|
||||||
{
|
{
|
||||||
/* We stop running once the Guest is dead. */
|
/* We stop running once the Guest is dead. */
|
||||||
while (!lg->dead) {
|
while (!lg->dead) {
|
||||||
/* First we run any hypercalls the Guest wants done: either in
|
/* First we run any hypercalls the Guest wants done. */
|
||||||
* the hypercall ring in "struct lguest_data", or directly by
|
if (lg->hcall)
|
||||||
* using int 31 (LGUEST_TRAP_ENTRY). */
|
|
||||||
do_hypercalls(lg);
|
do_hypercalls(lg);
|
||||||
|
|
||||||
/* It's possible the Guest did a SEND_DMA hypercall to the
|
/* It's possible the Guest did a SEND_DMA hypercall to the
|
||||||
* Launcher, in which case we return from the read() now. */
|
* Launcher, in which case we return from the read() now. */
|
||||||
if (lg->dma_is_pending) {
|
if (lg->dma_is_pending) {
|
||||||
|
|
|
@ -241,19 +241,6 @@ static void initialize(struct lguest *lg)
|
||||||
* is one other way we can do things for the Guest, as we see in
|
* is one other way we can do things for the Guest, as we see in
|
||||||
* emulate_insn(). */
|
* emulate_insn(). */
|
||||||
|
|
||||||
/*H:110 Tricky point: we mark the hypercall as "done" once we've done it.
|
|
||||||
* Normally we don't need to do this: the Guest will run again and update the
|
|
||||||
* trap number before we come back around the run_guest() loop to
|
|
||||||
* do_hypercalls().
|
|
||||||
*
|
|
||||||
* However, if we are signalled or the Guest sends DMA to the Launcher, that
|
|
||||||
* loop will exit without running the Guest. When it comes back it would try
|
|
||||||
* to re-run the hypercall. */
|
|
||||||
static void clear_hcall(struct lguest *lg)
|
|
||||||
{
|
|
||||||
lg->regs->trapnum = 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*H:100
|
/*H:100
|
||||||
* Hypercalls
|
* Hypercalls
|
||||||
*
|
*
|
||||||
|
@ -262,16 +249,12 @@ static void clear_hcall(struct lguest *lg)
|
||||||
*/
|
*/
|
||||||
void do_hypercalls(struct lguest *lg)
|
void do_hypercalls(struct lguest *lg)
|
||||||
{
|
{
|
||||||
/* Not initialized yet? */
|
/* Not initialized yet? This hypercall must do it. */
|
||||||
if (unlikely(!lg->lguest_data)) {
|
if (unlikely(!lg->lguest_data)) {
|
||||||
/* Did the Guest make a hypercall? We might have come back for
|
|
||||||
* some other reason (an interrupt, a different trap). */
|
|
||||||
if (lg->regs->trapnum == LGUEST_TRAP_ENTRY) {
|
|
||||||
/* Set up the "struct lguest_data" */
|
/* Set up the "struct lguest_data" */
|
||||||
initialize(lg);
|
initialize(lg);
|
||||||
/* The hypercall is done. */
|
/* Hcall is done. */
|
||||||
clear_hcall(lg);
|
lg->hcall = NULL;
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,12 +264,21 @@ void do_hypercalls(struct lguest *lg)
|
||||||
do_async_hcalls(lg);
|
do_async_hcalls(lg);
|
||||||
|
|
||||||
/* If we stopped reading the hypercall ring because the Guest did a
|
/* If we stopped reading the hypercall ring because the Guest did a
|
||||||
* SEND_DMA to the Launcher, we want to return now. Otherwise if the
|
* SEND_DMA to the Launcher, we want to return now. Otherwise we do
|
||||||
* Guest asked us to do a hypercall, we do it. */
|
* the hypercall. */
|
||||||
if (!lg->dma_is_pending && lg->regs->trapnum == LGUEST_TRAP_ENTRY) {
|
if (!lg->dma_is_pending) {
|
||||||
do_hcall(lg, lg->regs);
|
do_hcall(lg, lg->hcall);
|
||||||
/* The hypercall is done. */
|
/* Tricky point: we reset the hcall pointer to mark the
|
||||||
clear_hcall(lg);
|
* hypercall as "done". We use the hcall pointer rather than
|
||||||
|
* the trap number to indicate a hypercall is pending.
|
||||||
|
* Normally it doesn't matter: the Guest will run again and
|
||||||
|
* update the trap number before we come back here.
|
||||||
|
*
|
||||||
|
* However, if we are signalled or the Guest sends DMA to the
|
||||||
|
* Launcher, the run_guest() loop will exit without running the
|
||||||
|
* Guest. When it comes back it would try to re-run the
|
||||||
|
* hypercall. */
|
||||||
|
lg->hcall = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -106,6 +106,9 @@ struct lguest
|
||||||
u32 esp1;
|
u32 esp1;
|
||||||
u8 ss1;
|
u8 ss1;
|
||||||
|
|
||||||
|
/* If a hypercall was asked for, this points to the arguments. */
|
||||||
|
struct lguest_regs *hcall;
|
||||||
|
|
||||||
/* Do we need to stop what we're doing and return to userspace? */
|
/* Do we need to stop what we're doing and return to userspace? */
|
||||||
int break_out;
|
int break_out;
|
||||||
wait_queue_head_t break_wq;
|
wait_queue_head_t break_wq;
|
||||||
|
|
|
@ -316,13 +316,14 @@ void lguest_arch_handle_trap(struct lguest *lg)
|
||||||
return;
|
return;
|
||||||
break;
|
break;
|
||||||
case 32 ... 255:
|
case 32 ... 255:
|
||||||
/* These values mean a real interrupt occurred, in
|
/* These values mean a real interrupt occurred, in which case
|
||||||
* which case the Host handler has already been run.
|
* the Host handler has already been run. We just do a
|
||||||
* We just do a friendly check if another process
|
* friendly check if another process should now be run, then
|
||||||
* should now be run, then fall through to loop
|
* return to run the Guest again */
|
||||||
* around: */
|
|
||||||
cond_resched();
|
cond_resched();
|
||||||
case LGUEST_TRAP_ENTRY: /* Handled before re-entering Guest */
|
return;
|
||||||
|
case LGUEST_TRAP_ENTRY:
|
||||||
|
lg->hcall = lg->regs;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue