HV: Refine the usage of monitor/mwait to avoid the possible lockup

Based on SDM Vol2 the monitor uses the RAX register to setup the address
monitored by HW. The mwait uses the rax/rcx as the hints that the process
will enter. It is incorrect that the same value is used for monitor/mwait.
The ecx in mwait specifies the optional externsions.

At the same time it needs to check whether the the value of monitored addr
is already expected before entering mwait. Otherwise it will have possible
lockup.

V1->V2: Add the asm wrappper of monitor/mwait to avoid the mixed usage of
inline assembly in wait_sync_change

v2-v3: Remove the unnecessary line break in asm_monitor/asm_mwait.
       Follow Fei's comment to remove the mwait ecx hint setting that
treats the interrupt as break event. It only needs to check whether the
value of psync_change is already expected.

Tracked-On: #3442
Signed-off-by: Zhao Yakui <yakui.zhao@intel.com>
Reviewed-by: Yin Fengwei <fengwei.yin@intel.com>
This commit is contained in:
Zhao Yakui 2019-07-18 16:16:08 +08:00
parent 37f5f0771e
commit 3d65188e54

View File

@ -415,21 +415,29 @@ static void print_hv_banner(void)
printf(boot_msg); printf(boot_msg);
} }
static
inline void asm_monitor(const uint64_t *addr, uint64_t ecx, uint64_t edx)
{
asm volatile("monitor\n" : : "a" (addr), "c" (ecx), "d" (edx));
}
static
inline void asm_mwait(uint64_t eax, uint64_t ecx)
{
asm volatile("mwait\n" : : "a" (eax), "c" (ecx));
}
/* wait until *sync == wake_sync */ /* wait until *sync == wake_sync */
void wait_sync_change(uint64_t *sync, uint64_t wake_sync) void wait_sync_change(uint64_t *sync, uint64_t wake_sync)
{ {
if (has_monitor_cap()) { if (has_monitor_cap()) {
/* Wait for the event to be set using monitor/mwait */ /* Wait for the event to be set using monitor/mwait */
asm volatile ("1: cmpq %%rbx,(%%rax)\n" while (*((volatile uint64_t *)sync) != wake_sync) {
" je 2f\n" asm_monitor(sync, 0UL, 0UL);
" monitor\n" if (*((volatile uint64_t *)sync) != wake_sync) {
" mwait\n" asm_mwait(0UL, 0UL);
" jmp 1b\n" }
"2:\n" }
:
: "a" (sync), "d"(0), "c"(0),
"b"(wake_sync)
: "cc");
} else { } else {
/* Wait for the event to be set using pause */ /* Wait for the event to be set using pause */
asm volatile ("1: cmpq %%rbx,(%%rax)\n" asm volatile ("1: cmpq %%rbx,(%%rax)\n"