diff options
author | David Engraf <david.engraf@netcom.eu> | 2008-03-20 05:01:34 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-04-25 00:16:44 -0400 |
commit | e4cf3aa8f9cd6ee4d583b5d445b5c152acefcde4 (patch) | |
tree | 1e1a442763c227bbe0c72952f8e5e599ecd30a97 | |
parent | 28d1dfadd3ca07e7ec1c3de4f82ac2b8ece4be91 (diff) |
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>
-rw-r--r-- | drivers/usb/class/cdc-acm.c | 81 | ||||
-rw-r--r-- | drivers/usb/class/cdc-acm.h | 7 |
2 files changed, 45 insertions, 43 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 1ded83b66af0..d9b408113921 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c | |||
@@ -31,6 +31,7 @@ | |||
31 | * v0.23 - use softirq for rx processing, as needed by tty layer | 31 | * v0.23 - use softirq for rx processing, as needed by tty layer |
32 | * v0.24 - change probe method to evaluate CDC union descriptor | 32 | * v0.24 - change probe method to evaluate CDC union descriptor |
33 | * v0.25 - downstream tasks paralelized to maximize throughput | 33 | * v0.25 - downstream tasks paralelized to maximize throughput |
34 | * v0.26 - multiple write urbs, writesize increased | ||
34 | */ | 35 | */ |
35 | 36 | ||
36 | /* | 37 | /* |
@@ -72,7 +73,7 @@ | |||
72 | /* | 73 | /* |
73 | * Version Information | 74 | * Version Information |
74 | */ | 75 | */ |
75 | #define DRIVER_VERSION "v0.25" | 76 | #define DRIVER_VERSION "v0.26" |
76 | #define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek" | 77 | #define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek" |
77 | #define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters" | 78 | #define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters" |
78 | 79 | ||
@@ -118,7 +119,7 @@ static int acm_wb_alloc(struct acm *acm) | |||
118 | int i, wbn; | 119 | int i, wbn; |
119 | struct acm_wb *wb; | 120 | struct acm_wb *wb; |
120 | 121 | ||
121 | wbn = acm->write_current; | 122 | wbn = 0; |
122 | i = 0; | 123 | i = 0; |
123 | for (;;) { | 124 | for (;;) { |
124 | wb = &acm->wb[wbn]; | 125 | wb = &acm->wb[wbn]; |
@@ -132,11 +133,6 @@ static int acm_wb_alloc(struct acm *acm) | |||
132 | } | 133 | } |
133 | } | 134 | } |
134 | 135 | ||
135 | static void acm_wb_free(struct acm *acm, int wbn) | ||
136 | { | ||
137 | acm->wb[wbn].use = 0; | ||
138 | } | ||
139 | |||
140 | static int acm_wb_is_avail(struct acm *acm) | 136 | static int acm_wb_is_avail(struct acm *acm) |
141 | { | 137 | { |
142 | int i, n; | 138 | int i, n; |
@@ -156,26 +152,22 @@ static inline int acm_wb_is_used(struct acm *acm, int wbn) | |||
156 | /* | 152 | /* |
157 | * Finish write. | 153 | * Finish write. |
158 | */ | 154 | */ |
159 | static void acm_write_done(struct acm *acm) | 155 | static void acm_write_done(struct acm *acm, struct acm_wb *wb) |
160 | { | 156 | { |
161 | unsigned long flags; | 157 | unsigned long flags; |
162 | int wbn; | ||
163 | 158 | ||
164 | spin_lock_irqsave(&acm->write_lock, flags); | 159 | spin_lock_irqsave(&acm->write_lock, flags); |
165 | acm->write_ready = 1; | 160 | acm->write_ready = 1; |
166 | wbn = acm->write_current; | 161 | wb->use = 0; |
167 | acm_wb_free(acm, wbn); | ||
168 | acm->write_current = (wbn + 1) % ACM_NW; | ||
169 | spin_unlock_irqrestore(&acm->write_lock, flags); | 162 | spin_unlock_irqrestore(&acm->write_lock, flags); |
170 | } | 163 | } |
171 | 164 | ||
172 | /* | 165 | /* |
173 | * Poke write. | 166 | * Poke write. |
174 | */ | 167 | */ |
175 | static int acm_write_start(struct acm *acm) | 168 | static int acm_write_start(struct acm *acm, int wbn) |
176 | { | 169 | { |
177 | unsigned long flags; | 170 | unsigned long flags; |
178 | int wbn; | ||
179 | struct acm_wb *wb; | 171 | struct acm_wb *wb; |
180 | int rc; | 172 | int rc; |
181 | 173 | ||
@@ -190,24 +182,24 @@ static int acm_write_start(struct acm *acm) | |||
190 | return 0; /* A white lie */ | 182 | return 0; /* A white lie */ |
191 | } | 183 | } |
192 | 184 | ||
193 | wbn = acm->write_current; | ||
194 | if (!acm_wb_is_used(acm, wbn)) { | 185 | if (!acm_wb_is_used(acm, wbn)) { |
195 | spin_unlock_irqrestore(&acm->write_lock, flags); | 186 | spin_unlock_irqrestore(&acm->write_lock, flags); |
196 | return 0; | 187 | return 0; |
197 | } | 188 | } |
198 | wb = &acm->wb[wbn]; | 189 | wb = &acm->wb[wbn]; |
199 | 190 | ||
200 | acm->write_ready = 0; | 191 | if(acm_wb_is_avail(acm) <= 1) |
192 | acm->write_ready = 0; | ||
201 | spin_unlock_irqrestore(&acm->write_lock, flags); | 193 | spin_unlock_irqrestore(&acm->write_lock, flags); |
202 | 194 | ||
203 | acm->writeurb->transfer_buffer = wb->buf; | 195 | wb->urb->transfer_buffer = wb->buf; |
204 | acm->writeurb->transfer_dma = wb->dmah; | 196 | wb->urb->transfer_dma = wb->dmah; |
205 | acm->writeurb->transfer_buffer_length = wb->len; | 197 | wb->urb->transfer_buffer_length = wb->len; |
206 | acm->writeurb->dev = acm->dev; | 198 | wb->urb->dev = acm->dev; |
207 | 199 | ||
208 | if ((rc = usb_submit_urb(acm->writeurb, GFP_ATOMIC)) < 0) { | 200 | if ((rc = usb_submit_urb(wb->urb, GFP_ATOMIC)) < 0) { |
209 | dbg("usb_submit_urb(write bulk) failed: %d", rc); | 201 | dbg("usb_submit_urb(write bulk) failed: %d", rc); |
210 | acm_write_done(acm); | 202 | acm_write_done(acm, wb); |
211 | } | 203 | } |
212 | return rc; | 204 | return rc; |
213 | } | 205 | } |
@@ -450,12 +442,13 @@ urbs: | |||
450 | /* data interface wrote those outgoing bytes */ | 442 | /* data interface wrote those outgoing bytes */ |
451 | static void acm_write_bulk(struct urb *urb) | 443 | static void acm_write_bulk(struct urb *urb) |
452 | { | 444 | { |
453 | struct acm *acm = (struct acm *)urb->context; | 445 | struct acm *acm; |
446 | struct acm_wb *wb = (struct acm_wb *)urb->context; | ||
454 | 447 | ||
455 | dbg("Entering acm_write_bulk with status %d", urb->status); | 448 | dbg("Entering acm_write_bulk with status %d", urb->status); |
456 | 449 | ||
457 | acm_write_done(acm); | 450 | acm = wb->instance; |
458 | acm_write_start(acm); | 451 | acm_write_done(acm, wb); |
459 | if (ACM_READY(acm)) | 452 | if (ACM_READY(acm)) |
460 | schedule_work(&acm->work); | 453 | schedule_work(&acm->work); |
461 | } | 454 | } |
@@ -557,7 +550,8 @@ static void acm_tty_unregister(struct acm *acm) | |||
557 | usb_put_intf(acm->control); | 550 | usb_put_intf(acm->control); |
558 | acm_table[acm->minor] = NULL; | 551 | acm_table[acm->minor] = NULL; |
559 | usb_free_urb(acm->ctrlurb); | 552 | usb_free_urb(acm->ctrlurb); |
560 | usb_free_urb(acm->writeurb); | 553 | for (i = 0; i < ACM_NW; i++) |
554 | usb_free_urb(acm->wb[i].urb); | ||
561 | for (i = 0; i < nr; i++) | 555 | for (i = 0; i < nr; i++) |
562 | usb_free_urb(acm->ru[i].urb); | 556 | usb_free_urb(acm->ru[i].urb); |
563 | kfree(acm->country_codes); | 557 | kfree(acm->country_codes); |
@@ -578,7 +572,8 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp) | |||
578 | if (acm->dev) { | 572 | if (acm->dev) { |
579 | acm_set_control(acm, acm->ctrlout = 0); | 573 | acm_set_control(acm, acm->ctrlout = 0); |
580 | usb_kill_urb(acm->ctrlurb); | 574 | usb_kill_urb(acm->ctrlurb); |
581 | usb_kill_urb(acm->writeurb); | 575 | for (i = 0; i < ACM_NW; i++) |
576 | usb_kill_urb(acm->wb[i].urb); | ||
582 | for (i = 0; i < nr; i++) | 577 | for (i = 0; i < nr; i++) |
583 | usb_kill_urb(acm->ru[i].urb); | 578 | usb_kill_urb(acm->ru[i].urb); |
584 | usb_autopm_put_interface(acm->control); | 579 | 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 | spin_lock_irqsave(&acm->write_lock, flags); | 601 | spin_lock_irqsave(&acm->write_lock, flags); |
607 | if ((wbn = acm_wb_alloc(acm)) < 0) { | 602 | if ((wbn = acm_wb_alloc(acm)) < 0) { |
608 | spin_unlock_irqrestore(&acm->write_lock, flags); | 603 | spin_unlock_irqrestore(&acm->write_lock, flags); |
609 | acm_write_start(acm); | ||
610 | return 0; | 604 | return 0; |
611 | } | 605 | } |
612 | wb = &acm->wb[wbn]; | 606 | wb = &acm->wb[wbn]; |
@@ -617,7 +611,7 @@ static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int c | |||
617 | wb->len = count; | 611 | wb->len = count; |
618 | spin_unlock_irqrestore(&acm->write_lock, flags); | 612 | spin_unlock_irqrestore(&acm->write_lock, flags); |
619 | 613 | ||
620 | if ((stat = acm_write_start(acm)) < 0) | 614 | if ((stat = acm_write_start(acm, wbn)) < 0) |
621 | return stat; | 615 | return stat; |
622 | return count; | 616 | return count; |
623 | } | 617 | } |
@@ -977,7 +971,7 @@ skip_normal_probe: | |||
977 | 971 | ||
978 | ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize); | 972 | ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize); |
979 | readsize = le16_to_cpu(epread->wMaxPacketSize)* ( quirks == SINGLE_RX_URB ? 1 : 2); | 973 | readsize = le16_to_cpu(epread->wMaxPacketSize)* ( quirks == SINGLE_RX_URB ? 1 : 2); |
980 | acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize); | 974 | acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20; |
981 | acm->control = control_interface; | 975 | acm->control = control_interface; |
982 | acm->data = data_interface; | 976 | acm->data = data_interface; |
983 | acm->minor = minor; | 977 | acm->minor = minor; |
@@ -1032,10 +1026,19 @@ skip_normal_probe: | |||
1032 | goto alloc_fail7; | 1026 | goto alloc_fail7; |
1033 | } | 1027 | } |
1034 | } | 1028 | } |
1035 | acm->writeurb = usb_alloc_urb(0, GFP_KERNEL); | 1029 | for(i = 0; i < ACM_NW; i++) |
1036 | if (!acm->writeurb) { | 1030 | { |
1037 | dev_dbg(&intf->dev, "out of memory (writeurb kmalloc)\n"); | 1031 | struct acm_wb *snd = &(acm->wb[i]); |
1038 | goto alloc_fail7; | 1032 | |
1033 | if (!(snd->urb = usb_alloc_urb(0, GFP_KERNEL))) { | ||
1034 | dev_dbg(&intf->dev, "out of memory (write urbs usb_alloc_urb)"); | ||
1035 | goto alloc_fail7; | ||
1036 | } | ||
1037 | |||
1038 | usb_fill_bulk_urb(snd->urb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), | ||
1039 | NULL, acm->writesize, acm_write_bulk, snd); | ||
1040 | snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; | ||
1041 | snd->instance = acm; | ||
1039 | } | 1042 | } |
1040 | 1043 | ||
1041 | usb_set_intfdata (intf, acm); | 1044 | usb_set_intfdata (intf, acm); |
@@ -1071,10 +1074,6 @@ skip_countries: | |||
1071 | acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; | 1074 | acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; |
1072 | acm->ctrlurb->transfer_dma = acm->ctrl_dma; | 1075 | acm->ctrlurb->transfer_dma = acm->ctrl_dma; |
1073 | 1076 | ||
1074 | usb_fill_bulk_urb(acm->writeurb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), | ||
1075 | NULL, acm->writesize, acm_write_bulk, acm); | ||
1076 | acm->writeurb->transfer_flags |= URB_NO_FSBR | URB_NO_TRANSFER_DMA_MAP; | ||
1077 | |||
1078 | dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor); | 1077 | dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor); |
1079 | 1078 | ||
1080 | acm_set_control(acm, acm->ctrlout); | 1079 | acm_set_control(acm, acm->ctrlout); |
@@ -1092,7 +1091,8 @@ skip_countries: | |||
1092 | 1091 | ||
1093 | return 0; | 1092 | return 0; |
1094 | alloc_fail8: | 1093 | alloc_fail8: |
1095 | usb_free_urb(acm->writeurb); | 1094 | for (i = 0; i < ACM_NW; i++) |
1095 | usb_free_urb(acm->wb[i].urb); | ||
1096 | alloc_fail7: | 1096 | alloc_fail7: |
1097 | for (i = 0; i < num_rx_buf; i++) | 1097 | for (i = 0; i < num_rx_buf; i++) |
1098 | usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); | 1098 | 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 | tasklet_disable(&acm->urb_task); | 1116 | tasklet_disable(&acm->urb_task); |
1117 | 1117 | ||
1118 | usb_kill_urb(acm->ctrlurb); | 1118 | usb_kill_urb(acm->ctrlurb); |
1119 | usb_kill_urb(acm->writeurb); | 1119 | for(i = 0; i < ACM_NW; i++) |
1120 | usb_kill_urb(acm->wb[i].urb); | ||
1120 | for (i = 0; i < acm->rx_buflimit; i++) | 1121 | for (i = 0; i < acm->rx_buflimit; i++) |
1121 | usb_kill_urb(acm->ru[i].urb); | 1122 | usb_kill_urb(acm->ru[i].urb); |
1122 | 1123 | ||
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 8df6a57dcf9e..046e064b033a 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h | |||
@@ -59,7 +59,7 @@ | |||
59 | * when processing onlcr, so we only need 2 buffers. These values must be | 59 | * when processing onlcr, so we only need 2 buffers. These values must be |
60 | * powers of 2. | 60 | * powers of 2. |
61 | */ | 61 | */ |
62 | #define ACM_NW 2 | 62 | #define ACM_NW 16 |
63 | #define ACM_NR 16 | 63 | #define ACM_NR 16 |
64 | 64 | ||
65 | struct acm_wb { | 65 | struct acm_wb { |
@@ -67,6 +67,8 @@ struct acm_wb { | |||
67 | dma_addr_t dmah; | 67 | dma_addr_t dmah; |
68 | int len; | 68 | int len; |
69 | int use; | 69 | int use; |
70 | struct urb *urb; | ||
71 | struct acm *instance; | ||
70 | }; | 72 | }; |
71 | 73 | ||
72 | struct acm_rb { | 74 | struct acm_rb { |
@@ -88,7 +90,7 @@ struct acm { | |||
88 | struct usb_interface *control; /* control interface */ | 90 | struct usb_interface *control; /* control interface */ |
89 | struct usb_interface *data; /* data interface */ | 91 | struct usb_interface *data; /* data interface */ |
90 | struct tty_struct *tty; /* the corresponding tty */ | 92 | struct tty_struct *tty; /* the corresponding tty */ |
91 | struct urb *ctrlurb, *writeurb; /* urbs */ | 93 | struct urb *ctrlurb; /* urbs */ |
92 | u8 *ctrl_buffer; /* buffers of urbs */ | 94 | u8 *ctrl_buffer; /* buffers of urbs */ |
93 | dma_addr_t ctrl_dma; /* dma handles of buffers */ | 95 | dma_addr_t ctrl_dma; /* dma handles of buffers */ |
94 | u8 *country_codes; /* country codes from device */ | 96 | u8 *country_codes; /* country codes from device */ |
@@ -103,7 +105,6 @@ struct acm { | |||
103 | struct list_head spare_read_urbs; | 105 | struct list_head spare_read_urbs; |
104 | struct list_head spare_read_bufs; | 106 | struct list_head spare_read_bufs; |
105 | struct list_head filled_read_bufs; | 107 | struct list_head filled_read_bufs; |
106 | int write_current; /* current write buffer */ | ||
107 | int write_used; /* number of non-empty write buffers */ | 108 | int write_used; /* number of non-empty write buffers */ |
108 | int write_ready; /* write urb is not running */ | 109 | int write_ready; /* write urb is not running */ |
109 | spinlock_t write_lock; | 110 | spinlock_t write_lock; |