aboutsummaryrefslogtreecommitdiff
path: root/hw/intc
diff options
context:
space:
mode:
authorNicholas Piggin <npiggin@gmail.com>2025-05-12 13:10:21 +1000
committerCédric Le Goater <clg@redhat.com>2025-07-21 08:03:52 +0200
commitad9175f8a2d7b4ef7d63e9663a42e7f7a44bc3f5 (patch)
treee41a5ad36d6f9c35be906345b2e0e3e7c4301f52 /hw/intc
parent3abbec04e627396c32f2b7b7461961fb68c5c122 (diff)
downloadqemu-ad9175f8a2d7b4ef7d63e9663a42e7f7a44bc3f5.zip
qemu-ad9175f8a2d7b4ef7d63e9663a42e7f7a44bc3f5.tar.gz
qemu-ad9175f8a2d7b4ef7d63e9663a42e7f7a44bc3f5.tar.bz2
ppc/xive2: Set CPPR delivery should account for group priority
The group interrupt delivery flow selects the group backlog scan if LSMFB < IPB, but that scan may find an interrupt with a priority >= IPB. In that case, the VP-direct interrupt should be chosen. This extends to selecting the lowest prio between POOL and PHYS rings. Implement this just by re-starting the selection logic if the backlog irq was not found or priority did not match LSMFB (LSMFB is updated so next time around it would see the right value and not loop infinitely). Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Reviewed-by: Michael Kowal <kowal@linux.ibm.com> Reviewed-by: Caleb Schlossin <calebs@linux.ibm.com> Tested-by: Gautam Menghani <gautam@linux.ibm.com> Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-13-npiggin@gmail.com Signed-off-by: Cédric Le Goater <clg@redhat.com>
Diffstat (limited to 'hw/intc')
-rw-r--r--hw/intc/xive2.c32
1 files changed, 22 insertions, 10 deletions
diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c
index 181d1ae..cca121b 100644
--- a/hw/intc/xive2.c
+++ b/hw/intc/xive2.c
@@ -939,7 +939,7 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr)
{
uint8_t *regs = &tctx->regs[ring];
Xive2Router *xrtr = XIVE2_ROUTER(tctx->xptr);
- uint8_t old_cppr, backlog_prio, first_group, group_level = 0;
+ uint8_t old_cppr, backlog_prio, first_group, group_level;
uint8_t pipr_min, lsmfb_min, ring_min;
bool group_enabled;
uint32_t nvp_blk, nvp_idx;
@@ -961,10 +961,12 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr)
* Recompute the PIPR based on local pending interrupts. It will
* be adjusted below if needed in case of pending group interrupts.
*/
+again:
pipr_min = xive_ipb_to_pipr(regs[TM_IPB]);
group_enabled = !!regs[TM_LGS];
- lsmfb_min = (group_enabled) ? regs[TM_LSMFB] : 0xff;
+ lsmfb_min = group_enabled ? regs[TM_LSMFB] : 0xff;
ring_min = ring;
+ group_level = 0;
/* PHYS updates also depend on POOL values */
if (ring == TM_QW3_HV_PHYS) {
@@ -998,9 +1000,6 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr)
}
}
- /* PIPR should not be set to a value greater than CPPR */
- regs[TM_PIPR] = (pipr_min > cppr) ? cppr : pipr_min;
-
rc = xive2_tctx_get_nvp_indexes(tctx, ring_min, &nvp_blk, &nvp_idx);
if (rc) {
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: set CPPR on invalid context\n");
@@ -1019,7 +1018,7 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr)
if (group_enabled &&
lsmfb_min < cppr &&
- lsmfb_min < regs[TM_PIPR]) {
+ lsmfb_min < pipr_min) {
/*
* Thread has seen a group interrupt with a higher priority
* than the new cppr or pending local interrupt. Check the
@@ -1048,12 +1047,25 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr)
nvp_blk, nvp_idx,
first_group, &group_level);
tctx->regs[ring_min + TM_LSMFB] = backlog_prio;
- if (backlog_prio != 0xFF) {
- xive2_presenter_backlog_decr(tctx->xptr, nvp_blk, nvp_idx,
- backlog_prio, group_level);
- regs[TM_PIPR] = backlog_prio;
+ if (backlog_prio != lsmfb_min) {
+ /*
+ * If the group backlog scan finds a less favored or no interrupt,
+ * then re-do the processing which may turn up a more favored
+ * interrupt from IPB or the other pool. Backlog should not
+ * find a priority < LSMFB.
+ */
+ g_assert(backlog_prio >= lsmfb_min);
+ goto again;
}
+
+ xive2_presenter_backlog_decr(tctx->xptr, nvp_blk, nvp_idx,
+ backlog_prio, group_level);
+ pipr_min = backlog_prio;
}
+
+ /* PIPR should not be set to a value greater than CPPR */
+ regs[TM_PIPR] = (pipr_min > cppr) ? cppr : pipr_min;
+
/* CPPR has changed, check if we need to raise a pending exception */
xive_tctx_notify(tctx, ring_min, group_level);
}