Re: [PATCH 3/3] usb: gadget: f_mass_storage: Serialize wake and sleep execution

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Apr 07 2017, Thinh Nguyen wrote:
> f_mass_storage has a memorry barrier issue with the sleep and wake
> functions that can cause a deadlock. This results in intermittent hangs
> during MSC file transfer. The host will reset the device after receiving
> no response to resume the transfer. This issue is seen when dwc3 is
> processing 2 transfer-in-progress events at the same time, invoking
> completion handlers for CSW and CBW. Also this issue occurs depending on
> the system timing and latency.
>
> To increase the chance to hit this issue, you can force dwc3 driver to
> wait and process those 2 events at once by adding a small delay (~100us)
> in dwc3_check_event_buf() whenever the request is for CSW and read the
> event count again. Avoid debugging with printk and ftrace as extra
> delays and memory barrier will mask this issue.
>
> Scenario which can lead to failure:
> -----------------------------------
> 1) The main thread sleeps and waits for the next command.
> 2) bulk_in_complete() wakes up main thread for CSW.
> 3) bulk_out_complete() tries to wake up the running main thread for CBW.
> 4) thread_wakeup_needed is not loaded with correct value in sleep_thread().
> 5) Main thread goes to sleep.
>
> See more detail below:
>
> Note the 2 critical variables.
>  * common->thread_wakeup_needed
>  * bh->state
>
> Eval'd  | CPU0 (main thread)			   | CPU1
> --------+------------------------------------------+----------------------
>         | get_next_command()			   |
>         | ...					   |
> while(1)| while (bh->state != BUF_STATE_FULL) {	   |
>         |  for (;;) {				   |
>         |    set_current_state(TASK_INTERRUPTIBLE);|
> if(0)   |    if (thread_wakeup_needed)		   |
>         |    schedule()				   |
> 	|					   | bulk_in_complete() {
> 	|					   | smp_wmb();
> 	|					   | bh->state = BUF_STATE_EMPTY
> 	|					   | smp_wmb();
> 	|					   | thread_wakeup_needed = 1
> 	|					   | wake_up_process()
> 	|					   | }
> 	|  /* 2nd for loop iteration */		   |
>         |  for (;;) {				   |
>         |    set_current_state(TASK_INTERRUPTIBLE);|
> if(1)   |    if (thread_wakeup_needed)		   |
>         |      break				   |
>         |  }					   |
> 	|					   | bulk_out_complete() {
> 	|					   | smp_wmb();
> 	|  __set_current_state(TASK_RUNNING);	   |
> 	|  thread_wakeup_needed = 0;		   |
> 	|  smp_rmb();				   |
> 	|					   |
> 	|  /* 2nd while loop iteration */	   |
> while(1)| while (bh->state != BUF_STATE_FULL) {	   |
> 	|					   | bh->state = BUF_STATE_FULL;
> 	|					   | smp_wmb();
> 	|					-->| thread_wakeup_needed = 1
> 	|					   | wake_up_process()
> 	|					   | }
>         |  for (;;) {				   |
>         |    set_current_state(TASK_INTERRUPTIBLE);|
> if(0)   |    if (thread_wakeup_needed)		   |<--
>         |    schedule()				   |
>         |					   |
> 	|	<THREAD GOES TO SLEEP AND WAITS>   |
>
> thread_wakeup_needed is not guaranteed to be loaded before checking it.
> Note that this happens when wake_up_process() is called on a running
> process.
>
> This patch fixes this issue by explicitly use smp_mb() after clearing of
> thread_wakeup_needed in the sleep function. It serializes the order in
> which thread_wakeup_needed is loaded and stored, thus prevent loading
> the wrong value when checking it in the sleeper.
>
> However, a better solution in the future would be to use wait_queue
> method that takes care of managing memory barrier between waker and
> waiter.
>
> See DEFINE_WAIT_FUNC comment in kernel scheduling wait.c as this
> solution is similar to its implementation.
>
> Signed-off-by: Thinh Nguyen <thinhn@xxxxxxxxxxxx>

Acked-by: Michal Nazarewicz <mina86@xxxxxxxxxx>

> ---
>  drivers/usb/gadget/function/f_mass_storage.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
> index 4c8aacc232c0..f40433eb8259 100644
> --- a/drivers/usb/gadget/function/f_mass_storage.c
> +++ b/drivers/usb/gadget/function/f_mass_storage.c
> @@ -627,7 +627,7 @@ static int sleep_thread(struct fsg_common *common, bool can_freeze)
>  	}
>  	__set_current_state(TASK_RUNNING);
>  	common->thread_wakeup_needed = 0;
> -	smp_rmb();	/* ensure the latest bh->state is visible */
> +	smp_mb();	/* ensure the latest bh->state is visible */
>  	return rc;
>  }

-- 
Best regards
ミハウ “𝓶𝓲𝓷𝓪86” ナザレヴイツ
«If at first you don’t succeed, give up skydiving»
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux