From: sashiko-bot@kernel.org
To: "Fenglin Wu" <fenglin.wu@oss.qualcomm.com>
Cc: linux-rtc@vger.kernel.org,
"Alexandre Belloni" <alexandre.belloni@bootlin.com>
Subject: Re: [PATCH 2/3] soc: qcom: pmic_glink: Add GIO pmic_glink instance
Date: Wed, 11 Nov 2026 04:07:46 +0000 [thread overview]
Message-ID: <sashiko-outbox-208262@kernel.org> (raw)
In-Reply-To: <20261110-gio-framework-rtc-driver-2026-10-12-v1-2-e4164178183a@oss.qualcomm.com>
Thank you for your contribution! Sashiko AI review found 4 potential
issues to consider:
- [Medium] soc: qcom: pmic_glink: missing state checks in GIO read/write
paths
- [Medium] soc: qcom: pmic_glink: uncleared pending state in
pmic_glink_gio_callback()
- [Medium] soc: qcom: pmic_glink_gio: use-after-free of pgio->client on
unbind
- [Low] soc: qcom: pmic_glink: unused variable ret in
pmic_glink_gio_init_work()
--
commit 7a554fdd48ae7b7ceeeac57f7e2ff18534912ac9
Author: Fenglin Wu <fenglin.wu@oss.qualcomm.com>
soc: qcom: pmic_glink: Add GIO pmic_glink instance
This commit adds the PMIC GLINK transport driver for the Qualcomm GIO
(General I/O) interface and enables the GIO auxiliary device on Glymur.
> diff --git a/drivers/soc/qcom/pmic_glink_gio.c b/drivers/soc/qcom/pmic_glink_gio.c
> new file mode 100644
> index 0000000000000..f222c0d59220f
> --- /dev/null
> +++ b/drivers/soc/qcom/pmic_glink_gio.c
[ ... ]
> +static int pmic_glink_gio_write_sync(struct pmic_glink_gio *pgio, u32 addr,
> + const u8 *data, size_t length)
> +{
> + struct gio_write_req *req = &pgio->write_req;
> + int ret;
> +
> + if (!data || !length)
> + return -EFAULT;
> +
> + if (length > GIO_RW_BUFFER_SIZE) {
> + dev_err(pgio->dev, "Write length %zu exceeds maximum buffer size %d\n",
> + length, GIO_RW_BUFFER_SIZE);
> + return -EINVAL;
> + }
> +
> + reinit_completion(&pgio->ack);
> +
> + req->hdr.owner = cpu_to_le32(PMIC_GLINK_OWNER_GIO);
> + req->hdr.type = cpu_to_le32(PMIC_GLINK_REQ_RESP);
> + req->hdr.opcode = cpu_to_le32(GIO_OPCODE_BLOCK_WRITE);
> + req->offset = cpu_to_le32(addr);
> + req->length = cpu_to_le32(length);
> + memcpy(req->data, data, length);
> +
> + scoped_guard(spinlock_irqsave, &pgio->lock) {
> + pgio->pending = true;
> + pgio->pending_opcode = GIO_OPCODE_BLOCK_WRITE;
> + }
[Severity: Medium]
Can pmic_glink_gio_write_sync() and pmic_glink_gio_read() arm a
transaction after the service has already gone down or removal has
started?
Callers check READ_ONCE(gio->online) or READ_ONCE(pgio->service_up)
without holding pgio->lock:
CPU0 CPU1
pmic_glink_gio_init_work()
if (!READ_ONCE(pgio->service_up))
pmic_glink_gio_pdr_notify(..., DOWN)
guard(spinlock_irqsave)(&pgio->lock)
WRITE_ONCE(pgio->service_up, false)
/* pgio->pending is false */
pmic_gio_set_state(..., DOWN)
pmic_glink_gio_enable_alerts()
...
pmic_glink_gio_write_sync()
reinit_completion(&pgio->ack)
scoped_guard(..., &pgio->lock)
pgio->pending = true
pmic_glink_send(...)
pmic_glink_gio_wait_ack() <-- waits full 5s timeout
Because neither function checks pgio->removing or pgio->service_up
(or calls reinit_completion(&pgio->ack)) under pgio->lock, a concurrent
PDR down notification or pmic_glink_gio_remove() misses pgio->pending
and leaves the caller blocked for PMIC_GLINK_SEND_TIMEOUT.
[ ... ]
> +static void pmic_glink_gio_init_work(struct work_struct *work)
> +{
> + struct pmic_glink_gio *pgio = container_of(work, struct pmic_glink_gio, init_work);
> + int ret;
> +
> + if (!READ_ONCE(pgio->service_up))
> + return;
> +
> + pmic_glink_gio_enable_alerts(pgio);
> +}
[Severity: Low]
Does the unused local variable ret here trigger a -Wunused-variable
warning (and break the build under CONFIG_WERROR=y) at this commit?
ret is only used later in the series in commit "rtc: Add Qualcomm GIO
RTC driver", so its declaration should be moved there to keep bisection
clean.
[ ... ]
> + switch (opcode) {
> + case GIO_OPCODE_BLOCK_READ:
> + if (len != sizeof(struct gio_read_resp)) {
> + dev_warn(pgio->dev, "Invalid read response message, length: %zu\n", len);
> + return;
> + }
> +
> + rx_resp = data;
> +
> + scoped_guard(spinlock_irqsave, &pgio->lock) {
> + if (!pgio->pending || pgio->pending_opcode != GIO_OPCODE_BLOCK_READ) {
> + dev_warn(pgio->dev, "Unexpected GIO read response, dropping\n");
> + return;
> + }
> +
> + if (le32_to_cpu(rx_resp->ret_code) != 0) {
> + pgio->ack_error = -EREMOTEIO;
> + dev_warn(pgio->dev, "GIO read response failed (%u)\n",
> + le32_to_cpu(rx_resp->ret_code));
> + } else if (pgio->buf) {
> + copy_size = min_t(size_t, pgio->buf_size, GIO_RW_BUFFER_SIZE);
> + memcpy(pgio->buf, rx_resp->data, copy_size);
> + }
> + complete(&pgio->ack);
> + }
> + break;
> +
> + case GIO_OPCODE_BLOCK_WRITE:
> + if (len != sizeof(struct gio_write_resp)) {
> + dev_warn(pgio->dev, "Invalid write response message, length: %zu\n", len);
> + return;
> + }
> +
> + tx_resp = data;
> +
> + scoped_guard(spinlock_irqsave, &pgio->lock) {
> + if (!pgio->pending || pgio->pending_opcode != GIO_OPCODE_BLOCK_WRITE) {
> + dev_warn(pgio->dev, "Unexpected GIO write response, dropping\n");
> + return;
> + }
> +
> + if (le32_to_cpu(tx_resp->ret_code) != 0) {
> + pgio->ack_error = -EREMOTEIO;
> + dev_warn(pgio->dev, "GIO write response failed (%u)\n",
> + le32_to_cpu(tx_resp->ret_code));
> + }
> + complete(&pgio->ack);
> + }
> + break;
[Severity: Medium]
Should pmic_glink_gio_callback() clear pgio->pending and pgio->buf under
pgio->lock before calling complete(&pgio->ack)?
Leaving pgio->pending set until the waiter wakes up in
pmic_glink_gio_wait_ack() creates a window where another event can
corrupt the completed transaction's state:
CPU0 CPU1
pmic_glink_gio_wait_ack()
wait_for_completion_timeout()
pmic_glink_gio_callback()
lock(&pgio->lock)
memcpy(pgio->buf, ...)
complete(&pgio->ack)
unlock(&pgio->lock)
pmic_glink_gio_pdr_notify(..., DOWN)
lock(&pgio->lock)
if (!service_up && pgio->pending)
pgio->ack_error = -ENETRESET
unlock(&pgio->lock)
lock(&pgio->lock)
ret = pgio->ack_error <-- returns -ENETRESET instead of 0
A duplicate or late firmware response arriving in that window will
similarly see pgio->pending == true and overwrite pgio->buf or
pgio->ack_error.
[ ... ]
> + pgio->gio = devm_pmic_gio_register(dev, &pmic_glink_gio_ops, pgio);
> + if (IS_ERR(pgio->gio))
> + return dev_err_probe(dev, PTR_ERR(pgio->gio), "failed to register GIO device\n");
> +
> + pgio->wq = alloc_ordered_workqueue("pmic_glink_gio", 0);
> + if (!pgio->wq)
> + return -ENOMEM;
> +
> + ret = devm_add_action_or_reset(dev, pmic_glink_gio_destroy_wq, pgio->wq);
> + if (ret)
> + return ret;
> +
> + pgio->client = devm_pmic_glink_client_alloc(dev, PMIC_GLINK_OWNER_GIO,
> + pmic_glink_gio_callback,
> + pmic_glink_gio_pdr_notify,
> + pgio);
> + if (IS_ERR(pgio->client))
> + return PTR_ERR(pgio->client);
> +
> + pmic_glink_client_register(pgio->client);
> +
> + return 0;
> +}
> +
> +static void pmic_glink_gio_remove(struct auxiliary_device *adev)
> +{
> + struct pmic_glink_gio *pgio = dev_get_drvdata(&adev->dev);
> +
> + scoped_guard(spinlock_irqsave, &pgio->lock) {
> + pgio->removing = true;
> + WRITE_ONCE(pgio->service_up, false);
> + if (pgio->pending) {
> + pgio->pending = false;
> + pgio->buf = NULL;
> + pgio->buf_size = 0;
> + pgio->ack_error = -ENODEV;
> + complete(&pgio->ack);
> + }
> + pmic_gio_set_state(pgio->gio, PMIC_GIO_STATE_DOWN);
> + }
> +
> + cancel_work_sync(&pgio->init_work);
> + flush_workqueue(pgio->wq);
> +}
[Severity: Medium]
Can an in-flight regmap access (such as a regmap debugfs read) use
pgio->client after it is freed during unbind?
devm_pmic_gio_register() (which calls devm_regmap_init()) is registered
before devm_pmic_glink_client_alloc(), so devres frees pgio->client
before regmap_exit() removes the debugfs entries and drains active
readers:
CPU0 CPU1
regmap_debugfs_read_file()
...
pmic_gio_regmap_bus_read()
if (!READ_ONCE(gio->online))
pmic_glink_gio_remove()
pgio->removing = true
pmic_gio_set_state(..., DOWN)
devres_release_all()
_devm_pmic_glink_release_client()
kfree(pgio->client)
pmic_glink_gio_read()
pmic_glink_send(pgio->client, ...)
client->pg <-- use-after-free
Allocating pgio->client before devm_pmic_gio_register() ensures
devm_regmap_release() finishes draining regmap users before pgio->client
is released.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261110-gio-framework-rtc-driver-2026-10-12-v1-0-e4164178183a@oss.qualcomm.com?part=2
next prev parent reply other threads:[~2026-11-11 4:07 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-11 3:28 [PATCH 0/3] soc: qcom: Add Qualcomm PMIC GIO framework and RTC driver using it Fenglin Wu
2026-11-11 3:28 ` [PATCH 1/3] soc: qcom: Add pmic-gio GIO framework driver Fenglin Wu
2026-11-11 3:47 ` sashiko-bot
2026-11-11 3:28 ` [PATCH 2/3] soc: qcom: pmic_glink: Add GIO pmic_glink instance Fenglin Wu
2026-11-11 4:07 ` sashiko-bot [this message]
2026-11-11 13:14 ` Jeff Roach
2026-11-11 13:41 ` Jeff Roach
2026-11-12 1:11 ` Fenglin Wu
2026-11-12 7:10 ` Fenglin Wu
2026-11-11 3:28 ` [PATCH 3/3] rtc: Add Qualcomm GIO RTC driver Fenglin Wu
2026-11-11 4:20 ` sashiko-bot
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-208262@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=alexandre.belloni@bootlin.com \
--cc=fenglin.wu@oss.qualcomm.com \
--cc=linux-rtc@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