KVM: s390: fix handling of write errors in the tpi handler
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: David Hildenbrand <dahi@linux.vnet.ibm.com> Cc: stable@vger.kernel.org # 3.16+ Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
This commit is contained in:
parent
b75f4c9afa
commit
261520dcfc
@ -229,18 +229,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;
|
||||||
@ -251,30 +252,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)
|
|
||||||
kfree(inti);
|
|
||||||
else
|
|
||||||
kvm_s390_reinject_io_int(vcpu->kvm, inti);
|
kvm_s390_reinject_io_int(vcpu->kvm, inti);
|
||||||
no_interrupt:
|
/* don't set the cc, a pgm irq was injected or we drop to user space */
|
||||||
/* Set condition code and we're done. */
|
|
||||||
if (!rc)
|
|
||||||
kvm_s390_set_psw_cc(vcpu, cc);
|
|
||||||
return rc ? -EFAULT : 0;
|
return rc ? -EFAULT : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user