Commit e4cf3aa8 authored by David Engraf's avatar David Engraf Committed by Greg Kroah-Hartman

USB: increase cdc-acm write throughput

the following patch uses 16 write urbs and a writsize of wMaxPacketSize
* 20.  With this patch I get the maximum througput from my linux system
with 20MB/sec read and 15 MB/sec write (full speed 1 MB/sec both)

I also deleted the flag URB_NO_FSBR for the writeurbs, because this
makes my full speed devices significant slower.
Signed-off-by: default avatarDavid Engraf <david.engraf@netcom.eu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 28d1dfad
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
* v0.23 - use softirq for rx processing, as needed by tty layer * v0.23 - use softirq for rx processing, as needed by tty layer
* v0.24 - change probe method to evaluate CDC union descriptor * v0.24 - change probe method to evaluate CDC union descriptor
* v0.25 - downstream tasks paralelized to maximize throughput * v0.25 - downstream tasks paralelized to maximize throughput
* v0.26 - multiple write urbs, writesize increased
*/ */
/* /*
...@@ -72,7 +73,7 @@ ...@@ -72,7 +73,7 @@
/* /*
* Version Information * Version Information
*/ */
#define DRIVER_VERSION "v0.25" #define DRIVER_VERSION "v0.26"
#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek" #define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek"
#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters" #define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
...@@ -118,7 +119,7 @@ static int acm_wb_alloc(struct acm *acm) ...@@ -118,7 +119,7 @@ static int acm_wb_alloc(struct acm *acm)
int i, wbn; int i, wbn;
struct acm_wb *wb; struct acm_wb *wb;
wbn = acm->write_current; wbn = 0;
i = 0; i = 0;
for (;;) { for (;;) {
wb = &acm->wb[wbn]; wb = &acm->wb[wbn];
...@@ -132,11 +133,6 @@ static int acm_wb_alloc(struct acm *acm) ...@@ -132,11 +133,6 @@ static int acm_wb_alloc(struct acm *acm)
} }
} }
static void acm_wb_free(struct acm *acm, int wbn)
{
acm->wb[wbn].use = 0;
}
static int acm_wb_is_avail(struct acm *acm) static int acm_wb_is_avail(struct acm *acm)
{ {
int i, n; int i, n;
...@@ -156,26 +152,22 @@ static inline int acm_wb_is_used(struct acm *acm, int wbn) ...@@ -156,26 +152,22 @@ static inline int acm_wb_is_used(struct acm *acm, int wbn)
/* /*
* Finish write. * Finish write.
*/ */
static void acm_write_done(struct acm *acm) static void acm_write_done(struct acm *acm, struct acm_wb *wb)
{ {
unsigned long flags; unsigned long flags;
int wbn;
spin_lock_irqsave(&acm->write_lock, flags); spin_lock_irqsave(&acm->write_lock, flags);
acm->write_ready = 1; acm->write_ready = 1;
wbn = acm->write_current; wb->use = 0;
acm_wb_free(acm, wbn);
acm->write_current = (wbn + 1) % ACM_NW;
spin_unlock_irqrestore(&acm->write_lock, flags); spin_unlock_irqrestore(&acm->write_lock, flags);
} }
/* /*
* Poke write. * Poke write.
*/ */
static int acm_write_start(struct acm *acm) static int acm_write_start(struct acm *acm, int wbn)
{ {
unsigned long flags; unsigned long flags;
int wbn;
struct acm_wb *wb; struct acm_wb *wb;
int rc; int rc;
...@@ -190,24 +182,24 @@ static int acm_write_start(struct acm *acm) ...@@ -190,24 +182,24 @@ static int acm_write_start(struct acm *acm)
return 0; /* A white lie */ return 0; /* A white lie */
} }
wbn = acm->write_current;
if (!acm_wb_is_used(acm, wbn)) { if (!acm_wb_is_used(acm, wbn)) {
spin_unlock_irqrestore(&acm->write_lock, flags); spin_unlock_irqrestore(&acm->write_lock, flags);
return 0; return 0;
} }
wb = &acm->wb[wbn]; wb = &acm->wb[wbn];
if(acm_wb_is_avail(acm) <= 1)
acm->write_ready = 0; acm->write_ready = 0;
spin_unlock_irqrestore(&acm->write_lock, flags); spin_unlock_irqrestore(&acm->write_lock, flags);
acm->writeurb->transfer_buffer = wb->buf; wb->urb->transfer_buffer = wb->buf;
acm->writeurb->transfer_dma = wb->dmah; wb->urb->transfer_dma = wb->dmah;
acm->writeurb->transfer_buffer_length = wb->len; wb->urb->transfer_buffer_length = wb->len;
acm->writeurb->dev = acm->dev; wb->urb->dev = acm->dev;
if ((rc = usb_submit_urb(acm->writeurb, GFP_ATOMIC)) < 0) { if ((rc = usb_submit_urb(wb->urb, GFP_ATOMIC)) < 0) {
dbg("usb_submit_urb(write bulk) failed: %d", rc); dbg("usb_submit_urb(write bulk) failed: %d", rc);
acm_write_done(acm); acm_write_done(acm, wb);
} }
return rc; return rc;
} }
...@@ -450,12 +442,13 @@ urbs: ...@@ -450,12 +442,13 @@ urbs:
/* data interface wrote those outgoing bytes */ /* data interface wrote those outgoing bytes */
static void acm_write_bulk(struct urb *urb) static void acm_write_bulk(struct urb *urb)
{ {
struct acm *acm = (struct acm *)urb->context; struct acm *acm;
struct acm_wb *wb = (struct acm_wb *)urb->context;
dbg("Entering acm_write_bulk with status %d", urb->status); dbg("Entering acm_write_bulk with status %d", urb->status);
acm_write_done(acm); acm = wb->instance;
acm_write_start(acm); acm_write_done(acm, wb);
if (ACM_READY(acm)) if (ACM_READY(acm))
schedule_work(&acm->work); schedule_work(&acm->work);
} }
...@@ -557,7 +550,8 @@ static void acm_tty_unregister(struct acm *acm) ...@@ -557,7 +550,8 @@ static void acm_tty_unregister(struct acm *acm)
usb_put_intf(acm->control); usb_put_intf(acm->control);
acm_table[acm->minor] = NULL; acm_table[acm->minor] = NULL;
usb_free_urb(acm->ctrlurb); usb_free_urb(acm->ctrlurb);
usb_free_urb(acm->writeurb); for (i = 0; i < ACM_NW; i++)
usb_free_urb(acm->wb[i].urb);
for (i = 0; i < nr; i++) for (i = 0; i < nr; i++)
usb_free_urb(acm->ru[i].urb); usb_free_urb(acm->ru[i].urb);
kfree(acm->country_codes); kfree(acm->country_codes);
...@@ -578,7 +572,8 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp) ...@@ -578,7 +572,8 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp)
if (acm->dev) { if (acm->dev) {
acm_set_control(acm, acm->ctrlout = 0); acm_set_control(acm, acm->ctrlout = 0);
usb_kill_urb(acm->ctrlurb); usb_kill_urb(acm->ctrlurb);
usb_kill_urb(acm->writeurb); for (i = 0; i < ACM_NW; i++)
usb_kill_urb(acm->wb[i].urb);
for (i = 0; i < nr; i++) for (i = 0; i < nr; i++)
usb_kill_urb(acm->ru[i].urb); usb_kill_urb(acm->ru[i].urb);
usb_autopm_put_interface(acm->control); usb_autopm_put_interface(acm->control);
...@@ -606,7 +601,6 @@ static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int c ...@@ -606,7 +601,6 @@ static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int c
spin_lock_irqsave(&acm->write_lock, flags); spin_lock_irqsave(&acm->write_lock, flags);
if ((wbn = acm_wb_alloc(acm)) < 0) { if ((wbn = acm_wb_alloc(acm)) < 0) {
spin_unlock_irqrestore(&acm->write_lock, flags); spin_unlock_irqrestore(&acm->write_lock, flags);
acm_write_start(acm);
return 0; return 0;
} }
wb = &acm->wb[wbn]; wb = &acm->wb[wbn];
...@@ -617,7 +611,7 @@ static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int c ...@@ -617,7 +611,7 @@ static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int c
wb->len = count; wb->len = count;
spin_unlock_irqrestore(&acm->write_lock, flags); spin_unlock_irqrestore(&acm->write_lock, flags);
if ((stat = acm_write_start(acm)) < 0) if ((stat = acm_write_start(acm, wbn)) < 0)
return stat; return stat;
return count; return count;
} }
...@@ -977,7 +971,7 @@ skip_normal_probe: ...@@ -977,7 +971,7 @@ skip_normal_probe:
ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize); ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
readsize = le16_to_cpu(epread->wMaxPacketSize)* ( quirks == SINGLE_RX_URB ? 1 : 2); readsize = le16_to_cpu(epread->wMaxPacketSize)* ( quirks == SINGLE_RX_URB ? 1 : 2);
acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize); acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
acm->control = control_interface; acm->control = control_interface;
acm->data = data_interface; acm->data = data_interface;
acm->minor = minor; acm->minor = minor;
...@@ -1032,12 +1026,21 @@ skip_normal_probe: ...@@ -1032,12 +1026,21 @@ skip_normal_probe:
goto alloc_fail7; goto alloc_fail7;
} }
} }
acm->writeurb = usb_alloc_urb(0, GFP_KERNEL); for(i = 0; i < ACM_NW; i++)
if (!acm->writeurb) { {
dev_dbg(&intf->dev, "out of memory (writeurb kmalloc)\n"); struct acm_wb *snd = &(acm->wb[i]);
if (!(snd->urb = usb_alloc_urb(0, GFP_KERNEL))) {
dev_dbg(&intf->dev, "out of memory (write urbs usb_alloc_urb)");
goto alloc_fail7; goto alloc_fail7;
} }
usb_fill_bulk_urb(snd->urb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
NULL, acm->writesize, acm_write_bulk, snd);
snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
snd->instance = acm;
}
usb_set_intfdata (intf, acm); usb_set_intfdata (intf, acm);
i = device_create_file(&intf->dev, &dev_attr_bmCapabilities); i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
...@@ -1071,10 +1074,6 @@ skip_countries: ...@@ -1071,10 +1074,6 @@ skip_countries:
acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
acm->ctrlurb->transfer_dma = acm->ctrl_dma; acm->ctrlurb->transfer_dma = acm->ctrl_dma;
usb_fill_bulk_urb(acm->writeurb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
NULL, acm->writesize, acm_write_bulk, acm);
acm->writeurb->transfer_flags |= URB_NO_FSBR | URB_NO_TRANSFER_DMA_MAP;
dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor); dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
acm_set_control(acm, acm->ctrlout); acm_set_control(acm, acm->ctrlout);
...@@ -1092,7 +1091,8 @@ skip_countries: ...@@ -1092,7 +1091,8 @@ skip_countries:
return 0; return 0;
alloc_fail8: alloc_fail8:
usb_free_urb(acm->writeurb); for (i = 0; i < ACM_NW; i++)
usb_free_urb(acm->wb[i].urb);
alloc_fail7: alloc_fail7:
for (i = 0; i < num_rx_buf; i++) for (i = 0; i < num_rx_buf; i++)
usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
...@@ -1116,7 +1116,8 @@ static void stop_data_traffic(struct acm *acm) ...@@ -1116,7 +1116,8 @@ static void stop_data_traffic(struct acm *acm)
tasklet_disable(&acm->urb_task); tasklet_disable(&acm->urb_task);
usb_kill_urb(acm->ctrlurb); usb_kill_urb(acm->ctrlurb);
usb_kill_urb(acm->writeurb); for(i = 0; i < ACM_NW; i++)
usb_kill_urb(acm->wb[i].urb);
for (i = 0; i < acm->rx_buflimit; i++) for (i = 0; i < acm->rx_buflimit; i++)
usb_kill_urb(acm->ru[i].urb); usb_kill_urb(acm->ru[i].urb);
......
...@@ -59,7 +59,7 @@ ...@@ -59,7 +59,7 @@
* when processing onlcr, so we only need 2 buffers. These values must be * when processing onlcr, so we only need 2 buffers. These values must be
* powers of 2. * powers of 2.
*/ */
#define ACM_NW 2 #define ACM_NW 16
#define ACM_NR 16 #define ACM_NR 16
struct acm_wb { struct acm_wb {
...@@ -67,6 +67,8 @@ struct acm_wb { ...@@ -67,6 +67,8 @@ struct acm_wb {
dma_addr_t dmah; dma_addr_t dmah;
int len; int len;
int use; int use;
struct urb *urb;
struct acm *instance;
}; };
struct acm_rb { struct acm_rb {
...@@ -88,7 +90,7 @@ struct acm { ...@@ -88,7 +90,7 @@ struct acm {
struct usb_interface *control; /* control interface */ struct usb_interface *control; /* control interface */
struct usb_interface *data; /* data interface */ struct usb_interface *data; /* data interface */
struct tty_struct *tty; /* the corresponding tty */ struct tty_struct *tty; /* the corresponding tty */
struct urb *ctrlurb, *writeurb; /* urbs */ struct urb *ctrlurb; /* urbs */
u8 *ctrl_buffer; /* buffers of urbs */ u8 *ctrl_buffer; /* buffers of urbs */
dma_addr_t ctrl_dma; /* dma handles of buffers */ dma_addr_t ctrl_dma; /* dma handles of buffers */
u8 *country_codes; /* country codes from device */ u8 *country_codes; /* country codes from device */
...@@ -103,7 +105,6 @@ struct acm { ...@@ -103,7 +105,6 @@ struct acm {
struct list_head spare_read_urbs; struct list_head spare_read_urbs;
struct list_head spare_read_bufs; struct list_head spare_read_bufs;
struct list_head filled_read_bufs; struct list_head filled_read_bufs;
int write_current; /* current write buffer */
int write_used; /* number of non-empty write buffers */ int write_used; /* number of non-empty write buffers */
int write_ready; /* write urb is not running */ int write_ready; /* write urb is not running */
spinlock_t write_lock; spinlock_t write_lock;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment