Skip to content

Commit 5924b32

Browse files
Andrei Kuchynskigregkh
authored andcommitted
usb: typec: ucsi: displayport: Fix deadlock
commit 364618c 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 bca94cc commit 5924b32

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
@@ -1903,6 +1903,40 @@ void ucsi_set_drvdata(struct ucsi *ucsi, void *data)
19031903
}
19041904
EXPORT_SYMBOL_GPL(ucsi_set_drvdata);
19051905

1906+
/**
1907+
* ucsi_con_mutex_lock - Acquire the connector mutex
1908+
* @con: The connector interface to lock
1909+
*
1910+
* Returns true on success, false if the connector is disconnected
1911+
*/
1912+
bool ucsi_con_mutex_lock(struct ucsi_connector *con)
1913+
{
1914+
bool mutex_locked = false;
1915+
bool connected = true;
1916+
1917+
while (connected && !mutex_locked) {
1918+
mutex_locked = mutex_trylock(&con->lock) != 0;
1919+
connected = con->status.flags & UCSI_CONSTAT_CONNECTED;
1920+
if (connected && !mutex_locked)
1921+
msleep(20);
1922+
}
1923+
1924+
connected = connected && con->partner;
1925+
if (!connected && mutex_locked)
1926+
mutex_unlock(&con->lock);
1927+
1928+
return connected;
1929+
}
1930+
1931+
/**
1932+
* ucsi_con_mutex_unlock - Release the connector mutex
1933+
* @con: The connector interface to unlock
1934+
*/
1935+
void ucsi_con_mutex_unlock(struct ucsi_connector *con)
1936+
{
1937+
mutex_unlock(&con->lock);
1938+
}
1939+
19061940
/**
19071941
* ucsi_create - Allocate UCSI instance
19081942
* @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
@@ -91,6 +91,8 @@ int ucsi_register(struct ucsi *ucsi);
9191
void ucsi_unregister(struct ucsi *ucsi);
9292
void *ucsi_get_drvdata(struct ucsi *ucsi);
9393
void ucsi_set_drvdata(struct ucsi *ucsi, void *data);
94+
bool ucsi_con_mutex_lock(struct ucsi_connector *con);
95+
void ucsi_con_mutex_unlock(struct ucsi_connector *con);
9496

9597
void ucsi_connector_change(struct ucsi *ucsi, u8 num);
9698

0 commit comments

Comments
 (0)