diff options
author | Matias Zabaljauregui <zabaljauregui@gmail.com> | 2009-03-14 11:37:52 -0400 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2009-03-30 07:25:24 -0400 |
commit | 4cd8b5e2a159f18a1507f1187b44a1acbfa6341b (patch) | |
tree | 8a774c377e2c798e119af4b3664453c6f0d1d94d /drivers/lguest | |
parent | b7ff99ea53cd16de8f6166c0e98f19a7c6ca67ee (diff) |
lguest: use KVM hypercalls
Impact: cleanup
This patch allow us to use KVM hypercalls
Signed-off-by: Matias Zabaljauregui <zabaljauregui at gmail.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'drivers/lguest')
-rw-r--r-- | drivers/lguest/interrupts_and_traps.c | 7 | ||||
-rw-r--r-- | drivers/lguest/lguest_device.c | 4 | ||||
-rw-r--r-- | drivers/lguest/x86/core.c | 62 |
3 files changed, 67 insertions, 6 deletions
diff --git a/drivers/lguest/interrupts_and_traps.c b/drivers/lguest/interrupts_and_traps.c index 415fab0125ac..504091da1737 100644 --- a/drivers/lguest/interrupts_and_traps.c +++ b/drivers/lguest/interrupts_and_traps.c | |||
@@ -288,9 +288,10 @@ static int direct_trap(unsigned int num) | |||
288 | 288 | ||
289 | /* The Host needs to see page faults (for shadow paging and to save the | 289 | /* The Host needs to see page faults (for shadow paging and to save the |
290 | * fault address), general protection faults (in/out emulation) and | 290 | * fault address), general protection faults (in/out emulation) and |
291 | * device not available (TS handling), and of course, the hypercall | 291 | * device not available (TS handling), invalid opcode fault (kvm hcall), |
292 | * trap. */ | 292 | * and of course, the hypercall trap. */ |
293 | return num != 14 && num != 13 && num != 7 && num != LGUEST_TRAP_ENTRY; | 293 | return num != 14 && num != 13 && num != 7 && |
294 | num != 6 && num != LGUEST_TRAP_ENTRY; | ||
294 | } | 295 | } |
295 | /*:*/ | 296 | /*:*/ |
296 | 297 | ||
diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c index 8132533d71f9..df44d962626d 100644 --- a/drivers/lguest/lguest_device.c +++ b/drivers/lguest/lguest_device.c | |||
@@ -161,7 +161,7 @@ static void set_status(struct virtio_device *vdev, u8 status) | |||
161 | 161 | ||
162 | /* We set the status. */ | 162 | /* We set the status. */ |
163 | to_lgdev(vdev)->desc->status = status; | 163 | to_lgdev(vdev)->desc->status = status; |
164 | hcall(LHCALL_NOTIFY, (max_pfn<<PAGE_SHIFT) + offset, 0, 0); | 164 | kvm_hypercall1(LHCALL_NOTIFY, (max_pfn << PAGE_SHIFT) + offset); |
165 | } | 165 | } |
166 | 166 | ||
167 | static void lg_set_status(struct virtio_device *vdev, u8 status) | 167 | static void lg_set_status(struct virtio_device *vdev, u8 status) |
@@ -209,7 +209,7 @@ static void lg_notify(struct virtqueue *vq) | |||
209 | * virtqueue structure. */ | 209 | * virtqueue structure. */ |
210 | struct lguest_vq_info *lvq = vq->priv; | 210 | struct lguest_vq_info *lvq = vq->priv; |
211 | 211 | ||
212 | hcall(LHCALL_NOTIFY, lvq->config.pfn << PAGE_SHIFT, 0, 0); | 212 | kvm_hypercall1(LHCALL_NOTIFY, lvq->config.pfn << PAGE_SHIFT); |
213 | } | 213 | } |
214 | 214 | ||
215 | /* An extern declaration inside a C file is bad form. Don't do it. */ | 215 | /* An extern declaration inside a C file is bad form. Don't do it. */ |
diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c index bf7942327bda..a6b717644be0 100644 --- a/drivers/lguest/x86/core.c +++ b/drivers/lguest/x86/core.c | |||
@@ -290,6 +290,57 @@ static int emulate_insn(struct lg_cpu *cpu) | |||
290 | return 1; | 290 | return 1; |
291 | } | 291 | } |
292 | 292 | ||
293 | /* Our hypercalls mechanism used to be based on direct software interrupts. | ||
294 | * After Anthony's "Refactor hypercall infrastructure" kvm patch, we decided to | ||
295 | * change over to using kvm hypercalls. | ||
296 | * | ||
297 | * KVM_HYPERCALL is actually a "vmcall" instruction, which generates an invalid | ||
298 | * opcode fault (fault 6) on non-VT cpus, so the easiest solution seemed to be | ||
299 | * an *emulation approach*: if the fault was really produced by an hypercall | ||
300 | * (is_hypercall() does exactly this check), we can just call the corresponding | ||
301 | * hypercall host implementation function. | ||
302 | * | ||
303 | * But these invalid opcode faults are notably slower than software interrupts. | ||
304 | * So we implemented the *patching (or rewriting) approach*: every time we hit | ||
305 | * the KVM_HYPERCALL opcode in Guest code, we patch it to the old "int 0x1f" | ||
306 | * opcode, so next time the Guest calls this hypercall it will use the | ||
307 | * faster trap mechanism. | ||
308 | * | ||
309 | * Matias even benchmarked it to convince you: this shows the average cycle | ||
310 | * cost of a hypercall. For each alternative solution mentioned above we've | ||
311 | * made 5 runs of the benchmark: | ||
312 | * | ||
313 | * 1) direct software interrupt: 2915, 2789, 2764, 2721, 2898 | ||
314 | * 2) emulation technique: 3410, 3681, 3466, 3392, 3780 | ||
315 | * 3) patching (rewrite) technique: 2977, 2975, 2891, 2637, 2884 | ||
316 | * | ||
317 | * One two-line function is worth a 20% hypercall speed boost! | ||
318 | */ | ||
319 | static void rewrite_hypercall(struct lg_cpu *cpu) | ||
320 | { | ||
321 | /* This are the opcodes we use to patch the Guest. The opcode for "int | ||
322 | * $0x1f" is "0xcd 0x1f" but vmcall instruction is 3 bytes long, so we | ||
323 | * complete the sequence with a NOP (0x90). */ | ||
324 | u8 insn[3] = {0xcd, 0x1f, 0x90}; | ||
325 | |||
326 | __lgwrite(cpu, guest_pa(cpu, cpu->regs->eip), insn, sizeof(insn)); | ||
327 | } | ||
328 | |||
329 | static bool is_hypercall(struct lg_cpu *cpu) | ||
330 | { | ||
331 | u8 insn[3]; | ||
332 | |||
333 | /* This must be the Guest kernel trying to do something. | ||
334 | * The bottom two bits of the CS segment register are the privilege | ||
335 | * level. */ | ||
336 | if ((cpu->regs->cs & 3) != GUEST_PL) | ||
337 | return false; | ||
338 | |||
339 | /* Is it a vmcall? */ | ||
340 | __lgread(cpu, insn, guest_pa(cpu, cpu->regs->eip), sizeof(insn)); | ||
341 | return insn[0] == 0x0f && insn[1] == 0x01 && insn[2] == 0xc1; | ||
342 | } | ||
343 | |||
293 | /*H:050 Once we've re-enabled interrupts, we look at why the Guest exited. */ | 344 | /*H:050 Once we've re-enabled interrupts, we look at why the Guest exited. */ |
294 | void lguest_arch_handle_trap(struct lg_cpu *cpu) | 345 | void lguest_arch_handle_trap(struct lg_cpu *cpu) |
295 | { | 346 | { |
@@ -337,7 +388,7 @@ void lguest_arch_handle_trap(struct lg_cpu *cpu) | |||
337 | break; | 388 | break; |
338 | case 32 ... 255: | 389 | case 32 ... 255: |
339 | /* These values mean a real interrupt occurred, in which case | 390 | /* These values mean a real interrupt occurred, in which case |
340 | * the Host handler has already been run. We just do a | 391 | * the Host handler has already been run. We just do a |
341 | * friendly check if another process should now be run, then | 392 | * friendly check if another process should now be run, then |
342 | * return to run the Guest again */ | 393 | * return to run the Guest again */ |
343 | cond_resched(); | 394 | cond_resched(); |
@@ -347,6 +398,15 @@ void lguest_arch_handle_trap(struct lg_cpu *cpu) | |||
347 | * up the pointer now to indicate a hypercall is pending. */ | 398 | * up the pointer now to indicate a hypercall is pending. */ |
348 | cpu->hcall = (struct hcall_args *)cpu->regs; | 399 | cpu->hcall = (struct hcall_args *)cpu->regs; |
349 | return; | 400 | return; |
401 | case 6: | ||
402 | /* kvm hypercalls trigger an invalid opcode fault (6). | ||
403 | * We need to check if ring == GUEST_PL and | ||
404 | * faulting instruction == vmcall. */ | ||
405 | if (is_hypercall(cpu)) { | ||
406 | rewrite_hypercall(cpu); | ||
407 | return; | ||
408 | } | ||
409 | break; | ||
350 | } | 410 | } |
351 | 411 | ||
352 | /* We didn't handle the trap, so it needs to go to the Guest. */ | 412 | /* We didn't handle the trap, so it needs to go to the Guest. */ |