Bug#261706: usb-storage hangs all USB devices (3/3)
From
Horms@1:229/2 to
Matt Zimmerman on Thu Aug 12 05:30:08 2004
[continued from previous message]
+ if (count >= 512) {
static int rate = 0;
/*
* Data loss due to extreme circumstances.
@@ -718,13 +761,61 @@
* Neener, neener! Actually, it's probably an echo loop anyway.
* Only happens when getty starts talking to Visor.
*/
- if (++rate % 1000 < 5)
- err("too much data (%d)", count);
- job->len = POST_BSIZE;
+ if (++rate % 1000 < 3) {
+ err("too much data (%d) from %s", count,
+ from_user? "user": "kernel");
+ }
+ count = 512;
+ }
+
+ while (done < count) {
+ length = count - done;
+ if (length > POST_BSIZE)
+ length = POST_BSIZE;
+ if (length > port->bulk_out_size)
+ length = port->bulk_out_size;
+
+ rc = serial_post_one(port, from_user, gfp, buf + done, length); + if (rc <= 0) {
+ if (done != 0)
+ return done;
+ return rc;
+ }
+ done += rc;
+ }
+
+ return done;
+}
+
+static int serial_post_one(struct usb_serial_port *port, int from_user,
+ int gfp, const unsigned char *buf, int count)
+{
+ struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+ struct usb_serial_post_job *job;
+ unsigned long flags;
+
+ dbg("%s - port %d user %d count %d", __FUNCTION__, port->number, from_user, count);
+
+ job = kmalloc(sizeof(struct usb_serial_post_job), gfp);
+ if (job == NULL)
+ return -ENOMEM;
+
+ job->port = port;
+ if (count >= POST_BSIZE)
+ count = POST_BSIZE;
+ job->len = count;
+
+ if (from_user) {
+ if (copy_from_user(job->buff, buf, count)) {
+ kfree(job);
+ return -EFAULT;
+ }
+ } else {
+ memcpy(job->buff, buf, count);
}
- memcpy(job->buff, buf, job->len);
spin_lock_irqsave(&post_lock, flags);
+ port->write_backlog += count;
list_add_tail(&job->link, &post_list);
serial->ref++; /* Protect the port->sem from kfree() */
schedule_task(&post_task);
@@ -742,8 +833,13 @@
if (!serial)
return -ENODEV;
- if (in_interrupt())
- return POST_BSIZE;
+ if (in_interrupt()) {
+ retval = 0;
+ if (!port->write_busy && port->write_backlog == 0)
+ retval = port->bulk_out_size;
+ dbg("%s - returns %d", __FUNCTION__, retval);
+ return retval;
+ }
down (&port->sem);
@@ -776,10 +872,8 @@
down (&port->sem);
- dbg("%s = port %d", __FUNCTION__, port->number);
-
if (!port->open_count) {
- dbg("%s - port not open", __FUNCTION__);
+ dbg("%s - port %d: not open", __FUNCTION__, port->number);
goto exit;
}
@@ -1038,18 +1132,23 @@
{
struct usb_serial *serial = port->serial;
int result;
-
- dbg("%s - port %d", __FUNCTION__, port->number);
+ unsigned long flags;
if (count == 0) {
dbg("%s - write request of 0 bytes", __FUNCTION__);
return (0);
}
+ if (count < 0) {
+ err("%s - port %d: write request of %d bytes", __FUNCTION__,
+ port->number, count);
+ return (0);
+ }
/* only do something if we have a bulk out endpoint */
if (serial->num_bulk_out) {
- if (port->write_urb->status == -EINPROGRESS) {
- dbg("%s - already writing", __FUNCTION__);
+ if (port->write_busy) {
+ /* Happens when two threads run port_helper. Watch. */ + info("%s - already writing", __FUNCTION__);
return (0);
}
@@ -1058,12 +1157,10 @@
if (from_user) {
if (copy_from_user(port->write_urb->transfer_buffer, buf, count))
return -EFAULT;
- }
- else {
+ } else {
memcpy (port->write_urb->transfer_buffer, buf, count);
}
-
- usb_serial_debug_data (__FILE__, __FUNCTION__, count, port->write_urb->transfer_buffer);
+ dbg("%s - port %d [%d]", __FUNCTION__, port->number, count);
/* set up our urb */
usb_fill_bulk_urb (port->write_urb, serial->dev,
@@ -1075,10 +1172,18 @@
generic_write_bulk_callback), port);
/* send the data out the bulk port */
+ port->write_busy = 1;
result = usb_submit_urb(port->write_urb);
- if (result)
- err("%s - failed submitting write urb, error %d", __FUNCTION__, result);
- else
+ if (result) {
+ err("%s - port %d: failed submitting write urb (%d)",
+ __FUNCTION__, port->number, result);
+ port->write_busy = 0;
+ spin_lock_irqsave(&post_lock, flags);
+ if (port->write_backlog != 0)
+ schedule_task(&post_task);
+ spin_unlock_irqrestore(&post_lock, flags);
+
+ } else
result = count;
return result;
@@ -1093,14 +1198,12 @@
struct usb_serial *serial = port->serial;
int room = 0;
- dbg("%s - port %d", __FUNCTION__, port->number);
-
if (serial->num_bulk_out) {
- if (port->write_urb->status != -EINPROGRESS)
+ if (!port->write_busy && port->write_backlog == 0)
room = port->bulk_out_size;
}
- dbg("%s - returns %d", __FUNCTION__, room);
+ dbg("%s - port %d, returns %d", __FUNCTION__, port->number, room);
return (room);
}
@@ -1112,8 +1215,9 @@
dbg("%s - port %d", __FUNCTION__, port->number);
if (serial->num_bulk_out) {
- if (port->write_urb->status == -EINPROGRESS)
- chars = port->write_urb->transfer_buffer_length;
+ if (port->write_busy)
+ chars += port->write_urb->transfer_buffer_length;
+ chars += port->write_backlog; /* spin_lock... Baah */
}
dbg("%s - returns %d", __FUNCTION__, chars);
@@ -1177,14 +1281,16 @@
dbg("%s - port %d", __FUNCTION__, port->number);
+ port->write_busy = 0;
+ wmb();
+
if (!serial) {
- dbg("%s - bad serial pointer, exiting", __FUNCTION__);
+ err("%s - null serial pointer, exiting", __FUNCTION__);
return;
}
if (urb->status) {
dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
- return;
}
queue_task(&port->tqueue, &tq_immediate);
@@ -1210,12 +1316,18 @@
struct usb_serial_port *port = (struct usb_serial_port *)private;
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
struct tty_struct *tty;
+ unsigned long flags;
dbg("%s - port %d", __FUNCTION__, port->number);
if (!serial)
return;
+ spin_lock_irqsave(&post_lock, flags);
+ if (port->write_backlog != 0)
+ schedule_task(&post_task);
+ spin_unlock_irqrestore(&post_lock, flags);
+
tty = port->tty;
if (!tty)
return;
diff -urN -X dontdiff linux-2.4.22-1.2176/drivers/usb/serial/usb-serial.h linux-2.4.22-1.2176-u1/drivers/usb/serial/usb-serial.h
--- linux-2.4.22-1.2176/drivers/usb/serial/usb-serial.h 2004-03-11 20:53:43.000000000 -0800
+++ linux-2.4.22-1.2176-u1/drivers/usb/serial/usb-serial.h 2004-03-23 11:07:12.000000000 -0800
@@ -111,6 +111,8 @@
int bulk_out_size;
struct urb * write_urb;
__u8 bulk_out_endpointAddress;
+ char write_busy; /* URB is active */
+ int write_backlog; /* Fifo used */
wait_queue_head_t write_wait;
struct tq_struct tqueue;
--- SoupGate-Win32 v1.05
* Origin: you cannot sedate... all the things you hate (1:229/2)