This patch fixes a WARN_ON triggered by cdc-acm, over about
the past four months or so of kernels.  Sample call stack:

   >> [<c012046c>] local_bh_enable+0x8c/0x90
   >> [<f8991452>] ppp_asynctty_receive+0x62/0xb0 [ppp_async]
   >> [<c02144f3>] flush_to_ldisc+0xa3/0x120
   >> [<f891f20f>] acm_read_bulk+0xbf/0x140 [cdc_acm]
   >> [<c02684c9>] usb_hcd_giveback_urb+0x29/0x50
   >> [<c027670c>] dl_done_list+0x11c/0x130
   >> [<c0277075>] ohci_irq+0x85/0x170
   >> [<c0268526>] usb_hcd_irq+0x36/0x60
   >> [<c010aeba>] handle_IRQ_event+0x3a/0x70
   >> [<c010b227>] do_IRQ+0x97/0x140
   >> [<c0109624>] common_interrupt+0x18/0x20

Similar warnings may have been reported by usb-serial devices
that are accumulating much data.

- Dave



--- 1.51/drivers/usb/class/cdc-acm.c    Sun Oct 19 00:37:15 2003
+++ edited/drivers/usb/class/cdc-acm.c  Fri Oct 31 10:21:54 2003
@@ -1,5 +1,5 @@
 /*
- * acm.c  Version 0.22
+ * cdc-acm.c
  *
  * Copyright (c) 1999 Armin Fuerst     <[EMAIL PROTECTED]>
  * Copyright (c) 1999 Pavel Machek     <[EMAIL PROTECTED]>
@@ -26,6 +26,7 @@
  *     v0.21 - revert to probing on device for devices with multiple configs
  *     v0.22 - probe only the control interface. if usbcore doesn't choose the
  *             config we want, sysadmin changes bConfigurationValue in sysfs.
+ *     v0.23 - use softirq for rx processing, as needed by tty layer
  */
 
 /*
@@ -44,6 +45,8 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+#undef DEBUG
+
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/init.h>
@@ -54,14 +57,13 @@
 #include <linux/module.h>
 #include <linux/smp_lock.h>
 #include <asm/uaccess.h>
-#undef DEBUG
 #include <linux/usb.h>
 #include <asm/byteorder.h>
 
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v0.21"
+#define DRIVER_VERSION "v0.23"
 #define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik"
 #define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN 
adapters"
 
@@ -146,7 +148,8 @@
        struct tty_struct *tty;                         /* the corresponding tty */
        struct urb *ctrlurb, *readurb, *writeurb;       /* urbs */
        struct acm_line line;                           /* line coding (bits, stop, 
parity) */
-       struct work_struct work;                                        /* work queue 
entry for line discipline waking up */
+       struct work_struct work;                        /* work queue entry for line 
discipline waking up */
+       struct tasklet_struct bh;                       /* rx processing */
        unsigned int ctrlin;                            /* input control lines (DCD, 
DSR, RI, break, overruns) */
        unsigned int ctrlout;                           /* output control lines (DTR, 
RTS) */
        unsigned int writesize;                         /* max packet size for the 
output bulk endpoint */
@@ -184,9 +187,10 @@
 #define acm_send_break(acm, ms)                acm_ctrl_msg(acm, ACM_REQ_SEND_BREAK, 
ms, NULL, 0)
 
 /*
- * Interrupt handler for various ACM control events
+ * Interrupt handlers for various ACM device responses
  */
 
+/* control interface reports status changes with "interrupt" transfers */
 static void acm_ctrl_irq(struct urb *urb, struct pt_regs *regs)
 {
        struct acm *acm = urb->context;
@@ -251,20 +255,30 @@
                     __FUNCTION__, status);
 }
 
+/* data interface returns incoming bytes, or we got unthrottled */
 static void acm_read_bulk(struct urb *urb, struct pt_regs *regs)
 {
        struct acm *acm = urb->context;
-       struct tty_struct *tty = acm->tty;
-       unsigned char *data = urb->transfer_buffer;
-       int i = 0;
 
        if (!ACM_READY(acm))
                return;
 
        if (urb->status)
-               dbg("nonzero read bulk status received: %d", urb->status);
+               dev_dbg(&acm->data->dev, "bulk rx status %d\n", urb->status);
+
+       /* calling tty_flip_buffer_push() in_irq() isn't allowed */
+       tasklet_schedule(&acm->bh);
+}
+
+static void acm_rx_tasklet(unsigned long _acm)
+{
+       struct acm *acm = (void *)_acm;
+       struct urb *urb = acm->readurb;
+       struct tty_struct *tty = acm->tty;
+       unsigned char *data = urb->transfer_buffer;
+       int i = 0;
 
-       if (!urb->status && !acm->throttle)  {
+       if (urb->actual_length > 0 && !acm->throttle)  {
                for (i = 0; i < urb->actual_length && !acm->throttle; i++) {
                        /* if we insert more than TTY_FLIPBUF_SIZE characters,
                         * we drop them. */
@@ -285,10 +299,12 @@
        urb->actual_length = 0;
        urb->dev = acm->dev;
 
-       if (usb_submit_urb(urb, GFP_ATOMIC))
-               dbg("failed resubmitting read urb");
+       i = usb_submit_urb(urb, GFP_ATOMIC);
+       if (i)
+               dev_dbg(&acm->data->dev, "bulk rx resubmit %d\n", i);
 }
 
+/* data interface wrote those outgoing bytes */
 static void acm_write_bulk(struct urb *urb, struct pt_regs *regs)
 {
        struct acm *acm = (struct acm *)urb->context;
@@ -621,6 +637,8 @@
                        acm->minor = minor;
                        acm->dev = dev;
 
+                       acm->bh.func = acm_rx_tasklet;
+                       acm->bh.data = (unsigned long) acm;
                        INIT_WORK(&acm->work, acm_softint, acm);
 
                        if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, 
GFP_KERNEL))) {


Reply via email to