diff options
-rw-r--r-- | doc/xive.rst | 29 | ||||
-rw-r--r-- | hw/xive.c | 191 | ||||
-rw-r--r-- | include/xive.h | 15 |
3 files changed, 207 insertions, 28 deletions
diff --git a/doc/xive.rst b/doc/xive.rst index c35db6e..0997c72 100644 --- a/doc/xive.rst +++ b/doc/xive.rst @@ -657,17 +657,12 @@ This call returns information about a VP: - OPAL_XIVE_VP_ENABLED - This must be set for the VP to be usable and cleared before freeing it + Returns the enabled state of the VP - OPAL_XIVE_VP_SINGLE_ESCALATION (if available) - If this is set, the queues are configured such that all priorities - turn into a single escalation interrupt. This results in the loss of - priority 7 which can no longer be used. This this needs to be set - before any interrupt is routed to that priority. - - This feature is available if the "single-escalation-property" is - present in the xive device-tree node. + Returns whether single escalation mode is enabled for this VP + (see opal_xive_set_vp_info()). * cam_value: This is the value to program into the thread management area to dispatch that VP (ie, an encoding of the block + index). @@ -696,6 +691,24 @@ This call configures a VP: .. note:: This can be used to disable the boot time VPs though this isn't recommended. This must be used to enable allocated VPs. + - OPAL_XIVE_VP_SINGLE_ESCALATION (if available) + + If this is set, the queues are configured such that all priorities + turn into a single escalation interrupt. This results in the loss of + priority 7 which can no longer be used. This this needs to be set + before any interrupt is routed to that priority and queue 7 must not + have been already enabled. + + This feature is available if the "single-escalation-property" is + present in the xive device-tree node. + + .. warning:: When enabling single escalation, and pre-existing routing + and configuration of the individual queues escalation + is lost (except queue 7 which is the new merged escalation). + When further disabling it, the previous value is not + retrieved and the field cleared, escalation is disabled on + all the queues. + * report_cl_pair: This is the real address of the reporting cache line pair for that VP or 0 to disable. @@ -849,6 +849,14 @@ static struct xive_ive *xive_get_ive(struct xive *x, unsigned int isn) xive_err(x, "xive_get_ive, ESC ISN 0x%x EQ not found\n", isn); return NULL; } + + /* If using single-escalation, don't let anybody get to the individual + * esclation interrupts + */ + if (eq->w0 & EQ_W0_UNCOND_ESCALATE) + return NULL; + + /* Grab the buried IVE */ return (struct xive_ive *)(char *)&eq->w4; } else { /* Check the block matches */ @@ -1929,6 +1937,8 @@ static void xive_create_mmio_dt_node(struct xive *x) 12, 16, 21, 24); dt_add_property_cells(xive_dt_node, "ibm,xive-#priorities", 8); + if (x->rev >= XIVE_REV_2) + dt_add_property(xive_dt_node, "single-escalation-support", NULL, 0); xive_add_provisioning_properties(); } @@ -3912,9 +3922,32 @@ static int64_t opal_xive_get_queue_info(uint64_t vp, uint32_t prio, return OPAL_PARAMETER; if (out_escalate_irq) { + uint32_t esc_idx = idx; + + /* If escalations are routed to a single queue, fix up + * the escalation interrupt number here. + */ + if (eq->w0 & EQ_W0_UNCOND_ESCALATE) + esc_idx |= 7; *out_escalate_irq = - MAKE_ESCALATION_GIRQ(blk, idx); + MAKE_ESCALATION_GIRQ(blk, esc_idx); } + + /* If this is a single-escalation gather queue, that's all + * there is to return + */ + if (eq->w0 & EQ_W0_SILENT_ESCALATE) { + if (out_qflags) + *out_qflags = 0; + if (out_qpage) + *out_qpage = 0; + if (out_qsize) + *out_qsize = 0; + if (out_qeoi_page) + *out_qeoi_page = 0; + return OPAL_SUCCESS; + } + if (out_qpage) { if (eq->w0 & EQ_W0_ENQUEUE) *out_qpage = @@ -3978,6 +4011,12 @@ static int64_t opal_xive_set_queue_info(uint64_t vp, uint32_t prio, if (!old_eq) return OPAL_PARAMETER; + /* If this is a silent escalation queue, it cannot be + * configured directly + */ + if (old_eq->w0 & EQ_W0_SILENT_ESCALATE) + return OPAL_PARAMETER; + /* This shouldn't fail or xive_eq_for_target would have * failed already */ @@ -4098,9 +4137,32 @@ static int64_t opal_xive_get_vp_info(uint64_t vp_id, return OPAL_PARAMETER; if (out_flags) { + uint32_t eq_blk, eq_idx; + struct xive_eq *eq; + struct xive *eq_x; *out_flags = 0; + + /* We would like to a way to stash a SW bit in the VP to + * know whether silent escalation is enabled or not, but + * unlike what happens with EQs, the PC cache watch doesn't + * implement the reserved bit in the VPs... so we have to go + * look at EQ 7 instead. + */ + /* Grab EQ for prio 7 to check for silent escalation */ + if (!xive_eq_for_target(vp_id, 7, &eq_blk, &eq_idx)) + return OPAL_PARAMETER; + + eq_x = xive_from_vc_blk(eq_blk); + if (!eq_x) + return OPAL_PARAMETER; + + eq = xive_get_eq(x, eq_idx); + if (!eq) + return OPAL_PARAMETER; if (vp->w0 & VP_W0_VALID) *out_flags |= OPAL_XIVE_VP_ENABLED; + if (eq->w0 & EQ_W0_SILENT_ESCALATE) + *out_flags |= OPAL_XIVE_VP_SINGLE_ESCALATION; } if (out_cam_value) @@ -4117,6 +4179,94 @@ static int64_t opal_xive_get_vp_info(uint64_t vp_id, return OPAL_SUCCESS; } +static int64_t xive_setup_silent_gather(uint64_t vp_id, bool enable) +{ + uint32_t blk, idx, i; + struct xive_eq *eq_orig; + struct xive_eq eq; + struct xive *x; + int64_t rc; + + /* Get base EQ block */ + if (!xive_eq_for_target(vp_id, 0, &blk, &idx)) + return OPAL_PARAMETER; + x = xive_from_vc_blk(blk); + if (!x) + return OPAL_PARAMETER; + + /* Grab prio 7 */ + eq_orig = xive_get_eq(x, idx + 7); + if (!eq_orig) + return OPAL_PARAMETER; + + /* If trying to enable silent gather, make sure prio 7 is not + * already enabled as a normal queue + */ + if (enable && (eq_orig->w0 & EQ_W0_VALID) && + !(eq_orig->w0 & EQ_W0_SILENT_ESCALATE)) { + xive_dbg(x, "Attempt at enabling silent gather but" + " prio 7 queue already in use\n"); + return OPAL_PARAMETER; + } + + eq = *eq_orig; + + if (enable) { + /* W0: Enabled and "s" set, no other bit */ + eq.w0 &= EQ_W0_FIRMWARE; + eq.w0 |= EQ_W0_VALID | EQ_W0_SILENT_ESCALATE | + EQ_W0_ESCALATE_CTL | EQ_W0_BACKLOG; + + /* W1: Mark ESn as 01, ESe as 00 */ + eq.w1 &= ~EQ_W1_ESn_P; + eq.w1 |= EQ_W1_ESn_Q; + eq.w1 &= ~(EQ_W1_ESe); + } else if (eq.w0 & EQ_W0_SILENT_ESCALATE) + xive_cleanup_eq(&eq); + + if (!memcmp(eq_orig, &eq, sizeof(eq))) + rc = 0; + else + rc = xive_eqc_cache_update(x, blk, idx + 7, 0, 4, &eq, + false, false); + if (rc) + return rc; + + /* Mark/unmark all other prios with the new "u" bit and update + * escalation + */ + for (i = 0; i < 6; i++) { + eq_orig = xive_get_eq(x, idx + i); + if (!eq_orig) + continue; + eq = *eq_orig; + if (enable) { + /* Set new "u" bit */ + eq.w0 |= EQ_W0_UNCOND_ESCALATE; + + /* Re-route escalation interrupt (previous + * route is lost !) to the gather queue + */ + eq.w4 = SETFIELD(EQ_W4_ESC_EQ_BLOCK, + eq.w4, blk); + eq.w4 = SETFIELD(EQ_W4_ESC_EQ_INDEX, + eq.w4, idx + 7); + } else if (eq.w0 & EQ_W0_UNCOND_ESCALATE) { + /* Clear the "u" bit, disable escalations if it was set */ + eq.w0 &= ~EQ_W0_UNCOND_ESCALATE; + eq.w0 &= ~EQ_W0_ESCALATE_CTL; + } + if (!memcmp(eq_orig, &eq, sizeof(eq))) + continue; + rc = xive_eqc_cache_update(x, blk, idx + i, 0, 4, &eq, + false, false); + if (rc) + break; + } + + return rc; +} + static int64_t opal_xive_set_vp_info(uint64_t vp_id, uint64_t flags, uint64_t report_cl_pair) @@ -4141,22 +4291,39 @@ static int64_t opal_xive_set_vp_info(uint64_t vp_id, if (!vp) return OPAL_PARAMETER; + lock(&x->lock); + vp_new = *vp; if (flags & OPAL_XIVE_VP_ENABLED) { vp_new.w0 |= VP_W0_VALID; vp_new.w6 = report_cl_pair >> 32; vp_new.w7 = report_cl_pair & 0xffffffff; - } else - vp_new.w0 = vp_new.w6 = vp_new.w7 = 0; + if (flags & OPAL_XIVE_VP_SINGLE_ESCALATION) { + if (x->rev < XIVE_REV_2) { + xive_dbg(x, "Attempt at enabling single escalate" + " on xive rev %d failed\n", + x->rev); + return OPAL_PARAMETER; + } + rc = xive_setup_silent_gather(vp_id, true); + } else + rc = xive_setup_silent_gather(vp_id, false); + } else { + vp_new.w0 = vp_new.w6 = vp_new.w7 = 0; + rc = xive_setup_silent_gather(vp_id, false); + } - lock(&x->lock); - rc = xive_vpc_cache_update(x, blk, idx, 0, 8, &vp_new, false, false); if (rc) { - unlock(&x->lock); - return rc; + if (rc != OPAL_BUSY) + xive_dbg(x, "Silent gather setup failed with err %lld\n", rc); + goto bail; } + rc = xive_vpc_cache_update(x, blk, idx, 0, 8, &vp_new, false, false); + if (rc) + goto bail; + /* When disabling, we scrub clean (invalidate the entry) so * we can avoid cache ops in alloc/free */ @@ -4164,8 +4331,8 @@ static int64_t opal_xive_set_vp_info(uint64_t vp_id, xive_vpc_scrub_clean(x, blk, idx); unlock(&x->lock); - - return OPAL_SUCCESS; +bail: + return rc; } static void xive_cleanup_cpu_tima(struct cpu_thread *c) @@ -4529,10 +4696,8 @@ static int64_t opal_xive_free_vp_block(uint64_t vp_base) } /* VP must be disabled */ - if (vp->w0 & VP_W0_VALID) { - prerror("XIVE: Freeing enabled VP !\n"); - // XXX Disable it synchronously - } + if (vp->w0 & VP_W0_VALID) + return OPAL_XIVE_FREE_ACTIVE; /* Not populated */ if (vp->w1 == 0) diff --git a/include/xive.h b/include/xive.h index 63ee77b..ae7bb0b 100644 --- a/include/xive.h +++ b/include/xive.h @@ -397,13 +397,14 @@ struct xive_ive { /* EQ */ struct xive_eq { uint32_t w0; -#define EQ_W0_VALID PPC_BIT32(0) -#define EQ_W0_ENQUEUE PPC_BIT32(1) -#define EQ_W0_UCOND_NOTIFY PPC_BIT32(2) -#define EQ_W0_BACKLOG PPC_BIT32(3) -#define EQ_W0_PRECL_ESC_CTL PPC_BIT32(4) -#define EQ_W0_ESCALATE_CTL PPC_BIT32(5) -#define EQ_W0_END_OF_INTR PPC_BIT32(6) +#define EQ_W0_VALID PPC_BIT32(0) /* "v" bit */ +#define EQ_W0_ENQUEUE PPC_BIT32(1) /* "q" bit */ +#define EQ_W0_UCOND_NOTIFY PPC_BIT32(2) /* "n" bit */ +#define EQ_W0_BACKLOG PPC_BIT32(3) /* "b" bit */ +#define EQ_W0_PRECL_ESC_CTL PPC_BIT32(4) /* "p" bit */ +#define EQ_W0_ESCALATE_CTL PPC_BIT32(5) /* "e" bit */ +#define EQ_W0_UNCOND_ESCALATE PPC_BIT32(6) /* "u" bit - DD2.0 */ +#define EQ_W0_SILENT_ESCALATE PPC_BIT32(7) /* "s" bit - DD2.0 */ #define EQ_W0_QSIZE PPC_BITMASK32(12,15) #define EQ_W0_SW0 PPC_BIT32(16) #define EQ_W0_FIRMWARE EQ_W0_SW0 /* Owned by FW */ |