1
0
Fork 0

MLK-20346 usb: chipidea: change power_lost workqueue as freezable

Like commit d144dfea8a ("usb: chipidea: otg: change workqueue
ci_otg as freezable"), the power_lost work item may try to remove
hcd if controller is powered off during the system suspend, and
the similar deadlock happens, see below dumps.

Meanwhile, with this change, we need to disable USB interrupt
during the work item runs (after driver resume has finished),
otherwise, USB transfer will be timeout (5s) due to USB interrupt
is disabled and IAA watchdog is still not ready at that time.

Workqueue: events ci_power_lost_work
Call trace:
[<ffff000008085c44>] __switch_to+0x8c/0xd0
[<ffff000008d7bbf4>] __schedule+0x19c/0x5d8
[<ffff000008d7c068>] schedule+0x38/0xa0
[<ffff000008d7f3b4>] schedule_timeout+0x19c/0x338
[<ffff000008d7cc10>] wait_for_common+0xa0/0x148
[<ffff000008d7cccc>] wait_for_completion+0x14/0x20
[<ffff0000080e6040>] flush_work+0xd8/0x1f0
[<ffff0000080e61f4>] flush_delayed_work+0x3c/0x48
[<ffff0000081ae1c8>] wb_shutdown+0x90/0xd0
[<ffff0000081ae688>] bdi_unregister+0x58/0x1c0
[<ffff000008413a60>] del_gendisk+0x218/0x228
[<ffff00000871683c>] sd_remove+0x64/0xc0
[<ffff0000086b6eec>] device_release_driver_internal+0x154/0x1f0
[<ffff0000086b6f9c>] device_release_driver+0x14/0x20
[<ffff0000086b5d40>] bus_remove_device+0xc8/0x108
[<ffff0000086b2a08>] device_del+0x1f8/0x300
[<ffff0000087049ec>] __scsi_remove_device+0xec/0x128
[<ffff000008702c70>] scsi_forget_host+0x70/0x78
[<ffff0000086f7ee8>] scsi_remove_host+0xa0/0x140
[<ffff0000088e0588>] usb_stor_disconnect+0x50/0xc0
[<ffff00000887eab8>] usb_unbind_interface+0x78/0x280
[<ffff0000086b6eec>] device_release_driver_internal+0x154/0x1f0
[<ffff0000086b6f9c>] device_release_driver+0x14/0x20
[<ffff0000086b5d40>] bus_remove_device+0xc8/0x108
[<ffff0000086b2a08>] device_del+0x1f8/0x300
[<ffff00000887c364>] usb_disable_device+0xa4/0x210
[<ffff000008872cfc>] usb_disconnect+0x7c/0x240
[<ffff000008872e40>] usb_disconnect+0x1c0/0x240
[<ffff000008878e10>] usb_remove_hcd+0xc0/0x1d8
[<ffff0000088e7bac>] host_stop+0x34/0x90
[<ffff0000088e4088>] ci_handle_id_switch+0x70/0x1d0
[<ffff0000088e3038>] ci_power_lost_work+0x90/0xa8
[<ffff0000080e7100>] process_one_work+0x1e0/0x340
[<ffff0000080e72b0>] worker_thread+0x50/0x458
[<ffff0000080ed32c>] kthread+0xfc/0x128
[<ffff000008084eb8>] ret_from_fork+0x10/0x18

Reviewed-by: Jun Li <jun.li@nxp.com>
Signed-off-by: Peter Chen <peter.chen@nxp.com>
(cherry picked from commit d217dacbe793641497b38f48d063a6332242e8a9)
pull/10/head
Peter Chen 2018-11-16 16:40:48 +08:00
parent cebcf94b7a
commit f61f2b7294
2 changed files with 20 additions and 6 deletions

View File

@ -211,6 +211,8 @@ struct hw_bank {
* @wakeup_int: if wakeup interrupt occur
* @rev: The revision number for controller
* @mutex: protect code from concorrent running
* @power_lost_work: work item when controller power is lost
* @power_lost_wq: work queue for controller power is lost
*/
struct ci_hdrc {
struct device *dev;
@ -278,6 +280,7 @@ struct ci_hdrc {
u32 pm_portsc;
u32 pm_usbmode;
struct work_struct power_lost_work;
struct workqueue_struct *power_lost_wq;
struct mutex mutex;
};

View File

@ -963,6 +963,7 @@ static void ci_power_lost_work(struct work_struct *work)
struct ci_hdrc *ci = container_of(work, struct ci_hdrc,
power_lost_work);
disable_irq_nosync(ci->irq);
pm_runtime_get_sync(ci->dev);
if (!ci_otg_is_fsm_mode(ci))
ci_start_new_role(ci);
@ -1132,8 +1133,6 @@ static int ci_hdrc_probe(struct platform_device *pdev)
device_set_wakeup_capable(&pdev->dev, true);
/* Init workqueue for controller power lost handling */
INIT_WORK(&ci->power_lost_work, ci_power_lost_work);
mutex_init(&ci->mutex);
ret = dbg_create_files(ci);
@ -1144,8 +1143,19 @@ static int ci_hdrc_probe(struct platform_device *pdev)
if (ret)
goto remove_debug;
/* Init workqueue for controller power lost handling */
ci->power_lost_wq = create_freezable_workqueue("ci_power_lost");
if (!ci->power_lost_wq) {
dev_err(ci->dev, "can't create power_lost workqueue\n");
goto remove_sys_group;
}
INIT_WORK(&ci->power_lost_work, ci_power_lost_work);
return 0;
remove_sys_group:
sysfs_remove_group(&ci->dev->kobj, &ci_attr_group);
remove_debug:
dbg_remove_files(ci);
stop:
@ -1173,6 +1183,8 @@ static int ci_hdrc_remove(struct platform_device *pdev)
pm_runtime_put_noidle(&pdev->dev);
}
flush_workqueue(ci->power_lost_wq);
destroy_workqueue(ci->power_lost_wq);
dbg_remove_files(ci);
sysfs_remove_group(&ci->dev->kobj, &ci_attr_group);
ci_role_destroy(ci);
@ -1284,6 +1296,7 @@ static int ci_suspend(struct device *dev)
{
struct ci_hdrc *ci = dev_get_drvdata(dev);
flush_workqueue(ci->power_lost_wq);
if (ci->wq)
flush_workqueue(ci->wq);
/*
@ -1349,10 +1362,8 @@ static int ci_resume(struct device *dev)
if (ci->role != CI_ROLE_END && ci_role(ci)->resume)
ci_role(ci)->resume(ci, power_lost);
if (power_lost) {
disable_irq_nosync(ci->irq);
schedule_work(&ci->power_lost_work);
}
if (power_lost)
queue_work(ci->power_lost_wq, &ci->power_lost_work);
if (ci->supports_runtime_pm) {
pm_runtime_disable(dev);