Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: gadget: u_serial: process RX in workqueue instead of tasklet

Switch RX processing from tasklet to (delayed) work queue. This allows
receiver more room to process incoming data and prevents flood of
"ttyGS0: RX not scheduled?" messages on HS receive on slow CPU.

A side effect is 2.4MB/s zmodem transfer speed (up from 1.8MB/s)
on my test board.

Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>

authored by

Michał Mirosław and committed by
Felipe Balbi
8b4c62ae 539cf103

+14 -21
+14 -21
drivers/usb/gadget/function/u_serial.c
··· 16 16 17 17 #include <linux/kernel.h> 18 18 #include <linux/sched.h> 19 - #include <linux/interrupt.h> 20 19 #include <linux/device.h> 21 20 #include <linux/delay.h> 22 21 #include <linux/tty.h> ··· 25 26 #include <linux/module.h> 26 27 #include <linux/console.h> 27 28 #include <linux/kthread.h> 29 + #include <linux/workqueue.h> 28 30 #include <linux/kfifo.h> 29 31 30 32 #include "u_serial.h" ··· 110 110 int read_allocated; 111 111 struct list_head read_queue; 112 112 unsigned n_read; 113 - struct tasklet_struct push; 113 + struct delayed_work push; 114 114 115 115 struct list_head write_pool; 116 116 int write_started; ··· 352 352 * So QUEUE_SIZE packets plus however many the FIFO holds (usually two) 353 353 * can be buffered before the TTY layer's buffers (currently 64 KB). 354 354 */ 355 - static void gs_rx_push(unsigned long _port) 355 + static void gs_rx_push(struct work_struct *work) 356 356 { 357 - struct gs_port *port = (void *)_port; 357 + struct delayed_work *w = to_delayed_work(work); 358 + struct gs_port *port = container_of(w, struct gs_port, push); 358 359 struct tty_struct *tty; 359 360 struct list_head *queue = &port->read_queue; 360 361 bool disconnect = false; ··· 430 429 431 430 /* We want our data queue to become empty ASAP, keeping data 432 431 * in the tty and ldisc (not here). If we couldn't push any 433 - * this time around, there may be trouble unless there's an 434 - * implicit tty_unthrottle() call on its way... 432 + * this time around, RX may be starved, so wait until next jiffy. 435 433 * 436 - * REVISIT we should probably add a timer to keep the tasklet 437 - * from starving ... but it's not clear that case ever happens. 434 + * We may leave non-empty queue only when there is a tty, and 435 + * either it is throttled or there is no more room in flip buffer. 438 436 */ 439 - if (!list_empty(queue) && tty) { 440 - if (!tty_throttled(tty)) { 441 - if (do_push) 442 - tasklet_schedule(&port->push); 443 - else 444 - pr_warn("ttyGS%d: RX not scheduled?\n", 445 - port->port_num); 446 - } 447 - } 437 + if (!list_empty(queue) && !tty_throttled(tty)) 438 + schedule_delayed_work(&port->push, 1); 448 439 449 440 /* If we're still connected, refill the USB RX queue. */ 450 441 if (!disconnect && port->port_usb) ··· 452 459 /* Queue all received data until the tty layer is ready for it. */ 453 460 spin_lock(&port->port_lock); 454 461 list_add_tail(&req->list, &port->read_queue); 455 - tasklet_schedule(&port->push); 462 + schedule_delayed_work(&port->push, 0); 456 463 spin_unlock(&port->port_lock); 457 464 } 458 465 ··· 847 854 * rts/cts, or other handshaking with the host, but if the 848 855 * read queue backs up enough we'll be NAKing OUT packets. 849 856 */ 850 - tasklet_schedule(&port->push); 851 857 pr_vdebug("ttyGS%d: unthrottle\n", port->port_num); 858 + schedule_delayed_work(&port->push, 0); 852 859 } 853 860 spin_unlock_irqrestore(&port->port_lock, flags); 854 861 } ··· 1152 1159 init_waitqueue_head(&port->drain_wait); 1153 1160 init_waitqueue_head(&port->close_wait); 1154 1161 1155 - tasklet_init(&port->push, gs_rx_push, (unsigned long) port); 1162 + INIT_DELAYED_WORK(&port->push, gs_rx_push); 1156 1163 1157 1164 INIT_LIST_HEAD(&port->read_pool); 1158 1165 INIT_LIST_HEAD(&port->read_queue); ··· 1179 1186 1180 1187 static void gserial_free_port(struct gs_port *port) 1181 1188 { 1182 - tasklet_kill(&port->push); 1189 + cancel_delayed_work_sync(&port->push); 1183 1190 /* wait for old opens to finish */ 1184 1191 wait_event(port->close_wait, gs_closed(port)); 1185 1192 WARN_ON(port->port_usb != NULL);