powerpc/xive: Fix race condition leading to host crashes and hangs
Subsystems (3)
v2 cover powerpc/xive: Fix race condition leading to host crashes and hangs
This series fixes a race condition that has been observed in testing on POWER9 machines running KVM guests. An interrupt being freed by free_irq() can have an instance present in a XIVE interrupt queue, which can then be presented to the generic interrupt code after the data structures for it have been freed, leading to a variety of crashes and hangs.
This series is based on current upstream kernel source plus Cédric Le Goater's patch "KVM: PPC: Book3S HV: XIVE: Free escalation interrupts before disabling the VP", which is a pre-requisite for this series. As it touches both KVM and generic PPC code, this series will probably go in via Michael Ellerman's powerpc tree.
V2 of this patch series adds a patch fixing a bug noticed by Cédric, and also fixes a bug in patch 1/2 of the v1 series.
Paul.
arch/powerpc/include/asm/xive.h | 8 +++ arch/powerpc/kvm/book3s_hv_rmhandlers.S | 38 +++++++++----- arch/powerpc/kvm/book3s_xive.c | 42 +++++++++++++++- arch/powerpc/kvm/book3s_xive.h | 2 + arch/powerpc/kvm/book3s_xive_native.c | 6 +++ arch/powerpc/sysdev/xive/common.c | 87 ++++++++++++++++++++++++--------- 6 files changed, 146 insertions(+), 37 deletions(-)
PATCH v2 1/3 KVM: PPC: Book3S HV: Fix race in re-enabling XIVE escalation interrupts
Escalation interrupts are interrupts sent to the host by the XIVE hardware when it has an interrupt to deliver to a guest VCPU but that VCPU is not running anywhere in the system. Hence we disable the escalation interrupt for the VCPU being run when we enter the guest and re-enable it when the guest does an H_CEDE hypercall indicating it is idle.
It is possible that an escalation interrupt gets generated just as we are entering the guest. In that case the escalation interrupt may be using a queue entry in one of the interrupt queues, and that queue entry may not have been processed when the guest exits with an H_CEDE. The existing entry code detects this situation and does not clear the vcpu->arch.xive_esc_on flag as an indication that there is a pending queue entry (if the queue entry gets processed, xive_esc_irq() will clear the flag). There is a comment in the code saying that if the flag is still set on H_CEDE, we have to abort the cede rather than re-enabling the escalation interrupt, lest we end up with two occurrences of the escalation interrupt in the interrupt queue.
However, the exit code doesn't do that; it aborts the cede in the sense that vcpu->arch.ceded gets cleared, but it still enables the escalation interrupt by setting the source's PQ bits to 00. Instead we need to set the PQ bits to 10, indicating that an interrupt has been triggered. We also need to avoid setting vcpu->arch.xive_esc_on in this case (i.e. vcpu->arch.xive_esc_on seen to be set on H_CEDE) because xive_esc_irq() will run at some point and clear it, and if we race with that we may end up with an incorrect result (i.e. xive_esc_on set when the escalation interrupt has just been handled).
It is extremely unlikely that having two queue entries would cause observable problems; theoretically it could cause queue overflow, but the CPU would have to have thousands of interrupts targetted to it for that to be possible. However, this fix will also make it possible to determine accurately whether there is an unhandled escalation interrupt in the queue, which will be needed by the following patch.
---
v2: don't set xive_esc_on if we're not using a XIVE escalation interrupt.
arch/powerpc/kvm/book3s_hv_rmhandlers.S | 36 +++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 13 deletions(-)
arch/powerpc/kvm/book3s_hv_rmhandlers.S +23 -13
@@ -2831,29 +2831,39 @@ kvm_cede_prodded: kvm_cede_exit: ld r9, HSTATE_KVM_VCPU(r13) #ifdef CONFIG_KVM_XICS - /* Abort if we still have a pending escalation */ + /* are we using XIVE with single escalation? */ + ld r10, VCPU_XIVE_ESC_VADDR(r9) + cmpdi r10, 0 + beq 3f + li r6, XIVE_ESB_SET_PQ_00 + /* + * If we still have a pending escalation, abort the cede, + * and we must set PQ to 10 rather than 00 so that we don't + * potentially end up with two entries for the escalation + * interrupt in the XIVE interrupt queue. In that case + * we also don't want to set xive_esc_on to 1 here in + * case we race with xive_esc_irq(). + */ lbz r5, VCPU_XIVE_ESC_ON(r9) cmpwi r5, 0 - beq 1f + beq 4f li r0, 0 stb r0, VCPU_CEDED(r9) -1: /* Enable XIVE escalation */ - li r5, XIVE_ESB_SET_PQ_00 + li r6, XIVE_ESB_SET_PQ_10 + b 5f +4: li r0, 1 + stb r0, VCPU_XIVE_ESC_ON(r9) + /* make sure store to xive_esc_on is seen before xive_esc_irq runs */ + sync +5: /* Enable XIVE escalation */ mfmsr r0 andi. r0, r0, MSR_DR /* in real mode? */ beq 1f - ld r10, VCPU_XIVE_ESC_VADDR(r9) - cmpdi r10, 0 - beq 3f - ldx r0, r10, r5 + ldx r0, r10, r6 b 2f 1: ld r10, VCPU_XIVE_ESC_RADDR(r9) - cmpdi r10, 0 - beq 3f - ldcix r0, r10, r5 + ldcix r0, r10, r6 2: sync - li r0, 1 - stb r0, VCPU_XIVE_ESC_ON(r9) #endif /* CONFIG_KVM_XICS */ 3: b guest_exit_cont
Signature · 1 line
2.7.4
Re: [PATCH v2 1/3] KVM: PPC: Book3S HV: Fix race in re-enabling XIVE escalation interrupts
Quoted 61 lines · Paul Mackerras
Escalation interrupts are interrupts sent to the host by the XIVE hardware when it has an interrupt to deliver to a guest VCPU but that VCPU is not running anywhere in the system. Hence we disable the escalation interrupt for the VCPU being run when we enter the guest and re-enable it when the guest does an
H_CEDEhypercall indicating it is idle.It is possible that an escalation interrupt gets generated just as we are entering the guest. In that case the escalation interrupt may be using a queue entry in one of the interrupt queues, and that queue entry may not have been processed when the guest exits with an
H_CEDE.
The existing entry code detects this situation and does not clear thevcpu->arch.xive_esc_onflag as an indication that there is a pending queue entry (if the queue entry gets processed,xive_esc_irq()will clear the flag). There is a comment in the code saying that if the flag is still set onH_CEDE, we have to abort the cede rather than re-enabling the escalation interrupt, lest we end up with two occurrences of the escalation interrupt in the interrupt queue.However, the exit code doesn't do that; it aborts the cede in the sense
thatvcpu->arch.cededgets cleared, but it still enables the escalation
interrupt by setting the source's PQ bits to 00. Instead we need to set the PQ bits to 10, indicating that an interrupt has been triggered.
We also need to avoid settingvcpu->arch.xive_esc_onin this case (i.e.vcpu->arch.xive_esc_onseen to be set onH_CEDE) becausexive_esc_irq()will run at some point and clear it, and if we race with
that we may end up with an incorrect result (i.e.xive_esc_onset when
the escalation interrupt has just been handled).It is extremely unlikely that having two queue entries would cause observable problems; theoretically it could cause queue overflow, but the CPU would have to have thousands of interrupts targetted to it for
that to be possible. However, this fix will also make it possible to determine accurately whether there is an unhandled escalation interrupt in the queue, which will be needed by the following patch.Cc [email protected] # v4.16+Fixes 9b9b13a6d153 ("KVM: PPC: Book3S HV: Keep XIVE escalationinterrupt masked unless ceded")
Signed-off-by Paul Mackerras <[email protected]>---
v2: don't setxive_esc_onif we're not using a XIVE escalation interrupt.arch/powerpc/kvm/book3s_hv_rmhandlers.S | 36 +++++++++++++++++++++ ------------ 1 file changed, 23 insertions(+), 13 deletions(-)arch/powerpc/kvm/book3s_hv_rmhandlers.S +0
-0
arch/powerpc/kvm/book3s_hv_rmhandlers.S kvm_cede_prodded: · line 2838
Quoted 9 lines
arch/powerpc/kvm/book3s_hv_rmhandlers.S +5
-1@@ -2831,29 +2831,39 @@ kvm_cede_prodded: kvm_cede_exit: ld r9, HSTATE_KVM_VCPU(r13) #ifdef CONFIG_KVM_XICS - /* Abort if we still have a pending escalation */ + /* are we using XIVE with single escalation? */ + ld r10, VCPU_XIVE_ESC_VADDR(r9) + cmpdi r10, 0 + beq 3f + li r6, XIVE_ESB_SET_PQ_00
Would it make sense to put the above instruction down into the 4: label instead? If we do not branch to 4, r6 is overwriten anyway. I think that would save a load when we do not branch to 4. Also it would mean that you could use r5 everywhere instead of changing it to r6?
Quoted 44 lines
arch/powerpc/kvm/book3s_hv_rmhandlers.S +14
-3+ /* + * If we still have a pending escalation, abort the cede, + * and we must set PQ to 10 rather than 00 so that we don't + * potentially end up with two entries for the escalation + * interrupt in the XIVE interrupt queue. In that case + * we also don't want to set xive_esc_on to 1 here in + * case we race with xive_esc_irq(). + */ lbz r5, VCPU_XIVE_ESC_ON(r9) cmpwi r5, 0 - beq 1f + beq 4f li r0, 0 stb r0, VCPU_CEDED(r9) -1: /* Enable XIVE escalation */ - li r5, XIVE_ESB_SET_PQ_00 + li r6, XIVE_ESB_SET_PQ_10 + b 5f +4: li r0, 1 + stb r0, VCPU_XIVE_ESC_ON(r9) + /* make sure store to xive_esc_on is seen before xive_esc_irqruns */
arch/powerpc/kvm/book3s_hv_rmhandlers.S +4
-9+ sync +5: /* Enable XIVE escalation */ mfmsr r0 andi. r0, r0, MSR_DR /* in real mode? */ beq 1f - ld r10, VCPU_XIVE_ESC_VADDR(r9) - cmpdi r10, 0 - beq 3f - ldx r0, r10, r5 + ldx r0, r10, r6 b 2f 1: ld r10, VCPU_XIVE_ESC_RADDR(r9) - cmpdi r10, 0 - beq 3f - ldcix r0, r10, r5 + ldcix r0, r10, r6 2: sync - li r0, 1 - stb r0, VCPU_XIVE_ESC_ON(r9) #endif /* CONFIG_KVM_XICS */ 3: b guest_exit_cont
Re: [PATCH v2 1/3] KVM: PPC: Book3S HV: Fix race in re-enabling XIVE escalation interrupts
Quoted 1 line · Jordan Niethe
On Tue, 2019-08-13 at 20:03 +1000, Paul Mackerras wrote:
[snip]
Quoted 9 lines
Quoted 9 lines
arch/powerpc/kvm/book3s_hv_rmhandlers.S +0
-0@@ -2831,29 +2831,39 @@ kvm_cede_prodded: kvm_cede_exit: ld r9, HSTATE_KVM_VCPU(r13) #ifdef CONFIG_KVM_XICS
Quoted 8 lines
arch/powerpc/kvm/book3s_hv_rmhandlers.S kvm_cede_prodded: · line 2838
Quoted 6 lines
arch/powerpc/kvm/book3s_hv_rmhandlers.S +5
-1- /* Abort if we still have a pending escalation */ + /* are we using XIVE with single escalation? */ + ld r10, VCPU_XIVE_ESC_VADDR(r9) + cmpdi r10, 0 + beq 3f + li r6, XIVE_ESB_SET_PQ_00Would it make sense to put the above instruction down into the 4: label instead? If we do not branch to 4, r6 is overwriten anyway.
Right.
Quoted 1 line
I think that would save a load when we do not branch to 4. Also it
Well, li is a load immediate rather than a load ("load" would normally imply a load from memory). Load-immediate instructions are essentially free since they can easily be executed in parallel with other instructions and execute in a single cycle.
Quoted 2 lines
would mean that you could use r5 everywhere instead of changing it to r6?
Yes. If I have to respin the patch for other reasons then I will rearrange things as you suggest. I don't think it's worth respinning just for this change -- it won't reduce the total number of instructions, and I strongly doubt there would be any measurable performance difference.
Quoted 36 lines
Quoted 36 lines
arch/powerpc/kvm/book3s_hv_rmhandlers.S +14
-3+ /* + * If we still have a pending escalation, abort the cede, + * and we must set PQ to 10 rather than 00 so that we don't + * potentially end up with two entries for the escalation + * interrupt in the XIVE interrupt queue. In that case + * we also don't want to set xive_esc_on to 1 here in + * case we race with xive_esc_irq(). + */ lbz r5, VCPU_XIVE_ESC_ON(r9) cmpwi r5, 0 - beq 1f + beq 4f li r0, 0 stb r0, VCPU_CEDED(r9) -1: /* Enable XIVE escalation */ - li r5, XIVE_ESB_SET_PQ_00 + li r6, XIVE_ESB_SET_PQ_10 + b 5f +4: li r0, 1 + stb r0, VCPU_XIVE_ESC_ON(r9) + /* make sure store to xive_esc_on is seen before xive_esc_irqruns */
arch/powerpc/kvm/book3s_hv_rmhandlers.S +3
-6+ sync +5: /* Enable XIVE escalation */ mfmsr r0 andi. r0, r0, MSR_DR /* in real mode? */ beq 1f - ld r10, VCPU_XIVE_ESC_VADDR(r9) - cmpdi r10, 0 - beq 3f - ldx r0, r10, r5 + ldx r0, r10, r6 b 2f 1: ld r10, VCPU_XIVE_ESC_RADDR(r9) - cmpdi r10, 0 - beq 3f
Quoted 8 lines
arch/powerpc/kvm/book3s_hv_rmhandlers.S kvm_cede_prodded: · line 2868
Quoted 8 lines
arch/powerpc/kvm/book3s_hv_rmhandlers.S +1
-3- ldcix r0, r10, r5 + ldcix r0, r10, r6 2: sync - li r0, 1 - stb r0, VCPU_XIVE_ESC_ON(r9) #endif /* CONFIG_KVM_XICS */ 3: b guest_exit_cont
Paul.
Michael Ellerman
AppliedRe: [PATCH v2 1/3] KVM: PPC: Book3S HV: Fix race in re-enabling XIVE escalation interrupts
In reply to Paul MackerrasQuoted 36 lines · Paul Mackerras
Escalation interrupts are interrupts sent to the host by the XIVE hardware when it has an interrupt to deliver to a guest VCPU but that VCPU is not running anywhere in the system. Hence we disable the escalation interrupt for the VCPU being run when we enter the guest and re-enable it when the guest does an
H_CEDEhypercall indicating it is idle.It is possible that an escalation interrupt gets generated just as we are entering the guest. In that case the escalation interrupt may be using a queue entry in one of the interrupt queues, and that queue entry may not have been processed when the guest exits with an
H_CEDE. The existing entry code detects this situation and does not clear thevcpu->arch.xive_esc_onflag as an indication that there is a pending queue entry (if the queue entry gets processed,xive_esc_irq()will clear the flag). There is a comment in the code saying that if the flag is still set onH_CEDE, we have to abort the cede rather than re-enabling the escalation interrupt, lest we end up with two occurrences of the escalation interrupt in the interrupt queue.However, the exit code doesn't do that; it aborts the cede in the sense that
vcpu->arch.cededgets cleared, but it still enables the escalation interrupt by setting the source's PQ bits to 00. Instead we need to set the PQ bits to 10, indicating that an interrupt has been triggered. We also need to avoid settingvcpu->arch.xive_esc_onin this case (i.e.vcpu->arch.xive_esc_onseen to be set onH_CEDE) becausexive_esc_irq()will run at some point and clear it, and if we race with that we may end up with an incorrect result (i.e.xive_esc_onset when the escalation interrupt has just been handled).It is extremely unlikely that having two queue entries would cause observable problems; theoretically it could cause queue overflow, but the CPU would have to have thousands of interrupts targetted to it for that to be possible. However, this fix will also make it possible to determine accurately whether there is an unhandled escalation interrupt in the queue, which will be needed by the following patch.
Quoted 3 lines
Cc [email protected] # v4.16+Fixes 9b9b13a6d153 ("KVM: PPC: Book3S HV: Keep XIVE escalation interrupt masked unless ceded")Signed-off-by Paul Mackerras <[email protected]>
Applied to powerpc topic/ppc-kvm, thanks.
https://git.kernel.org/powerpc/c/959c5d5134786b4988b6fdd08e444aa67d1667ed
cheers
PATCH v2 2/3 KVM: PPC: Book3S HV: Don't push XIVE context when not using XIVE device
In reply to Paul MackerrasAt present, when running a guest on POWER9 using HV KVM but not using an in-kernel interrupt controller (XICS or XIVE), for example if QEMU is run with the kernel_irqchip=off option, the guest entry code goes ahead and tries to load the guest context into the XIVE hardware, even though no context has been set up.
To fix this, we check that the "CAM word" is non-zero before pushing it to the hardware. The CAM word is initialized to a non-zero value in kvmppc_xive_connect_vcpu() and kvmppc_xive_native_connect_vcpu(), and is now cleared in kvmppc_xive_{,native_}cleanup_vcpu.
--- arch/powerpc/kvm/book3s_hv_rmhandlers.S | 2 ++ arch/powerpc/kvm/book3s_xive.c | 11 ++++++++++- arch/powerpc/kvm/book3s_xive_native.c | 3 +++ 3 files changed, 15 insertions(+), 1 deletion(-)
arch/powerpc/kvm/book3s_hv_rmhandlers.S +2 -0
@@ -942,6 +942,8 @@ ALT_FTR_SECTION_END_IFCLR(CPU_FTR_ARCH_300) ld r11, VCPU_XIVE_SAVED_STATE(r4) li r9, TM_QW1_OS lwz r8, VCPU_XIVE_CAM_WORD(r4) + cmpwi r8, 0 + beq no_xive li r7, TM_QW1_OS + TM_WORD2 mfmsr r0 andi. r0, r0, MSR_DR /* in real mode? */
arch/powerpc/kvm/book3s_xive.c +10 -1
@@ -67,8 +67,14 @@ void kvmppc_xive_push_vcpu(struct kvm_vcpu *vcpu)void __iomem *tima = local_paca->kvm_hstate.xive_tima_virt;u64 pq; -if (!tima) +/* +* Nothing to do if the platform doesn't have a XIVE +* or this vCPU doesn't have its own XIVE context +* (e.g. because it's not using an in-kernel interrupt controller). +*/ +if (!tima || !vcpu->arch.xive_cam_word)return ; +eieio ();__raw_writeq (vcpu->arch.xive_saved_state.w01, tima +TM_QW1_OS );__raw_writel (vcpu->arch.xive_cam_word, tima +TM_QW1_OS +TM_WORD2 );
@@ -1146,6 +1152,9 @@ void kvmppc_xive_cleanup_vcpu(struct kvm_vcpu *vcpu)/* Disable the VP */ xive_native_disable_vp (xc->vp_id); +/* Clear the cam word so guest entry won't try to push context */ + vcpu->arch.xive_cam_word =0 ; +/* Free the queues */ for (i =0 ; i <KVMPPC_XIVE_Q_COUNT ; i++) {struct xive_q *q = &xc->queues[i];
arch/powerpc/kvm/book3s_xive_native.c +3 -0
@@ -81,6 +81,9 @@ void kvmppc_xive_native_cleanup_vcpu(struct kvm_vcpu *vcpu)/* Disable the VP */ xive_native_disable_vp (xc->vp_id); +/* Clear the cam word so guest entry won't try to push context */ + vcpu->arch.xive_cam_word =0 ; +/* Free the queues */ for (i =0 ; i <KVMPPC_XIVE_Q_COUNT ; i++) {kvmppc_xive_native_cleanup_queue (vcpu, i);
Signature · 1 line
2.7.4
Re: [PATCH v2 2/3] KVM: PPC: Book3S HV: Don't push XIVE context when not using XIVE device
Quoted 10 lines · Paul Mackerras
At present, when running a guest on POWER9 using HV KVM but not using an in-kernel interrupt controller (XICS or XIVE), for example if QEMU is run with the kernel_irqchip=off option, the guest entry code goes ahead and tries to load the guest context into the XIVE hardware, even though no context has been set up.
To fix this, we check that the "CAM word" is non-zero before pushing it to the hardware. The CAM word is initialized to a non-zero value in
kvmppc_xive_connect_vcpu()andkvmppc_xive_native_connect_vcpu(), and is now cleared in kvmppc_xive_{,native_}cleanup_vcpu.
If a "CAM word" is defined, it means the vCPU (VP) was enabled at the XIVE HW level. So this is the criteria to consider that a vCPU needs to update (push) its XIVE thread interrupt context when scheduled to run.
Thanks,
C.
Quoted 69 lines
Cc [email protected] # v4.11+Reported-by Cédric Le Goater <[email protected]>Fixes 5af50993850a ("KVM: PPC: Book3S HV: Native usage of the XIVE interrupt controller")Signed-off-by Paul Mackerras <[email protected]>--- arch/powerpc/kvm/book3s_hv_rmhandlers.S | 2 ++ arch/powerpc/kvm/book3s_xive.c | 11 ++++++++++- arch/powerpc/kvm/book3s_xive_native.c | 3 +++ 3 files changed, 15 insertions(+), 1 deletion(-)arch/powerpc/kvm/book3s_hv_rmhandlers.S +2
-0@@ -942,6 +942,8 @@ ALT_FTR_SECTION_END_IFCLR(CPU_FTR_ARCH_300) ld r11, VCPU_XIVE_SAVED_STATE(r4) li r9, TM_QW1_OS lwz r8, VCPU_XIVE_CAM_WORD(r4) + cmpwi r8, 0 + beq no_xive li r7, TM_QW1_OS + TM_WORD2 mfmsr r0 andi. r0, r0, MSR_DR /* in real mode? */arch/powerpc/kvm/book3s_xive.c +10
-1@@ -67,8 +67,14 @@ void kvmppc_xive_push_vcpu(struct kvm_vcpu *vcpu)void __iomem *tima = local_paca->kvm_hstate.xive_tima_virt;u64 pq; -if (!tima) +/* +* Nothing to do if the platform doesn't have a XIVE +* or this vCPU doesn't have its own XIVE context +* (e.g. because it's not using an in-kernel interrupt controller). +*/ +if (!tima || !vcpu->arch.xive_cam_word)return ; +eieio ();__raw_writeq (vcpu->arch.xive_saved_state.w01, tima +TM_QW1_OS );__raw_writel (vcpu->arch.xive_cam_word, tima +TM_QW1_OS +TM_WORD2 );@@ -1146,6 +1152,9 @@ void kvmppc_xive_cleanup_vcpu(struct kvm_vcpu *vcpu)/* Disable the VP */ xive_native_disable_vp (xc->vp_id); +/* Clear the cam word so guest entry won't try to push context */ + vcpu->arch.xive_cam_word =0 ; +/* Free the queues */ for (i =0 ; i <KVMPPC_XIVE_Q_COUNT ; i++) {struct xive_q *q = &xc->queues[i];arch/powerpc/kvm/book3s_xive_native.c +3
-0@@ -81,6 +81,9 @@ void kvmppc_xive_native_cleanup_vcpu(struct kvm_vcpu *vcpu)/* Disable the VP */ xive_native_disable_vp (xc->vp_id); +/* Clear the cam word so guest entry won't try to push context */ + vcpu->arch.xive_cam_word =0 ; +/* Free the queues */ for (i =0 ; i <KVMPPC_XIVE_Q_COUNT ; i++) {kvmppc_xive_native_cleanup_queue (vcpu, i);
Michael Ellerman
AppliedRe: [PATCH v2 2/3] KVM: PPC: Book3S HV: Don't push XIVE context when not using XIVE device
In reply to Paul MackerrasQuoted 11 lines · Paul Mackerras
At present, when running a guest on POWER9 using HV KVM but not using an in-kernel interrupt controller (XICS or XIVE), for example if QEMU is run with the kernel_irqchip=off option, the guest entry code goes ahead and tries to load the guest context into the XIVE hardware, even though no context has been set up.
To fix this, we check that the "CAM word" is non-zero before pushing it to the hardware. The CAM word is initialized to a non-zero value in
kvmppc_xive_connect_vcpu()andkvmppc_xive_native_connect_vcpu(), and is now cleared in kvmppc_xive_{,native_}cleanup_vcpu.
Quoted 5 lines
Cc [email protected] # v4.11+Reported-by Cédric Le Goater <[email protected]>Fixes 5af50993850a ("KVM: PPC: Book3S HV: Native usage of the XIVE interrupt controller")Signed-off-by Paul Mackerras <[email protected]>Reviewed-by Cédric Le Goater <[email protected]>
Applied to powerpc topic/ppc-kvm, thanks.
https://git.kernel.org/powerpc/c/8d4ba9c931bc384bcc6889a43915aaaf19d3e499
cheers
PATCH v2 3/3 powerpc/xive: Implement get_irqchip_state method for XIVE to fix shutdown race
In reply to Paul MackerrasTesting has revealed the existence of a race condition where a XIVE interrupt being shut down can be in one of the XIVE interrupt queues (of which there are up to 8 per CPU, one for each priority) at the point where free_irq() is called. If this happens, can return an interrupt number which has been shut down. This can lead to various symptoms:
- irq_to_desc(irq) can be NULL. In this case, no end-of-interrupt function gets called, resulting in the CPU's elevated interrupt priority (numerically lowered CPPR) never gets reset. That then means that the CPU stops processing interrupts, causing device timeouts and other errors in various device drivers.
- The irq descriptor or related data structures can be in the process of being freed as the interrupt code is using them. This typically leads to crashes due to bad pointer dereferences.
This race is basically what commit 62e0468650c3 ("genirq: Add optional hardware synchronization for shutdown", 2019-06-28) is intended to fix, given a get_irqchip_state() method for the interrupt controller being used. It works by polling the interrupt controller when an interrupt is being freed until the controller says it is not pending.
With XIVE, the PQ bits of the interrupt source indicate the state of the interrupt source, and in particular the P bit goes from 0 to 1 at the point where the hardware writes an entry into the interrupt queue that this interrupt is directed towards. Normally, the code will then process the interrupt and do an end-of-interrupt (EOI) operation which will reset PQ to 00 (assuming another interrupt hasn't been generated in the meantime). However, there are situations where the code resets P even though a queue entry exists (for example, by setting PQ to 01, which disables the interrupt source), and also situations where the code leaves P at 1 after removing the queue entry (for example, this is done for escalation interrupts so they cannot fire again until they are explicitly re-enabled).
The code already has a 'saved_p' flag for the interrupt source which indicates that a queue entry exists, although it isn't maintained consistently. This patch adds a 'stale_p' flag to indicate that P has been left at 1 after processing a queue entry, and adds code to set and clear saved_p and stale_p as necessary to maintain a consistent indication of whether a queue entry may or may not exist.
With this, we can implement xive_get_irqchip_state() by looking at stale_p, saved_p and the ESB PQ bits for the interrupt.
There is some additional code to handle escalation interrupts properly; because they are enabled and disabled in KVM assembly code, which does not have access to the xive_irq_data struct for the escalation interrupt. Hence, stale_p may be incorrect when the escalation interrupt is freed in kvmppc_xive_{,native_}cleanup_vcpu(). Fortunately, we can fix it up by looking at vcpu->arch.xive_esc_on, with some careful attention to barriers in order to ensure the correct result if xive_esc_irq() races with kvmppc_xive_cleanup_vcpu().
Finally, this adds code to make noise on the console (pr_crit and WARN_ON(1)) if we find an interrupt queue entry for an interrupt which does not have a descriptor. While this won't catch the race reliably, if it does get triggered it will be an indication that the race is occurring and needs to be debugged.
---
v2: call xive_cleanup_single_escalation
from kvmppc_xive_native_cleanup_vcpu() too.
arch/powerpc/include/asm/xive.h | 8 ++++ arch/powerpc/kvm/book3s_xive.c | 31 +++++++++++++ arch/powerpc/kvm/book3s_xive.h | 2 + arch/powerpc/kvm/book3s_xive_native.c | 3 ++ arch/powerpc/sysdev/xive/common.c | 87 ++++++++++++++++++++++++++--------- 5 files changed, 108 insertions(+), 23 deletions(-)
arch/powerpc/include/asm/xive.h +8 -0
@@ -46,7 +46,15 @@ struct xive_irq_data {/* Setup/used by frontend */ int target; +/* +* saved_p means that there is a queue entry for this interrupt +* in some CPU's queue (not including guest vcpu queues), even +* if P is not set in the source ESB. +* stale_p means that there is no queue entry for this interrupt +* in some CPU's queue, even if P is set in the source ESB. +*/ bool saved_p; +bool stale_p; };#define XIVE_IRQ_FLAG_STORE_EOI 0x01#define XIVE_IRQ_FLAG_LSI 0x02
arch/powerpc/kvm/book3s_xive.c +31 -0
@@ -166,6 +166,9 @@ static irqreturn_t xive_esc_irq(int irq, void *data)*/ vcpu->arch.xive_esc_on = false; +/* This orders xive_esc_on = false vs. subsequent stale_p = true */ +smp_wmb ();/* goes with smp_mb() in cleanup_single_escalation */ +return IRQ_HANDLED ; }
@@ -1119,6 +1122,31 @@ void kvmppc_xive_disable_vcpu_interrupts(struct kvm_vcpu *vcpu) vcpu->arch.xive_esc_raddr =0 ; } +/* +* In single escalation mode, the escalation interrupt is marked so +* that EOI doesn't re-enable it, but just sets the stale_p flag to +* indicate that the P bit has already been dealt with. However, the +* assembly code that enters the guest sets PQ to 00 without clearing +* stale_p (because it has no easy way to address it). Hence we have +* to adjust stale_p before shutting down the interrupt. +*/ +void xive_cleanup_single_escalation (struct kvm_vcpu *vcpu, +struct kvmppc_xive_vcpu *xc,int irq) +{ +struct irq_data *d =irq_get_irq_data (irq); +struct xive_irq_data *xd =irq_data_get_irq_handler_data (d); + +/* +* This slightly odd sequence gives the right result +* (i.e. stale_p set if xive_esc_on is false) even if +* we race with xive_esc_irq() and xive_irq_eoi(). +*/ + xd->stale_p = false; +smp_mb ();/* paired with smb_wmb in xive_esc_irq */ +if (!vcpu->arch.xive_esc_on) + xd->stale_p = true; +} +void kvmppc_xive_cleanup_vcpu (struct kvm_vcpu *vcpu) {struct kvmppc_xive_vcpu *xc = vcpu->arch.xive_vcpu;
@@ -1143,6 +1171,9 @@ void kvmppc_xive_cleanup_vcpu(struct kvm_vcpu *vcpu)/* Free escalations */ for (i =0 ; i <KVMPPC_XIVE_Q_COUNT ; i++) {if (xc->esc_virq[i]) { +if (xc->xive->single_escalation) +xive_cleanup_single_escalation (vcpu, xc, + xc->esc_virq[i]);free_irq (xc->esc_virq[i], vcpu);irq_dispose_mapping (xc->esc_virq[i]);kfree (xc->esc_virq_names[i]);
arch/powerpc/kvm/book3s_xive.h +2 -0
@@ -282,6 +282,8 @@ int kvmppc_xive_select_target(struct kvm *kvm, u32 *server, u8 prio);int kvmppc_xive_attach_escalation (struct kvm_vcpu *vcpu,u8 prio,bool single_escalation);struct kvmppc_xive *kvmppc_xive_get_device (struct kvm *kvm,u32 type); +void xive_cleanup_single_escalation (struct kvm_vcpu *vcpu, +struct kvmppc_xive_vcpu *xc,int irq);#endif /* CONFIG_KVM_XICS */ #endif /* _KVM_PPC_BOOK3S_XICS_H */
arch/powerpc/kvm/book3s_xive_native.c +3 -0
@@ -71,6 +71,9 @@ void kvmppc_xive_native_cleanup_vcpu(struct kvm_vcpu *vcpu)for (i =0 ; i <KVMPPC_XIVE_Q_COUNT ; i++) {/* Free the escalation irq */ if (xc->esc_virq[i]) { +if (xc->xive->single_escalation) +xive_cleanup_single_escalation (vcpu, xc, + xc->esc_virq[i]);free_irq (xc->esc_virq[i], vcpu);irq_dispose_mapping (xc->esc_virq[i]);kfree (xc->esc_virq_names[i]);
arch/powerpc/sysdev/xive/common.c +64 -23
191 lines not on this page · raw message
Signature · 1 line
2.7.4
Michael Ellerman
AppliedRe: [PATCH v2 3/3] powerpc/xive: Implement get_irqchip_state method for XIVE to fix shutdown race
Quoted 55 lines · Paul Mackerras
Testing has revealed the existence of a race condition where a XIVE interrupt being shut down can be in one of the XIVE interrupt queues (of which there are up to 8 per CPU, one for each priority) at the point where
free_irq()is called. If this happens, can return an interrupt number which has been shut down. This can lead to various symptoms:-
irq_to_desc(irq)can be NULL. In this case, no end-of-interrupt function gets called, resulting in the CPU's elevated interrupt priority (numerically lowered CPPR) never gets reset. That then means that the CPU stops processing interrupts, causing device timeouts and other errors in various device drivers.- The irq descriptor or related data structures can be in the process of being freed as the interrupt code is using them. This typically leads to crashes due to bad pointer dereferences.
This race is basically what commit
62e0468650c3("genirq: Add optional hardware synchronization for shutdown", 2019-06-28) is intended to fix, given aget_irqchip_state()method for the interrupt controller being used. It works by polling the interrupt controller when an interrupt is being freed until the controller says it is not pending.With XIVE, the PQ bits of the interrupt source indicate the state of the interrupt source, and in particular the P bit goes from 0 to 1 at the point where the hardware writes an entry into the interrupt queue that this interrupt is directed towards. Normally, the code will then process the interrupt and do an end-of-interrupt (EOI) operation which will reset PQ to 00 (assuming another interrupt hasn't been generated in the meantime). However, there are situations where the code resets P even though a queue entry exists (for example, by setting PQ to 01, which disables the interrupt source), and also situations where the code leaves P at 1 after removing the queue entry (for example, this is done for escalation interrupts so they cannot fire again until they are explicitly re-enabled).
The code already has a '
saved_p' flag for the interrupt source which indicates that a queue entry exists, although it isn't maintained consistently. This patch adds a 'stale_p' flag to indicate that P has been left at 1 after processing a queue entry, and adds code to set and clearsaved_pandstale_pas necessary to maintain a consistent indication of whether a queue entry may or may not exist.With this, we can implement
xive_get_irqchip_state()by looking atstale_p,saved_pand the ESB PQ bits for the interrupt.There is some additional code to handle escalation interrupts properly; because they are enabled and disabled in KVM assembly code, which does not have access to the
xive_irq_datastruct for the escalation interrupt. Hence,stale_pmay be incorrect when the escalation interrupt is freed in kvmppc_xive_{,native_}cleanup_vcpu(). Fortunately, we can fix it up by looking atvcpu->arch.xive_esc_on, with some careful attention to barriers in order to ensure the correct result ifxive_esc_irq()races withkvmppc_xive_cleanup_vcpu().
Quoted 7 lines
Finally, this adds code to make noise on the console (
pr_critandWARN_ON(1)) if we find an interrupt queue entry for an interrupt which does not have a descriptor. While this won't catch the race reliably, if it does get triggered it will be an indication that the race is occurring and needs to be debugged.Signed-off-by Paul Mackerras <[email protected]>
Applied to powerpc topic/ppc-kvm, thanks.
https://git.kernel.org/powerpc/c/da15c03b047dca891d37b9f4ef9ca14d84a6484f
cheers