Skip to content

Commit 81f3053

Browse files
bonziniAnthony Liguori
authored and
Anthony Liguori
committed
target-i386: yield to another VCPU on PAUSE
After commit b1bbfe7 (aio / timers: On timer modification, qemu_notify or aio_notify, 2013-08-21) FreeBSD guests report a huge slowdown. The problem shows up as soon as FreeBSD turns out its periodic (~1 ms) tick, but the timers are only the trigger for a pre-existing problem. Before the offending patch, setting a timer did a timer_settime system call. After, setting the timer exits the event loop (which uses poll) and reenters it with a new deadline. This does not cause any slowdown; the difference is between one system call (timer_settime and a signal delivery (SIGALRM) before the patch, and two system calls afterwards (write to a pipe or eventfd + calling poll again when re-entering the event loop). Unfortunately, the exit/enter causes the main loop to grab the iothread lock, which in turns kicks the VCPU thread out of execution. This causes TCG to execute the next VCPU in its round-robin scheduling of VCPUS. When the second VCPU is mostly unused, FreeBSD runs a "pause" instruction in its idle loop which only burns cycles without any progress. As soon as the timer tick expires, the first VCPU runs the interrupt handler but very soon it sets it again---and QEMU then goes back doing nothing in the second VCPU. The fix is to make the pause instruction do "cpu_loop_exit". Cc: Richard Henderson <[email protected]> Reported-by: Luigi Rizzo <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]> Reviewed-by: Richard Henderson <[email protected]> Message-id: [email protected] Signed-off-by: Anthony Liguori <[email protected]>
1 parent 1eb1bd9 commit 81f3053

File tree

3 files changed

+25
-3
lines changed

3 files changed

+25
-3
lines changed

Diff for: target-i386/helper.h

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ DEF_HELPER_2(sysret, void, env, int)
5858
DEF_HELPER_2(hlt, void, env, int)
5959
DEF_HELPER_2(monitor, void, env, tl)
6060
DEF_HELPER_2(mwait, void, env, int)
61+
DEF_HELPER_2(pause, void, env, int)
6162
DEF_HELPER_1(debug, void, env)
6263
DEF_HELPER_1(reset_rf, void, env)
6364
DEF_HELPER_3(raise_interrupt, void, env, int, int)

Diff for: target-i386/misc_helper.c

+20-2
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,15 @@ void helper_rdmsr(CPUX86State *env)
566566
}
567567
#endif
568568

569+
static void do_pause(X86CPU *cpu)
570+
{
571+
CPUX86State *env = &cpu->env;
572+
573+
/* Just let another CPU run. */
574+
env->exception_index = EXCP_INTERRUPT;
575+
cpu_loop_exit(env);
576+
}
577+
569578
static void do_hlt(X86CPU *cpu)
570579
{
571580
CPUState *cs = CPU(cpu);
@@ -611,13 +620,22 @@ void helper_mwait(CPUX86State *env, int next_eip_addend)
611620
cs = CPU(cpu);
612621
/* XXX: not complete but not completely erroneous */
613622
if (cs->cpu_index != 0 || CPU_NEXT(cs) != NULL) {
614-
/* more than one CPU: do not sleep because another CPU may
615-
wake this one */
623+
do_pause(cpu);
616624
} else {
617625
do_hlt(cpu);
618626
}
619627
}
620628

629+
void helper_pause(CPUX86State *env, int next_eip_addend)
630+
{
631+
X86CPU *cpu = x86_env_get_cpu(env);
632+
633+
cpu_svm_check_intercept_param(env, SVM_EXIT_PAUSE, 0);
634+
env->eip += next_eip_addend;
635+
636+
do_pause(cpu);
637+
}
638+
621639
void helper_debug(CPUX86State *env)
622640
{
623641
env->exception_index = EXCP_DEBUG;

Diff for: target-i386/translate.c

+4-1
Original file line numberDiff line numberDiff line change
@@ -7224,7 +7224,10 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
72247224
goto do_xchg_reg_eax;
72257225
}
72267226
if (prefixes & PREFIX_REPZ) {
7227-
gen_svm_check_intercept(s, pc_start, SVM_EXIT_PAUSE);
7227+
gen_update_cc_op(s);
7228+
gen_jmp_im(pc_start - s->cs_base);
7229+
gen_helper_pause(cpu_env, tcg_const_i32(s->pc - pc_start));
7230+
s->is_jmp = DISAS_TB_JUMP;
72287231
}
72297232
break;
72307233
case 0x9b: /* fwait */

0 commit comments

Comments
 (0)