From: sashiko-bot@kernel.org
To: DhruvinRajpura <dhruvin.rajpura@oss.qualcomm.com>
Cc: robh@kernel.org, conor+dt@kernel.org, devicetree@vger.kernel.org
Subject: Re: [PATCH 2/2] power: supply: qcom_battmgr: Add thermal mitigation support
Date: Tue, 09 Jun 2026 09:30:31 +0000 [thread overview]
Message-ID: <20260609093031.B30BE1F00893@smtp.kernel.org> (raw)
In-Reply-To: <20260609-cooling_device_reg-v1-2-e15bddcb0086@qti.qualcomm.com>
Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
New issues:
- [High] Software state properties are updated outside of `battmgr->lock`, creating data races and state desynchronization.
- [High] Thermal mitigation state is lost on firmware restart (PDR) if the charging limit was throttled to 0 uA.
- [High] Calling `devm_thermal_of_cooling_device_register()` from an asynchronous workqueue causes a resource leak and use-after-free race with device unbind.
- [Medium] The SM8550 variant lacks the `CHARGE_CONTROL_LIMIT` and `CHARGE_CONTROL_LIMIT_MAX` sysfs properties.
- [Medium] Reading the charge control limits triggers synchronous, blocking firmware requests whose results are ignored.
- [Medium] Userspace sysfs race allows throttling to 0 uA due to uninitialized `thermal_levels[0]`.
Pre-existing issues:
- [High] `qcom_battmgr_request` is called without acquiring `battmgr->lock` in `qcom_battmgr_enable_worker`.
--
commit c50b550e81a56aef117f049101952557cf7ba5cd
Author: Dhruvin Rajpura <drajpura@qti.qualcomm.com>
power: supply: qcom_battmgr: Add thermal mitigation support
This patch explicitly registers a thermal cooling device for the battery
manager to throttle charging current during thermal stress. It maps discrete
current levels from the qcom,thermal-mitigation DT property to cooling
states. It also exposes CHARGE_CONTROL_LIMIT properties to sysfs to allow
userspace observation and control.
> diff --git a/drivers/power/supply/qcom_battmgr.c b/drivers/power/supply/qcom_battmgr.c
> --- a/drivers/power/supply/qcom_battmgr.c
> +++ b/drivers/power/supply/qcom_battmgr.c
> @@ -449,6 +460,8 @@ static const u8 sm8350_bat_prop_map[] = {
> [POWER_SUPPLY_PROP_POWER_NOW] = BATT_POWER_NOW,
> [POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD] = BATT_CHG_CTRL_START_THR,
> [POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD] = BATT_CHG_CTRL_END_THR,
> + [POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT] = BATT_CHG_CTRL_LIM,
> + [POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX] = BATT_CHG_CTRL_LIM_MAX,
> };
[Severity: Medium]
Does mapping BATT_CHG_CTRL_LIM trigger a blocking BATTMGR_BAT_PROPERTY_GET
request over GLINK when userspace reads POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT?
Looking at qcom_battmgr_bat_get_property(), the result of this firmware update
is discarded, and the locally cached curr_thermal_level is returned instead.
Could this be avoided to prevent a useless IPC call?
[ ... ]
> @@ -770,6 +789,55 @@ static int qcom_battmgr_bat_is_writeable(struct power_supply *psy,
> return 0;
> }
>
> +static int __battery_psy_set_charge_current(struct qcom_battmgr *battmgr,
> + u32 fcc_ua)
> +{
> + int ret;
> +
> + mutex_lock(&battmgr->lock);
> + ret = qcom_battmgr_request_property(battmgr, BATTMGR_BAT_PROPERTY_SET,
> + BATT_CHG_CTRL_LIM, fcc_ua);
> + mutex_unlock(&battmgr->lock);
> + if (ret < 0) {
> + dev_err(battmgr->dev, "Failed to set FCC %u, ret=%d\n", fcc_ua, ret);
> + } else {
> + dev_dbg(battmgr->dev, "Set FCC to %u uA\n", fcc_ua);
> + battmgr->last_fcc_ua = fcc_ua;
[Severity: High]
Can concurrent updates desynchronize the software tracking variables from the
hardware state?
The GLINK request is protected by battmgr->lock, but last_fcc_ua is assigned
here after the lock is released.
> + }
> +
> + return ret;
> +}
> +
> +static int battery_psy_set_charge_current(struct qcom_battmgr *battmgr,
> + int val)
> +{
> + u32 fcc_ua, prev_fcc_ua;
> + int ret;
> +
> + if (!battmgr->num_thermal_levels)
> + return 0;
> +
> + if (battmgr->num_thermal_levels < 0) {
> + dev_err(battmgr->dev, "Incorrect num_thermal_levels\n");
> + return -EINVAL;
> + }
> +
> + if (val < 0 || val > battmgr->num_thermal_levels)
> + return -EINVAL;
> +
> + fcc_ua = battmgr->thermal_levels[val];
[Severity: Medium]
If sysfs writes to CHARGE_CONTROL_LIMIT occur after probe() but before
qcom_battmgr_enable_worker() has initialized thermal_levels[0], could this read
a zero-initialized value and improperly throttle charging to 0 uA?
> + prev_fcc_ua = battmgr->thermal_fcc_ua;
> + battmgr->thermal_fcc_ua = fcc_ua;
> +
> + ret = __battery_psy_set_charge_current(battmgr, fcc_ua);
> + if (!ret)
> + battmgr->curr_thermal_level = val;
> + else
> + battmgr->thermal_fcc_ua = prev_fcc_ua;
[Severity: High]
Additionally, thermal_fcc_ua and curr_thermal_level are updated here completely
outside of battmgr->lock. If thermal framework and sysfs operations concurrently
set the charge current, could the software state interleave with hardware
updates?
> +
> + return ret;
> +}
[ ... ]
> @@ -892,6 +977,8 @@ static const struct power_supply_desc sm8350_bat_psy_desc = {
> .properties = sm8350_bat_props,
> .num_properties = ARRAY_SIZE(sm8350_bat_props),
> .get_property = qcom_battmgr_bat_get_property,
> + .set_property = qcom_battmgr_bat_set_property,
> + .property_is_writeable = qcom_battmgr_prop_is_writeable,
> };
>
> static const enum power_supply_property sm8550_bat_props[] = {
[Severity: Medium]
The commit message states that CHARGE_CONTROL_LIMIT and
CHARGE_CONTROL_LIMIT_MAX are exposed to allow userspace control. Should
these properties also be added to sm8550_bat_props to expose them for the
SM8550 variant?
[ ... ]
> +static int qcom_battmgr_register_thermal_cooling_device(struct qcom_battmgr *battmgr)
> +{
> + struct thermal_cooling_device *tcd;
> + u32 prev;
> + int ret, i;
[ ... ]
> + battmgr->thermal_levels[0] = battmgr->status.max_charge_ctl_limit;
> + battmgr->thermal_fcc_ua = battmgr->status.max_charge_ctl_limit;
> +
> + /* Register Thermal Cooling Device */
> + tcd = devm_thermal_of_cooling_device_register(battmgr->dev,
> + battmgr->dev->of_node,
> + (char *)battmgr->bat_psy->desc->name,
> + battmgr, &battery_tcd_ops);
[Severity: High]
Is it safe to allocate devm_ resources from an asynchronous workqueue?
If the driver unbinds, devres_release_all() will free the battmgr structure.
If qcom_battmgr_enable_worker() runs concurrently or just after, could it
register a cooling device to a dead device's devres list, leading to a
resource leak and eventual use-after-free when the thermal core polls it?
[ ... ]
> @@ -1596,6 +1834,18 @@ static void qcom_battmgr_enable_worker(struct work_struct *work)
> struct qcom_battmgr *battmgr = container_of(work, struct qcom_battmgr, enable_work);
> struct qcom_battmgr_enable_request req = {
> .hdr.owner = cpu_to_le32(PMIC_GLINK_OWNER_BATTMGR),
> .hdr.type = cpu_to_le32(PMIC_GLINK_NOTIFY),
> .hdr.opcode = cpu_to_le32(BATTMGR_REQUEST_NOTIFICATION),
> };
> int ret;
>
> ret = qcom_battmgr_request(battmgr, &req, sizeof(req));
[Severity: High]
This isn't a bug introduced by this patch, but is it intentional that
qcom_battmgr_request() is called here without holding battmgr->lock?
If sysfs operations take the lock simultaneously, could this concurrent
execution mutate the GLINK communication state and corrupt
reinit_completion(&battmgr->ack)?
> if (ret)
> dev_err(battmgr->dev, "failed to request power notifications\n");
> +
> + if (!battmgr->thermal_dev_registered) {
> + ret = qcom_battmgr_register_thermal_cooling_device(battmgr);
> + if (ret < 0)
> + dev_err(battmgr->dev, "failed to register thermal cooling device ret:%d\n",
> + ret);
> + } else if (battmgr->last_fcc_ua) {
[Severity: High]
Does checking if (battmgr->last_fcc_ua) prevent restoration of the mitigation
limit after a firmware restart?
If the device was legitimately throttled to 0 microamps due to extreme heat,
this check evaluates to false and skips the restoration, which could cause
the firmware to resume charging at the maximum limit.
> + ret = __battery_psy_set_charge_current(battmgr, battmgr->last_fcc_ua);
> + if (ret < 0)
> + dev_err(battmgr->dev, "Failed to set FCC (%u uA), ret:%d\n",
> + battmgr->last_fcc_ua, ret);
> + }
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260609-cooling_device_reg-v1-0-e15bddcb0086@qti.qualcomm.com?part=2
next prev parent reply other threads:[~2026-06-09 9:30 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-09 9:16 [PATCH 0/2] power: supply: qcom_battmgr: Add thermal mitigation support DhruvinRajpura
2026-06-09 9:16 ` [PATCH 1/2] bindings: power: supply: qcom,pmic-glink: Document thermal-mitigation DhruvinRajpura
2026-06-09 9:26 ` sashiko-bot
2026-06-09 9:16 ` [PATCH 2/2] power: supply: qcom_battmgr: Add thermal mitigation support DhruvinRajpura
2026-06-09 9:30 ` sashiko-bot [this message]
2026-06-09 9:42 ` Konrad Dybcio
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=20260609093031.B30BE1F00893@smtp.kernel.org \
--to=sashiko-bot@kernel.org \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=dhruvin.rajpura@oss.qualcomm.com \
--cc=robh@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