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: David Engraf <david.engraf@netcom.eu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Родитель
28d1dfadd3
Коммит
e4cf3aa8f9
|
@ -31,6 +31,7 @@
|
|||
* v0.23 - use softirq for rx processing, as needed by tty layer
|
||||
* v0.24 - change probe method to evaluate CDC union descriptor
|
||||
* v0.25 - downstream tasks paralelized to maximize throughput
|
||||
* v0.26 - multiple write urbs, writesize increased
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -72,7 +73,7 @@
|
|||
/*
|
||||
* 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_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
|
||||
|
||||
|
@ -118,7 +119,7 @@ static int acm_wb_alloc(struct acm *acm)
|
|||
int i, wbn;
|
||||
struct acm_wb *wb;
|
||||
|
||||
wbn = acm->write_current;
|
||||
wbn = 0;
|
||||
i = 0;
|
||||
for (;;) {
|
||||
wb = &acm->wb[wbn];
|
||||
|
@ -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)
|
||||
{
|
||||
int i, n;
|
||||
|
@ -156,26 +152,22 @@ static inline int acm_wb_is_used(struct acm *acm, int wbn)
|
|||
/*
|
||||
* 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;
|
||||
int wbn;
|
||||
|
||||
spin_lock_irqsave(&acm->write_lock, flags);
|
||||
acm->write_ready = 1;
|
||||
wbn = acm->write_current;
|
||||
acm_wb_free(acm, wbn);
|
||||
acm->write_current = (wbn + 1) % ACM_NW;
|
||||
wb->use = 0;
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Poke write.
|
||||
*/
|
||||
static int acm_write_start(struct acm *acm)
|
||||
static int acm_write_start(struct acm *acm, int wbn)
|
||||
{
|
||||
unsigned long flags;
|
||||
int wbn;
|
||||
struct acm_wb *wb;
|
||||
int rc;
|
||||
|
||||
|
@ -190,24 +182,24 @@ static int acm_write_start(struct acm *acm)
|
|||
return 0; /* A white lie */
|
||||
}
|
||||
|
||||
wbn = acm->write_current;
|
||||
if (!acm_wb_is_used(acm, wbn)) {
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
wb = &acm->wb[wbn];
|
||||
|
||||
acm->write_ready = 0;
|
||||
if(acm_wb_is_avail(acm) <= 1)
|
||||
acm->write_ready = 0;
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
|
||||
acm->writeurb->transfer_buffer = wb->buf;
|
||||
acm->writeurb->transfer_dma = wb->dmah;
|
||||
acm->writeurb->transfer_buffer_length = wb->len;
|
||||
acm->writeurb->dev = acm->dev;
|
||||
wb->urb->transfer_buffer = wb->buf;
|
||||
wb->urb->transfer_dma = wb->dmah;
|
||||
wb->urb->transfer_buffer_length = wb->len;
|
||||
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);
|
||||
acm_write_done(acm);
|
||||
acm_write_done(acm, wb);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
@ -450,12 +442,13 @@ urbs:
|
|||
/* data interface wrote those outgoing bytes */
|
||||
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);
|
||||
|
||||
acm_write_done(acm);
|
||||
acm_write_start(acm);
|
||||
acm = wb->instance;
|
||||
acm_write_done(acm, wb);
|
||||
if (ACM_READY(acm))
|
||||
schedule_work(&acm->work);
|
||||
}
|
||||
|
@ -557,7 +550,8 @@ static void acm_tty_unregister(struct acm *acm)
|
|||
usb_put_intf(acm->control);
|
||||
acm_table[acm->minor] = NULL;
|
||||
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++)
|
||||
usb_free_urb(acm->ru[i].urb);
|
||||
kfree(acm->country_codes);
|
||||
|
@ -578,7 +572,8 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp)
|
|||
if (acm->dev) {
|
||||
acm_set_control(acm, acm->ctrlout = 0);
|
||||
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++)
|
||||
usb_kill_urb(acm->ru[i].urb);
|
||||
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
|
|||
spin_lock_irqsave(&acm->write_lock, flags);
|
||||
if ((wbn = acm_wb_alloc(acm)) < 0) {
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
acm_write_start(acm);
|
||||
return 0;
|
||||
}
|
||||
wb = &acm->wb[wbn];
|
||||
|
@ -617,7 +611,7 @@ static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int c
|
|||
wb->len = count;
|
||||
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 count;
|
||||
}
|
||||
|
@ -977,7 +971,7 @@ skip_normal_probe:
|
|||
|
||||
ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
|
||||
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->data = data_interface;
|
||||
acm->minor = minor;
|
||||
|
@ -1032,10 +1026,19 @@ skip_normal_probe:
|
|||
goto alloc_fail7;
|
||||
}
|
||||
}
|
||||
acm->writeurb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!acm->writeurb) {
|
||||
dev_dbg(&intf->dev, "out of memory (writeurb kmalloc)\n");
|
||||
goto alloc_fail7;
|
||||
for(i = 0; i < ACM_NW; i++)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -1071,10 +1074,6 @@ skip_countries:
|
|||
acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
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);
|
||||
|
||||
acm_set_control(acm, acm->ctrlout);
|
||||
|
@ -1092,7 +1091,8 @@ skip_countries:
|
|||
|
||||
return 0;
|
||||
alloc_fail8:
|
||||
usb_free_urb(acm->writeurb);
|
||||
for (i = 0; i < ACM_NW; i++)
|
||||
usb_free_urb(acm->wb[i].urb);
|
||||
alloc_fail7:
|
||||
for (i = 0; i < num_rx_buf; i++)
|
||||
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)
|
|||
tasklet_disable(&acm->urb_task);
|
||||
|
||||
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++)
|
||||
usb_kill_urb(acm->ru[i].urb);
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
* when processing onlcr, so we only need 2 buffers. These values must be
|
||||
* powers of 2.
|
||||
*/
|
||||
#define ACM_NW 2
|
||||
#define ACM_NW 16
|
||||
#define ACM_NR 16
|
||||
|
||||
struct acm_wb {
|
||||
|
@ -67,6 +67,8 @@ struct acm_wb {
|
|||
dma_addr_t dmah;
|
||||
int len;
|
||||
int use;
|
||||
struct urb *urb;
|
||||
struct acm *instance;
|
||||
};
|
||||
|
||||
struct acm_rb {
|
||||
|
@ -88,7 +90,7 @@ struct acm {
|
|||
struct usb_interface *control; /* control interface */
|
||||
struct usb_interface *data; /* data interface */
|
||||
struct tty_struct *tty; /* the corresponding tty */
|
||||
struct urb *ctrlurb, *writeurb; /* urbs */
|
||||
struct urb *ctrlurb; /* urbs */
|
||||
u8 *ctrl_buffer; /* buffers of urbs */
|
||||
dma_addr_t ctrl_dma; /* dma handles of buffers */
|
||||
u8 *country_codes; /* country codes from device */
|
||||
|
@ -103,7 +105,6 @@ struct acm {
|
|||
struct list_head spare_read_urbs;
|
||||
struct list_head spare_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_ready; /* write urb is not running */
|
||||
spinlock_t write_lock;
|
||||
|
|
Загрузка…
Ссылка в новой задаче