Skip to content

Commit e5fbab5

Browse files
David Brownellgregkh
authored andcommitted
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>
1 parent 934da46 commit e5fbab5

File tree

2 files changed

+35
-5
lines changed

2 files changed

+35
-5
lines changed

drivers/usb/class/cdc-acm.c

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
*/
5252

5353
#undef DEBUG
54+
#undef VERBOSE_DEBUG
5455

5556
#include <linux/kernel.h>
5657
#include <linux/errno.h>
@@ -70,6 +71,9 @@
7071

7172
#include "cdc-acm.h"
7273

74+
75+
#define ACM_CLOSE_TIMEOUT 15 /* seconds to let writes drain */
76+
7377
/*
7478
* Version Information
7579
*/
@@ -85,6 +89,12 @@ static DEFINE_MUTEX(open_mutex);
8589

8690
#define ACM_READY(acm) (acm && acm->dev && acm->used)
8791

92+
#ifdef VERBOSE_DEBUG
93+
#define verbose 1
94+
#else
95+
#define verbose 0
96+
#endif
97+
8898
/*
8999
* Functions for ACM control messages.
90100
*/
@@ -136,11 +146,14 @@ static int acm_wb_alloc(struct acm *acm)
136146
static int acm_wb_is_avail(struct acm *acm)
137147
{
138148
int i, n;
149+
unsigned long flags;
139150

140151
n = ACM_NW;
152+
spin_lock_irqsave(&acm->write_lock, flags);
141153
for (i = 0; i < ACM_NW; i++) {
142154
n -= acm->wb[i].use;
143155
}
156+
spin_unlock_irqrestore(&acm->write_lock, flags);
144157
return n;
145158
}
146159

@@ -467,22 +480,28 @@ static void acm_rx_tasklet(unsigned long _acm)
467480
/* data interface wrote those outgoing bytes */
468481
static void acm_write_bulk(struct urb *urb)
469482
{
470-
struct acm *acm;
471483
struct acm_wb *wb = urb->context;
484+
struct acm *acm = wb->instance;
472485

473-
dbg("Entering acm_write_bulk with status %d", urb->status);
486+
if (verbose || urb->status
487+
|| (urb->actual_length != urb->transfer_buffer_length))
488+
dev_dbg(&acm->data->dev, "tx %d/%d bytes -- > %d\n",
489+
urb->actual_length,
490+
urb->transfer_buffer_length,
491+
urb->status);
474492

475-
acm = wb->instance;
476493
acm_write_done(acm, wb);
477494
if (ACM_READY(acm))
478495
schedule_work(&acm->work);
496+
else
497+
wake_up_interruptible(&acm->drain_wait);
479498
}
480499

481500
static void acm_softint(struct work_struct *work)
482501
{
483502
struct acm *acm = container_of(work, struct acm, work);
484-
dbg("Entering acm_softint.");
485-
503+
504+
dev_vdbg(&acm->data->dev, "tx work\n");
486505
if (!ACM_READY(acm))
487506
return;
488507
tty_wakeup(acm->tty);
@@ -603,6 +622,8 @@ static void acm_tty_unregister(struct acm *acm)
603622
kfree(acm);
604623
}
605624

625+
static int acm_tty_chars_in_buffer(struct tty_struct *tty);
626+
606627
static void acm_tty_close(struct tty_struct *tty, struct file *filp)
607628
{
608629
struct acm *acm = tty->driver_data;
@@ -617,6 +638,13 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp)
617638
if (acm->dev) {
618639
usb_autopm_get_interface(acm->control);
619640
acm_set_control(acm, acm->ctrlout = 0);
641+
642+
/* try letting the last writes drain naturally */
643+
wait_event_interruptible_timeout(acm->drain_wait,
644+
(ACM_NW == acm_wb_is_avail(acm))
645+
|| !acm->dev,
646+
ACM_CLOSE_TIMEOUT * HZ);
647+
620648
usb_kill_urb(acm->ctrlurb);
621649
for (i = 0; i < ACM_NW; i++)
622650
usb_kill_urb(acm->wb[i].urb);
@@ -1047,6 +1075,7 @@ static int acm_probe (struct usb_interface *intf,
10471075
acm->urb_task.data = (unsigned long) acm;
10481076
INIT_WORK(&acm->work, acm_softint);
10491077
INIT_WORK(&acm->waker, acm_waker);
1078+
init_waitqueue_head(&acm->drain_wait);
10501079
spin_lock_init(&acm->throttle_lock);
10511080
spin_lock_init(&acm->write_lock);
10521081
spin_lock_init(&acm->read_lock);

drivers/usb/class/cdc-acm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ struct acm {
113113
struct usb_cdc_line_coding line; /* bits, stop, parity */
114114
struct work_struct work; /* work queue entry for line discipline waking up */
115115
struct work_struct waker;
116+
wait_queue_head_t drain_wait; /* close processing */
116117
struct tasklet_struct urb_task; /* rx processing */
117118
spinlock_t throttle_lock; /* synchronize throtteling and read callback */
118119
unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */

0 commit comments

Comments
 (0)