summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJack Pham <jackp@codeaurora.org>2016-12-16 17:16:32 -0800
committerJack Pham <jackp@codeaurora.org>2016-12-16 17:23:51 -0800
commit7aedfdd8f2dd282976faa9c98f473f056851fb85 (patch)
tree0af99986c115bef8d6bede1b6796dd2a1b99b329
parent3162449f7d245d45f007d4ea3224576ddf1bcc63 (diff)
usb: pd: Register power_supply notifier after completing init
There is a rare but possible chance that just after registering the psy_changed notifier callback with the power_supply framework that it could be called immediately, schedule the usbpd_sm, and execute the worker on another thread, all before usbpd_create() has completed. Since some structures, notably pd->rx_lock, haven't been fully initialized yet, this leads to spin_lock() call accessing uninitialized data. Fix this by moving the power_supply_reg_notifier() call to the very end of usbpd_create() after everything has been initialized. Change-Id: I7d2bc9eca8e7e30dbc656be620a0f4fd8eea2239 Signed-off-by: Jack Pham <jackp@codeaurora.org>
-rw-r--r--drivers/usb/pd/policy_engine.c24
1 files changed, 12 insertions, 12 deletions
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index 2fbe4c8faa79..dfc63b47ae81 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -2974,11 +2974,6 @@ struct usbpd *usbpd_create(struct device *parent)
goto destroy_wq;
}
- pd->psy_nb.notifier_call = psy_changed;
- ret = power_supply_reg_notifier(&pd->psy_nb);
- if (ret)
- goto put_psy;
-
/*
* associate extcon with the parent dev as it could have a DT
* node which will be useful for extcon_get_edev_by_phandle()
@@ -2987,26 +2982,26 @@ struct usbpd *usbpd_create(struct device *parent)
if (IS_ERR(pd->extcon)) {
usbpd_err(&pd->dev, "failed to allocate extcon device\n");
ret = PTR_ERR(pd->extcon);
- goto unreg_psy;
+ goto put_psy;
}
pd->extcon->mutually_exclusive = usbpd_extcon_exclusive;
ret = devm_extcon_dev_register(parent, pd->extcon);
if (ret) {
usbpd_err(&pd->dev, "failed to register extcon device\n");
- goto unreg_psy;
+ goto put_psy;
}
pd->vbus = devm_regulator_get(parent, "vbus");
if (IS_ERR(pd->vbus)) {
ret = PTR_ERR(pd->vbus);
- goto unreg_psy;
+ goto put_psy;
}
pd->vconn = devm_regulator_get(parent, "vconn");
if (IS_ERR(pd->vconn)) {
ret = PTR_ERR(pd->vconn);
- goto unreg_psy;
+ goto put_psy;
}
pd->vconn_is_external = device_property_present(parent,
@@ -3031,7 +3026,7 @@ struct usbpd *usbpd_create(struct device *parent)
&pd->dr_desc);
if (IS_ERR(pd->dual_role)) {
usbpd_err(&pd->dev, "could not register dual_role instance\n");
- goto unreg_psy;
+ goto put_psy;
} else {
pd->dual_role->drv_data = pd;
}
@@ -3045,13 +3040,18 @@ struct usbpd *usbpd_create(struct device *parent)
INIT_LIST_HEAD(&pd->svid_handlers);
init_completion(&pd->swap_complete);
+ pd->psy_nb.notifier_call = psy_changed;
+ ret = power_supply_reg_notifier(&pd->psy_nb);
+ if (ret)
+ goto del_inst;
+
/* force read initial power_supply values */
psy_changed(&pd->psy_nb, PSY_EVENT_PROP_CHANGED, pd->usb_psy);
return pd;
-unreg_psy:
- power_supply_unreg_notifier(&pd->psy_nb);
+del_inst:
+ list_del(&pd->instance);
put_psy:
power_supply_put(pd->usb_psy);
destroy_wq: