Skip to content

Commit af785e4

Browse files
Andrei Kuchynskivijay-suman
authored andcommitted
usb: typec: ucsi: displayport: Fix deadlock
commit 364618c89d4c57c85e5fc51a2446cd939bf57802 upstream. This patch introduces the ucsi_con_mutex_lock / ucsi_con_mutex_unlock functions to the UCSI driver. ucsi_con_mutex_lock ensures the connector mutex is only locked if a connection is established and the partner pointer is valid. This resolves a deadlock scenario where ucsi_displayport_remove_partner holds con->mutex waiting for dp_altmode_work to complete while dp_altmode_work attempts to acquire it. Cc: stable <[email protected]> Fixes: af8622f ("usb: typec: ucsi: Support for DisplayPort alt mode") Signed-off-by: Andrei Kuchynski <[email protected]> Reviewed-by: Heikki Krogerus <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]> (cherry picked from commit f4bd982563c2fd41ec9ca6c517c392d759db801c) Signed-off-by: Vijayendra Suman <[email protected]>
1 parent bdf9bec commit af785e4

File tree

3 files changed

+48
-8
lines changed

3 files changed

+48
-8
lines changed

drivers/usb/typec/ucsi/displayport.c

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo)
5454
u8 cur = 0;
5555
int ret;
5656

57-
mutex_lock(&dp->con->lock);
57+
if (!ucsi_con_mutex_lock(dp->con))
58+
return -ENOTCONN;
5859

5960
if (!dp->override && dp->initialized) {
6061
const struct typec_altmode *p = typec_altmode_get_partner(alt);
@@ -100,7 +101,7 @@ static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo)
100101
schedule_work(&dp->work);
101102
ret = 0;
102103
err_unlock:
103-
mutex_unlock(&dp->con->lock);
104+
ucsi_con_mutex_unlock(dp->con);
104105

105106
return ret;
106107
}
@@ -112,7 +113,8 @@ static int ucsi_displayport_exit(struct typec_altmode *alt)
112113
u64 command;
113114
int ret = 0;
114115

115-
mutex_lock(&dp->con->lock);
116+
if (!ucsi_con_mutex_lock(dp->con))
117+
return -ENOTCONN;
116118

117119
if (!dp->override) {
118120
const struct typec_altmode *p = typec_altmode_get_partner(alt);
@@ -144,7 +146,7 @@ static int ucsi_displayport_exit(struct typec_altmode *alt)
144146
schedule_work(&dp->work);
145147

146148
out_unlock:
147-
mutex_unlock(&dp->con->lock);
149+
ucsi_con_mutex_unlock(dp->con);
148150

149151
return ret;
150152
}
@@ -202,20 +204,21 @@ static int ucsi_displayport_vdm(struct typec_altmode *alt,
202204
int cmd = PD_VDO_CMD(header);
203205
int svdm_version;
204206

205-
mutex_lock(&dp->con->lock);
207+
if (!ucsi_con_mutex_lock(dp->con))
208+
return -ENOTCONN;
206209

207210
if (!dp->override && dp->initialized) {
208211
const struct typec_altmode *p = typec_altmode_get_partner(alt);
209212

210213
dev_warn(&p->dev,
211214
"firmware doesn't support alternate mode overriding\n");
212-
mutex_unlock(&dp->con->lock);
215+
ucsi_con_mutex_unlock(dp->con);
213216
return -EOPNOTSUPP;
214217
}
215218

216219
svdm_version = typec_altmode_get_svdm_version(alt);
217220
if (svdm_version < 0) {
218-
mutex_unlock(&dp->con->lock);
221+
ucsi_con_mutex_unlock(dp->con);
219222
return svdm_version;
220223
}
221224

@@ -259,7 +262,7 @@ static int ucsi_displayport_vdm(struct typec_altmode *alt,
259262
break;
260263
}
261264

262-
mutex_unlock(&dp->con->lock);
265+
ucsi_con_mutex_unlock(dp->con);
263266

264267
return 0;
265268
}

drivers/usb/typec/ucsi/ucsi.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,6 +1351,40 @@ void ucsi_set_drvdata(struct ucsi *ucsi, void *data)
13511351
}
13521352
EXPORT_SYMBOL_GPL(ucsi_set_drvdata);
13531353

1354+
/**
1355+
* ucsi_con_mutex_lock - Acquire the connector mutex
1356+
* @con: The connector interface to lock
1357+
*
1358+
* Returns true on success, false if the connector is disconnected
1359+
*/
1360+
bool ucsi_con_mutex_lock(struct ucsi_connector *con)
1361+
{
1362+
bool mutex_locked = false;
1363+
bool connected = true;
1364+
1365+
while (connected && !mutex_locked) {
1366+
mutex_locked = mutex_trylock(&con->lock) != 0;
1367+
connected = con->status.flags & UCSI_CONSTAT_CONNECTED;
1368+
if (connected && !mutex_locked)
1369+
msleep(20);
1370+
}
1371+
1372+
connected = connected && con->partner;
1373+
if (!connected && mutex_locked)
1374+
mutex_unlock(&con->lock);
1375+
1376+
return connected;
1377+
}
1378+
1379+
/**
1380+
* ucsi_con_mutex_unlock - Release the connector mutex
1381+
* @con: The connector interface to unlock
1382+
*/
1383+
void ucsi_con_mutex_unlock(struct ucsi_connector *con)
1384+
{
1385+
mutex_unlock(&con->lock);
1386+
}
1387+
13541388
/**
13551389
* ucsi_create - Allocate UCSI instance
13561390
* @dev: Device interface to the PPM (Platform Policy Manager)

drivers/usb/typec/ucsi/ucsi.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
struct ucsi;
1717
struct ucsi_altmode;
18+
struct ucsi_connector;
1819

1920
/* UCSI offsets (Bytes) */
2021
#define UCSI_VERSION 0
@@ -62,6 +63,8 @@ int ucsi_register(struct ucsi *ucsi);
6263
void ucsi_unregister(struct ucsi *ucsi);
6364
void *ucsi_get_drvdata(struct ucsi *ucsi);
6465
void ucsi_set_drvdata(struct ucsi *ucsi, void *data);
66+
bool ucsi_con_mutex_lock(struct ucsi_connector *con);
67+
void ucsi_con_mutex_unlock(struct ucsi_connector *con);
6568

6669
void ucsi_connector_change(struct ucsi *ucsi, u8 num);
6770

0 commit comments

Comments
 (0)