* [PATCH v2] platform/chrome: cros_ec_typec: Stop altmode work during partner removal
@ 2026-10-02 20:01 Myeonghun Pak
0 siblings, 0 replies; only message in thread
From: Myeonghun Pak @ 2026-10-02 20:01 UTC (permalink / raw)
To: Benson Leung, Abhishek Pandit-Subedi, Jameson Thies,
Andrei Kuchynski, Tzung-Bi Shih
Cc: Guenter Roeck, chrome-platform, linux-kernel, mhun512, stable, Ijae Kim
DisplayPort and Thunderbolt port altmodes queue work to deliver VDM
responses. Partner removal and port teardown do not drain this work,
allowing it to race with partner driver removal or access freed port
altmode data.
Disable and drain port altmode work in the common partner cleanup path.
Partner drivers can still queue responses during removal, so keep the
work disabled until they are gone. Clear pending response and DP status
state, then re-enable it for the next connection. Cancel the work before
unregistering port altmodes during final teardown.
Allocate list nodes before registering altmodes so allocation failures
also use the common cleanup path.
This issue was identified during our ongoing static-analysis research
while reviewing kernel code.
Fixes: dbb3fc0ffa95 ("platform/chrome: cros_ec_typec: Displayport support")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Co-developed-by: Ijae Kim <ae878000@gmail.com>
Signed-off-by: Ijae Kim <ae878000@gmail.com>
Signed-off-by: Myeonghun Pak <mhun512@gmail.com>
---
Changes in v2:
- Quiesce port work in cros_typec_unregister_altmodes() before removing
partner altmodes, covering disconnects and discovery cleanup.
- Re-enable work after resetting pending response and DP status state so
port altmodes remain usable after reconnecting a partner.
- Cancel work in cros_typec_unregister_port_altmodes() during final port
teardown, following the review suggestion to use cancel_work_sync().
- Allocate list bookkeeping before registering an altmode so allocation
failures use the common guarded cleanup path.
- Drop the v1 Reviewed-by tag due to the reworked cleanup paths.
Previous version:
https://lore.kernel.org/all/20260917204209.97699-1-mhun512@gmail.com/
Validation: apply checks, whitespace checks and static source review.
No build or runtime testing was performed for this revision.
Remaining review point: an in-flight port active sysfs callback may still
queue work after the final cancel_work_sync(), before the port altmode
is unregistered. Please confirm whether that ordering needs additional
synchronization.
drivers/platform/chrome/cros_ec_typec.c | 27 ++++++++----
drivers/platform/chrome/cros_typec_altmode.c | 46 ++++++++++++++++++++
drivers/platform/chrome/cros_typec_altmode.h | 9 ++++
3 files changed, 73 insertions(+), 9 deletions(-)
diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c
index 50a68819ceb7bbfbd888ca919d678d4c75237c26..2d6cbb7f51445fde4b4e8339081e921a9a143a92 100644
--- a/drivers/platform/chrome/cros_ec_typec.c
+++ b/drivers/platform/chrome/cros_ec_typec.c
@@ -282,11 +282,19 @@ static void cros_typec_unregister_altmodes(struct cros_typec_data *typec, int po
struct list_head *head;
head = is_partner ? &port->partner_mode_list : &port->plug_mode_list;
+ /* Partner drivers can queue port work until their removal completes. */
+ if (is_partner)
+ cros_typec_altmodes_set_enabled(port, false);
+
list_for_each_entry_safe(node, tmp, head, list) {
list_del(&node->list);
typec_unregister_altmode(node->amode);
devm_kfree(typec->dev, node);
}
+
+ /* Port altmodes are reused when a partner reconnects. */
+ if (is_partner)
+ cros_typec_altmodes_set_enabled(port, true);
}
/*
@@ -366,8 +374,10 @@ static void cros_typec_unregister_port_altmodes(struct cros_typec_port *port)
{
int i;
- for (i = 0; i < CROS_EC_ALTMODE_MAX; i++)
+ for (i = 0; i < CROS_EC_ALTMODE_MAX; i++) {
+ cros_typec_altmode_cancel(port->port_altmode[i]);
typec_unregister_altmode(port->port_altmode[i]);
+ }
}
static void cros_unregister_ports(struct cros_typec_data *typec)
@@ -901,24 +911,23 @@ static int cros_typec_register_altmodes(struct cros_typec_data *typec, int port_
desc.mode = j + 1;
desc.vdo = sop_disc->svids[i].mode_vdo[j];
+ node = devm_kzalloc(typec->dev, sizeof(*node), GFP_KERNEL);
+ if (!node) {
+ ret = -ENOMEM;
+ goto err_cleanup;
+ }
+
if (is_partner)
amode = typec_partner_register_altmode(port->partner, &desc);
else
amode = typec_plug_register_altmode(port->plug, &desc);
if (IS_ERR(amode)) {
+ devm_kfree(typec->dev, node);
ret = PTR_ERR(amode);
goto err_cleanup;
}
- /* If no memory is available we should unregister and exit. */
- node = devm_kzalloc(typec->dev, sizeof(*node), GFP_KERNEL);
- if (!node) {
- ret = -ENOMEM;
- typec_unregister_altmode(amode);
- goto err_cleanup;
- }
-
node->amode = amode;
if (is_partner)
diff --git a/drivers/platform/chrome/cros_typec_altmode.c b/drivers/platform/chrome/cros_typec_altmode.c
index 66c546bf89b532d3bae1de322a1cfb1205e0190f..4b255cabac60a406ea359788604a6bf21d87e08d 100644
--- a/drivers/platform/chrome/cros_typec_altmode.c
+++ b/drivers/platform/chrome/cros_typec_altmode.c
@@ -37,6 +37,52 @@ struct cros_typec_dp_data {
bool pending_status_update;
};
+void cros_typec_altmode_cancel(struct typec_altmode *alt)
+{
+ struct cros_typec_altmode_data *adata;
+
+ if (!alt)
+ return;
+
+ adata = typec_altmode_get_drvdata(alt);
+ if (adata)
+ cancel_work_sync(&adata->work);
+}
+
+void cros_typec_altmodes_set_enabled(struct cros_typec_port *port, bool enabled)
+{
+ struct cros_typec_altmode_data *adata;
+ struct cros_typec_dp_data *dp_data;
+ int i;
+
+ for (i = 0; i < CROS_EC_ALTMODE_MAX; i++) {
+ if (!port->port_altmode[i])
+ continue;
+
+ adata = typec_altmode_get_drvdata(port->port_altmode[i]);
+ if (!adata)
+ continue;
+
+ if (!enabled) {
+ disable_work_sync(&adata->work);
+ continue;
+ }
+
+ mutex_lock(&adata->lock);
+ adata->header = 0;
+ adata->vdo_data = NULL;
+ adata->vdo_size = 0;
+ if (adata->sid == USB_TYPEC_DP_SID) {
+ dp_data = container_of(adata, struct cros_typec_dp_data, adata);
+ dp_data->configured = false;
+ dp_data->pending_status_update = false;
+ }
+ mutex_unlock(&adata->lock);
+
+ enable_work(&adata->work);
+ }
+}
+
static void cros_typec_altmode_work(struct work_struct *work)
{
struct cros_typec_altmode_data *data =
diff --git a/drivers/platform/chrome/cros_typec_altmode.h b/drivers/platform/chrome/cros_typec_altmode.h
index 3f2aa95d065af709643ad653df487a9987780da4..bcfd8fed2073dad1e95bfba807e1af7a4dbe91e2 100644
--- a/drivers/platform/chrome/cros_typec_altmode.h
+++ b/drivers/platform/chrome/cros_typec_altmode.h
@@ -11,6 +11,15 @@ struct typec_altmode;
struct typec_altmode_desc;
struct typec_displayport_data;
+#if IS_ENABLED(CONFIG_CROS_EC_TYPEC_ALTMODES)
+void cros_typec_altmode_cancel(struct typec_altmode *alt);
+void cros_typec_altmodes_set_enabled(struct cros_typec_port *port, bool enabled);
+#else
+static inline void cros_typec_altmode_cancel(struct typec_altmode *alt) {}
+static inline void
+cros_typec_altmodes_set_enabled(struct cros_typec_port *port, bool enabled) {}
+#endif
+
#if IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE)
struct typec_altmode *
cros_typec_register_displayport(struct cros_typec_port *port,
--
2.53.0
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2026-10-02 20:01 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-02 20:01 [PATCH v2] platform/chrome: cros_ec_typec: Stop altmode work during partner removal Myeonghun Pak
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®