Hello, I found a bug where the io_ti.c driver calls schedule while in an interrupt. The problem stems from usbserial.c allowing a serial_throttle call proceed while in an interrupt, which results in sending a urb to clear the CTS bit of the usb serial port, and thus a call to schedule.
To work around this, I added serial_throttle capabilities to the post_helper already found in the usbserial.c driver. Also, some of the addresses found in my stack trace are strange, that is because the device is being driven from a virtual machine running in user-mode, but the bug and fix where confirmed on a RedHat Workstation 3.0 host running the 2.4.21-20.EL kernel. I am using the usb-uhci.c driver and the io_ti.c usb serial port device. The patch is based on the redhat 2.4.21-20 source tree. This is my first patch, so please be gentle. --Sam --------------------------------- Sam King Ph.D. Student Computer Science Department University of Michigan, Ann Arbor Scheduling in interrupt Kernel panic: kernel BUG at sched.c:564! #0 panic (fmt=0xa026724e "kernel BUG at %s:%d!\n") at panic.c:88 #1 0xa00141e9 in schedule () at sched.c:564 #2 0xa001410b in schedule_timeout (timeout=52) at sched.c:451 #3 0xa01ba0b3 in usb_start_wait_urb (urb=0xaf1b13e0, timeout=52, #actual_length=0xa02a3420) at usb.c:1095 #4 0xa01ba1f6 in usb_internal_control_msg (usb_dev=0xaf892e00, #pipe=2147485184, cmd=0xaf0714a0, data=0x0, len=0, timeout=52) at usb.c:1140 #5 0xa01ba2be in usb_control_msg (dev=0xaf892e00, pipe=2147485184, #request=134 '\206', requesttype=64 '@', value=0, index=3, data=0x0, size=0, #timeout=52) at usb.c:1189 #6 0xa01caab1 in TISendVendorRequestSync (dev=0xaf892e00, request=134 #'\206', value=0, index=3, data=0x0, size=0) at usb.h:1014 #7 0xa01cab3c in TIWriteCommandSync (dev=0xaf892e00, command=134 #'\206', moduleid=3 '\003', value=0, data=0x0, size=0) at io_ti.c:238 #8 0xa01ccfbd in TIClearRts (port=0xaf302dc0) at io_ti.c:1430 #9 0xa01ce998 in edge_throttle (port=0xaf2be41c) at io_ti.c:2129 #10 0xa01c7a72 in serial_throttle (tty=0xaeeed000) at usbserial.c:691 #11 0xa016d52d in n_tty_receive_buf (tty=0xaeeed000, cp=0xaeeed364 'f' <repeats 17 times>, #"asdfasdfwegfasgfdsfgdsgasdf\n\rasdfasdfasd", 'f' <repeats 13 times>, #"asdfasdfwegfasgfdsfgdsgasdf\n\rasdfasdfasd", 'f' <repeats 18 times>, #fp=0xaeeed764 "", count=64) at n_tty.c:806 #12 0xa016ad51 in flush_to_ldisc (private_=0xaeeed000) at #tty_io.c:1977 #13 0xa016ae43 in tty_flip_buffer_push (tty=0xaeeed000) at #tty_io.c:2028 #14 0xa01cdae9 in edge_bulk_in_callback (urb=0xaf302b40) at #io_ti.c:1746 #15 0xa01c68ba in process_urb (s=0xaf8a8460, p=0xaf302b44) at #usb-uhci.c:2770 #16 0xa01c6a5b in uhci_interrupt (irq=18, __uhci=0xaf8a8460, #regs=0xa02a0274) at usb-uhci.c:2843 --- /usr/src/linux-2.4/drivers/usb/serial/usbserial.c 2004-08-18 20:30:12.000000000 -0400 +++ usbserial.c 2004-09-14 10:03:14.000000000 -0400 @@ -351,13 +351,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; @@ -368,11 +372,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); @@ -498,19 +503,26 @@ 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); + } up(&port->sem); spin_lock_irqsave(&post_lock, flags); @@ -719,7 +731,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); @@ -737,11 +749,11 @@ 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; @@ -764,27 +776,35 @@ 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; @@ -801,13 +821,15 @@ count = POST_BSIZE; job->len = count; - 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); @@ -884,29 +906,49 @@ return retval; } -static void serial_throttle (struct tty_struct * tty) +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; - - down (&port->sem); - + 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_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; + + 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_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. 13. 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