Blob Blame History Raw
From: QingFeng Hao <haoqf@linux.vnet.ibm.com>
Subject: KVM: s390: Inject machine check into the nested guest
Patch-mainline: v4.13-rc1
Git-commit: d52cd2076eb2ace9fe95dbf795f6d93587453914
References: FATE#324256,LTC#160417,bsc#1066327

Summary:     kvm: Robust Machine Check Handling
Description: A machine check is caused by a machine malfunction and not by data
             or instructions. The machine check could happen on CPU, I/O,
             storage etc.
             There are several kinds of machine checks, e.g. System Damage(SD),
             Instruction Processing Damage(IPD), Delayed Access Exception(DAE),
             Channel Report Pending(CRW), External Damage(ED).
             In general, they are of two types: exigent condition and
             repressible condition. The 64 bits Machine-Check-Interruption
             Code(MCIC) in lowcore is set to indicate the current machine
             check's type with the validity bits when a machine check happens.
             Meanwhile, the machine check handler set in lowcore is called to
             handle it. Through the validity bits in MCIC, the program can
             determine if the machine check could be recovered.
             The present machine check handler can handle almost all of the
             machine checks and validate the registers to recover the machine
             by the validity bits in MCIC. It can also inject the CRW machine
             check request from QEMU to the guest.
             VS1522 improves it to inject most of the host supported machine
             checks to the guest if they happen during the guest's running
             and the necessary validity bits to recover are set. Exceptions are
             the SD and Timer Facility Damage. The original handling is kept
             and they will not be injected into the guest.
             Additionally, this line item reinjects the DAE machine check
             if it happens during the guest's running instead of damage host
             because DAE could be caused by the program's using an improper
             procedure to update the DAT tables.
             The program will not inject the machine check for device hotplug
             such as CRW into the guest. For machine checks happening during
             the nested guest's(guest-3) running, guest-2 will reinject them
             into guest-3.

Upstream-Description:

             KVM: s390: Inject machine check into the nested guest

             With vsie feature enabled, kvm can support nested guests (guest-3).
             So inject machine check to the guest-2 if it happens when the nested
             guest is running. And guest-2 will detect the machine check belongs
             to guest-3 and reinject it into guest-3.
             The host (guest-1) tries to inject the machine check to the picked
             destination vcpu if it's a floating machine check.

             Signed-off-by: QingFeng Hao <haoqf@linux.vnet.ibm.com>
             Acked-by: Christian Borntraeger <borntraeger@de.ibm.com>
             Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>


Signed-off-by: QingFeng Hao <haoqf@linux.vnet.ibm.com>
Acked-by: Hannes Reinecke <hare@suse.com>
---
 arch/s390/kvm/vsie.c |   23 +++++++++++++++++++----
 1 file changed, 19 insertions(+), 4 deletions(-)

--- a/arch/s390/kvm/vsie.c
+++ b/arch/s390/kvm/vsie.c
@@ -26,13 +26,18 @@
 
 struct vsie_page {
 	struct kvm_s390_sie_block scb_s;	/* 0x0000 */
+	/*
+	 * the backup info for machine check. ensure it's at
+	 * the same offset as that in struct sie_page!
+	 */
+	struct mcck_volatile_info mcck_info;    /* 0x0200 */
 	/* the pinned originial scb */
-	struct kvm_s390_sie_block *scb_o;	/* 0x0200 */
+	struct kvm_s390_sie_block *scb_o;	/* 0x0218 */
 	/* the shadow gmap in use by the vsie_page */
-	struct gmap *gmap;			/* 0x0208 */
+	struct gmap *gmap;			/* 0x0220 */
 	/* address of the last reported fault to guest2 */
-	unsigned long fault_addr;		/* 0x0210 */
-	__u8 reserved[0x0700 - 0x0218];		/* 0x0218 */
+	unsigned long fault_addr;		/* 0x0228 */
+	__u8 reserved[0x0700 - 0x0230];		/* 0x0230 */
 	struct kvm_s390_crypto_cb crycb;	/* 0x0700 */
 	__u8 fac[S390_ARCH_FAC_LIST_SIZE_BYTE];	/* 0x0800 */
 };
@@ -801,6 +806,8 @@
 {
 	struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s;
 	struct kvm_s390_sie_block *scb_o = vsie_page->scb_o;
+	struct mcck_volatile_info *mcck_info;
+	struct sie_page *sie_page;
 	int rc;
 
 	handle_last_fault(vcpu, vsie_page);
@@ -822,6 +829,14 @@
 	local_irq_enable();
 	vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
 
+	if (rc == -EINTR) {
+		VCPU_EVENT(vcpu, 3, "%s", "machine check");
+		sie_page = container_of(scb_s, struct sie_page, sie_block);
+		mcck_info = &sie_page->mcck_info;
+		kvm_s390_reinject_machine_check(vcpu, mcck_info);
+		return 0;
+	}
+
 	if (rc > 0)
 		rc = 0; /* we could still have an icpt */
 	else if (rc == -EFAULT)