KVM: s390: Use generic VIRT_XFER_TO_GUEST_WORK functions

Switch to using the generic infrastructure to check for and handle pending
work before transitioning into guest mode.

xfer_to_guest_mode_handle_work() does a few more things than the current
code does when deciding whether or not to exit the __vcpu_run() loop. The
exittime tests from kvm-unit-tests, in my tests, were within a few percent
compared to before this series, which is within noise tolerance.

Co-developed-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Andrew Donnellan <ajd@linux.ibm.com>
Acked-by: Janosch Frank <frankja@linux.ibm.com>
[frankja@linux.ibm.com: Removed semicolon]
Signed-off-by: Janosch Frank <frankja@linux.ibm.com>
This commit is contained in:
Andrew Donnellan 2025-11-26 16:33:12 +11:00 committed by Janosch Frank
parent d0139059e3
commit 2bd1337a12
3 changed files with 32 additions and 12 deletions

View File

@ -30,6 +30,7 @@ config KVM
select HAVE_KVM_NO_POLL select HAVE_KVM_NO_POLL
select KVM_VFIO select KVM_VFIO
select MMU_NOTIFIER select MMU_NOTIFIER
select VIRT_XFER_TO_GUEST_WORK
help help
Support hosting paravirtualized guest machines using the SIE Support hosting paravirtualized guest machines using the SIE
virtualization capability on the mainframe. This should work virtualization capability on the mainframe. This should work

View File

@ -14,6 +14,7 @@
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/entry-virt.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/fs.h> #include <linux/fs.h>
@ -4675,9 +4676,6 @@ static int vcpu_pre_run(struct kvm_vcpu *vcpu)
vcpu->arch.sie_block->gg14 = vcpu->run->s.regs.gprs[14]; vcpu->arch.sie_block->gg14 = vcpu->run->s.regs.gprs[14];
vcpu->arch.sie_block->gg15 = vcpu->run->s.regs.gprs[15]; vcpu->arch.sie_block->gg15 = vcpu->run->s.regs.gprs[15];
if (need_resched())
schedule();
if (!kvm_is_ucontrol(vcpu->kvm)) { if (!kvm_is_ucontrol(vcpu->kvm)) {
rc = kvm_s390_deliver_pending_interrupts(vcpu); rc = kvm_s390_deliver_pending_interrupts(vcpu);
if (rc || guestdbg_exit_pending(vcpu)) if (rc || guestdbg_exit_pending(vcpu))
@ -4982,12 +4980,12 @@ static int __vcpu_run(struct kvm_vcpu *vcpu)
*/ */
kvm_vcpu_srcu_read_lock(vcpu); kvm_vcpu_srcu_read_lock(vcpu);
do { while (true) {
rc = vcpu_pre_run(vcpu); rc = vcpu_pre_run(vcpu);
kvm_vcpu_srcu_read_unlock(vcpu);
if (rc || guestdbg_exit_pending(vcpu)) if (rc || guestdbg_exit_pending(vcpu))
break; break;
kvm_vcpu_srcu_read_unlock(vcpu);
/* /*
* As PF_VCPU will be used in fault handler, between * As PF_VCPU will be used in fault handler, between
* guest_timing_enter_irqoff and guest_timing_exit_irqoff * guest_timing_enter_irqoff and guest_timing_exit_irqoff
@ -4999,7 +4997,17 @@ static int __vcpu_run(struct kvm_vcpu *vcpu)
sizeof(sie_page->pv_grregs)); sizeof(sie_page->pv_grregs));
} }
xfer_to_guest_mode_check:
local_irq_disable(); local_irq_disable();
xfer_to_guest_mode_prepare();
if (xfer_to_guest_mode_work_pending()) {
local_irq_enable();
rc = kvm_xfer_to_guest_mode_handle_work(vcpu);
if (rc)
break;
goto xfer_to_guest_mode_check;
}
guest_timing_enter_irqoff(); guest_timing_enter_irqoff();
__disable_cpu_timer_accounting(vcpu); __disable_cpu_timer_accounting(vcpu);
@ -5029,9 +5037,12 @@ static int __vcpu_run(struct kvm_vcpu *vcpu)
kvm_vcpu_srcu_read_lock(vcpu); kvm_vcpu_srcu_read_lock(vcpu);
rc = vcpu_post_run(vcpu, exit_reason); rc = vcpu_post_run(vcpu, exit_reason);
} while (!signal_pending(current) && !guestdbg_exit_pending(vcpu) && !rc); if (rc || guestdbg_exit_pending(vcpu)) {
kvm_vcpu_srcu_read_unlock(vcpu);
break;
}
}
kvm_vcpu_srcu_read_unlock(vcpu);
return rc; return rc;
} }

View File

@ -1180,12 +1180,23 @@ static int do_vsie_run(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
current->thread.gmap_int_code = 0; current->thread.gmap_int_code = 0;
barrier(); barrier();
if (!kvm_s390_vcpu_sie_inhibited(vcpu)) { if (!kvm_s390_vcpu_sie_inhibited(vcpu)) {
xfer_to_guest_mode_check:
local_irq_disable(); local_irq_disable();
xfer_to_guest_mode_prepare();
if (xfer_to_guest_mode_work_pending()) {
local_irq_enable();
rc = kvm_xfer_to_guest_mode_handle_work(vcpu);
if (rc)
goto skip_sie;
goto xfer_to_guest_mode_check;
}
guest_timing_enter_irqoff(); guest_timing_enter_irqoff();
rc = kvm_s390_enter_exit_sie(scb_s, vcpu->run->s.regs.gprs, vsie_page->gmap->asce); rc = kvm_s390_enter_exit_sie(scb_s, vcpu->run->s.regs.gprs, vsie_page->gmap->asce);
guest_timing_exit_irqoff(); guest_timing_exit_irqoff();
local_irq_enable(); local_irq_enable();
} }
skip_sie:
barrier(); barrier();
vcpu->arch.sie_block->prog0c &= ~PROG_IN_SIE; vcpu->arch.sie_block->prog0c &= ~PROG_IN_SIE;
@ -1345,13 +1356,11 @@ static int vsie_run(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
* but rewind the PSW to re-enter SIE once that's completed * but rewind the PSW to re-enter SIE once that's completed
* instead of passing a "no action" intercept to the guest. * instead of passing a "no action" intercept to the guest.
*/ */
if (signal_pending(current) || if (kvm_s390_vcpu_has_irq(vcpu, 0) ||
kvm_s390_vcpu_has_irq(vcpu, 0) ||
kvm_s390_vcpu_sie_inhibited(vcpu)) { kvm_s390_vcpu_sie_inhibited(vcpu)) {
kvm_s390_rewind_psw(vcpu, 4); kvm_s390_rewind_psw(vcpu, 4);
break; break;
} }
cond_resched();
} }
if (rc == -EFAULT) { if (rc == -EFAULT) {
@ -1483,8 +1492,7 @@ int kvm_s390_handle_vsie(struct kvm_vcpu *vcpu)
if (unlikely(scb_addr & 0x1ffUL)) if (unlikely(scb_addr & 0x1ffUL))
return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
if (signal_pending(current) || kvm_s390_vcpu_has_irq(vcpu, 0) || if (kvm_s390_vcpu_has_irq(vcpu, 0) || kvm_s390_vcpu_sie_inhibited(vcpu)) {
kvm_s390_vcpu_sie_inhibited(vcpu)) {
kvm_s390_rewind_psw(vcpu, 4); kvm_s390_rewind_psw(vcpu, 4);
return 0; return 0;
} }