]>
Commit | Line | Data |
---|---|---|
6c0d4664 GKH |
1 | From d9e93c08d8d985e5ef89436ebc9f4aad7e31559f Mon Sep 17 00:00:00 2001 |
2 | From: xiao jin <jin.xiao@intel.com> | |
3 | Date: Mon, 26 May 2014 19:23:14 +0200 | |
4 | Subject: USB: usb_wwan: fix race between write and resume | |
5 | ||
6 | From: xiao jin <jin.xiao@intel.com> | |
7 | ||
8 | commit d9e93c08d8d985e5ef89436ebc9f4aad7e31559f upstream. | |
9 | ||
10 | We find a race between write and resume. usb_wwan_resume run play_delayed() | |
11 | and spin_unlock, but intfdata->suspended still is not set to zero. | |
12 | At this time usb_wwan_write is called and anchor the urb to delay | |
13 | list. Then resume keep running but the delayed urb have no chance | |
14 | to be commit until next resume. If the time of next resume is far | |
15 | away, tty will be blocked in tty_wait_until_sent during time. The | |
16 | race also can lead to writes being reordered. | |
17 | ||
18 | This patch put play_Delayed and intfdata->suspended together in the | |
19 | spinlock, it's to avoid the write race during resume. | |
20 | ||
21 | Fixes: 383cedc3bb43 ("USB: serial: full autosuspend support for the | |
22 | option driver") | |
23 | ||
24 | Signed-off-by: xiao jin <jin.xiao@intel.com> | |
25 | Signed-off-by: Zhang, Qi1 <qi1.zhang@intel.com> | |
26 | Reviewed-by: David Cohen <david.a.cohen@linux.intel.com> | |
27 | Signed-off-by: Johan Hovold <jhovold@gmail.com> | |
28 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
29 | ||
1bcc855e GKH |
30 | --- |
31 | drivers/usb/serial/usb_wwan.c | 8 ++------ | |
32 | 1 file changed, 2 insertions(+), 6 deletions(-) | |
33 | ||
6c0d4664 GKH |
34 | --- a/drivers/usb/serial/usb_wwan.c |
35 | +++ b/drivers/usb/serial/usb_wwan.c | |
1bcc855e | 36 | @@ -731,17 +731,15 @@ int usb_wwan_resume(struct usb_serial *s |
6c0d4664 GKH |
37 | } |
38 | } | |
39 | ||
40 | + spin_lock_irq(&intfdata->susp_lock); | |
41 | for (i = 0; i < serial->num_ports; i++) { | |
42 | /* walk all ports */ | |
43 | port = serial->port[i]; | |
44 | portdata = usb_get_serial_port_data(port); | |
45 | ||
46 | /* skip closed ports */ | |
47 | - spin_lock_irq(&intfdata->susp_lock); | |
1bcc855e | 48 | - if (!portdata->opened) { |
6c0d4664 GKH |
49 | - spin_unlock_irq(&intfdata->susp_lock); |
50 | + if (!portdata || !portdata->opened) | |
51 | continue; | |
52 | - } | |
53 | ||
54 | for (j = 0; j < N_IN_URB; j++) { | |
55 | urb = portdata->in_urbs[j]; | |
1bcc855e | 56 | @@ -754,9 +752,7 @@ int usb_wwan_resume(struct usb_serial *s |
6c0d4664 GKH |
57 | } |
58 | } | |
59 | play_delayed(port); | |
60 | - spin_unlock_irq(&intfdata->susp_lock); | |
61 | } | |
62 | - spin_lock_irq(&intfdata->susp_lock); | |
63 | intfdata->suspended = 0; | |
64 | spin_unlock_irq(&intfdata->susp_lock); | |
65 | err_out: |