linux-media.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v5 0/9] Asynchronous UVC
@ 2018-11-06 21:27 Kieran Bingham
  2018-11-06 21:27 ` [PATCH v5 1/9] media: uvcvideo: Refactor URB descriptors Kieran Bingham
                   ` (10 more replies)
  0 siblings, 11 replies; 27+ messages in thread
From: Kieran Bingham @ 2018-11-06 21:27 UTC (permalink / raw)
  To: Laurent Pinchart, linux-media
  Cc: Guennadi Liakhovetski, Olivier BRAUN, Troy Kisky, Randy Dunlap,
	Philipp Zabel, Ezequiel Garcia, Kieran Bingham

From: Kieran Bingham <kieran.bingham@ideasonboard.com>

The Linux UVC driver has long provided adequate performance capabilities for
web-cams and low data rate video devices in Linux while resolutions were low.

Modern USB cameras are now capable of high data rates thanks to USB3 with
1080p, and even 4k capture resolutions supported.

Cameras such as the Stereolabs ZED (bulk transfers) or the Logitech BRIO
(isochronous transfers) can generate more data than an embedded ARM core is
able to process on a single core, resulting in frame loss.

A large part of this performance impact is from the requirement to
‘memcpy’ frames out from URB packets to destination frames. This unfortunate
requirement is due to the UVC protocol allowing a variable length header, and
thus it is not possible to provide the target frame buffers directly.

Extra throughput is possible by moving the actual memcpy actions to a work
queue, and moving the memcpy out of interrupt context thus allowing work tasks
to be scheduled across multiple cores.

This series has been tested on both the ZED and BRIO cameras on arm64
platforms, and with thanks to Randy Dunlap, a Dynex 1.3MP Webcam, a Sonix USB2
Camera, and a built in Toshiba Laptop camera, and with thanks to Philipp Zabel
for testing on a Lite-On internal Laptop Webcam, Logitech C910 (USB2 isoc),
Oculus Sensor (USB3 isoc), and Microsoft HoloLens Sensors (USB3 bulk).

As far as I am aware iSight devices, and devices which use UVC to encode data
(output device) have not yet been tested - but should find no ill effect (at
least not until they are tested of course :D )

Tested-by: Randy Dunlap <rdunlap@infradead.org>
Tested-by: Philipp Zabel <philipp.zabel@gmail.com>

v2:
 - Fix race reported by Guennadi

v3:
 - Fix similar race reported by Laurent
 - Only queue work if required (encode/isight do not queue work)
 - Refactor/Rename variables for clarity

v4:
 - (Yet another) Rework of the uninitialise path.
   This time to hopefully clean up the shutdown races for good.
   use usb_poison_urb() to halt all URBs, then flush the work queue
   before freeing.
 - Rebase to latest linux-media/master

v5:
 - Provide lockdep validation
 - rename uvc_queue_requeue -> uvc_queue_buffer_requeue()
 - Fix comments and periods throughout
 - Rebase to media/v4.20-2
 - Use GFP_KERNEL allocation in uvc_video_copy_data_work()
 - Fix function documentation for uvc_video_copy_data_work()
 - Add periods to the end of sentences
 - Rename 'decode' variable to 'op' in uvc_video_decode_data()
 - Move uvc_urb->async_operations initialisation to before use
 - Move async workqueue to match uvc_streaming lifetime instead of
   streamon/streamoff
 - bracket the for_each_uvc_urb() macro

 - New patches added to series:
    media: uvcvideo: Split uvc_video_enable into two
    media: uvcvideo: Rename uvc_{un,}init_video()
    media: uvcvideo: Utilise for_each_uvc_urb iterator

Kieran Bingham (9):
  media: uvcvideo: Refactor URB descriptors
  media: uvcvideo: Convert decode functions to use new context structure
  media: uvcvideo: Protect queue internals with helper
  media: uvcvideo: queue: Simplify spin-lock usage
  media: uvcvideo: queue: Support asynchronous buffer handling
  media: uvcvideo: Move decode processing to process context
  media: uvcvideo: Split uvc_video_enable into two
  media: uvcvideo: Rename uvc_{un,}init_video()
  media: uvcvideo: Utilise for_each_uvc_urb iterator

 drivers/media/usb/uvc/uvc_driver.c |   2 +-
 drivers/media/usb/uvc/uvc_isight.c |   6 +-
 drivers/media/usb/uvc/uvc_queue.c  | 110 +++++++++---
 drivers/media/usb/uvc/uvc_video.c  | 282 +++++++++++++++++++-----------
 drivers/media/usb/uvc/uvcvideo.h   |  65 ++++++-
 5 files changed, 331 insertions(+), 134 deletions(-)

base-commit: dafb7f9aef2fd44991ff1691721ff765a23be27b
-- 
git-series 0.9.1

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v5 1/9] media: uvcvideo: Refactor URB descriptors
  2018-11-06 21:27 [PATCH v5 0/9] Asynchronous UVC Kieran Bingham
@ 2018-11-06 21:27 ` Kieran Bingham
  2018-11-06 21:27 ` [PATCH v5 2/9] media: uvcvideo: Convert decode functions to use new context structure Kieran Bingham
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 27+ messages in thread
From: Kieran Bingham @ 2018-11-06 21:27 UTC (permalink / raw)
  To: Laurent Pinchart, linux-media
  Cc: Guennadi Liakhovetski, Olivier BRAUN, Troy Kisky, Randy Dunlap,
	Philipp Zabel, Ezequiel Garcia, Kieran Bingham

From: Kieran Bingham <kieran.bingham@ideasonboard.com>

We currently store three separate arrays for each URB reference we hold.

Objectify the data needed to track URBs into a single uvc_urb structure,
allowing better object management and tracking of the URB.

All accesses to the data pointers through stream, are converted to use a
uvc_urb pointer for consistency.

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

---
v2:
 - Re-describe URB context structure
 - Re-name uvc_urb->{urb_buffer,urb_dma}{buffer,dma}

v3:
 - No change

v4:
 - Rebase on top of linux-media/master (v4.16-rc4, metadata additions)

 drivers/media/usb/uvc/uvc_video.c | 49 +++++++++++++++++++-------------
 drivers/media/usb/uvc/uvcvideo.h  | 18 ++++++++++--
 2 files changed, 45 insertions(+), 22 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index 86a99f461fd8..113881bed2a4 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -1506,14 +1506,16 @@ static void uvc_free_urb_buffers(struct uvc_streaming *stream)
 	unsigned int i;
 
 	for (i = 0; i < UVC_URBS; ++i) {
-		if (stream->urb_buffer[i]) {
+		struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
+
+		if (uvc_urb->buffer) {
 #ifndef CONFIG_DMA_NONCOHERENT
 			usb_free_coherent(stream->dev->udev, stream->urb_size,
-				stream->urb_buffer[i], stream->urb_dma[i]);
+					uvc_urb->buffer, uvc_urb->dma);
 #else
-			kfree(stream->urb_buffer[i]);
+			kfree(uvc_urb->buffer);
 #endif
-			stream->urb_buffer[i] = NULL;
+			uvc_urb->buffer = NULL;
 		}
 	}
 
@@ -1551,16 +1553,18 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,
 	/* Retry allocations until one succeed. */
 	for (; npackets > 1; npackets /= 2) {
 		for (i = 0; i < UVC_URBS; ++i) {
+			struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
+
 			stream->urb_size = psize * npackets;
 #ifndef CONFIG_DMA_NONCOHERENT
-			stream->urb_buffer[i] = usb_alloc_coherent(
+			uvc_urb->buffer = usb_alloc_coherent(
 				stream->dev->udev, stream->urb_size,
-				gfp_flags | __GFP_NOWARN, &stream->urb_dma[i]);
+				gfp_flags | __GFP_NOWARN, &uvc_urb->dma);
 #else
-			stream->urb_buffer[i] =
+			uvc_urb->buffer =
 			    kmalloc(stream->urb_size, gfp_flags | __GFP_NOWARN);
 #endif
-			if (!stream->urb_buffer[i]) {
+			if (!uvc_urb->buffer) {
 				uvc_free_urb_buffers(stream);
 				break;
 			}
@@ -1590,13 +1594,15 @@ static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers)
 	uvc_video_stats_stop(stream);
 
 	for (i = 0; i < UVC_URBS; ++i) {
-		urb = stream->urb[i];
+		struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
+
+		urb = uvc_urb->urb;
 		if (urb == NULL)
 			continue;
 
 		usb_kill_urb(urb);
 		usb_free_urb(urb);
-		stream->urb[i] = NULL;
+		uvc_urb->urb = NULL;
 	}
 
 	if (free_buffers)
@@ -1651,6 +1657,8 @@ static int uvc_init_video_isoc(struct uvc_streaming *stream,
 	size = npackets * psize;
 
 	for (i = 0; i < UVC_URBS; ++i) {
+		struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
+
 		urb = usb_alloc_urb(npackets, gfp_flags);
 		if (urb == NULL) {
 			uvc_uninit_video(stream, 1);
@@ -1663,12 +1671,12 @@ static int uvc_init_video_isoc(struct uvc_streaming *stream,
 				ep->desc.bEndpointAddress);
 #ifndef CONFIG_DMA_NONCOHERENT
 		urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
-		urb->transfer_dma = stream->urb_dma[i];
+		urb->transfer_dma = uvc_urb->dma;
 #else
 		urb->transfer_flags = URB_ISO_ASAP;
 #endif
 		urb->interval = ep->desc.bInterval;
-		urb->transfer_buffer = stream->urb_buffer[i];
+		urb->transfer_buffer = uvc_urb->buffer;
 		urb->complete = uvc_video_complete;
 		urb->number_of_packets = npackets;
 		urb->transfer_buffer_length = size;
@@ -1678,7 +1686,7 @@ static int uvc_init_video_isoc(struct uvc_streaming *stream,
 			urb->iso_frame_desc[j].length = psize;
 		}
 
-		stream->urb[i] = urb;
+		uvc_urb->urb = urb;
 	}
 
 	return 0;
@@ -1717,21 +1725,22 @@ static int uvc_init_video_bulk(struct uvc_streaming *stream,
 		size = 0;
 
 	for (i = 0; i < UVC_URBS; ++i) {
+		struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
+
 		urb = usb_alloc_urb(0, gfp_flags);
 		if (urb == NULL) {
 			uvc_uninit_video(stream, 1);
 			return -ENOMEM;
 		}
 
-		usb_fill_bulk_urb(urb, stream->dev->udev, pipe,
-			stream->urb_buffer[i], size, uvc_video_complete,
-			stream);
+		usb_fill_bulk_urb(urb, stream->dev->udev, pipe, uvc_urb->buffer,
+				  size, uvc_video_complete, stream);
 #ifndef CONFIG_DMA_NONCOHERENT
 		urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
-		urb->transfer_dma = stream->urb_dma[i];
+		urb->transfer_dma = uvc_urb->dma;
 #endif
 
-		stream->urb[i] = urb;
+		uvc_urb->urb = urb;
 	}
 
 	return 0;
@@ -1822,7 +1831,9 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
 
 	/* Submit the URBs. */
 	for (i = 0; i < UVC_URBS; ++i) {
-		ret = usb_submit_urb(stream->urb[i], gfp_flags);
+		struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
+
+		ret = usb_submit_urb(uvc_urb->urb, gfp_flags);
 		if (ret < 0) {
 			uvc_printk(KERN_ERR, "Failed to submit URB %u "
 					"(%d).\n", i, ret);
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index c0cbd833d0a4..29104b968f12 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -487,6 +487,20 @@ struct uvc_stats_stream {
 
 #define UVC_METATADA_BUF_SIZE 1024
 
+/**
+ * struct uvc_urb - URB context management structure
+ *
+ * @urb: the URB described by this context structure
+ * @buffer: memory storage for the URB
+ * @dma: DMA coherent addressing for the urb_buffer
+ */
+struct uvc_urb {
+	struct urb *urb;
+
+	char *buffer;
+	dma_addr_t dma;
+};
+
 struct uvc_streaming {
 	struct list_head list;
 	struct uvc_device *dev;
@@ -535,9 +549,7 @@ struct uvc_streaming {
 		u32 max_payload_size;
 	} bulk;
 
-	struct urb *urb[UVC_URBS];
-	char *urb_buffer[UVC_URBS];
-	dma_addr_t urb_dma[UVC_URBS];
+	struct uvc_urb uvc_urb[UVC_URBS];
 	unsigned int urb_size;
 
 	u32 sequence;
-- 
git-series 0.9.1

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v5 2/9] media: uvcvideo: Convert decode functions to use new context structure
  2018-11-06 21:27 [PATCH v5 0/9] Asynchronous UVC Kieran Bingham
  2018-11-06 21:27 ` [PATCH v5 1/9] media: uvcvideo: Refactor URB descriptors Kieran Bingham
@ 2018-11-06 21:27 ` Kieran Bingham
  2018-11-06 21:27 ` [PATCH v5 3/9] media: uvcvideo: Protect queue internals with helper Kieran Bingham
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 27+ messages in thread
From: Kieran Bingham @ 2018-11-06 21:27 UTC (permalink / raw)
  To: Laurent Pinchart, linux-media
  Cc: Guennadi Liakhovetski, Olivier BRAUN, Troy Kisky, Randy Dunlap,
	Philipp Zabel, Ezequiel Garcia, Kieran Bingham

From: Kieran Bingham <kieran.bingham@ideasonboard.com>

The URB completion handlers currently reference the stream context.

Now that each URB has its own context structure, convert the decode (and
one encode) functions to utilise this context for URB management.

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

---
v2:
 - fix checkpatch warning (pre-existing in code)

v3: (none)

v4:
 - Rebase on top of linux-media/master (v4.16-rc4, metadata additions)

 drivers/media/usb/uvc/uvc_isight.c |  6 ++++--
 drivers/media/usb/uvc/uvc_video.c  | 26 ++++++++++++++++++--------
 drivers/media/usb/uvc/uvcvideo.h   |  8 +++++---
 3 files changed, 27 insertions(+), 13 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_isight.c b/drivers/media/usb/uvc/uvc_isight.c
index 81e6f2187bfb..39a4e4482b23 100644
--- a/drivers/media/usb/uvc/uvc_isight.c
+++ b/drivers/media/usb/uvc/uvc_isight.c
@@ -99,9 +99,11 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf,
 	return 0;
 }
 
-void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream,
-			struct uvc_buffer *buf, struct uvc_buffer *meta_buf)
+void uvc_video_decode_isight(struct uvc_urb *uvc_urb, struct uvc_buffer *buf,
+			struct uvc_buffer *meta_buf)
 {
+	struct urb *urb = uvc_urb->urb;
+	struct uvc_streaming *stream = uvc_urb->stream;
 	int ret, i;
 
 	for (i = 0; i < urb->number_of_packets; ++i) {
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index 113881bed2a4..6d4384695964 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -1291,9 +1291,11 @@ static void uvc_video_next_buffers(struct uvc_streaming *stream,
 	*video_buf = uvc_queue_next_buffer(&stream->queue, *video_buf);
 }
 
-static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream,
+static void uvc_video_decode_isoc(struct uvc_urb *uvc_urb,
 			struct uvc_buffer *buf, struct uvc_buffer *meta_buf)
 {
+	struct urb *urb = uvc_urb->urb;
+	struct uvc_streaming *stream = uvc_urb->stream;
 	u8 *mem;
 	int ret, i;
 
@@ -1334,9 +1336,11 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream,
 	}
 }
 
-static void uvc_video_decode_bulk(struct urb *urb, struct uvc_streaming *stream,
+static void uvc_video_decode_bulk(struct uvc_urb *uvc_urb,
 			struct uvc_buffer *buf, struct uvc_buffer *meta_buf)
 {
+	struct urb *urb = uvc_urb->urb;
+	struct uvc_streaming *stream = uvc_urb->stream;
 	u8 *mem;
 	int len, ret;
 
@@ -1402,9 +1406,12 @@ static void uvc_video_decode_bulk(struct urb *urb, struct uvc_streaming *stream,
 	}
 }
 
-static void uvc_video_encode_bulk(struct urb *urb, struct uvc_streaming *stream,
+static void uvc_video_encode_bulk(struct uvc_urb *uvc_urb,
 	struct uvc_buffer *buf, struct uvc_buffer *meta_buf)
 {
+	struct urb *urb = uvc_urb->urb;
+	struct uvc_streaming *stream = uvc_urb->stream;
+
 	u8 *mem = urb->transfer_buffer;
 	int len = stream->urb_size, ret;
 
@@ -1447,7 +1454,8 @@ static void uvc_video_encode_bulk(struct urb *urb, struct uvc_streaming *stream,
 
 static void uvc_video_complete(struct urb *urb)
 {
-	struct uvc_streaming *stream = urb->context;
+	struct uvc_urb *uvc_urb = urb->context;
+	struct uvc_streaming *stream = uvc_urb->stream;
 	struct uvc_video_queue *queue = &stream->queue;
 	struct uvc_video_queue *qmeta = &stream->meta.queue;
 	struct vb2_queue *vb2_qmeta = stream->meta.vdev.queue;
@@ -1490,7 +1498,7 @@ static void uvc_video_complete(struct urb *urb)
 		spin_unlock_irqrestore(&qmeta->irqlock, flags);
 	}
 
-	stream->decode(urb, stream, buf, buf_meta);
+	stream->decode(uvc_urb, buf, buf_meta);
 
 	if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
 		uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n",
@@ -1568,6 +1576,8 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,
 				uvc_free_urb_buffers(stream);
 				break;
 			}
+
+			uvc_urb->stream = stream;
 		}
 
 		if (i == UVC_URBS) {
@@ -1666,7 +1676,7 @@ static int uvc_init_video_isoc(struct uvc_streaming *stream,
 		}
 
 		urb->dev = stream->dev->udev;
-		urb->context = stream;
+		urb->context = uvc_urb;
 		urb->pipe = usb_rcvisocpipe(stream->dev->udev,
 				ep->desc.bEndpointAddress);
 #ifndef CONFIG_DMA_NONCOHERENT
@@ -1733,8 +1743,8 @@ static int uvc_init_video_bulk(struct uvc_streaming *stream,
 			return -ENOMEM;
 		}
 
-		usb_fill_bulk_urb(urb, stream->dev->udev, pipe, uvc_urb->buffer,
-				  size, uvc_video_complete, stream);
+		usb_fill_bulk_urb(urb, stream->dev->udev, pipe,	uvc_urb->buffer,
+				  size, uvc_video_complete, uvc_urb);
 #ifndef CONFIG_DMA_NONCOHERENT
 		urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
 		urb->transfer_dma = uvc_urb->dma;
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 29104b968f12..f7f8db6fc91a 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -491,11 +491,13 @@ struct uvc_stats_stream {
  * struct uvc_urb - URB context management structure
  *
  * @urb: the URB described by this context structure
+ * @stream: UVC streaming context
  * @buffer: memory storage for the URB
  * @dma: DMA coherent addressing for the urb_buffer
  */
 struct uvc_urb {
 	struct urb *urb;
+	struct uvc_streaming *stream;
 
 	char *buffer;
 	dma_addr_t dma;
@@ -531,8 +533,8 @@ struct uvc_streaming {
 	/* Buffers queue. */
 	unsigned int frozen : 1;
 	struct uvc_video_queue queue;
-	void (*decode) (struct urb *urb, struct uvc_streaming *video,
-			struct uvc_buffer *buf, struct uvc_buffer *meta_buf);
+	void (*decode)(struct uvc_urb *uvc_urb, struct uvc_buffer *buf,
+		       struct uvc_buffer *meta_buf);
 
 	struct {
 		struct video_device vdev;
@@ -818,7 +820,7 @@ struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
 					    u8 epaddr);
 
 /* Quirks support */
-void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream,
+void uvc_video_decode_isight(struct uvc_urb *uvc_urb,
 			     struct uvc_buffer *buf,
 			     struct uvc_buffer *meta_buf);
 
-- 
git-series 0.9.1

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v5 3/9] media: uvcvideo: Protect queue internals with helper
  2018-11-06 21:27 [PATCH v5 0/9] Asynchronous UVC Kieran Bingham
  2018-11-06 21:27 ` [PATCH v5 1/9] media: uvcvideo: Refactor URB descriptors Kieran Bingham
  2018-11-06 21:27 ` [PATCH v5 2/9] media: uvcvideo: Convert decode functions to use new context structure Kieran Bingham
@ 2018-11-06 21:27 ` Kieran Bingham
  2018-11-06 21:27 ` [PATCH v5 4/9] media: uvcvideo: queue: Simplify spin-lock usage Kieran Bingham
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 27+ messages in thread
From: Kieran Bingham @ 2018-11-06 21:27 UTC (permalink / raw)
  To: Laurent Pinchart, linux-media
  Cc: Guennadi Liakhovetski, Olivier BRAUN, Troy Kisky, Randy Dunlap,
	Philipp Zabel, Ezequiel Garcia, Kieran Bingham

From: Kieran Bingham <kieran.bingham@ideasonboard.com>

The URB completion operation obtains the current buffer by reading
directly into the queue internal interface.

Protect this queue abstraction by providing a helper
uvc_queue_get_current_buffer() which can be used by both the decode
task, and the uvc_queue_next_buffer() functions.

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

---

v2:
 - Fix coding style of conditional statements

v3:
 - No change

v4:
 - Rebase on top of linux-media/master (v4.16-rc4, metadata additions)

 drivers/media/usb/uvc/uvc_queue.c | 33 +++++++++++++++++++++++++++-----
 drivers/media/usb/uvc/uvc_video.c |  6 +-----
 drivers/media/usb/uvc/uvcvideo.h  |  1 +-
 3 files changed, 30 insertions(+), 10 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
index 8964e16f2b22..74f9483911d0 100644
--- a/drivers/media/usb/uvc/uvc_queue.c
+++ b/drivers/media/usb/uvc/uvc_queue.c
@@ -430,6 +430,33 @@ void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect)
 	spin_unlock_irqrestore(&queue->irqlock, flags);
 }
 
+/*
+ * uvc_queue_get_current_buffer: Obtain the current working output buffer
+ *
+ * Buffers may span multiple packets, and even URBs, therefore the active buffer
+ * remains on the queue until the EOF marker.
+ */
+static struct uvc_buffer *
+__uvc_queue_get_current_buffer(struct uvc_video_queue *queue)
+{
+	if (list_empty(&queue->irqqueue))
+		return NULL;
+
+	return list_first_entry(&queue->irqqueue, struct uvc_buffer, queue);
+}
+
+struct uvc_buffer *uvc_queue_get_current_buffer(struct uvc_video_queue *queue)
+{
+	struct uvc_buffer *nextbuf;
+	unsigned long flags;
+
+	spin_lock_irqsave(&queue->irqlock, flags);
+	nextbuf = __uvc_queue_get_current_buffer(queue);
+	spin_unlock_irqrestore(&queue->irqlock, flags);
+
+	return nextbuf;
+}
+
 struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
 		struct uvc_buffer *buf)
 {
@@ -446,11 +473,7 @@ struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
 
 	spin_lock_irqsave(&queue->irqlock, flags);
 	list_del(&buf->queue);
-	if (!list_empty(&queue->irqqueue))
-		nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
-					   queue);
-	else
-		nextbuf = NULL;
+	nextbuf = __uvc_queue_get_current_buffer(queue);
 	spin_unlock_irqrestore(&queue->irqlock, flags);
 
 	buf->state = buf->error ? UVC_BUF_STATE_ERROR : UVC_BUF_STATE_DONE;
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index 6d4384695964..7a7779e1b466 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -1484,11 +1484,7 @@ static void uvc_video_complete(struct urb *urb)
 		return;
 	}
 
-	spin_lock_irqsave(&queue->irqlock, flags);
-	if (!list_empty(&queue->irqqueue))
-		buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
-				       queue);
-	spin_unlock_irqrestore(&queue->irqlock, flags);
+	buf = uvc_queue_get_current_buffer(queue);
 
 	if (vb2_qmeta) {
 		spin_lock_irqsave(&qmeta->irqlock, flags);
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index f7f8db6fc91a..bdb6d8daedab 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -725,6 +725,7 @@ int uvc_queue_streamoff(struct uvc_video_queue *queue, enum v4l2_buf_type type);
 void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect);
 struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
 					 struct uvc_buffer *buf);
+struct uvc_buffer *uvc_queue_get_current_buffer(struct uvc_video_queue *queue);
 int uvc_queue_mmap(struct uvc_video_queue *queue,
 		   struct vm_area_struct *vma);
 __poll_t uvc_queue_poll(struct uvc_video_queue *queue, struct file *file,
-- 
git-series 0.9.1

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v5 4/9] media: uvcvideo: queue: Simplify spin-lock usage
  2018-11-06 21:27 [PATCH v5 0/9] Asynchronous UVC Kieran Bingham
                   ` (2 preceding siblings ...)
  2018-11-06 21:27 ` [PATCH v5 3/9] media: uvcvideo: Protect queue internals with helper Kieran Bingham
@ 2018-11-06 21:27 ` Kieran Bingham
  2018-11-06 21:27 ` [PATCH v5 5/9] media: uvcvideo: queue: Support asynchronous buffer handling Kieran Bingham
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 27+ messages in thread
From: Kieran Bingham @ 2018-11-06 21:27 UTC (permalink / raw)
  To: Laurent Pinchart, linux-media
  Cc: Guennadi Liakhovetski, Olivier BRAUN, Troy Kisky, Randy Dunlap,
	Philipp Zabel, Ezequiel Garcia, Kieran Bingham

From: Kieran Bingham <kieran.bingham@ideasonboard.com>

Both uvc_start_streaming(), and uvc_stop_streaming() are called from
userspace context, with interrupts enabled. As such, they do not need to
save the IRQ state, and can use spin_lock_irq() and spin_unlock_irq()
respectively.

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

---

v4:
 - Rebase to v4.16 (linux-media/master)

v5:
 - Provide lockdep validation

 drivers/media/usb/uvc/uvc_queue.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
index 74f9483911d0..bebf2415d9de 100644
--- a/drivers/media/usb/uvc/uvc_queue.c
+++ b/drivers/media/usb/uvc/uvc_queue.c
@@ -169,18 +169,19 @@ static int uvc_start_streaming(struct vb2_queue *vq, unsigned int count)
 {
 	struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
 	struct uvc_streaming *stream = uvc_queue_to_stream(queue);
-	unsigned long flags;
 	int ret;
 
+	lockdep_assert_irqs_enabled();
+
 	queue->buf_used = 0;
 
 	ret = uvc_video_enable(stream, 1);
 	if (ret == 0)
 		return 0;
 
-	spin_lock_irqsave(&queue->irqlock, flags);
+	spin_lock_irq(&queue->irqlock);
 	uvc_queue_return_buffers(queue, UVC_BUF_STATE_QUEUED);
-	spin_unlock_irqrestore(&queue->irqlock, flags);
+	spin_unlock_irq(&queue->irqlock);
 
 	return ret;
 }
@@ -188,14 +189,15 @@ static int uvc_start_streaming(struct vb2_queue *vq, unsigned int count)
 static void uvc_stop_streaming(struct vb2_queue *vq)
 {
 	struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
-	unsigned long flags;
+
+	lockdep_assert_irqs_enabled();
 
 	if (vq->type != V4L2_BUF_TYPE_META_CAPTURE)
 		uvc_video_enable(uvc_queue_to_stream(queue), 0);
 
-	spin_lock_irqsave(&queue->irqlock, flags);
+	spin_lock_irq(&queue->irqlock);
 	uvc_queue_return_buffers(queue, UVC_BUF_STATE_ERROR);
-	spin_unlock_irqrestore(&queue->irqlock, flags);
+	spin_unlock_irq(&queue->irqlock);
 }
 
 static const struct vb2_ops uvc_queue_qops = {
-- 
git-series 0.9.1

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v5 5/9] media: uvcvideo: queue: Support asynchronous buffer handling
  2018-11-06 21:27 [PATCH v5 0/9] Asynchronous UVC Kieran Bingham
                   ` (3 preceding siblings ...)
  2018-11-06 21:27 ` [PATCH v5 4/9] media: uvcvideo: queue: Simplify spin-lock usage Kieran Bingham
@ 2018-11-06 21:27 ` Kieran Bingham
  2018-11-06 22:38   ` Laurent Pinchart
  2018-11-06 21:27 ` [PATCH v5 6/9] media: uvcvideo: Move decode processing to process context Kieran Bingham
                   ` (5 subsequent siblings)
  10 siblings, 1 reply; 27+ messages in thread
From: Kieran Bingham @ 2018-11-06 21:27 UTC (permalink / raw)
  To: Laurent Pinchart, linux-media
  Cc: Guennadi Liakhovetski, Olivier BRAUN, Troy Kisky, Randy Dunlap,
	Philipp Zabel, Ezequiel Garcia, Kieran Bingham

From: Kieran Bingham <kieran.bingham@ideasonboard.com>

The buffer queue interface currently operates sequentially, processing
buffers after they have fully completed.

In preparation for supporting parallel tasks operating on the buffers,
we will need to support buffers being processed on multiple CPUs.

Adapt the uvc_queue_next_buffer() such that a reference count tracks the
active use of the buffer, returning the buffer to the VB2 stack at
completion.

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>

v5:
 - uvc_queue_requeue() -> uvc_queue_buffer_requeue()
 - Fix comment
---
 drivers/media/usb/uvc/uvc_queue.c | 61 ++++++++++++++++++++++++++------
 drivers/media/usb/uvc/uvcvideo.h  |  4 ++-
 2 files changed, 54 insertions(+), 11 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
index bebf2415d9de..cd8c03341de0 100644
--- a/drivers/media/usb/uvc/uvc_queue.c
+++ b/drivers/media/usb/uvc/uvc_queue.c
@@ -142,6 +142,7 @@ static void uvc_buffer_queue(struct vb2_buffer *vb)
 
 	spin_lock_irqsave(&queue->irqlock, flags);
 	if (likely(!(queue->flags & UVC_QUEUE_DISCONNECTED))) {
+		kref_init(&buf->ref);
 		list_add_tail(&buf->queue, &queue->irqqueue);
 	} else {
 		/* If the device is disconnected return the buffer to userspace
@@ -459,28 +460,66 @@ struct uvc_buffer *uvc_queue_get_current_buffer(struct uvc_video_queue *queue)
 	return nextbuf;
 }
 
-struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
+/*
+ * uvc_queue_buffer_requeue: Requeue a buffer on our internal irqqueue
+ *
+ * Reuse a buffer through our internal queue without the need to 'prepare'.
+ * The buffer will be returned to userspace through the uvc_buffer_queue call if
+ * the device has been disconnected.
+ */
+static void uvc_queue_buffer_requeue(struct uvc_video_queue *queue,
 		struct uvc_buffer *buf)
 {
-	struct uvc_buffer *nextbuf;
-	unsigned long flags;
+	buf->error = 0;
+	buf->state = UVC_BUF_STATE_QUEUED;
+	buf->bytesused = 0;
+	vb2_set_plane_payload(&buf->buf.vb2_buf, 0, 0);
+
+	uvc_buffer_queue(&buf->buf.vb2_buf);
+}
+
+static void uvc_queue_buffer_complete(struct kref *ref)
+{
+	struct uvc_buffer *buf = container_of(ref, struct uvc_buffer, ref);
+	struct vb2_buffer *vb = &buf->buf.vb2_buf;
+	struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
 
 	if ((queue->flags & UVC_QUEUE_DROP_CORRUPTED) && buf->error) {
-		buf->error = 0;
-		buf->state = UVC_BUF_STATE_QUEUED;
-		buf->bytesused = 0;
-		vb2_set_plane_payload(&buf->buf.vb2_buf, 0, 0);
-		return buf;
+		uvc_queue_buffer_requeue(queue, buf);
+		return;
 	}
 
+	buf->state = buf->error ? UVC_BUF_STATE_ERROR : UVC_BUF_STATE_DONE;
+	vb2_set_plane_payload(&buf->buf.vb2_buf, 0, buf->bytesused);
+	vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+/*
+ * Release a reference on the buffer. Complete the buffer when the last
+ * reference is released.
+ */
+void uvc_queue_buffer_release(struct uvc_buffer *buf)
+{
+	kref_put(&buf->ref, uvc_queue_buffer_complete);
+}
+
+/*
+ * Remove this buffer from the queue. Lifetime will persist while async actions
+ * are still running (if any), and uvc_queue_buffer_release will give the buffer
+ * back to VB2 when all users have completed.
+ */
+struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
+		struct uvc_buffer *buf)
+{
+	struct uvc_buffer *nextbuf;
+	unsigned long flags;
+
 	spin_lock_irqsave(&queue->irqlock, flags);
 	list_del(&buf->queue);
 	nextbuf = __uvc_queue_get_current_buffer(queue);
 	spin_unlock_irqrestore(&queue->irqlock, flags);
 
-	buf->state = buf->error ? UVC_BUF_STATE_ERROR : UVC_BUF_STATE_DONE;
-	vb2_set_plane_payload(&buf->buf.vb2_buf, 0, buf->bytesused);
-	vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE);
+	uvc_queue_buffer_release(buf);
 
 	return nextbuf;
 }
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index bdb6d8daedab..1bc17da7f3d4 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -410,6 +410,9 @@ struct uvc_buffer {
 	unsigned int bytesused;
 
 	u32 pts;
+
+	/* Asynchronous buffer handling. */
+	struct kref ref;
 };
 
 #define UVC_QUEUE_DISCONNECTED		(1 << 0)
@@ -726,6 +729,7 @@ void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect);
 struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
 					 struct uvc_buffer *buf);
 struct uvc_buffer *uvc_queue_get_current_buffer(struct uvc_video_queue *queue);
+void uvc_queue_buffer_release(struct uvc_buffer *buf);
 int uvc_queue_mmap(struct uvc_video_queue *queue,
 		   struct vm_area_struct *vma);
 __poll_t uvc_queue_poll(struct uvc_video_queue *queue, struct file *file,
-- 
git-series 0.9.1

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v5 6/9] media: uvcvideo: Move decode processing to process context
  2018-11-06 21:27 [PATCH v5 0/9] Asynchronous UVC Kieran Bingham
                   ` (4 preceding siblings ...)
  2018-11-06 21:27 ` [PATCH v5 5/9] media: uvcvideo: queue: Support asynchronous buffer handling Kieran Bingham
@ 2018-11-06 21:27 ` Kieran Bingham
  2018-11-06 22:58   ` Laurent Pinchart
  2018-11-06 21:27 ` [PATCH v5 7/9] media: uvcvideo: Split uvc_video_enable into two Kieran Bingham
                   ` (4 subsequent siblings)
  10 siblings, 1 reply; 27+ messages in thread
From: Kieran Bingham @ 2018-11-06 21:27 UTC (permalink / raw)
  To: Laurent Pinchart, linux-media
  Cc: Guennadi Liakhovetski, Olivier BRAUN, Troy Kisky, Randy Dunlap,
	Philipp Zabel, Ezequiel Garcia, Kieran Bingham

From: Kieran Bingham <kieran.bingham@ideasonboard.com>

Newer high definition cameras, and cameras with multiple lenses such as
the range of stereo-vision cameras now available have ever increasing
data rates.

The inclusion of a variable length packet header in URB packets mean
that we must memcpy the frame data out to our destination 'manually'.
This can result in data rates of up to 2 gigabits per second being
processed.

To improve efficiency, and maximise throughput, handle the URB decode
processing through a work queue to move it from interrupt context, and
allow multiple processors to work on URBs in parallel.

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>

---
v2:
 - Lock full critical section of usb_submit_urb()

v3:
 - Fix race on submitting uvc_video_decode_data_work() to work queue.
 - Rename uvc_decode_op -> uvc_copy_op (Generic to encode/decode)
 - Rename decodes -> copy_operations
 - Don't queue work if there is no async task
 - obtain copy op structure directly in uvc_video_decode_data()
 - uvc_video_decode_data_work() -> uvc_video_copy_data_work()

v4:
 - Provide for_each_uvc_urb()
 - Simplify fix for shutdown race to flush queue before freeing URBs
 - Rebase to v4.16-rc4 (linux-media/master) adjusting for metadata
   conflicts.

v5:
 - Rebase to media/v4.20-2
 - Use GFP_KERNEL allocation in uvc_video_copy_data_work()
 - Fix function documentation for uvc_video_copy_data_work()
 - Add periods to the end of sentences
 - Rename 'decode' variable to 'op' in uvc_video_decode_data()
 - Move uvc_urb->async_operations initialisation to before use
 - Move async workqueue to match uvc_streaming lifetime instead of
   streamon/streamoff

 drivers/media/usb/uvc/uvc_driver.c |   2 +-
 drivers/media/usb/uvc/uvc_video.c  | 110 +++++++++++++++++++++++-------
 drivers/media/usb/uvc/uvcvideo.h   |  28 ++++++++-
 3 files changed, 116 insertions(+), 24 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index bc369a0934a3..e61a6d26e812 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -1883,6 +1883,8 @@ static void uvc_unregister_video(struct uvc_device *dev)
 		video_unregister_device(&stream->vdev);
 		video_unregister_device(&stream->meta.vdev);
 
+		destroy_workqueue(stream->async_wq);
+
 		uvc_debugfs_cleanup_stream(stream);
 	}
 }
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index 7a7779e1b466..ce9e40444507 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -1094,21 +1094,54 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
 	return data[0];
 }
 
-static void uvc_video_decode_data(struct uvc_streaming *stream,
+/*
+ * uvc_video_decode_data_work: Asynchronous memcpy processing
+ *
+ * Copy URB data to video buffers in process context, releasing buffer
+ * references and requeuing the URB when done.
+ */
+static void uvc_video_copy_data_work(struct work_struct *work)
+{
+	struct uvc_urb *uvc_urb = container_of(work, struct uvc_urb, work);
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < uvc_urb->async_operations; i++) {
+		struct uvc_copy_op *op = &uvc_urb->copy_operations[i];
+
+		memcpy(op->dst, op->src, op->len);
+
+		/* Release reference taken on this buffer. */
+		uvc_queue_buffer_release(op->buf);
+	}
+
+	ret = usb_submit_urb(uvc_urb->urb, GFP_KERNEL);
+	if (ret < 0)
+		uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n",
+			   ret);
+}
+
+static void uvc_video_decode_data(struct uvc_urb *uvc_urb,
 		struct uvc_buffer *buf, const u8 *data, int len)
 {
-	unsigned int maxlen, nbytes;
-	void *mem;
+	unsigned int active_op = uvc_urb->async_operations;
+	struct uvc_copy_op *op = &uvc_urb->copy_operations[active_op];
+	unsigned int maxlen;
 
 	if (len <= 0)
 		return;
 
-	/* Copy the video data to the buffer. */
 	maxlen = buf->length - buf->bytesused;
-	mem = buf->mem + buf->bytesused;
-	nbytes = min((unsigned int)len, maxlen);
-	memcpy(mem, data, nbytes);
-	buf->bytesused += nbytes;
+
+	/* Take a buffer reference for async work. */
+	kref_get(&buf->ref);
+
+	op->buf = buf;
+	op->src = data;
+	op->dst = buf->mem + buf->bytesused;
+	op->len = min_t(unsigned int, len, maxlen);
+
+	buf->bytesused += op->len;
 
 	/* Complete the current frame if the buffer size was exceeded. */
 	if (len > maxlen) {
@@ -1116,6 +1149,8 @@ static void uvc_video_decode_data(struct uvc_streaming *stream,
 		buf->error = 1;
 		buf->state = UVC_BUF_STATE_READY;
 	}
+
+	uvc_urb->async_operations++;
 }
 
 static void uvc_video_decode_end(struct uvc_streaming *stream,
@@ -1324,7 +1359,7 @@ static void uvc_video_decode_isoc(struct uvc_urb *uvc_urb,
 		uvc_video_decode_meta(stream, meta_buf, mem, ret);
 
 		/* Decode the payload data. */
-		uvc_video_decode_data(stream, buf, mem + ret,
+		uvc_video_decode_data(uvc_urb, buf, mem + ret,
 			urb->iso_frame_desc[i].actual_length - ret);
 
 		/* Process the header again. */
@@ -1384,9 +1419,9 @@ static void uvc_video_decode_bulk(struct uvc_urb *uvc_urb,
 	 * sure buf is never dereferenced if NULL.
 	 */
 
-	/* Process video data. */
+	/* Prepare video data for processing. */
 	if (!stream->bulk.skip_payload && buf != NULL)
-		uvc_video_decode_data(stream, buf, mem, len);
+		uvc_video_decode_data(uvc_urb, buf, mem, len);
 
 	/* Detect the payload end by a URB smaller than the maximum size (or
 	 * a payload size equal to the maximum) and process the header again.
@@ -1472,7 +1507,7 @@ static void uvc_video_complete(struct urb *urb)
 		uvc_printk(KERN_WARNING, "Non-zero status (%d) in video "
 			"completion handler.\n", urb->status);
 		/* fall through */
-	case -ENOENT:		/* usb_kill_urb() called. */
+	case -ENOENT:		/* usb_poison_urb() called. */
 		if (stream->frozen)
 			return;
 		/* fall through */
@@ -1494,12 +1529,26 @@ static void uvc_video_complete(struct urb *urb)
 		spin_unlock_irqrestore(&qmeta->irqlock, flags);
 	}
 
+	/* Re-initialise the URB async work. */
+	uvc_urb->async_operations = 0;
+
+	/*
+	 * Process the URB headers, and optionally queue expensive memcpy tasks
+	 * to be deferred to a work queue.
+	 */
 	stream->decode(uvc_urb, buf, buf_meta);
 
-	if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
-		uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n",
-			ret);
+	/* If no async work is needed, resubmit the URB immediately. */
+	if (!uvc_urb->async_operations) {
+		ret = usb_submit_urb(uvc_urb->urb, GFP_ATOMIC);
+		if (ret < 0)
+			uvc_printk(KERN_ERR,
+				   "Failed to resubmit video URB (%d).\n",
+				   ret);
+		return;
 	}
+
+	queue_work(stream->async_wq, &uvc_urb->work);
 }
 
 /*
@@ -1594,20 +1643,22 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,
  */
 static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers)
 {
-	struct urb *urb;
-	unsigned int i;
+	struct uvc_urb *uvc_urb;
 
 	uvc_video_stats_stop(stream);
 
-	for (i = 0; i < UVC_URBS; ++i) {
-		struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
+	/*
+	 * We must poison the URBs rather than kill them to ensure that even
+	 * after the completion handler returns, any asynchronous workqueues
+	 * will be prevented from resubmitting the URBs.
+	 */
+	for_each_uvc_urb(uvc_urb, stream)
+		usb_poison_urb(uvc_urb->urb);
 
-		urb = uvc_urb->urb;
-		if (urb == NULL)
-			continue;
+	flush_workqueue(stream->async_wq);
 
-		usb_kill_urb(urb);
-		usb_free_urb(urb);
+	for_each_uvc_urb(uvc_urb, stream) {
+		usb_free_urb(uvc_urb->urb);
 		uvc_urb->urb = NULL;
 	}
 
@@ -1932,6 +1983,7 @@ int uvc_video_init(struct uvc_streaming *stream)
 	struct uvc_streaming_control *probe = &stream->ctrl;
 	struct uvc_format *format = NULL;
 	struct uvc_frame *frame = NULL;
+	struct uvc_urb *uvc_urb;
 	unsigned int i;
 	int ret;
 
@@ -2017,6 +2069,16 @@ int uvc_video_init(struct uvc_streaming *stream)
 		}
 	}
 
+	/* Allocate a stream specific work queue for asynchronous tasks. */
+	stream->async_wq = alloc_workqueue("uvcvideo", WQ_UNBOUND | WQ_HIGHPRI,
+					   0);
+	if (!stream->async_wq)
+		return -ENOMEM;
+
+	/* Prepare asynchronous work items. */
+	for_each_uvc_urb(uvc_urb, stream)
+		INIT_WORK(&uvc_urb->work, uvc_video_copy_data_work);
+
 	return 0;
 }
 
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 1bc17da7f3d4..0953e2e59a79 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -491,12 +491,30 @@ struct uvc_stats_stream {
 #define UVC_METATADA_BUF_SIZE 1024
 
 /**
+ * struct uvc_copy_op: Context structure to schedule asynchronous memcpy
+ *
+ * @buf: active buf object for this operation
+ * @dst: copy destination address
+ * @src: copy source address
+ * @len: copy length
+ */
+struct uvc_copy_op {
+	struct uvc_buffer *buf;
+	void *dst;
+	const __u8 *src;
+	size_t len;
+};
+
+/**
  * struct uvc_urb - URB context management structure
  *
  * @urb: the URB described by this context structure
  * @stream: UVC streaming context
  * @buffer: memory storage for the URB
  * @dma: DMA coherent addressing for the urb_buffer
+ * @async_operations: counter to indicate the number of copy operations
+ * @copy_operations: work descriptors for asynchronous copy operations
+ * @work: work queue entry for asynchronous decode
  */
 struct uvc_urb {
 	struct urb *urb;
@@ -504,6 +522,10 @@ struct uvc_urb {
 
 	char *buffer;
 	dma_addr_t dma;
+
+	unsigned int async_operations;
+	struct uvc_copy_op copy_operations[UVC_MAX_PACKETS];
+	struct work_struct work;
 };
 
 struct uvc_streaming {
@@ -536,6 +558,7 @@ struct uvc_streaming {
 	/* Buffers queue. */
 	unsigned int frozen : 1;
 	struct uvc_video_queue queue;
+	struct workqueue_struct *async_wq;
 	void (*decode)(struct uvc_urb *uvc_urb, struct uvc_buffer *buf,
 		       struct uvc_buffer *meta_buf);
 
@@ -589,6 +612,11 @@ struct uvc_streaming {
 	} clock;
 };
 
+#define for_each_uvc_urb(uvc_urb, uvc_streaming) \
+	for ((uvc_urb) = &(uvc_streaming)->uvc_urb[0]; \
+	     (uvc_urb) < &(uvc_streaming)->uvc_urb[UVC_URBS]; \
+	     ++(uvc_urb))
+
 struct uvc_device_info {
 	u32	quirks;
 	u32	meta_format;
-- 
git-series 0.9.1

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v5 7/9] media: uvcvideo: Split uvc_video_enable into two
  2018-11-06 21:27 [PATCH v5 0/9] Asynchronous UVC Kieran Bingham
                   ` (5 preceding siblings ...)
  2018-11-06 21:27 ` [PATCH v5 6/9] media: uvcvideo: Move decode processing to process context Kieran Bingham
@ 2018-11-06 21:27 ` Kieran Bingham
  2018-11-06 23:08   ` Laurent Pinchart
  2018-11-06 21:27 ` [PATCH v5 8/9] media: uvcvideo: Rename uvc_{un,}init_video() Kieran Bingham
                   ` (3 subsequent siblings)
  10 siblings, 1 reply; 27+ messages in thread
From: Kieran Bingham @ 2018-11-06 21:27 UTC (permalink / raw)
  To: Laurent Pinchart, linux-media
  Cc: Guennadi Liakhovetski, Olivier BRAUN, Troy Kisky, Randy Dunlap,
	Philipp Zabel, Ezequiel Garcia, Kieran Bingham

From: Kieran Bingham <kieran.bingham@ideasonboard.com>

uvc_video_enable() is used both to start and stop the video stream
object, however the single function entry point shares no code between
the two operations.

Split the function into two distinct calls, and rename to
uvc_video_start_streaming() and uvc_video_stop_streaming() as
appropriate.

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
---
 drivers/media/usb/uvc/uvc_queue.c |  4 +-
 drivers/media/usb/uvc/uvc_video.c | 56 +++++++++++++++-----------------
 drivers/media/usb/uvc/uvcvideo.h  |  3 +-
 3 files changed, 31 insertions(+), 32 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
index cd8c03341de0..682698ec1118 100644
--- a/drivers/media/usb/uvc/uvc_queue.c
+++ b/drivers/media/usb/uvc/uvc_queue.c
@@ -176,7 +176,7 @@ static int uvc_start_streaming(struct vb2_queue *vq, unsigned int count)
 
 	queue->buf_used = 0;
 
-	ret = uvc_video_enable(stream, 1);
+	ret = uvc_video_start_streaming(stream);
 	if (ret == 0)
 		return 0;
 
@@ -194,7 +194,7 @@ static void uvc_stop_streaming(struct vb2_queue *vq)
 	lockdep_assert_irqs_enabled();
 
 	if (vq->type != V4L2_BUF_TYPE_META_CAPTURE)
-		uvc_video_enable(uvc_queue_to_stream(queue), 0);
+		uvc_video_stop_streaming(uvc_queue_to_stream(queue));
 
 	spin_lock_irq(&queue->irqlock);
 	uvc_queue_return_buffers(queue, UVC_BUF_STATE_ERROR);
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index ce9e40444507..0d35e933856a 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -2082,38 +2082,10 @@ int uvc_video_init(struct uvc_streaming *stream)
 	return 0;
 }
 
-/*
- * Enable or disable the video stream.
- */
-int uvc_video_enable(struct uvc_streaming *stream, int enable)
+int uvc_video_start_streaming(struct uvc_streaming *stream)
 {
 	int ret;
 
-	if (!enable) {
-		uvc_uninit_video(stream, 1);
-		if (stream->intf->num_altsetting > 1) {
-			usb_set_interface(stream->dev->udev,
-					  stream->intfnum, 0);
-		} else {
-			/* UVC doesn't specify how to inform a bulk-based device
-			 * when the video stream is stopped. Windows sends a
-			 * CLEAR_FEATURE(HALT) request to the video streaming
-			 * bulk endpoint, mimic the same behaviour.
-			 */
-			unsigned int epnum = stream->header.bEndpointAddress
-					   & USB_ENDPOINT_NUMBER_MASK;
-			unsigned int dir = stream->header.bEndpointAddress
-					 & USB_ENDPOINT_DIR_MASK;
-			unsigned int pipe;
-
-			pipe = usb_sndbulkpipe(stream->dev->udev, epnum) | dir;
-			usb_clear_halt(stream->dev->udev, pipe);
-		}
-
-		uvc_video_clock_cleanup(stream);
-		return 0;
-	}
-
 	ret = uvc_video_clock_init(stream);
 	if (ret < 0)
 		return ret;
@@ -2136,3 +2108,29 @@ int uvc_video_enable(struct uvc_streaming *stream, int enable)
 
 	return ret;
 }
+
+int uvc_video_stop_streaming(struct uvc_streaming *stream)
+{
+	uvc_uninit_video(stream, 1);
+	if (stream->intf->num_altsetting > 1) {
+		usb_set_interface(stream->dev->udev,
+				  stream->intfnum, 0);
+	} else {
+		/* UVC doesn't specify how to inform a bulk-based device
+		 * when the video stream is stopped. Windows sends a
+		 * CLEAR_FEATURE(HALT) request to the video streaming
+		 * bulk endpoint, mimic the same behaviour.
+		 */
+		unsigned int epnum = stream->header.bEndpointAddress
+				   & USB_ENDPOINT_NUMBER_MASK;
+		unsigned int dir = stream->header.bEndpointAddress
+				 & USB_ENDPOINT_DIR_MASK;
+		unsigned int pipe;
+
+		pipe = usb_sndbulkpipe(stream->dev->udev, epnum) | dir;
+		usb_clear_halt(stream->dev->udev, pipe);
+	}
+
+	uvc_video_clock_cleanup(stream);
+	return 0;
+}
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 0953e2e59a79..c0a120496a1f 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -784,7 +784,8 @@ void uvc_mc_cleanup_entity(struct uvc_entity *entity);
 int uvc_video_init(struct uvc_streaming *stream);
 int uvc_video_suspend(struct uvc_streaming *stream);
 int uvc_video_resume(struct uvc_streaming *stream, int reset);
-int uvc_video_enable(struct uvc_streaming *stream, int enable);
+int uvc_video_start_streaming(struct uvc_streaming *stream);
+int uvc_video_stop_streaming(struct uvc_streaming *stream);
 int uvc_probe_video(struct uvc_streaming *stream,
 		    struct uvc_streaming_control *probe);
 int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit,
-- 
git-series 0.9.1

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v5 8/9] media: uvcvideo: Rename uvc_{un,}init_video()
  2018-11-06 21:27 [PATCH v5 0/9] Asynchronous UVC Kieran Bingham
                   ` (6 preceding siblings ...)
  2018-11-06 21:27 ` [PATCH v5 7/9] media: uvcvideo: Split uvc_video_enable into two Kieran Bingham
@ 2018-11-06 21:27 ` Kieran Bingham
  2018-11-06 23:13   ` Laurent Pinchart
  2018-11-06 21:27 ` [PATCH v5 9/9] media: uvcvideo: Utilise for_each_uvc_urb iterator Kieran Bingham
                   ` (2 subsequent siblings)
  10 siblings, 1 reply; 27+ messages in thread
From: Kieran Bingham @ 2018-11-06 21:27 UTC (permalink / raw)
  To: Laurent Pinchart, linux-media
  Cc: Guennadi Liakhovetski, Olivier BRAUN, Troy Kisky, Randy Dunlap,
	Philipp Zabel, Ezequiel Garcia, Kieran Bingham

From: Kieran Bingham <kieran.bingham@ideasonboard.com>

We have both uvc_init_video() and uvc_video_init() calls which can be
quite confusing to determine the process for each. Now that video
uvc_video_enable() has been renamed to uvc_video_start_streaming(),
adapt these calls to suit the new flow.

Rename uvc_init_video() to uvc_video_start() and uvc_uninit_video() to
uvc_video_stop().

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
---
 drivers/media/usb/uvc/uvc_video.c | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index 0d35e933856a..020022e6ade4 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -1641,7 +1641,7 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,
 /*
  * Uninitialize isochronous/bulk URBs and free transfer buffers.
  */
-static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers)
+static void uvc_video_stop(struct uvc_streaming *stream, int free_buffers)
 {
 	struct uvc_urb *uvc_urb;
 
@@ -1718,7 +1718,7 @@ static int uvc_init_video_isoc(struct uvc_streaming *stream,
 
 		urb = usb_alloc_urb(npackets, gfp_flags);
 		if (urb == NULL) {
-			uvc_uninit_video(stream, 1);
+			uvc_video_stop(stream, 1);
 			return -ENOMEM;
 		}
 
@@ -1786,7 +1786,7 @@ static int uvc_init_video_bulk(struct uvc_streaming *stream,
 
 		urb = usb_alloc_urb(0, gfp_flags);
 		if (urb == NULL) {
-			uvc_uninit_video(stream, 1);
+			uvc_video_stop(stream, 1);
 			return -ENOMEM;
 		}
 
@@ -1806,7 +1806,7 @@ static int uvc_init_video_bulk(struct uvc_streaming *stream,
 /*
  * Initialize isochronous/bulk URBs and allocate transfer buffers.
  */
-static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
+static int uvc_video_start(struct uvc_streaming *stream, gfp_t gfp_flags)
 {
 	struct usb_interface *intf = stream->intf;
 	struct usb_host_endpoint *ep;
@@ -1894,7 +1894,7 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
 		if (ret < 0) {
 			uvc_printk(KERN_ERR, "Failed to submit URB %u "
 					"(%d).\n", i, ret);
-			uvc_uninit_video(stream, 1);
+			uvc_video_stop(stream, 1);
 			return ret;
 		}
 	}
@@ -1925,7 +1925,7 @@ int uvc_video_suspend(struct uvc_streaming *stream)
 		return 0;
 
 	stream->frozen = 1;
-	uvc_uninit_video(stream, 0);
+	uvc_video_stop(stream, 0);
 	usb_set_interface(stream->dev->udev, stream->intfnum, 0);
 	return 0;
 }
@@ -1961,7 +1961,7 @@ int uvc_video_resume(struct uvc_streaming *stream, int reset)
 	if (ret < 0)
 		return ret;
 
-	return uvc_init_video(stream, GFP_NOIO);
+	return uvc_video_start(stream, GFP_NOIO);
 }
 
 /* ------------------------------------------------------------------------
@@ -2095,7 +2095,7 @@ int uvc_video_start_streaming(struct uvc_streaming *stream)
 	if (ret < 0)
 		goto error_commit;
 
-	ret = uvc_init_video(stream, GFP_KERNEL);
+	ret = uvc_video_start(stream, GFP_KERNEL);
 	if (ret < 0)
 		goto error_video;
 
@@ -2111,7 +2111,7 @@ int uvc_video_start_streaming(struct uvc_streaming *stream)
 
 int uvc_video_stop_streaming(struct uvc_streaming *stream)
 {
-	uvc_uninit_video(stream, 1);
+	uvc_video_stop(stream, 1);
 	if (stream->intf->num_altsetting > 1) {
 		usb_set_interface(stream->dev->udev,
 				  stream->intfnum, 0);
-- 
git-series 0.9.1

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v5 9/9] media: uvcvideo: Utilise for_each_uvc_urb iterator
  2018-11-06 21:27 [PATCH v5 0/9] Asynchronous UVC Kieran Bingham
                   ` (7 preceding siblings ...)
  2018-11-06 21:27 ` [PATCH v5 8/9] media: uvcvideo: Rename uvc_{un,}init_video() Kieran Bingham
@ 2018-11-06 21:27 ` Kieran Bingham
  2018-11-06 23:21   ` Laurent Pinchart
  2018-11-06 23:31 ` [PATCH v5 0/9] Asynchronous UVC Laurent Pinchart
  2018-11-27 20:17 ` Pavel Machek
  10 siblings, 1 reply; 27+ messages in thread
From: Kieran Bingham @ 2018-11-06 21:27 UTC (permalink / raw)
  To: Laurent Pinchart, linux-media
  Cc: Guennadi Liakhovetski, Olivier BRAUN, Troy Kisky, Randy Dunlap,
	Philipp Zabel, Ezequiel Garcia, Kieran Bingham

From: Kieran Bingham <kieran.bingham@ideasonboard.com>

A new iterator is available for processing UVC URB structures. This
simplifies the processing of the internal stream data.

Convert the manual loop iterators to the new helper, adding an index
helper to keep the existing debug print.

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>

---
This patch converts to using the iterator which for most hunks makes
sense. The only one with uncertainty is in uvc_alloc_urb_buffers() where
the loop index is used to determine if all the buffers were successfully
allocated.

As the loop index is not incremented by the loops, we can obtain the
buffer index - but then we are offset and out-by-one.

Adjusting this in the code is fine - but at that point it feels like
it's not adding much value. I've left that hunk in for this patch - but
that part could be reverted if desired - unless anyone has a better
rework of the buffer check?

 drivers/media/usb/uvc/uvc_video.c | 51 ++++++++++++++++----------------
 drivers/media/usb/uvc/uvcvideo.h  |  3 ++-
 2 files changed, 29 insertions(+), 25 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index 020022e6ade4..f6e5db7ea50e 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -1556,20 +1556,19 @@ static void uvc_video_complete(struct urb *urb)
  */
 static void uvc_free_urb_buffers(struct uvc_streaming *stream)
 {
-	unsigned int i;
+	struct uvc_urb *uvc_urb;
 
-	for (i = 0; i < UVC_URBS; ++i) {
-		struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
+	for_each_uvc_urb(uvc_urb, stream) {
+		if (!uvc_urb->buffer)
+			continue;
 
-		if (uvc_urb->buffer) {
 #ifndef CONFIG_DMA_NONCOHERENT
-			usb_free_coherent(stream->dev->udev, stream->urb_size,
-					uvc_urb->buffer, uvc_urb->dma);
+		usb_free_coherent(stream->dev->udev, stream->urb_size,
+				  uvc_urb->buffer, uvc_urb->dma);
 #else
-			kfree(uvc_urb->buffer);
+		kfree(uvc_urb->buffer);
 #endif
-			uvc_urb->buffer = NULL;
-		}
+		uvc_urb->buffer = NULL;
 	}
 
 	stream->urb_size = 0;
@@ -1589,8 +1588,9 @@ static void uvc_free_urb_buffers(struct uvc_streaming *stream)
 static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,
 	unsigned int size, unsigned int psize, gfp_t gfp_flags)
 {
+	struct uvc_urb *uvc_urb;
 	unsigned int npackets;
-	unsigned int i;
+	unsigned int i = 0;
 
 	/* Buffers are already allocated, bail out. */
 	if (stream->urb_size)
@@ -1605,8 +1605,12 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,
 
 	/* Retry allocations until one succeed. */
 	for (; npackets > 1; npackets /= 2) {
-		for (i = 0; i < UVC_URBS; ++i) {
-			struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
+		for_each_uvc_urb(uvc_urb, stream) {
+			/*
+			 * Track how many URBs we allocate, adding one to the
+			 * index to account for our zero index.
+			 */
+			i = uvc_urb_index(uvc_urb) + 1;
 
 			stream->urb_size = psize * npackets;
 #ifndef CONFIG_DMA_NONCOHERENT
@@ -1700,7 +1704,8 @@ static int uvc_init_video_isoc(struct uvc_streaming *stream,
 	struct usb_host_endpoint *ep, gfp_t gfp_flags)
 {
 	struct urb *urb;
-	unsigned int npackets, i, j;
+	struct uvc_urb *uvc_urb;
+	unsigned int npackets, j;
 	u16 psize;
 	u32 size;
 
@@ -1713,9 +1718,7 @@ static int uvc_init_video_isoc(struct uvc_streaming *stream,
 
 	size = npackets * psize;
 
-	for (i = 0; i < UVC_URBS; ++i) {
-		struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
-
+	for_each_uvc_urb(uvc_urb, stream) {
 		urb = usb_alloc_urb(npackets, gfp_flags);
 		if (urb == NULL) {
 			uvc_video_stop(stream, 1);
@@ -1757,7 +1760,8 @@ static int uvc_init_video_bulk(struct uvc_streaming *stream,
 	struct usb_host_endpoint *ep, gfp_t gfp_flags)
 {
 	struct urb *urb;
-	unsigned int npackets, pipe, i;
+	struct uvc_urb *uvc_urb;
+	unsigned int npackets, pipe;
 	u16 psize;
 	u32 size;
 
@@ -1781,9 +1785,7 @@ static int uvc_init_video_bulk(struct uvc_streaming *stream,
 	if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
 		size = 0;
 
-	for (i = 0; i < UVC_URBS; ++i) {
-		struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
-
+	for_each_uvc_urb(uvc_urb, stream) {
 		urb = usb_alloc_urb(0, gfp_flags);
 		if (urb == NULL) {
 			uvc_video_stop(stream, 1);
@@ -1810,6 +1812,7 @@ static int uvc_video_start(struct uvc_streaming *stream, gfp_t gfp_flags)
 {
 	struct usb_interface *intf = stream->intf;
 	struct usb_host_endpoint *ep;
+	struct uvc_urb *uvc_urb;
 	unsigned int i;
 	int ret;
 
@@ -1887,13 +1890,11 @@ static int uvc_video_start(struct uvc_streaming *stream, gfp_t gfp_flags)
 		return ret;
 
 	/* Submit the URBs. */
-	for (i = 0; i < UVC_URBS; ++i) {
-		struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
-
+	for_each_uvc_urb(uvc_urb, stream) {
 		ret = usb_submit_urb(uvc_urb->urb, gfp_flags);
 		if (ret < 0) {
-			uvc_printk(KERN_ERR, "Failed to submit URB %u "
-					"(%d).\n", i, ret);
+			uvc_printk(KERN_ERR, "Failed to submit URB %u (%d).\n",
+				   uvc_urb_index(uvc_urb), ret);
 			uvc_video_stop(stream, 1);
 			return ret;
 		}
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index c0a120496a1f..6a0f1b59356c 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -617,6 +617,9 @@ struct uvc_streaming {
 	     (uvc_urb) < &(uvc_streaming)->uvc_urb[UVC_URBS]; \
 	     ++(uvc_urb))
 
+#define uvc_urb_index(uvc_urb) \
+	(unsigned int)((uvc_urb) - (&(uvc_urb)->stream->uvc_urb[0]))
+
 struct uvc_device_info {
 	u32	quirks;
 	u32	meta_format;
-- 
git-series 0.9.1

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 5/9] media: uvcvideo: queue: Support asynchronous buffer handling
  2018-11-06 21:27 ` [PATCH v5 5/9] media: uvcvideo: queue: Support asynchronous buffer handling Kieran Bingham
@ 2018-11-06 22:38   ` Laurent Pinchart
  0 siblings, 0 replies; 27+ messages in thread
From: Laurent Pinchart @ 2018-11-06 22:38 UTC (permalink / raw)
  To: Kieran Bingham
  Cc: linux-media, Guennadi Liakhovetski, Olivier BRAUN, Troy Kisky,
	Randy Dunlap, Philipp Zabel, Ezequiel Garcia, Kieran Bingham

Hi Kieran,

Thank you for the patch.

On Tuesday, 6 November 2018 23:27:16 EET Kieran Bingham wrote:
> From: Kieran Bingham <kieran.bingham@ideasonboard.com>
> 
> The buffer queue interface currently operates sequentially, processing
> buffers after they have fully completed.
> 
> In preparation for supporting parallel tasks operating on the buffers,
> we will need to support buffers being processed on multiple CPUs.
> 
> Adapt the uvc_queue_next_buffer() such that a reference count tracks the
> active use of the buffer, returning the buffer to the VB2 stack at
> completion.
> 
> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> 
> v5:
>  - uvc_queue_requeue() -> uvc_queue_buffer_requeue()
>  - Fix comment
> ---
>  drivers/media/usb/uvc/uvc_queue.c | 61 ++++++++++++++++++++++++++------
>  drivers/media/usb/uvc/uvcvideo.h  |  4 ++-
>  2 files changed, 54 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/media/usb/uvc/uvc_queue.c
> b/drivers/media/usb/uvc/uvc_queue.c index bebf2415d9de..cd8c03341de0 100644
> --- a/drivers/media/usb/uvc/uvc_queue.c
> +++ b/drivers/media/usb/uvc/uvc_queue.c
> @@ -142,6 +142,7 @@ static void uvc_buffer_queue(struct vb2_buffer *vb)
> 
>  	spin_lock_irqsave(&queue->irqlock, flags);
>  	if (likely(!(queue->flags & UVC_QUEUE_DISCONNECTED))) {
> +		kref_init(&buf->ref);
>  		list_add_tail(&buf->queue, &queue->irqqueue);
>  	} else {
>  		/* If the device is disconnected return the buffer to userspace
> @@ -459,28 +460,66 @@ struct uvc_buffer *uvc_queue_get_current_buffer(struct
> uvc_video_queue *queue) return nextbuf;
>  }
> 
> -struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
> +/*
> + * uvc_queue_buffer_requeue: Requeue a buffer on our internal irqqueue
> + *
> + * Reuse a buffer through our internal queue without the need to 'prepare'.
> + * The buffer will be returned to userspace through the uvc_buffer_queue
> call if + * the device has been disconnected.
> + */
> +static void uvc_queue_buffer_requeue(struct uvc_video_queue *queue,
>  		struct uvc_buffer *buf)
>  {
> -	struct uvc_buffer *nextbuf;
> -	unsigned long flags;
> +	buf->error = 0;
> +	buf->state = UVC_BUF_STATE_QUEUED;
> +	buf->bytesused = 0;
> +	vb2_set_plane_payload(&buf->buf.vb2_buf, 0, 0);
> +
> +	uvc_buffer_queue(&buf->buf.vb2_buf);
> +}
> +
> +static void uvc_queue_buffer_complete(struct kref *ref)
> +{
> +	struct uvc_buffer *buf = container_of(ref, struct uvc_buffer, ref);
> +	struct vb2_buffer *vb = &buf->buf.vb2_buf;
> +	struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
> 
>  	if ((queue->flags & UVC_QUEUE_DROP_CORRUPTED) && buf->error) {
> -		buf->error = 0;
> -		buf->state = UVC_BUF_STATE_QUEUED;
> -		buf->bytesused = 0;
> -		vb2_set_plane_payload(&buf->buf.vb2_buf, 0, 0);
> -		return buf;
> +		uvc_queue_buffer_requeue(queue, buf);
> +		return;
>  	}
> 
> +	buf->state = buf->error ? UVC_BUF_STATE_ERROR : UVC_BUF_STATE_DONE;
> +	vb2_set_plane_payload(&buf->buf.vb2_buf, 0, buf->bytesused);
> +	vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE);
> +}
> +
> +/*
> + * Release a reference on the buffer. Complete the buffer when the last
> + * reference is released.
> + */
> +void uvc_queue_buffer_release(struct uvc_buffer *buf)
> +{
> +	kref_put(&buf->ref, uvc_queue_buffer_complete);
> +}
> +
> +/*
> + * Remove this buffer from the queue. Lifetime will persist while async
> actions + * are still running (if any), and uvc_queue_buffer_release will
> give the buffer + * back to VB2 when all users have completed.
> + */
> +struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
> +		struct uvc_buffer *buf)
> +{
> +	struct uvc_buffer *nextbuf;
> +	unsigned long flags;
> +
>  	spin_lock_irqsave(&queue->irqlock, flags);
>  	list_del(&buf->queue);
>  	nextbuf = __uvc_queue_get_current_buffer(queue);
>  	spin_unlock_irqrestore(&queue->irqlock, flags);
> 
> -	buf->state = buf->error ? UVC_BUF_STATE_ERROR : UVC_BUF_STATE_DONE;
> -	vb2_set_plane_payload(&buf->buf.vb2_buf, 0, buf->bytesused);
> -	vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE);
> +	uvc_queue_buffer_release(buf);
> 
>  	return nextbuf;
>  }
> diff --git a/drivers/media/usb/uvc/uvcvideo.h
> b/drivers/media/usb/uvc/uvcvideo.h index bdb6d8daedab..1bc17da7f3d4 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -410,6 +410,9 @@ struct uvc_buffer {
>  	unsigned int bytesused;
> 
>  	u32 pts;
> +
> +	/* Asynchronous buffer handling. */
> +	struct kref ref;
>  };
> 
>  #define UVC_QUEUE_DISCONNECTED		(1 << 0)
> @@ -726,6 +729,7 @@ void uvc_queue_cancel(struct uvc_video_queue *queue, int
> disconnect); struct uvc_buffer *uvc_queue_next_buffer(struct
> uvc_video_queue *queue, struct uvc_buffer *buf);
>  struct uvc_buffer *uvc_queue_get_current_buffer(struct uvc_video_queue
> *queue); +void uvc_queue_buffer_release(struct uvc_buffer *buf);
>  int uvc_queue_mmap(struct uvc_video_queue *queue,
>  		   struct vm_area_struct *vma);
>  __poll_t uvc_queue_poll(struct uvc_video_queue *queue, struct file *file,


-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 6/9] media: uvcvideo: Move decode processing to process context
  2018-11-06 21:27 ` [PATCH v5 6/9] media: uvcvideo: Move decode processing to process context Kieran Bingham
@ 2018-11-06 22:58   ` Laurent Pinchart
  2018-11-07 12:22     ` Kieran Bingham
  0 siblings, 1 reply; 27+ messages in thread
From: Laurent Pinchart @ 2018-11-06 22:58 UTC (permalink / raw)
  To: Kieran Bingham
  Cc: linux-media, Guennadi Liakhovetski, Olivier BRAUN, Troy Kisky,
	Randy Dunlap, Philipp Zabel, Ezequiel Garcia, Kieran Bingham

Hi Kieran,

Thank you for the patch.

On Tuesday, 6 November 2018 23:27:17 EET Kieran Bingham wrote:
> From: Kieran Bingham <kieran.bingham@ideasonboard.com>
> 
> Newer high definition cameras, and cameras with multiple lenses such as
> the range of stereo-vision cameras now available have ever increasing
> data rates.
> 
> The inclusion of a variable length packet header in URB packets mean
> that we must memcpy the frame data out to our destination 'manually'.
> This can result in data rates of up to 2 gigabits per second being
> processed.
> 
> To improve efficiency, and maximise throughput, handle the URB decode
> processing through a work queue to move it from interrupt context, and
> allow multiple processors to work on URBs in parallel.
> 
> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

I wonder if we shouldn't, as a future improvement, only queue async work when 
the quantity of data to be copied is above a certain threshold.

> ---
> v2:
>  - Lock full critical section of usb_submit_urb()
> 
> v3:
>  - Fix race on submitting uvc_video_decode_data_work() to work queue.
>  - Rename uvc_decode_op -> uvc_copy_op (Generic to encode/decode)
>  - Rename decodes -> copy_operations
>  - Don't queue work if there is no async task
>  - obtain copy op structure directly in uvc_video_decode_data()
>  - uvc_video_decode_data_work() -> uvc_video_copy_data_work()
> 
> v4:
>  - Provide for_each_uvc_urb()
>  - Simplify fix for shutdown race to flush queue before freeing URBs
>  - Rebase to v4.16-rc4 (linux-media/master) adjusting for metadata
>    conflicts.
> 
> v5:
>  - Rebase to media/v4.20-2
>  - Use GFP_KERNEL allocation in uvc_video_copy_data_work()
>  - Fix function documentation for uvc_video_copy_data_work()
>  - Add periods to the end of sentences
>  - Rename 'decode' variable to 'op' in uvc_video_decode_data()
>  - Move uvc_urb->async_operations initialisation to before use
>  - Move async workqueue to match uvc_streaming lifetime instead of
>    streamon/streamoff
> 
>  drivers/media/usb/uvc/uvc_driver.c |   2 +-
>  drivers/media/usb/uvc/uvc_video.c  | 110 +++++++++++++++++++++++-------
>  drivers/media/usb/uvc/uvcvideo.h   |  28 ++++++++-
>  3 files changed, 116 insertions(+), 24 deletions(-)
> 
> diff --git a/drivers/media/usb/uvc/uvc_driver.c
> b/drivers/media/usb/uvc/uvc_driver.c index bc369a0934a3..e61a6d26e812
> 100644
> --- a/drivers/media/usb/uvc/uvc_driver.c
> +++ b/drivers/media/usb/uvc/uvc_driver.c
> @@ -1883,6 +1883,8 @@ static void uvc_unregister_video(struct uvc_device
> *dev) video_unregister_device(&stream->vdev);
>  		video_unregister_device(&stream->meta.vdev);
> 
> +		destroy_workqueue(stream->async_wq);
> +
>  		uvc_debugfs_cleanup_stream(stream);
>  	}
>  }
> diff --git a/drivers/media/usb/uvc/uvc_video.c
> b/drivers/media/usb/uvc/uvc_video.c index 7a7779e1b466..ce9e40444507 100644
> --- a/drivers/media/usb/uvc/uvc_video.c
> +++ b/drivers/media/usb/uvc/uvc_video.c
> @@ -1094,21 +1094,54 @@ static int uvc_video_decode_start(struct
> uvc_streaming *stream, return data[0];
>  }
> 
> -static void uvc_video_decode_data(struct uvc_streaming *stream,
> +/*
> + * uvc_video_decode_data_work: Asynchronous memcpy processing
> + *
> + * Copy URB data to video buffers in process context, releasing buffer
> + * references and requeuing the URB when done.
> + */
> +static void uvc_video_copy_data_work(struct work_struct *work)
> +{
> +	struct uvc_urb *uvc_urb = container_of(work, struct uvc_urb, work);
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0; i < uvc_urb->async_operations; i++) {
> +		struct uvc_copy_op *op = &uvc_urb->copy_operations[i];
> +
> +		memcpy(op->dst, op->src, op->len);
> +
> +		/* Release reference taken on this buffer. */
> +		uvc_queue_buffer_release(op->buf);
> +	}
> +
> +	ret = usb_submit_urb(uvc_urb->urb, GFP_KERNEL);
> +	if (ret < 0)
> +		uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n",
> +			   ret);
> +}
> +
> +static void uvc_video_decode_data(struct uvc_urb *uvc_urb,
>  		struct uvc_buffer *buf, const u8 *data, int len)
>  {
> -	unsigned int maxlen, nbytes;
> -	void *mem;
> +	unsigned int active_op = uvc_urb->async_operations;
> +	struct uvc_copy_op *op = &uvc_urb->copy_operations[active_op];
> +	unsigned int maxlen;
> 
>  	if (len <= 0)
>  		return;
> 
> -	/* Copy the video data to the buffer. */
>  	maxlen = buf->length - buf->bytesused;
> -	mem = buf->mem + buf->bytesused;
> -	nbytes = min((unsigned int)len, maxlen);
> -	memcpy(mem, data, nbytes);
> -	buf->bytesused += nbytes;
> +
> +	/* Take a buffer reference for async work. */
> +	kref_get(&buf->ref);
> +
> +	op->buf = buf;
> +	op->src = data;
> +	op->dst = buf->mem + buf->bytesused;
> +	op->len = min_t(unsigned int, len, maxlen);
> +
> +	buf->bytesused += op->len;
> 
>  	/* Complete the current frame if the buffer size was exceeded. */
>  	if (len > maxlen) {
> @@ -1116,6 +1149,8 @@ static void uvc_video_decode_data(struct uvc_streaming
> *stream, buf->error = 1;
>  		buf->state = UVC_BUF_STATE_READY;
>  	}
> +
> +	uvc_urb->async_operations++;
>  }
> 
>  static void uvc_video_decode_end(struct uvc_streaming *stream,
> @@ -1324,7 +1359,7 @@ static void uvc_video_decode_isoc(struct uvc_urb
> *uvc_urb, uvc_video_decode_meta(stream, meta_buf, mem, ret);
> 
>  		/* Decode the payload data. */
> -		uvc_video_decode_data(stream, buf, mem + ret,
> +		uvc_video_decode_data(uvc_urb, buf, mem + ret,
>  			urb->iso_frame_desc[i].actual_length - ret);
> 
>  		/* Process the header again. */
> @@ -1384,9 +1419,9 @@ static void uvc_video_decode_bulk(struct uvc_urb
> *uvc_urb, * sure buf is never dereferenced if NULL.
>  	 */
> 
> -	/* Process video data. */
> +	/* Prepare video data for processing. */
>  	if (!stream->bulk.skip_payload && buf != NULL)
> -		uvc_video_decode_data(stream, buf, mem, len);
> +		uvc_video_decode_data(uvc_urb, buf, mem, len);
> 
>  	/* Detect the payload end by a URB smaller than the maximum size (or
>  	 * a payload size equal to the maximum) and process the header again.
> @@ -1472,7 +1507,7 @@ static void uvc_video_complete(struct urb *urb)
>  		uvc_printk(KERN_WARNING, "Non-zero status (%d) in video "
>  			"completion handler.\n", urb->status);
>  		/* fall through */
> -	case -ENOENT:		/* usb_kill_urb() called. */
> +	case -ENOENT:		/* usb_poison_urb() called. */
>  		if (stream->frozen)
>  			return;
>  		/* fall through */
> @@ -1494,12 +1529,26 @@ static void uvc_video_complete(struct urb *urb)
>  		spin_unlock_irqrestore(&qmeta->irqlock, flags);
>  	}
> 
> +	/* Re-initialise the URB async work. */
> +	uvc_urb->async_operations = 0;
> +
> +	/*
> +	 * Process the URB headers, and optionally queue expensive memcpy tasks
> +	 * to be deferred to a work queue.
> +	 */
>  	stream->decode(uvc_urb, buf, buf_meta);
> 
> -	if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
> -		uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n",
> -			ret);
> +	/* If no async work is needed, resubmit the URB immediately. */
> +	if (!uvc_urb->async_operations) {
> +		ret = usb_submit_urb(uvc_urb->urb, GFP_ATOMIC);
> +		if (ret < 0)
> +			uvc_printk(KERN_ERR,
> +				   "Failed to resubmit video URB (%d).\n",
> +				   ret);
> +		return;
>  	}
> +
> +	queue_work(stream->async_wq, &uvc_urb->work);
>  }
> 
>  /*
> @@ -1594,20 +1643,22 @@ static int uvc_alloc_urb_buffers(struct
> uvc_streaming *stream, */
>  static void uvc_uninit_video(struct uvc_streaming *stream, int
> free_buffers) {
> -	struct urb *urb;
> -	unsigned int i;
> +	struct uvc_urb *uvc_urb;
> 
>  	uvc_video_stats_stop(stream);
> 
> -	for (i = 0; i < UVC_URBS; ++i) {
> -		struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
> +	/*
> +	 * We must poison the URBs rather than kill them to ensure that even
> +	 * after the completion handler returns, any asynchronous workqueues
> +	 * will be prevented from resubmitting the URBs.
> +	 */
> +	for_each_uvc_urb(uvc_urb, stream)
> +		usb_poison_urb(uvc_urb->urb);
> 
> -		urb = uvc_urb->urb;
> -		if (urb == NULL)
> -			continue;
> +	flush_workqueue(stream->async_wq);
> 
> -		usb_kill_urb(urb);
> -		usb_free_urb(urb);
> +	for_each_uvc_urb(uvc_urb, stream) {
> +		usb_free_urb(uvc_urb->urb);
>  		uvc_urb->urb = NULL;
>  	}
> 
> @@ -1932,6 +1983,7 @@ int uvc_video_init(struct uvc_streaming *stream)
>  	struct uvc_streaming_control *probe = &stream->ctrl;
>  	struct uvc_format *format = NULL;
>  	struct uvc_frame *frame = NULL;
> +	struct uvc_urb *uvc_urb;
>  	unsigned int i;
>  	int ret;
> 
> @@ -2017,6 +2069,16 @@ int uvc_video_init(struct uvc_streaming *stream)
>  		}
>  	}
> 
> +	/* Allocate a stream specific work queue for asynchronous tasks. */
> +	stream->async_wq = alloc_workqueue("uvcvideo", WQ_UNBOUND | WQ_HIGHPRI,
> +					   0);
> +	if (!stream->async_wq)
> +		return -ENOMEM;
> +
> +	/* Prepare asynchronous work items. */
> +	for_each_uvc_urb(uvc_urb, stream)
> +		INIT_WORK(&uvc_urb->work, uvc_video_copy_data_work);
> +
>  	return 0;
>  }
> 
> diff --git a/drivers/media/usb/uvc/uvcvideo.h
> b/drivers/media/usb/uvc/uvcvideo.h index 1bc17da7f3d4..0953e2e59a79 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -491,12 +491,30 @@ struct uvc_stats_stream {
>  #define UVC_METATADA_BUF_SIZE 1024
> 
>  /**
> + * struct uvc_copy_op: Context structure to schedule asynchronous memcpy
> + *
> + * @buf: active buf object for this operation
> + * @dst: copy destination address
> + * @src: copy source address
> + * @len: copy length
> + */
> +struct uvc_copy_op {
> +	struct uvc_buffer *buf;
> +	void *dst;
> +	const __u8 *src;
> +	size_t len;
> +};
> +
> +/**
>   * struct uvc_urb - URB context management structure
>   *
>   * @urb: the URB described by this context structure
>   * @stream: UVC streaming context
>   * @buffer: memory storage for the URB
>   * @dma: DMA coherent addressing for the urb_buffer
> + * @async_operations: counter to indicate the number of copy operations
> + * @copy_operations: work descriptors for asynchronous copy operations
> + * @work: work queue entry for asynchronous decode
>   */
>  struct uvc_urb {
>  	struct urb *urb;
> @@ -504,6 +522,10 @@ struct uvc_urb {
> 
>  	char *buffer;
>  	dma_addr_t dma;
> +
> +	unsigned int async_operations;
> +	struct uvc_copy_op copy_operations[UVC_MAX_PACKETS];
> +	struct work_struct work;
>  };
> 
>  struct uvc_streaming {
> @@ -536,6 +558,7 @@ struct uvc_streaming {
>  	/* Buffers queue. */
>  	unsigned int frozen : 1;
>  	struct uvc_video_queue queue;
> +	struct workqueue_struct *async_wq;
>  	void (*decode)(struct uvc_urb *uvc_urb, struct uvc_buffer *buf,
>  		       struct uvc_buffer *meta_buf);
> 
> @@ -589,6 +612,11 @@ struct uvc_streaming {
>  	} clock;
>  };
> 
> +#define for_each_uvc_urb(uvc_urb, uvc_streaming) \
> +	for ((uvc_urb) = &(uvc_streaming)->uvc_urb[0]; \
> +	     (uvc_urb) < &(uvc_streaming)->uvc_urb[UVC_URBS]; \
> +	     ++(uvc_urb))
> +
>  struct uvc_device_info {
>  	u32	quirks;
>  	u32	meta_format;


-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 7/9] media: uvcvideo: Split uvc_video_enable into two
  2018-11-06 21:27 ` [PATCH v5 7/9] media: uvcvideo: Split uvc_video_enable into two Kieran Bingham
@ 2018-11-06 23:08   ` Laurent Pinchart
  2018-11-07 12:20     ` Kieran Bingham
  0 siblings, 1 reply; 27+ messages in thread
From: Laurent Pinchart @ 2018-11-06 23:08 UTC (permalink / raw)
  To: Kieran Bingham
  Cc: linux-media, Guennadi Liakhovetski, Olivier BRAUN, Troy Kisky,
	Randy Dunlap, Philipp Zabel, Ezequiel Garcia, Kieran Bingham

Hi Kieran,

Thank you for the patch.

On Tuesday, 6 November 2018 23:27:18 EET Kieran Bingham wrote:
> From: Kieran Bingham <kieran.bingham@ideasonboard.com>
> 
> uvc_video_enable() is used both to start and stop the video stream
> object, however the single function entry point shares no code between
> the two operations.
> 
> Split the function into two distinct calls, and rename to
> uvc_video_start_streaming() and uvc_video_stop_streaming() as
> appropriate.
> 
> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> ---
>  drivers/media/usb/uvc/uvc_queue.c |  4 +-
>  drivers/media/usb/uvc/uvc_video.c | 56 +++++++++++++++-----------------
>  drivers/media/usb/uvc/uvcvideo.h  |  3 +-
>  3 files changed, 31 insertions(+), 32 deletions(-)
> 
> diff --git a/drivers/media/usb/uvc/uvc_queue.c
> b/drivers/media/usb/uvc/uvc_queue.c index cd8c03341de0..682698ec1118 100644
> --- a/drivers/media/usb/uvc/uvc_queue.c
> +++ b/drivers/media/usb/uvc/uvc_queue.c
> @@ -176,7 +176,7 @@ static int uvc_start_streaming(struct vb2_queue *vq,
> unsigned int count)
> 
>  	queue->buf_used = 0;
> 
> -	ret = uvc_video_enable(stream, 1);
> +	ret = uvc_video_start_streaming(stream);
>  	if (ret == 0)
>  		return 0;
> 
> @@ -194,7 +194,7 @@ static void uvc_stop_streaming(struct vb2_queue *vq)
>  	lockdep_assert_irqs_enabled();
> 
>  	if (vq->type != V4L2_BUF_TYPE_META_CAPTURE)
> -		uvc_video_enable(uvc_queue_to_stream(queue), 0);
> +		uvc_video_stop_streaming(uvc_queue_to_stream(queue));
> 
>  	spin_lock_irq(&queue->irqlock);
>  	uvc_queue_return_buffers(queue, UVC_BUF_STATE_ERROR);
> diff --git a/drivers/media/usb/uvc/uvc_video.c
> b/drivers/media/usb/uvc/uvc_video.c index ce9e40444507..0d35e933856a 100644
> --- a/drivers/media/usb/uvc/uvc_video.c
> +++ b/drivers/media/usb/uvc/uvc_video.c
> @@ -2082,38 +2082,10 @@ int uvc_video_init(struct uvc_streaming *stream)
>  	return 0;
>  }
> 
> -/*
> - * Enable or disable the video stream.
> - */
> -int uvc_video_enable(struct uvc_streaming *stream, int enable)
> +int uvc_video_start_streaming(struct uvc_streaming *stream)
>  {
>  	int ret;
> 
> -	if (!enable) {
> -		uvc_uninit_video(stream, 1);
> -		if (stream->intf->num_altsetting > 1) {
> -			usb_set_interface(stream->dev->udev,
> -					  stream->intfnum, 0);
> -		} else {
> -			/* UVC doesn't specify how to inform a bulk-based device
> -			 * when the video stream is stopped. Windows sends a
> -			 * CLEAR_FEATURE(HALT) request to the video streaming
> -			 * bulk endpoint, mimic the same behaviour.
> -			 */
> -			unsigned int epnum = stream->header.bEndpointAddress
> -					   & USB_ENDPOINT_NUMBER_MASK;
> -			unsigned int dir = stream->header.bEndpointAddress
> -					 & USB_ENDPOINT_DIR_MASK;
> -			unsigned int pipe;
> -
> -			pipe = usb_sndbulkpipe(stream->dev->udev, epnum) | dir;
> -			usb_clear_halt(stream->dev->udev, pipe);
> -		}
> -
> -		uvc_video_clock_cleanup(stream);
> -		return 0;
> -	}
> -
>  	ret = uvc_video_clock_init(stream);
>  	if (ret < 0)
>  		return ret;
> @@ -2136,3 +2108,29 @@ int uvc_video_enable(struct uvc_streaming *stream,
> int enable)
> 
>  	return ret;
>  }
> +
> +int uvc_video_stop_streaming(struct uvc_streaming *stream)
> +{
> +	uvc_uninit_video(stream, 1);
> +	if (stream->intf->num_altsetting > 1) {
> +		usb_set_interface(stream->dev->udev,
> +				  stream->intfnum, 0);

This now holds on a single line.

> +	} else {
> +		/* UVC doesn't specify how to inform a bulk-based device

Let's fix the checkpatch.pl warning here.

> +		 * when the video stream is stopped. Windows sends a
> +		 * CLEAR_FEATURE(HALT) request to the video streaming
> +		 * bulk endpoint, mimic the same behaviour.
> +		 */
> +		unsigned int epnum = stream->header.bEndpointAddress
> +				   & USB_ENDPOINT_NUMBER_MASK;
> +		unsigned int dir = stream->header.bEndpointAddress
> +				 & USB_ENDPOINT_DIR_MASK;
> +		unsigned int pipe;
> +
> +		pipe = usb_sndbulkpipe(stream->dev->udev, epnum) | dir;
> +		usb_clear_halt(stream->dev->udev, pipe);
> +	}
> +
> +	uvc_video_clock_cleanup(stream);
> +	return 0;

As this always return 0 you can make it a void function.

Apart from that,

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

I'll take the patch in my tree with the above changes.

> +}
> diff --git a/drivers/media/usb/uvc/uvcvideo.h
> b/drivers/media/usb/uvc/uvcvideo.h index 0953e2e59a79..c0a120496a1f 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -784,7 +784,8 @@ void uvc_mc_cleanup_entity(struct uvc_entity *entity);
>  int uvc_video_init(struct uvc_streaming *stream);
>  int uvc_video_suspend(struct uvc_streaming *stream);
>  int uvc_video_resume(struct uvc_streaming *stream, int reset);
> -int uvc_video_enable(struct uvc_streaming *stream, int enable);
> +int uvc_video_start_streaming(struct uvc_streaming *stream);
> +int uvc_video_stop_streaming(struct uvc_streaming *stream);
>  int uvc_probe_video(struct uvc_streaming *stream,
>  		    struct uvc_streaming_control *probe);
>  int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit,

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 8/9] media: uvcvideo: Rename uvc_{un,}init_video()
  2018-11-06 21:27 ` [PATCH v5 8/9] media: uvcvideo: Rename uvc_{un,}init_video() Kieran Bingham
@ 2018-11-06 23:13   ` Laurent Pinchart
  2018-11-07 14:30     ` Kieran Bingham
  0 siblings, 1 reply; 27+ messages in thread
From: Laurent Pinchart @ 2018-11-06 23:13 UTC (permalink / raw)
  To: Kieran Bingham
  Cc: linux-media, Guennadi Liakhovetski, Olivier BRAUN, Troy Kisky,
	Randy Dunlap, Philipp Zabel, Ezequiel Garcia, Kieran Bingham

Hi Kieran,

Thank you for the patch.

On Tuesday, 6 November 2018 23:27:19 EET Kieran Bingham wrote:
> From: Kieran Bingham <kieran.bingham@ideasonboard.com>
> 
> We have both uvc_init_video() and uvc_video_init() calls which can be
> quite confusing to determine the process for each. Now that video
> uvc_video_enable() has been renamed to uvc_video_start_streaming(),
> adapt these calls to suit the new flow.
> 
> Rename uvc_init_video() to uvc_video_start() and uvc_uninit_video() to
> uvc_video_stop().

I agree that these functions are badly named and should be renamed. We are 
however entering the nitpicking territory :-) The two functions do more that 
starting and stopping, they also allocate and free URBs and the associated 
buffers. It could also be argued that they don't actually start and stop 
anything, as beyond URB management, they just queue the URBs initially and 
kill them. I thus wonder if we could come up with better names.

> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> ---
>  drivers/media/usb/uvc/uvc_video.c | 18 +++++++++---------
>  1 file changed, 9 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/media/usb/uvc/uvc_video.c
> b/drivers/media/usb/uvc/uvc_video.c index 0d35e933856a..020022e6ade4 100644
> --- a/drivers/media/usb/uvc/uvc_video.c
> +++ b/drivers/media/usb/uvc/uvc_video.c
> @@ -1641,7 +1641,7 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming
> *stream, /*
>   * Uninitialize isochronous/bulk URBs and free transfer buffers.
>   */
> -static void uvc_uninit_video(struct uvc_streaming *stream, int
> free_buffers) +static void uvc_video_stop(struct uvc_streaming *stream, int
> free_buffers) {
>  	struct uvc_urb *uvc_urb;
> 
> @@ -1718,7 +1718,7 @@ static int uvc_init_video_isoc(struct uvc_streaming
> *stream,
> 
>  		urb = usb_alloc_urb(npackets, gfp_flags);
>  		if (urb == NULL) {
> -			uvc_uninit_video(stream, 1);
> +			uvc_video_stop(stream, 1);
>  			return -ENOMEM;
>  		}
> 
> @@ -1786,7 +1786,7 @@ static int uvc_init_video_bulk(struct uvc_streaming
> *stream,
> 
>  		urb = usb_alloc_urb(0, gfp_flags);
>  		if (urb == NULL) {
> -			uvc_uninit_video(stream, 1);
> +			uvc_video_stop(stream, 1);
>  			return -ENOMEM;
>  		}
> 
> @@ -1806,7 +1806,7 @@ static int uvc_init_video_bulk(struct uvc_streaming
> *stream, /*
>   * Initialize isochronous/bulk URBs and allocate transfer buffers.
>   */
> -static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
> +static int uvc_video_start(struct uvc_streaming *stream, gfp_t gfp_flags)
>  {
>  	struct usb_interface *intf = stream->intf;
>  	struct usb_host_endpoint *ep;
> @@ -1894,7 +1894,7 @@ static int uvc_init_video(struct uvc_streaming
> *stream, gfp_t gfp_flags) if (ret < 0) {
>  			uvc_printk(KERN_ERR, "Failed to submit URB %u "
>  					"(%d).\n", i, ret);
> -			uvc_uninit_video(stream, 1);
> +			uvc_video_stop(stream, 1);
>  			return ret;
>  		}
>  	}
> @@ -1925,7 +1925,7 @@ int uvc_video_suspend(struct uvc_streaming *stream)
>  		return 0;
> 
>  	stream->frozen = 1;
> -	uvc_uninit_video(stream, 0);
> +	uvc_video_stop(stream, 0);
>  	usb_set_interface(stream->dev->udev, stream->intfnum, 0);
>  	return 0;
>  }
> @@ -1961,7 +1961,7 @@ int uvc_video_resume(struct uvc_streaming *stream, int
> reset) if (ret < 0)
>  		return ret;
> 
> -	return uvc_init_video(stream, GFP_NOIO);
> +	return uvc_video_start(stream, GFP_NOIO);
>  }
> 
>  /* ------------------------------------------------------------------------
> @@ -2095,7 +2095,7 @@ int uvc_video_start_streaming(struct uvc_streaming
> *stream) if (ret < 0)
>  		goto error_commit;
> 
> -	ret = uvc_init_video(stream, GFP_KERNEL);
> +	ret = uvc_video_start(stream, GFP_KERNEL);
>  	if (ret < 0)
>  		goto error_video;
> 
> @@ -2111,7 +2111,7 @@ int uvc_video_start_streaming(struct uvc_streaming
> *stream)
> 
>  int uvc_video_stop_streaming(struct uvc_streaming *stream)
>  {
> -	uvc_uninit_video(stream, 1);
> +	uvc_video_stop(stream, 1);
>  	if (stream->intf->num_altsetting > 1) {
>  		usb_set_interface(stream->dev->udev,
>  				  stream->intfnum, 0);


-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 9/9] media: uvcvideo: Utilise for_each_uvc_urb iterator
  2018-11-06 21:27 ` [PATCH v5 9/9] media: uvcvideo: Utilise for_each_uvc_urb iterator Kieran Bingham
@ 2018-11-06 23:21   ` Laurent Pinchart
  2018-11-07 13:50     ` Kieran Bingham
  0 siblings, 1 reply; 27+ messages in thread
From: Laurent Pinchart @ 2018-11-06 23:21 UTC (permalink / raw)
  To: Kieran Bingham
  Cc: linux-media, Guennadi Liakhovetski, Olivier BRAUN, Troy Kisky,
	Randy Dunlap, Philipp Zabel, Ezequiel Garcia, Kieran Bingham

Hi Kieran,

Thank you for the patch.

On Tuesday, 6 November 2018 23:27:20 EET Kieran Bingham wrote:
> From: Kieran Bingham <kieran.bingham@ideasonboard.com>
> 
> A new iterator is available for processing UVC URB structures. This
> simplifies the processing of the internal stream data.
> 
> Convert the manual loop iterators to the new helper, adding an index
> helper to keep the existing debug print.
> 
> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> 
> ---
> This patch converts to using the iterator which for most hunks makes
> sense. The only one with uncertainty is in uvc_alloc_urb_buffers() where
> the loop index is used to determine if all the buffers were successfully
> allocated.
> 
> As the loop index is not incremented by the loops, we can obtain the
> buffer index - but then we are offset and out-by-one.
> 
> Adjusting this in the code is fine - but at that point it feels like
> it's not adding much value. I've left that hunk in for this patch - but
> that part could be reverted if desired - unless anyone has a better
> rework of the buffer check?
> 
>  drivers/media/usb/uvc/uvc_video.c | 51 ++++++++++++++++----------------
>  drivers/media/usb/uvc/uvcvideo.h  |  3 ++-
>  2 files changed, 29 insertions(+), 25 deletions(-)
> 
> diff --git a/drivers/media/usb/uvc/uvc_video.c
> b/drivers/media/usb/uvc/uvc_video.c index 020022e6ade4..f6e5db7ea50e 100644
> --- a/drivers/media/usb/uvc/uvc_video.c
> +++ b/drivers/media/usb/uvc/uvc_video.c
> @@ -1556,20 +1556,19 @@ static void uvc_video_complete(struct urb *urb)
>   */
>  static void uvc_free_urb_buffers(struct uvc_streaming *stream)
>  {
> -	unsigned int i;
> +	struct uvc_urb *uvc_urb;
> 
> -	for (i = 0; i < UVC_URBS; ++i) {
> -		struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
> +	for_each_uvc_urb(uvc_urb, stream) {
> +		if (!uvc_urb->buffer)
> +			continue;
> 
> -		if (uvc_urb->buffer) {
>  #ifndef CONFIG_DMA_NONCOHERENT
> -			usb_free_coherent(stream->dev->udev, stream->urb_size,
> -					uvc_urb->buffer, uvc_urb->dma);
> +		usb_free_coherent(stream->dev->udev, stream->urb_size,
> +				  uvc_urb->buffer, uvc_urb->dma);
>  #else
> -			kfree(uvc_urb->buffer);
> +		kfree(uvc_urb->buffer);
>  #endif
> -			uvc_urb->buffer = NULL;
> -		}
> +		uvc_urb->buffer = NULL;
>  	}
> 
>  	stream->urb_size = 0;
> @@ -1589,8 +1588,9 @@ static void uvc_free_urb_buffers(struct uvc_streaming
> *stream) static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,
>  	unsigned int size, unsigned int psize, gfp_t gfp_flags)
>  {
> +	struct uvc_urb *uvc_urb;
>  	unsigned int npackets;
> -	unsigned int i;
> +	unsigned int i = 0;
> 
>  	/* Buffers are already allocated, bail out. */
>  	if (stream->urb_size)
> @@ -1605,8 +1605,12 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming
> *stream,
> 
>  	/* Retry allocations until one succeed. */
>  	for (; npackets > 1; npackets /= 2) {
> -		for (i = 0; i < UVC_URBS; ++i) {
> -			struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
> +		for_each_uvc_urb(uvc_urb, stream) {
> +			/*
> +			 * Track how many URBs we allocate, adding one to the
> +			 * index to account for our zero index.
> +			 */
> +			i = uvc_urb_index(uvc_urb) + 1;

That's a bit ugly indeed, I think we could keep the existing loop;

>  			stream->urb_size = psize * npackets;
>  #ifndef CONFIG_DMA_NONCOHERENT
> @@ -1700,7 +1704,8 @@ static int uvc_init_video_isoc(struct uvc_streaming
> *stream, struct usb_host_endpoint *ep, gfp_t gfp_flags)
>  {
>  	struct urb *urb;
> -	unsigned int npackets, i, j;
> +	struct uvc_urb *uvc_urb;
> +	unsigned int npackets, j;

j without i seems weird, could you rename it ?

>  	u16 psize;
>  	u32 size;
> 
> @@ -1713,9 +1718,7 @@ static int uvc_init_video_isoc(struct uvc_streaming
> *stream,
> 
>  	size = npackets * psize;
> 
> -	for (i = 0; i < UVC_URBS; ++i) {
> -		struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
> -
> +	for_each_uvc_urb(uvc_urb, stream) {
>  		urb = usb_alloc_urb(npackets, gfp_flags);
>  		if (urb == NULL) {
>  			uvc_video_stop(stream, 1);
> @@ -1757,7 +1760,8 @@ static int uvc_init_video_bulk(struct uvc_streaming
> *stream, struct usb_host_endpoint *ep, gfp_t gfp_flags)
>  {
>  	struct urb *urb;
> -	unsigned int npackets, pipe, i;
> +	struct uvc_urb *uvc_urb;
> +	unsigned int npackets, pipe;
>  	u16 psize;
>  	u32 size;
> 
> @@ -1781,9 +1785,7 @@ static int uvc_init_video_bulk(struct uvc_streaming
> *stream, if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
>  		size = 0;
> 
> -	for (i = 0; i < UVC_URBS; ++i) {
> -		struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
> -
> +	for_each_uvc_urb(uvc_urb, stream) {
>  		urb = usb_alloc_urb(0, gfp_flags);
>  		if (urb == NULL) {
>  			uvc_video_stop(stream, 1);
> @@ -1810,6 +1812,7 @@ static int uvc_video_start(struct uvc_streaming
> *stream, gfp_t gfp_flags) {
>  	struct usb_interface *intf = stream->intf;
>  	struct usb_host_endpoint *ep;
> +	struct uvc_urb *uvc_urb;
>  	unsigned int i;
>  	int ret;
> 
> @@ -1887,13 +1890,11 @@ static int uvc_video_start(struct uvc_streaming
> *stream, gfp_t gfp_flags) return ret;
> 
>  	/* Submit the URBs. */
> -	for (i = 0; i < UVC_URBS; ++i) {
> -		struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
> -
> +	for_each_uvc_urb(uvc_urb, stream) {
>  		ret = usb_submit_urb(uvc_urb->urb, gfp_flags);
>  		if (ret < 0) {
> -			uvc_printk(KERN_ERR, "Failed to submit URB %u "
> -					"(%d).\n", i, ret);
> +			uvc_printk(KERN_ERR, "Failed to submit URB %u (%d).\n",
> +				   uvc_urb_index(uvc_urb), ret);
>  			uvc_video_stop(stream, 1);
>  			return ret;
>  		}
> diff --git a/drivers/media/usb/uvc/uvcvideo.h
> b/drivers/media/usb/uvc/uvcvideo.h index c0a120496a1f..6a0f1b59356c 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -617,6 +617,9 @@ struct uvc_streaming {
>  	     (uvc_urb) < &(uvc_streaming)->uvc_urb[UVC_URBS]; \
>  	     ++(uvc_urb))
> 
> +#define uvc_urb_index(uvc_urb) \
> +	(unsigned int)((uvc_urb) - (&(uvc_urb)->stream->uvc_urb[0]))
> +
>  struct uvc_device_info {
>  	u32	quirks;
>  	u32	meta_format;

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 0/9] Asynchronous UVC
  2018-11-06 21:27 [PATCH v5 0/9] Asynchronous UVC Kieran Bingham
                   ` (8 preceding siblings ...)
  2018-11-06 21:27 ` [PATCH v5 9/9] media: uvcvideo: Utilise for_each_uvc_urb iterator Kieran Bingham
@ 2018-11-06 23:31 ` Laurent Pinchart
  2018-11-08 17:01   ` Laurent Pinchart
  2018-11-27 20:17 ` Pavel Machek
  10 siblings, 1 reply; 27+ messages in thread
From: Laurent Pinchart @ 2018-11-06 23:31 UTC (permalink / raw)
  To: Kieran Bingham
  Cc: linux-media, Guennadi Liakhovetski, Olivier BRAUN, Troy Kisky,
	Randy Dunlap, Philipp Zabel, Ezequiel Garcia, Kieran Bingham

Hi Kieran,

Thank you for the patches.

On Tuesday, 6 November 2018 23:27:11 EET Kieran Bingham wrote:
> From: Kieran Bingham <kieran.bingham@ideasonboard.com>
> 
> The Linux UVC driver has long provided adequate performance capabilities for
> web-cams and low data rate video devices in Linux while resolutions were
> low.
> 
> Modern USB cameras are now capable of high data rates thanks to USB3 with
> 1080p, and even 4k capture resolutions supported.
> 
> Cameras such as the Stereolabs ZED (bulk transfers) or the Logitech BRIO
> (isochronous transfers) can generate more data than an embedded ARM core is
> able to process on a single core, resulting in frame loss.
> 
> A large part of this performance impact is from the requirement to
> ‘memcpy’ frames out from URB packets to destination frames. This unfortunate
> requirement is due to the UVC protocol allowing a variable length header,
> and thus it is not possible to provide the target frame buffers directly.
> 
> Extra throughput is possible by moving the actual memcpy actions to a work
> queue, and moving the memcpy out of interrupt context thus allowing work
> tasks to be scheduled across multiple cores.
> 
> This series has been tested on both the ZED and BRIO cameras on arm64
> platforms, and with thanks to Randy Dunlap, a Dynex 1.3MP Webcam, a Sonix
> USB2 Camera, and a built in Toshiba Laptop camera, and with thanks to
> Philipp Zabel for testing on a Lite-On internal Laptop Webcam, Logitech
> C910 (USB2 isoc), Oculus Sensor (USB3 isoc), and Microsoft HoloLens Sensors
> (USB3 bulk).
> 
> As far as I am aware iSight devices, and devices which use UVC to encode
> data (output device) have not yet been tested - but should find no ill
> effect (at least not until they are tested of course :D )

:-D

I'm not sure whether anyone is still using those devices with Linux. I 
wouldn't be surprised if we realized down the road that they already don't 
work.

> Tested-by: Randy Dunlap <rdunlap@infradead.org>
> Tested-by: Philipp Zabel <philipp.zabel@gmail.com>
> 
> v2:
>  - Fix race reported by Guennadi
> 
> v3:
>  - Fix similar race reported by Laurent
>  - Only queue work if required (encode/isight do not queue work)
>  - Refactor/Rename variables for clarity
> 
> v4:
>  - (Yet another) Rework of the uninitialise path.
>    This time to hopefully clean up the shutdown races for good.
>    use usb_poison_urb() to halt all URBs, then flush the work queue
>    before freeing.
>  - Rebase to latest linux-media/master
> 
> v5:
>  - Provide lockdep validation
>  - rename uvc_queue_requeue -> uvc_queue_buffer_requeue()
>  - Fix comments and periods throughout
>  - Rebase to media/v4.20-2
>  - Use GFP_KERNEL allocation in uvc_video_copy_data_work()
>  - Fix function documentation for uvc_video_copy_data_work()
>  - Add periods to the end of sentences
>  - Rename 'decode' variable to 'op' in uvc_video_decode_data()
>  - Move uvc_urb->async_operations initialisation to before use
>  - Move async workqueue to match uvc_streaming lifetime instead of
>    streamon/streamoff
>  - bracket the for_each_uvc_urb() macro
> 
>  - New patches added to series:
>     media: uvcvideo: Split uvc_video_enable into two
>     media: uvcvideo: Rename uvc_{un,}init_video()
>     media: uvcvideo: Utilise for_each_uvc_urb iterator
> 
> Kieran Bingham (9):
>   media: uvcvideo: Refactor URB descriptors
>   media: uvcvideo: Convert decode functions to use new context structure
>   media: uvcvideo: Protect queue internals with helper
>   media: uvcvideo: queue: Simplify spin-lock usage
>   media: uvcvideo: queue: Support asynchronous buffer handling
>   media: uvcvideo: Move decode processing to process context
>   media: uvcvideo: Split uvc_video_enable into two

I've taken the above patches in my tree.

>   media: uvcvideo: Rename uvc_{un,}init_video()
>   media: uvcvideo: Utilise for_each_uvc_urb iterator

And I've sent review comments for these two.

>  drivers/media/usb/uvc/uvc_driver.c |   2 +-
>  drivers/media/usb/uvc/uvc_isight.c |   6 +-
>  drivers/media/usb/uvc/uvc_queue.c  | 110 +++++++++---
>  drivers/media/usb/uvc/uvc_video.c  | 282 +++++++++++++++++++-----------
>  drivers/media/usb/uvc/uvcvideo.h   |  65 ++++++-
>  5 files changed, 331 insertions(+), 134 deletions(-)
> 
> base-commit: dafb7f9aef2fd44991ff1691721ff765a23be27b

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 7/9] media: uvcvideo: Split uvc_video_enable into two
  2018-11-06 23:08   ` Laurent Pinchart
@ 2018-11-07 12:20     ` Kieran Bingham
  0 siblings, 0 replies; 27+ messages in thread
From: Kieran Bingham @ 2018-11-07 12:20 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: linux-media, Guennadi Liakhovetski, Olivier BRAUN, Troy Kisky,
	Randy Dunlap, Philipp Zabel, Ezequiel Garcia, Kieran Bingham

Hi Laurent,

On 06/11/2018 23:08, Laurent Pinchart wrote:
> Hi Kieran,
> 
> Thank you for the patch.
> 
> On Tuesday, 6 November 2018 23:27:18 EET Kieran Bingham wrote:
>> From: Kieran Bingham <kieran.bingham@ideasonboard.com>
>>
>> uvc_video_enable() is used both to start and stop the video stream
>> object, however the single function entry point shares no code between
>> the two operations.
>>
>> Split the function into two distinct calls, and rename to
>> uvc_video_start_streaming() and uvc_video_stop_streaming() as
>> appropriate.
>>
>> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
>> ---
>>  drivers/media/usb/uvc/uvc_queue.c |  4 +-
>>  drivers/media/usb/uvc/uvc_video.c | 56 +++++++++++++++-----------------
>>  drivers/media/usb/uvc/uvcvideo.h  |  3 +-
>>  3 files changed, 31 insertions(+), 32 deletions(-)
>>
>> diff --git a/drivers/media/usb/uvc/uvc_queue.c
>> b/drivers/media/usb/uvc/uvc_queue.c index cd8c03341de0..682698ec1118 100644
>> --- a/drivers/media/usb/uvc/uvc_queue.c
>> +++ b/drivers/media/usb/uvc/uvc_queue.c
>> @@ -176,7 +176,7 @@ static int uvc_start_streaming(struct vb2_queue *vq,
>> unsigned int count)
>>
>>  	queue->buf_used = 0;
>>
>> -	ret = uvc_video_enable(stream, 1);
>> +	ret = uvc_video_start_streaming(stream);
>>  	if (ret == 0)
>>  		return 0;
>>
>> @@ -194,7 +194,7 @@ static void uvc_stop_streaming(struct vb2_queue *vq)
>>  	lockdep_assert_irqs_enabled();
>>
>>  	if (vq->type != V4L2_BUF_TYPE_META_CAPTURE)
>> -		uvc_video_enable(uvc_queue_to_stream(queue), 0);
>> +		uvc_video_stop_streaming(uvc_queue_to_stream(queue));
>>
>>  	spin_lock_irq(&queue->irqlock);
>>  	uvc_queue_return_buffers(queue, UVC_BUF_STATE_ERROR);
>> diff --git a/drivers/media/usb/uvc/uvc_video.c
>> b/drivers/media/usb/uvc/uvc_video.c index ce9e40444507..0d35e933856a 100644
>> --- a/drivers/media/usb/uvc/uvc_video.c
>> +++ b/drivers/media/usb/uvc/uvc_video.c
>> @@ -2082,38 +2082,10 @@ int uvc_video_init(struct uvc_streaming *stream)
>>  	return 0;
>>  }
>>
>> -/*
>> - * Enable or disable the video stream.
>> - */
>> -int uvc_video_enable(struct uvc_streaming *stream, int enable)
>> +int uvc_video_start_streaming(struct uvc_streaming *stream)
>>  {
>>  	int ret;
>>
>> -	if (!enable) {
>> -		uvc_uninit_video(stream, 1);
>> -		if (stream->intf->num_altsetting > 1) {
>> -			usb_set_interface(stream->dev->udev,
>> -					  stream->intfnum, 0);
>> -		} else {
>> -			/* UVC doesn't specify how to inform a bulk-based device
>> -			 * when the video stream is stopped. Windows sends a
>> -			 * CLEAR_FEATURE(HALT) request to the video streaming
>> -			 * bulk endpoint, mimic the same behaviour.
>> -			 */
>> -			unsigned int epnum = stream->header.bEndpointAddress
>> -					   & USB_ENDPOINT_NUMBER_MASK;
>> -			unsigned int dir = stream->header.bEndpointAddress
>> -					 & USB_ENDPOINT_DIR_MASK;
>> -			unsigned int pipe;
>> -
>> -			pipe = usb_sndbulkpipe(stream->dev->udev, epnum) | dir;
>> -			usb_clear_halt(stream->dev->udev, pipe);
>> -		}
>> -
>> -		uvc_video_clock_cleanup(stream);
>> -		return 0;
>> -	}
>> -
>>  	ret = uvc_video_clock_init(stream);
>>  	if (ret < 0)
>>  		return ret;
>> @@ -2136,3 +2108,29 @@ int uvc_video_enable(struct uvc_streaming *stream,
>> int enable)
>>
>>  	return ret;
>>  }
>> +
>> +int uvc_video_stop_streaming(struct uvc_streaming *stream)
>> +{
>> +	uvc_uninit_video(stream, 1);
>> +	if (stream->intf->num_altsetting > 1) {
>> +		usb_set_interface(stream->dev->udev,
>> +				  stream->intfnum, 0);
> 
> This now holds on a single line.

Ah yes.

> 
>> +	} else {
>> +		/* UVC doesn't specify how to inform a bulk-based device
> 
> Let's fix the checkpatch.pl warning here.

Oh ? I didn't get any checkpatch warnings. Do I need to add some
parameters to my checkpatch now?

> 
>> +		 * when the video stream is stopped. Windows sends a
>> +		 * CLEAR_FEATURE(HALT) request to the video streaming
>> +		 * bulk endpoint, mimic the same behaviour.
>> +		 */
>> +		unsigned int epnum = stream->header.bEndpointAddress
>> +				   & USB_ENDPOINT_NUMBER_MASK;
>> +		unsigned int dir = stream->header.bEndpointAddress
>> +				 & USB_ENDPOINT_DIR_MASK;
>> +		unsigned int pipe;
>> +
>> +		pipe = usb_sndbulkpipe(stream->dev->udev, epnum) | dir;
>> +		usb_clear_halt(stream->dev->udev, pipe);
>> +	}
>> +
>> +	uvc_video_clock_cleanup(stream);
>> +	return 0;
> 
> As this always return 0 you can make it a void function.

Certainly.

> 
> Apart from that,
> 
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> 
> I'll take the patch in my tree with the above changes.
> 

Great, thanks.

--
KB

>> +}
>> diff --git a/drivers/media/usb/uvc/uvcvideo.h
>> b/drivers/media/usb/uvc/uvcvideo.h index 0953e2e59a79..c0a120496a1f 100644
>> --- a/drivers/media/usb/uvc/uvcvideo.h
>> +++ b/drivers/media/usb/uvc/uvcvideo.h
>> @@ -784,7 +784,8 @@ void uvc_mc_cleanup_entity(struct uvc_entity *entity);
>>  int uvc_video_init(struct uvc_streaming *stream);
>>  int uvc_video_suspend(struct uvc_streaming *stream);
>>  int uvc_video_resume(struct uvc_streaming *stream, int reset);
>> -int uvc_video_enable(struct uvc_streaming *stream, int enable);
>> +int uvc_video_start_streaming(struct uvc_streaming *stream);
>> +int uvc_video_stop_streaming(struct uvc_streaming *stream);
>>  int uvc_probe_video(struct uvc_streaming *stream,
>>  		    struct uvc_streaming_control *probe);
>>  int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit,
> 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 6/9] media: uvcvideo: Move decode processing to process context
  2018-11-06 22:58   ` Laurent Pinchart
@ 2018-11-07 12:22     ` Kieran Bingham
  0 siblings, 0 replies; 27+ messages in thread
From: Kieran Bingham @ 2018-11-07 12:22 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: linux-media, Guennadi Liakhovetski, Olivier BRAUN, Troy Kisky,
	Randy Dunlap, Philipp Zabel, Ezequiel Garcia, Kieran Bingham

On 06/11/2018 22:58, Laurent Pinchart wrote:
> Hi Kieran,
> 
> Thank you for the patch.
> 
> On Tuesday, 6 November 2018 23:27:17 EET Kieran Bingham wrote:
>> From: Kieran Bingham <kieran.bingham@ideasonboard.com>
>>
>> Newer high definition cameras, and cameras with multiple lenses such as
>> the range of stereo-vision cameras now available have ever increasing
>> data rates.
>>
>> The inclusion of a variable length packet header in URB packets mean
>> that we must memcpy the frame data out to our destination 'manually'.
>> This can result in data rates of up to 2 gigabits per second being
>> processed.
>>
>> To improve efficiency, and maximise throughput, handle the URB decode
>> processing through a work queue to move it from interrupt context, and
>> allow multiple processors to work on URBs in parallel.
>>
>> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> 
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> 
> I wonder if we shouldn't, as a future improvement, only queue async work when 
> the quantity of data to be copied is above a certain threshold.


Possibly - lets keep it in mind for when we get back to looking at
Keiichi's patch and any cache management for further performance
improvements

--
Kieran

> 
>> ---
>> v2:
>>  - Lock full critical section of usb_submit_urb()
>>
>> v3:
>>  - Fix race on submitting uvc_video_decode_data_work() to work queue.
>>  - Rename uvc_decode_op -> uvc_copy_op (Generic to encode/decode)
>>  - Rename decodes -> copy_operations
>>  - Don't queue work if there is no async task
>>  - obtain copy op structure directly in uvc_video_decode_data()
>>  - uvc_video_decode_data_work() -> uvc_video_copy_data_work()
>>
>> v4:
>>  - Provide for_each_uvc_urb()
>>  - Simplify fix for shutdown race to flush queue before freeing URBs
>>  - Rebase to v4.16-rc4 (linux-media/master) adjusting for metadata
>>    conflicts.
>>
>> v5:
>>  - Rebase to media/v4.20-2
>>  - Use GFP_KERNEL allocation in uvc_video_copy_data_work()
>>  - Fix function documentation for uvc_video_copy_data_work()
>>  - Add periods to the end of sentences
>>  - Rename 'decode' variable to 'op' in uvc_video_decode_data()
>>  - Move uvc_urb->async_operations initialisation to before use
>>  - Move async workqueue to match uvc_streaming lifetime instead of
>>    streamon/streamoff
>>
>>  drivers/media/usb/uvc/uvc_driver.c |   2 +-
>>  drivers/media/usb/uvc/uvc_video.c  | 110 +++++++++++++++++++++++-------
>>  drivers/media/usb/uvc/uvcvideo.h   |  28 ++++++++-
>>  3 files changed, 116 insertions(+), 24 deletions(-)
>>
>> diff --git a/drivers/media/usb/uvc/uvc_driver.c
>> b/drivers/media/usb/uvc/uvc_driver.c index bc369a0934a3..e61a6d26e812
>> 100644
>> --- a/drivers/media/usb/uvc/uvc_driver.c
>> +++ b/drivers/media/usb/uvc/uvc_driver.c
>> @@ -1883,6 +1883,8 @@ static void uvc_unregister_video(struct uvc_device
>> *dev) video_unregister_device(&stream->vdev);
>>  		video_unregister_device(&stream->meta.vdev);
>>
>> +		destroy_workqueue(stream->async_wq);
>> +
>>  		uvc_debugfs_cleanup_stream(stream);
>>  	}
>>  }
>> diff --git a/drivers/media/usb/uvc/uvc_video.c
>> b/drivers/media/usb/uvc/uvc_video.c index 7a7779e1b466..ce9e40444507 100644
>> --- a/drivers/media/usb/uvc/uvc_video.c
>> +++ b/drivers/media/usb/uvc/uvc_video.c
>> @@ -1094,21 +1094,54 @@ static int uvc_video_decode_start(struct
>> uvc_streaming *stream, return data[0];
>>  }
>>
>> -static void uvc_video_decode_data(struct uvc_streaming *stream,
>> +/*
>> + * uvc_video_decode_data_work: Asynchronous memcpy processing
>> + *
>> + * Copy URB data to video buffers in process context, releasing buffer
>> + * references and requeuing the URB when done.
>> + */
>> +static void uvc_video_copy_data_work(struct work_struct *work)
>> +{
>> +	struct uvc_urb *uvc_urb = container_of(work, struct uvc_urb, work);
>> +	unsigned int i;
>> +	int ret;
>> +
>> +	for (i = 0; i < uvc_urb->async_operations; i++) {
>> +		struct uvc_copy_op *op = &uvc_urb->copy_operations[i];
>> +
>> +		memcpy(op->dst, op->src, op->len);
>> +
>> +		/* Release reference taken on this buffer. */
>> +		uvc_queue_buffer_release(op->buf);
>> +	}
>> +
>> +	ret = usb_submit_urb(uvc_urb->urb, GFP_KERNEL);
>> +	if (ret < 0)
>> +		uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n",
>> +			   ret);
>> +}
>> +
>> +static void uvc_video_decode_data(struct uvc_urb *uvc_urb,
>>  		struct uvc_buffer *buf, const u8 *data, int len)
>>  {
>> -	unsigned int maxlen, nbytes;
>> -	void *mem;
>> +	unsigned int active_op = uvc_urb->async_operations;
>> +	struct uvc_copy_op *op = &uvc_urb->copy_operations[active_op];
>> +	unsigned int maxlen;
>>
>>  	if (len <= 0)
>>  		return;
>>
>> -	/* Copy the video data to the buffer. */
>>  	maxlen = buf->length - buf->bytesused;
>> -	mem = buf->mem + buf->bytesused;
>> -	nbytes = min((unsigned int)len, maxlen);
>> -	memcpy(mem, data, nbytes);
>> -	buf->bytesused += nbytes;
>> +
>> +	/* Take a buffer reference for async work. */
>> +	kref_get(&buf->ref);
>> +
>> +	op->buf = buf;
>> +	op->src = data;
>> +	op->dst = buf->mem + buf->bytesused;
>> +	op->len = min_t(unsigned int, len, maxlen);
>> +
>> +	buf->bytesused += op->len;
>>
>>  	/* Complete the current frame if the buffer size was exceeded. */
>>  	if (len > maxlen) {
>> @@ -1116,6 +1149,8 @@ static void uvc_video_decode_data(struct uvc_streaming
>> *stream, buf->error = 1;
>>  		buf->state = UVC_BUF_STATE_READY;
>>  	}
>> +
>> +	uvc_urb->async_operations++;
>>  }
>>
>>  static void uvc_video_decode_end(struct uvc_streaming *stream,
>> @@ -1324,7 +1359,7 @@ static void uvc_video_decode_isoc(struct uvc_urb
>> *uvc_urb, uvc_video_decode_meta(stream, meta_buf, mem, ret);
>>
>>  		/* Decode the payload data. */
>> -		uvc_video_decode_data(stream, buf, mem + ret,
>> +		uvc_video_decode_data(uvc_urb, buf, mem + ret,
>>  			urb->iso_frame_desc[i].actual_length - ret);
>>
>>  		/* Process the header again. */
>> @@ -1384,9 +1419,9 @@ static void uvc_video_decode_bulk(struct uvc_urb
>> *uvc_urb, * sure buf is never dereferenced if NULL.
>>  	 */
>>
>> -	/* Process video data. */
>> +	/* Prepare video data for processing. */
>>  	if (!stream->bulk.skip_payload && buf != NULL)
>> -		uvc_video_decode_data(stream, buf, mem, len);
>> +		uvc_video_decode_data(uvc_urb, buf, mem, len);
>>
>>  	/* Detect the payload end by a URB smaller than the maximum size (or
>>  	 * a payload size equal to the maximum) and process the header again.
>> @@ -1472,7 +1507,7 @@ static void uvc_video_complete(struct urb *urb)
>>  		uvc_printk(KERN_WARNING, "Non-zero status (%d) in video "
>>  			"completion handler.\n", urb->status);
>>  		/* fall through */
>> -	case -ENOENT:		/* usb_kill_urb() called. */
>> +	case -ENOENT:		/* usb_poison_urb() called. */
>>  		if (stream->frozen)
>>  			return;
>>  		/* fall through */
>> @@ -1494,12 +1529,26 @@ static void uvc_video_complete(struct urb *urb)
>>  		spin_unlock_irqrestore(&qmeta->irqlock, flags);
>>  	}
>>
>> +	/* Re-initialise the URB async work. */
>> +	uvc_urb->async_operations = 0;
>> +
>> +	/*
>> +	 * Process the URB headers, and optionally queue expensive memcpy tasks
>> +	 * to be deferred to a work queue.
>> +	 */
>>  	stream->decode(uvc_urb, buf, buf_meta);
>>
>> -	if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
>> -		uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n",
>> -			ret);
>> +	/* If no async work is needed, resubmit the URB immediately. */
>> +	if (!uvc_urb->async_operations) {
>> +		ret = usb_submit_urb(uvc_urb->urb, GFP_ATOMIC);
>> +		if (ret < 0)
>> +			uvc_printk(KERN_ERR,
>> +				   "Failed to resubmit video URB (%d).\n",
>> +				   ret);
>> +		return;
>>  	}
>> +
>> +	queue_work(stream->async_wq, &uvc_urb->work);
>>  }
>>
>>  /*
>> @@ -1594,20 +1643,22 @@ static int uvc_alloc_urb_buffers(struct
>> uvc_streaming *stream, */
>>  static void uvc_uninit_video(struct uvc_streaming *stream, int
>> free_buffers) {
>> -	struct urb *urb;
>> -	unsigned int i;
>> +	struct uvc_urb *uvc_urb;
>>
>>  	uvc_video_stats_stop(stream);
>>
>> -	for (i = 0; i < UVC_URBS; ++i) {
>> -		struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
>> +	/*
>> +	 * We must poison the URBs rather than kill them to ensure that even
>> +	 * after the completion handler returns, any asynchronous workqueues
>> +	 * will be prevented from resubmitting the URBs.
>> +	 */
>> +	for_each_uvc_urb(uvc_urb, stream)
>> +		usb_poison_urb(uvc_urb->urb);
>>
>> -		urb = uvc_urb->urb;
>> -		if (urb == NULL)
>> -			continue;
>> +	flush_workqueue(stream->async_wq);
>>
>> -		usb_kill_urb(urb);
>> -		usb_free_urb(urb);
>> +	for_each_uvc_urb(uvc_urb, stream) {
>> +		usb_free_urb(uvc_urb->urb);
>>  		uvc_urb->urb = NULL;
>>  	}
>>
>> @@ -1932,6 +1983,7 @@ int uvc_video_init(struct uvc_streaming *stream)
>>  	struct uvc_streaming_control *probe = &stream->ctrl;
>>  	struct uvc_format *format = NULL;
>>  	struct uvc_frame *frame = NULL;
>> +	struct uvc_urb *uvc_urb;
>>  	unsigned int i;
>>  	int ret;
>>
>> @@ -2017,6 +2069,16 @@ int uvc_video_init(struct uvc_streaming *stream)
>>  		}
>>  	}
>>
>> +	/* Allocate a stream specific work queue for asynchronous tasks. */
>> +	stream->async_wq = alloc_workqueue("uvcvideo", WQ_UNBOUND | WQ_HIGHPRI,
>> +					   0);
>> +	if (!stream->async_wq)
>> +		return -ENOMEM;
>> +
>> +	/* Prepare asynchronous work items. */
>> +	for_each_uvc_urb(uvc_urb, stream)
>> +		INIT_WORK(&uvc_urb->work, uvc_video_copy_data_work);
>> +
>>  	return 0;
>>  }
>>
>> diff --git a/drivers/media/usb/uvc/uvcvideo.h
>> b/drivers/media/usb/uvc/uvcvideo.h index 1bc17da7f3d4..0953e2e59a79 100644
>> --- a/drivers/media/usb/uvc/uvcvideo.h
>> +++ b/drivers/media/usb/uvc/uvcvideo.h
>> @@ -491,12 +491,30 @@ struct uvc_stats_stream {
>>  #define UVC_METATADA_BUF_SIZE 1024
>>
>>  /**
>> + * struct uvc_copy_op: Context structure to schedule asynchronous memcpy
>> + *
>> + * @buf: active buf object for this operation
>> + * @dst: copy destination address
>> + * @src: copy source address
>> + * @len: copy length
>> + */
>> +struct uvc_copy_op {
>> +	struct uvc_buffer *buf;
>> +	void *dst;
>> +	const __u8 *src;
>> +	size_t len;
>> +};
>> +
>> +/**
>>   * struct uvc_urb - URB context management structure
>>   *
>>   * @urb: the URB described by this context structure
>>   * @stream: UVC streaming context
>>   * @buffer: memory storage for the URB
>>   * @dma: DMA coherent addressing for the urb_buffer
>> + * @async_operations: counter to indicate the number of copy operations
>> + * @copy_operations: work descriptors for asynchronous copy operations
>> + * @work: work queue entry for asynchronous decode
>>   */
>>  struct uvc_urb {
>>  	struct urb *urb;
>> @@ -504,6 +522,10 @@ struct uvc_urb {
>>
>>  	char *buffer;
>>  	dma_addr_t dma;
>> +
>> +	unsigned int async_operations;
>> +	struct uvc_copy_op copy_operations[UVC_MAX_PACKETS];
>> +	struct work_struct work;
>>  };
>>
>>  struct uvc_streaming {
>> @@ -536,6 +558,7 @@ struct uvc_streaming {
>>  	/* Buffers queue. */
>>  	unsigned int frozen : 1;
>>  	struct uvc_video_queue queue;
>> +	struct workqueue_struct *async_wq;
>>  	void (*decode)(struct uvc_urb *uvc_urb, struct uvc_buffer *buf,
>>  		       struct uvc_buffer *meta_buf);
>>
>> @@ -589,6 +612,11 @@ struct uvc_streaming {
>>  	} clock;
>>  };
>>
>> +#define for_each_uvc_urb(uvc_urb, uvc_streaming) \
>> +	for ((uvc_urb) = &(uvc_streaming)->uvc_urb[0]; \
>> +	     (uvc_urb) < &(uvc_streaming)->uvc_urb[UVC_URBS]; \
>> +	     ++(uvc_urb))
>> +
>>  struct uvc_device_info {
>>  	u32	quirks;
>>  	u32	meta_format;
> 
> 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 9/9] media: uvcvideo: Utilise for_each_uvc_urb iterator
  2018-11-06 23:21   ` Laurent Pinchart
@ 2018-11-07 13:50     ` Kieran Bingham
  0 siblings, 0 replies; 27+ messages in thread
From: Kieran Bingham @ 2018-11-07 13:50 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: linux-media, Guennadi Liakhovetski, Olivier BRAUN, Troy Kisky,
	Randy Dunlap, Philipp Zabel, Ezequiel Garcia, Kieran Bingham

Hi Laurent,

On 06/11/2018 23:21, Laurent Pinchart wrote:
> Hi Kieran,
> 
> Thank you for the patch.
> 
> On Tuesday, 6 November 2018 23:27:20 EET Kieran Bingham wrote:
>> From: Kieran Bingham <kieran.bingham@ideasonboard.com>
>>
>> A new iterator is available for processing UVC URB structures. This
>> simplifies the processing of the internal stream data.
>>
>> Convert the manual loop iterators to the new helper, adding an index
>> helper to keep the existing debug print.
>>
>> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
>>
>> ---
>> This patch converts to using the iterator which for most hunks makes
>> sense. The only one with uncertainty is in uvc_alloc_urb_buffers() where
>> the loop index is used to determine if all the buffers were successfully
>> allocated.
>>
>> As the loop index is not incremented by the loops, we can obtain the
>> buffer index - but then we are offset and out-by-one.
>>
>> Adjusting this in the code is fine - but at that point it feels like
>> it's not adding much value. I've left that hunk in for this patch - but
>> that part could be reverted if desired - unless anyone has a better
>> rework of the buffer check?
>>
>>  drivers/media/usb/uvc/uvc_video.c | 51 ++++++++++++++++----------------
>>  drivers/media/usb/uvc/uvcvideo.h  |  3 ++-
>>  2 files changed, 29 insertions(+), 25 deletions(-)
>>
>> diff --git a/drivers/media/usb/uvc/uvc_video.c
>> b/drivers/media/usb/uvc/uvc_video.c index 020022e6ade4..f6e5db7ea50e 100644
>> --- a/drivers/media/usb/uvc/uvc_video.c
>> +++ b/drivers/media/usb/uvc/uvc_video.c
>> @@ -1556,20 +1556,19 @@ static void uvc_video_complete(struct urb *urb)
>>   */
>>  static void uvc_free_urb_buffers(struct uvc_streaming *stream)
>>  {
>> -	unsigned int i;
>> +	struct uvc_urb *uvc_urb;
>>
>> -	for (i = 0; i < UVC_URBS; ++i) {
>> -		struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
>> +	for_each_uvc_urb(uvc_urb, stream) {
>> +		if (!uvc_urb->buffer)
>> +			continue;
>>
>> -		if (uvc_urb->buffer) {
>>  #ifndef CONFIG_DMA_NONCOHERENT
>> -			usb_free_coherent(stream->dev->udev, stream->urb_size,
>> -					uvc_urb->buffer, uvc_urb->dma);
>> +		usb_free_coherent(stream->dev->udev, stream->urb_size,
>> +				  uvc_urb->buffer, uvc_urb->dma);
>>  #else
>> -			kfree(uvc_urb->buffer);
>> +		kfree(uvc_urb->buffer);
>>  #endif
>> -			uvc_urb->buffer = NULL;
>> -		}
>> +		uvc_urb->buffer = NULL;
>>  	}
>>
>>  	stream->urb_size = 0;
>> @@ -1589,8 +1588,9 @@ static void uvc_free_urb_buffers(struct uvc_streaming
>> *stream) static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,
>>  	unsigned int size, unsigned int psize, gfp_t gfp_flags)
>>  {
>> +	struct uvc_urb *uvc_urb;
>>  	unsigned int npackets;
>> -	unsigned int i;
>> +	unsigned int i = 0;
>>
>>  	/* Buffers are already allocated, bail out. */
>>  	if (stream->urb_size)
>> @@ -1605,8 +1605,12 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming
>> *stream,
>>
>>  	/* Retry allocations until one succeed. */
>>  	for (; npackets > 1; npackets /= 2) {
>> -		for (i = 0; i < UVC_URBS; ++i) {
>> -			struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
>> +		for_each_uvc_urb(uvc_urb, stream) {
>> +			/*
>> +			 * Track how many URBs we allocate, adding one to the
>> +			 * index to account for our zero index.
>> +			 */
>> +			i = uvc_urb_index(uvc_urb) + 1;
> 
> That's a bit ugly indeed, I think we could keep the existing loop;

I do agree, as stated I left it in for 'completeness' of the patch. But
I don't think it adds value here.

Will remove.


>>  			stream->urb_size = psize * npackets;
>>  #ifndef CONFIG_DMA_NONCOHERENT
>> @@ -1700,7 +1704,8 @@ static int uvc_init_video_isoc(struct uvc_streaming
>> *stream, struct usb_host_endpoint *ep, gfp_t gfp_flags)
>>  {
>>  	struct urb *urb;
>> -	unsigned int npackets, i, j;
>> +	struct uvc_urb *uvc_urb;
>> +	unsigned int npackets, j;
> 
> j without i seems weird, could you rename it ?


Sure. Done

> 
>>  	u16 psize;
>>  	u32 size;
>>
>> @@ -1713,9 +1718,7 @@ static int uvc_init_video_isoc(struct uvc_streaming
>> *stream,
>>
>>  	size = npackets * psize;
>>
>> -	for (i = 0; i < UVC_URBS; ++i) {
>> -		struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
>> -
>> +	for_each_uvc_urb(uvc_urb, stream) {
>>  		urb = usb_alloc_urb(npackets, gfp_flags);
>>  		if (urb == NULL) {
>>  			uvc_video_stop(stream, 1);
>> @@ -1757,7 +1760,8 @@ static int uvc_init_video_bulk(struct uvc_streaming
>> *stream, struct usb_host_endpoint *ep, gfp_t gfp_flags)
>>  {
>>  	struct urb *urb;
>> -	unsigned int npackets, pipe, i;
>> +	struct uvc_urb *uvc_urb;
>> +	unsigned int npackets, pipe;
>>  	u16 psize;
>>  	u32 size;
>>
>> @@ -1781,9 +1785,7 @@ static int uvc_init_video_bulk(struct uvc_streaming
>> *stream, if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
>>  		size = 0;
>>
>> -	for (i = 0; i < UVC_URBS; ++i) {
>> -		struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
>> -
>> +	for_each_uvc_urb(uvc_urb, stream) {
>>  		urb = usb_alloc_urb(0, gfp_flags);
>>  		if (urb == NULL) {
>>  			uvc_video_stop(stream, 1);
>> @@ -1810,6 +1812,7 @@ static int uvc_video_start(struct uvc_streaming
>> *stream, gfp_t gfp_flags) {
>>  	struct usb_interface *intf = stream->intf;
>>  	struct usb_host_endpoint *ep;
>> +	struct uvc_urb *uvc_urb;
>>  	unsigned int i;
>>  	int ret;
>>
>> @@ -1887,13 +1890,11 @@ static int uvc_video_start(struct uvc_streaming
>> *stream, gfp_t gfp_flags) return ret;
>>
>>  	/* Submit the URBs. */
>> -	for (i = 0; i < UVC_URBS; ++i) {
>> -		struct uvc_urb *uvc_urb = &stream->uvc_urb[i];
>> -
>> +	for_each_uvc_urb(uvc_urb, stream) {
>>  		ret = usb_submit_urb(uvc_urb->urb, gfp_flags);
>>  		if (ret < 0) {
>> -			uvc_printk(KERN_ERR, "Failed to submit URB %u "
>> -					"(%d).\n", i, ret);
>> +			uvc_printk(KERN_ERR, "Failed to submit URB %u (%d).\n",
>> +				   uvc_urb_index(uvc_urb), ret);
>>  			uvc_video_stop(stream, 1);
>>  			return ret;
>>  		}
>> diff --git a/drivers/media/usb/uvc/uvcvideo.h
>> b/drivers/media/usb/uvc/uvcvideo.h index c0a120496a1f..6a0f1b59356c 100644
>> --- a/drivers/media/usb/uvc/uvcvideo.h
>> +++ b/drivers/media/usb/uvc/uvcvideo.h
>> @@ -617,6 +617,9 @@ struct uvc_streaming {
>>  	     (uvc_urb) < &(uvc_streaming)->uvc_urb[UVC_URBS]; \
>>  	     ++(uvc_urb))
>>
>> +#define uvc_urb_index(uvc_urb) \
>> +	(unsigned int)((uvc_urb) - (&(uvc_urb)->stream->uvc_urb[0]))
>> +
>>  struct uvc_device_info {
>>  	u32	quirks;
>>  	u32	meta_format;
> 


-- 
Regards
--
Kieran

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 8/9] media: uvcvideo: Rename uvc_{un,}init_video()
  2018-11-06 23:13   ` Laurent Pinchart
@ 2018-11-07 14:30     ` Kieran Bingham
  2018-11-07 20:25       ` Laurent Pinchart
  0 siblings, 1 reply; 27+ messages in thread
From: Kieran Bingham @ 2018-11-07 14:30 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: linux-media, Guennadi Liakhovetski, Olivier BRAUN, Troy Kisky,
	Randy Dunlap, Philipp Zabel, Ezequiel Garcia, Kieran Bingham

Hi Laurent,

On 06/11/2018 23:13, Laurent Pinchart wrote:
> Hi Kieran,
> 
> Thank you for the patch.
> 
> On Tuesday, 6 November 2018 23:27:19 EET Kieran Bingham wrote:
>> From: Kieran Bingham <kieran.bingham@ideasonboard.com>
>>
>> We have both uvc_init_video() and uvc_video_init() calls which can be
>> quite confusing to determine the process for each. Now that video
>> uvc_video_enable() has been renamed to uvc_video_start_streaming(),
>> adapt these calls to suit the new flow.
>>
>> Rename uvc_init_video() to uvc_video_start() and uvc_uninit_video() to
>> uvc_video_stop().
> 
> I agree that these functions are badly named and should be renamed. We are 
> however entering the nitpicking territory :-) The two functions do more that 
> starting and stopping, they also allocate and free URBs and the associated 
> buffers. It could also be argued that they don't actually start and stop 
> anything, as beyond URB management, they just queue the URBs initially and 
> kill them. I thus wonder if we could come up with better names.

Well the act of killing (poisoning now) the URBs will certainly stop the
stream, but I guess submitting the URBs isn't necessarily the key act to
starting the stream.

I believe that needs the interface to be set correctly, and the buffers
to be available?

Although - I've just double-checked uvc_{video_start,init_video}() and
that is indeed what it does?

 - start stats
 - Initialise endpoints
   - Perform allocations
 - Submit URBs

Am I missing something? Is there another step that is pivotal to
starting the USB packet/urb stream flow after this point ?


Is it not true that the USB stack will start processing data at
submitting URB completion callbacks after the end of uvc_video_start();
and will no longer process data at the end of uvc_video_stop() (and thus
no more completion callbacks)?

 (That's a real question to verify my interpretation)

To me - these functions feel like the real 'start' and 'stop' components
of the data stream - hence my choice in naming.


Is your concern that you would like the functions to be more descriptive
over their other actions such as? :

  uvc_video_initialise_start()
  uvc_video_allocate_init_start()

Or something else? (I don't think those two are good names though)

--
Kieran

>> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
>> ---
>>  drivers/media/usb/uvc/uvc_video.c | 18 +++++++++---------
>>  1 file changed, 9 insertions(+), 9 deletions(-)
>>
>> diff --git a/drivers/media/usb/uvc/uvc_video.c
>> b/drivers/media/usb/uvc/uvc_video.c index 0d35e933856a..020022e6ade4 100644
>> --- a/drivers/media/usb/uvc/uvc_video.c
>> +++ b/drivers/media/usb/uvc/uvc_video.c
>> @@ -1641,7 +1641,7 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming
>> *stream, /*
>>   * Uninitialize isochronous/bulk URBs and free transfer buffers.
>>   */
>> -static void uvc_uninit_video(struct uvc_streaming *stream, int
>> free_buffers) +static void uvc_video_stop(struct uvc_streaming *stream, int
>> free_buffers) {
>>  	struct uvc_urb *uvc_urb;
>>
>> @@ -1718,7 +1718,7 @@ static int uvc_init_video_isoc(struct uvc_streaming
>> *stream,
>>
>>  		urb = usb_alloc_urb(npackets, gfp_flags);
>>  		if (urb == NULL) {
>> -			uvc_uninit_video(stream, 1);
>> +			uvc_video_stop(stream, 1);
>>  			return -ENOMEM;
>>  		}
>>
>> @@ -1786,7 +1786,7 @@ static int uvc_init_video_bulk(struct uvc_streaming
>> *stream,
>>
>>  		urb = usb_alloc_urb(0, gfp_flags);
>>  		if (urb == NULL) {
>> -			uvc_uninit_video(stream, 1);
>> +			uvc_video_stop(stream, 1);
>>  			return -ENOMEM;
>>  		}
>>
>> @@ -1806,7 +1806,7 @@ static int uvc_init_video_bulk(struct uvc_streaming
>> *stream, /*
>>   * Initialize isochronous/bulk URBs and allocate transfer buffers.
>>   */
>> -static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
>> +static int uvc_video_start(struct uvc_streaming *stream, gfp_t gfp_flags)
>>  {
>>  	struct usb_interface *intf = stream->intf;
>>  	struct usb_host_endpoint *ep;
>> @@ -1894,7 +1894,7 @@ static int uvc_init_video(struct uvc_streaming
>> *stream, gfp_t gfp_flags) if (ret < 0) {
>>  			uvc_printk(KERN_ERR, "Failed to submit URB %u "
>>  					"(%d).\n", i, ret);
>> -			uvc_uninit_video(stream, 1);
>> +			uvc_video_stop(stream, 1);
>>  			return ret;
>>  		}
>>  	}
>> @@ -1925,7 +1925,7 @@ int uvc_video_suspend(struct uvc_streaming *stream)
>>  		return 0;
>>
>>  	stream->frozen = 1;
>> -	uvc_uninit_video(stream, 0);
>> +	uvc_video_stop(stream, 0);
>>  	usb_set_interface(stream->dev->udev, stream->intfnum, 0);
>>  	return 0;
>>  }
>> @@ -1961,7 +1961,7 @@ int uvc_video_resume(struct uvc_streaming *stream, int
>> reset) if (ret < 0)
>>  		return ret;
>>
>> -	return uvc_init_video(stream, GFP_NOIO);
>> +	return uvc_video_start(stream, GFP_NOIO);
>>  }
>>
>>  /* ------------------------------------------------------------------------
>> @@ -2095,7 +2095,7 @@ int uvc_video_start_streaming(struct uvc_streaming
>> *stream) if (ret < 0)
>>  		goto error_commit;
>>
>> -	ret = uvc_init_video(stream, GFP_KERNEL);
>> +	ret = uvc_video_start(stream, GFP_KERNEL);
>>  	if (ret < 0)
>>  		goto error_video;
>>
>> @@ -2111,7 +2111,7 @@ int uvc_video_start_streaming(struct uvc_streaming
>> *stream)
>>
>>  int uvc_video_stop_streaming(struct uvc_streaming *stream)
>>  {
>> -	uvc_uninit_video(stream, 1);
>> +	uvc_video_stop(stream, 1);
>>  	if (stream->intf->num_altsetting > 1) {
>>  		usb_set_interface(stream->dev->udev,
>>  				  stream->intfnum, 0);
> 
> 


-- 
Regards
--
Kieran

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 8/9] media: uvcvideo: Rename uvc_{un,}init_video()
  2018-11-07 14:30     ` Kieran Bingham
@ 2018-11-07 20:25       ` Laurent Pinchart
  2018-11-09 15:41         ` Philipp Zabel
  0 siblings, 1 reply; 27+ messages in thread
From: Laurent Pinchart @ 2018-11-07 20:25 UTC (permalink / raw)
  To: kieran.bingham
  Cc: linux-media, Guennadi Liakhovetski, Olivier BRAUN, Troy Kisky,
	Randy Dunlap, Philipp Zabel, Ezequiel Garcia

Hi Kieran,

On Wednesday, 7 November 2018 16:30:46 EET Kieran Bingham wrote:
> On 06/11/2018 23:13, Laurent Pinchart wrote:
> > On Tuesday, 6 November 2018 23:27:19 EET Kieran Bingham wrote:
> >> From: Kieran Bingham <kieran.bingham@ideasonboard.com>
> >> 
> >> We have both uvc_init_video() and uvc_video_init() calls which can be
> >> quite confusing to determine the process for each. Now that video
> >> uvc_video_enable() has been renamed to uvc_video_start_streaming(),
> >> adapt these calls to suit the new flow.
> >> 
> >> Rename uvc_init_video() to uvc_video_start() and uvc_uninit_video() to
> >> uvc_video_stop().
> > 
> > I agree that these functions are badly named and should be renamed. We are
> > however entering the nitpicking territory :-) The two functions do more
> > that starting and stopping, they also allocate and free URBs and the
> > associated buffers. It could also be argued that they don't actually
> > start and stop anything, as beyond URB management, they just queue the
> > URBs initially and kill them. I thus wonder if we could come up with
> > better names.
> 
> Well the act of killing (poisoning now) the URBs will certainly stop the
> stream, but I guess submitting the URBs isn't necessarily the key act to
> starting the stream.
> 
> I believe that needs the interface to be set correctly, and the buffers
> to be available?
> 
> Although - I've just double-checked uvc_{video_start,init_video}() and
> that is indeed what it does?
> 
>  - start stats
>  - Initialise endpoints
>    - Perform allocations
>  - Submit URBs
> 
> Am I missing something? Is there another step that is pivotal to
> starting the USB packet/urb stream flow after this point ?
> 
> 
> Is it not true that the USB stack will start processing data at
> submitting URB completion callbacks after the end of uvc_video_start();
> and will no longer process data at the end of uvc_video_stop() (and thus
> no more completion callbacks)?
> 
>  (That's a real question to verify my interpretation)
> 
> To me - these functions feel like the real 'start' and 'stop' components
> of the data stream - hence my choice in naming.

The other part of the start operation is committing the streaming parameters 
(see uvc_video_start_streaming()). For the stop operation it's issuing a 
SET_INTERFACE or CLEAR_FEATURE(HALT) request (see uvc_video_stop_streaming()).

> Is your concern that you would like the functions to be more descriptive
> over their other actions such as? :
> 
>   uvc_video_initialise_start()
>   uvc_video_allocate_init_start()
> 
> Or something else? (I don't think those two are good names though)

Probably something else :-) A possibly equally bad proposal would be 
uvc_video_start_transfer() and uvc_video_stop_transfer().

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 0/9] Asynchronous UVC
  2018-11-06 23:31 ` [PATCH v5 0/9] Asynchronous UVC Laurent Pinchart
@ 2018-11-08 17:01   ` Laurent Pinchart
  2018-11-09 13:25     ` Kieran Bingham
  0 siblings, 1 reply; 27+ messages in thread
From: Laurent Pinchart @ 2018-11-08 17:01 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Kieran Bingham, linux-media, Guennadi Liakhovetski,
	Olivier BRAUN, Troy Kisky, Randy Dunlap, Philipp Zabel,
	Ezequiel Garcia, Kieran Bingham

Hi Kieran,

On Wednesday, 7 November 2018 01:31:29 EET Laurent Pinchart wrote:
> On Tuesday, 6 November 2018 23:27:11 EET Kieran Bingham wrote:
> > From: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > 
> > The Linux UVC driver has long provided adequate performance capabilities
> > for web-cams and low data rate video devices in Linux while resolutions
> > were low.
> > 
> > Modern USB cameras are now capable of high data rates thanks to USB3 with
> > 1080p, and even 4k capture resolutions supported.
> > 
> > Cameras such as the Stereolabs ZED (bulk transfers) or the Logitech BRIO
> > (isochronous transfers) can generate more data than an embedded ARM core
> > is able to process on a single core, resulting in frame loss.
> > 
> > A large part of this performance impact is from the requirement to
> > ‘memcpy’ frames out from URB packets to destination frames. This
> > unfortunate requirement is due to the UVC protocol allowing a variable
> > length header, and thus it is not possible to provide the target frame
> > buffers directly.
> > 
> > Extra throughput is possible by moving the actual memcpy actions to a work
> > queue, and moving the memcpy out of interrupt context thus allowing work
> > tasks to be scheduled across multiple cores.
> > 
> > This series has been tested on both the ZED and BRIO cameras on arm64
> > platforms, and with thanks to Randy Dunlap, a Dynex 1.3MP Webcam, a Sonix
> > USB2 Camera, and a built in Toshiba Laptop camera, and with thanks to
> > Philipp Zabel for testing on a Lite-On internal Laptop Webcam, Logitech
> > C910 (USB2 isoc), Oculus Sensor (USB3 isoc), and Microsoft HoloLens
> > Sensors (USB3 bulk).
> > 
> > As far as I am aware iSight devices, and devices which use UVC to encode
> > data (output device) have not yet been tested - but should find no ill
> > effect (at least not until they are tested of course :D )
>
> :-D
> 
> I'm not sure whether anyone is still using those devices with Linux. I
> wouldn't be surprised if we realized down the road that they already don't
> work.
> 
> > Tested-by: Randy Dunlap <rdunlap@infradead.org>
> > Tested-by: Philipp Zabel <philipp.zabel@gmail.com>
> > 
> > v2:
> >  - Fix race reported by Guennadi
> > 
> > v3:
> >  - Fix similar race reported by Laurent
> >  - Only queue work if required (encode/isight do not queue work)
> >  - Refactor/Rename variables for clarity
> > 
> > v4:
> >  - (Yet another) Rework of the uninitialise path.
> >  
> >    This time to hopefully clean up the shutdown races for good.
> >    use usb_poison_urb() to halt all URBs, then flush the work queue
> >    before freeing.
> >  
> >  - Rebase to latest linux-media/master
> > 
> > v5:
> >  - Provide lockdep validation
> >  - rename uvc_queue_requeue -> uvc_queue_buffer_requeue()
> >  - Fix comments and periods throughout
> >  - Rebase to media/v4.20-2
> >  - Use GFP_KERNEL allocation in uvc_video_copy_data_work()
> >  - Fix function documentation for uvc_video_copy_data_work()
> >  - Add periods to the end of sentences
> >  - Rename 'decode' variable to 'op' in uvc_video_decode_data()
> >  - Move uvc_urb->async_operations initialisation to before use
> >  - Move async workqueue to match uvc_streaming lifetime instead of
> >  
> >    streamon/streamoff
> >  
> >  - bracket the for_each_uvc_urb() macro
> >  
> >  - New patches added to series:
> >     media: uvcvideo: Split uvc_video_enable into two
> >     media: uvcvideo: Rename uvc_{un,}init_video()
> >     media: uvcvideo: Utilise for_each_uvc_urb iterator
> > 
> > Kieran Bingham (9):
> >   media: uvcvideo: Refactor URB descriptors
> >   media: uvcvideo: Convert decode functions to use new context structure
> >   media: uvcvideo: Protect queue internals with helper
> >   media: uvcvideo: queue: Simplify spin-lock usage
> >   media: uvcvideo: queue: Support asynchronous buffer handling
> >   media: uvcvideo: Move decode processing to process context
> >   media: uvcvideo: Split uvc_video_enable into two
> 
> I've taken the above patches in my tree.

I'm afraid I'll have to drop them :-( If I disconnect the camera while in use,
I get the following oops:

[  237.514625] usb 2-1.4: USB disconnect, device number 5
[  237.516123] uvcvideo: Failed to resubmit video URB (-19).
[  237.549470] BUG: unable to handle kernel paging request at 000000004bec0091
[  237.549476] PGD 0 P4D 0 
[  237.549481] Oops: 0000 [#1] PREEMPT SMP PTI
[  237.549485] CPU: 3 PID: 5332 Comm: luvcview Tainted: G        W  O      4.18.16-gentoo #1
[  237.549487] Hardware name: Dell Inc. Latitude E6420/0K0DNP, BIOS A08 10/18/2011
[  237.549493] RIP: 0010:flush_workqueue_prep_pwqs+0x49/0x120
[  237.549494] Code: 47 48 85 c0 0f 85 e1 00 00 00 c7 45 48 01 00 00 00 48 8b 45 00 4c 8d 78 90 48 39 c5 0f 84 d0 00 00 00 c6 44 24 07 00 4d 63 f4 <4d> 8b 2f 4c 89 ef e8 5c b3 89 00 45 85 e4 78 1d 41 83 7f 14 ff 75 
[  237.549525] RSP: 0018:ffffc9000154fc50 EFLAGS: 00010296
[  237.549527] RAX: 000000004bec0101 RBX: 0000000000000002 RCX: 0000000000000002
[  237.549529] RDX: 0000000000000002 RSI: 0000000000000001 RDI: ffff88030241c600
[  237.549530] RBP: ffff88030241c600 R08: 0000000000000000 R09: 0000000000000000
[  237.549532] R10: ffffc9000154fd30 R11: ffff8802faa09398 R12: 0000000000000001
[  237.549534] R13: ffff88030241c668 R14: 0000000000000001 R15: 000000004bec0091
[  237.549536] FS:  0000000000000000(0000) GS:ffff88032dd80000(0000) knlGS:0000000000000000
[  237.549537] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  237.549539] CR2: 000000004bec0091 CR3: 000000000220a001 CR4: 00000000000606e0
[  237.549540] Call Trace:
[  237.549545]  flush_workqueue+0x161/0x390
[  237.549549]  ? preempt_count_add+0x8d/0x90
[  237.549557]  ? uvc_uninit_video+0x51/0xa0 [uvcvideo]
[  237.549560]  uvc_uninit_video+0x51/0xa0 [uvcvideo]
[  237.549565]  uvc_video_stop_streaming+0xe/0x80 [uvcvideo]
[  237.549568]  uvc_stop_streaming+0x17/0x40 [uvcvideo]
[  237.549573]  __vb2_queue_cancel+0x25/0x1c0 [videobuf2_common]
[  237.549576]  vb2_core_queue_release+0x19/0x40 [videobuf2_common]
[  237.549580]  uvc_queue_release+0x1c/0x30 [uvcvideo]
[  237.549584]  uvc_v4l2_release+0x9d/0xd0 [uvcvideo]
[  237.549594]  v4l2_release+0x2d/0x80 [videodev]
[  237.549598]  __fput+0x95/0x1d0
[  237.549602]  task_work_run+0x84/0xa0
[  237.549605]  do_exit+0x2a3/0xad0
[  237.549608]  do_group_exit+0x2e/0xa0
[  237.549610]  __x64_sys_exit_group+0xf/0x10
[  237.549613]  do_syscall_64+0x3d/0xf0
[  237.549617]  entry_SYSCALL_64_after_hwframe+0x44/0xa9
[  237.549619] RIP: 0033:0x7f2b0b6097a6
[  237.549620] Code: Bad RIP value.
[  237.549625] RSP: 002b:00007ffd2ddf5088 EFLAGS: 00000246 ORIG_RAX: 00000000000000e7
[  237.549627] RAX: ffffffffffffffda RBX: 00007f2b0b8f8740 RCX: 00007f2b0b6097a6
[  237.549629] RDX: 0000000000000000 RSI: 000000000000003c RDI: 0000000000000000
[  237.549630] RBP: 0000000000000000 R08: 00000000000000e7 R09: ffffffffffffff80
[  237.549632] R10: 00007f2b05e37168 R11: 0000000000000246 R12: 00007f2b0b8f8740
[  237.549634] R13: 0000000000000001 R14: 00007f2b0b9016e8 R15: 0000000000000000
[  237.549636] Modules linked in: uvcvideo(O) videobuf2_vmalloc(O) videobuf2_memops(O) videobuf2_v4l2(O) videobuf2_common(O) v4l2_common(O) videodev(O) media(O) xt_conntrack ip6table_filter binfmt_misc dell_laptop snd_hda_codec_hdmi dell_smbios dcdbas coretemp hwmon kvm_intel kvm irqbypass snd_hda_codec_idt snd_hda_codec_generic iwldvm snd_hda_intel snd_hda_codec snd_hwdep snd_hda_core snd_pcm snd_timer iwlwifi snd [last unloaded: media]
[  237.549659] CR2: 000000004bec0091
[  237.549662] ---[ end trace 574358b4f247dc6f ]---
[  237.549664] RIP: 0010:flush_workqueue_prep_pwqs+0x49/0x120
[  237.549665] Code: 47 48 85 c0 0f 85 e1 00 00 00 c7 45 48 01 00 00 00 48 8b 45 00 4c 8d 78 90 48 39 c5 0f 84 d0 00 00 00 c6 44 24 07 00 4d 63 f4 <4d> 8b 2f 4c 89 ef e8 5c b3 89 00 45 85 e4 78 1d 41 83 7f 14 ff 75 
[  237.549696] RSP: 0018:ffffc9000154fc50 EFLAGS: 00010296
[  237.549699] RAX: 000000004bec0101 RBX: 0000000000000002 RCX: 0000000000000002
[  237.549700] RDX: 0000000000000002 RSI: 0000000000000001 RDI: ffff88030241c600
[  237.549702] RBP: ffff88030241c600 R08: 0000000000000000 R09: 0000000000000000
[  237.549704] R10: ffffc9000154fd30 R11: ffff8802faa09398 R12: 0000000000000001
[  237.549706] R13: ffff88030241c668 R14: 0000000000000001 R15: 000000004bec0091
[  237.549708] FS:  0000000000000000(0000) GS:ffff88032dd80000(0000) knlGS:0000000000000000
[  237.549710] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  237.549712] CR2: 00007f2b0b60977c CR3: 000000000220a001 CR4: 00000000000606e0
[  237.549714] Fixing recursive fault but reboot is needed!

> >   media: uvcvideo: Rename uvc_{un,}init_video()
> >   media: uvcvideo: Utilise for_each_uvc_urb iterator
> 
> And I've sent review comments for these two.
> 
> >  drivers/media/usb/uvc/uvc_driver.c |   2 +-
> >  drivers/media/usb/uvc/uvc_isight.c |   6 +-
> >  drivers/media/usb/uvc/uvc_queue.c  | 110 +++++++++---
> >  drivers/media/usb/uvc/uvc_video.c  | 282 +++++++++++++++++++-----------
> >  drivers/media/usb/uvc/uvcvideo.h   |  65 ++++++-
> >  5 files changed, 331 insertions(+), 134 deletions(-)
> > 
> > base-commit: dafb7f9aef2fd44991ff1691721ff765a23be27b

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 0/9] Asynchronous UVC
  2018-11-08 17:01   ` Laurent Pinchart
@ 2018-11-09 13:25     ` Kieran Bingham
  0 siblings, 0 replies; 27+ messages in thread
From: Kieran Bingham @ 2018-11-09 13:25 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Kieran Bingham, linux-media, Guennadi Liakhovetski,
	Olivier BRAUN, Troy Kisky, Randy Dunlap, Philipp Zabel,
	Ezequiel Garcia

Hi Laurent,

On 08/11/2018 17:01, Laurent Pinchart wrote:
> Hi Kieran,
> 
> On Wednesday, 7 November 2018 01:31:29 EET Laurent Pinchart wrote:
>> On Tuesday, 6 November 2018 23:27:11 EET Kieran Bingham wrote:
>>> From: Kieran Bingham <kieran.bingham@ideasonboard.com>
>>>
>>> The Linux UVC driver has long provided adequate performance capabilities
>>> for web-cams and low data rate video devices in Linux while resolutions
>>> were low.
>>>
>>> Modern USB cameras are now capable of high data rates thanks to USB3 with
>>> 1080p, and even 4k capture resolutions supported.
>>>
>>> Cameras such as the Stereolabs ZED (bulk transfers) or the Logitech BRIO
>>> (isochronous transfers) can generate more data than an embedded ARM core
>>> is able to process on a single core, resulting in frame loss.
>>>
>>> A large part of this performance impact is from the requirement to
>>> ‘memcpy’ frames out from URB packets to destination frames. This
>>> unfortunate requirement is due to the UVC protocol allowing a variable
>>> length header, and thus it is not possible to provide the target frame
>>> buffers directly.
>>>
>>> Extra throughput is possible by moving the actual memcpy actions to a work
>>> queue, and moving the memcpy out of interrupt context thus allowing work
>>> tasks to be scheduled across multiple cores.
>>>
>>> This series has been tested on both the ZED and BRIO cameras on arm64
>>> platforms, and with thanks to Randy Dunlap, a Dynex 1.3MP Webcam, a Sonix
>>> USB2 Camera, and a built in Toshiba Laptop camera, and with thanks to
>>> Philipp Zabel for testing on a Lite-On internal Laptop Webcam, Logitech
>>> C910 (USB2 isoc), Oculus Sensor (USB3 isoc), and Microsoft HoloLens
>>> Sensors (USB3 bulk).
>>>
>>> As far as I am aware iSight devices, and devices which use UVC to encode
>>> data (output device) have not yet been tested - but should find no ill
>>> effect (at least not until they are tested of course :D )
>>
>> :-D
>>
>> I'm not sure whether anyone is still using those devices with Linux. I
>> wouldn't be surprised if we realized down the road that they already don't
>> work.
>>
>>> Tested-by: Randy Dunlap <rdunlap@infradead.org>
>>> Tested-by: Philipp Zabel <philipp.zabel@gmail.com>
>>>
>>> v2:
>>>  - Fix race reported by Guennadi
>>>
>>> v3:
>>>  - Fix similar race reported by Laurent
>>>  - Only queue work if required (encode/isight do not queue work)
>>>  - Refactor/Rename variables for clarity
>>>
>>> v4:
>>>  - (Yet another) Rework of the uninitialise path.
>>>  
>>>    This time to hopefully clean up the shutdown races for good.
>>>    use usb_poison_urb() to halt all URBs, then flush the work queue
>>>    before freeing.
>>>  
>>>  - Rebase to latest linux-media/master
>>>
>>> v5:
>>>  - Provide lockdep validation
>>>  - rename uvc_queue_requeue -> uvc_queue_buffer_requeue()
>>>  - Fix comments and periods throughout
>>>  - Rebase to media/v4.20-2
>>>  - Use GFP_KERNEL allocation in uvc_video_copy_data_work()
>>>  - Fix function documentation for uvc_video_copy_data_work()
>>>  - Add periods to the end of sentences
>>>  - Rename 'decode' variable to 'op' in uvc_video_decode_data()
>>>  - Move uvc_urb->async_operations initialisation to before use
>>>  - Move async workqueue to match uvc_streaming lifetime instead of
>>>  
>>>    streamon/streamoff
>>>  
>>>  - bracket the for_each_uvc_urb() macro
>>>  
>>>  - New patches added to series:
>>>     media: uvcvideo: Split uvc_video_enable into two
>>>     media: uvcvideo: Rename uvc_{un,}init_video()
>>>     media: uvcvideo: Utilise for_each_uvc_urb iterator
>>>
>>> Kieran Bingham (9):
>>>   media: uvcvideo: Refactor URB descriptors
>>>   media: uvcvideo: Convert decode functions to use new context structure
>>>   media: uvcvideo: Protect queue internals with helper
>>>   media: uvcvideo: queue: Simplify spin-lock usage
>>>   media: uvcvideo: queue: Support asynchronous buffer handling
>>>   media: uvcvideo: Move decode processing to process context
>>>   media: uvcvideo: Split uvc_video_enable into two
>>
>> I've taken the above patches in my tree.
> 
> I'm afraid I'll have to drop them :-( If I disconnect the camera while in use,
> I get the following oops:

Argh - that's what we get for that 'last minute' change of the lifetime
of the workqueue.

Ok - I'll see what we can do instead.
--
Kieran



> 
> [  237.514625] usb 2-1.4: USB disconnect, device number 5
> [  237.516123] uvcvideo: Failed to resubmit video URB (-19).
> [  237.549470] BUG: unable to handle kernel paging request at 000000004bec0091
> [  237.549476] PGD 0 P4D 0 
> [  237.549481] Oops: 0000 [#1] PREEMPT SMP PTI
> [  237.549485] CPU: 3 PID: 5332 Comm: luvcview Tainted: G        W  O      4.18.16-gentoo #1
> [  237.549487] Hardware name: Dell Inc. Latitude E6420/0K0DNP, BIOS A08 10/18/2011
> [  237.549493] RIP: 0010:flush_workqueue_prep_pwqs+0x49/0x120
> [  237.549494] Code: 47 48 85 c0 0f 85 e1 00 00 00 c7 45 48 01 00 00 00 48 8b 45 00 4c 8d 78 90 48 39 c5 0f 84 d0 00 00 00 c6 44 24 07 00 4d 63 f4 <4d> 8b 2f 4c 89 ef e8 5c b3 89 00 45 85 e4 78 1d 41 83 7f 14 ff 75 
> [  237.549525] RSP: 0018:ffffc9000154fc50 EFLAGS: 00010296
> [  237.549527] RAX: 000000004bec0101 RBX: 0000000000000002 RCX: 0000000000000002
> [  237.549529] RDX: 0000000000000002 RSI: 0000000000000001 RDI: ffff88030241c600
> [  237.549530] RBP: ffff88030241c600 R08: 0000000000000000 R09: 0000000000000000
> [  237.549532] R10: ffffc9000154fd30 R11: ffff8802faa09398 R12: 0000000000000001
> [  237.549534] R13: ffff88030241c668 R14: 0000000000000001 R15: 000000004bec0091
> [  237.549536] FS:  0000000000000000(0000) GS:ffff88032dd80000(0000) knlGS:0000000000000000
> [  237.549537] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> [  237.549539] CR2: 000000004bec0091 CR3: 000000000220a001 CR4: 00000000000606e0
> [  237.549540] Call Trace:
> [  237.549545]  flush_workqueue+0x161/0x390
> [  237.549549]  ? preempt_count_add+0x8d/0x90
> [  237.549557]  ? uvc_uninit_video+0x51/0xa0 [uvcvideo]
> [  237.549560]  uvc_uninit_video+0x51/0xa0 [uvcvideo]
> [  237.549565]  uvc_video_stop_streaming+0xe/0x80 [uvcvideo]
> [  237.549568]  uvc_stop_streaming+0x17/0x40 [uvcvideo]
> [  237.549573]  __vb2_queue_cancel+0x25/0x1c0 [videobuf2_common]
> [  237.549576]  vb2_core_queue_release+0x19/0x40 [videobuf2_common]
> [  237.549580]  uvc_queue_release+0x1c/0x30 [uvcvideo]
> [  237.549584]  uvc_v4l2_release+0x9d/0xd0 [uvcvideo]
> [  237.549594]  v4l2_release+0x2d/0x80 [videodev]
> [  237.549598]  __fput+0x95/0x1d0
> [  237.549602]  task_work_run+0x84/0xa0
> [  237.549605]  do_exit+0x2a3/0xad0
> [  237.549608]  do_group_exit+0x2e/0xa0
> [  237.549610]  __x64_sys_exit_group+0xf/0x10
> [  237.549613]  do_syscall_64+0x3d/0xf0
> [  237.549617]  entry_SYSCALL_64_after_hwframe+0x44/0xa9
> [  237.549619] RIP: 0033:0x7f2b0b6097a6
> [  237.549620] Code: Bad RIP value.
> [  237.549625] RSP: 002b:00007ffd2ddf5088 EFLAGS: 00000246 ORIG_RAX: 00000000000000e7
> [  237.549627] RAX: ffffffffffffffda RBX: 00007f2b0b8f8740 RCX: 00007f2b0b6097a6
> [  237.549629] RDX: 0000000000000000 RSI: 000000000000003c RDI: 0000000000000000
> [  237.549630] RBP: 0000000000000000 R08: 00000000000000e7 R09: ffffffffffffff80
> [  237.549632] R10: 00007f2b05e37168 R11: 0000000000000246 R12: 00007f2b0b8f8740
> [  237.549634] R13: 0000000000000001 R14: 00007f2b0b9016e8 R15: 0000000000000000
> [  237.549636] Modules linked in: uvcvideo(O) videobuf2_vmalloc(O) videobuf2_memops(O) videobuf2_v4l2(O) videobuf2_common(O) v4l2_common(O) videodev(O) media(O) xt_conntrack ip6table_filter binfmt_misc dell_laptop snd_hda_codec_hdmi dell_smbios dcdbas coretemp hwmon kvm_intel kvm irqbypass snd_hda_codec_idt snd_hda_codec_generic iwldvm snd_hda_intel snd_hda_codec snd_hwdep snd_hda_core snd_pcm snd_timer iwlwifi snd [last unloaded: media]
> [  237.549659] CR2: 000000004bec0091
> [  237.549662] ---[ end trace 574358b4f247dc6f ]---
> [  237.549664] RIP: 0010:flush_workqueue_prep_pwqs+0x49/0x120
> [  237.549665] Code: 47 48 85 c0 0f 85 e1 00 00 00 c7 45 48 01 00 00 00 48 8b 45 00 4c 8d 78 90 48 39 c5 0f 84 d0 00 00 00 c6 44 24 07 00 4d 63 f4 <4d> 8b 2f 4c 89 ef e8 5c b3 89 00 45 85 e4 78 1d 41 83 7f 14 ff 75 
> [  237.549696] RSP: 0018:ffffc9000154fc50 EFLAGS: 00010296
> [  237.549699] RAX: 000000004bec0101 RBX: 0000000000000002 RCX: 0000000000000002
> [  237.549700] RDX: 0000000000000002 RSI: 0000000000000001 RDI: ffff88030241c600
> [  237.549702] RBP: ffff88030241c600 R08: 0000000000000000 R09: 0000000000000000
> [  237.549704] R10: ffffc9000154fd30 R11: ffff8802faa09398 R12: 0000000000000001
> [  237.549706] R13: ffff88030241c668 R14: 0000000000000001 R15: 000000004bec0091
> [  237.549708] FS:  0000000000000000(0000) GS:ffff88032dd80000(0000) knlGS:0000000000000000
> [  237.549710] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> [  237.549712] CR2: 00007f2b0b60977c CR3: 000000000220a001 CR4: 00000000000606e0
> [  237.549714] Fixing recursive fault but reboot is needed!
> 
>>>   media: uvcvideo: Rename uvc_{un,}init_video()
>>>   media: uvcvideo: Utilise for_each_uvc_urb iterator
>>
>> And I've sent review comments for these two.
>>
>>>  drivers/media/usb/uvc/uvc_driver.c |   2 +-
>>>  drivers/media/usb/uvc/uvc_isight.c |   6 +-
>>>  drivers/media/usb/uvc/uvc_queue.c  | 110 +++++++++---
>>>  drivers/media/usb/uvc/uvc_video.c  | 282 +++++++++++++++++++-----------
>>>  drivers/media/usb/uvc/uvcvideo.h   |  65 ++++++-
>>>  5 files changed, 331 insertions(+), 134 deletions(-)
>>>
>>> base-commit: dafb7f9aef2fd44991ff1691721ff765a23be27b
> 

-- 
Regards
--
Kieran

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 8/9] media: uvcvideo: Rename uvc_{un,}init_video()
  2018-11-07 20:25       ` Laurent Pinchart
@ 2018-11-09 15:41         ` Philipp Zabel
  2018-11-09 16:08           ` Kieran Bingham
  0 siblings, 1 reply; 27+ messages in thread
From: Philipp Zabel @ 2018-11-09 15:41 UTC (permalink / raw)
  To: Laurent Pinchart, kieran.bingham
  Cc: linux-media, Guennadi Liakhovetski, Olivier BRAUN, Troy Kisky,
	Randy Dunlap, Philipp Zabel, Ezequiel Garcia

On Wed, 2018-11-07 at 22:25 +0200, Laurent Pinchart wrote:
> Hi Kieran,
> 
> On Wednesday, 7 November 2018 16:30:46 EET Kieran Bingham wrote:
> > On 06/11/2018 23:13, Laurent Pinchart wrote:
> > > On Tuesday, 6 November 2018 23:27:19 EET Kieran Bingham wrote:
> > > > From: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > > > 
> > > > We have both uvc_init_video() and uvc_video_init() calls which can be
> > > > quite confusing to determine the process for each. Now that video
> > > > uvc_video_enable() has been renamed to uvc_video_start_streaming(),
> > > > adapt these calls to suit the new flow.
> > > > 
> > > > Rename uvc_init_video() to uvc_video_start() and uvc_uninit_video() to
> > > > uvc_video_stop().
> > > 
> > > I agree that these functions are badly named and should be renamed. We are
> > > however entering the nitpicking territory :-) The two functions do more
> > > that starting and stopping, they also allocate and free URBs and the
> > > associated buffers. It could also be argued that they don't actually
> > > start and stop anything, as beyond URB management, they just queue the
> > > URBs initially and kill them. I thus wonder if we could come up with
> > > better names.
> > 
> > Well the act of killing (poisoning now) the URBs will certainly stop the
> > stream, but I guess submitting the URBs isn't necessarily the key act to
> > starting the stream.
> > 
> > I believe that needs the interface to be set correctly, and the buffers
> > to be available?
> > 
> > Although - I've just double-checked uvc_{video_start,init_video}() and
> > that is indeed what it does?
> > 
> >  - start stats
> >  - Initialise endpoints
> >    - Perform allocations
> >  - Submit URBs
> > 
> > Am I missing something? Is there another step that is pivotal to
> > starting the USB packet/urb stream flow after this point ?
> > 
> > 
> > Is it not true that the USB stack will start processing data at
> > submitting URB completion callbacks after the end of uvc_video_start();
> > and will no longer process data at the end of uvc_video_stop() (and thus
> > no more completion callbacks)?
> > 
> >  (That's a real question to verify my interpretation)
> > 
> > To me - these functions feel like the real 'start' and 'stop' components
> > of the data stream - hence my choice in naming.
> 
> The other part of the start operation is committing the streaming parameters 
> (see uvc_video_start_streaming()). For the stop operation it's issuing a 
> SET_INTERFACE or CLEAR_FEATURE(HALT) request (see uvc_video_stop_streaming()).
> 
> > Is your concern that you would like the functions to be more descriptive
> > over their other actions such as? :
> > 
> >   uvc_video_initialise_start()
> >   uvc_video_allocate_init_start()
> > 
> > Or something else? (I don't think those two are good names though)
> 
> Probably something else :-) A possibly equally bad proposal would be 
> uvc_video_start_transfer() and uvc_video_stop_transfer().

I think this is still better than what we have now.

At least it contains "transfer" to make it clear it deals with the
isoc/bulk transfer setup/teardown part of streaming, not actually
starting or stopping the device streaming.

regards
Philipp

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 8/9] media: uvcvideo: Rename uvc_{un,}init_video()
  2018-11-09 15:41         ` Philipp Zabel
@ 2018-11-09 16:08           ` Kieran Bingham
  0 siblings, 0 replies; 27+ messages in thread
From: Kieran Bingham @ 2018-11-09 16:08 UTC (permalink / raw)
  To: Philipp Zabel, Laurent Pinchart
  Cc: linux-media, Guennadi Liakhovetski, Olivier BRAUN, Troy Kisky,
	Randy Dunlap, Philipp Zabel, Ezequiel Garcia

On 09/11/2018 15:41, Philipp Zabel wrote:
> On Wed, 2018-11-07 at 22:25 +0200, Laurent Pinchart wrote:
>> Hi Kieran,
>>
>> On Wednesday, 7 November 2018 16:30:46 EET Kieran Bingham wrote:
>>> On 06/11/2018 23:13, Laurent Pinchart wrote:
>>>> On Tuesday, 6 November 2018 23:27:19 EET Kieran Bingham wrote:
>>>>> From: Kieran Bingham <kieran.bingham@ideasonboard.com>
>>>>>
>>>>> We have both uvc_init_video() and uvc_video_init() calls which can be
>>>>> quite confusing to determine the process for each. Now that video
>>>>> uvc_video_enable() has been renamed to uvc_video_start_streaming(),
>>>>> adapt these calls to suit the new flow.
>>>>>
>>>>> Rename uvc_init_video() to uvc_video_start() and uvc_uninit_video() to
>>>>> uvc_video_stop().
>>>>
>>>> I agree that these functions are badly named and should be renamed. We are
>>>> however entering the nitpicking territory :-) The two functions do more
>>>> that starting and stopping, they also allocate and free URBs and the
>>>> associated buffers. It could also be argued that they don't actually
>>>> start and stop anything, as beyond URB management, they just queue the
>>>> URBs initially and kill them. I thus wonder if we could come up with
>>>> better names.
>>>
>>> Well the act of killing (poisoning now) the URBs will certainly stop the
>>> stream, but I guess submitting the URBs isn't necessarily the key act to
>>> starting the stream.
>>>
>>> I believe that needs the interface to be set correctly, and the buffers
>>> to be available?
>>>
>>> Although - I've just double-checked uvc_{video_start,init_video}() and
>>> that is indeed what it does?
>>>
>>>  - start stats
>>>  - Initialise endpoints
>>>    - Perform allocations
>>>  - Submit URBs
>>>
>>> Am I missing something? Is there another step that is pivotal to
>>> starting the USB packet/urb stream flow after this point ?
>>>
>>>
>>> Is it not true that the USB stack will start processing data at
>>> submitting URB completion callbacks after the end of uvc_video_start();
>>> and will no longer process data at the end of uvc_video_stop() (and thus
>>> no more completion callbacks)?
>>>
>>>  (That's a real question to verify my interpretation)
>>>
>>> To me - these functions feel like the real 'start' and 'stop' components
>>> of the data stream - hence my choice in naming.
>>
>> The other part of the start operation is committing the streaming parameters 
>> (see uvc_video_start_streaming()). For the stop operation it's issuing a 
>> SET_INTERFACE or CLEAR_FEATURE(HALT) request (see uvc_video_stop_streaming()).
>>
>>> Is your concern that you would like the functions to be more descriptive
>>> over their other actions such as? :
>>>
>>>   uvc_video_initialise_start()
>>>   uvc_video_allocate_init_start()
>>>
>>> Or something else? (I don't think those two are good names though)
>>
>> Probably something else :-) A possibly equally bad proposal would be 
>> uvc_video_start_transfer() and uvc_video_stop_transfer().
> 
> I think this is still better than what we have now.
> 
> At least it contains "transfer" to make it clear it deals with the
> isoc/bulk transfer setup/teardown part of streaming, not actually
> starting or stopping the device streaming.

Ok - uvc_video_start_transfer() and uvc_video_stop_transfer() it shall
be then :)

v6 with fixed stream object / workqueue lifetime and updates to the last
two patches coming ... soon :)
--
Kieran



> 
> regards
> Philipp
> 

-- 
Regards
--
Kieran

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 0/9] Asynchronous UVC
  2018-11-06 21:27 [PATCH v5 0/9] Asynchronous UVC Kieran Bingham
                   ` (9 preceding siblings ...)
  2018-11-06 23:31 ` [PATCH v5 0/9] Asynchronous UVC Laurent Pinchart
@ 2018-11-27 20:17 ` Pavel Machek
  2018-11-27 21:48   ` Laurent Pinchart
  10 siblings, 1 reply; 27+ messages in thread
From: Pavel Machek @ 2018-11-27 20:17 UTC (permalink / raw)
  To: Kieran Bingham
  Cc: Laurent Pinchart, linux-media, Guennadi Liakhovetski,
	Olivier BRAUN, Troy Kisky, Randy Dunlap, Philipp Zabel,
	Ezequiel Garcia, Kieran Bingham

[-- Attachment #1: Type: text/plain, Size: 1539 bytes --]

On Tue 2018-11-06 21:27:11, Kieran Bingham wrote:
> From: Kieran Bingham <kieran.bingham@ideasonboard.com>
> 
> The Linux UVC driver has long provided adequate performance capabilities for
> web-cams and low data rate video devices in Linux while resolutions were low.
> 
> Modern USB cameras are now capable of high data rates thanks to USB3 with
> 1080p, and even 4k capture resolutions supported.
> 
> Cameras such as the Stereolabs ZED (bulk transfers) or the Logitech BRIO
> (isochronous transfers) can generate more data than an embedded ARM core is
> able to process on a single core, resulting in frame loss.
> 
> A large part of this performance impact is from the requirement to
> ‘memcpy’ frames out from URB packets to destination frames. This unfortunate
> requirement is due to the UVC protocol allowing a variable length header, and
> thus it is not possible to provide the target frame buffers directly.
> 
> Extra throughput is possible by moving the actual memcpy actions to a work
> queue, and moving the memcpy out of interrupt context thus allowing work tasks
> to be scheduled across multiple cores.

Hmm. Doing memcpy() on many cores is improvement but... not really.
Would it be possible to improve kernel<->user interface, so it says
"data is in this buffer, and it starts here" and so that memcpy in
kernel is not neccessary?
									Pavel

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 0/9] Asynchronous UVC
  2018-11-27 20:17 ` Pavel Machek
@ 2018-11-27 21:48   ` Laurent Pinchart
  0 siblings, 0 replies; 27+ messages in thread
From: Laurent Pinchart @ 2018-11-27 21:48 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Kieran Bingham, linux-media, Guennadi Liakhovetski,
	Olivier BRAUN, Troy Kisky, Randy Dunlap, Philipp Zabel,
	Ezequiel Garcia, Kieran Bingham

Hi Pavel,

On Tuesday, 27 November 2018 22:17:30 EET Pavel Machek wrote:
> On Tue 2018-11-06 21:27:11, Kieran Bingham wrote:
> > From: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > 
> > The Linux UVC driver has long provided adequate performance capabilities
> > for web-cams and low data rate video devices in Linux while resolutions
> > were low.
> > 
> > Modern USB cameras are now capable of high data rates thanks to USB3 with
> > 1080p, and even 4k capture resolutions supported.
> > 
> > Cameras such as the Stereolabs ZED (bulk transfers) or the Logitech BRIO
> > (isochronous transfers) can generate more data than an embedded ARM core
> > is able to process on a single core, resulting in frame loss.
> > 
> > A large part of this performance impact is from the requirement to
> > ‘memcpy’ frames out from URB packets to destination frames. This
> > unfortunate requirement is due to the UVC protocol allowing a variable
> > length header, and thus it is not possible to provide the target frame
> > buffers directly.
> > 
> > Extra throughput is possible by moving the actual memcpy actions to a work
> > queue, and moving the memcpy out of interrupt context thus allowing work
> > tasks to be scheduled across multiple cores.
> 
> Hmm. Doing memcpy() on many cores is improvement but... not really.
> Would it be possible to improve kernel<->user interface, so it says
> "data is in this buffer, and it starts here" and so that memcpy in
> kernel is not neccessary?

Unfortunately not, as the UVC protocol segments the frame in a large number of 
small packets, each prefixed with a variable-length header. It's a poorly 
designed protocol from that point of view.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2018-11-28  8:47 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-11-06 21:27 [PATCH v5 0/9] Asynchronous UVC Kieran Bingham
2018-11-06 21:27 ` [PATCH v5 1/9] media: uvcvideo: Refactor URB descriptors Kieran Bingham
2018-11-06 21:27 ` [PATCH v5 2/9] media: uvcvideo: Convert decode functions to use new context structure Kieran Bingham
2018-11-06 21:27 ` [PATCH v5 3/9] media: uvcvideo: Protect queue internals with helper Kieran Bingham
2018-11-06 21:27 ` [PATCH v5 4/9] media: uvcvideo: queue: Simplify spin-lock usage Kieran Bingham
2018-11-06 21:27 ` [PATCH v5 5/9] media: uvcvideo: queue: Support asynchronous buffer handling Kieran Bingham
2018-11-06 22:38   ` Laurent Pinchart
2018-11-06 21:27 ` [PATCH v5 6/9] media: uvcvideo: Move decode processing to process context Kieran Bingham
2018-11-06 22:58   ` Laurent Pinchart
2018-11-07 12:22     ` Kieran Bingham
2018-11-06 21:27 ` [PATCH v5 7/9] media: uvcvideo: Split uvc_video_enable into two Kieran Bingham
2018-11-06 23:08   ` Laurent Pinchart
2018-11-07 12:20     ` Kieran Bingham
2018-11-06 21:27 ` [PATCH v5 8/9] media: uvcvideo: Rename uvc_{un,}init_video() Kieran Bingham
2018-11-06 23:13   ` Laurent Pinchart
2018-11-07 14:30     ` Kieran Bingham
2018-11-07 20:25       ` Laurent Pinchart
2018-11-09 15:41         ` Philipp Zabel
2018-11-09 16:08           ` Kieran Bingham
2018-11-06 21:27 ` [PATCH v5 9/9] media: uvcvideo: Utilise for_each_uvc_urb iterator Kieran Bingham
2018-11-06 23:21   ` Laurent Pinchart
2018-11-07 13:50     ` Kieran Bingham
2018-11-06 23:31 ` [PATCH v5 0/9] Asynchronous UVC Laurent Pinchart
2018-11-08 17:01   ` Laurent Pinchart
2018-11-09 13:25     ` Kieran Bingham
2018-11-27 20:17 ` Pavel Machek
2018-11-27 21:48   ` Laurent Pinchart

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).