From 869410f82cbbb1464772046d87de8d18a916e706 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 14 Apr 2011 11:21:04 -0400 Subject: USB: add queued-unlinks test case to usbtest driver This patch (as1452b) adds a new test case to the usbtest driver. Test 24 exercises the unlink-from-queue pathways in the host. It queues a user-specified number of bulk-OUT URBs of user-specified size, unlinks the fourth- and second-from-last URBs in the queue, and then waits to see if all the URBs complete in the expected way (except of course that the unlinked URBs might complete normally, if they weren't unlinked soon enough). This new test has confirmed the existence of a bug in the ehci-hcd driver, to be fixed by a separate patch. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbtest.c | 120 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 118 insertions(+), 2 deletions(-) (limited to 'drivers/usb/misc/usbtest.c') diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 388cc128072a..58a5685fb7d1 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -1195,6 +1195,104 @@ static int unlink_simple(struct usbtest_dev *dev, int pipe, int len) /*-------------------------------------------------------------------------*/ +struct queued_ctx { + struct completion complete; + atomic_t pending; + unsigned num; + int status; + struct urb **urbs; +}; + +static void unlink_queued_callback(struct urb *urb) +{ + int status = urb->status; + struct queued_ctx *ctx = urb->context; + + if (ctx->status) + goto done; + if (urb == ctx->urbs[ctx->num - 4] || urb == ctx->urbs[ctx->num - 2]) { + if (status == -ECONNRESET) + goto done; + /* What error should we report if the URB completed normally? */ + } + if (status != 0) + ctx->status = status; + + done: + if (atomic_dec_and_test(&ctx->pending)) + complete(&ctx->complete); +} + +static int unlink_queued(struct usbtest_dev *dev, int pipe, unsigned num, + unsigned size) +{ + struct queued_ctx ctx; + struct usb_device *udev = testdev_to_usbdev(dev); + void *buf; + dma_addr_t buf_dma; + int i; + int retval = -ENOMEM; + + init_completion(&ctx.complete); + atomic_set(&ctx.pending, 1); /* One more than the actual value */ + ctx.num = num; + ctx.status = 0; + + buf = usb_alloc_coherent(udev, size, GFP_KERNEL, &buf_dma); + if (!buf) + return retval; + memset(buf, 0, size); + + /* Allocate and init the urbs we'll queue */ + ctx.urbs = kcalloc(num, sizeof(struct urb *), GFP_KERNEL); + if (!ctx.urbs) + goto free_buf; + for (i = 0; i < num; i++) { + ctx.urbs[i] = usb_alloc_urb(0, GFP_KERNEL); + if (!ctx.urbs[i]) + goto free_urbs; + usb_fill_bulk_urb(ctx.urbs[i], udev, pipe, buf, size, + unlink_queued_callback, &ctx); + ctx.urbs[i]->transfer_dma = buf_dma; + ctx.urbs[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + } + + /* Submit all the URBs and then unlink URBs num - 4 and num - 2. */ + for (i = 0; i < num; i++) { + atomic_inc(&ctx.pending); + retval = usb_submit_urb(ctx.urbs[i], GFP_KERNEL); + if (retval != 0) { + dev_err(&dev->intf->dev, "submit urbs[%d] fail %d\n", + i, retval); + atomic_dec(&ctx.pending); + ctx.status = retval; + break; + } + } + if (i == num) { + usb_unlink_urb(ctx.urbs[num - 4]); + usb_unlink_urb(ctx.urbs[num - 2]); + } else { + while (--i >= 0) + usb_unlink_urb(ctx.urbs[i]); + } + + if (atomic_dec_and_test(&ctx.pending)) /* The extra count */ + complete(&ctx.complete); + wait_for_completion(&ctx.complete); + retval = ctx.status; + + free_urbs: + for (i = 0; i < num; i++) + usb_free_urb(ctx.urbs[i]); + kfree(ctx.urbs); + free_buf: + usb_free_coherent(udev, size, buf, buf_dma); + return retval; +} + +/*-------------------------------------------------------------------------*/ + static int verify_not_halted(struct usbtest_dev *tdev, int ep, struct urb *urb) { int retval; @@ -1970,8 +2068,6 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) dev->in_iso_pipe, dev->iso_in, 0); break; - /* FIXME unlink from queue (ring with N urbs) */ - /* FIXME scatterlist cancel (needs helper thread) */ /* Tests for bulk I/O using DMA mapping by core and odd address */ @@ -2064,6 +2160,26 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) dev->in_iso_pipe, dev->iso_in, 1); break; + /* unlink URBs from a bulk-OUT queue */ + case 24: + if (dev->out_pipe == 0 || !param->length || param->sglen < 4) + break; + retval = 0; + dev_info(&intf->dev, "TEST 17: unlink from %d queues of " + "%d %d-byte writes\n", + param->iterations, param->sglen, param->length); + for (i = param->iterations; retval == 0 && i > 0; --i) { + retval = unlink_queued(dev, dev->out_pipe, + param->sglen, param->length); + if (retval) { + dev_err(&intf->dev, + "unlink queued writes failed %d, " + "iterations left %d\n", retval, i); + break; + } + } + break; + } do_gettimeofday(¶m->duration); param->duration.tv_sec -= start.tv_sec; -- cgit v1.2.3 From 14b76ed9e613965e02603c31febd85c96e32e094 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 14 Apr 2011 15:45:42 +0200 Subject: usb/usbtest: print super on super speed Signed-off-by: Sebastian Andrzej Siewior Reviewed-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbtest.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/usb/misc/usbtest.c') diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 58a5685fb7d1..7e74741ddf7b 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -2308,6 +2308,9 @@ usbtest_probe(struct usb_interface *intf, const struct usb_device_id *id) case USB_SPEED_HIGH: tmp = "high"; break; + case USB_SPEED_SUPER: + tmp = "super"; + break; default: tmp = "unknown"; break; -- cgit v1.2.3 From 67e7d64bcece93c84fd6e3832e7c6d91e57c06dc Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 14 Apr 2011 16:17:21 +0200 Subject: usb/usbtest: fix test10 on superpseed dummy_hcd + g_zero: |./testusb -a -s 1024 -v 1024 -t 10 | usbtest 2-1:3.0: subtest 14 error, status 0 with patch: ./testusb -a -t 10 |unknown speed /proc/bus/usb/001/002 | dummy_udc dummy_udc: disabled ep-a | dummy_udc dummy_udc: disabled ep-b | dummy_udc dummy_udc: enabled ep-a (ep1in-bulk) maxpacket 1024 | dummy_udc dummy_udc: enabled ep-b (ep2out-bulk) maxpacket 1024 | zero gadget: source/sink enabled | usbtest 1-1:3.0: TEST 10: queue 32 control calls, 1000 times | dummy_hcd dummy_hcd: timer fired with no URBs pending? |/proc/bus/usb/001/002 test 10, 0.022370 secs Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbtest.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/usb/misc/usbtest.c') diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 7e74741ddf7b..7e0bb2af8e63 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -1030,6 +1030,8 @@ test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param *param) req.wValue = cpu_to_le16((USB_DT_DEVICE << 8) | 0); /* device descriptor size == 18 bytes */ len = udev->descriptor.bMaxPacketSize0; + if (udev->speed == USB_SPEED_SUPER) + len = 512; switch (len) { case 8: len = 24; -- cgit v1.2.3 From 304b0b572c66bcd89df13e856db16503609c1a24 Mon Sep 17 00:00:00 2001 From: Greg Dietsche Date: Sun, 8 May 2011 22:51:43 -0500 Subject: usb: fix warning in usbtest module v2 On amd64 unsigned is not as wide as pointer and this causes a compiler warning. Switching to unsigned long corrects the problem. Signed-off-by: Greg Dietsche Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbtest.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb/misc/usbtest.c') diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 7e0bb2af8e63..c5676202d74a 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -268,9 +268,9 @@ static inline void simple_fill_buf(struct urb *urb) } } -static inline unsigned buffer_offset(void *buf) +static inline unsigned long buffer_offset(void *buf) { - return (unsigned)buf & (ARCH_KMALLOC_MINALIGN - 1); + return (unsigned long)buf & (ARCH_KMALLOC_MINALIGN - 1); } static int check_guard_bytes(struct usbtest_dev *tdev, struct urb *urb) @@ -329,7 +329,7 @@ static int simple_check_buf(struct usbtest_dev *tdev, struct urb *urb) static void simple_free_urb(struct urb *urb) { - unsigned offset = buffer_offset(urb->transfer_buffer); + unsigned long offset = buffer_offset(urb->transfer_buffer); if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) usb_free_coherent( -- cgit v1.2.3