From: sashiko-bot@kernel.org
To: "Abel Vesa" <abel.vesa@oss.qualcomm.com>
Cc: linux-clk@vger.kernel.org
Subject: Re: [PATCH RFC 2/3] clk: qcom: common: Restore the clock controller after hibernation
Date: Wed, 28 Oct 2026 10:44:18 +0000 [thread overview]
Message-ID: <sashiko-outbox-187761@kernel.org> (raw)
In-Reply-To: <20261028-b4-clk-restore-after-hibernation-v1-2-86cf62747a7f@oss.qualcomm.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] clk: qcom: common: dangling stack pointer in cc->desc after probe
- [High] clk: qcom: common: qcom_cc_restore() corrupts clk_rcg2_shared_ops state
--
commit a92f46011ef8a0320b3784fea1ac76fde1a0f05f
Author: Abel Vesa <abel.vesa@oss.qualcomm.com>
clk: qcom: common: Restore the clock controller after hibernation
This commit adds a hibernation restore callback to qcom_cc_pm_ops that
redoes probe-time PLL and register configuration and calls
clk_restore_context_dev() to restore cached clock state after hibernation.
> diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
> index 08fd62ac780d3..ff243fc781887 100644
> --- a/drivers/clk/qcom/common.c
> +++ b/drivers/clk/qcom/common.c
[ ... ]
> @@ -333,9 +340,13 @@ int qcom_cc_really_probe(struct device *dev,
> size_t num_clk_hws = desc->num_clk_hws;
> struct clk_hw **clk_hws = desc->clk_hws;
>
> - cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL);
> + cc = devres_alloc(qcom_cc_release, sizeof(*cc), GFP_KERNEL);
> if (!cc)
> return -ENOMEM;
> + devres_add(dev, cc);
> +
> + cc->desc = desc;
> + cc->regmap = regmap;
[Severity: High]
Can storing the desc pointer in cc->desc leave a dangling stack pointer after
probe returns?
Several Qualcomm clock controller drivers allocate a local struct qcom_cc_desc
on the stack in their probe functions to customize fields per SoC variant and
pass &desc to qcom_cc_really_probe():
drivers/clk/qcom/camcc-sa8775p.c:cam_cc_sa8775p_probe() {
struct qcom_cc_desc desc = cam_cc_sa8775p_desc;
...
desc.driver_data = &cam_cc_qcs8300_driver_data;
}
ret = qcom_cc_really_probe(&pdev->dev, &desc, regmap);
}
drivers/clk/qcom/videocc-sm8350.c:video_cc_sm8350_probe() {
struct qcom_cc_desc desc = video_cc_sm8350_desc;
...
desc.driver_data = &video_cc_sc8280xp_driver_data;
...
ret = qcom_cc_really_probe(&pdev->dev, &desc, regmap);
}
drivers/clk/qcom/gcc-ipq5018.c:gcc_ipq5018_probe() {
struct qcom_cc_desc ipq5018_desc = gcc_ipq5018_desc;
...
return qcom_cc_really_probe(&pdev->dev, &ipq5018_desc, regmap);
}
When qcom_cc_restore() later dereferences cc->desc (checking desc->use_rpm and
desc->driver_data), the probe stack frame has already been reclaimed. Would it
be safer to store desc->use_rpm and desc->driver_data directly in struct
qcom_cc instead of retaining the desc pointer?
>
> ret = devm_pm_domain_attach_list(dev, NULL, &cc->pd_list);
> if (ret < 0 && ret != -EEXIST)
> @@ -434,6 +445,45 @@ int qcom_cc_really_probe(struct device *dev,
> }
> EXPORT_SYMBOL_GPL(qcom_cc_really_probe);
>
> +/* Redo the probe time setup and the cached clock state after a power cycle */
> +static int qcom_cc_restore(struct device *dev)
> +{
> + struct qcom_cc *cc = devres_find(dev, qcom_cc_release, NULL, NULL);
> + const struct qcom_cc_desc *desc;
> + int ret = 0;
> +
> + if (!cc)
> + return 0;
> +
> + desc = cc->desc;
> +
> + if (desc->use_rpm) {
> + ret = pm_runtime_resume_and_get(dev);
> + if (ret)
> + return ret;
> + }
> +
> + if (desc->driver_data) {
> + ret = qcom_cc_clk_pll_configure(desc->driver_data, cc->regmap);
> + if (ret)
> + goto put_rpm;
> +
> + qcom_cc_clk_regs_configure(dev, desc->driver_data, cc->regmap);
> + }
> +
> + clk_restore_context_dev(dev);
[Severity: High]
Does calling clk_restore_context_dev() here corrupt the hardware state of
clocks using clk_rcg2_shared_ops (as well as clk_rcg2_shared_floor_ops and
clk_rcg2_shared_no_init_park_ops)?
In clk_core_restore_state(), the clock framework restores a clock by calling
set_parent(), set_rate(), prepare(), and enable() in sequence:
drivers/clk/clk.c:clk_core_restore_state() {
if (parent && core->num_parents > 1 && core->ops->set_parent) {
index = clk_fetch_parent_index(core, parent);
if (index >= 0)
core->ops->set_parent(core->hw, index);
}
if (core->rate && core->ops->set_rate)
core->ops->set_rate(core->hw, core->rate,
parent ? parent->rate : 0);
if (core->prepare_count && core->ops->prepare)
core->ops->prepare(core->hw);
if (core->enable_count && core->ops->enable) {
flags = clk_enable_lock();
core->ops->enable(core->hw);
clk_enable_unlock(flags);
}
}
Because clk_rcg2_shared_ops does not implement .is_enabled,
clk_hw_is_enabled(hw) evaluates to (core->enable_count > 0).
When a shared RCG is enabled across hibernation (core->enable_count > 0),
clk_rcg2_shared_set_parent() and __clk_rcg2_shared_set_rate() see
clk_hw_is_enabled(hw) == true and write the new configuration directly to
hardware CFG_REG without updating rcg->parked_cfg:
drivers/clk/qcom/clk-rcg2.c:__clk_rcg2_shared_set_rate() {
...
if (!clk_hw_is_enabled(hw))
return __clk_rcg2_configure(rcg, f, &rcg->parked_cfg);
return clk_rcg2_shared_force_enable_clear(hw, f);
}
Immediately afterwards, clk_core_restore_state() invokes core->ops->enable(),
which calls clk_rcg2_shared_enable():
drivers/clk/qcom/clk-rcg2.c:clk_rcg2_shared_enable() {
...
/* Write back the stored configuration corresponding to current rate */
ret = regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, rcg->parked_cfg);
if (ret)
return ret;
ret = update_config(rcg);
...
}
Since runtime rate and parent changes while the clock was enabled never
updated rcg->parked_cfg, won't clk_rcg2_shared_enable() overwrite CFG_REG with
a stale rcg->parked_cfg and clobber the rate and parent just programmed by
set_parent() and set_rate()?
Conversely, when core->enable_count == 0, clk_core_restore_state() does not
call .init (clk_rcg2_shared_init()) or .disable, and set_parent()/set_rate()
only update rcg->parked_cfg in memory without writing to hardware registers,
leaving any RCG enabled by boot firmware unparked in hardware.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261028-b4-clk-restore-after-hibernation-v1-0-86cf62747a7f@oss.qualcomm.com?part=2
next prev parent reply other threads:[~2026-10-28 10:44 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-28 9:52 [PATCH RFC 0/3] clk: Restore the clocks of a provider after a hibernation Abel Vesa
2026-10-28 9:52 ` [PATCH RFC 1/3] clk: Add a way to restore the clocks of a provider after a power cycle Abel Vesa
2026-10-28 10:13 ` sashiko-bot
2026-10-28 9:52 ` [PATCH RFC 2/3] clk: qcom: common: Restore the clock controller after hibernation Abel Vesa
2026-10-28 10:44 ` sashiko-bot [this message]
2026-10-28 9:52 ` [PATCH RFC 3/3] clk: qcom: dispcc-glymur: Restore the clocks " Abel Vesa
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-187761@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=abel.vesa@oss.qualcomm.com \
--cc=linux-clk@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox