[PATCH] USB: serial: Fix atomic schedule in u_serial.c

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi all:
      When tty->low_latency = 1, tty_flip_buffer_push() may sleep in
tasklet context which will lead to a kernel crash.I think we should
use workqueue instead of tasklet in gs_rx_push() in u_serial.c. Here
is the patch. It works fine.

--- a/drivers/usb/gadget/u_serial.c	2010-01-19 02:23:35.000000000 +0800
+++ b/drivers/usb/gadget/u_serial.c	2010-01-20 14:42:17.000000000 +0800
@@ -105,7 +105,7 @@ struct gs_port {
 	struct list_head	read_pool;
 	struct list_head	read_queue;
 	unsigned		n_read;
-	struct tasklet_struct	push;
+	struct work_struct	push;

 	struct list_head	write_pool;
 	struct gs_buf		port_write_buf;
@@ -464,9 +464,9 @@ __acquires(&port->port_lock)
  * So QUEUE_SIZE packets plus however many the FIFO holds (usually two)
  * can be buffered before the TTY layer's buffers (currently 64 KB).
  */
-static void gs_rx_push(unsigned long _port)
+static void gs_rx_push(struct work_struct *work)
 {
-	struct gs_port		*port = (void *)_port;
+	struct gs_port		*port = container_of(work, struct gs_port, push);
 	struct tty_struct	*tty;
 	struct list_head	*queue = &port->read_queue;
 	bool			disconnect = false;
@@ -560,7 +560,7 @@ recycle:
 	if (!list_empty(queue) && tty) {
 		if (!test_bit(TTY_THROTTLED, &tty->flags)) {
 			if (do_push)
-				tasklet_schedule(&port->push);
+				schedule_work(&port->push);
 			else
 				pr_warning(PREFIX "%d: RX not scheduled?\n",
 					port->port_num);
@@ -581,7 +581,7 @@ static void gs_read_complete(struct usb_
 	/* Queue all received data until the tty layer is ready for it. */
 	spin_lock(&port->port_lock);
 	list_add_tail(&req->list, &port->read_queue);
-	tasklet_schedule(&port->push);
+	schedule_work(&port->push);
 	spin_unlock(&port->port_lock);
 }

@@ -975,7 +975,7 @@ static void gs_unthrottle(struct tty_str
 		 * rts/cts, or other handshaking with the host, but if the
 		 * read queue backs up enough we'll be NAKing OUT packets.
 		 */
-		tasklet_schedule(&port->push);
+		schedule_work(&port->push);
 		pr_vdebug(PREFIX "%d: unthrottle\n", port->port_num);
 	}
 	spin_unlock_irqrestore(&port->port_lock, flags);
@@ -1028,7 +1028,7 @@ gs_port_alloc(unsigned port_num, struct
 	init_waitqueue_head(&port->close_wait);
 	init_waitqueue_head(&port->drain_wait);

-	tasklet_init(&port->push, gs_rx_push, (unsigned long) port);
+	INIT_WORK(&port->push, gs_rx_push);

 	INIT_LIST_HEAD(&port->read_pool);
 	INIT_LIST_HEAD(&port->read_queue);
@@ -1182,7 +1182,7 @@ void gserial_cleanup(void)
 		ports[i].port = NULL;
 		mutex_unlock(&ports[i].lock);

-		tasklet_kill(&port->push);
+		flush_scheduled_work();

 		/* wait for old opens to finish */
 		wait_event(port->close_wait, gs_closed(port));
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux