On Wed, Nov 12, 2003 at 05:09:10PM -0800, David Meggy wrote:
> This is on an Arm7 board, using the pl2303 module, using linux
> 2.4.21-rmk1. [...]
Please try the attached.
Float failed to provide a trace, so I am not sure if he/she
has the same problem, and if so, is the patch likely to help.
-- Pete
diff -ur linux-2.4.20-20.7.5/drivers/usb/serial/usbserial.c
linux-2.4.20-20.7.5-u1/drivers/usb/serial/usbserial.c
--- linux-2.4.20-20.7.5/drivers/usb/serial/usbserial.c 2003-11-17 22:30:34.000000000
-0500
+++ linux-2.4.20-20.7.5-u1/drivers/usb/serial/usbserial.c 2003-11-17
22:33:16.000000000 -0500
@@ -297,6 +297,7 @@
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
#include <linux/usb.h>
#ifdef CONFIG_USB_SERIAL_DEBUG
@@ -347,10 +348,24 @@
};
#endif
+/*
+ * The post kludge structures and variables.
+ */
+#define POST_BSIZE 100 /* little below 128 in total */
+struct usb_serial_post_job {
+ struct list_head link;
+ struct usb_serial_port *port;
+ int len;
+ char buff[POST_BSIZE];
+};
+static spinlock_t post_lock = SPIN_LOCK_UNLOCKED; /* Also covers ->ref */
+static struct list_head post_list = LIST_HEAD_INIT(post_list);
+static struct tq_struct post_task;
/* local function prototypes */
static int serial_open (struct tty_struct *tty, struct file * filp);
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_write_room (struct tty_struct *tty);
static int serial_chars_in_buffer (struct tty_struct *tty);
@@ -448,6 +463,53 @@
return;
}
+/*
+ * The post kludge.
+ *
+ * Our component drivers are hideously buggy and written by people
+ * who have difficulty understanding the concept of spinlocks.
+ * There were so many races and lockups that Greg K-H made a watershed
+ * decision to provide what is essentially a single-threaded sandbox
+ * for component drivers, protected by a semaphore. It helped a lot, but
+ * for one little problem: when tty->low_latency is set, line disciplines
+ * can call ->write from an interrupt, where the semaphore oopses.
+ *
+ * Rather than open the whole can of worms again, we just post writes
+ * into a helper which can sleep.
+ *
+ * Kernel 2.6 has a proper fix, reportedly.
+ *
+ * XXX Nothing prevents this from looping forever.
+ */
+static void post_helper(void *arg)
+{
+ struct usb_serial_post_job *job;
+ struct usb_serial_port *port;
+ struct usb_serial *serial;
+ unsigned int flags;
+
+ spin_lock_irqsave(&post_lock, flags);
+ while (!list_empty(&post_list)) {
+ job = list_entry(post_list.next, struct usb_serial_post_job, link);
+ list_del(&job->link);
+ spin_unlock_irqrestore(&post_lock, flags);
+
+ port = job->port;
+ serial = get_usb_serial (port, __FUNCTION__);
+
+ down(&port->sem);
+ if (port->tty != NULL)
+ __serial_write(port, 0, job->buff, job->len);
+ up(&port->sem);
+
+ kfree(job);
+ spin_lock_irqsave(&post_lock, flags);
+ if (--serial->ref == 0)
+ kfree(serial);
+ }
+ spin_unlock_irqrestore(&post_lock, flags);
+}
+
#ifdef USES_EZUSB_FUNCTIONS
/* EZ-USB Control and Status Register. Bit 0 controls 8051 reset */
#define CPUCS_REG 0x7F92
@@ -576,23 +638,21 @@
/* if disconnect beat us to the punch here, there's nothing to do */
if (tty->driver_data) {
+ /* post_helper(NULL); */ /* Correct, but unimportant for echo.*/
__serial_close(port, filp);
}
up (&port->sem);
}
-static int serial_write (struct tty_struct * tty, int from_user, const unsigned char
*buf, int count)
+static int __serial_write (struct usb_serial_port *port, int from_user, const
unsigned char *buf, int count)
{
- struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
int retval = -EINVAL;
if (!serial)
return -ENODEV;
- down (&port->sem);
-
dbg("%s - port %d, %d byte(s)", __FUNCTION__, port->number, count);
if (!port->open_count) {
@@ -607,10 +667,68 @@
retval = generic_write(port, from_user, buf, count);
exit:
- up (&port->sem);
return retval;
}
+static int serial_write (struct tty_struct * tty, int from_user, const unsigned char
*buf, int count)
+{
+ struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+ struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+ struct usb_serial_post_job *job;
+ unsigned long flags;
+ int rc;
+
+ if (!in_interrupt()) {
+ /*
+ * Run post_list to reduce a possiblity of reordered writes.
+ * Tasks can make keventd to sleep, sometimes for a long time.
+ */
+ post_helper(NULL);
+
+ down(&port->sem);
+ rc = __serial_write(port, from_user, buf, count);
+ up(&port->sem);
+ return rc;
+ }
+
+ if (from_user) {
+ /*
+ * This is a BUG-able offense because we cannot
+ * pagefault while in_interrupt, but we want to see
+ * something in dmesg rather than just blinking LEDs.
+ */
+ err("user data in interrupt write");
+ return -EINVAL;
+ }
+
+ job = kmalloc(sizeof(struct usb_serial_post_job), GFP_ATOMIC);
+ if (job == NULL)
+ return -ENOMEM;
+
+ job->port = port;
+ if ((job->len = count) >= POST_BSIZE) {
+ static int rate = 0;
+ /*
+ * Data loss due to extreme circumstances.
+ * It's a ususal thing on serial to lose characters, isn't it?
+ * 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;
+ }
+ memcpy(job->buff, buf, job->len);
+
+ 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
};
-------------------------------------------------------
This SF.net email is sponsored by: SF.net Giveback Program.
Does SourceForge.net help you be more productive? Does it
help you create better code? SHARE THE LOVE, and help us help
YOU! Click Here: http://sourceforge.net/donate/
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel