Skip to content

Commit fac4b86

Browse files
jackp780gregkh
authored andcommitted
usb: ucsi: Ensure connector delayed work items are flushed
During ucsi_unregister() when destroying a connector's workqueue, there may still be pending delayed work items that haven't been scheduled yet. Because queue_delayed_work() uses a separate timer to schedule a work item, the destroy_workqueue() call is not aware of any pending items. Hence when a pending item's timer expires it would then try to queue on a dangling workqueue pointer. Fix this by keeping track of all work items in a list, so that prior to destroying the workqueue any pending items can be flushed. Do this by calling mod_delayed_work() as that will cause pending items to get queued immediately, which then allows the ensuing destroy_workqueue() to implicitly drain all currently queued items to completion and free themselves. Fixes: b9aa02c ("usb: typec: ucsi: Add polling mechanism for partner tasks like alt mode checking") Suggested-by: Heikki Krogerus <[email protected]> Co-developed-by: Linyu Yuan <[email protected]> Signed-off-by: Linyu Yuan <[email protected]> Signed-off-by: Jack Pham <[email protected]> Reviewed-by: Heikki Krogerus <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent bd449ad commit fac4b86

File tree

2 files changed

+22
-3
lines changed

2 files changed

+22
-3
lines changed

drivers/usb/typec/ucsi/ucsi.c

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ EXPORT_SYMBOL_GPL(ucsi_send_command);
187187

188188
struct ucsi_work {
189189
struct delayed_work work;
190+
struct list_head node;
190191
unsigned long delay;
191192
unsigned int count;
192193
struct ucsi_connector *con;
@@ -202,17 +203,20 @@ static void ucsi_poll_worker(struct work_struct *work)
202203
mutex_lock(&con->lock);
203204

204205
if (!con->partner) {
206+
list_del(&uwork->node);
205207
mutex_unlock(&con->lock);
206208
kfree(uwork);
207209
return;
208210
}
209211

210212
ret = uwork->cb(con);
211213

212-
if (uwork->count-- && (ret == -EBUSY || ret == -ETIMEDOUT))
214+
if (uwork->count-- && (ret == -EBUSY || ret == -ETIMEDOUT)) {
213215
queue_delayed_work(con->wq, &uwork->work, uwork->delay);
214-
else
216+
} else {
217+
list_del(&uwork->node);
215218
kfree(uwork);
219+
}
216220

217221
mutex_unlock(&con->lock);
218222
}
@@ -236,6 +240,7 @@ static int ucsi_partner_task(struct ucsi_connector *con,
236240
uwork->con = con;
237241
uwork->cb = cb;
238242

243+
list_add_tail(&uwork->node, &con->partner_tasks);
239244
queue_delayed_work(con->wq, &uwork->work, delay);
240245

241246
return 0;
@@ -1056,6 +1061,7 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
10561061
INIT_WORK(&con->work, ucsi_handle_connector_change);
10571062
init_completion(&con->complete);
10581063
mutex_init(&con->lock);
1064+
INIT_LIST_HEAD(&con->partner_tasks);
10591065
con->num = index + 1;
10601066
con->ucsi = ucsi;
10611067

@@ -1420,8 +1426,20 @@ void ucsi_unregister(struct ucsi *ucsi)
14201426
ucsi_unregister_altmodes(&ucsi->connector[i],
14211427
UCSI_RECIPIENT_CON);
14221428
ucsi_unregister_port_psy(&ucsi->connector[i]);
1423-
if (ucsi->connector[i].wq)
1429+
1430+
if (ucsi->connector[i].wq) {
1431+
struct ucsi_work *uwork;
1432+
1433+
mutex_lock(&ucsi->connector[i].lock);
1434+
/*
1435+
* queue delayed items immediately so they can execute
1436+
* and free themselves before the wq is destroyed
1437+
*/
1438+
list_for_each_entry(uwork, &ucsi->connector[i].partner_tasks, node)
1439+
mod_delayed_work(ucsi->connector[i].wq, &uwork->work, 0);
1440+
mutex_unlock(&ucsi->connector[i].lock);
14241441
destroy_workqueue(ucsi->connector[i].wq);
1442+
}
14251443
typec_unregister_port(ucsi->connector[i].port);
14261444
}
14271445

drivers/usb/typec/ucsi/ucsi.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ struct ucsi_connector {
322322
struct work_struct work;
323323
struct completion complete;
324324
struct workqueue_struct *wq;
325+
struct list_head partner_tasks;
325326

326327
struct typec_port *port;
327328
struct typec_partner *partner;

0 commit comments

Comments
 (0)