Re: [PATCH 1/3] powernv/cpuidle: Parse dt idle properties into global structure

From: Gautham R Shenoy
Date: Wed Jun 20 2018 - 00:41:56 EST


Hi Akshay,

On Tue, Jun 19, 2018 at 10:34:26AM +0530, Akshay Adiga wrote:
> Device-tree parsing happens in twice, once while deciding idle state to
> be used for hotplug and once during cpuidle init. Hence, parsing the
> device tree and caching it will reduce code duplication. Parsing code
> has been moved to pnv_parse_cpuidle_dt() from pnv_probe_idle_states().
>
> Setting up things so that number of available idle states can be
> accessible to cpuidle-powernv driver. Hence adding nr_pnv_idle_states to
> track number of idle states.
>
> Signed-off-by: Akshay Adiga <akshay.adiga@xxxxxxxxxxxxxxxxxx>
> ---
> arch/powerpc/include/asm/cpuidle.h | 14 +++
> arch/powerpc/platforms/powernv/idle.c | 197 ++++++++++++++++++++++++----------
> 2 files changed, 152 insertions(+), 59 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/cpuidle.h b/arch/powerpc/include/asm/cpuidle.h
> index e210a83..55ee7e3 100644
> --- a/arch/powerpc/include/asm/cpuidle.h
> +++ b/arch/powerpc/include/asm/cpuidle.h
> @@ -79,6 +79,20 @@ struct stop_sprs {
> u64 mmcra;
> };
>
> +#define PNV_IDLE_NAME_LEN 16
> +struct pnv_idle_states_t {
> + char name[PNV_IDLE_NAME_LEN];
> + u32 latency_ns;
> + u32 residency_ns;
> + /*
> + * Register value/mask used to select different idle states.
> + * PMICR in POWER8 and PSSCR in POWER9
> + */
> + u64 pm_ctrl_reg_val;
> + u64 pm_ctrl_reg_mask;

We don't use pmicr on POWER8. So I don't mind if you rename this to
psscr_val and psscr_mask.

Or atleast have
union {
u64 psscr; /* P9 onwards */
u64 pmicr /* P7 and P8 */
} val;

union {
u64 psscr; /* P9 onwards */
u64 pmicr; /* P7 and P8 */
} mask;





> + u32 flags;
> +};
> +
> extern u32 pnv_fastsleep_workaround_at_entry[];
> extern u32 pnv_fastsleep_workaround_at_exit[];
>
> diff --git a/arch/powerpc/platforms/powernv/idle.c b/arch/powerpc/platforms/powernv/idle.c
> index 1c5d067..07be984 100644
> --- a/arch/powerpc/platforms/powernv/idle.c
> +++ b/arch/powerpc/platforms/powernv/idle.c
> @@ -36,6 +36,8 @@
> #define P9_STOP_SPR_PSSCR 855
>
> static u32 supported_cpuidle_states;
> +struct pnv_idle_states_t *pnv_idle_states;
> +int nr_pnv_idle_states;
>
> /*
> * The default stop state that will be used by ppc_md.power_save
> @@ -625,45 +627,8 @@ int validate_psscr_val_mask(u64 *psscr_val, u64 *psscr_mask, u32 flags)
> static int __init pnv_power9_idle_init(struct device_node *np, u32 *flags,
> int dt_idle_states)

We are not using np, flags in this function right? Also dt_idle_states
can be obtained from the global variable nr_pnv_idle_states.


> {
> - u64 *psscr_val = NULL;
> - u64 *psscr_mask = NULL;
> - u32 *residency_ns = NULL;
> u64 max_residency_ns = 0;
> - int rc = 0, i;
> -
> - psscr_val = kcalloc(dt_idle_states, sizeof(*psscr_val), GFP_KERNEL);
> - psscr_mask = kcalloc(dt_idle_states, sizeof(*psscr_mask), GFP_KERNEL);
> - residency_ns = kcalloc(dt_idle_states, sizeof(*residency_ns),
> - GFP_KERNEL);
> -
> - if (!psscr_val || !psscr_mask || !residency_ns) {
> - rc = -1;
> - goto out;
> - }
> -
> - if (of_property_read_u64_array(np,
> - "ibm,cpu-idle-state-psscr",
> - psscr_val, dt_idle_states)) {
> - pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-psscr in DT\n");
> - rc = -1;
> - goto out;
> - }
> -
> - if (of_property_read_u64_array(np,
> - "ibm,cpu-idle-state-psscr-mask",
> - psscr_mask, dt_idle_states)) {
> - pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-psscr-mask in DT\n");
> - rc = -1;
> - goto out;
> - }
> -
> - if (of_property_read_u32_array(np,
> - "ibm,cpu-idle-state-residency-ns",
> - residency_ns, dt_idle_states)) {
> - pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-residency-ns in DT\n");
> - rc = -1;
> - goto out;
> - }
> + int i;
>
> /*
> * Set pnv_first_deep_stop_state, pnv_deepest_stop_psscr_{val,mask},
> @@ -681,31 +646,33 @@ static int __init pnv_power9_idle_init(struct device_node *np, u32 *flags,
> 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;
> + struct pnv_idle_states_t *state = &pnv_idle_states[i];
> + u64 psscr_rl = state->pm_ctrl_reg_val & PSSCR_RL_MASK;
>
> - if ((flags[i] & OPAL_PM_LOSE_FULL_CONTEXT) &&
> - (pnv_first_deep_stop_state > psscr_rl))
> + if ((state->flags & 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]);
> + err = validate_psscr_val_mask(&state->pm_ctrl_reg_val,
> + &state->pm_ctrl_reg_mask,
> + state->flags);


We could have a "bool valid" field in the pnv_idle_states_t struct
for explicitly recording any invalid states in order to prevent any
other subsystems from using it. We are doing the validation of the
psscr_val and mask twice today - once in this code and once again in
the cpuidle code.


> if (err) {
> - report_invalid_psscr_val(psscr_val[i], err);
> + report_invalid_psscr_val(state->pm_ctrl_reg_val, err);
> continue;
> }
>
> - 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];
> - pnv_deepest_stop_flag = flags[i];
> + if (max_residency_ns < state->residency_ns) {
> + max_residency_ns = state->residency_ns;
> + pnv_deepest_stop_psscr_val = state->pm_ctrl_reg_val;
> + pnv_deepest_stop_psscr_mask = state->pm_ctrl_reg_mask;
> + pnv_deepest_stop_flag = state->flags;
> deepest_stop_found = true;
> }
>
> if (!default_stop_found &&
> - (flags[i] & OPAL_PM_STOP_INST_FAST)) {
> - pnv_default_stop_val = psscr_val[i];
> - pnv_default_stop_mask = psscr_mask[i];
> + (state->flags & OPAL_PM_STOP_INST_FAST)) {
> + pnv_default_stop_val = state->pm_ctrl_reg_val;
> + pnv_default_stop_mask = state->pm_ctrl_reg_mask;
> default_stop_found = true;
> }
> }
> @@ -728,11 +695,8 @@ static int __init pnv_power9_idle_init(struct device_node *np, u32 *flags,
>
> pr_info("cpuidle-powernv: Requested Level (RL) value of first deep stop = 0x%llx\n",
> pnv_first_deep_stop_state);
> -out:
> - kfree(psscr_val);
> - kfree(psscr_mask);
> - kfree(residency_ns);
> - return rc;
> +
> + return 0;
> }
>
> /*
> @@ -776,14 +740,129 @@ static void __init pnv_probe_idle_states(void)
> out:
> kfree(flags);
> }
> -static int __init pnv_init_idle_states(void)
> +
> +/*
> + * This function is use to parse device-tree and populates all the information
> + * into pnv_idle_states structure. Also it sets up nr_pnv_idle_states and
> + * the number of cpuidle states discovered through device-tree.

"Also it sets up nr_pnv_idle_states *which is* the number of cpuidle
states discovered through the device-tree."

> + */
> +
> +static int pnv_parse_cpuidle_dt(void)
> {
> + struct device_node *np;
> + int nr_idle_states, i;
> + u32 *temp_u32;
> + u64 *temp_u64;
> + const char **temp_string;
> +
> + np = of_find_node_by_path("/ibm,opal/power-mgt");
> + if (!np) {
> + pr_warn("opal: PowerMgmt Node not found\n");
> + return -ENODEV;
> + }
> + nr_idle_states = of_property_count_u32_elems(np,
> + "ibm,cpu-idle-state-flags");
> +
> + pnv_idle_states = kcalloc(nr_idle_states, sizeof(*pnv_idle_states),
> + GFP_KERNEL);
> + temp_u32 = kcalloc(nr_idle_states, sizeof(u32), GFP_KERNEL);
> + temp_u64 = kcalloc(nr_idle_states, sizeof(u64), GFP_KERNEL);
> + temp_string = kcalloc(nr_idle_states, sizeof(char *), GFP_KERNEL);

We should ensure that the allocations has succeeded here, else
bail out and disable the idle states.

> +
> + /* Read flags */
> + if (of_property_read_u32_array(np,
> + "ibm,cpu-idle-state-flags", temp_u32, nr_idle_states)) {
> + pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-flags in DT\n");
> + goto out;
> + }

There was some device-tree hardening in the cpuidle code which I think
can be moved to this place.

> + for (i = 0; i < nr_idle_states; i++)
> + pnv_idle_states[i].flags = temp_u32[i];
>
> + /* Read latencies */
> + if (of_property_read_u32_array(np,
> + "ibm,cpu-idle-state-latencies-ns", temp_u32, nr_idle_states)) {
> + pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-latencies-ns in DT\n");
> + goto out;
> + }
> + for (i = 0; i < nr_idle_states; i++)
> + pnv_idle_states[i].latency_ns = temp_u32[i];
> +
> + /* Read residencies */
> + if (of_property_read_u32_array(np,
> + "ibm,cpu-idle-state-residency-ns", temp_u32, nr_idle_states)) {
> + pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-latencies-ns in DT\n");
> + goto out;
> + }
> + for (i = 0; i < nr_idle_states; i++)
> + pnv_idle_states[i].residency_ns = temp_u32[i];
> +
> + /* For power9 */
> + if (cpu_has_feature(CPU_FTR_ARCH_300)) {
> + /* Read pm_crtl_val */
> + if (of_property_read_u64_array(np,
> + "ibm,cpu-idle-state-psscr", temp_u64, nr_idle_states)) {
> + pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-psscr in DT\n");
> + goto out;
> + }
> + for (i = 0; i < nr_idle_states; i++)
> + pnv_idle_states[i].pm_ctrl_reg_val = temp_u64[i];
> +
> + /* Read pm_crtl_mask */
> + if (of_property_read_u64_array(np,
> + "ibm,cpu-idle-state-psscr-mask", temp_u64, nr_idle_states)) {
> + pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-psscr-mask in DT\n");
> + goto out;
> + }
> + for (i = 0; i < nr_idle_states; i++)
> + pnv_idle_states[i].pm_ctrl_reg_mask = temp_u64[i];
> + } else { /* Power8 and Power7 */
> + /* Read pm_crtl_val */
> + if (of_property_read_u64_array(np,
> + "ibm,cpu-idle-state-pmicr", temp_u64, nr_idle_states)) {
> + pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-psscr in DT\n");
> + goto out;
> + }
> + for (i = 0; i < nr_idle_states; i++)
> + pnv_idle_states[i].pm_ctrl_reg_val = temp_u64[i];
> +
> + /* Read pm_crtl_mask */
> + if (of_property_read_u64_array(np,
> + "ibm,cpu-idle-state-pmicr-mask", temp_u64, nr_idle_states)) {
> + pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-psscr-mask in DT\n");
> + goto out;
> + }
> + for (i = 0; i < nr_idle_states; i++)
> + pnv_idle_states[i].pm_ctrl_reg_mask = temp_u64[i];

We don't use the pmicr val and the mask in the kernel for either
POWER7 or POWER8. So, is this "else" block required ?

> +
> + }
> + if (of_property_read_string_array(np,
> + "ibm,cpu-idle-state-names", temp_string, nr_idle_states) < 0) {
> + pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-names in DT\n");
> + goto out;
> + }
> + for (i = 0; i < nr_idle_states; i++)
> + strncpy(pnv_idle_states[i].name, temp_string[i],
> + PNV_IDLE_NAME_LEN);
> + nr_pnv_idle_states = nr_idle_states;
> +out:
> + kfree(temp_u32);
> + kfree(temp_u64);
> + kfree(temp_string);
> + return 0;

We shouldn't be returning 0 we have come to "out" due to any failure
in the device-tree parsing ?

> +}
> +
> +static int __init pnv_init_idle_states(void)
> +{
> + int rc = 0;
> supported_cpuidle_states = 0;
>
> + /* In case we error out nr_pnv_idle_states will be zero */
> + nr_pnv_idle_states = 0;
> if (cpuidle_disable != IDLE_NO_OVERRIDE)
> goto out;
> -
> + rc = pnv_parse_cpuidle_dt();
> + if (rc)
> + return rc;
> pnv_probe_idle_states();
>
> if (!(supported_cpuidle_states & OPAL_PM_SLEEP_ENABLED_ER1)) {
> --
> 2.5.5
>