<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">
From: David Brownell &lt;david-b@pacbell.net&gt;

Fixes "Badness in local_bh_enable at kernel/softirq.c:121"

This driver calls ppp rx functions in hard irq context, which is deadlocky. 
Those functions expect to run in softirq context.



 25-akpm/drivers/usb/class/cdc-acm.c |   42 +++++++++++++++++++++++++-----------
 1 files changed, 30 insertions(+), 12 deletions(-)

diff -puN drivers/usb/class/cdc-acm.c~cdc-acm-softirq-rx drivers/usb/class/cdc-acm.c
--- 25/drivers/usb/class/cdc-acm.c~cdc-acm-softirq-rx	Fri Oct 31 13:54:23 2003
+++ 25-akpm/drivers/usb/class/cdc-acm.c	Fri Oct 31 13:54:23 2003
@@ -1,5 +1,5 @@
 /*
- * acm.c  Version 0.22
+ * cdc-acm.c
  *
  * Copyright (c) 1999 Armin Fuerst	&lt;fuerst@in.tum.de&gt;
  * Copyright (c) 1999 Pavel Machek	&lt;pavel@suse.cz&gt;
@@ -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 &lt;linux/kernel.h&gt;
 #include &lt;linux/errno.h&gt;
 #include &lt;linux/init.h&gt;
@@ -54,14 +57,13 @@
 #include &lt;linux/module.h&gt;
 #include &lt;linux/smp_lock.h&gt;
 #include &lt;asm/uaccess.h&gt;
-#undef DEBUG
 #include &lt;linux/usb.h&gt;
 #include &lt;asm/byteorder.h&gt;
 
 /*
  * 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 acm {
 	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 @@ static int acm_ctrl_msg(struct acm *acm,
 #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-&gt;context;
@@ -251,20 +255,30 @@ exit:
 		     __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-&gt;context;
-	struct tty_struct *tty = acm-&gt;tty;
-	unsigned char *data = urb-&gt;transfer_buffer;
-	int i = 0;
 
 	if (!ACM_READY(acm))
 		return;
 
 	if (urb-&gt;status)
-		dbg("nonzero read bulk status received: %d", urb-&gt;status);
+		dev_dbg(&amp;acm-&gt;data-&gt;dev, "bulk rx status %d\n", urb-&gt;status);
+
+	/* calling tty_flip_buffer_push() in_irq() isn't allowed */
+	tasklet_schedule(&amp;acm-&gt;bh);
+}
+
+static void acm_rx_tasklet(unsigned long _acm)
+{
+	struct acm *acm = (void *)_acm;
+	struct urb *urb = acm-&gt;readurb;
+	struct tty_struct *tty = acm-&gt;tty;
+	unsigned char *data = urb-&gt;transfer_buffer;
+	int i = 0;
 
-	if (!urb-&gt;status &amp;&amp; !acm-&gt;throttle)  {
+	if (urb-&gt;actual_length &gt; 0 &amp;&amp; !acm-&gt;throttle)  {
 		for (i = 0; i &lt; urb-&gt;actual_length &amp;&amp; !acm-&gt;throttle; i++) {
 			/* if we insert more than TTY_FLIPBUF_SIZE characters,
 			 * we drop them. */
@@ -285,10 +299,12 @@ static void acm_read_bulk(struct urb *ur
 	urb-&gt;actual_length = 0;
 	urb-&gt;dev = acm-&gt;dev;
 
-	if (usb_submit_urb(urb, GFP_ATOMIC))
-		dbg("failed resubmitting read urb");
+	i = usb_submit_urb(urb, GFP_ATOMIC);
+	if (i)
+		dev_dbg(&amp;acm-&gt;data-&gt;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-&gt;context;
@@ -621,6 +637,8 @@ static int acm_probe (struct usb_interfa
 			acm-&gt;minor = minor;
 			acm-&gt;dev = dev;
 
+			acm-&gt;bh.func = acm_rx_tasklet;
+			acm-&gt;bh.data = (unsigned long) acm;
 			INIT_WORK(&amp;acm-&gt;work, acm_softint, acm);
 
 			if (!(buf = kmalloc(ctrlsize + readsize + acm-&gt;writesize, GFP_KERNEL))) {

_
</pre></body></html>