diff options
| author | Greg Kroah-Hartman <gregkh@suse.de> | 2008-08-07 21:23:49 -0700 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-08-07 21:23:49 -0700 |
| commit | 4e0781592b3976c117346ce46eb8fa5f4b7552ef (patch) | |
| tree | 467b0dad426a03adf38cd6d0c68c35e53815cd48 /usb.current | |
| parent | 9b11efecf242453bef9ffda66c50a74278538d16 (diff) | |
| download | patches-4e0781592b3976c117346ce46eb8fa5f4b7552ef.tar.gz | |
usb patches actuall added this time...
Diffstat (limited to 'usb.current')
| -rw-r--r-- | usb.current/usb-cdc-acm-bugfix-release.patch | 71 | ||||
| -rw-r--r-- | usb.current/usb-cdc-acm-drain-writes-on-close.patch | 156 | ||||
| -rw-r--r-- | usb.current/usb-cdc-acm-stop-dropping-tx-buffers.patch | 145 |
3 files changed, 372 insertions, 0 deletions
diff --git a/usb.current/usb-cdc-acm-bugfix-release.patch b/usb.current/usb-cdc-acm-bugfix-release.patch new file mode 100644 index 00000000000000..f41e91c01993a0 --- /dev/null +++ b/usb.current/usb-cdc-acm-bugfix-release.patch @@ -0,0 +1,71 @@ +From david-b@pacbell.net Thu Aug 7 21:10:21 2008 +From: David Brownell <david-b@pacbell.net> +Date: Wed, 6 Aug 2008 18:41:12 -0700 +Subject: usb: cdc-acm: bugfix release() +To: Greg KH <greg@kroah.com> +Cc: linux-usb@vger.kernel.org, Oliver Neukum <oneukum@suse.de> +Message-ID: <200808061841.13144.david-b@pacbell.net> +Content-Disposition: inline + +From: David Brownell <dbrownell@users.sourceforge.net> + +Bugfixes to the usb_driver_release_interface() usage; + + (a) make sure releasing *either* interface first will release + the other, instead of insisting it be the control interface; + + (b) remove the recently-added self-deadlock. + +(The "fix disconnect bug in cdc-acm" patch was incomplete and incorrect.) + +Plus a small "sparse" fix: rename a local variable so it doesn't +shadow a function parameter. + +Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> +Acked-by: Oliver Neukum <oneukum@suse.de> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/class/cdc-acm.c | 15 +++++++++------ + 1 file changed, 9 insertions(+), 6 deletions(-) + +--- a/drivers/usb/class/cdc-acm.c ++++ b/drivers/usb/class/cdc-acm.c +@@ -1108,9 +1108,11 @@ skip_normal_probe: + rcv->instance = acm; + } + for (i = 0; i < num_rx_buf; i++) { +- struct acm_rb *buf = &(acm->rb[i]); ++ struct acm_rb *rb = &(acm->rb[i]); + +- if (!(buf->base = usb_buffer_alloc(acm->dev, readsize, GFP_KERNEL, &buf->dma))) { ++ rb->base = usb_buffer_alloc(acm->dev, readsize, ++ GFP_KERNEL, &rb->dma); ++ if (!rb->base) { + dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)\n"); + goto alloc_fail7; + } +@@ -1172,6 +1174,7 @@ skip_countries: + acm_set_line(acm, &acm->line); + + usb_driver_claim_interface(&acm_driver, data_interface, acm); ++ usb_set_intfdata(data_interface, acm); + + usb_get_intf(control_interface); + tty_register_device(acm_tty_driver, minor, &control_interface->dev); +@@ -1221,11 +1224,11 @@ static void acm_disconnect(struct usb_in + struct acm *acm = usb_get_intfdata(intf); + struct usb_device *usb_dev = interface_to_usbdev(intf); + +- mutex_lock(&open_mutex); +- if (!acm || !acm->dev) { +- mutex_unlock(&open_mutex); ++ /* sibling interface is already cleaning up */ ++ if (!acm) + return; +- } ++ ++ mutex_lock(&open_mutex); + if (acm->country_codes){ + device_remove_file(&acm->control->dev, + &dev_attr_wCountryCodes); diff --git a/usb.current/usb-cdc-acm-drain-writes-on-close.patch b/usb.current/usb-cdc-acm-drain-writes-on-close.patch new file mode 100644 index 00000000000000..226361dc2c0d11 --- /dev/null +++ b/usb.current/usb-cdc-acm-drain-writes-on-close.patch @@ -0,0 +1,156 @@ +From david-b@pacbell.net Thu Aug 7 21:12:03 2008 +From: David Brownell <david-b@pacbell.net> +Date: Wed, 6 Aug 2008 18:46:10 -0700 +Subject: usb: cdc-acm: drain writes on close +To: Greg KH <greg@kroah.com> +Cc: linux-usb@vger.kernel.org, Oliver Neukum <oneukum@suse.de> +Message-ID: <200808061846.10641.david-b@pacbell.net> +Content-Disposition: inline + + +From: David Brownell <dbrownell@users.sourceforge.net> + +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> + +--- + drivers/usb/class/cdc-acm.c | 39 ++++++++++++++++++++++++++++++++++----- + drivers/usb/class/cdc-acm.h | 1 + + 2 files changed, 35 insertions(+), 5 deletions(-) + +--- a/drivers/usb/class/cdc-acm.c ++++ b/drivers/usb/class/cdc-acm.c +@@ -51,6 +51,7 @@ + */ + + #undef DEBUG ++#undef VERBOSE_DEBUG + + #include <linux/kernel.h> + #include <linux/errno.h> +@@ -70,6 +71,9 @@ + + #include "cdc-acm.h" + ++ ++#define ACM_CLOSE_TIMEOUT 15 /* seconds to let writes drain */ ++ + /* + * Version Information + */ +@@ -85,6 +89,12 @@ static DEFINE_MUTEX(open_mutex); + + #define ACM_READY(acm) (acm && acm->dev && acm->used) + ++#ifdef VERBOSE_DEBUG ++#define verbose 1 ++#else ++#define verbose 0 ++#endif ++ + /* + * Functions for ACM control messages. + */ +@@ -136,11 +146,14 @@ static int acm_wb_alloc(struct acm *acm) + static int acm_wb_is_avail(struct acm *acm) + { + int i, n; ++ unsigned long flags; + + n = ACM_NW; ++ spin_lock_irqsave(&acm->write_lock, flags); + for (i = 0; i < ACM_NW; i++) { + n -= acm->wb[i].use; + } ++ spin_unlock_irqrestore(&acm->write_lock, flags); + return n; + } + +@@ -467,22 +480,28 @@ urbs: + /* data interface wrote those outgoing bytes */ + static void acm_write_bulk(struct urb *urb) + { +- struct acm *acm; + struct acm_wb *wb = urb->context; ++ struct acm *acm = wb->instance; + +- dbg("Entering acm_write_bulk with status %d", urb->status); ++ if (verbose || urb->status ++ || (urb->actual_length != urb->transfer_buffer_length)) ++ dev_dbg(&acm->data->dev, "tx %d/%d bytes -- > %d\n", ++ urb->actual_length, ++ urb->transfer_buffer_length, ++ urb->status); + +- acm = wb->instance; + acm_write_done(acm, wb); + if (ACM_READY(acm)) + schedule_work(&acm->work); ++ else ++ wake_up_interruptible(&acm->drain_wait); + } + + static void acm_softint(struct work_struct *work) + { + struct acm *acm = container_of(work, struct acm, work); +- dbg("Entering acm_softint."); +- ++ ++ dev_vdbg(&acm->data->dev, "tx work\n"); + if (!ACM_READY(acm)) + return; + tty_wakeup(acm->tty); +@@ -603,6 +622,8 @@ static void acm_tty_unregister(struct ac + kfree(acm); + } + ++static int acm_tty_chars_in_buffer(struct tty_struct *tty); ++ + static void acm_tty_close(struct tty_struct *tty, struct file *filp) + { + struct acm *acm = tty->driver_data; +@@ -617,6 +638,13 @@ static void acm_tty_close(struct tty_str + if (acm->dev) { + usb_autopm_get_interface(acm->control); + acm_set_control(acm, acm->ctrlout = 0); ++ ++ /* try letting the last writes drain naturally */ ++ wait_event_interruptible_timeout(acm->drain_wait, ++ (ACM_NW == acm_wb_is_avail(acm)) ++ || !acm->dev, ++ ACM_CLOSE_TIMEOUT * HZ); ++ + usb_kill_urb(acm->ctrlurb); + for (i = 0; i < ACM_NW; i++) + usb_kill_urb(acm->wb[i].urb); +@@ -1047,6 +1075,7 @@ skip_normal_probe: + acm->urb_task.data = (unsigned long) acm; + INIT_WORK(&acm->work, acm_softint); + INIT_WORK(&acm->waker, acm_waker); ++ init_waitqueue_head(&acm->drain_wait); + spin_lock_init(&acm->throttle_lock); + spin_lock_init(&acm->write_lock); + spin_lock_init(&acm->read_lock); +--- a/drivers/usb/class/cdc-acm.h ++++ b/drivers/usb/class/cdc-acm.h +@@ -113,6 +113,7 @@ struct acm { + struct usb_cdc_line_coding line; /* bits, stop, parity */ + struct work_struct work; /* work queue entry for line discipline waking up */ + struct work_struct waker; ++ wait_queue_head_t drain_wait; /* close processing */ + struct tasklet_struct urb_task; /* rx processing */ + spinlock_t throttle_lock; /* synchronize throtteling and read callback */ + unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ diff --git a/usb.current/usb-cdc-acm-stop-dropping-tx-buffers.patch b/usb.current/usb-cdc-acm-stop-dropping-tx-buffers.patch new file mode 100644 index 00000000000000..7b7cc9c4979a40 --- /dev/null +++ b/usb.current/usb-cdc-acm-stop-dropping-tx-buffers.patch @@ -0,0 +1,145 @@ +From david-b@pacbell.net Thu Aug 7 21:10:47 2008 +From: David Brownell <david-b@pacbell.net> +Date: Wed, 6 Aug 2008 18:44:12 -0700 +Subject: usb: cdc-acm: stop dropping tx buffers +To: Greg KH <greg@kroah.com> +Cc: linux-usb@vger.kernel.org, Oliver Neukum <oneukum@suse.de>, David Engraf <david.engraf@netcom.eu> +Message-ID: <200808061844.12730.david-b@pacbell.net> +Content-Disposition: inline + + +From: David Brownell <dbrownell@users.sourceforge.net> + +The "increase cdc-acm write throughput" patch left in place two +now-obsolete mechanisms, either of which can make the cdc-acm +driver drop TX data (nasty!). This patch removes them: + + - The write_ready flag ... if an URB and buffer were found, + they can (and should!) always be used. + + - TX path acm_wb_is_used() ... used when the buffer was just + allocated, so that check is pointless. + +Also fix a won't-yet-matter leak of a write buffer on a disconnect path. + +Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> +Cc: David Engraf <david.engraf@netcom.eu> +Acked-by: Oliver Neukum <oneukum@suse.de> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/class/cdc-acm.c | 32 +++----------------------------- + drivers/usb/class/cdc-acm.h | 2 -- + 2 files changed, 3 insertions(+), 31 deletions(-) + +--- a/drivers/usb/class/cdc-acm.c ++++ b/drivers/usb/class/cdc-acm.c +@@ -144,11 +144,6 @@ static int acm_wb_is_avail(struct acm *a + return n; + } + +-static inline int acm_wb_is_used(struct acm *acm, int wbn) +-{ +- return acm->wb[wbn].use; +-} +- + /* + * Finish write. + */ +@@ -157,7 +152,6 @@ static void acm_write_done(struct acm *a + unsigned long flags; + + spin_lock_irqsave(&acm->write_lock, flags); +- acm->write_ready = 1; + wb->use = 0; + acm->transmitting--; + spin_unlock_irqrestore(&acm->write_lock, flags); +@@ -190,40 +184,25 @@ static int acm_start_wb(struct acm *acm, + static int acm_write_start(struct acm *acm, int wbn) + { + unsigned long flags; +- struct acm_wb *wb; ++ struct acm_wb *wb = &acm->wb[wbn]; + int rc; + + spin_lock_irqsave(&acm->write_lock, flags); + if (!acm->dev) { ++ wb->use = 0; + spin_unlock_irqrestore(&acm->write_lock, flags); + return -ENODEV; + } + +- if (!acm->write_ready) { +- spin_unlock_irqrestore(&acm->write_lock, flags); +- return 0; /* A white lie */ +- } +- +- wb = &acm->wb[wbn]; +- if(acm_wb_is_avail(acm) <= 1) +- acm->write_ready = 0; +- + dbg("%s susp_count: %d", __func__, acm->susp_count); + if (acm->susp_count) { +- acm->old_ready = acm->write_ready; + acm->delayed_wb = wb; +- acm->write_ready = 0; + schedule_work(&acm->waker); + spin_unlock_irqrestore(&acm->write_lock, flags); + return 0; /* A white lie */ + } + usb_mark_last_busy(acm->dev); + +- if (!acm_wb_is_used(acm, wbn)) { +- spin_unlock_irqrestore(&acm->write_lock, flags); +- return 0; +- } +- + rc = acm_start_wb(acm, wb); + spin_unlock_irqrestore(&acm->write_lock, flags); + +@@ -512,7 +491,6 @@ static void acm_softint(struct work_stru + static void acm_waker(struct work_struct *waker) + { + struct acm *acm = container_of(waker, struct acm, waker); +- unsigned long flags; + int rv; + + rv = usb_autopm_get_interface(acm->control); +@@ -524,9 +502,6 @@ static void acm_waker(struct work_struct + acm_start_wb(acm, acm->delayed_wb); + acm->delayed_wb = NULL; + } +- spin_lock_irqsave(&acm->write_lock, flags); +- acm->write_ready = acm->old_ready; +- spin_unlock_irqrestore(&acm->write_lock, flags); + usb_autopm_put_interface(acm->control); + } + +@@ -697,7 +672,7 @@ static int acm_tty_write_room(struct tty + * Do not let the line discipline to know that we have a reserve, + * or it might get too enthusiastic. + */ +- return (acm->write_ready && acm_wb_is_avail(acm)) ? acm->writesize : 0; ++ return acm_wb_is_avail(acm) ? acm->writesize : 0; + } + + static int acm_tty_chars_in_buffer(struct tty_struct *tty) +@@ -1076,7 +1051,6 @@ skip_normal_probe: + spin_lock_init(&acm->write_lock); + spin_lock_init(&acm->read_lock); + mutex_init(&acm->mutex); +- acm->write_ready = 1; + acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress); + + buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma); +--- a/drivers/usb/class/cdc-acm.h ++++ b/drivers/usb/class/cdc-acm.h +@@ -106,8 +106,6 @@ struct acm { + struct list_head spare_read_bufs; + struct list_head filled_read_bufs; + int write_used; /* number of non-empty write buffers */ +- int write_ready; /* write urb is not running */ +- int old_ready; + int processing; + int transmitting; + spinlock_t write_lock; |
