Skip to content

Commit

Permalink
usb: misc: usbtest: add bulk queue test
Browse files Browse the repository at this point in the history
The bulk queue tests are used to show 'best performance' for bulk
transfer, we are often asked this question by users. The implementation
is the same with iso test, that is queue request at interrupt completion,
so we reuse the iso structures, and rename them as common one.

It's result should be very close to IC simulation, in order
to get that, the device side should also need to prepare enough
queue.

We have got the 'best performance' (IN: 41MB, OUT: 39MB) at i.mx platform
(USB2, ARM Cortex A9, stream mode need to enable) with below command:

Host side:
modprobe usbtest
./testusb -a -t 27 -g 64 -s 16384
./testusb -a -t 28 -g 64 -s 16384
Gadget side:
modprobe g_zero loopdefault=1 qlen=64 buflen=16384

Signed-off-by: Peter Chen <peter.chen@freescale.com>
Cc: Greg KH <gregkh@linuxfoundation.org>
Cc: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Felipe Balbi <balbi@ti.com>
  • Loading branch information
Peter Chen authored and Felipe Balbi committed Oct 13, 2015
1 parent 44e4a60 commit 145f48c
Showing 1 changed file with 74 additions and 31 deletions.
105 changes: 74 additions & 31 deletions drivers/usb/misc/usbtest.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
static int override_alt = -1;
module_param_named(alt, override_alt, int, 0644);
MODULE_PARM_DESC(alt, ">= 0 to override altsetting selection");
static void complicated_callback(struct urb *urb);

/*-------------------------------------------------------------------------*/

Expand Down Expand Up @@ -239,7 +240,8 @@ static struct urb *usbtest_alloc_urb(
unsigned long bytes,
unsigned transfer_flags,
unsigned offset,
u8 bInterval)
u8 bInterval,
usb_complete_t complete_fn)
{
struct urb *urb;

Expand All @@ -248,10 +250,10 @@ static struct urb *usbtest_alloc_urb(
return urb;

if (bInterval)
usb_fill_int_urb(urb, udev, pipe, NULL, bytes, simple_callback,
usb_fill_int_urb(urb, udev, pipe, NULL, bytes, complete_fn,
NULL, bInterval);
else
usb_fill_bulk_urb(urb, udev, pipe, NULL, bytes, simple_callback,
usb_fill_bulk_urb(urb, udev, pipe, NULL, bytes, complete_fn,
NULL);

urb->interval = (udev->speed == USB_SPEED_HIGH)
Expand Down Expand Up @@ -296,7 +298,17 @@ static struct urb *simple_alloc_urb(
u8 bInterval)
{
return usbtest_alloc_urb(udev, pipe, bytes, URB_NO_TRANSFER_DMA_MAP, 0,
bInterval);
bInterval, simple_callback);
}

static struct urb *complicated_alloc_urb(
struct usb_device *udev,
int pipe,
unsigned long bytes,
u8 bInterval)
{
return usbtest_alloc_urb(udev, pipe, bytes, URB_NO_TRANSFER_DMA_MAP, 0,
bInterval, complicated_callback);
}

static unsigned pattern;
Expand Down Expand Up @@ -1795,12 +1807,12 @@ static int ctrl_out(struct usbtest_dev *dev,

/*-------------------------------------------------------------------------*/

/* ISO tests ... mimics common usage
/* ISO/BULK tests ... mimics common usage
* - buffer length is split into N packets (mostly maxpacket sized)
* - multi-buffers according to sglen
*/

struct iso_context {
struct transfer_context {
unsigned count;
unsigned pending;
spinlock_t lock;
Expand All @@ -1809,11 +1821,12 @@ struct iso_context {
unsigned long errors;
unsigned long packet_count;
struct usbtest_dev *dev;
bool is_iso;
};

static void iso_callback(struct urb *urb)
static void complicated_callback(struct urb *urb)
{
struct iso_context *ctx = urb->context;
struct transfer_context *ctx = urb->context;

spin_lock(&ctx->lock);
ctx->count--;
Expand All @@ -1822,7 +1835,7 @@ static void iso_callback(struct urb *urb)
if (urb->error_count > 0)
ctx->errors += urb->error_count;
else if (urb->status != 0)
ctx->errors += urb->number_of_packets;
ctx->errors += (ctx->is_iso ? urb->number_of_packets : 1);
else if (urb->actual_length != urb->transfer_buffer_length)
ctx->errors++;
else if (check_guard_bytes(ctx->dev, urb) != 0)
Expand Down Expand Up @@ -1909,18 +1922,18 @@ static struct urb *iso_alloc_urb(
urb->iso_frame_desc[i].offset = maxp * i;
}

urb->complete = iso_callback;
urb->complete = complicated_callback;
/* urb->context = SET BY CALLER */
urb->interval = 1 << (desc->bInterval - 1);
urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
return urb;
}

static int
test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param,
test_queue(struct usbtest_dev *dev, struct usbtest_param *param,
int pipe, struct usb_endpoint_descriptor *desc, unsigned offset)
{
struct iso_context context;
struct transfer_context context;
struct usb_device *udev;
unsigned i;
unsigned long packets = 0;
Expand All @@ -1930,20 +1943,20 @@ test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param,
memset(&context, 0, sizeof(context));
context.count = param->iterations * param->sglen;
context.dev = dev;
context.is_iso = !!desc;
init_completion(&context.done);
spin_lock_init(&context.lock);

udev = testdev_to_usbdev(dev);
dev_info(&dev->intf->dev,
"iso period %d %sframes, wMaxPacket %d, transactions: %d\n",
1 << (desc->bInterval - 1),
(udev->speed == USB_SPEED_HIGH) ? "micro" : "",
usb_endpoint_maxp(desc) & 0x7ff,
1 + (0x3 & (usb_endpoint_maxp(desc) >> 11)));

for (i = 0; i < param->sglen; i++) {
urbs[i] = iso_alloc_urb(udev, pipe, desc,
if (context.is_iso)
urbs[i] = iso_alloc_urb(udev, pipe, desc,
param->length, offset);
else
urbs[i] = complicated_alloc_urb(udev, pipe,
param->length, 0);

if (!urbs[i]) {
status = -ENOMEM;
goto fail;
Expand All @@ -1952,11 +1965,21 @@ test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param,
urbs[i]->context = &context;
}
packets *= param->iterations;
dev_info(&dev->intf->dev,
"total %lu msec (%lu packets)\n",
(packets * (1 << (desc->bInterval - 1)))
/ ((udev->speed == USB_SPEED_HIGH) ? 8 : 1),
packets);

if (context.is_iso) {
dev_info(&dev->intf->dev,
"iso period %d %sframes, wMaxPacket %d, transactions: %d\n",
1 << (desc->bInterval - 1),
(udev->speed == USB_SPEED_HIGH) ? "micro" : "",
usb_endpoint_maxp(desc) & 0x7ff,
1 + (0x3 & (usb_endpoint_maxp(desc) >> 11)));

dev_info(&dev->intf->dev,
"total %lu msec (%lu packets)\n",
(packets * (1 << (desc->bInterval - 1)))
/ ((udev->speed == USB_SPEED_HIGH) ? 8 : 1),
packets);
}

spin_lock_irq(&context.lock);
for (i = 0; i < param->sglen; i++) {
Expand Down Expand Up @@ -1993,7 +2016,8 @@ test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param,
;
else if (context.submit_error)
status = -EACCES;
else if (context.errors > context.packet_count / 10)
else if (context.errors >
(context.is_iso ? context.packet_count / 10 : 0))
status = -EIO;
return status;

Expand All @@ -2014,8 +2038,8 @@ static int test_unaligned_bulk(
const char *label)
{
int retval;
struct urb *urb = usbtest_alloc_urb(
testdev_to_usbdev(tdev), pipe, length, transfer_flags, 1, 0);
struct urb *urb = usbtest_alloc_urb(testdev_to_usbdev(tdev),
pipe, length, transfer_flags, 1, 0, simple_callback);

if (!urb)
return -ENOMEM;
Expand Down Expand Up @@ -2342,7 +2366,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
param->iterations,
param->sglen, param->length);
/* FIRMWARE: iso sink */
retval = test_iso_queue(dev, param,
retval = test_queue(dev, param,
dev->out_iso_pipe, dev->iso_out, 0);
break;

Expand All @@ -2355,7 +2379,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
param->iterations,
param->sglen, param->length);
/* FIRMWARE: iso source */
retval = test_iso_queue(dev, param,
retval = test_queue(dev, param,
dev->in_iso_pipe, dev->iso_in, 0);
break;

Expand Down Expand Up @@ -2436,7 +2460,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
"TEST 22: write %d iso odd, %d entries of %d bytes\n",
param->iterations,
param->sglen, param->length);
retval = test_iso_queue(dev, param,
retval = test_queue(dev, param,
dev->out_iso_pipe, dev->iso_out, 1);
break;

Expand All @@ -2447,7 +2471,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
"TEST 23: read %d iso odd, %d entries of %d bytes\n",
param->iterations,
param->sglen, param->length);
retval = test_iso_queue(dev, param,
retval = test_queue(dev, param,
dev->in_iso_pipe, dev->iso_in, 1);
break;

Expand Down Expand Up @@ -2504,6 +2528,25 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
retval = simple_io(dev, urb, param->iterations, 0, 0, "test26");
simple_free_urb(urb);
break;
case 27:
/* We do performance test, so ignore data compare */
if (dev->out_pipe == 0 || param->sglen == 0 || pattern != 0)
break;
dev_info(&intf->dev,
"TEST 27: bulk write %dMbytes\n", (param->iterations *
param->sglen * param->length) / (1024 * 1024));
retval = test_queue(dev, param,
dev->out_pipe, NULL, 0);
break;
case 28:
if (dev->in_pipe == 0 || param->sglen == 0 || pattern != 0)
break;
dev_info(&intf->dev,
"TEST 28: bulk read %dMbytes\n", (param->iterations *
param->sglen * param->length) / (1024 * 1024));
retval = test_queue(dev, param,
dev->in_pipe, NULL, 0);
break;
}
do_gettimeofday(&param->duration);
param->duration.tv_sec -= start.tv_sec;
Expand Down

0 comments on commit 145f48c

Please sign in to comment.