Loading drivers/lguest/interrupts_and_traps.c +2 −4 Original line number Diff line number Diff line Loading @@ -375,11 +375,9 @@ static bool direct_trap(unsigned int num) /* * The Host needs to see page faults (for shadow paging and to save the * fault address), general protection faults (in/out emulation) and * device not available (TS handling), invalid opcode fault (kvm hcall), * and of course, the hypercall trap. * device not available (TS handling) and of course, the hypercall trap. */ return num != 14 && num != 13 && num != 7 && num != 6 && num != LGUEST_TRAP_ENTRY; return num != 14 && num != 13 && num != 7 && num != LGUEST_TRAP_ENTRY; } /*:*/ Loading drivers/lguest/x86/core.c +0 −77 Original line number Diff line number Diff line Loading @@ -352,69 +352,6 @@ static int emulate_insn(struct lg_cpu *cpu) return 1; } /* * Our hypercalls mechanism used to be based on direct software interrupts. * After Anthony's "Refactor hypercall infrastructure" kvm patch, we decided to * change over to using kvm hypercalls. * * KVM_HYPERCALL is actually a "vmcall" instruction, which generates an invalid * opcode fault (fault 6) on non-VT cpus, so the easiest solution seemed to be * an *emulation approach*: if the fault was really produced by an hypercall * (is_hypercall() does exactly this check), we can just call the corresponding * hypercall host implementation function. * * But these invalid opcode faults are notably slower than software interrupts. * So we implemented the *patching (or rewriting) approach*: every time we hit * the KVM_HYPERCALL opcode in Guest code, we patch it to the old "int 0x1f" * opcode, so next time the Guest calls this hypercall it will use the * faster trap mechanism. * * Matias even benchmarked it to convince you: this shows the average cycle * cost of a hypercall. For each alternative solution mentioned above we've * made 5 runs of the benchmark: * * 1) direct software interrupt: 2915, 2789, 2764, 2721, 2898 * 2) emulation technique: 3410, 3681, 3466, 3392, 3780 * 3) patching (rewrite) technique: 2977, 2975, 2891, 2637, 2884 * * One two-line function is worth a 20% hypercall speed boost! */ static void rewrite_hypercall(struct lg_cpu *cpu) { /* * This are the opcodes we use to patch the Guest. The opcode for "int * $0x1f" is "0xcd 0x1f" but vmcall instruction is 3 bytes long, so we * complete the sequence with a NOP (0x90). */ u8 insn[3] = {0xcd, 0x1f, 0x90}; __lgwrite(cpu, guest_pa(cpu, cpu->regs->eip), insn, sizeof(insn)); /* * The above write might have caused a copy of that page to be made * (if it was read-only). We need to make sure the Guest has * up-to-date pagetables. As this doesn't happen often, we can just * drop them all. */ guest_pagetable_clear_all(cpu); } static bool is_hypercall(struct lg_cpu *cpu) { u8 insn[3]; /* * This must be the Guest kernel trying to do something. * The bottom two bits of the CS segment register are the privilege * level. */ if ((cpu->regs->cs & 3) != GUEST_PL) return false; /* Is it a vmcall? */ __lgread(cpu, insn, guest_pa(cpu, cpu->regs->eip), sizeof(insn)); return insn[0] == 0x0f && insn[1] == 0x01 && insn[2] == 0xc1; } /*H:050 Once we've re-enabled interrupts, we look at why the Guest exited. */ void lguest_arch_handle_trap(struct lg_cpu *cpu) { Loading @@ -429,20 +366,6 @@ void lguest_arch_handle_trap(struct lg_cpu *cpu) if (emulate_insn(cpu)) return; } /* * If KVM is active, the vmcall instruction triggers a General * Protection Fault. Normally it triggers an invalid opcode * fault (6): */ case 6: /* * We need to check if ring == GUEST_PL and faulting * instruction == vmcall. */ if (is_hypercall(cpu)) { rewrite_hypercall(cpu); return; } break; case 14: /* We've intercepted a Page Fault. */ /* Loading Loading
drivers/lguest/interrupts_and_traps.c +2 −4 Original line number Diff line number Diff line Loading @@ -375,11 +375,9 @@ static bool direct_trap(unsigned int num) /* * The Host needs to see page faults (for shadow paging and to save the * fault address), general protection faults (in/out emulation) and * device not available (TS handling), invalid opcode fault (kvm hcall), * and of course, the hypercall trap. * device not available (TS handling) and of course, the hypercall trap. */ return num != 14 && num != 13 && num != 7 && num != 6 && num != LGUEST_TRAP_ENTRY; return num != 14 && num != 13 && num != 7 && num != LGUEST_TRAP_ENTRY; } /*:*/ Loading
drivers/lguest/x86/core.c +0 −77 Original line number Diff line number Diff line Loading @@ -352,69 +352,6 @@ static int emulate_insn(struct lg_cpu *cpu) return 1; } /* * Our hypercalls mechanism used to be based on direct software interrupts. * After Anthony's "Refactor hypercall infrastructure" kvm patch, we decided to * change over to using kvm hypercalls. * * KVM_HYPERCALL is actually a "vmcall" instruction, which generates an invalid * opcode fault (fault 6) on non-VT cpus, so the easiest solution seemed to be * an *emulation approach*: if the fault was really produced by an hypercall * (is_hypercall() does exactly this check), we can just call the corresponding * hypercall host implementation function. * * But these invalid opcode faults are notably slower than software interrupts. * So we implemented the *patching (or rewriting) approach*: every time we hit * the KVM_HYPERCALL opcode in Guest code, we patch it to the old "int 0x1f" * opcode, so next time the Guest calls this hypercall it will use the * faster trap mechanism. * * Matias even benchmarked it to convince you: this shows the average cycle * cost of a hypercall. For each alternative solution mentioned above we've * made 5 runs of the benchmark: * * 1) direct software interrupt: 2915, 2789, 2764, 2721, 2898 * 2) emulation technique: 3410, 3681, 3466, 3392, 3780 * 3) patching (rewrite) technique: 2977, 2975, 2891, 2637, 2884 * * One two-line function is worth a 20% hypercall speed boost! */ static void rewrite_hypercall(struct lg_cpu *cpu) { /* * This are the opcodes we use to patch the Guest. The opcode for "int * $0x1f" is "0xcd 0x1f" but vmcall instruction is 3 bytes long, so we * complete the sequence with a NOP (0x90). */ u8 insn[3] = {0xcd, 0x1f, 0x90}; __lgwrite(cpu, guest_pa(cpu, cpu->regs->eip), insn, sizeof(insn)); /* * The above write might have caused a copy of that page to be made * (if it was read-only). We need to make sure the Guest has * up-to-date pagetables. As this doesn't happen often, we can just * drop them all. */ guest_pagetable_clear_all(cpu); } static bool is_hypercall(struct lg_cpu *cpu) { u8 insn[3]; /* * This must be the Guest kernel trying to do something. * The bottom two bits of the CS segment register are the privilege * level. */ if ((cpu->regs->cs & 3) != GUEST_PL) return false; /* Is it a vmcall? */ __lgread(cpu, insn, guest_pa(cpu, cpu->regs->eip), sizeof(insn)); return insn[0] == 0x0f && insn[1] == 0x01 && insn[2] == 0xc1; } /*H:050 Once we've re-enabled interrupts, we look at why the Guest exited. */ void lguest_arch_handle_trap(struct lg_cpu *cpu) { Loading @@ -429,20 +366,6 @@ void lguest_arch_handle_trap(struct lg_cpu *cpu) if (emulate_insn(cpu)) return; } /* * If KVM is active, the vmcall instruction triggers a General * Protection Fault. Normally it triggers an invalid opcode * fault (6): */ case 6: /* * We need to check if ring == GUEST_PL and faulting * instruction == vmcall. */ if (is_hypercall(cpu)) { rewrite_hypercall(cpu); return; } break; case 14: /* We've intercepted a Page Fault. */ /* Loading