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

usb: cdc-acm: drain writes on close

Add a mechanism to let the write queue drain naturally before
closing the TTY, rather than always losing that data. There
is a timeout, so it can't wait too long.

Provide missing locking inside acm_wb_is_avail(); it matters
more now. Note, this presumes an earlier patch was applied,
removing a call to this routine where the lock was held.

Slightly improved diagnostics on write URB completion, so we
can tell when a write URB gets killed and, if so, how much
data it wrote first ... and so that I/O path is normally
silent (and can't much change timings).

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

David Brownell and committed by
Greg Kroah-Hartman
e5fbab51 934da463

+35 -5
+34 -5
drivers/usb/class/cdc-acm.c
··· 51 51 */ 52 52 53 53 #undef DEBUG 54 + #undef VERBOSE_DEBUG 54 55 55 56 #include <linux/kernel.h> 56 57 #include <linux/errno.h> ··· 71 70 72 71 #include "cdc-acm.h" 73 72 73 + 74 + #define ACM_CLOSE_TIMEOUT 15 /* seconds to let writes drain */ 75 + 74 76 /* 75 77 * Version Information 76 78 */ ··· 88 84 static DEFINE_MUTEX(open_mutex); 89 85 90 86 #define ACM_READY(acm) (acm && acm->dev && acm->used) 87 + 88 + #ifdef VERBOSE_DEBUG 89 + #define verbose 1 90 + #else 91 + #define verbose 0 92 + #endif 91 93 92 94 /* 93 95 * Functions for ACM control messages. ··· 146 136 static int acm_wb_is_avail(struct acm *acm) 147 137 { 148 138 int i, n; 139 + unsigned long flags; 149 140 150 141 n = ACM_NW; 142 + spin_lock_irqsave(&acm->write_lock, flags); 151 143 for (i = 0; i < ACM_NW; i++) { 152 144 n -= acm->wb[i].use; 153 145 } 146 + spin_unlock_irqrestore(&acm->write_lock, flags); 154 147 return n; 155 148 } 156 149 ··· 480 467 /* data interface wrote those outgoing bytes */ 481 468 static void acm_write_bulk(struct urb *urb) 482 469 { 483 - struct acm *acm; 484 470 struct acm_wb *wb = urb->context; 471 + struct acm *acm = wb->instance; 485 472 486 - dbg("Entering acm_write_bulk with status %d", urb->status); 473 + if (verbose || urb->status 474 + || (urb->actual_length != urb->transfer_buffer_length)) 475 + dev_dbg(&acm->data->dev, "tx %d/%d bytes -- > %d\n", 476 + urb->actual_length, 477 + urb->transfer_buffer_length, 478 + urb->status); 487 479 488 - acm = wb->instance; 489 480 acm_write_done(acm, wb); 490 481 if (ACM_READY(acm)) 491 482 schedule_work(&acm->work); 483 + else 484 + wake_up_interruptible(&acm->drain_wait); 492 485 } 493 486 494 487 static void acm_softint(struct work_struct *work) 495 488 { 496 489 struct acm *acm = container_of(work, struct acm, work); 497 - dbg("Entering acm_softint."); 498 - 490 + 491 + dev_vdbg(&acm->data->dev, "tx work\n"); 499 492 if (!ACM_READY(acm)) 500 493 return; 501 494 tty_wakeup(acm->tty); ··· 622 603 kfree(acm); 623 604 } 624 605 606 + static int acm_tty_chars_in_buffer(struct tty_struct *tty); 607 + 625 608 static void acm_tty_close(struct tty_struct *tty, struct file *filp) 626 609 { 627 610 struct acm *acm = tty->driver_data; ··· 638 617 if (acm->dev) { 639 618 usb_autopm_get_interface(acm->control); 640 619 acm_set_control(acm, acm->ctrlout = 0); 620 + 621 + /* try letting the last writes drain naturally */ 622 + wait_event_interruptible_timeout(acm->drain_wait, 623 + (ACM_NW == acm_wb_is_avail(acm)) 624 + || !acm->dev, 625 + ACM_CLOSE_TIMEOUT * HZ); 626 + 641 627 usb_kill_urb(acm->ctrlurb); 642 628 for (i = 0; i < ACM_NW; i++) 643 629 usb_kill_urb(acm->wb[i].urb); ··· 1075 1047 acm->urb_task.data = (unsigned long) acm; 1076 1048 INIT_WORK(&acm->work, acm_softint); 1077 1049 INIT_WORK(&acm->waker, acm_waker); 1050 + init_waitqueue_head(&acm->drain_wait); 1078 1051 spin_lock_init(&acm->throttle_lock); 1079 1052 spin_lock_init(&acm->write_lock); 1080 1053 spin_lock_init(&acm->read_lock);
+1
drivers/usb/class/cdc-acm.h
··· 113 113 struct usb_cdc_line_coding line; /* bits, stop, parity */ 114 114 struct work_struct work; /* work queue entry for line discipline waking up */ 115 115 struct work_struct waker; 116 + wait_queue_head_t drain_wait; /* close processing */ 116 117 struct tasklet_struct urb_task; /* rx processing */ 117 118 spinlock_t throttle_lock; /* synchronize throtteling and read callback */ 118 119 unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */