Skip to content

Commit 364618c

Browse files
Andrei Kuchynskigregkh
authored andcommitted
usb: typec: ucsi: displayport: Fix deadlock
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 <stable@kernel.org> Fixes: af8622f ("usb: typec: ucsi: Support for DisplayPort alt mode") Signed-off-by: Andrei Kuchynski <akuchynski@chromium.org> Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com> Link: https://lore.kernel.org/r/20250424084429.3220757-2-akuchynski@chromium.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 9f657a9 commit 364618c

File tree

3 files changed

+47
-8
lines changed

3 files changed

+47
-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
@@ -1922,6 +1922,40 @@ void ucsi_set_drvdata(struct ucsi *ucsi, void *data)
19221922
}
19231923
EXPORT_SYMBOL_GPL(ucsi_set_drvdata);
19241924

1925+
/**
1926+
* ucsi_con_mutex_lock - Acquire the connector mutex
1927+
* @con: The connector interface to lock
1928+
*
1929+
* Returns true on success, false if the connector is disconnected
1930+
*/
1931+
bool ucsi_con_mutex_lock(struct ucsi_connector *con)
1932+
{
1933+
bool mutex_locked = false;
1934+
bool connected = true;
1935+
1936+
while (connected && !mutex_locked) {
1937+
mutex_locked = mutex_trylock(&con->lock) != 0;
1938+
connected = UCSI_CONSTAT(con, CONNECTED);
1939+
if (connected && !mutex_locked)
1940+
msleep(20);
1941+
}
1942+
1943+
connected = connected && con->partner;
1944+
if (!connected && mutex_locked)
1945+
mutex_unlock(&con->lock);
1946+
1947+
return connected;
1948+
}
1949+
1950+
/**
1951+
* ucsi_con_mutex_unlock - Release the connector mutex
1952+
* @con: The connector interface to unlock
1953+
*/
1954+
void ucsi_con_mutex_unlock(struct ucsi_connector *con)
1955+
{
1956+
mutex_unlock(&con->lock);
1957+
}
1958+
19251959
/**
19261960
* ucsi_create - Allocate UCSI instance
19271961
* @dev: Device interface to the PPM (Platform Policy Manager)

drivers/usb/typec/ucsi/ucsi.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ int ucsi_register(struct ucsi *ucsi);
9494
void ucsi_unregister(struct ucsi *ucsi);
9595
void *ucsi_get_drvdata(struct ucsi *ucsi);
9696
void ucsi_set_drvdata(struct ucsi *ucsi, void *data);
97+
bool ucsi_con_mutex_lock(struct ucsi_connector *con);
98+
void ucsi_con_mutex_unlock(struct ucsi_connector *con);
9799

98100
void ucsi_connector_change(struct ucsi *ucsi, u8 num);
99101

0 commit comments

Comments
 (0)