Here is a patch to fix the problem of a down function call being made
from an interrupt handler.

In the 2.4 kernel, usbserial.c uses semaphores to correct for improper
locking.  However, the serial_write function can be called from an
interrupt handler, which causes problems when the down function call
leads to a schedule call.  To fix this, a kernel task is used to defer
handling until after the interrupt service routine completes.

There is a similar problem with serial_throttle.  When the tty buffer
fills, it calls serial_throttle to notify the low level driver of the
condition.  This can happen from an interrupt handler (as is the case
with the edgeport usb serial converter io_ti.c and uhci.c host
controller), so I augmented the post_helper kernel task to handle
serial_throttle calls also.

This is not a problem in the 2.6 kernel since the usbserial.c
semaphore is no longer used.

As a side effect, it also fixes the problem of io_ti.c making a call
to schedule when handling a throttle function call.

Below is a patch against the 2.4.27 kernel.

--Sam

---------------------------------
Sam King
Ph.D. Student
Computer Science Department
University of Michigan, Ann Arbor


Signed-off-by: Sam King <[EMAIL PROTECTED]>

--- drivers/usb/serial/usbserial.orig   2004-09-28 10:21:54.000000000 -0400
+++ drivers/usb/serial/usbserial.c      2004-09-28 11:32:40.000000000 -0400
@@ -350,13 +350,17 @@
 /*
  * The post kludge structures and variables.
  */
-#define POST_BSIZE     100     /* little below 128 in total */
+#define POST_JOB_WRITE    0
+#define POST_JOB_THROTTLE 1
+#define POST_BSIZE       100   /* little below 128 in total */
 struct usb_serial_post_job {
        struct list_head link;
+        int type;
        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;
@@ -367,11 +371,12 @@
 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 usb_serial_port *port, int from_user,
-    int gfp, const unsigned char *buf, int count);
+    int gfp, const unsigned char *buf, int count, int type);
 static int  serial_post_one(struct usb_serial_port *port, int from_user,
-    int gfp, const unsigned char *buf, int count);
+    int gfp, const unsigned char *buf, int count, int type);
 static int  serial_write_room (struct tty_struct *tty);
 static int  serial_chars_in_buffer (struct tty_struct *tty);
+static void __serial_throttle (struct tty_struct * tty);
 static void serial_throttle (struct tty_struct * tty);
 static void serial_unthrottle (struct tty_struct * tty);
 static int  serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int 
cmd, unsigned long arg);
@@ -497,19 +502,28 @@
                port = job->port;
                /* get_usb_serial checks port->tty, so cannot be used */
                serial = port->serial;
-               if (port->write_busy) {
-                       dbg("%s - port %d busy", __FUNCTION__, port->number);
-                       pos = pos->next;
-                       continue;
+               if(job->type == POST_JOB_WRITE) {
+                       if (port->write_busy) {
+                               dbg("%s - port %d busy", __FUNCTION__, port->number);
+                               pos = pos->next;
+                               continue;
+                       }
                }
                list_del(&job->link);
                spin_unlock_irqrestore(&post_lock, flags);

                down(&port->sem);
-               dbg("%s - port %d len %d backlog %d", __FUNCTION__,
-                   port->number, job->len, port->write_backlog);
-               if (port->tty != NULL)
-                       __serial_write(port, 0, job->buff, job->len);
+               if(job->type == POST_JOB_WRITE) {
+                       dbg("%s - port %d len %d backlog %d", __FUNCTION__,
+                           port->number, job->len, port->write_backlog);
+                       if (port->tty != NULL)
+                               __serial_write(port, 0, job->buff, job->len);
+               } else if(job->type == POST_JOB_THROTTLE) {
+                       if(port->tty != NULL)
+                               __serial_throttle(port->tty);
+               } else {
+                       BUG();
+               }
                up(&port->sem);

                spin_lock_irqsave(&post_lock, flags);
@@ -722,7 +736,7 @@
                if (port->write_busy) {
                        up(&port->sem);
                        return serial_post_job(port, from_user, GFP_KERNEL,
-                           buf, count);
+                           buf, count, POST_JOB_WRITE);
                }

                rc = __serial_write(port, from_user, buf, count);
@@ -740,11 +754,12 @@
                return -EINVAL;
        }

-       return serial_post_job(port, 0, GFP_ATOMIC, buf, count);
+       return serial_post_job(port, 0, GFP_ATOMIC, buf, count,
+                              POST_JOB_WRITE);
 }

 static int serial_post_job(struct usb_serial_port *port, int from_user,
-    int gfp, const unsigned char *buf, int count)
+    int gfp, const unsigned char *buf, int count, int type)
 {
        int done = 0, length;
        int rc;
@@ -767,27 +782,34 @@
                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;
+       if(type == POST_JOB_WRITE) {
+               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, type);
+                       if (rc <= 0) {
+                               if (done != 0)
+                                       return done;
+                               return rc;
+                       }
+                       done += rc;
                }
-               done += rc;
+       } else if(type == POST_JOB_THROTTLE) {
+               serial_post_one(port, from_user, gfp, buf, count, type);
+       } else {
+               BUG();
        }

        return done;
 }

 static int serial_post_one(struct usb_serial_port *port, int from_user,
-    int gfp, const unsigned char *buf, int count)
+    int gfp, const unsigned char *buf, int count, int type)
 {
        struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
        struct usb_serial_post_job *job;
@@ -803,14 +825,17 @@
        if (count >= POST_BSIZE)
                count = POST_BSIZE;
        job->len = count;
+       job->type = type;

-       if (from_user) {
-               if (copy_from_user(job->buff, buf, count)) {
-                       kfree(job);
-                       return -EFAULT;
+       if(type == POST_JOB_WRITE) {
+               if (from_user) {
+                       if (copy_from_user(job->buff, buf, count)) {
+                               kfree(job);
+                               return -EFAULT;
+                       }
+               } else {
+                       memcpy(job->buff, buf, count);
                }
-       } else {
-               memcpy(job->buff, buf, count);
        }

        spin_lock_irqsave(&post_lock, flags);
@@ -895,21 +920,36 @@
        if (!serial)
                return;

-       down (&port->sem);
+       if(!in_interrupt()) {
+               post_helper(NULL);
+
+               down(&port->sem);
+               __serial_throttle(tty);
+               up (&port->sem);
+               return;
+       }
+
+       serial_post_job(port, 0, GFP_ATOMIC, NULL, 0, POST_JOB_THROTTLE);
+}
+
+static void __serial_throttle (struct tty_struct * tty)
+{
+       struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+       struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+
+       if (!serial)
+               return;

        dbg("%s - port %d", __FUNCTION__, port->number);

        if (!port->open_count) {
                dbg ("%s - port not open", __FUNCTION__);
-               goto exit;
+               return;
        }

        /* pass on to the driver specific version of this function */
        if (serial->type->throttle)
                serial->type->throttle(port);
-
-exit:
-       up (&port->sem);
 }

 static void serial_unthrottle (struct tty_struct * tty)




-------------------------------------------------------
This SF.Net email is sponsored by: YOU BE THE JUDGE. Be one of 170
Project Admins to receive an Apple iPod Mini FREE for your judgement on
who ports your project to Linux PPC the best. Sponsored by IBM.
Deadline: Sept. 24. Go here: http://sf.net/ppc_contest.php
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to