[PATCH] usb: otg-fsm: Cancel HNP polling work when not used
Peter Chen
hzpeterchen at gmail.com
Thu Jun 30 02:12:22 PDT 2016
On Wed, Jun 29, 2016 at 07:02:32PM -0700, Stephen Boyd wrote:
> Quoting Peter Chen (2016-06-29 02:43:47)
> > On Mon, Jun 27, 2016 at 06:18:27PM -0700, Stephen Boyd wrote:
> > It introduces circular locking after applying it, otg_statemachine calls
> > otg_leave_state, and otg_leave_state calls otg_statemachine again due
> > to flush work, see below dump:
> >
> > I suggest moving initialization/flush hnp polling work to chipidea driver.
> >
> > [ 183.086987] ======================================================
> > [ 183.093183] [ INFO: possible circular locking dependency detected ]
> > [ 183.099471] 4.7.0-rc4-00012-gf1f333f-dirty #856 Not tainted
> > [ 183.105059] -------------------------------------------------------
> > [ 183.111341] kworker/0:2/114 is trying to acquire lock:
> > [ 183.116492] (&ci->fsm.lock){+.+.+.}, at: [<806118dc>]
> > otg_statemachine+0x20/0x470
> > [ 183.124199]
> > [ 183.124199] but task is already holding lock:
> > [ 183.130052] ((&(&fsm->hnp_polling_work)->work)){+.+...}, at:
> > [<80140368>] process_one_work+0x128/0x418
> > [ 183.139568]
> > [ 183.139568] which lock already depends on the new lock.
> > [ 183.139568]
> > [ 183.147765]
> > [ 183.147765] the existing dependency chain (in reverse order) is:
> > [ 183.155265]
> > -> #1 ((&(&fsm->hnp_polling_work)->work)){+.+...}:
> > [ 183.161371] [<8013e97c>] flush_work+0x44/0x234
> > [ 183.166469] [<801411a8>] __cancel_work_timer+0x98/0x1c8
> > [ 183.172347] [<80141304>] cancel_delayed_work_sync+0x14/0x18
> > [ 183.178570] [<80610ef8>] otg_set_state+0x290/0xc54
> > [ 183.184011] [<806119d0>] otg_statemachine+0x114/0x470
> > [ 183.189712] [<8060a590>] ci_otg_fsm_work+0x40/0x190
> > [ 183.195239] [<806054d0>] ci_otg_work+0xcc/0x1e4
> > [ 183.200430] [<801403d4>] process_one_work+0x194/0x418
> > [ 183.206136] [<8014068c>] worker_thread+0x34/0x4fc
> > [ 183.211489] [<80146d08>] kthread+0xdc/0xf8
> > [ 183.216238] [<80108ab0>] ret_from_fork+0x14/0x24
> > [ 183.221514]
> > -> #0 (&ci->fsm.lock){+.+.+.}:
> > [ 183.225880] [<8016ff94>] lock_acquire+0x78/0x98
> > [ 183.231062] [<80947c18>] mutex_lock_nested+0x54/0x3ec
> > [ 183.236773] [<806118dc>] otg_statemachine+0x20/0x470
> > [ 183.242388] [<80611df4>] otg_hnp_polling_work+0xc8/0x1a4
> > [ 183.248352] [<801403d4>] process_one_work+0x194/0x418
> > [ 183.254055] [<8014068c>] worker_thread+0x34/0x4fc
> > [ 183.259409] [<80146d08>] kthread+0xdc/0xf8
> > [ 183.264154] [<80108ab0>] ret_from_fork+0x14/0x24
> > [ 183.269424]
> > [ 183.269424] other info that might help us debug this:
> > [ 183.269424]
> > [ 183.277451] Possible unsafe locking scenario:
> > [ 183.277451]
> > [ 183.283389] CPU0 CPU1
> > [ 183.287931] ---- ----
> > [ 183.292473] lock((&(&fsm->hnp_polling_work)->work));
> > [ 183.297665] lock(&ci->fsm.lock);
> > [ 183.303639]
> > lock((&(&fsm->hnp_polling_work)->work));
> > [ 183.311347] lock(&ci->fsm.lock);
> > [ 183.314801]
>
> Hm.. perhaps we should do cancel_delayed_work() then and not require any
> sync? That would require some locking in the polling worker, but that
> looks racy anyway as it runs in parallel to the state machine so it
> probably needs to lock with the FSM. Completely untested patch as I'm
> going home from work now.
>
Wait your tested patch:)
Peter
> ----8<----
> diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c
> index 73eec8c12235..6f4b7f0c81ff 100644
> --- a/drivers/usb/common/usb-otg-fsm.c
> +++ b/drivers/usb/common/usb-otg-fsm.c
> @@ -70,7 +70,11 @@ static void otg_stop_hnp_polling(struct otg_fsm *fsm)
> if (!fsm->host_req_flag)
> return;
>
> - cancel_delayed_work_sync(&fsm->hnp_polling_work);
> + /*
> + * We don't call cancel_delayed_work_sync() here because the
> + * worker is synchronized to this function via the fsm lock.
> + */
> + cancel_delayed_work(&fsm->hnp_polling_work);
> }
>
> /* Called when leaving a state. Do state clean up jobs here */
> @@ -136,23 +140,27 @@ static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
> }
> }
>
> +static int __otg_statemachine(struct otg_fsm *fsm);
> +
> static void otg_hnp_polling_work(struct work_struct *work)
> {
> struct otg_fsm *fsm = container_of(to_delayed_work(work),
> struct otg_fsm, hnp_polling_work);
> struct usb_device *udev;
> - enum usb_otg_state state = fsm->otg->state;
> + enum usb_otg_state state;
> u8 flag;
> int retval;
>
> + mutex_lock(&fsm->lock);
> + state = fsm->otg->state;
> if (state != OTG_STATE_A_HOST && state != OTG_STATE_B_HOST)
> - return;
> + goto unlock;
>
> udev = usb_hub_find_child(fsm->otg->host->root_hub, 1);
> if (!udev) {
> dev_err(fsm->otg->host->controller,
> "no usb dev connected, can't start HNP polling\n");
> - return;
> + goto unlock;
> }
>
> *fsm->host_req_flag = 0;
> @@ -168,7 +176,7 @@ static void otg_hnp_polling_work(struct work_struct *work)
> USB_CTRL_GET_TIMEOUT);
> if (retval != 1) {
> dev_err(&udev->dev, "Get one byte OTG status failed\n");
> - return;
> + goto unlock;
> }
>
> flag = *fsm->host_req_flag;
> @@ -176,10 +184,10 @@ static void otg_hnp_polling_work(struct work_struct *work)
> /* Continue HNP polling */
> schedule_delayed_work(&fsm->hnp_polling_work,
> msecs_to_jiffies(T_HOST_REQ_POLL));
> - return;
> + goto unlock;
> } else if (flag != HOST_REQUEST_FLAG) {
> dev_err(&udev->dev, "host request flag %d is invalid\n", flag);
> - return;
> + goto unlock;
> }
>
> /* Host request flag is set */
> @@ -200,7 +208,9 @@ static void otg_hnp_polling_work(struct work_struct *work)
> fsm->b_bus_req = 0;
> }
>
> - otg_statemachine(fsm);
> + __otg_statemachine(fsm);
> +unlock:
> + mutex_unlock(&fsm->lock);
> }
>
> static void otg_start_hnp_polling(struct otg_fsm *fsm)
> @@ -340,12 +350,10 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
> }
>
> /* State change judgement */
> -int otg_statemachine(struct otg_fsm *fsm)
> +static int __otg_statemachine(struct otg_fsm *fsm)
> {
> enum usb_otg_state state;
>
> - mutex_lock(&fsm->lock);
> -
> state = fsm->otg->state;
> fsm->state_changed = 0;
> /* State machine state change judgement */
> @@ -458,9 +466,16 @@ int otg_statemachine(struct otg_fsm *fsm)
> default:
> break;
> }
> - mutex_unlock(&fsm->lock);
>
> VDBG("quit statemachine, changed = %d\n", fsm->state_changed);
> return fsm->state_changed;
> }
> +
> +int otg_statemachine(struct otg_fsm *fsm)
> +{
> + int ret;
> + mutex_lock(&fsm->lock);
> + ret = __otg_statemachine(fsm);
> + mutex_unlock(&fsm->lock);
> +}
> EXPORT_SYMBOL_GPL(otg_statemachine);
--
Best Regards,
Peter Chen
More information about the linux-arm-kernel
mailing list