Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

usb: typec: ucsi: Fix AB BA lock inversion

Lockdep reports an AB BA lock inversion between ucsi_init() and
ucsi_handle_connector_change():

AB order:

1. ucsi_init takes ucsi->ppm_lock (it runs with that locked for the
duration of the function)
2. usci_init eventually end up calling ucsi_register_displayport,
which takes ucsi_connector->lock

BA order:

1. ucsi_handle_connector_change work is started, takes ucsi_connector->lock
2. ucsi_handle_connector_change calls ucsi_send_command which takes
ucsi->ppm_lock

The ppm_lock really only needs to be hold during 2 functions:
ucsi_reset_ppm() and ucsi_run_command().

This commit fixes the AB BA lock inversion by making ucsi_init drop the
ucsi->ppm_lock before it starts registering ports; and replacing any
ucsi_run_command() calls after this point with ucsi_send_command()
(which is a wrapper around run_command taking the lock while handling
the command).

Some of the replacing of ucsi_run_command with ucsi_send_command
in the helpers used during port registration also fixes a number of
code paths after registration which call ucsi_run_command() without
holding the ppm_lock:
1. ucsi_altmode_update_active() call in ucsi/displayport.c
2. ucsi_register_altmodes() call from ucsi_handle_connector_change()
(through ucsi_partner_change())

Cc: stable@vger.kernel.org
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-2-hdegoede@redhat.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Hans de Goede and committed by
Greg Kroah-Hartman
0ff0705a 7a2f2974

+9 -9
+9 -9
drivers/usb/typec/ucsi/ucsi.c
··· 205 205 int i; 206 206 207 207 command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(con->num); 208 - ret = ucsi_run_command(con->ucsi, command, &cur, sizeof(cur)); 208 + ret = ucsi_send_command(con->ucsi, command, &cur, sizeof(cur)); 209 209 if (ret < 0) { 210 210 if (con->ucsi->version > 0x0100) { 211 211 dev_err(con->ucsi->dev, ··· 354 354 command |= UCSI_GET_ALTMODE_RECIPIENT(recipient); 355 355 command |= UCSI_GET_ALTMODE_CONNECTOR_NUMBER(con->num); 356 356 command |= UCSI_GET_ALTMODE_OFFSET(i); 357 - len = ucsi_run_command(con->ucsi, command, &alt, sizeof(alt)); 357 + len = ucsi_send_command(con->ucsi, command, &alt, sizeof(alt)); 358 358 /* 359 359 * We are collecting all altmodes first and then registering. 360 360 * Some type-C device will return zero length data beyond last ··· 431 431 command |= UCSI_GET_ALTMODE_RECIPIENT(recipient); 432 432 command |= UCSI_GET_ALTMODE_CONNECTOR_NUMBER(con->num); 433 433 command |= UCSI_GET_ALTMODE_OFFSET(i); 434 - len = ucsi_run_command(con->ucsi, command, alt, sizeof(alt)); 434 + len = ucsi_send_command(con->ucsi, command, alt, sizeof(alt)); 435 435 if (len <= 0) 436 436 return len; 437 437 ··· 904 904 /* Get connector capability */ 905 905 command = UCSI_GET_CONNECTOR_CAPABILITY; 906 906 command |= UCSI_CONNECTOR_NUMBER(con->num); 907 - ret = ucsi_run_command(ucsi, command, &con->cap, sizeof(con->cap)); 907 + ret = ucsi_send_command(ucsi, command, &con->cap, sizeof(con->cap)); 908 908 if (ret < 0) 909 909 return ret; 910 910 ··· 953 953 954 954 /* Get the status */ 955 955 command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); 956 - ret = ucsi_run_command(ucsi, command, &con->status, 957 - sizeof(con->status)); 956 + ret = ucsi_send_command(ucsi, command, &con->status, sizeof(con->status)); 958 957 if (ret < 0) { 959 958 dev_err(ucsi->dev, "con%d: failed to get status\n", con->num); 960 959 return 0; ··· 1043 1044 goto err_reset; 1044 1045 } 1045 1046 1047 + mutex_unlock(&ucsi->ppm_lock); 1048 + 1046 1049 /* Register all connectors */ 1047 1050 for (i = 0; i < ucsi->cap.num_connectors; i++) { 1048 1051 ret = ucsi_register_port(ucsi, i); ··· 1055 1054 /* Enable all notifications */ 1056 1055 ucsi->ntfy = UCSI_ENABLE_NTFY_ALL; 1057 1056 command = UCSI_SET_NOTIFICATION_ENABLE | ucsi->ntfy; 1058 - ret = ucsi_run_command(ucsi, command, NULL, 0); 1057 + ret = ucsi_send_command(ucsi, command, NULL, 0); 1059 1058 if (ret < 0) 1060 1059 goto err_unregister; 1061 - 1062 - mutex_unlock(&ucsi->ppm_lock); 1063 1060 1064 1061 return 0; 1065 1062 ··· 1070 1071 con->port = NULL; 1071 1072 } 1072 1073 1074 + mutex_lock(&ucsi->ppm_lock); 1073 1075 err_reset: 1074 1076 ucsi_reset_ppm(ucsi); 1075 1077 err: