On Tue, Aug 23, 2022 at 08:54:55AM +0200, Takashi Iwai wrote: > The recent commit 87d0e2f41b8c ("usb: typec: ucsi: add a common > function ucsi_unregister_connectors()") introduced a regression that > caused NULL dereference at reading the power supply sysfs. It's a > stale sysfs entry that should have been removed but remains with NULL > ops. The commit changed the error handling to skip the entries after > a NULL con->wq, and this leaves the power device unreleased. > > For addressing the regression, the straight revert is applied here. > Further code improvements can be done from the scratch again. > > Fixes: 87d0e2f41b8c ("usb: typec: ucsi: add a common function ucsi_unregister_connectors()") > Link: https://bugzilla.suse.com/show_bug.cgi?id=1202386 > Link: https://lore.kernel.org/r/87r11cmbx0.wl-tiwai@xxxxxxx > Cc: <stable@xxxxxxxxxxxxxxx> > Signed-off-by: Takashi Iwai <tiwai@xxxxxxx> Acked-by: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx> > --- > drivers/usb/typec/ucsi/ucsi.c | 53 ++++++++++++++++------------------- > 1 file changed, 24 insertions(+), 29 deletions(-) > > diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c > index 1aea46493b85..7f2624f42724 100644 > --- a/drivers/usb/typec/ucsi/ucsi.c > +++ b/drivers/usb/typec/ucsi/ucsi.c > @@ -1200,32 +1200,6 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) > return ret; > } > > -static void ucsi_unregister_connectors(struct ucsi *ucsi) > -{ > - struct ucsi_connector *con; > - int i; > - > - if (!ucsi->connector) > - return; > - > - for (i = 0; i < ucsi->cap.num_connectors; i++) { > - con = &ucsi->connector[i]; > - > - if (!con->wq) > - break; > - > - cancel_work_sync(&con->work); > - ucsi_unregister_partner(con); > - ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON); > - ucsi_unregister_port_psy(con); > - destroy_workqueue(con->wq); > - typec_unregister_port(con->port); > - } > - > - kfree(ucsi->connector); > - ucsi->connector = NULL; > -} > - > /** > * ucsi_init - Initialize UCSI interface > * @ucsi: UCSI to be initialized > @@ -1234,6 +1208,7 @@ static void ucsi_unregister_connectors(struct ucsi *ucsi) > */ > static int ucsi_init(struct ucsi *ucsi) > { > + struct ucsi_connector *con; > u64 command; > int ret; > int i; > @@ -1264,7 +1239,7 @@ static int ucsi_init(struct ucsi *ucsi) > } > > /* Allocate the connectors. Released in ucsi_unregister() */ > - ucsi->connector = kcalloc(ucsi->cap.num_connectors, > + ucsi->connector = kcalloc(ucsi->cap.num_connectors + 1, > sizeof(*ucsi->connector), GFP_KERNEL); > if (!ucsi->connector) { > ret = -ENOMEM; > @@ -1288,7 +1263,15 @@ static int ucsi_init(struct ucsi *ucsi) > return 0; > > err_unregister: > - ucsi_unregister_connectors(ucsi); > + for (con = ucsi->connector; con->port; con++) { > + ucsi_unregister_partner(con); > + ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON); > + ucsi_unregister_port_psy(con); > + if (con->wq) > + destroy_workqueue(con->wq); > + typec_unregister_port(con->port); > + con->port = NULL; > + } > > err_reset: > memset(&ucsi->cap, 0, sizeof(ucsi->cap)); > @@ -1402,6 +1385,7 @@ EXPORT_SYMBOL_GPL(ucsi_register); > void ucsi_unregister(struct ucsi *ucsi) > { > u64 cmd = UCSI_SET_NOTIFICATION_ENABLE; > + int i; > > /* Make sure that we are not in the middle of driver initialization */ > cancel_delayed_work_sync(&ucsi->work); > @@ -1409,7 +1393,18 @@ void ucsi_unregister(struct ucsi *ucsi) > /* Disable notifications */ > ucsi->ops->async_write(ucsi, UCSI_CONTROL, &cmd, sizeof(cmd)); > > - ucsi_unregister_connectors(ucsi); > + for (i = 0; i < ucsi->cap.num_connectors; i++) { > + cancel_work_sync(&ucsi->connector[i].work); > + ucsi_unregister_partner(&ucsi->connector[i]); > + ucsi_unregister_altmodes(&ucsi->connector[i], > + UCSI_RECIPIENT_CON); > + ucsi_unregister_port_psy(&ucsi->connector[i]); > + if (ucsi->connector[i].wq) > + destroy_workqueue(ucsi->connector[i].wq); > + typec_unregister_port(ucsi->connector[i].port); > + } > + > + kfree(ucsi->connector); > } > EXPORT_SYMBOL_GPL(ucsi_unregister); > > -- > 2.35.3 thanks, -- heikki