From ae9aa8c06183574621689d1365ced58222aaed63 Mon Sep 17 00:00:00 2001 From: Shuo A Liu Date: Fri, 21 Dec 2018 10:23:44 +0800 Subject: [PATCH] hv: decouple IO completion polling from idle thread IO completion polling will access vcpu and vm structs. If doing it in idle thread, there might be some race issues between vm destroying and idle thread. They are running on different cores. Got suggestion from Fengwei, decouple the polling action from idle thread and just do it in vcpu thread, then we can guarantee idle thread in really idle status. Tracked-On: #1821 Signed-off-by: Shuo A Liu Reviewed-by: Yin Fengwei --- hypervisor/arch/x86/io.c | 11 +++++-- hypervisor/common/io_request.c | 49 ++++++++++++++--------------- hypervisor/common/schedule.c | 1 - hypervisor/include/arch/x86/ioreq.h | 9 ------ 4 files changed, 33 insertions(+), 37 deletions(-) diff --git a/hypervisor/arch/x86/io.c b/hypervisor/arch/x86/io.c index 6817ea6c1..af1b963fe 100644 --- a/hypervisor/arch/x86/io.c +++ b/hypervisor/arch/x86/io.c @@ -152,7 +152,11 @@ void emulate_io_post(struct acrn_vcpu *vcpu) switch (vcpu->req.type) { case REQ_MMIO: - request_vcpu_pre_work(vcpu, ACRN_VCPU_MMIO_COMPLETE); + if (!vcpu->vm->sw.is_completion_polling) { + request_vcpu_pre_work(vcpu, ACRN_VCPU_MMIO_COMPLETE); + } else { + dm_emulate_mmio_post(vcpu); + } break; case REQ_PORTIO: @@ -173,7 +177,10 @@ void emulate_io_post(struct acrn_vcpu *vcpu) break; } - resume_vcpu(vcpu); + if (!vcpu->vm->sw.is_completion_polling) { + resume_vcpu(vcpu); + } + } /** diff --git a/hypervisor/common/io_request.c b/hypervisor/common/io_request.c index ace9a0873..57daddaf8 100644 --- a/hypervisor/common/io_request.c +++ b/hypervisor/common/io_request.c @@ -78,29 +78,6 @@ static inline bool has_complete_ioreq(struct acrn_vcpu *vcpu) return (get_vhm_req_state(vcpu->vm, vcpu->vcpu_id) == REQ_STATE_COMPLETE); } -/** - * @brief Handle completed ioreq if any one pending - * - * @param pcpu_id The physical cpu id of vcpu whose IO request to be checked - * - * @return None - */ -void handle_complete_ioreq(uint16_t pcpu_id) -{ - struct acrn_vcpu *vcpu = get_ever_run_vcpu(pcpu_id); - struct acrn_vm *vm; - - if (vcpu != NULL) { - vm = vcpu->vm; - if (vm->sw.is_completion_polling) { - if (has_complete_ioreq(vcpu)) { - /* we have completed ioreq pending */ - emulate_io_post(vcpu); - } - } - } -} - /** * @brief Deliver \p io_req to SOS and suspend \p vcpu till its completion * @@ -113,6 +90,7 @@ int32_t acrn_insert_request_wait(struct acrn_vcpu *vcpu, const struct io_request { union vhm_request_buffer *req_buf = NULL; struct vhm_request *vhm_req; + bool is_polling = false; uint16_t cur; if (vcpu->vm->sw.io_shared_page == NULL) { @@ -133,14 +111,17 @@ int32_t acrn_insert_request_wait(struct acrn_vcpu *vcpu, const struct io_request &io_req->reqs, sizeof(union vhm_io_request)); if (vcpu->vm->sw.is_completion_polling) { vhm_req->completion_polling = 1U; + is_polling = true; } clac(); - /* pause vcpu, wait for VHM to handle the MMIO request. + /* pause vcpu in notification mode , wait for VHM to handle the MMIO request. * TODO: when pause_vcpu changed to switch vcpu out directlly, we * should fix the race issue between req.processed update and vcpu pause */ - pause_vcpu(vcpu, VCPU_PAUSED); + if (!is_polling) { + pause_vcpu(vcpu, VCPU_PAUSED); + } /* Must clear the signal before we mark req as pending * Once we mark it pending, VHM may process req and signal us @@ -158,6 +139,24 @@ int32_t acrn_insert_request_wait(struct acrn_vcpu *vcpu, const struct io_request /* signal VHM */ fire_vhm_interrupt(); + /* Polling completion of the request in polling mode */ + if (is_polling) { + /* + * Now, we only have one case that will schedule out this vcpu + * from IO completion polling status, it's pause_vcpu to VCPU_ZOMBIE. + * In this case, we cannot come back to polling status again. Currently, + * it's OK as we needn't handle IO completion in zombie status. + */ + while (!need_reschedule(vcpu->pcpu_id)) { + if (has_complete_ioreq(vcpu)) { + /* we have completed ioreq pending */ + emulate_io_post(vcpu); + break; + } + __asm __volatile("pause" ::: "memory"); + } + } + return 0; } diff --git a/hypervisor/common/schedule.c b/hypervisor/common/schedule.c index 4d749801b..570e357a9 100644 --- a/hypervisor/common/schedule.c +++ b/hypervisor/common/schedule.c @@ -178,7 +178,6 @@ void default_idle(void) cpu_dead(); } else { CPU_IRQ_ENABLE(); - handle_complete_ioreq(pcpu_id); cpu_do_idle(); CPU_IRQ_DISABLE(); } diff --git a/hypervisor/include/arch/x86/ioreq.h b/hypervisor/include/arch/x86/ioreq.h index 8798de92a..b25b1ad83 100644 --- a/hypervisor/include/arch/x86/ioreq.h +++ b/hypervisor/include/arch/x86/ioreq.h @@ -296,15 +296,6 @@ int32_t acrn_insert_request_wait(struct acrn_vcpu *vcpu, const struct io_request */ void reset_vm_ioreqs(struct acrn_vm *vm); -/** - * @brief Handle completed ioreq if any one pending - * - * @param pcpu_id The physical cpu id of vcpu whose IO request to be checked - * - * @return None - */ -void handle_complete_ioreq(uint16_t pcpu_id); - /** * @brief Get the state of VHM request *