Skip to content

Commit 1f80ba3

Browse files
committed
KVM: PPC: Book3S HV: Fix XICS-on-XIVE H_IPI when priority = 0
This fixes a bug in the XICS emulation on POWER9 machines which is triggered by the guest doing a H_IPI with priority = 0 (the highest priority). What happens is that the notification interrupt arrives at the destination at priority zero. The loop in scan_interrupts() sees that a priority 0 interrupt is pending, but because xc->mfrr is zero, we break out of the loop before taking the notification interrupt out of the queue and EOI-ing it. (This doesn't happen when xc->mfrr != 0; in that case we process the priority-0 notification interrupt on the first iteration of the loop, and then break out of a subsequent iteration of the loop with hirq == XICS_IPI.) To fix this, we move the prio >= xc->mfrr check down to near the end of the loop. However, there are then some other things that need to be adjusted. Since we are potentially handling the notification interrupt and also delivering an IPI to the guest in the same loop iteration, we need to update pending and handle any q->pending_count value before the xc->mfrr check, rather than at the end of the loop. Also, we need to update the queue pointers when we have processed and EOI-ed the notification interrupt, since we may not do it later. Signed-off-by: Paul Mackerras <[email protected]>
1 parent 6fabc9f commit 1f80ba3

File tree

1 file changed

+40
-38
lines changed

1 file changed

+40
-38
lines changed

arch/powerpc/kvm/book3s_xive_template.c

Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -130,24 +130,14 @@ static u32 GLUE(X_PFX,scan_interrupts)(struct kvmppc_xive_vcpu *xc,
130130
*/
131131
prio = ffs(pending) - 1;
132132

133-
/*
134-
* If the most favoured prio we found pending is less
135-
* favored (or equal) than a pending IPI, we return
136-
* the IPI instead.
137-
*
138-
* Note: If pending was 0 and mfrr is 0xff, we will
139-
* not spurriously take an IPI because mfrr cannot
140-
* then be smaller than cppr.
141-
*/
142-
if (prio >= xc->mfrr && xc->mfrr < xc->cppr) {
143-
prio = xc->mfrr;
144-
hirq = XICS_IPI;
145-
break;
146-
}
147-
148133
/* Don't scan past the guest cppr */
149-
if (prio >= xc->cppr || prio > 7)
134+
if (prio >= xc->cppr || prio > 7) {
135+
if (xc->mfrr < xc->cppr) {
136+
prio = xc->mfrr;
137+
hirq = XICS_IPI;
138+
}
150139
break;
140+
}
151141

152142
/* Grab queue and pointers */
153143
q = &xc->queues[prio];
@@ -184,9 +174,12 @@ static u32 GLUE(X_PFX,scan_interrupts)(struct kvmppc_xive_vcpu *xc,
184174
* been set and another occurrence of the IPI will trigger.
185175
*/
186176
if (hirq == XICS_IPI || (prio == 0 && !qpage)) {
187-
if (scan_type == scan_fetch)
177+
if (scan_type == scan_fetch) {
188178
GLUE(X_PFX,source_eoi)(xc->vp_ipi,
189179
&xc->vp_ipi_data);
180+
q->idx = idx;
181+
q->toggle = toggle;
182+
}
190183
/* Loop back on same queue with updated idx/toggle */
191184
#ifdef XIVE_RUNTIME_CHECKS
192185
WARN_ON(hirq && hirq != XICS_IPI);
@@ -199,32 +192,41 @@ static u32 GLUE(X_PFX,scan_interrupts)(struct kvmppc_xive_vcpu *xc,
199192
if (hirq == XICS_DUMMY)
200193
goto skip_ipi;
201194

202-
/* If fetching, update queue pointers */
203-
if (scan_type == scan_fetch) {
204-
q->idx = idx;
205-
q->toggle = toggle;
206-
}
207-
208-
/* Something found, stop searching */
209-
if (hirq)
210-
break;
211-
212-
/* Clear the pending bit on the now empty queue */
213-
pending &= ~(1 << prio);
195+
/* Clear the pending bit if the queue is now empty */
196+
if (!hirq) {
197+
pending &= ~(1 << prio);
214198

215-
/*
216-
* Check if the queue count needs adjusting due to
217-
* interrupts being moved away.
218-
*/
219-
if (atomic_read(&q->pending_count)) {
220-
int p = atomic_xchg(&q->pending_count, 0);
221-
if (p) {
199+
/*
200+
* Check if the queue count needs adjusting due to
201+
* interrupts being moved away.
202+
*/
203+
if (atomic_read(&q->pending_count)) {
204+
int p = atomic_xchg(&q->pending_count, 0);
205+
if (p) {
222206
#ifdef XIVE_RUNTIME_CHECKS
223-
WARN_ON(p > atomic_read(&q->count));
207+
WARN_ON(p > atomic_read(&q->count));
224208
#endif
225-
atomic_sub(p, &q->count);
209+
atomic_sub(p, &q->count);
210+
}
226211
}
227212
}
213+
214+
/*
215+
* If the most favoured prio we found pending is less
216+
* favored (or equal) than a pending IPI, we return
217+
* the IPI instead.
218+
*/
219+
if (prio >= xc->mfrr && xc->mfrr < xc->cppr) {
220+
prio = xc->mfrr;
221+
hirq = XICS_IPI;
222+
break;
223+
}
224+
225+
/* If fetching, update queue pointers */
226+
if (scan_type == scan_fetch) {
227+
q->idx = idx;
228+
q->toggle = toggle;
229+
}
228230
}
229231

230232
/* If we are just taking a "peek", do nothing else */

0 commit comments

Comments
 (0)