Bug#261706: usb-storage hangs all USB devices (2/3)
From
Horms@1:229/2 to
Matt Zimmerman on Thu Aug 12 05:30:08 2004
[continued from previous message]
+ spin_lock_irqsave(&post_lock, flags);
+ list_add_tail(&job->link, &post_list);
+ serial->ref++; /* Protect the port->sem from kfree() */
+ schedule_task(&post_task);
+ spin_unlock_irqrestore(&post_lock, flags);
+
+ return count;
+}
+
static int serial_write_room (struct tty_struct *tty)
{
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
@@ -620,6 +738,9 @@
if (!serial)
return -ENODEV;
+ if (in_interrupt())
+ return POST_BSIZE;
+
down (&port->sem);
dbg("%s - port %d", __FUNCTION__, port->number);
@@ -1128,6 +1249,7 @@
int num_ports;
int max_endpoints;
const struct usb_device_id *id_pattern = NULL;
+ unsigned long flags;
/* loop through our list of known serial converters, and see if this
device matches. */
@@ -1338,11 +1460,15 @@
init_MUTEX (&port->sem);
}
+ spin_lock_irqsave(&post_lock, flags);
+ serial->ref = 1;
+ spin_unlock_irqrestore(&post_lock, flags);
+
/* if this device type has a startup function, call it */
if (type->startup) {
i = type->startup (serial);
if (i < 0)
- goto probe_error;
+ goto startup_error;
if (i > 0)
return serial;
}
@@ -1357,6 +1483,12 @@
return serial; /* success */
+startup_error:
+ spin_lock_irqsave(&post_lock, flags);
+ if (serial->ref != 1) {
+ err("bug in component startup: ref %d\n", serial->ref);
+ }
+ spin_unlock_irqrestore(&post_lock, flags);
probe_error:
for (i = 0; i < num_bulk_in; ++i) {
port = &serial->port[i];
@@ -1392,6 +1524,7 @@
{
struct usb_serial *serial = (struct usb_serial *) ptr;
struct usb_serial_port *port;
+ unsigned long flags;
int i;
dbg ("%s", __FUNCTION__);
@@ -1452,7 +1585,10 @@
return_serial (serial);
/* free up any memory that we allocated */
- kfree (serial);
+ spin_lock_irqsave(&post_lock, flags);
+ if (--serial->ref == 0)
+ kfree(serial);
+ spin_unlock_irqrestore(&post_lock, flags);
} else {
info("device disconnected");
@@ -1504,6 +1640,7 @@
for (i = 0; i < SERIAL_TTY_MINORS; ++i) {
serial_table[i] = NULL;
}
+ post_task.routine = post_helper;
/* register the tty driver */
serial_tty_driver.init_termios = tty_std_termios;
diff -ur linux-2.4.20-20.7.5/drivers/usb/serial/usb-serial.h linux-2.4.20-20.7.5-u1/drivers/usb/serial/usb-serial.h
--- linux-2.4.20-20.7.5/drivers/usb/serial/usb-serial.h 2002-11-28 18:53:15.000000000 -0500
+++ linux-2.4.20-20.7.5-u1/drivers/usb/serial/usb-serial.h 2003-11-17 22:32:55.000000000 -0500
@@ -151,6 +151,9 @@
__u16 product;
struct usb_serial_port port[MAX_NUM_PORTS];
void * private;
+#ifndef __GENKSYMS__
+ int ref;
+#endif
};
diff -urN -X dontdiff linux-2.4.22-1.2176/drivers/usb/serial/usbserial.c linux-2.4.22-1.2176-u1/drivers/usb/serial/usbserial.c
--- linux-2.4.22-1.2176/drivers/usb/serial/usbserial.c 2004-03-11 20:53:43.000000000 -0800
+++ linux-2.4.22-1.2176-u1/drivers/usb/serial/usbserial.c 2004-03-23 11:07:12.000000000 -0800
@@ -367,6 +367,10 @@
static void serial_close (struct tty_struct *tty, struct file * filp);
static int __serial_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count);
+static int serial_post_job(struct u