Skip to content

Commit f32451c

Browse files
Andrei Kuchynskigregkh
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]>
1 parent d8ef614 commit f32451c

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
@@ -1398,6 +1398,40 @@ void ucsi_set_drvdata(struct ucsi *ucsi, void *data)
13981398
}
13991399
EXPORT_SYMBOL_GPL(ucsi_set_drvdata);
14001400

1401+
/**
1402+
* ucsi_con_mutex_lock - Acquire the connector mutex
1403+
* @con: The connector interface to lock
1404+
*
1405+
* Returns true on success, false if the connector is disconnected
1406+
*/
1407+
bool ucsi_con_mutex_lock(struct ucsi_connector *con)
1408+
{
1409+
bool mutex_locked = false;
1410+
bool connected = true;
1411+
1412+
while (connected && !mutex_locked) {
1413+
mutex_locked = mutex_trylock(&con->lock) != 0;
1414+
connected = con->status.flags & UCSI_CONSTAT_CONNECTED;
1415+
if (connected && !mutex_locked)
1416+
msleep(20);
1417+
}
1418+
1419+
connected = connected && con->partner;
1420+
if (!connected && mutex_locked)
1421+
mutex_unlock(&con->lock);
1422+
1423+
return connected;
1424+
}
1425+
1426+
/**
1427+
* ucsi_con_mutex_unlock - Release the connector mutex
1428+
* @con: The connector interface to unlock
1429+
*/
1430+
void ucsi_con_mutex_unlock(struct ucsi_connector *con)
1431+
{
1432+
mutex_unlock(&con->lock);
1433+
}
1434+
14011435
/**
14021436
* ucsi_create - Allocate UCSI instance
14031437
* @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)