usb: typec: ucsi: Hold con->lock for the entire duration of ucsi_register_port()
Commit081da1325d
("usb: typec: ucsi: displayport: Fix a potential race during registration") made the ucsi code hold con->lock in ucsi_register_displayport(). But we really don't want any interactions with the connector to run before the port-registration process is fully complete. This commit moves the taking of con->lock from ucsi_register_displayport() into ucsi_register_port() to achieve this. Cc: stable@vger.kernel.org Fixes:081da1325d
("usb: typec: ucsi: displayport: Fix a potential race during registration") Signed-off-by: Hans de Goede <hdegoede@redhat.com> Acked-by: Heikki Krogerus <heikki.krogerus@linux.intel.com> Reviewed-by: Guenter Roeck <linux@roeck-us.net> Link: https://lore.kernel.org/r/20200809141904.4317-5-hdegoede@redhat.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Родитель
25794e3079
Коммит
bed97b3096
|
@ -288,8 +288,6 @@ struct typec_altmode *ucsi_register_displayport(struct ucsi_connector *con,
|
||||||
struct typec_altmode *alt;
|
struct typec_altmode *alt;
|
||||||
struct ucsi_dp *dp;
|
struct ucsi_dp *dp;
|
||||||
|
|
||||||
mutex_lock(&con->lock);
|
|
||||||
|
|
||||||
/* We can't rely on the firmware with the capabilities. */
|
/* We can't rely on the firmware with the capabilities. */
|
||||||
desc->vdo |= DP_CAP_DP_SIGNALING | DP_CAP_RECEPTACLE;
|
desc->vdo |= DP_CAP_DP_SIGNALING | DP_CAP_RECEPTACLE;
|
||||||
|
|
||||||
|
@ -298,15 +296,12 @@ struct typec_altmode *ucsi_register_displayport(struct ucsi_connector *con,
|
||||||
desc->vdo |= all_assignments << 16;
|
desc->vdo |= all_assignments << 16;
|
||||||
|
|
||||||
alt = typec_port_register_altmode(con->port, desc);
|
alt = typec_port_register_altmode(con->port, desc);
|
||||||
if (IS_ERR(alt)) {
|
if (IS_ERR(alt))
|
||||||
mutex_unlock(&con->lock);
|
|
||||||
return alt;
|
return alt;
|
||||||
}
|
|
||||||
|
|
||||||
dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL);
|
dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL);
|
||||||
if (!dp) {
|
if (!dp) {
|
||||||
typec_unregister_altmode(alt);
|
typec_unregister_altmode(alt);
|
||||||
mutex_unlock(&con->lock);
|
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,7 +314,5 @@ struct typec_altmode *ucsi_register_displayport(struct ucsi_connector *con,
|
||||||
alt->ops = &ucsi_displayport_ops;
|
alt->ops = &ucsi_displayport_ops;
|
||||||
typec_altmode_set_drvdata(alt, dp);
|
typec_altmode_set_drvdata(alt, dp);
|
||||||
|
|
||||||
mutex_unlock(&con->lock);
|
|
||||||
|
|
||||||
return alt;
|
return alt;
|
||||||
}
|
}
|
||||||
|
|
|
@ -898,12 +898,15 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
|
||||||
con->num = index + 1;
|
con->num = index + 1;
|
||||||
con->ucsi = ucsi;
|
con->ucsi = ucsi;
|
||||||
|
|
||||||
|
/* Delay other interactions with the con until registration is complete */
|
||||||
|
mutex_lock(&con->lock);
|
||||||
|
|
||||||
/* Get connector capability */
|
/* Get connector capability */
|
||||||
command = UCSI_GET_CONNECTOR_CAPABILITY;
|
command = UCSI_GET_CONNECTOR_CAPABILITY;
|
||||||
command |= UCSI_CONNECTOR_NUMBER(con->num);
|
command |= UCSI_CONNECTOR_NUMBER(con->num);
|
||||||
ret = ucsi_send_command(ucsi, command, &con->cap, sizeof(con->cap));
|
ret = ucsi_send_command(ucsi, command, &con->cap, sizeof(con->cap));
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
goto out;
|
||||||
|
|
||||||
if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DRP)
|
if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DRP)
|
||||||
cap->data = TYPEC_PORT_DRD;
|
cap->data = TYPEC_PORT_DRD;
|
||||||
|
@ -935,26 +938,32 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
|
||||||
|
|
||||||
ret = ucsi_register_port_psy(con);
|
ret = ucsi_register_port_psy(con);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
goto out;
|
||||||
|
|
||||||
/* Register the connector */
|
/* Register the connector */
|
||||||
con->port = typec_register_port(ucsi->dev, cap);
|
con->port = typec_register_port(ucsi->dev, cap);
|
||||||
if (IS_ERR(con->port))
|
if (IS_ERR(con->port)) {
|
||||||
return PTR_ERR(con->port);
|
ret = PTR_ERR(con->port);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/* Alternate modes */
|
/* Alternate modes */
|
||||||
ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_CON);
|
ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_CON);
|
||||||
if (ret)
|
if (ret) {
|
||||||
dev_err(ucsi->dev, "con%d: failed to register alt modes\n",
|
dev_err(ucsi->dev, "con%d: failed to register alt modes\n",
|
||||||
con->num);
|
con->num);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/* Get the status */
|
/* Get the status */
|
||||||
command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num);
|
command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num);
|
||||||
ret = ucsi_send_command(ucsi, command, &con->status, sizeof(con->status));
|
ret = ucsi_send_command(ucsi, command, &con->status, sizeof(con->status));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(ucsi->dev, "con%d: failed to get status\n", con->num);
|
dev_err(ucsi->dev, "con%d: failed to get status\n", con->num);
|
||||||
return 0;
|
ret = 0;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
ret = 0; /* ucsi_send_command() returns length on success */
|
||||||
|
|
||||||
switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) {
|
switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) {
|
||||||
case UCSI_CONSTAT_PARTNER_TYPE_UFP:
|
case UCSI_CONSTAT_PARTNER_TYPE_UFP:
|
||||||
|
@ -979,17 +988,21 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
|
||||||
|
|
||||||
if (con->partner) {
|
if (con->partner) {
|
||||||
ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP);
|
ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP);
|
||||||
if (ret)
|
if (ret) {
|
||||||
dev_err(ucsi->dev,
|
dev_err(ucsi->dev,
|
||||||
"con%d: failed to register alternate modes\n",
|
"con%d: failed to register alternate modes\n",
|
||||||
con->num);
|
con->num);
|
||||||
else
|
ret = 0;
|
||||||
|
} else {
|
||||||
ucsi_altmode_update_active(con);
|
ucsi_altmode_update_active(con);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trace_ucsi_register_port(con->num, &con->status);
|
trace_ucsi_register_port(con->num, &con->status);
|
||||||
|
|
||||||
return 0;
|
out:
|
||||||
|
mutex_unlock(&con->lock);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Загрузка…
Ссылка в новой задаче