Commit 19881aff authored by David Hildenbrand's avatar David Hildenbrand Committed by Sasha Levin

KVM: s390: fix handling of write errors in the tpi handler

[ Upstream commit 261520dc ]

If the I/O interrupt could not be written to the guest provided
area (e.g. access exception), a program exception was injected into the
guest but "inti" wasn't freed, therefore resulting in a memory leak.

In addition, the I/O interrupt wasn't reinjected. Therefore the dequeued
interrupt is lost.

This patch fixes the problem while cleaning up the function and making the
cc and rc logic easier to handle.
Signed-off-by: default avatarDavid Hildenbrand <dahi@linux.vnet.ibm.com>
Cc: stable@vger.kernel.org # 3.16+
Signed-off-by: default avatarChristian Borntraeger <borntraeger@de.ibm.com>
Signed-off-by: default avatarSasha Levin <sasha.levin@oracle.com>
parent 67c5b95c
...@@ -228,18 +228,19 @@ static int handle_tpi(struct kvm_vcpu *vcpu) ...@@ -228,18 +228,19 @@ static int handle_tpi(struct kvm_vcpu *vcpu)
struct kvm_s390_interrupt_info *inti; struct kvm_s390_interrupt_info *inti;
unsigned long len; unsigned long len;
u32 tpi_data[3]; u32 tpi_data[3];
int cc, rc; int rc;
u64 addr; u64 addr;
rc = 0;
addr = kvm_s390_get_base_disp_s(vcpu); addr = kvm_s390_get_base_disp_s(vcpu);
if (addr & 3) if (addr & 3)
return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
cc = 0;
inti = kvm_s390_get_io_int(vcpu->kvm, vcpu->arch.sie_block->gcr[6], 0); inti = kvm_s390_get_io_int(vcpu->kvm, vcpu->arch.sie_block->gcr[6], 0);
if (!inti) if (!inti) {
goto no_interrupt; kvm_s390_set_psw_cc(vcpu, 0);
cc = 1; return 0;
}
tpi_data[0] = inti->io.subchannel_id << 16 | inti->io.subchannel_nr; tpi_data[0] = inti->io.subchannel_id << 16 | inti->io.subchannel_nr;
tpi_data[1] = inti->io.io_int_parm; tpi_data[1] = inti->io.io_int_parm;
tpi_data[2] = inti->io.io_int_word; tpi_data[2] = inti->io.io_int_word;
...@@ -250,30 +251,35 @@ static int handle_tpi(struct kvm_vcpu *vcpu) ...@@ -250,30 +251,35 @@ static int handle_tpi(struct kvm_vcpu *vcpu)
*/ */
len = sizeof(tpi_data) - 4; len = sizeof(tpi_data) - 4;
rc = write_guest(vcpu, addr, &tpi_data, len); rc = write_guest(vcpu, addr, &tpi_data, len);
if (rc) if (rc) {
return kvm_s390_inject_prog_cond(vcpu, rc); rc = kvm_s390_inject_prog_cond(vcpu, rc);
goto reinject_interrupt;
}
} else { } else {
/* /*
* Store the three-word I/O interruption code into * Store the three-word I/O interruption code into
* the appropriate lowcore area. * the appropriate lowcore area.
*/ */
len = sizeof(tpi_data); len = sizeof(tpi_data);
if (write_guest_lc(vcpu, __LC_SUBCHANNEL_ID, &tpi_data, len)) if (write_guest_lc(vcpu, __LC_SUBCHANNEL_ID, &tpi_data, len)) {
/* failed writes to the low core are not recoverable */
rc = -EFAULT; rc = -EFAULT;
goto reinject_interrupt;
}
} }
/* irq was successfully handed to the guest */
kfree(inti);
kvm_s390_set_psw_cc(vcpu, 1);
return 0;
reinject_interrupt:
/* /*
* If we encounter a problem storing the interruption code, the * If we encounter a problem storing the interruption code, the
* instruction is suppressed from the guest's view: reinject the * instruction is suppressed from the guest's view: reinject the
* interrupt. * interrupt.
*/ */
if (!rc) kvm_s390_reinject_io_int(vcpu->kvm, inti);
kfree(inti); /* don't set the cc, a pgm irq was injected or we drop to user space */
else
kvm_s390_reinject_io_int(vcpu->kvm, inti);
no_interrupt:
/* Set condition code and we're done. */
if (!rc)
kvm_s390_set_psw_cc(vcpu, cc);
return rc ? -EFAULT : 0; return rc ? -EFAULT : 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment