locking problems in cdc-wdm

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

 



I should have noticed this before making changes to this driver, but
there is really something wrong with the locking in wdm_pre_reset/
wdm_post_reset.  And it is there before any of my changes.

The unmodified stable linux-3.2.y cdc-wdm does:

static int wdm_pre_reset(struct usb_interface *intf)
{
	struct wdm_device *desc = usb_get_intfdata(intf);

	mutex_lock(&desc->lock);
	kill_urbs(desc);

	/*
	 * we notify everybody using poll of
	 * an exceptional situation
	 * must be done before recovery lest a spontaneous
	 * message from the device is lost
	 */
	spin_lock_irq(&desc->iuspin);
	desc->rerr = -EINTR;
	spin_unlock_irq(&desc->iuspin);
	wake_up_all(&desc->wait);
	return 0;
}


The problem here is that wdm_read or wdm_write may be blocking on the
desc->wait waitqueue while holding the desc->lock mutex.  Which means
that wdm_pre_reset will have to wait for something else to wake up
wdm_read or wdm_write before it can finish. This is the same problem
which used to be present in wdm_disconnect.

Now I don't know how I am supposed to trigger a reset, but loading
usb-storage seems to do the trick for devices with mass storage
interfaces.  So I'm using that as a trigger.

I am testing with cdc-wdm from the stable linux-3.2.y branch
(i.e. without any of my recent changes) with the following diff to add
two debug printk's and allow binding to the Huawei modem (I will happily
demonstrate this on the Ericsson modem without any diff at all, if
someone tells me how to trigger a reset):

diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index efe6849..a338175 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -630,7 +630,7 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
        struct usb_cdc_dmm_desc *dmhd;
        u8 *buffer = intf->altsetting->extra;
        int buflen = intf->altsetting->extralen;
-       u16 maxcom = 0;
+       u16 maxcom = 512;
 
        if (!buffer)
                goto out;
@@ -676,8 +676,8 @@ next_desc:
 
        rv = -EINVAL;
        iface = intf->cur_altsetting;
-       if (iface->desc.bNumEndpoints != 1)
-               goto err;
+//     if (iface->desc.bNumEndpoints != 1)
+//             goto err;
        ep = &iface->endpoint[0].desc;
        if (!ep || !usb_endpoint_is_int_in(ep))
                goto err;
@@ -854,6 +854,7 @@ static int wdm_pre_reset(struct usb_interface *intf)
 {
        struct wdm_device *desc = usb_get_intfdata(intf);
 
+       dev_dbg(&intf->dev, "%s()\n", __func__); 
        mutex_lock(&desc->lock);
        kill_urbs(desc);
 
@@ -875,6 +876,7 @@ static int wdm_post_reset(struct usb_interface *intf)
        struct wdm_device *desc = usb_get_intfdata(intf);
        int rv;
 
+       dev_dbg(&intf->dev, "%s()\n", __func__); 
        rv = recover_from_urb_loss(desc);
        mutex_unlock(&desc->lock);
        return 0;



First, if not doing a blocking read or write then the reset work just
fine:


Jan 31 10:05:48 nemi kernel: [20438.808049] cdc_wdm 2-1:1.3: wdm2_suspend
Jan 31 10:05:48 nemi kernel: [20438.808140] option: option_instat_callback: error -2
Jan 31 10:06:01 nemi kernel: [20451.477056] Initializing USB Mass Storage driver...
Jan 31 10:06:01 nemi kernel: [20451.524742] cdc_wdm 2-1:1.3: wdm2_resume
Jan 31 10:06:01 nemi kernel: [20451.524853] scsi26 : usb-storage 2-1:1.4
Jan 31 10:06:01 nemi kernel: [20451.525924] scsi27 : usb-storage 2-1:1.5
Jan 31 10:06:01 nemi kernel: [20451.526828] scsi28 : usb-storage 2-2:1.0
Jan 31 10:06:01 nemi kernel: [20451.529000] usbcore: registered new interface driver usb-storage
Jan 31 10:06:01 nemi kernel: [20451.529005] USB Mass Storage support registered.
Jan 31 10:06:02 nemi kernel: [20452.525875] scsi 26:0:0:0: CD-ROM            HUAWEI   Mass Storage     2.31 PQ: 0 ANSI: 0
Jan 31 10:06:02 nemi kernel: [20452.529103] scsi 28:0:0:0: Direct-Access     SEMC     Mass Storage     0001 PQ: 0 ANSI: 2
Jan 31 10:06:02 nemi kernel: [20452.529598] scsi 28:0:0:1: CD-ROM            SEMC     CD-ROM           0001 PQ: 0 ANSI: 2
Jan 31 10:06:23 nemi kernel: [20473.824273] option: option_instat_callback: error -108
Jan 31 10:06:23 nemi kernel: [20473.824409] option1 ttyUSB2: GSM modem (1-port) converter now disconnected from ttyUSB2
Jan 31 10:06:23 nemi kernel: [20473.824439] option 2-1:1.0: device disconnected
Jan 31 10:06:23 nemi kernel: [20473.825406] option1 ttyUSB1: GSM modem (1-port) converter now disconnected from ttyUSB1
Jan 31 10:06:23 nemi kernel: [20473.825434] option 2-1:1.1: device disconnected
Jan 31 10:06:23 nemi kernel: [20473.826926] option1 ttyUSB0: GSM modem (1-port) converter now disconnected from ttyUSB0
Jan 31 10:06:23 nemi kernel: [20473.826956] option 2-1:1.2: device disconnected
Jan 31 10:06:23 nemi kernel: [20473.826963] cdc_wdm 2-1:1.3: wdm_pre_reset()
Jan 31 10:06:24 nemi kernel: [20473.936060] usb 2-1: reset high-speed USB device number 11 using ehci_hcd
Jan 31 10:06:24 nemi kernel: [20474.071217] cdc_wdm 2-1:1.3: wdm_post_reset()
Jan 31 10:06:24 nemi kernel: [20474.071270] option 2-1:1.2: GSM modem (1-port) converter detected
Jan 31 10:06:24 nemi kernel: [20474.072067] usb 2-1: GSM modem (1-port) converter now attached to ttyUSB0
Jan 31 10:06:24 nemi kernel: [20474.072104] option 2-1:1.1: GSM modem (1-port) converter detected
Jan 31 10:06:24 nemi kernel: [20474.073510] usb 2-1: GSM modem (1-port) converter now attached to ttyUSB1
Jan 31 10:06:24 nemi kernel: [20474.073554] option 2-1:1.0: GSM modem (1-port) converter detected
Jan 31 10:06:24 nemi kernel: [20474.074178] usb 2-1: GSM modem (1-port) converter now attached to ttyUSB2
Jan 31 10:06:24 nemi kernel: [20474.080716] scsi 27:0:0:0: Direct-Access     HUAWEI   SD Storage       2.31 PQ: 0 ANSI: 2
Jan 31 10:06:24 nemi kernel: [20474.084822] sd 27:0:0:0: [sdb] Attached SCSI removable disk
Jan 31 10:06:24 nemi kernel: [20474.098566] sr1: scsi-1 drive
Jan 31 10:06:24 nemi kernel: [20474.098786] sr 26:0:0:0: Attached scsi CD-ROM sr1
Jan 31 10:06:24 nemi kernel: [20474.101687] sr2: scsi3-mmc drive: 0x/0x caddy
Jan 31 10:06:24 nemi kernel: [20474.101867] sr 28:0:0:1: Attached scsi CD-ROM sr2
Jan 31 10:06:24 nemi kernel: [20474.102905] sd 28:0:0:0: [sdc] Attached SCSI removable disk



But if I run "cat /dev/cdc-wdm2" while loading usb-storage then
everything just stops at the pre_reset, as expected:

Jan 31 10:07:03 nemi kernel: [20513.014686] Initializing USB Mass Storage driver...
Jan 31 10:07:03 nemi kernel: [20513.015083] scsi29 : usb-storage 2-1:1.4
Jan 31 10:07:03 nemi kernel: [20513.015344] scsi30 : usb-storage 2-1:1.5
Jan 31 10:07:03 nemi kernel: [20513.015722] scsi31 : usb-storage 2-2:1.0
Jan 31 10:07:03 nemi kernel: [20513.016890] usbcore: registered new interface driver usb-storage
Jan 31 10:07:03 nemi kernel: [20513.016895] USB Mass Storage support registered.
Jan 31 10:07:04 nemi kernel: [20514.013654] scsi 29:0:0:0: CD-ROM            HUAWEI   Mass Storage     2.31 PQ: 0 ANSI: 0
Jan 31 10:07:04 nemi kernel: [20514.018318] scsi 31:0:0:0: Direct-Access     SEMC     Mass Storage     0001 PQ: 0 ANSI: 2
Jan 31 10:07:04 nemi kernel: [20514.023467] sr1: scsi-1 drive
Jan 31 10:07:04 nemi kernel: [20514.023990] sr 29:0:0:0: Attached scsi CD-ROM sr1
Jan 31 10:07:04 nemi kernel: [20514.024491] scsi 31:0:0:1: CD-ROM            SEMC     CD-ROM           0001 PQ: 0 ANSI: 2
Jan 31 10:07:24 nemi kernel: [20534.880287] option: option_instat_callback: error -108
Jan 31 10:07:24 nemi kernel: [20534.880422] option1 ttyUSB2: GSM modem (1-port) converter now disconnected from ttyUSB2
Jan 31 10:07:24 nemi kernel: [20534.880453] option 2-1:1.0: device disconnected
Jan 31 10:07:24 nemi kernel: [20534.880549] option1 ttyUSB1: GSM modem (1-port) converter now disconnected from ttyUSB1
Jan 31 10:07:24 nemi kernel: [20534.880576] option 2-1:1.1: device disconnected
Jan 31 10:07:24 nemi kernel: [20534.881411] option1 ttyUSB0: GSM modem (1-port) converter now disconnected from ttyUSB0
Jan 31 10:07:24 nemi kernel: [20534.881440] option 2-1:1.2: device disconnected
Jan 31 10:07:24 nemi kernel: [20534.881448] cdc_wdm 2-1:1.3: wdm_pre_reset()


The device appears completely dead at this point.  lsusb will hang,  the
option driver is disconnected, and these drivers are just waiting:

nemi:/tmp# ls -l /sys/bus/usb/devices/2-1/*/driver
lrwxrwxrwx 1 root root 0 Jan 31 10:37 /sys/bus/usb/devices/2-1/2-1:1.3/driver -> ../../../../../../bus/usb/drivers/cdc_wdm
lrwxrwxrwx 1 root root 0 Jan 31 10:07 /sys/bus/usb/devices/2-1/2-1:1.4/driver -> ../../../../../../bus/usb/drivers/usb-storage
lrwxrwxrwx 1 root root 0 Jan 31 10:37 /sys/bus/usb/devices/2-1/2-1:1.5/driver -> ../../../../../../bus/usb/drivers/usb-storage


Killing the blocking read revives the device again:

bjorn@nemi:~$ cat /dev/cdc-wdm2 
^C

Jan 31 10:38:41 nemi kernel: [22411.768051] usb 2-1: reset high-speed USB device number 11 using ehci_hcd
Jan 31 10:38:41 nemi kernel: [22411.903182] cdc_wdm 2-1:1.3: wdm_post_reset()
Jan 31 10:38:41 nemi kernel: [22411.903222] cdc_wdm 2-1:1.3: wdm_release: cleanup
Jan 31 10:38:41 nemi kernel: [22411.903263] option 2-1:1.2: GSM modem (1-port) converter detected
Jan 31 10:38:41 nemi kernel: [22411.904037] usb 2-1: GSM modem (1-port) converter now attached to ttyUSB0
Jan 31 10:38:41 nemi kernel: [22411.904080] option 2-1:1.1: GSM modem (1-port) converter detected
Jan 31 10:38:42 nemi kernel: [22411.905843] usb 2-1: GSM modem (1-port) converter now attached to ttyUSB1
Jan 31 10:38:42 nemi kernel: [22411.905889] option 2-1:1.0: GSM modem (1-port) converter detected
Jan 31 10:38:42 nemi kernel: [22411.906970] usb 2-1: GSM modem (1-port) converter now attached to ttyUSB2
Jan 31 10:38:42 nemi kernel: [22411.912936] scsi 30:0:0:0: Direct-Access     HUAWEI   SD Storage       2.31 PQ: 0 ANSI: 2
Jan 31 10:38:42 nemi kernel: [22411.918662] sr2: scsi3-mmc drive: 0x/0x caddy
Jan 31 10:38:42 nemi kernel: [22411.918906] sr 31:0:0:1: Attached scsi CD-ROM sr2
Jan 31 10:38:42 nemi kernel: [22411.919403] sd 31:0:0:0: [sdc] Attached SCSI removable disk
Jan 31 10:38:42 nemi kernel: [22411.919530] sd 30:0:0:0: [sdb] Attached SCSI removable disk



The problem was not changed by my split in separate read and write
locks, as I made sure to take both locks at this point. 

Now the problem can be "fixed" like it was in disconnect, by reordering.
But that's really not a good solution here. We just end up racing
against the next reader or writer waiting for the lock with a fair
chance of winning, but still with a chance of losing...

If we're going to take the read/write mutex in pre_reset then we really
need to first set a flag telling wdm_read and wdm_write not to block
with the mutex held, like we do in disconnect.  But all this seems
unnecessarily complex.  Do we really need to take the locks in reset?
What problem is this trying to solve?  Is it avoiding submitting an urb
in wdm_write?  In that case, why not just set the WDM_IN_USE flag?



Bjørn
--
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