Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751342AbdHDHEq (ORCPT ); Fri, 4 Aug 2017 03:04:46 -0400 Received: from mx0a-001b2d01.pphosted.com ([148.163.156.1]:55350 "EHLO mx0a-001b2d01.pphosted.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751282AbdHDHEn (ORCPT ); Fri, 4 Aug 2017 03:04:43 -0400 From: "Gautham R. Shenoy" To: Michael Ellerman , Michael Neuling , Nicholas Piggin , Vaidyanathan Srinivasan , Shilpasri G Bhat , "Rafael J. Wysocki" , Akshay Adiga Cc: linuxppc-dev@lists.ozlabs.org, linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, "Gautham R. Shenoy" Subject: [PATCH] powernv:idle: Disable LOSE_FULL_CONTEXT states when stop-api fails. Date: Fri, 4 Aug 2017 12:34:22 +0530 X-Mailer: git-send-email 1.8.3.1 X-TM-AS-GCONF: 00 x-cbid: 17080407-0024-0000-0000-000016F8FA4C X-IBM-SpamModules-Scores: X-IBM-SpamModules-Versions: BY=3.00007481; HX=3.00000241; KW=3.00000007; PH=3.00000004; SC=3.00000216; SDB=6.00897324; UDB=6.00448977; IPR=6.00677508; BA=6.00005509; NDR=6.00000001; ZLA=6.00000005; ZF=6.00000009; ZB=6.00000000; ZP=6.00000000; ZH=6.00000000; ZU=6.00000002; MB=3.00016527; XFM=3.00000015; UTC=2017-08-04 07:04:40 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 17080407-0025-0000-0000-00004C243999 Message-Id: <1501830262-32399-1-git-send-email-ego@linux.vnet.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:,, definitions=2017-08-04_02:,, signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 suspectscore=0 malwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1706020000 definitions=main-1708040106 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 8422 Lines: 254 From: "Gautham R. Shenoy" Currently, we use the opal call opal_slw_set_reg() to inform the Sleep-Winkle Engine (SLW) to restore the contents of some of the Hypervisor state on wakeup from deep idle states that lose full hypervisor context (characterized by the flag OPAL_PM_LOSE_FULL_CONTEXT). However, the current code has a bug in that if opal_slw_set_reg() fails, we don't disable the use of these deep states (winkle on POWER8, stop4 onwards on POWER9). This patch fixes this bug by ensuring that if programing the sleep-winkle engine to restore the hypervisor states in pnv_save_sprs_for_deep_states() fails, then we exclude such states by excluding their flags supported_cpuidle_states. Further, we ensure in the initialization of the cpuidle-powernv driver to only include those states whose flags are present in supported_cpuidle_states. Fixes: 1e1601b38e6 ("powerpc/powernv/idle: Restore SPRs for deep idle states via stop API.") Signed-off-by: Gautham R. Shenoy --- arch/powerpc/platforms/powernv/idle.c | 126 +++++++++++++++++++++++++++------- drivers/cpuidle/cpuidle-powernv.c | 9 +++ 2 files changed, 110 insertions(+), 25 deletions(-) diff --git a/arch/powerpc/platforms/powernv/idle.c b/arch/powerpc/platforms/powernv/idle.c index 2abee07..5f4c206 100644 --- a/arch/powerpc/platforms/powernv/idle.c +++ b/arch/powerpc/platforms/powernv/idle.c @@ -184,9 +184,6 @@ static void pnv_alloc_idle_core_states(void) } update_subcore_sibling_mask(); - - if (supported_cpuidle_states & OPAL_PM_LOSE_FULL_CONTEXT) - pnv_save_sprs_for_deep_states(); } u32 pnv_get_supported_cpuidle_states(void) @@ -467,8 +464,39 @@ int validate_psscr_val_mask(u64 *psscr_val, u64 *psscr_mask, u32 flags) return err; } +static void __init pnv_power8_idle_init(struct device_node *np, u32 *flags, + int dt_idle_states) +{ + bool disable_full_context_loss = false; + bool sprs_for_lose_full_context_saved = false; + + int rc = 0, i; + + for (i = 0; i < dt_idle_states; i++) { + if (flags[i] & OPAL_PM_LOSE_FULL_CONTEXT) { + if (sprs_for_lose_full_context_saved) + goto add_flags; + + if (disable_full_context_loss) + continue; + + rc = pnv_save_sprs_for_deep_states(); + + if (unlikely(rc)) { + pr_warn("cpuidle-powernv: Disabling full context loss idle states.\n"); + pr_warn("cpuidle-powernv: Offlined CPUs will be put to shallow idle state.\n"); + disable_full_context_loss = true; + continue; + } + + sprs_for_lose_full_context_saved = true; + } +add_flags: + supported_cpuidle_states |= flags[i]; + } +} /* - * pnv_arch300_idle_init: Initializes the default idle state, first + * pnv_power9_idle_init: Initializes the default idle state, first * deep idle state and deepest idle state on * ISA 3.0 CPUs. * @@ -485,6 +513,9 @@ static int __init pnv_power9_idle_init(struct device_node *np, u32 *flags, u32 *residency_ns = NULL; u64 max_residency_ns = 0; int rc = 0, i; + bool save_sprs_for_full_context_loss = false; + bool disable_full_context_loss = false; + unsigned long invalid_states_mask = 0; psscr_val = kcalloc(dt_idle_states, sizeof(*psscr_val), GFP_KERNEL); psscr_mask = kcalloc(dt_idle_states, sizeof(*psscr_mask), GFP_KERNEL); @@ -521,35 +552,83 @@ static int __init pnv_power9_idle_init(struct device_node *np, u32 *flags, } /* + * States that have OPAL_PM_LOSE_FULL_CONTEXT flag set require + * the assistance of the slw engine to restore certain SPRs on + * wakeup from these states. The function to program the slw + * engine via stop-api expects pnv_deep_stop_psscr_val to be + * set before it is called. + * + * Hence, we first set the pnv_deepest_stop_psscr_{val,mask} + * to the value corresponding to deepest state. + */ + for (i = 0; i < dt_idle_states; i++) { + int err; + + err = validate_psscr_val_mask(&psscr_val[i], &psscr_mask[i], + flags[i]); + if (err) { + report_invalid_psscr_val(psscr_val[i], err); + set_bit(i, &invalid_states_mask); + continue; + } + + if (flags[i] & OPAL_PM_LOSE_FULL_CONTEXT) + save_sprs_for_full_context_loss = true; + + if (max_residency_ns < residency_ns[i]) { + max_residency_ns = residency_ns[i]; + pnv_deepest_stop_psscr_val = psscr_val[i]; + pnv_deepest_stop_psscr_mask = psscr_mask[i]; + deepest_stop_found = true; + } + } + + /* + * Program the SLW via stop-api to restore some of the SPRs + * after wakeup from a LOSE_FULL_CONTEXT idle state. + */ + if (save_sprs_for_full_context_loss) { + int rc; + + rc = pnv_save_sprs_for_deep_states(); + if (unlikely(rc)) { + pr_warn("cpuidle-powernv: Disabling full context loss idle states.\n"); + pr_warn("cpuidle-powernv: Idle powersavings impacted.\n"); + disable_full_context_loss = true; + max_residency_ns = 0; + deepest_stop_found = false; + } + } + + /* * Set pnv_first_deep_stop_state, pnv_deepest_stop_psscr_{val,mask}, * and the pnv_default_stop_{val,mask}. * * pnv_first_deep_stop_state should be set to the first stop * level to cause hypervisor state loss. * - * pnv_deepest_stop_{val,mask} should be set to values corresponding to - * the deepest stop state. + * If the stop-api failed above, then pnv_deepest_stop_{val,mask} + * should be set to values corresponding to the deepest stop + * state that doesn't have OPAL_PM_LOSE_FULL_CONTEXT set. * * pnv_default_stop_{val,mask} should be set to values corresponding to * the shallowest (OPAL_PM_STOP_INST_FAST) loss-less stop state. */ pnv_first_deep_stop_state = MAX_STOP_STATE; for (i = 0; i < dt_idle_states; i++) { - int err; u64 psscr_rl = psscr_val[i] & PSSCR_RL_MASK; - if ((flags[i] & OPAL_PM_LOSE_FULL_CONTEXT) && - (pnv_first_deep_stop_state > psscr_rl)) - pnv_first_deep_stop_state = psscr_rl; - - err = validate_psscr_val_mask(&psscr_val[i], &psscr_mask[i], - flags[i]); - if (err) { - report_invalid_psscr_val(psscr_val[i], err); + if (test_bit(i, &invalid_states_mask)) continue; + + if (flags[i] & OPAL_PM_LOSE_FULL_CONTEXT) { + if (disable_full_context_loss) + continue; + else if (pnv_first_deep_stop_state > psscr_rl) + pnv_first_deep_stop_state = psscr_rl; } - if (max_residency_ns < residency_ns[i]) { + if (unlikely(max_residency_ns < residency_ns[i])) { max_residency_ns = residency_ns[i]; pnv_deepest_stop_psscr_val = psscr_val[i]; pnv_deepest_stop_psscr_mask = psscr_mask[i]; @@ -562,6 +641,8 @@ static int __init pnv_power9_idle_init(struct device_node *np, u32 *flags, pnv_default_stop_mask = psscr_mask[i]; default_stop_found = true; } + + supported_cpuidle_states |= flags[i]; } if (unlikely(!default_stop_found)) { @@ -597,7 +678,6 @@ static void __init pnv_probe_idle_states(void) struct device_node *np; int dt_idle_states; u32 *flags = NULL; - int i; np = of_find_node_by_path("/ibm,opal/power-mgt"); if (!np) { @@ -619,14 +699,10 @@ static void __init pnv_probe_idle_states(void) goto out; } - if (cpu_has_feature(CPU_FTR_ARCH_300)) { - if (pnv_power9_idle_init(np, flags, dt_idle_states)) - goto out; - } - - for (i = 0; i < dt_idle_states; i++) - supported_cpuidle_states |= flags[i]; - + if (cpu_has_feature(CPU_FTR_ARCH_300)) + pnv_power9_idle_init(np, flags, dt_idle_states); + else + pnv_power8_idle_init(np, flags, dt_idle_states); out: kfree(flags); } diff --git a/drivers/cpuidle/cpuidle-powernv.c b/drivers/cpuidle/cpuidle-powernv.c index 37b0698..1a5875e 100644 --- a/drivers/cpuidle/cpuidle-powernv.c +++ b/drivers/cpuidle/cpuidle-powernv.c @@ -235,6 +235,7 @@ static inline int validate_dt_prop_sizes(const char *prop1, int prop1_len, return -1; } +extern u32 pnv_get_supported_cpuidle_states(void); static int powernv_add_idle_states(void) { struct device_node *power_mgt; @@ -362,6 +363,14 @@ static int powernv_add_idle_states(void) for (i = 0; i < dt_idle_states; i++) { unsigned int exit_latency, target_residency; bool stops_timebase = false; + u32 supported_flags = pnv_get_supported_cpuidle_states(); + + /* + * If a certain deep state isn't marked in + * supported_cpuidle_states, we skip it here. + */ + if ((flags[i] & supported_flags) != flags[i]) + continue; /* * If an idle state has exit latency beyond * POWERNV_THRESHOLD_LATENCY_NS then don't use it -- 1.8.3.1