All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCHv17 00/34] Request API
@ 2018-08-04 12:44 Hans Verkuil
  2018-08-04 12:44 ` [PATCHv17 01/34] Documentation: v4l: document request API Hans Verkuil
                   ` (33 more replies)
  0 siblings, 34 replies; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:44 UTC (permalink / raw)
  To: linux-media

From: Hans Verkuil <hans.verkuil@cisco.com>

Hi all,

This is version 17 of the Request API series.

The (minor) changes compared to v16 are:

- Incorporated Tomasz' documentation comments.

- Dropped patch 28/34: "v4l2-mem2mem: Simplify exiting the function in
  v4l2_m2m_try_schedule". This is really independent of the Request API
  and is part of a v4l2-mem2mem patch series from Ezequiel Garcia:

  https://www.mail-archive.com/linux-media@vger.kernel.org/msg133969.html

- Added this patch as it is needed by the cedrus driver:
  https://patchwork.linuxtv.org/patch/50771/

- Rebased to the latest master.

The goal is to get this merged together with a staging cedrus driver for
4.20.

This patch series is also available here:

https://git.linuxtv.org/hverkuil/media_tree.git/log/?h=reqv17

The patched v4l2-compliance (and rebased to the latest v4l-utils
as well) is available here:

https://git.linuxtv.org/hverkuil/v4l-utils.git/log/?h=request

Regards,

	Hans

Alexandre Courbot (2):
  Documentation: v4l: document request API
  videodev2.h: add request_fd field to v4l2_ext_controls

Hans Verkuil (31):
  uapi/linux/media.h: add request API
  media-request: implement media requests
  media-request: add media_request_get_by_fd
  media-request: add media_request_object_find
  v4l2-device.h: add v4l2_device_supports_requests() helper
  v4l2-dev: lock req_queue_mutex
  v4l2-ctrls: v4l2_ctrl_add_handler: add from_other_dev
  v4l2-ctrls: prepare internal structs for request API
  v4l2-ctrls: alloc memory for p_req
  v4l2-ctrls: use ref in helper instead of ctrl
  v4l2-ctrls: add core request support
  v4l2-ctrls: support g/s_ext_ctrls for requests
  v4l2-ctrls: add v4l2_ctrl_request_hdl_find/put/ctrl_find functions
  vb2: store userspace data in vb2_v4l2_buffer
  davinci_vpfe: remove bogus vb2->state check
  vb2: drop VB2_BUF_STATE_PREPARED, use bool prepared/synced instead
  videodev2.h: Add request_fd field to v4l2_buffer
  vb2: add init_buffer buffer op
  videobuf2-core: embed media_request_object
  videobuf2-core: integrate with media requests
  videobuf2-v4l2: integrate with media requests
  videobuf2-core: add request helper functions
  videobuf2-v4l2: add vb2_request_queue/validate helpers
  videobuf2-core: add uses_requests/qbuf flags
  videobuf2-v4l2: refuse qbuf if queue uses requests or vv.
  v4l2-mem2mem: add vb2_m2m_request_queue
  vim2m: use workqueue
  vim2m: support requests
  vivid: add mc
  vivid: add request support
  RFC: media-requests: add debugfs node

Sakari Ailus (1):
  media: doc: Add media-request.h header to documentation build

 Documentation/media/kapi/mc-core.rst          |   2 +
 .../media/uapi/mediactl/media-controller.rst  |   1 +
 .../media/uapi/mediactl/media-funcs.rst       |   6 +
 .../uapi/mediactl/media-ioc-request-alloc.rst |  78 +++
 .../uapi/mediactl/media-request-ioc-queue.rst |  82 +++
 .../mediactl/media-request-ioc-reinit.rst     |  51 ++
 .../media/uapi/mediactl/request-api.rst       | 247 ++++++++
 .../uapi/mediactl/request-func-close.rst      |  49 ++
 .../uapi/mediactl/request-func-ioctl.rst      |  68 +++
 .../media/uapi/mediactl/request-func-poll.rst |  77 +++
 Documentation/media/uapi/v4l/buffer.rst       |  21 +-
 .../media/uapi/v4l/vidioc-g-ext-ctrls.rst     |  94 ++--
 Documentation/media/uapi/v4l/vidioc-qbuf.rst  |  32 +-
 .../media/videodev2.h.rst.exceptions          |   1 +
 drivers/media/Makefile                        |   3 +-
 .../media/common/videobuf2/videobuf2-core.c   | 262 +++++++--
 .../media/common/videobuf2/videobuf2-v4l2.c   | 501 ++++++++++++-----
 drivers/media/dvb-core/dvb_vb2.c              |   5 +-
 drivers/media/dvb-frontends/rtl2832_sdr.c     |   5 +-
 drivers/media/media-device.c                  |  55 ++
 drivers/media/media-devnode.c                 |  17 +
 drivers/media/media-request.c                 | 493 ++++++++++++++++
 drivers/media/pci/bt8xx/bttv-driver.c         |   2 +-
 drivers/media/pci/cx23885/cx23885-417.c       |   2 +-
 drivers/media/pci/cx88/cx88-blackbird.c       |   2 +-
 drivers/media/pci/cx88/cx88-video.c           |   2 +-
 drivers/media/pci/saa7134/saa7134-empress.c   |   4 +-
 drivers/media/pci/saa7134/saa7134-video.c     |   2 +-
 .../media/platform/exynos4-is/fimc-capture.c  |   2 +-
 drivers/media/platform/omap3isp/ispvideo.c    |   4 +-
 drivers/media/platform/rcar-vin/rcar-core.c   |   2 +-
 drivers/media/platform/rcar_drif.c            |   2 +-
 .../media/platform/s3c-camif/camif-capture.c  |   4 +-
 drivers/media/platform/s5p-mfc/s5p_mfc_dec.c  |   4 +-
 drivers/media/platform/s5p-mfc/s5p_mfc_enc.c  |   4 +-
 .../media/platform/soc_camera/soc_camera.c    |   7 +-
 drivers/media/platform/vim2m.c                |  51 +-
 drivers/media/platform/vivid/vivid-core.c     |  69 +++
 drivers/media/platform/vivid/vivid-core.h     |   8 +
 drivers/media/platform/vivid/vivid-ctrls.c    |  46 +-
 .../media/platform/vivid/vivid-kthread-cap.c  |  12 +
 .../media/platform/vivid/vivid-kthread-out.c  |  12 +
 drivers/media/platform/vivid/vivid-sdr-cap.c  |  16 +
 drivers/media/platform/vivid/vivid-vbi-cap.c  |  10 +
 drivers/media/platform/vivid/vivid-vbi-out.c  |  10 +
 drivers/media/platform/vivid/vivid-vid-cap.c  |  10 +
 drivers/media/platform/vivid/vivid-vid-out.c  |  10 +
 drivers/media/usb/cpia2/cpia2_v4l.c           |   2 +-
 drivers/media/usb/cx231xx/cx231xx-417.c       |   2 +-
 drivers/media/usb/cx231xx/cx231xx-video.c     |   4 +-
 drivers/media/usb/msi2500/msi2500.c           |   2 +-
 drivers/media/usb/tm6000/tm6000-video.c       |   2 +-
 drivers/media/usb/uvc/uvc_queue.c             |   5 +-
 drivers/media/usb/uvc/uvc_v4l2.c              |   3 +-
 drivers/media/usb/uvc/uvcvideo.h              |   1 +
 drivers/media/v4l2-core/v4l2-compat-ioctl32.c |  14 +-
 drivers/media/v4l2-core/v4l2-ctrls.c          | 531 +++++++++++++++++-
 drivers/media/v4l2-core/v4l2-dev.c            |  13 +
 drivers/media/v4l2-core/v4l2-device.c         |   3 +-
 drivers/media/v4l2-core/v4l2-ioctl.c          |  44 +-
 drivers/media/v4l2-core/v4l2-mem2mem.c        |  63 ++-
 drivers/media/v4l2-core/v4l2-subdev.c         |   9 +-
 .../staging/media/davinci_vpfe/vpfe_video.c   |   7 +-
 drivers/staging/media/imx/imx-media-dev.c     |   2 +-
 drivers/staging/media/imx/imx-media-fim.c     |   2 +-
 drivers/staging/media/omap4iss/iss_video.c    |   3 +-
 drivers/usb/gadget/function/uvc_queue.c       |   2 +-
 include/media/media-device.h                  |  32 ++
 include/media/media-devnode.h                 |   4 +
 include/media/media-request.h                 | 381 +++++++++++++
 include/media/v4l2-ctrls.h                    | 118 +++-
 include/media/v4l2-device.h                   |  11 +
 include/media/v4l2-mem2mem.h                  |   4 +
 include/media/videobuf2-core.h                |  58 +-
 include/media/videobuf2-v4l2.h                |  20 +-
 include/uapi/linux/media.h                    |  12 +
 include/uapi/linux/videodev2.h                |  14 +-
 77 files changed, 3432 insertions(+), 383 deletions(-)
 create mode 100644 Documentation/media/uapi/mediactl/media-ioc-request-alloc.rst
 create mode 100644 Documentation/media/uapi/mediactl/media-request-ioc-queue.rst
 create mode 100644 Documentation/media/uapi/mediactl/media-request-ioc-reinit.rst
 create mode 100644 Documentation/media/uapi/mediactl/request-api.rst
 create mode 100644 Documentation/media/uapi/mediactl/request-func-close.rst
 create mode 100644 Documentation/media/uapi/mediactl/request-func-ioctl.rst
 create mode 100644 Documentation/media/uapi/mediactl/request-func-poll.rst
 create mode 100644 drivers/media/media-request.c
 create mode 100644 include/media/media-request.h

-- 
2.18.0

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

* [PATCHv17 01/34] Documentation: v4l: document request API
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
@ 2018-08-04 12:44 ` Hans Verkuil
  2018-08-06 23:44   ` Pavel Machek
  2018-08-09 17:43   ` Mauro Carvalho Chehab
  2018-08-04 12:44 ` [PATCHv17 02/34] uapi/linux/media.h: add " Hans Verkuil
                   ` (32 subsequent siblings)
  33 siblings, 2 replies; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:44 UTC (permalink / raw)
  To: linux-media; +Cc: Alexandre Courbot, Hans Verkuil

From: Alexandre Courbot <acourbot@chromium.org>

Document the request API for V4L2 devices, and amend the documentation
of system calls influenced by it.

Signed-off-by: Alexandre Courbot <acourbot@chromium.org>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 .../media/uapi/mediactl/media-controller.rst  |   1 +
 .../media/uapi/mediactl/media-funcs.rst       |   6 +
 .../uapi/mediactl/media-ioc-request-alloc.rst |  78 ++++++
 .../uapi/mediactl/media-request-ioc-queue.rst |  82 ++++++
 .../mediactl/media-request-ioc-reinit.rst     |  51 ++++
 .../media/uapi/mediactl/request-api.rst       | 247 ++++++++++++++++++
 .../uapi/mediactl/request-func-close.rst      |  49 ++++
 .../uapi/mediactl/request-func-ioctl.rst      |  68 +++++
 .../media/uapi/mediactl/request-func-poll.rst |  77 ++++++
 Documentation/media/uapi/v4l/buffer.rst       |  21 +-
 .../media/uapi/v4l/vidioc-g-ext-ctrls.rst     |  94 ++++---
 Documentation/media/uapi/v4l/vidioc-qbuf.rst  |  32 ++-
 .../media/videodev2.h.rst.exceptions          |   1 +
 13 files changed, 771 insertions(+), 36 deletions(-)
 create mode 100644 Documentation/media/uapi/mediactl/media-ioc-request-alloc.rst
 create mode 100644 Documentation/media/uapi/mediactl/media-request-ioc-queue.rst
 create mode 100644 Documentation/media/uapi/mediactl/media-request-ioc-reinit.rst
 create mode 100644 Documentation/media/uapi/mediactl/request-api.rst
 create mode 100644 Documentation/media/uapi/mediactl/request-func-close.rst
 create mode 100644 Documentation/media/uapi/mediactl/request-func-ioctl.rst
 create mode 100644 Documentation/media/uapi/mediactl/request-func-poll.rst

diff --git a/Documentation/media/uapi/mediactl/media-controller.rst b/Documentation/media/uapi/mediactl/media-controller.rst
index 0eea4f9a07d5..66aff38cd499 100644
--- a/Documentation/media/uapi/mediactl/media-controller.rst
+++ b/Documentation/media/uapi/mediactl/media-controller.rst
@@ -21,6 +21,7 @@ Part IV - Media Controller API
     media-controller-intro
     media-controller-model
     media-types
+    request-api
     media-funcs
     media-header
 
diff --git a/Documentation/media/uapi/mediactl/media-funcs.rst b/Documentation/media/uapi/mediactl/media-funcs.rst
index 076856501cdb..260f9dcadcde 100644
--- a/Documentation/media/uapi/mediactl/media-funcs.rst
+++ b/Documentation/media/uapi/mediactl/media-funcs.rst
@@ -16,3 +16,9 @@ Function Reference
     media-ioc-enum-entities
     media-ioc-enum-links
     media-ioc-setup-link
+    media-ioc-request-alloc
+    request-func-close
+    request-func-ioctl
+    request-func-poll
+    media-request-ioc-queue
+    media-request-ioc-reinit
diff --git a/Documentation/media/uapi/mediactl/media-ioc-request-alloc.rst b/Documentation/media/uapi/mediactl/media-ioc-request-alloc.rst
new file mode 100644
index 000000000000..11dcc14ca0bd
--- /dev/null
+++ b/Documentation/media/uapi/mediactl/media-ioc-request-alloc.rst
@@ -0,0 +1,78 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. _media_ioc_request_alloc:
+
+*****************************
+ioctl MEDIA_IOC_REQUEST_ALLOC
+*****************************
+
+Name
+====
+
+MEDIA_IOC_REQUEST_ALLOC - Allocate a request
+
+
+Synopsis
+========
+
+.. c:function:: int ioctl( int fd, MEDIA_IOC_REQUEST_ALLOC, struct media_request_alloc *argp )
+    :name: MEDIA_IOC_REQUEST_ALLOC
+
+
+Arguments
+=========
+
+``fd``
+    File descriptor returned by :ref:`open() <media-func-open>`.
+
+``argp``
+    Pointer to struct :c:type:`media_request_alloc`.
+
+
+Description
+===========
+
+If the media device supports :ref:`requests <media-request-api>`, then
+this ioctl can be used to allocate a request. If it is not supported, then
+``errno`` is set to ``ENOTTY``. A request is accessed through a file descriptor
+that is returned in struct :c:type:`media_request_alloc`.
+
+If the request was successfully allocated, then the request file descriptor
+can be passed to the :ref:`VIDIOC_QBUF <VIDIOC_QBUF>`,
+:ref:`VIDIOC_G_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>`,
+:ref:`VIDIOC_S_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` and
+:ref:`VIDIOC_TRY_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` ioctls.
+
+In addition, the request can be queued by calling
+:ref:`MEDIA_REQUEST_IOC_QUEUE` and re-initialized by calling
+:ref:`MEDIA_REQUEST_IOC_REINIT`.
+
+Finally, the file descriptor can be :ref:`polled <request-func-poll>` to wait
+for the request to complete.
+
+The request will remain allocated until all the file descriptors associated
+with it are closed by :ref:`close() <request-func-close>` and the driver no
+longer uses the request internally.
+
+.. c:type:: media_request_alloc
+
+.. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.7cm}|
+
+.. flat-table:: struct media_request_alloc
+    :header-rows:  0
+    :stub-columns: 0
+    :widths:       1 1 2
+
+    *  -  __s32
+       -  ``fd``
+       -  The file descriptor of the request.
+
+Return Value
+============
+
+On success 0 is returned, on error -1 and the ``errno`` variable is set
+appropriately. The generic error codes are described at the
+:ref:`Generic Error Codes <gen-errors>` chapter.
+
+ENOTTY
+    The driver has no support for requests.
diff --git a/Documentation/media/uapi/mediactl/media-request-ioc-queue.rst b/Documentation/media/uapi/mediactl/media-request-ioc-queue.rst
new file mode 100644
index 000000000000..86a341cbc59e
--- /dev/null
+++ b/Documentation/media/uapi/mediactl/media-request-ioc-queue.rst
@@ -0,0 +1,82 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. _media_request_ioc_queue:
+
+*****************************
+ioctl MEDIA_REQUEST_IOC_QUEUE
+*****************************
+
+Name
+====
+
+MEDIA_REQUEST_IOC_QUEUE - Queue a request
+
+
+Synopsis
+========
+
+.. c:function:: int ioctl( int request_fd, MEDIA_REQUEST_IOC_QUEUE )
+    :name: MEDIA_REQUEST_IOC_QUEUE
+
+
+Arguments
+=========
+
+``request_fd``
+    File descriptor returned by :ref:`MEDIA_IOC_REQUEST_ALLOC`.
+
+
+Description
+===========
+
+If the media device supports :ref:`requests <media-request-api>`, then
+this request ioctl can be used to queue a previously allocated request.
+
+If the request was successfully queued, then the file descriptor can be
+:ref:`polled <request-func-poll>` to wait for the request to complete.
+
+If the request was already queued before, then ``EBUSY`` is returned.
+Other errors can be returned if the contents of the request contained
+invalid or inconsistent data, see the next section for a list of
+common error codes. On error both the request and driver state are unchanged.
+
+Typically if you get an error here, then that means that the application
+did something wrong and you have to fix the application.
+
+Once a request is queued, then the driver is required to gracefully handle
+errors that occur when the request is applied to the hardware. The
+exception is the ``EIO`` error which signals a fatal error that requires
+the application to stop streaming to reset the hardware state.
+
+It is not allowed to mix queuing requests with queuing buffers directly
+(without a request). ``EPERM`` will be returned if the first buffer was
+queued directly and you next try to queue a request, or vice versa.
+
+A request must contain at least one buffer, otherwise this ioctl will
+return an ``ENOENT`` error.
+
+Return Value
+============
+
+On success 0 is returned, on error -1 and the ``errno`` variable is set
+appropriately. The generic error codes are described at the
+:ref:`Generic Error Codes <gen-errors>` chapter.
+
+EBUSY
+    The request was already queued.
+EPERM
+    The application queued the first buffer directly, but later attempted
+    to use a request. It is not permitted to mix the two APIs.
+ENOENT
+    The request did not contain any buffers. All requests are required
+    to have at least one buffer. This can also be returned if required
+    controls are missing.
+ENOMEM
+    Out of memory when allocating internal data structures for this
+    request.
+EINVAL
+    The request has invalid data.
+EIO
+    The hardware is in a bad state. To recover, the application needs to
+    stop streaming to reset the hardware state and then try to restart
+    streaming.
diff --git a/Documentation/media/uapi/mediactl/media-request-ioc-reinit.rst b/Documentation/media/uapi/mediactl/media-request-ioc-reinit.rst
new file mode 100644
index 000000000000..7ede0584a0d7
--- /dev/null
+++ b/Documentation/media/uapi/mediactl/media-request-ioc-reinit.rst
@@ -0,0 +1,51 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. _media_request_ioc_reinit:
+
+******************************
+ioctl MEDIA_REQUEST_IOC_REINIT
+******************************
+
+Name
+====
+
+MEDIA_REQUEST_IOC_REINIT - Re-initialize a request
+
+
+Synopsis
+========
+
+.. c:function:: int ioctl( int request_fd, MEDIA_REQUEST_IOC_REINIT )
+    :name: MEDIA_REQUEST_IOC_REINIT
+
+
+Arguments
+=========
+
+``request_fd``
+    File descriptor returned by :ref:`MEDIA_IOC_REQUEST_ALLOC`.
+
+Description
+===========
+
+If the media device supports :ref:`requests <media-request-api>`, then
+this request ioctl can be used to re-initialize a previously allocated
+request.
+
+Re-initializing a request will clear any existing data from the request.
+This avoids having to :ref:`close() <request-func-close>` a completed
+request and allocate a new request. Instead the completed request can just
+be re-initialized and it is ready to be used again.
+
+A request can only be re-initialized if it either has not been queued
+yet, or if it was queued and completed. Otherwise it will set ``errno``
+to ``EBUSY``. No other error codes can be returned.
+
+Return Value
+============
+
+On success 0 is returned, on error -1 and the ``errno`` variable is set
+appropriately.
+
+EBUSY
+    The request is queued but not yet completed.
diff --git a/Documentation/media/uapi/mediactl/request-api.rst b/Documentation/media/uapi/mediactl/request-api.rst
new file mode 100644
index 000000000000..5d1c4674d4bb
--- /dev/null
+++ b/Documentation/media/uapi/mediactl/request-api.rst
@@ -0,0 +1,247 @@
+.. -*- coding: utf-8; mode: rst -*-
+
+.. _media-request-api:
+
+Request API
+===========
+
+The Request API has been designed to allow V4L2 to deal with requirements of
+modern devices (stateless codecs, complex camera pipelines, ...) and APIs
+(Android Codec v2). One such requirement is the ability for devices belonging to
+the same pipeline to reconfigure and collaborate closely on a per-frame basis.
+Another is support of stateless codecs, which require controls to be applied
+to specific frames (aka 'per-frame controls') in order to be used efficiently.
+
+Supporting these features without the Request API is not always possible and if
+it is, it is terribly inefficient: user-space would have to flush all activity
+on the media pipeline, reconfigure it for the next frame, queue the buffers to
+be processed with that configuration, and wait until they are all available for
+dequeuing before considering the next frame. This defeats the purpose of having
+buffer queues since in practice only one buffer would be queued at a time.
+
+The Request API allows a specific configuration of the pipeline (media
+controller topology + controls for each media entity) to be associated with
+specific buffers. The parameters are applied by each participating device as
+buffers associated to a request flow in. This allows user-space to schedule
+several tasks ("requests") with different parameters in advance, knowing that
+the parameters will be applied when needed to get the expected result. Control
+values at the time of request completion are also available for reading.
+
+Usage
+=====
+
+The Request API is used on top of standard media controller and V4L2 calls,
+which are augmented with an extra ``request_fd`` parameter. Requests themselves
+are allocated from the supporting media controller node.
+
+Request Allocation
+------------------
+
+User-space allocates requests using :ref:`MEDIA_IOC_REQUEST_ALLOC`
+for the media device node. This returns a file descriptor representing the
+request. Typically, several such requests will be allocated.
+
+Request Preparation
+-------------------
+
+Standard V4L2 ioctls can then receive a request file descriptor to express the
+fact that the ioctl is part of said request, and is not to be applied
+immediately. See :ref:`MEDIA_IOC_REQUEST_ALLOC` for a list of ioctls that
+support this. Controls set with a ``request_fd`` parameter are stored instead
+of being immediately applied, and buffers queued to a request do not enter the
+regular buffer queue until the request itself is queued.
+
+Request Submission
+------------------
+
+Once the parameters and buffers of the request are specified, it can be
+queued by calling :ref:`MEDIA_REQUEST_IOC_QUEUE` on the request FD. A request
+must contain at least one buffer, otherwise ``ENOENT`` is returned.
+This will make the buffers associated to the request available to their driver,
+which can then apply the associated controls as buffers are processed. A queued
+request cannot be modified anymore.
+
+.. caution::
+   For :ref:`memory-to-memory devices <codec>` you can use requests only for
+   output buffers, not for capture buffers. Attempting to add a capture buffer
+   to a request will result in an ``EPERM`` error.
+
+If the request contains parameters for multiple entities, individual drivers may
+synchronize so the requested pipeline's topology is applied before the buffers
+are processed. Media controller drivers do a best effort implementation since
+perfect atomicity may not be possible due to hardware limitations.
+
+.. caution::
+
+   It is not allowed to mix queuing requests with directly queuing buffers: whichever
+   method is used first locks this in place until :ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>`
+   is called or the device is :ref:`closed <func-close>`. Attempts to
+   directly queue a buffer when earlier a buffer was queued via a request or
+   vice versa will result in an ``EPERM`` error.
+
+Controls can still be set without a request and are applied immediately,
+regardless of whether a request is in use or not.
+
+.. caution::
+
+   Setting the same control through a request and also directly can lead to
+   undefined behavior!
+
+User-space can :ref:`poll() <request-func-poll>` a request FD in order to
+wait until the request completes. A request is considered complete once all its
+associated buffers are available for dequeuing and all the associated controls
+have been updated with the values at the time of completion. Note that user-space
+does not need to wait for the request to complete to dequeue its buffers: buffers
+that are available halfway through a request can be dequeued independently of the
+request's state.
+
+A completed request contains the state of the request at the time of the
+request completion. User-space can query that state by calling
+:ref:`ioctl VIDIOC_G_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` with the request FD.
+Calling :ref:`ioctl VIDIOC_G_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` for a
+request that has been queued but not yet completed will return ``EBUSY``
+since the control values might be changed at any time by the driver while the
+request is in flight.
+
+Recycling and Destruction
+-------------------------
+
+Finally, a completed request can either be discarded or be reused. Calling
+:ref:`close() <request-func-close>` on a request FD will make that FD unusable
+and the request will be freed once it is no longer in use by the kernel. That
+is, if the request is queued and then the FD is closed, then it won't be freed
+until the driver completed the request.
+
+The :ref:`MEDIA_REQUEST_IOC_REINIT` will clear a request's state and make it
+available again. No state is retained by this operation: the request is as
+if it had just been allocated.
+
+Example for a Codec Device
+--------------------------
+
+For use-cases such as :ref:`codecs <codec>`, the request API can be used
+to associate specific controls to
+be applied by the driver for the OUTPUT buffer, allowing user-space
+to queue many such buffers in advance. It can also take advantage of requests'
+ability to capture the state of controls when the request completes to read back
+information that may be subject to change.
+
+Put into code, after obtaining a request, user-space can assign controls and one
+OUTPUT buffer to it:
+
+.. code-block:: c
+
+	struct v4l2_buffer buf;
+	struct v4l2_ext_controls ctrls;
+	struct media_request_alloc alloc = { 0 };
+	int req_fd;
+	...
+	if (ioctl(media_fd, MEDIA_IOC_REQUEST_ALLOC, &alloc))
+		return errno;
+	req_fd = alloc.fd;
+	...
+	ctrls.which = V4L2_CTRL_WHICH_REQUEST_VAL;
+	ctrls.request_fd = req_fd;
+	if (ioctl(codec_fd, VIDIOC_S_EXT_CTRLS, &ctrls))
+		return errno;
+	...
+	buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+	buf.flags |= V4L2_BUF_FLAG_REQUEST_FD;
+	buf.request_fd = req_fd;
+	if (ioctl(codec_fd, VIDIOC_QBUF, &buf))
+		return errno;
+
+Note that there is typically no need to use the Request API for CAPTURE buffers
+since there are no per-frame settings to report there.
+
+Once the request is fully prepared, it can be queued to the driver:
+
+.. code-block:: c
+
+	if (ioctl(req_fd, MEDIA_REQUEST_IOC_QUEUE))
+		return errno;
+
+User-space can then either wait for the request to complete by calling poll() on
+its file descriptor, or start dequeuing CAPTURE buffers. Most likely, it will
+want to get CAPTURE buffers as soon as possible and this can be done using a
+regular DQBUF:
+
+.. code-block:: c
+
+	struct v4l2_buffer buf;
+
+	memset(&buf, 0, sizeof(buf));
+	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	if (ioctl(codec_fd, VIDIOC_DQBUF, &buf))
+		return errno;
+
+Note that this example assumes for simplicity that for every OUTPUT buffer
+there will be one CAPTURE buffer, but this does not have to be the case.
+
+We can then, after ensuring that the request is completed via polling the
+request FD, query control values at the time of its completion via a
+call to :ref:`VIDIOC_G_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>`.
+This is particularly useful for volatile controls for which we want to
+query values as soon as the capture buffer is produced.
+
+.. code-block:: c
+
+	struct pollfd pfd = { .events = POLLPRI, .fd = req_fd };
+	poll(&pfd, 1, -1);
+	...
+	ctrls.which = V4L2_CTRL_WHICH_REQUEST_VAL;
+	ctrls.request_fd = req_fd;
+	if (ioctl(codec_fd, VIDIOC_G_EXT_CTRLS, &ctrls))
+		return errno;
+
+Once we don't need the request anymore, we can either recycle it for reuse with
+:ref:`MEDIA_REQUEST_IOC_REINIT`...
+
+.. code-block:: c
+
+	if (ioctl(req_fd, MEDIA_REQUEST_IOC_REINIT))
+		return errno;
+
+... or close its file descriptor to completely dispose of it.
+
+.. code-block:: c
+
+	close(req_fd);
+
+Example for a Simple Capture Device
+-----------------------------------
+
+With a simple capture device, requests can be used to specify controls to apply
+for a given CAPTURE buffer.
+
+.. code-block:: c
+
+	struct v4l2_buffer buf;
+	struct v4l2_ext_controls ctrls;
+	struct media_request_alloc alloc = { 0 };
+	int req_fd;
+	...
+	if (ioctl(media_fd, MEDIA_IOC_REQUEST_ALLOC, &alloc))
+		return errno;
+	req_fd = alloc.fd;
+	...
+	ctrls.which = V4L2_CTRL_WHICH_REQUEST_VAL;
+	ctrls.request_fd = req_fd;
+	if (ioctl(camera_fd, VIDIOC_S_EXT_CTRLS, &ctrls))
+		return errno;
+	...
+	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	buf.flags |= V4L2_BUF_FLAG_REQUEST_FD;
+	buf.request_fd = req_fd;
+	if (ioctl(camera_fd, VIDIOC_QBUF, &buf))
+		return errno;
+
+Once the request is fully prepared, it can be queued to the driver:
+
+.. code-block:: c
+
+	if (ioctl(req_fd, MEDIA_REQUEST_IOC_QUEUE))
+		return errno;
+
+User-space can then dequeue buffers, wait for the request completion, query
+controls and recycle the request as in the M2M example above.
diff --git a/Documentation/media/uapi/mediactl/request-func-close.rst b/Documentation/media/uapi/mediactl/request-func-close.rst
new file mode 100644
index 000000000000..2dd9958f9b0e
--- /dev/null
+++ b/Documentation/media/uapi/mediactl/request-func-close.rst
@@ -0,0 +1,49 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. -*- coding: utf-8; mode: rst -*-
+
+.. _request-func-close:
+
+***************
+request close()
+***************
+
+Name
+====
+
+request-close - Close a request file descriptor
+
+
+Synopsis
+========
+
+.. code-block:: c
+
+    #include <unistd.h>
+
+
+.. c:function:: int close( int fd )
+    :name: req-close
+
+Arguments
+=========
+
+``fd``
+    File descriptor returned by :ref:`MEDIA_IOC_REQUEST_ALLOC`.
+
+
+Description
+===========
+
+Closes the request file descriptor. Resources associated with the request
+are freed once all file descriptors associated with the request are closed
+and the driver has completed the request.
+
+
+Return Value
+============
+
+:ref:`close() <request-func-close>` returns 0 on success. On error, -1 is
+returned, and ``errno`` is set appropriately. Possible error codes are:
+
+EBADF
+    ``fd`` is not a valid open file descriptor.
diff --git a/Documentation/media/uapi/mediactl/request-func-ioctl.rst b/Documentation/media/uapi/mediactl/request-func-ioctl.rst
new file mode 100644
index 000000000000..33cadfd6a90b
--- /dev/null
+++ b/Documentation/media/uapi/mediactl/request-func-ioctl.rst
@@ -0,0 +1,68 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. -*- coding: utf-8; mode: rst -*-
+
+.. _request-func-ioctl:
+
+***************
+request ioctl()
+***************
+
+Name
+====
+
+request-ioctl - Control a request file descriptor
+
+
+Synopsis
+========
+
+.. code-block:: c
+
+    #include <sys/ioctl.h>
+
+
+.. c:function:: int ioctl( int fd, int cmd, void *argp )
+    :name: req-ioctl
+
+Arguments
+=========
+
+``fd``
+    File descriptor returned by :ref:`MEDIA_IOC_REQUEST_ALLOC`.
+
+``cmd``
+    The request ioctl command code as defined in the media.h header file, for
+    example :ref:`MEDIA_REQUEST_IOC_QUEUE`.
+
+``argp``
+    Pointer to a request-specific structure.
+
+
+Description
+===========
+
+The :ref:`ioctl() <request-func-ioctl>` function manipulates request
+parameters. The argument ``fd`` must be an open file descriptor.
+
+The ioctl ``cmd`` code specifies the request function to be called. It
+has encoded in it whether the argument is an input, output or read/write
+parameter, and the size of the argument ``argp`` in bytes.
+
+Macros and structures definitions specifying request ioctl commands and
+their parameters are located in the media.h header file. All request ioctl
+commands, their respective function and parameters are specified in
+:ref:`media-user-func`.
+
+
+Return Value
+============
+
+On success 0 is returned, on error -1 and the ``errno`` variable is set
+appropriately. The generic error codes are described at the
+:ref:`Generic Error Codes <gen-errors>` chapter.
+
+Command-specific error codes are listed in the individual command
+descriptions.
+
+When an ioctl that takes an output or read/write parameter fails, the
+parameter remains unmodified.
diff --git a/Documentation/media/uapi/mediactl/request-func-poll.rst b/Documentation/media/uapi/mediactl/request-func-poll.rst
new file mode 100644
index 000000000000..206d8660d25a
--- /dev/null
+++ b/Documentation/media/uapi/mediactl/request-func-poll.rst
@@ -0,0 +1,77 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. -*- coding: utf-8; mode: rst -*-
+
+.. _request-func-poll:
+
+**************
+request poll()
+**************
+
+Name
+====
+
+request-poll - Wait for some event on a file descriptor
+
+
+Synopsis
+========
+
+.. code-block:: c
+
+    #include <sys/poll.h>
+
+
+.. c:function:: int poll( struct pollfd *ufds, unsigned int nfds, int timeout )
+   :name: request-poll
+
+Arguments
+=========
+
+``ufds``
+   List of FD events to be watched
+
+``nfds``
+   Number of FD events at the \*ufds array
+
+``timeout``
+   Timeout to wait for events
+
+
+Description
+===========
+
+With the :c:func:`poll() <request-func-poll>` function applications can wait
+for a request to complete.
+
+On success :c:func:`poll() <request-func-poll>` returns the number of file
+descriptors that have been selected (that is, file descriptors for which the
+``revents`` field of the respective struct :c:type:`pollfd`
+is non-zero). Request file descriptor set the ``POLLPRI`` flag in ``revents``
+when the request was completed.  When the function times out it returns
+a value of zero, on failure it returns -1 and the ``errno`` variable is
+set appropriately.
+
+Attempting to poll for a request that is completed or not yet queued will
+set the ``POLLERR`` flag in ``revents``.
+
+
+Return Value
+============
+
+On success, :c:func:`poll() <request-func-poll>` returns the number of
+structures which have non-zero ``revents`` fields, or zero if the call
+timed out. On error -1 is returned, and the ``errno`` variable is set
+appropriately:
+
+``EBADF``
+    One or more of the ``ufds`` members specify an invalid file
+    descriptor.
+
+``EFAULT``
+    ``ufds`` references an inaccessible memory area.
+
+``EINTR``
+    The call was interrupted by a signal.
+
+``EINVAL``
+    The ``nfds`` argument is greater than ``OPEN_MAX``.
diff --git a/Documentation/media/uapi/v4l/buffer.rst b/Documentation/media/uapi/v4l/buffer.rst
index e2c85ddc990b..dd0065a95ea0 100644
--- a/Documentation/media/uapi/v4l/buffer.rst
+++ b/Documentation/media/uapi/v4l/buffer.rst
@@ -306,10 +306,15 @@ struct v4l2_buffer
       - A place holder for future extensions. Drivers and applications
 	must set this to 0.
     * - __u32
-      - ``reserved``
+      - ``request_fd``
       -
-      - A place holder for future extensions. Drivers and applications
-	must set this to 0.
+      - The file descriptor of the request to queue the buffer to. If specified
+        and flag ``V4L2_BUF_FLAG_REQUEST_FD`` is set, then the buffer will be
+	queued to that request. This is set by the user when calling
+	:ref:`ioctl VIDIOC_QBUF <VIDIOC_QBUF>` and ignored by other ioctls.
+	If the device does not support requests, then ``EPERM`` will be returned.
+	If requests are supported but an invalid request FD is given, then
+	``ENOENT`` will be returned.
 
 
 
@@ -514,6 +519,11 @@ Buffer Flags
 	streaming may continue as normal and the buffer may be reused
 	normally. Drivers set this flag when the ``VIDIOC_DQBUF`` ioctl is
 	called.
+    * .. _`V4L2-BUF-FLAG-IN-REQUEST`:
+
+      - ``V4L2_BUF_FLAG_IN_REQUEST``
+      - 0x00000080
+      - This buffer is part of a request that hasn't been queued yet.
     * .. _`V4L2-BUF-FLAG-KEYFRAME`:
 
       - ``V4L2_BUF_FLAG_KEYFRAME``
@@ -589,6 +599,11 @@ Buffer Flags
 	the format. Any Any subsequent call to the
 	:ref:`VIDIOC_DQBUF <VIDIOC_QBUF>` ioctl will not block anymore,
 	but return an ``EPIPE`` error code.
+    * .. _`V4L2-BUF-FLAG-REQUEST-FD`:
+
+      - ``V4L2_BUF_FLAG_REQUEST_FD``
+      - 0x00800000
+      - The ``request_fd`` field contains a valid file descriptor.
     * .. _`V4L2-BUF-FLAG-TIMESTAMP-MASK`:
 
       - ``V4L2_BUF_FLAG_TIMESTAMP_MASK``
diff --git a/Documentation/media/uapi/v4l/vidioc-g-ext-ctrls.rst b/Documentation/media/uapi/v4l/vidioc-g-ext-ctrls.rst
index 2011c2b2ee67..c592074be273 100644
--- a/Documentation/media/uapi/v4l/vidioc-g-ext-ctrls.rst
+++ b/Documentation/media/uapi/v4l/vidioc-g-ext-ctrls.rst
@@ -95,6 +95,26 @@ appropriate. In the first case the new value is set in struct
 is inappropriate (e.g. the given menu index is not supported by the menu
 control), then this will also result in an ``EINVAL`` error code error.
 
+If ``request_fd`` is set to a not-yet-queued :ref:`request <media-request-api>`
+file descriptor and ``which`` is set to ``V4L2_CTRL_WHICH_REQUEST_VAL``,
+then the controls are not applied immediately when calling
+:ref:`VIDIOC_S_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>`, but instead are applied by
+the driver for the buffer associated with the same request.
+If the device does not support requests, then ``EPERM`` will be returned.
+If requests are supported but an invalid request FD is given, then
+``ENOENT`` will be returned.
+
+An attempt to call :ref:`VIDIOC_S_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` for a
+request that has already been queued will result in an ``EBUSY`` error.
+
+If ``request_fd`` is specified and ``which`` is set to ``V4L2_CTRL_WHICH_REQUEST_VAL``
+during a call to :ref:`VIDIOC_G_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>`, then the
+returned values will be the values currently set for the request (or the
+hardware value if none is set) if the request has not yet been queued, or the
+values of the controls at the time of request completion if it has already
+completed. Attempting to get controls while the request has been queued but
+not yet completed will result in an ``EBUSY`` error.
+
 The driver will only set/get these controls if all control values are
 correct. This prevents the situation where only some of the controls
 were set/get. Only low-level errors (e. g. a failed i2c command) can
@@ -110,15 +130,13 @@ still cause this situation.
 .. flat-table:: struct v4l2_ext_control
     :header-rows:  0
     :stub-columns: 0
-    :widths:       1 1 1 2
+    :widths:       1 1 3
 
     * - __u32
       - ``id``
-      -
       - Identifies the control, set by the application.
     * - __u32
       - ``size``
-      -
       - The total size in bytes of the payload of this control. This is
 	normally 0, but for pointer controls this should be set to the
 	size of the memory containing the payload, or that will receive
@@ -135,51 +153,43 @@ still cause this situation.
 	   *length* of the string may well be much smaller.
     * - __u32
       - ``reserved2``\ [1]
-      -
       - Reserved for future extensions. Drivers and applications must set
 	the array to zero.
-    * - union
+    * - union {
       - (anonymous)
-    * -
-      - __s32
+    * - __s32
       - ``value``
       - New value or current value. Valid if this control is not of type
 	``V4L2_CTRL_TYPE_INTEGER64`` and ``V4L2_CTRL_FLAG_HAS_PAYLOAD`` is
 	not set.
-    * -
-      - __s64
+    * - __s64
       - ``value64``
       - New value or current value. Valid if this control is of type
 	``V4L2_CTRL_TYPE_INTEGER64`` and ``V4L2_CTRL_FLAG_HAS_PAYLOAD`` is
 	not set.
-    * -
-      - char *
+    * - char *
       - ``string``
       - A pointer to a string. Valid if this control is of type
 	``V4L2_CTRL_TYPE_STRING``.
-    * -
-      - __u8 *
+    * - __u8 *
       - ``p_u8``
       - A pointer to a matrix control of unsigned 8-bit values. Valid if
 	this control is of type ``V4L2_CTRL_TYPE_U8``.
-    * -
-      - __u16 *
+    * - __u16 *
       - ``p_u16``
       - A pointer to a matrix control of unsigned 16-bit values. Valid if
 	this control is of type ``V4L2_CTRL_TYPE_U16``.
-    * -
-      - __u32 *
+    * - __u32 *
       - ``p_u32``
       - A pointer to a matrix control of unsigned 32-bit values. Valid if
 	this control is of type ``V4L2_CTRL_TYPE_U32``.
-    * -
-      - void *
+    * - void *
       - ``ptr``
       - A pointer to a compound type which can be an N-dimensional array
 	and/or a compound type (the control's type is >=
 	``V4L2_CTRL_COMPOUND_TYPES``). Valid if
 	``V4L2_CTRL_FLAG_HAS_PAYLOAD`` is set for this control.
-
+    * - }
 
 .. tabularcolumns:: |p{4.0cm}|p{2.2cm}|p{2.1cm}|p{8.2cm}|
 
@@ -190,12 +200,11 @@ still cause this situation.
 .. flat-table:: struct v4l2_ext_controls
     :header-rows:  0
     :stub-columns: 0
-    :widths:       1 1 2 1
+    :widths:       1 1 3
 
-    * - union
+    * - union {
       - (anonymous)
-    * -
-      - __u32
+    * - __u32
       - ``ctrl_class``
       - The control class to which all controls belong, see
 	:ref:`ctrl-class`. Drivers that use a kernel framework for
@@ -204,18 +213,21 @@ still cause this situation.
 	support this can be tested by setting ``ctrl_class`` to 0 and
 	calling :ref:`VIDIOC_TRY_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` with a ``count`` of 0. If that
 	succeeds, then the driver supports this feature.
-    * -
-      - __u32
+    * - __u32
       - ``which``
       - Which value of the control to get/set/try.
 	``V4L2_CTRL_WHICH_CUR_VAL`` will return the current value of the
-	control and ``V4L2_CTRL_WHICH_DEF_VAL`` will return the default
-	value of the control.
+	control, ``V4L2_CTRL_WHICH_DEF_VAL`` will return the default
+	value of the control and ``V4L2_CTRL_WHICH_REQUEST_VAL`` indicates that
+	these controls have to be retrieved from a request or tried/set for
+	a request. In the latter case the ``request_fd`` field contains the
+	file descriptor of the request that should be used. If the device
+	does not support requests, then ``EPERM`` will be returned.
 
 	.. note::
 
-	   You can only get the default value of the control,
-	   you cannot set or try it.
+	   When using ``V4L2_CTRL_WHICH_DEF_VAL`` note that You can only
+	   get the default value of the control, you cannot set or try it.
 
 	For backwards compatibility you can also use a control class here
 	(see :ref:`ctrl-class`). In that case all controls have to
@@ -226,6 +238,7 @@ still cause this situation.
 	by setting ctrl_class to ``V4L2_CTRL_WHICH_CUR_VAL`` and calling
 	VIDIOC_TRY_EXT_CTRLS with a count of 0. If that fails, then the
 	driver does not support ``V4L2_CTRL_WHICH_CUR_VAL``.
+    * - }
     * - __u32
       - ``count``
       - The number of controls in the controls array. May also be zero.
@@ -272,8 +285,15 @@ still cause this situation.
 	then you can call :ref:`VIDIOC_TRY_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` to try to discover the
 	actual control that failed the validation step. Unfortunately,
 	there is no ``TRY`` equivalent for :ref:`VIDIOC_G_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>`.
+    * - __s32
+      - ``request_fd``
+      - File descriptor of the request to be used by this operation. Only
+	valid if ``which`` is set to ``V4L2_CTRL_WHICH_REQUEST_VAL``.
+	If the device does not support requests, then ``EPERM`` will be returned.
+	If requests are supported but an invalid request FD is given, then
+	``ENOENT`` will be returned.
     * - __u32
-      - ``reserved``\ [2]
+      - ``reserved``\ [1]
       - Reserved for future extensions.
 
 	Drivers and applications must set the array to zero.
@@ -362,7 +382,9 @@ ERANGE
 EBUSY
     The control is temporarily not changeable, possibly because another
     applications took over control of the device function this control
-    belongs to.
+    belongs to, or (if the ``which`` field was set to
+    ``V4L2_CTRL_WHICH_REQUEST_VAL``) the request was queued but not yet
+    completed.
 
 ENOSPC
     The space reserved for the control's payload is insufficient. The
@@ -372,3 +394,11 @@ ENOSPC
 EACCES
     Attempt to try or set a read-only control or to get a write-only
     control.
+
+EPERM
+    The ``which`` field was set to ``V4L2_CTRL_WHICH_REQUEST_VAL`` but the
+    device does not support requests.
+
+ENOENT
+    The ``which`` field was set to ``V4L2_CTRL_WHICH_REQUEST_VAL`` but the
+    the given ``request_fd`` was invalid.
diff --git a/Documentation/media/uapi/v4l/vidioc-qbuf.rst b/Documentation/media/uapi/v4l/vidioc-qbuf.rst
index 9e448a4aa3aa..0e415f2551b2 100644
--- a/Documentation/media/uapi/v4l/vidioc-qbuf.rst
+++ b/Documentation/media/uapi/v4l/vidioc-qbuf.rst
@@ -65,7 +65,7 @@ To enqueue a :ref:`memory mapped <mmap>` buffer applications set the
 with a pointer to this structure the driver sets the
 ``V4L2_BUF_FLAG_MAPPED`` and ``V4L2_BUF_FLAG_QUEUED`` flags and clears
 the ``V4L2_BUF_FLAG_DONE`` flag in the ``flags`` field, or it returns an
-EINVAL error code.
+``EINVAL`` error code.
 
 To enqueue a :ref:`user pointer <userp>` buffer applications set the
 ``memory`` field to ``V4L2_MEMORY_USERPTR``, the ``m.userptr`` field to
@@ -98,6 +98,25 @@ dequeued, until the :ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` or
 :ref:`VIDIOC_REQBUFS` ioctl is called, or until the
 device is closed.
 
+The ``request_fd`` field can be used with the ``VIDIOC_QBUF`` ioctl to specify
+the file descriptor of a :ref:`request <media-request-api>`, if requests are
+in use. Setting it means that the buffer will not be passed to the driver
+until the request itself is queued. Also, the driver will apply any
+settings associated with the request for this buffer. This field will
+be ignored unless the ``V4L2_BUF_FLAG_REQUEST_FD`` flag is set.
+If the device does not support requests, then ``EPERM`` will be returned.
+If requests are supported but an invalid request FD is given, then
+``ENOENT`` will be returned.
+
+.. caution::
+   It is not allowed to mix queuing requests with queuing buffers directly.
+   ``EPERM`` will be returned if the first buffer was queued directly and
+   then the application tries to queue a request, or vice versa.
+
+   For :ref:`memory-to-memory devices <codec>` you can specify the
+   ``request_fd`` only for output buffers, not for capture buffers. Attempting
+   to specify this for a capture buffer will result in an ``EPERM`` error.
+
 Applications call the ``VIDIOC_DQBUF`` ioctl to dequeue a filled
 (capturing) or displayed (output) buffer from the driver's outgoing
 queue. They just set the ``type``, ``memory`` and ``reserved`` fields of
@@ -153,3 +172,14 @@ EPIPE
     ``VIDIOC_DQBUF`` returns this on an empty capture queue for mem2mem
     codecs if a buffer with the ``V4L2_BUF_FLAG_LAST`` was already
     dequeued and no new buffers are expected to become available.
+
+EPERM
+    The ``V4L2_BUF_FLAG_REQUEST_FD`` flag was set but the device does not
+    support requests. Or the first buffer was queued via a request, but
+    the application now tries to queue it directly, or vice versa (it is
+    not permitted to mix the two APIs). Or an attempt is made to queue a
+    CAPTURE buffer to a request for a :ref:`memory-to-memory device <codec>`.
+
+ENOENT
+    The ``V4L2_BUF_FLAG_REQUEST_FD`` flag was set but the the given
+    ``request_fd`` was invalid.
diff --git a/Documentation/media/videodev2.h.rst.exceptions b/Documentation/media/videodev2.h.rst.exceptions
index ca9f0edc579e..99256a2c003e 100644
--- a/Documentation/media/videodev2.h.rst.exceptions
+++ b/Documentation/media/videodev2.h.rst.exceptions
@@ -514,6 +514,7 @@ ignore define V4L2_CTRL_DRIVER_PRIV
 ignore define V4L2_CTRL_MAX_DIMS
 ignore define V4L2_CTRL_WHICH_CUR_VAL
 ignore define V4L2_CTRL_WHICH_DEF_VAL
+ignore define V4L2_CTRL_WHICH_REQUEST_VAL
 ignore define V4L2_OUT_CAP_CUSTOM_TIMINGS
 ignore define V4L2_CID_MAX_CTRLS
 
-- 
2.18.0

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

* [PATCHv17 02/34] uapi/linux/media.h: add request API
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
  2018-08-04 12:44 ` [PATCHv17 01/34] Documentation: v4l: document request API Hans Verkuil
@ 2018-08-04 12:44 ` Hans Verkuil
  2018-08-09 17:53   ` Mauro Carvalho Chehab
  2018-08-04 12:44 ` [PATCHv17 03/34] media-request: implement media requests Hans Verkuil
                   ` (31 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:44 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

Define the public request API.

This adds the new MEDIA_IOC_REQUEST_ALLOC ioctl to allocate a request
and two ioctls that operate on a request in order to queue the
contents of the request to the driver and to re-initialize the
request.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 include/uapi/linux/media.h | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h
index 36f76e777ef9..cf77f00a0f2d 100644
--- a/include/uapi/linux/media.h
+++ b/include/uapi/linux/media.h
@@ -364,11 +364,23 @@ struct media_v2_topology {
 
 /* ioctls */
 
+struct __attribute__ ((packed)) media_request_alloc {
+	__s32 fd;
+};
+
 #define MEDIA_IOC_DEVICE_INFO	_IOWR('|', 0x00, struct media_device_info)
 #define MEDIA_IOC_ENUM_ENTITIES	_IOWR('|', 0x01, struct media_entity_desc)
 #define MEDIA_IOC_ENUM_LINKS	_IOWR('|', 0x02, struct media_links_enum)
 #define MEDIA_IOC_SETUP_LINK	_IOWR('|', 0x03, struct media_link_desc)
 #define MEDIA_IOC_G_TOPOLOGY	_IOWR('|', 0x04, struct media_v2_topology)
+#define MEDIA_IOC_REQUEST_ALLOC	_IOWR('|', 0x05, struct media_request_alloc)
+
+/*
+ * These ioctls are called on the request file descriptor as returned
+ * by MEDIA_IOC_REQUEST_ALLOC.
+ */
+#define MEDIA_REQUEST_IOC_QUEUE		_IO('|',  0x80)
+#define MEDIA_REQUEST_IOC_REINIT	_IO('|',  0x81)
 
 #ifndef __KERNEL__
 
-- 
2.18.0

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

* [PATCHv17 03/34] media-request: implement media requests
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
  2018-08-04 12:44 ` [PATCHv17 01/34] Documentation: v4l: document request API Hans Verkuil
  2018-08-04 12:44 ` [PATCHv17 02/34] uapi/linux/media.h: add " Hans Verkuil
@ 2018-08-04 12:44 ` Hans Verkuil
  2018-08-09 18:37   ` Mauro Carvalho Chehab
  2018-08-14 13:33   ` Hans Verkuil
  2018-08-04 12:44 ` [PATCHv17 04/34] media: doc: Add media-request.h header to documentation build Hans Verkuil
                   ` (30 subsequent siblings)
  33 siblings, 2 replies; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:44 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil, Sakari Ailus

From: Hans Verkuil <hans.verkuil@cisco.com>

Add initial media request support:

1) Add MEDIA_IOC_REQUEST_ALLOC ioctl support to media-device.c
2) Add struct media_request to store request objects.
3) Add struct media_request_object to represent a request object.
4) Add MEDIA_REQUEST_IOC_QUEUE/REINIT ioctl support.

Basic lifecycle: the application allocates a request, adds
objects to it, queues the request, polls until it is completed
and can then read the final values of the objects at the time
of completion. When it closes the file descriptor the request
memory will be freed (actually, when the last user of that request
releases the request).

Drivers will bind an object to a request (the 'adds objects to it'
phase), when MEDIA_REQUEST_IOC_QUEUE is called the request is
validated (req_validate op), then queued (the req_queue op).

When done with an object it can either be unbound from the request
(e.g. when the driver has finished with a vb2 buffer) or marked as
completed (e.g. for controls associated with a buffer). When all
objects in the request are completed (or unbound), then the request
fd will signal an exception (poll).

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Co-developed-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Co-developed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Co-developed-by: Alexandre Courbot <acourbot@chromium.org>
---
 drivers/media/Makefile        |   3 +-
 drivers/media/media-device.c  |  14 ++
 drivers/media/media-request.c | 423 ++++++++++++++++++++++++++++++++++
 include/media/media-device.h  |  21 ++
 include/media/media-request.h | 327 ++++++++++++++++++++++++++
 5 files changed, 787 insertions(+), 1 deletion(-)
 create mode 100644 drivers/media/media-request.c
 create mode 100644 include/media/media-request.h

diff --git a/drivers/media/Makefile b/drivers/media/Makefile
index 594b462ddf0e..985d35ec6b29 100644
--- a/drivers/media/Makefile
+++ b/drivers/media/Makefile
@@ -3,7 +3,8 @@
 # Makefile for the kernel multimedia device drivers.
 #
 
-media-objs	:= media-device.o media-devnode.o media-entity.o
+media-objs	:= media-device.o media-devnode.o media-entity.o \
+		   media-request.o
 
 #
 # I2C drivers should come before other drivers, otherwise they'll fail
diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
index fcdf3d5dc4b6..4b9a8de05562 100644
--- a/drivers/media/media-device.c
+++ b/drivers/media/media-device.c
@@ -30,6 +30,7 @@
 #include <media/media-device.h>
 #include <media/media-devnode.h>
 #include <media/media-entity.h>
+#include <media/media-request.h>
 
 #ifdef CONFIG_MEDIA_CONTROLLER
 
@@ -377,6 +378,15 @@ static long media_device_get_topology(struct media_device *mdev, void *arg)
 	return ret;
 }
 
+static long media_device_request_alloc(struct media_device *mdev,
+				       struct media_request_alloc *alloc)
+{
+	if (!mdev->ops || !mdev->ops->req_validate || !mdev->ops->req_queue)
+		return -ENOTTY;
+
+	return media_request_alloc(mdev, alloc);
+}
+
 static long copy_arg_from_user(void *karg, void __user *uarg, unsigned int cmd)
 {
 	/* All media IOCTLs are _IOWR() */
@@ -425,6 +435,7 @@ static const struct media_ioctl_info ioctl_info[] = {
 	MEDIA_IOC(ENUM_LINKS, media_device_enum_links, MEDIA_IOC_FL_GRAPH_MUTEX),
 	MEDIA_IOC(SETUP_LINK, media_device_setup_link, MEDIA_IOC_FL_GRAPH_MUTEX),
 	MEDIA_IOC(G_TOPOLOGY, media_device_get_topology, MEDIA_IOC_FL_GRAPH_MUTEX),
+	MEDIA_IOC(REQUEST_ALLOC, media_device_request_alloc, 0),
 };
 
 static long media_device_ioctl(struct file *filp, unsigned int cmd,
@@ -697,6 +708,8 @@ void media_device_init(struct media_device *mdev)
 	INIT_LIST_HEAD(&mdev->pads);
 	INIT_LIST_HEAD(&mdev->links);
 	INIT_LIST_HEAD(&mdev->entity_notify);
+
+	mutex_init(&mdev->req_queue_mutex);
 	mutex_init(&mdev->graph_mutex);
 	ida_init(&mdev->entity_internal_idx);
 
@@ -710,6 +723,7 @@ void media_device_cleanup(struct media_device *mdev)
 	mdev->entity_internal_idx_max = 0;
 	media_graph_walk_cleanup(&mdev->pm_count_walk);
 	mutex_destroy(&mdev->graph_mutex);
+	mutex_destroy(&mdev->req_queue_mutex);
 }
 EXPORT_SYMBOL_GPL(media_device_cleanup);
 
diff --git a/drivers/media/media-request.c b/drivers/media/media-request.c
new file mode 100644
index 000000000000..253068f51a1f
--- /dev/null
+++ b/drivers/media/media-request.c
@@ -0,0 +1,423 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Media device request objects
+ *
+ * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ * Copyright (C) 2018 Intel Corporation
+ * Copyright (C) 2018 Google, Inc.
+ *
+ * Author: Hans Verkuil <hans.verkuil@cisco.com>
+ * Author: Sakari Ailus <sakari.ailus@linux.intel.com>
+ */
+
+#include <linux/anon_inodes.h>
+#include <linux/file.h>
+#include <linux/refcount.h>
+
+#include <media/media-device.h>
+#include <media/media-request.h>
+
+static const char * const request_state[] = {
+	[MEDIA_REQUEST_STATE_IDLE]	 = "idle",
+	[MEDIA_REQUEST_STATE_VALIDATING] = "validating",
+	[MEDIA_REQUEST_STATE_QUEUED]	 = "queued",
+	[MEDIA_REQUEST_STATE_COMPLETE]	 = "complete",
+	[MEDIA_REQUEST_STATE_CLEANING]	 = "cleaning",
+	[MEDIA_REQUEST_STATE_UPDATING]	 = "updating",
+};
+
+static const char *
+media_request_state_str(enum media_request_state state)
+{
+	BUILD_BUG_ON(ARRAY_SIZE(request_state) != NR_OF_MEDIA_REQUEST_STATE);
+
+	if (WARN_ON(state >= ARRAY_SIZE(request_state)))
+		return "invalid";
+	return request_state[state];
+}
+
+static void media_request_clean(struct media_request *req)
+{
+	struct media_request_object *obj, *obj_safe;
+
+	/* Just a sanity check. No other code path is allowed to change this. */
+	WARN_ON(req->state != MEDIA_REQUEST_STATE_CLEANING);
+	WARN_ON(req->updating_count);
+
+	list_for_each_entry_safe(obj, obj_safe, &req->objects, list) {
+		media_request_object_unbind(obj);
+		media_request_object_put(obj);
+	}
+
+	req->updating_count = 0;
+	WARN_ON(req->num_incomplete_objects);
+	req->num_incomplete_objects = 0;
+	wake_up_interruptible_all(&req->poll_wait);
+}
+
+static void media_request_release(struct kref *kref)
+{
+	struct media_request *req =
+		container_of(kref, struct media_request, kref);
+	struct media_device *mdev = req->mdev;
+
+	dev_dbg(mdev->dev, "request: release %s\n", req->debug_str);
+
+	/* No other users, no need for a spinlock */
+	req->state = MEDIA_REQUEST_STATE_CLEANING;
+
+	media_request_clean(req);
+
+	if (mdev->ops->req_free)
+		mdev->ops->req_free(req);
+	else
+		kfree(req);
+}
+
+void media_request_put(struct media_request *req)
+{
+	kref_put(&req->kref, media_request_release);
+}
+EXPORT_SYMBOL_GPL(media_request_put);
+
+static int media_request_close(struct inode *inode, struct file *filp)
+{
+	struct media_request *req = filp->private_data;
+
+	media_request_put(req);
+	return 0;
+}
+
+static unsigned int media_request_poll(struct file *filp,
+				       struct poll_table_struct *wait)
+{
+	struct media_request *req = filp->private_data;
+	unsigned long flags;
+	unsigned int ret = 0;
+
+	if (!(poll_requested_events(wait) & POLLPRI))
+		return 0;
+
+	spin_lock_irqsave(&req->lock, flags);
+	if (req->state == MEDIA_REQUEST_STATE_COMPLETE) {
+		ret = POLLPRI;
+		goto unlock;
+	}
+	if (req->state != MEDIA_REQUEST_STATE_QUEUED) {
+		ret = POLLERR;
+		goto unlock;
+	}
+
+	poll_wait(filp, &req->poll_wait, wait);
+
+unlock:
+	spin_unlock_irqrestore(&req->lock, flags);
+	return ret;
+}
+
+static long media_request_ioctl_queue(struct media_request *req)
+{
+	struct media_device *mdev = req->mdev;
+	enum media_request_state state;
+	unsigned long flags;
+	int ret;
+
+	dev_dbg(mdev->dev, "request: queue %s\n", req->debug_str);
+
+	/*
+	 * Ensure the request that is validated will be the one that gets queued
+	 * next by serialising the queueing process. This mutex is also used
+	 * to serialize with canceling a vb2 queue and with setting values such
+	 * as controls in a request.
+	 */
+	mutex_lock(&mdev->req_queue_mutex);
+
+	media_request_get(req);
+
+	spin_lock_irqsave(&req->lock, flags);
+	if (req->state == MEDIA_REQUEST_STATE_IDLE)
+		req->state = MEDIA_REQUEST_STATE_VALIDATING;
+	state = req->state;
+	spin_unlock_irqrestore(&req->lock, flags);
+	if (state != MEDIA_REQUEST_STATE_VALIDATING) {
+		dev_dbg(mdev->dev,
+			"request: unable to queue %s, request in state %s\n",
+			req->debug_str, media_request_state_str(state));
+		mutex_unlock(&mdev->req_queue_mutex);
+		return -EBUSY;
+	}
+
+	ret = mdev->ops->req_validate(req);
+
+	/*
+	 * If the req_validate was successful, then we mark the state as QUEUED
+	 * and call req_queue. The reason we set the state first is that this
+	 * allows req_queue to unbind or complete the queued objects in case
+	 * they are immediately 'consumed'. State changes from QUEUED to another
+	 * state can only happen if either the driver changes the state or if
+	 * the user cancels the vb2 queue. The driver can only change the state
+	 * after each object is queued through the req_queue op (and note that
+	 * that op cannot fail), so setting the state to QUEUED up front is
+	 * safe.
+	 *
+	 * The other reason for changing the state is if the vb2 queue is
+	 * canceled, and that uses the req_queue_mutex which is still locked
+	 * while req_queue is called, so that's safe as well.
+	 */
+	spin_lock_irqsave(&req->lock, flags);
+	req->state = ret ? MEDIA_REQUEST_STATE_IDLE
+			 : MEDIA_REQUEST_STATE_QUEUED;
+	spin_unlock_irqrestore(&req->lock, flags);
+
+	if (!ret)
+		mdev->ops->req_queue(req);
+
+	mutex_unlock(&mdev->req_queue_mutex);
+
+	if (ret) {
+		dev_dbg(mdev->dev, "request: can't queue %s (%d)\n",
+			req->debug_str, ret);
+		media_request_put(req);
+	}
+
+	return ret;
+}
+
+static long media_request_ioctl_reinit(struct media_request *req)
+{
+	struct media_device *mdev = req->mdev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&req->lock, flags);
+	if (req->state != MEDIA_REQUEST_STATE_IDLE &&
+	    req->state != MEDIA_REQUEST_STATE_COMPLETE) {
+		dev_dbg(mdev->dev,
+			"request: %s not in idle or complete state, cannot reinit\n",
+			req->debug_str);
+		spin_unlock_irqrestore(&req->lock, flags);
+		return -EBUSY;
+	}
+	req->state = MEDIA_REQUEST_STATE_CLEANING;
+	spin_unlock_irqrestore(&req->lock, flags);
+
+	media_request_clean(req);
+
+	spin_lock_irqsave(&req->lock, flags);
+	req->state = MEDIA_REQUEST_STATE_IDLE;
+	spin_unlock_irqrestore(&req->lock, flags);
+
+	return 0;
+}
+
+static long media_request_ioctl(struct file *filp, unsigned int cmd,
+				unsigned long arg)
+{
+	struct media_request *req = filp->private_data;
+
+	switch (cmd) {
+	case MEDIA_REQUEST_IOC_QUEUE:
+		return media_request_ioctl_queue(req);
+	case MEDIA_REQUEST_IOC_REINIT:
+		return media_request_ioctl_reinit(req);
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+static const struct file_operations request_fops = {
+	.owner = THIS_MODULE,
+	.poll = media_request_poll,
+	.unlocked_ioctl = media_request_ioctl,
+	.release = media_request_close,
+};
+
+int media_request_alloc(struct media_device *mdev,
+			struct media_request_alloc *alloc)
+{
+	struct media_request *req;
+	struct file *filp;
+	char comm[TASK_COMM_LEN];
+	int fd;
+	int ret;
+
+	/* Either both are NULL or both are non-NULL */
+	if (WARN_ON(!mdev->ops->req_alloc ^ !mdev->ops->req_free))
+		return -ENOMEM;
+
+	fd = get_unused_fd_flags(O_CLOEXEC);
+	if (fd < 0)
+		return fd;
+
+	filp = anon_inode_getfile("request", &request_fops, NULL, O_CLOEXEC);
+	if (IS_ERR(filp)) {
+		ret = PTR_ERR(filp);
+		goto err_put_fd;
+	}
+
+	if (mdev->ops->req_alloc)
+		req = mdev->ops->req_alloc(mdev);
+	else
+		req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req) {
+		ret = -ENOMEM;
+		goto err_fput;
+	}
+
+	filp->private_data = req;
+	req->mdev = mdev;
+	req->state = MEDIA_REQUEST_STATE_IDLE;
+	req->num_incomplete_objects = 0;
+	kref_init(&req->kref);
+	INIT_LIST_HEAD(&req->objects);
+	spin_lock_init(&req->lock);
+	init_waitqueue_head(&req->poll_wait);
+	req->updating_count = 0;
+
+	alloc->fd = fd;
+
+	get_task_comm(comm, current);
+	snprintf(req->debug_str, sizeof(req->debug_str), "%s:%d",
+		 comm, fd);
+	dev_dbg(mdev->dev, "request: allocated %s\n", req->debug_str);
+
+	fd_install(fd, filp);
+
+	return 0;
+
+err_fput:
+	fput(filp);
+
+err_put_fd:
+	put_unused_fd(fd);
+
+	return ret;
+}
+
+static void media_request_object_release(struct kref *kref)
+{
+	struct media_request_object *obj =
+		container_of(kref, struct media_request_object, kref);
+	struct media_request *req = obj->req;
+
+	if (WARN_ON(req))
+		media_request_object_unbind(obj);
+	obj->ops->release(obj);
+}
+
+void media_request_object_put(struct media_request_object *obj)
+{
+	kref_put(&obj->kref, media_request_object_release);
+}
+EXPORT_SYMBOL_GPL(media_request_object_put);
+
+void media_request_object_init(struct media_request_object *obj)
+{
+	obj->ops = NULL;
+	obj->req = NULL;
+	obj->priv = NULL;
+	obj->completed = false;
+	INIT_LIST_HEAD(&obj->list);
+	kref_init(&obj->kref);
+}
+EXPORT_SYMBOL_GPL(media_request_object_init);
+
+int media_request_object_bind(struct media_request *req,
+			      const struct media_request_object_ops *ops,
+			      void *priv,
+			      struct media_request_object *obj)
+{
+	unsigned long flags;
+	int ret = -EBUSY;
+
+	if (WARN_ON(!ops->release))
+		return -EPERM;
+
+	spin_lock_irqsave(&req->lock, flags);
+
+	if (WARN_ON(req->state != MEDIA_REQUEST_STATE_UPDATING))
+		goto unlock;
+
+	obj->req = req;
+	obj->ops = ops;
+	obj->priv = priv;
+
+	list_add_tail(&obj->list, &req->objects);
+	req->num_incomplete_objects++;
+	ret = 0;
+
+unlock:
+	spin_unlock_irqrestore(&req->lock, flags);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(media_request_object_bind);
+
+void media_request_object_unbind(struct media_request_object *obj)
+{
+	struct media_request *req = obj->req;
+	unsigned long flags;
+	bool completed = false;
+
+	if (WARN_ON(!req))
+		return;
+
+	spin_lock_irqsave(&req->lock, flags);
+	list_del(&obj->list);
+	obj->req = NULL;
+
+	if (req->state == MEDIA_REQUEST_STATE_COMPLETE)
+		goto unlock;
+
+	if (WARN_ON(req->state == MEDIA_REQUEST_STATE_VALIDATING))
+		goto unlock;
+
+	if (req->state == MEDIA_REQUEST_STATE_CLEANING) {
+		if (!obj->completed)
+			req->num_incomplete_objects--;
+		goto unlock;
+	}
+
+	if (WARN_ON(!req->num_incomplete_objects))
+		goto unlock;
+
+	req->num_incomplete_objects--;
+	if (req->state == MEDIA_REQUEST_STATE_QUEUED &&
+	    !req->num_incomplete_objects) {
+		req->state = MEDIA_REQUEST_STATE_COMPLETE;
+		completed = true;
+		wake_up_interruptible_all(&req->poll_wait);
+	}
+
+unlock:
+	spin_unlock_irqrestore(&req->lock, flags);
+	if (obj->ops->unbind)
+		obj->ops->unbind(obj);
+	if (completed)
+		media_request_put(req);
+}
+EXPORT_SYMBOL_GPL(media_request_object_unbind);
+
+void media_request_object_complete(struct media_request_object *obj)
+{
+	struct media_request *req = obj->req;
+	unsigned long flags;
+	bool completed = false;
+
+	spin_lock_irqsave(&req->lock, flags);
+	if (obj->completed)
+		goto unlock;
+	obj->completed = true;
+	if (WARN_ON(!req->num_incomplete_objects) ||
+	    WARN_ON(req->state != MEDIA_REQUEST_STATE_QUEUED))
+		goto unlock;
+
+	if (!--req->num_incomplete_objects) {
+		req->state = MEDIA_REQUEST_STATE_COMPLETE;
+		wake_up_interruptible_all(&req->poll_wait);
+		completed = true;
+	}
+unlock:
+	spin_unlock_irqrestore(&req->lock, flags);
+	if (completed)
+		media_request_put(req);
+}
+EXPORT_SYMBOL_GPL(media_request_object_complete);
diff --git a/include/media/media-device.h b/include/media/media-device.h
index bcc6ec434f1f..110b89567671 100644
--- a/include/media/media-device.h
+++ b/include/media/media-device.h
@@ -27,6 +27,7 @@
 
 struct ida;
 struct device;
+struct media_device;
 
 /**
  * struct media_entity_notify - Media Entity Notify
@@ -50,10 +51,26 @@ struct media_entity_notify {
  * struct media_device_ops - Media device operations
  * @link_notify: Link state change notification callback. This callback is
  *		 called with the graph_mutex held.
+ * @req_alloc: Allocate a request. Set this if you need to allocate a struct
+ *	       larger then struct media_request. @req_alloc and @req_free must
+ *	       either both be set or both be NULL.
+ * @req_free: Free a request. Set this if @req_alloc was set as well, leave
+ *	      to NULL otherwise.
+ * @req_validate: Validate a request, but do not queue yet. The req_queue_mutex
+ *	          lock is held when this op is called.
+ * @req_queue: Queue a validated request, cannot fail. If something goes
+ *	       wrong when queueing this request then it should be marked
+ *	       as such internally in the driver and any related buffers
+ *	       must eventually return to vb2 with state VB2_BUF_STATE_ERROR.
+ *	       The req_queue_mutex lock is held when this op is called.
  */
 struct media_device_ops {
 	int (*link_notify)(struct media_link *link, u32 flags,
 			   unsigned int notification);
+	struct media_request *(*req_alloc)(struct media_device *mdev);
+	void (*req_free)(struct media_request *req);
+	int (*req_validate)(struct media_request *req);
+	void (*req_queue)(struct media_request *req);
 };
 
 /**
@@ -88,6 +105,8 @@ struct media_device_ops {
  * @disable_source: Disable Source Handler function pointer
  *
  * @ops:	Operation handler callbacks
+ * @req_queue_mutex: Serialise the MEDIA_REQUEST_IOC_QUEUE ioctl w.r.t.
+ *		     other operations that stop or start streaming.
  *
  * This structure represents an abstract high-level media device. It allows easy
  * access to entities and provides basic media device-level support. The
@@ -158,6 +177,8 @@ struct media_device {
 	void (*disable_source)(struct media_entity *entity);
 
 	const struct media_device_ops *ops;
+
+	struct mutex req_queue_mutex;
 };
 
 /* We don't need to include pci.h or usb.h here */
diff --git a/include/media/media-request.h b/include/media/media-request.h
new file mode 100644
index 000000000000..fe5a04fb970c
--- /dev/null
+++ b/include/media/media-request.h
@@ -0,0 +1,327 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Media device request objects
+ *
+ * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ * Copyright (C) 2018 Intel Corporation
+ *
+ * Author: Hans Verkuil <hans.verkuil@cisco.com>
+ * Author: Sakari Ailus <sakari.ailus@linux.intel.com>
+ */
+
+#ifndef MEDIA_REQUEST_H
+#define MEDIA_REQUEST_H
+
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/refcount.h>
+
+#include <media/media-device.h>
+
+/**
+ * enum media_request_state - media request state
+ *
+ * @MEDIA_REQUEST_STATE_IDLE:		Idle
+ * @MEDIA_REQUEST_STATE_VALIDATING:	Validating the request, no state changes
+ *					allowed
+ * @MEDIA_REQUEST_STATE_QUEUED:		Queued
+ * @MEDIA_REQUEST_STATE_COMPLETE:	Completed, the request is done
+ * @MEDIA_REQUEST_STATE_CLEANING:	Cleaning, the request is being re-inited
+ * @MEDIA_REQUEST_STATE_UPDATING:	The request is being updated, i.e.
+ *					request objects are being added,
+ *					modified or removed
+ * @NR_OF_MEDIA_REQUEST_STATE:		The number of media request states, used
+ *					internally for sanity check purposes
+ */
+enum media_request_state {
+	MEDIA_REQUEST_STATE_IDLE,
+	MEDIA_REQUEST_STATE_VALIDATING,
+	MEDIA_REQUEST_STATE_QUEUED,
+	MEDIA_REQUEST_STATE_COMPLETE,
+	MEDIA_REQUEST_STATE_CLEANING,
+	MEDIA_REQUEST_STATE_UPDATING,
+	NR_OF_MEDIA_REQUEST_STATE,
+};
+
+struct media_request_object;
+
+/**
+ * struct media_request - Media device request
+ * @mdev: Media device this request belongs to
+ * @kref: Reference count
+ * @debug_str: Prefix for debug messages (process name:fd)
+ * @state: The state of the request
+ * @updating_count: count the number of request updates that are in progress
+ * @objects: List of @struct media_request_object request objects
+ * @num_incomplete_objects: The number of incomplete objects in the request
+ * @poll_wait: Wait queue for poll
+ * @lock: Serializes access to this struct
+ */
+struct media_request {
+	struct media_device *mdev;
+	struct kref kref;
+	char debug_str[TASK_COMM_LEN + 11];
+	enum media_request_state state;
+	unsigned int updating_count;
+	struct list_head objects;
+	unsigned int num_incomplete_objects;
+	struct wait_queue_head poll_wait;
+	spinlock_t lock;
+};
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+
+/**
+ * media_request_lock_for_update - Lock the request for updating its objects
+ *
+ * @req: The media request
+ *
+ * Use before updating a request, i.e. adding, modifying or removing a request
+ * object in it. A reference to the request must be held during the update. This
+ * usually takes place automatically through a file handle. Use
+ * @media_request_unlock_for_update when done.
+ */
+static inline int __must_check
+media_request_lock_for_update(struct media_request *req)
+{
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&req->lock, flags);
+	if (req->state == MEDIA_REQUEST_STATE_IDLE ||
+	    req->state == MEDIA_REQUEST_STATE_UPDATING) {
+		req->state = MEDIA_REQUEST_STATE_UPDATING;
+		req->updating_count++;
+	} else {
+		ret = -EBUSY;
+	}
+	spin_unlock_irqrestore(&req->lock, flags);
+
+	return ret;
+}
+
+/**
+ * media_request_unlock_for_update - Unlock a request previously locked for
+ *				     update
+ *
+ * @req: The media request
+ *
+ * Unlock a request that has previously been locked using
+ * @media_request_lock_for_update.
+ */
+static inline void media_request_unlock_for_update(struct media_request *req)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&req->lock, flags);
+	WARN_ON(req->updating_count <= 0);
+	if (!--req->updating_count)
+		req->state = MEDIA_REQUEST_STATE_IDLE;
+	spin_unlock_irqrestore(&req->lock, flags);
+}
+
+/**
+ * media_request_get - Get the media request
+ *
+ * @req: The media request
+ *
+ * Get the media request.
+ */
+static inline void media_request_get(struct media_request *req)
+{
+	kref_get(&req->kref);
+}
+
+/**
+ * media_request_put - Put the media request
+ *
+ * @req: The media request
+ *
+ * Put the media request. The media request will be released
+ * when the refcount reaches 0.
+ */
+void media_request_put(struct media_request *req);
+
+/**
+ * media_request_alloc - Allocate the media request
+ *
+ * @mdev: Media device this request belongs to
+ * @alloc: Store the request's file descriptor in this struct
+ *
+ * Allocated the media request and put the fd in @alloc->fd.
+ */
+int media_request_alloc(struct media_device *mdev,
+			struct media_request_alloc *alloc);
+
+#else
+
+static inline void media_request_get(struct media_request *req)
+{
+}
+
+static inline void media_request_put(struct media_request *req)
+{
+}
+
+#endif
+
+/**
+ * struct media_request_object_ops - Media request object operations
+ * @prepare: Validate and prepare the request object, optional.
+ * @unprepare: Unprepare the request object, optional.
+ * @queue: Queue the request object, optional.
+ * @unbind: Unbind the request object, optional.
+ * @release: Release the request object, required.
+ */
+struct media_request_object_ops {
+	int (*prepare)(struct media_request_object *object);
+	void (*unprepare)(struct media_request_object *object);
+	void (*queue)(struct media_request_object *object);
+	void (*unbind)(struct media_request_object *object);
+	void (*release)(struct media_request_object *object);
+};
+
+/**
+ * struct media_request_object - An opaque object that belongs to a media
+ *				 request
+ *
+ * @ops: object's operations
+ * @priv: object's priv pointer
+ * @req: the request this object belongs to (can be NULL)
+ * @list: List entry of the object for @struct media_request
+ * @kref: Reference count of the object, acquire before releasing req->lock
+ * @completed: If true, then this object was completed.
+ *
+ * An object related to the request. This struct is always embedded in
+ * another struct that contains the actual data for this request object.
+ */
+struct media_request_object {
+	const struct media_request_object_ops *ops;
+	void *priv;
+	struct media_request *req;
+	struct list_head list;
+	struct kref kref;
+	bool completed;
+};
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+
+/**
+ * media_request_object_get - Get a media request object
+ *
+ * @obj: The object
+ *
+ * Get a media request object.
+ */
+static inline void media_request_object_get(struct media_request_object *obj)
+{
+	kref_get(&obj->kref);
+}
+
+/**
+ * media_request_object_put - Put a media request object
+ *
+ * @obj: The object
+ *
+ * Put a media request object. Once all references are gone, the
+ * object's memory is released.
+ */
+void media_request_object_put(struct media_request_object *obj);
+
+/**
+ * media_request_object_init - Initialise a media request object
+ *
+ * @obj: The object
+ *
+ * Initialise a media request object. The object will be released using the
+ * release callback of the ops once it has no references (this function
+ * initialises references to one).
+ */
+void media_request_object_init(struct media_request_object *obj);
+
+/**
+ * media_request_object_bind - Bind a media request object to a request
+ *
+ * @req: The media request
+ * @ops: The object ops for this object
+ * @priv: A driver-specific priv pointer associated with this object
+ * @obj: The object
+ *
+ * Bind this object to the request and set the ops and priv values of
+ * the object so it can be found later with media_request_object_find().
+ *
+ * Every bound object must be unbound or completed by the kernel at some
+ * point in time, otherwise the request will never complete. When the
+ * request is released all completed objects will be unbound by the
+ * request core code.
+ */
+int media_request_object_bind(struct media_request *req,
+			      const struct media_request_object_ops *ops,
+			      void *priv,
+			      struct media_request_object *obj);
+
+/**
+ * media_request_object_unbind - Unbind a media request object
+ *
+ * @obj: The object
+ *
+ * Unbind the media request object from the request.
+ */
+void media_request_object_unbind(struct media_request_object *obj);
+
+/**
+ * media_request_object_complete - Mark the media request object as complete
+ *
+ * @obj: The object
+ *
+ * Mark the media request object as complete. Only bound objects can
+ * be completed.
+ */
+void media_request_object_complete(struct media_request_object *obj);
+
+#else
+
+static inline int __must_check
+media_request_lock_for_update(struct media_request *req)
+{
+	return -EINVAL;
+}
+
+static inline void media_request_unlock_for_update(struct media_request *req)
+{
+}
+
+static inline void media_request_object_get(struct media_request_object *obj)
+{
+}
+
+static inline void media_request_object_put(struct media_request_object *obj)
+{
+}
+
+static inline void media_request_object_init(struct media_request_object *obj)
+{
+	obj->ops = NULL;
+	obj->req = NULL;
+}
+
+static inline int media_request_object_bind(struct media_request *req,
+			       const struct media_request_object_ops *ops,
+			       void *priv,
+			       struct media_request_object *obj)
+{
+	return 0;
+}
+
+static inline void media_request_object_unbind(struct media_request_object *obj)
+{
+}
+
+static inline void media_request_object_complete(struct media_request_object *obj)
+{
+}
+
+#endif
+
+#endif
-- 
2.18.0

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

* [PATCHv17 04/34] media: doc: Add media-request.h header to documentation build
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (2 preceding siblings ...)
  2018-08-04 12:44 ` [PATCHv17 03/34] media-request: implement media requests Hans Verkuil
@ 2018-08-04 12:44 ` Hans Verkuil
  2018-08-09 18:43   ` Mauro Carvalho Chehab
  2018-08-04 12:44 ` [PATCHv17 05/34] media-request: add media_request_get_by_fd Hans Verkuil
                   ` (29 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:44 UTC (permalink / raw)
  To: linux-media; +Cc: Sakari Ailus, Hans Verkuil

From: Sakari Ailus <sakari.ailus@linux.intel.com>

media-request.h has been recently added; add it to the documentation build
as well.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 Documentation/media/kapi/mc-core.rst | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Documentation/media/kapi/mc-core.rst b/Documentation/media/kapi/mc-core.rst
index 0c05503eaf1f..69362b3135c2 100644
--- a/Documentation/media/kapi/mc-core.rst
+++ b/Documentation/media/kapi/mc-core.rst
@@ -262,3 +262,5 @@ in the end provide a way to use driver-specific callbacks.
 .. kernel-doc:: include/media/media-devnode.h
 
 .. kernel-doc:: include/media/media-entity.h
+
+.. kernel-doc:: include/media/media-request.h
-- 
2.18.0

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

* [PATCHv17 05/34] media-request: add media_request_get_by_fd
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (3 preceding siblings ...)
  2018-08-04 12:44 ` [PATCHv17 04/34] media: doc: Add media-request.h header to documentation build Hans Verkuil
@ 2018-08-04 12:44 ` Hans Verkuil
  2018-08-09 19:55   ` Mauro Carvalho Chehab
  2018-08-04 12:44 ` [PATCHv17 06/34] media-request: add media_request_object_find Hans Verkuil
                   ` (28 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:44 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

Add media_request_get_by_fd() to find a request based on the file
descriptor.

The caller has to call media_request_put() for the returned
request since this function increments the refcount.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/media-request.c | 40 +++++++++++++++++++++++++++++++++++
 include/media/media-request.h | 24 +++++++++++++++++++++
 2 files changed, 64 insertions(+)

diff --git a/drivers/media/media-request.c b/drivers/media/media-request.c
index 253068f51a1f..4b523f3a03a3 100644
--- a/drivers/media/media-request.c
+++ b/drivers/media/media-request.c
@@ -231,6 +231,46 @@ static const struct file_operations request_fops = {
 	.release = media_request_close,
 };
 
+struct media_request *
+media_request_get_by_fd(struct media_device *mdev, int request_fd)
+{
+	struct file *filp;
+	struct media_request *req;
+
+	if (!mdev || !mdev->ops ||
+	    !mdev->ops->req_validate || !mdev->ops->req_queue)
+		return ERR_PTR(-EPERM);
+
+	filp = fget(request_fd);
+	if (!filp)
+		return ERR_PTR(-ENOENT);
+
+	if (filp->f_op != &request_fops)
+		goto err_fput;
+	req = filp->private_data;
+	if (req->mdev != mdev)
+		goto err_fput;
+
+	/*
+	 * Note: as long as someone has an open filehandle of the request,
+	 * the request can never be released. The fget() above ensures that
+	 * even if userspace closes the request filehandle, the release()
+	 * fop won't be called, so the media_request_get() always succeeds
+	 * and there is no race condition where the request was released
+	 * before media_request_get() is called.
+	 */
+	media_request_get(req);
+	fput(filp);
+
+	return req;
+
+err_fput:
+	fput(filp);
+
+	return ERR_PTR(-ENOENT);
+}
+EXPORT_SYMBOL_GPL(media_request_get_by_fd);
+
 int media_request_alloc(struct media_device *mdev,
 			struct media_request_alloc *alloc)
 {
diff --git a/include/media/media-request.h b/include/media/media-request.h
index fe5a04fb970c..66ec9d09fcd8 100644
--- a/include/media/media-request.h
+++ b/include/media/media-request.h
@@ -143,6 +143,24 @@ static inline void media_request_get(struct media_request *req)
  */
 void media_request_put(struct media_request *req);
 
+/**
+ * media_request_get_by_fd - Get a media request by fd
+ *
+ * @mdev: Media device this request belongs to
+ * @request_fd: The file descriptor of the request
+ *
+ * Get the request represented by @request_fd that is owned
+ * by the media device.
+ *
+ * Return a -EPERM error pointer if requests are not supported
+ * by this driver. Return -ENOENT if the request was not found.
+ * Return the pointer to the request if found: the caller will
+ * have to call @media_request_put when it finished using the
+ * request.
+ */
+struct media_request *
+media_request_get_by_fd(struct media_device *mdev, int request_fd);
+
 /**
  * media_request_alloc - Allocate the media request
  *
@@ -164,6 +182,12 @@ static inline void media_request_put(struct media_request *req)
 {
 }
 
+static inline struct media_request *
+media_request_get_by_fd(struct media_device *mdev, int request_fd)
+{
+	return ERR_PTR(-EPERM);
+}
+
 #endif
 
 /**
-- 
2.18.0

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

* [PATCHv17 06/34] media-request: add media_request_object_find
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (4 preceding siblings ...)
  2018-08-04 12:44 ` [PATCHv17 05/34] media-request: add media_request_get_by_fd Hans Verkuil
@ 2018-08-04 12:44 ` Hans Verkuil
  2018-08-09 19:56   ` Mauro Carvalho Chehab
  2018-08-04 12:44 ` [PATCHv17 07/34] v4l2-device.h: add v4l2_device_supports_requests() helper Hans Verkuil
                   ` (27 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:44 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

Add media_request_object_find to find a request object inside a
request based on ops and priv values.

Objects of the same type (vb2 buffer, control handler) will have
the same ops value. And objects that refer to the same 'parent'
object (e.g. the v4l2_ctrl_handler that has the current driver
state) will have the same priv value.

The caller has to call media_request_object_put() for the returned
object since this function increments the refcount.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/media-request.c | 25 +++++++++++++++++++++++++
 include/media/media-request.h | 28 ++++++++++++++++++++++++++++
 2 files changed, 53 insertions(+)

diff --git a/drivers/media/media-request.c b/drivers/media/media-request.c
index 4b523f3a03a3..a5b70a4e613b 100644
--- a/drivers/media/media-request.c
+++ b/drivers/media/media-request.c
@@ -344,6 +344,31 @@ static void media_request_object_release(struct kref *kref)
 	obj->ops->release(obj);
 }
 
+struct media_request_object *
+media_request_object_find(struct media_request *req,
+			  const struct media_request_object_ops *ops,
+			  void *priv)
+{
+	struct media_request_object *obj;
+	struct media_request_object *found = NULL;
+	unsigned long flags;
+
+	if (WARN_ON(!ops || !priv))
+		return NULL;
+
+	spin_lock_irqsave(&req->lock, flags);
+	list_for_each_entry(obj, &req->objects, list) {
+		if (obj->ops == ops && obj->priv == priv) {
+			media_request_object_get(obj);
+			found = obj;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&req->lock, flags);
+	return found;
+}
+EXPORT_SYMBOL_GPL(media_request_object_find);
+
 void media_request_object_put(struct media_request_object *obj)
 {
 	kref_put(&obj->kref, media_request_object_release);
diff --git a/include/media/media-request.h b/include/media/media-request.h
index 66ec9d09fcd8..fd08d7a431a1 100644
--- a/include/media/media-request.h
+++ b/include/media/media-request.h
@@ -253,6 +253,26 @@ static inline void media_request_object_get(struct media_request_object *obj)
  */
 void media_request_object_put(struct media_request_object *obj);
 
+/**
+ * media_request_object_find - Find an object in a request
+ *
+ * @req: The media request
+ * @ops: Find an object with this ops value
+ * @priv: Find an object with this priv value
+ *
+ * Both @ops and @priv must be non-NULL.
+ *
+ * Returns the object pointer or NULL if not found. The caller must
+ * call media_request_object_put() once it finished using the object.
+ *
+ * Since this function needs to walk the list of objects it takes
+ * the @req->lock spin lock to make this safe.
+ */
+struct media_request_object *
+media_request_object_find(struct media_request *req,
+			  const struct media_request_object_ops *ops,
+			  void *priv);
+
 /**
  * media_request_object_init - Initialise a media request object
  *
@@ -324,6 +344,14 @@ static inline void media_request_object_put(struct media_request_object *obj)
 {
 }
 
+static inline struct media_request_object *
+media_request_object_find(struct media_request *req,
+			  const struct media_request_object_ops *ops,
+			  void *priv)
+{
+	return NULL;
+}
+
 static inline void media_request_object_init(struct media_request_object *obj)
 {
 	obj->ops = NULL;
-- 
2.18.0

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

* [PATCHv17 07/34] v4l2-device.h: add v4l2_device_supports_requests() helper
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (5 preceding siblings ...)
  2018-08-04 12:44 ` [PATCHv17 06/34] media-request: add media_request_object_find Hans Verkuil
@ 2018-08-04 12:44 ` Hans Verkuil
  2018-08-09 19:57   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 08/34] v4l2-dev: lock req_queue_mutex Hans Verkuil
                   ` (26 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:44 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

Add a simple helper function that tests if the driver supports
the request API.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 include/media/v4l2-device.h | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h
index b330e4a08a6b..ac7677a183ff 100644
--- a/include/media/v4l2-device.h
+++ b/include/media/v4l2-device.h
@@ -211,6 +211,17 @@ static inline void v4l2_subdev_notify(struct v4l2_subdev *sd,
 		sd->v4l2_dev->notify(sd, notification, arg);
 }
 
+/**
+ * v4l2_device_supports_requests - Test if requests are supported.
+ *
+ * @v4l2_dev: pointer to struct v4l2_device
+ */
+static inline bool v4l2_device_supports_requests(struct v4l2_device *v4l2_dev)
+{
+	return v4l2_dev->mdev && v4l2_dev->mdev->ops &&
+	       v4l2_dev->mdev->ops->req_queue;
+}
+
 /* Helper macros to iterate over all subdevs. */
 
 /**
-- 
2.18.0

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

* [PATCHv17 08/34] v4l2-dev: lock req_queue_mutex
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (6 preceding siblings ...)
  2018-08-04 12:44 ` [PATCHv17 07/34] v4l2-device.h: add v4l2_device_supports_requests() helper Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-09 20:03   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 09/34] videodev2.h: add request_fd field to v4l2_ext_controls Hans Verkuil
                   ` (25 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil, Sakari Ailus

From: Hans Verkuil <hans.verkuil@cisco.com>

We need to serialize streamon/off with queueing new requests.
These ioctls may trigger the cancellation of a streaming
operation, and that should not be mixed with queuing a new
request at the same time.

Finally close() needs this lock since that too can trigger the
cancellation of a streaming operation.

We take the req_queue_mutex here before any other locks since
it is a very high-level lock.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/v4l2-core/v4l2-dev.c   | 13 +++++++++++++
 drivers/media/v4l2-core/v4l2-ioctl.c | 22 +++++++++++++++++++++-
 2 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
index 69e775930fc4..53018e4a4c78 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -444,8 +444,21 @@ static int v4l2_release(struct inode *inode, struct file *filp)
 	struct video_device *vdev = video_devdata(filp);
 	int ret = 0;
 
+	/*
+	 * We need to serialize the release() with queueing new requests.
+	 * The release() may trigger the cancellation of a streaming
+	 * operation, and that should not be mixed with queueing a new
+	 * request at the same time.
+	 */
+	if (v4l2_device_supports_requests(vdev->v4l2_dev))
+		mutex_lock(&vdev->v4l2_dev->mdev->req_queue_mutex);
+
 	if (vdev->fops->release)
 		ret = vdev->fops->release(filp);
+
+	if (v4l2_device_supports_requests(vdev->v4l2_dev))
+		mutex_unlock(&vdev->v4l2_dev->mdev->req_queue_mutex);
+
 	if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
 		dprintk("%s: release\n",
 			video_device_node_name(vdev));
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 54afc9c7ee6e..ea475d833dd6 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -2780,6 +2780,7 @@ static long __video_do_ioctl(struct file *file,
 		unsigned int cmd, void *arg)
 {
 	struct video_device *vfd = video_devdata(file);
+	struct mutex *req_queue_lock = NULL;
 	struct mutex *lock; /* ioctl serialization mutex */
 	const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
 	bool write_only = false;
@@ -2799,10 +2800,27 @@ static long __video_do_ioctl(struct file *file,
 	if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags))
 		vfh = file->private_data;
 
+	/*
+	 * We need to serialize streamon/off with queueing new requests.
+	 * These ioctls may trigger the cancellation of a streaming
+	 * operation, and that should not be mixed with queueing a new
+	 * request at the same time.
+	 */
+	if (v4l2_device_supports_requests(vfd->v4l2_dev) &&
+	    (cmd == VIDIOC_STREAMON || cmd == VIDIOC_STREAMOFF)) {
+		req_queue_lock = &vfd->v4l2_dev->mdev->req_queue_mutex;
+
+		if (mutex_lock_interruptible(req_queue_lock))
+			return -ERESTARTSYS;
+	}
+
 	lock = v4l2_ioctl_get_lock(vfd, vfh, cmd, arg);
 
-	if (lock && mutex_lock_interruptible(lock))
+	if (lock && mutex_lock_interruptible(lock)) {
+		if (req_queue_lock)
+			mutex_unlock(req_queue_lock);
 		return -ERESTARTSYS;
+	}
 
 	if (!video_is_registered(vfd)) {
 		ret = -ENODEV;
@@ -2861,6 +2879,8 @@ static long __video_do_ioctl(struct file *file,
 unlock:
 	if (lock)
 		mutex_unlock(lock);
+	if (req_queue_lock)
+		mutex_unlock(req_queue_lock);
 	return ret;
 }
 
-- 
2.18.0

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

* [PATCHv17 09/34] videodev2.h: add request_fd field to v4l2_ext_controls
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (7 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 08/34] v4l2-dev: lock req_queue_mutex Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-09 20:04   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 10/34] v4l2-ctrls: v4l2_ctrl_add_handler: add from_other_dev Hans Verkuil
                   ` (24 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Alexandre Courbot, Hans Verkuil

From: Alexandre Courbot <acourbot@chromium.org>

If 'which' is V4L2_CTRL_WHICH_REQUEST_VAL, then the 'request_fd' field
can be used to specify a request for the G/S/TRY_EXT_CTRLS ioctls.

Signed-off-by: Alexandre Courbot <acourbot@chromium.org>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 5 ++++-
 drivers/media/v4l2-core/v4l2-ioctl.c          | 6 +++---
 include/uapi/linux/videodev2.h                | 4 +++-
 3 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index 6481212fda77..dcce86c1fe40 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -834,7 +834,8 @@ struct v4l2_ext_controls32 {
 	__u32 which;
 	__u32 count;
 	__u32 error_idx;
-	__u32 reserved[2];
+	__s32 request_fd;
+	__u32 reserved[1];
 	compat_caddr_t controls; /* actually struct v4l2_ext_control32 * */
 };
 
@@ -909,6 +910,7 @@ static int get_v4l2_ext_controls32(struct file *file,
 	    get_user(count, &p32->count) ||
 	    put_user(count, &p64->count) ||
 	    assign_in_user(&p64->error_idx, &p32->error_idx) ||
+	    assign_in_user(&p64->request_fd, &p32->request_fd) ||
 	    copy_in_user(p64->reserved, p32->reserved, sizeof(p64->reserved)))
 		return -EFAULT;
 
@@ -974,6 +976,7 @@ static int put_v4l2_ext_controls32(struct file *file,
 	    get_user(count, &p64->count) ||
 	    put_user(count, &p32->count) ||
 	    assign_in_user(&p32->error_idx, &p64->error_idx) ||
+	    assign_in_user(&p32->request_fd, &p64->request_fd) ||
 	    copy_in_user(p32->reserved, p64->reserved, sizeof(p32->reserved)) ||
 	    get_user(kcontrols, &p64->controls))
 		return -EFAULT;
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index ea475d833dd6..03241d6b7ef8 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -590,8 +590,8 @@ static void v4l_print_ext_controls(const void *arg, bool write_only)
 	const struct v4l2_ext_controls *p = arg;
 	int i;
 
-	pr_cont("which=0x%x, count=%d, error_idx=%d",
-			p->which, p->count, p->error_idx);
+	pr_cont("which=0x%x, count=%d, error_idx=%d, request_fd=%d",
+			p->which, p->count, p->error_idx, p->request_fd);
 	for (i = 0; i < p->count; i++) {
 		if (!p->controls[i].size)
 			pr_cont(", id/val=0x%x/0x%x",
@@ -907,7 +907,7 @@ static int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv)
 	__u32 i;
 
 	/* zero the reserved fields */
-	c->reserved[0] = c->reserved[1] = 0;
+	c->reserved[0] = 0;
 	for (i = 0; i < c->count; i++)
 		c->controls[i].reserved2[0] = 0;
 
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 5d1a3685bea9..1df0fa983db6 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -1599,7 +1599,8 @@ struct v4l2_ext_controls {
 	};
 	__u32 count;
 	__u32 error_idx;
-	__u32 reserved[2];
+	__s32 request_fd;
+	__u32 reserved[1];
 	struct v4l2_ext_control *controls;
 };
 
@@ -1612,6 +1613,7 @@ struct v4l2_ext_controls {
 #define V4L2_CTRL_MAX_DIMS	  (4)
 #define V4L2_CTRL_WHICH_CUR_VAL   0
 #define V4L2_CTRL_WHICH_DEF_VAL   0x0f000000
+#define V4L2_CTRL_WHICH_REQUEST_VAL 0x0f010000
 
 enum v4l2_ctrl_type {
 	V4L2_CTRL_TYPE_INTEGER	     = 1,
-- 
2.18.0

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

* [PATCHv17 10/34] v4l2-ctrls: v4l2_ctrl_add_handler: add from_other_dev
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (8 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 09/34] videodev2.h: add request_fd field to v4l2_ext_controls Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-09 20:10   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 11/34] v4l2-ctrls: prepare internal structs for request API Hans Verkuil
                   ` (23 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil, Alexandre Courbot

From: Hans Verkuil <hans.verkuil@cisco.com>

Add a 'bool from_other_dev' argument: set to true if the two
handlers refer to different devices (e.g. it is true when
inheriting controls from a subdev into a main v4l2 bridge
driver).

This will be used later when implementing support for the
request API since we need to skip such controls.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Alexandre Courbot <acourbot@chromium.org>
---
 drivers/media/dvb-frontends/rtl2832_sdr.c     |  5 +-
 drivers/media/pci/bt8xx/bttv-driver.c         |  2 +-
 drivers/media/pci/cx23885/cx23885-417.c       |  2 +-
 drivers/media/pci/cx88/cx88-blackbird.c       |  2 +-
 drivers/media/pci/cx88/cx88-video.c           |  2 +-
 drivers/media/pci/saa7134/saa7134-empress.c   |  4 +-
 drivers/media/pci/saa7134/saa7134-video.c     |  2 +-
 .../media/platform/exynos4-is/fimc-capture.c  |  2 +-
 drivers/media/platform/rcar-vin/rcar-core.c   |  2 +-
 drivers/media/platform/rcar_drif.c            |  2 +-
 .../media/platform/soc_camera/soc_camera.c    |  3 +-
 drivers/media/platform/vivid/vivid-ctrls.c    | 46 +++++++++----------
 drivers/media/usb/cx231xx/cx231xx-417.c       |  2 +-
 drivers/media/usb/cx231xx/cx231xx-video.c     |  4 +-
 drivers/media/usb/msi2500/msi2500.c           |  2 +-
 drivers/media/usb/tm6000/tm6000-video.c       |  2 +-
 drivers/media/v4l2-core/v4l2-ctrls.c          | 11 +++--
 drivers/media/v4l2-core/v4l2-device.c         |  3 +-
 drivers/staging/media/imx/imx-media-dev.c     |  2 +-
 drivers/staging/media/imx/imx-media-fim.c     |  2 +-
 include/media/v4l2-ctrls.h                    |  8 +++-
 21 files changed, 61 insertions(+), 49 deletions(-)

diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c
index d448d9d4879c..7d0c89e269ab 100644
--- a/drivers/media/dvb-frontends/rtl2832_sdr.c
+++ b/drivers/media/dvb-frontends/rtl2832_sdr.c
@@ -1394,7 +1394,8 @@ static int rtl2832_sdr_probe(struct platform_device *pdev)
 	case RTL2832_SDR_TUNER_E4000:
 		v4l2_ctrl_handler_init(&dev->hdl, 9);
 		if (subdev)
-			v4l2_ctrl_add_handler(&dev->hdl, subdev->ctrl_handler, NULL);
+			v4l2_ctrl_add_handler(&dev->hdl, subdev->ctrl_handler,
+					      NULL, true);
 		break;
 	case RTL2832_SDR_TUNER_R820T:
 	case RTL2832_SDR_TUNER_R828D:
@@ -1423,7 +1424,7 @@ static int rtl2832_sdr_probe(struct platform_device *pdev)
 		v4l2_ctrl_handler_init(&dev->hdl, 2);
 		if (subdev)
 			v4l2_ctrl_add_handler(&dev->hdl, subdev->ctrl_handler,
-					      NULL);
+					      NULL, true);
 		break;
 	default:
 		v4l2_ctrl_handler_init(&dev->hdl, 0);
diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c
index cf05e11da01b..e86154092558 100644
--- a/drivers/media/pci/bt8xx/bttv-driver.c
+++ b/drivers/media/pci/bt8xx/bttv-driver.c
@@ -4211,7 +4211,7 @@ static int bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id)
 	/* register video4linux + input */
 	if (!bttv_tvcards[btv->c.type].no_video) {
 		v4l2_ctrl_add_handler(&btv->radio_ctrl_handler, hdl,
-				v4l2_ctrl_radio_filter);
+				v4l2_ctrl_radio_filter, false);
 		if (btv->radio_ctrl_handler.error) {
 			result = btv->radio_ctrl_handler.error;
 			goto fail2;
diff --git a/drivers/media/pci/cx23885/cx23885-417.c b/drivers/media/pci/cx23885/cx23885-417.c
index a71f3c7569ce..762823871c78 100644
--- a/drivers/media/pci/cx23885/cx23885-417.c
+++ b/drivers/media/pci/cx23885/cx23885-417.c
@@ -1527,7 +1527,7 @@ int cx23885_417_register(struct cx23885_dev *dev)
 	dev->cxhdl.priv = dev;
 	dev->cxhdl.func = cx23885_api_func;
 	cx2341x_handler_set_50hz(&dev->cxhdl, tsport->height == 576);
-	v4l2_ctrl_add_handler(&dev->ctrl_handler, &dev->cxhdl.hdl, NULL);
+	v4l2_ctrl_add_handler(&dev->ctrl_handler, &dev->cxhdl.hdl, NULL, false);
 
 	/* Allocate and initialize V4L video device */
 	dev->v4l_device = cx23885_video_dev_alloc(tsport,
diff --git a/drivers/media/pci/cx88/cx88-blackbird.c b/drivers/media/pci/cx88/cx88-blackbird.c
index 7a4876cf9f08..722dd101c9b0 100644
--- a/drivers/media/pci/cx88/cx88-blackbird.c
+++ b/drivers/media/pci/cx88/cx88-blackbird.c
@@ -1183,7 +1183,7 @@ static int cx8802_blackbird_probe(struct cx8802_driver *drv)
 	err = cx2341x_handler_init(&dev->cxhdl, 36);
 	if (err)
 		goto fail_core;
-	v4l2_ctrl_add_handler(&dev->cxhdl.hdl, &core->video_hdl, NULL);
+	v4l2_ctrl_add_handler(&dev->cxhdl.hdl, &core->video_hdl, NULL, false);
 
 	/* blackbird stuff */
 	pr_info("cx23416 based mpeg encoder (blackbird reference design)\n");
diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c
index 7b113bad70d2..85e2b6c9fb1c 100644
--- a/drivers/media/pci/cx88/cx88-video.c
+++ b/drivers/media/pci/cx88/cx88-video.c
@@ -1378,7 +1378,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev,
 		if (vc->id == V4L2_CID_CHROMA_AGC)
 			core->chroma_agc = vc;
 	}
-	v4l2_ctrl_add_handler(&core->video_hdl, &core->audio_hdl, NULL);
+	v4l2_ctrl_add_handler(&core->video_hdl, &core->audio_hdl, NULL, false);
 
 	/* load and configure helper modules */
 
diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c
index 66acfd35ffc6..fc75ce00dbf8 100644
--- a/drivers/media/pci/saa7134/saa7134-empress.c
+++ b/drivers/media/pci/saa7134/saa7134-empress.c
@@ -265,9 +265,9 @@ static int empress_init(struct saa7134_dev *dev)
 		 "%s empress (%s)", dev->name,
 		 saa7134_boards[dev->board].name);
 	v4l2_ctrl_handler_init(hdl, 21);
-	v4l2_ctrl_add_handler(hdl, &dev->ctrl_handler, empress_ctrl_filter);
+	v4l2_ctrl_add_handler(hdl, &dev->ctrl_handler, empress_ctrl_filter, false);
 	if (dev->empress_sd)
-		v4l2_ctrl_add_handler(hdl, dev->empress_sd->ctrl_handler, NULL);
+		v4l2_ctrl_add_handler(hdl, dev->empress_sd->ctrl_handler, NULL, true);
 	if (hdl->error) {
 		video_device_release(dev->empress_dev);
 		return hdl->error;
diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c
index 1a50ec9d084f..41d46488d22e 100644
--- a/drivers/media/pci/saa7134/saa7134-video.c
+++ b/drivers/media/pci/saa7134/saa7134-video.c
@@ -2136,7 +2136,7 @@ int saa7134_video_init1(struct saa7134_dev *dev)
 		hdl = &dev->radio_ctrl_handler;
 		v4l2_ctrl_handler_init(hdl, 2);
 		v4l2_ctrl_add_handler(hdl, &dev->ctrl_handler,
-				v4l2_ctrl_radio_filter);
+				v4l2_ctrl_radio_filter, false);
 		if (hdl->error)
 			return hdl->error;
 	}
diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c
index a3cdac188190..2164375f0ee0 100644
--- a/drivers/media/platform/exynos4-is/fimc-capture.c
+++ b/drivers/media/platform/exynos4-is/fimc-capture.c
@@ -1424,7 +1424,7 @@ static int fimc_link_setup(struct media_entity *entity,
 		return 0;
 
 	return v4l2_ctrl_add_handler(&vc->ctx->ctrls.handler,
-				     sensor->ctrl_handler, NULL);
+				     sensor->ctrl_handler, NULL, true);
 }
 
 static const struct media_entity_operations fimc_sd_media_ops = {
diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c
index ce09799976ef..42f1084bedef 100644
--- a/drivers/media/platform/rcar-vin/rcar-core.c
+++ b/drivers/media/platform/rcar-vin/rcar-core.c
@@ -476,7 +476,7 @@ static int rvin_parallel_subdevice_attach(struct rvin_dev *vin,
 		return ret;
 
 	ret = v4l2_ctrl_add_handler(&vin->ctrl_handler, subdev->ctrl_handler,
-				    NULL);
+				    NULL, true);
 	if (ret < 0) {
 		v4l2_ctrl_handler_free(&vin->ctrl_handler);
 		return ret;
diff --git a/drivers/media/platform/rcar_drif.c b/drivers/media/platform/rcar_drif.c
index 81413ab52475..8c3388b9f5bd 100644
--- a/drivers/media/platform/rcar_drif.c
+++ b/drivers/media/platform/rcar_drif.c
@@ -1164,7 +1164,7 @@ static int rcar_drif_notify_complete(struct v4l2_async_notifier *notifier)
 	}
 
 	ret = v4l2_ctrl_add_handler(&sdr->ctrl_hdl,
-				    sdr->ep.subdev->ctrl_handler, NULL);
+				    sdr->ep.subdev->ctrl_handler, NULL, true);
 	if (ret) {
 		rdrif_err(sdr, "failed: ctrl add hdlr ret %d\n", ret);
 		goto error;
diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index 66d613629167..901c07f49351 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -1181,7 +1181,8 @@ static int soc_camera_probe_finish(struct soc_camera_device *icd)
 
 	v4l2_subdev_call(sd, video, g_tvnorms, &icd->vdev->tvnorms);
 
-	ret = v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler, NULL);
+	ret = v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler,
+				    NULL, true);
 	if (ret < 0)
 		return ret;
 
diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/platform/vivid/vivid-ctrls.c
index 5429193fbb91..5655f39d8e76 100644
--- a/drivers/media/platform/vivid/vivid-ctrls.c
+++ b/drivers/media/platform/vivid/vivid-ctrls.c
@@ -1662,59 +1662,59 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap,
 		v4l2_ctrl_auto_cluster(2, &dev->autogain, 0, true);
 
 	if (dev->has_vid_cap) {
-		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_gen, NULL);
-		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_vid, NULL);
-		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_aud, NULL);
-		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_streaming, NULL);
-		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_sdtv_cap, NULL);
-		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_loop_cap, NULL);
-		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_fb, NULL);
+		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_gen, NULL, false);
+		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_vid, NULL, false);
+		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_aud, NULL, false);
+		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_streaming, NULL, false);
+		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_sdtv_cap, NULL, false);
+		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_loop_cap, NULL, false);
+		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_fb, NULL, false);
 		if (hdl_vid_cap->error)
 			return hdl_vid_cap->error;
 		dev->vid_cap_dev.ctrl_handler = hdl_vid_cap;
 	}
 	if (dev->has_vid_out) {
-		v4l2_ctrl_add_handler(hdl_vid_out, hdl_user_gen, NULL);
-		v4l2_ctrl_add_handler(hdl_vid_out, hdl_user_aud, NULL);
-		v4l2_ctrl_add_handler(hdl_vid_out, hdl_streaming, NULL);
-		v4l2_ctrl_add_handler(hdl_vid_out, hdl_fb, NULL);
+		v4l2_ctrl_add_handler(hdl_vid_out, hdl_user_gen, NULL, false);
+		v4l2_ctrl_add_handler(hdl_vid_out, hdl_user_aud, NULL, false);
+		v4l2_ctrl_add_handler(hdl_vid_out, hdl_streaming, NULL, false);
+		v4l2_ctrl_add_handler(hdl_vid_out, hdl_fb, NULL, false);
 		if (hdl_vid_out->error)
 			return hdl_vid_out->error;
 		dev->vid_out_dev.ctrl_handler = hdl_vid_out;
 	}
 	if (dev->has_vbi_cap) {
-		v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_user_gen, NULL);
-		v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_streaming, NULL);
-		v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_sdtv_cap, NULL);
-		v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_loop_cap, NULL);
+		v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_user_gen, NULL, false);
+		v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_streaming, NULL, false);
+		v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_sdtv_cap, NULL, false);
+		v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_loop_cap, NULL, false);
 		if (hdl_vbi_cap->error)
 			return hdl_vbi_cap->error;
 		dev->vbi_cap_dev.ctrl_handler = hdl_vbi_cap;
 	}
 	if (dev->has_vbi_out) {
-		v4l2_ctrl_add_handler(hdl_vbi_out, hdl_user_gen, NULL);
-		v4l2_ctrl_add_handler(hdl_vbi_out, hdl_streaming, NULL);
+		v4l2_ctrl_add_handler(hdl_vbi_out, hdl_user_gen, NULL, false);
+		v4l2_ctrl_add_handler(hdl_vbi_out, hdl_streaming, NULL, false);
 		if (hdl_vbi_out->error)
 			return hdl_vbi_out->error;
 		dev->vbi_out_dev.ctrl_handler = hdl_vbi_out;
 	}
 	if (dev->has_radio_rx) {
-		v4l2_ctrl_add_handler(hdl_radio_rx, hdl_user_gen, NULL);
-		v4l2_ctrl_add_handler(hdl_radio_rx, hdl_user_aud, NULL);
+		v4l2_ctrl_add_handler(hdl_radio_rx, hdl_user_gen, NULL, false);
+		v4l2_ctrl_add_handler(hdl_radio_rx, hdl_user_aud, NULL, false);
 		if (hdl_radio_rx->error)
 			return hdl_radio_rx->error;
 		dev->radio_rx_dev.ctrl_handler = hdl_radio_rx;
 	}
 	if (dev->has_radio_tx) {
-		v4l2_ctrl_add_handler(hdl_radio_tx, hdl_user_gen, NULL);
-		v4l2_ctrl_add_handler(hdl_radio_tx, hdl_user_aud, NULL);
+		v4l2_ctrl_add_handler(hdl_radio_tx, hdl_user_gen, NULL, false);
+		v4l2_ctrl_add_handler(hdl_radio_tx, hdl_user_aud, NULL, false);
 		if (hdl_radio_tx->error)
 			return hdl_radio_tx->error;
 		dev->radio_tx_dev.ctrl_handler = hdl_radio_tx;
 	}
 	if (dev->has_sdr_cap) {
-		v4l2_ctrl_add_handler(hdl_sdr_cap, hdl_user_gen, NULL);
-		v4l2_ctrl_add_handler(hdl_sdr_cap, hdl_streaming, NULL);
+		v4l2_ctrl_add_handler(hdl_sdr_cap, hdl_user_gen, NULL, false);
+		v4l2_ctrl_add_handler(hdl_sdr_cap, hdl_streaming, NULL, false);
 		if (hdl_sdr_cap->error)
 			return hdl_sdr_cap->error;
 		dev->sdr_cap_dev.ctrl_handler = hdl_sdr_cap;
diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c
index 2f3b0564d676..e3cb9eefd36a 100644
--- a/drivers/media/usb/cx231xx/cx231xx-417.c
+++ b/drivers/media/usb/cx231xx/cx231xx-417.c
@@ -1992,7 +1992,7 @@ int cx231xx_417_register(struct cx231xx *dev)
 	dev->mpeg_ctrl_handler.ops = &cx231xx_ops;
 	if (dev->sd_cx25840)
 		v4l2_ctrl_add_handler(&dev->mpeg_ctrl_handler.hdl,
-				dev->sd_cx25840->ctrl_handler, NULL);
+				dev->sd_cx25840->ctrl_handler, NULL, false);
 	if (dev->mpeg_ctrl_handler.hdl.error) {
 		err = dev->mpeg_ctrl_handler.hdl.error;
 		dprintk(3, "%s: can't add cx25840 controls\n", dev->name);
diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c
index f7fcd733a2ca..2dedb18f63a0 100644
--- a/drivers/media/usb/cx231xx/cx231xx-video.c
+++ b/drivers/media/usb/cx231xx/cx231xx-video.c
@@ -2204,10 +2204,10 @@ int cx231xx_register_analog_devices(struct cx231xx *dev)
 
 	if (dev->sd_cx25840) {
 		v4l2_ctrl_add_handler(&dev->ctrl_handler,
-				dev->sd_cx25840->ctrl_handler, NULL);
+				dev->sd_cx25840->ctrl_handler, NULL, true);
 		v4l2_ctrl_add_handler(&dev->radio_ctrl_handler,
 				dev->sd_cx25840->ctrl_handler,
-				v4l2_ctrl_radio_filter);
+				v4l2_ctrl_radio_filter, true);
 	}
 
 	if (dev->ctrl_handler.error)
diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c
index 65ef755adfdc..4aacd77a5d58 100644
--- a/drivers/media/usb/msi2500/msi2500.c
+++ b/drivers/media/usb/msi2500/msi2500.c
@@ -1278,7 +1278,7 @@ static int msi2500_probe(struct usb_interface *intf,
 	}
 
 	/* currently all controls are from subdev */
-	v4l2_ctrl_add_handler(&dev->hdl, sd->ctrl_handler, NULL);
+	v4l2_ctrl_add_handler(&dev->hdl, sd->ctrl_handler, NULL, true);
 
 	dev->v4l2_dev.ctrl_handler = &dev->hdl;
 	dev->vdev.v4l2_dev = &dev->v4l2_dev;
diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c
index 96055de6e8ce..176abbf5fbba 100644
--- a/drivers/media/usb/tm6000/tm6000-video.c
+++ b/drivers/media/usb/tm6000/tm6000-video.c
@@ -1625,7 +1625,7 @@ int tm6000_v4l2_register(struct tm6000_core *dev)
 	v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops,
 			V4L2_CID_HUE, -128, 127, 1, 0);
 	v4l2_ctrl_add_handler(&dev->ctrl_handler,
-			&dev->radio_ctrl_handler, NULL);
+			&dev->radio_ctrl_handler, NULL, false);
 
 	if (dev->radio_ctrl_handler.error)
 		ret = dev->radio_ctrl_handler.error;
diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index 599c1cbff3b9..404291f00715 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -2016,7 +2016,8 @@ EXPORT_SYMBOL(v4l2_ctrl_find);
 
 /* Allocate a new v4l2_ctrl_ref and hook it into the handler. */
 static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
-			   struct v4l2_ctrl *ctrl)
+			   struct v4l2_ctrl *ctrl,
+			   bool from_other_dev)
 {
 	struct v4l2_ctrl_ref *ref;
 	struct v4l2_ctrl_ref *new_ref;
@@ -2040,6 +2041,7 @@ static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
 	if (!new_ref)
 		return handler_set_err(hdl, -ENOMEM);
 	new_ref->ctrl = ctrl;
+	new_ref->from_other_dev = from_other_dev;
 	if (ctrl->handler == hdl) {
 		/* By default each control starts in a cluster of its own.
 		   new_ref->ctrl is basically a cluster array with one
@@ -2220,7 +2222,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 		ctrl->type_ops->init(ctrl, idx, ctrl->p_new);
 	}
 
-	if (handler_new_ref(hdl, ctrl)) {
+	if (handler_new_ref(hdl, ctrl, false)) {
 		kvfree(ctrl);
 		return NULL;
 	}
@@ -2389,7 +2391,8 @@ EXPORT_SYMBOL(v4l2_ctrl_new_int_menu);
 /* Add the controls from another handler to our own. */
 int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
 			  struct v4l2_ctrl_handler *add,
-			  bool (*filter)(const struct v4l2_ctrl *ctrl))
+			  bool (*filter)(const struct v4l2_ctrl *ctrl),
+			  bool from_other_dev)
 {
 	struct v4l2_ctrl_ref *ref;
 	int ret = 0;
@@ -2412,7 +2415,7 @@ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
 		/* Filter any unwanted controls */
 		if (filter && !filter(ctrl))
 			continue;
-		ret = handler_new_ref(hdl, ctrl);
+		ret = handler_new_ref(hdl, ctrl, from_other_dev);
 		if (ret)
 			break;
 	}
diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c
index 3940e55c72f1..5189fb9f741f 100644
--- a/drivers/media/v4l2-core/v4l2-device.c
+++ b/drivers/media/v4l2-core/v4l2-device.c
@@ -178,7 +178,8 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
 
 	sd->v4l2_dev = v4l2_dev;
 	/* This just returns 0 if either of the two args is NULL */
-	err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler, NULL);
+	err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler,
+				    NULL, true);
 	if (err)
 		goto error_module;
 
diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c
index b0be80f05767..b03a4b7bb769 100644
--- a/drivers/staging/media/imx/imx-media-dev.c
+++ b/drivers/staging/media/imx/imx-media-dev.c
@@ -391,7 +391,7 @@ static int imx_media_inherit_controls(struct imx_media_dev *imxmd,
 
 		ret = v4l2_ctrl_add_handler(vfd->ctrl_handler,
 					    sd->ctrl_handler,
-					    NULL);
+					    NULL, true);
 		if (ret)
 			return ret;
 	}
diff --git a/drivers/staging/media/imx/imx-media-fim.c b/drivers/staging/media/imx/imx-media-fim.c
index 6df189135db8..8cf773eef9da 100644
--- a/drivers/staging/media/imx/imx-media-fim.c
+++ b/drivers/staging/media/imx/imx-media-fim.c
@@ -463,7 +463,7 @@ int imx_media_fim_add_controls(struct imx_media_fim *fim)
 {
 	/* add the FIM controls to the calling subdev ctrl handler */
 	return v4l2_ctrl_add_handler(fim->sd->ctrl_handler,
-				     &fim->ctrl_handler, NULL);
+				     &fim->ctrl_handler, NULL, false);
 }
 EXPORT_SYMBOL_GPL(imx_media_fim_add_controls);
 
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index f615ba1b29dd..192e31c21faf 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -247,6 +247,8 @@ struct v4l2_ctrl {
  * @ctrl:	The actual control information.
  * @helper:	Pointer to helper struct. Used internally in
  *		``prepare_ext_ctrls`` function at ``v4l2-ctrl.c``.
+ * @from_other_dev: If true, then @ctrl was defined in another
+ *		device than the &struct v4l2_ctrl_handler.
  *
  * Each control handler has a list of these refs. The list_head is used to
  * keep a sorted-by-control-ID list of all controls, while the next pointer
@@ -257,6 +259,7 @@ struct v4l2_ctrl_ref {
 	struct v4l2_ctrl_ref *next;
 	struct v4l2_ctrl *ctrl;
 	struct v4l2_ctrl_helper *helper;
+	bool from_other_dev;
 };
 
 /**
@@ -633,6 +636,8 @@ typedef bool (*v4l2_ctrl_filter)(const struct v4l2_ctrl *ctrl);
  * @add:	The control handler whose controls you want to add to
  *		the @hdl control handler.
  * @filter:	This function will filter which controls should be added.
+ * @from_other_dev: If true, then the controls in @add were defined in another
+ *		device than @hdl.
  *
  * Does nothing if either of the two handlers is a NULL pointer.
  * If @filter is NULL, then all controls are added. Otherwise only those
@@ -642,7 +647,8 @@ typedef bool (*v4l2_ctrl_filter)(const struct v4l2_ctrl *ctrl);
  */
 int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
 			  struct v4l2_ctrl_handler *add,
-			  v4l2_ctrl_filter filter);
+			  v4l2_ctrl_filter filter,
+			  bool from_other_dev);
 
 /**
  * v4l2_ctrl_radio_filter() - Standard filter for radio controls.
-- 
2.18.0

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

* [PATCHv17 11/34] v4l2-ctrls: prepare internal structs for request API
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (9 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 10/34] v4l2-ctrls: v4l2_ctrl_add_handler: add from_other_dev Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-09 20:16   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 12/34] v4l2-ctrls: alloc memory for p_req Hans Verkuil
                   ` (22 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil, Alexandre Courbot

From: Hans Verkuil <hans.verkuil@cisco.com>

Embed and initialize a media_request_object in struct v4l2_ctrl_handler.

Add a p_req field to struct v4l2_ctrl_ref that will store the
request value.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Alexandre Courbot <acourbot@chromium.org>
---
 drivers/media/v4l2-core/v4l2-ctrls.c |  1 +
 include/media/v4l2-ctrls.h           | 10 ++++++++++
 2 files changed, 11 insertions(+)

diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index 404291f00715..b33a8bee82b0 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -1901,6 +1901,7 @@ int v4l2_ctrl_handler_init_class(struct v4l2_ctrl_handler *hdl,
 				      sizeof(hdl->buckets[0]),
 				      GFP_KERNEL | __GFP_ZERO);
 	hdl->error = hdl->buckets ? 0 : -ENOMEM;
+	media_request_object_init(&hdl->req_obj);
 	return hdl->error;
 }
 EXPORT_SYMBOL(v4l2_ctrl_handler_init_class);
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index 192e31c21faf..3f4e062d4e3d 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -20,6 +20,7 @@
 #include <linux/list.h>
 #include <linux/mutex.h>
 #include <linux/videodev2.h>
+#include <media/media-request.h>
 
 /* forward references */
 struct file;
@@ -249,6 +250,11 @@ struct v4l2_ctrl {
  *		``prepare_ext_ctrls`` function at ``v4l2-ctrl.c``.
  * @from_other_dev: If true, then @ctrl was defined in another
  *		device than the &struct v4l2_ctrl_handler.
+ * @p_req:	If the control handler containing this control reference
+ *		is bound to a media request, then this points to the
+ *		value of the control that should be applied when the request
+ *		is executed, or to the value of the control at the time
+ *		that the request was completed.
  *
  * Each control handler has a list of these refs. The list_head is used to
  * keep a sorted-by-control-ID list of all controls, while the next pointer
@@ -260,6 +266,7 @@ struct v4l2_ctrl_ref {
 	struct v4l2_ctrl *ctrl;
 	struct v4l2_ctrl_helper *helper;
 	bool from_other_dev;
+	union v4l2_ctrl_ptr p_req;
 };
 
 /**
@@ -283,6 +290,8 @@ struct v4l2_ctrl_ref {
  * @notify_priv: Passed as argument to the v4l2_ctrl notify callback.
  * @nr_of_buckets: Total number of buckets in the array.
  * @error:	The error code of the first failed control addition.
+ * @req_obj:	The &struct media_request_object, used to link into a
+ *		&struct media_request. This request object has a refcount.
  */
 struct v4l2_ctrl_handler {
 	struct mutex _lock;
@@ -295,6 +304,7 @@ struct v4l2_ctrl_handler {
 	void *notify_priv;
 	u16 nr_of_buckets;
 	int error;
+	struct media_request_object req_obj;
 };
 
 /**
-- 
2.18.0

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

* [PATCHv17 12/34] v4l2-ctrls: alloc memory for p_req
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (10 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 11/34] v4l2-ctrls: prepare internal structs for request API Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-09 20:19   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 13/34] v4l2-ctrls: use ref in helper instead of ctrl Hans Verkuil
                   ` (21 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

To store request data the handler_new_ref() allocates memory
for it if needed.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/v4l2-core/v4l2-ctrls.c | 20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index b33a8bee82b0..171ab389afdd 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -2018,13 +2018,18 @@ EXPORT_SYMBOL(v4l2_ctrl_find);
 /* Allocate a new v4l2_ctrl_ref and hook it into the handler. */
 static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
 			   struct v4l2_ctrl *ctrl,
-			   bool from_other_dev)
+			   struct v4l2_ctrl_ref **ctrl_ref,
+			   bool from_other_dev, bool allocate_req)
 {
 	struct v4l2_ctrl_ref *ref;
 	struct v4l2_ctrl_ref *new_ref;
 	u32 id = ctrl->id;
 	u32 class_ctrl = V4L2_CTRL_ID2WHICH(id) | 1;
 	int bucket = id % hdl->nr_of_buckets;	/* which bucket to use */
+	unsigned int sz_extra = 0;
+
+	if (ctrl_ref)
+		*ctrl_ref = NULL;
 
 	/*
 	 * Automatically add the control class if it is not yet present and
@@ -2038,11 +2043,16 @@ static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
 	if (hdl->error)
 		return hdl->error;
 
-	new_ref = kzalloc(sizeof(*new_ref), GFP_KERNEL);
+	if (allocate_req)
+		sz_extra = ctrl->elems * ctrl->elem_size;
+	new_ref = kzalloc(sizeof(*new_ref) + sz_extra, GFP_KERNEL);
 	if (!new_ref)
 		return handler_set_err(hdl, -ENOMEM);
 	new_ref->ctrl = ctrl;
 	new_ref->from_other_dev = from_other_dev;
+	if (sz_extra)
+		new_ref->p_req.p = &new_ref[1];
+
 	if (ctrl->handler == hdl) {
 		/* By default each control starts in a cluster of its own.
 		   new_ref->ctrl is basically a cluster array with one
@@ -2082,6 +2092,8 @@ static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
 	/* Insert the control node in the hash */
 	new_ref->next = hdl->buckets[bucket];
 	hdl->buckets[bucket] = new_ref;
+	if (ctrl_ref)
+		*ctrl_ref = new_ref;
 
 unlock:
 	mutex_unlock(hdl->lock);
@@ -2223,7 +2235,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 		ctrl->type_ops->init(ctrl, idx, ctrl->p_new);
 	}
 
-	if (handler_new_ref(hdl, ctrl, false)) {
+	if (handler_new_ref(hdl, ctrl, NULL, false, false)) {
 		kvfree(ctrl);
 		return NULL;
 	}
@@ -2416,7 +2428,7 @@ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
 		/* Filter any unwanted controls */
 		if (filter && !filter(ctrl))
 			continue;
-		ret = handler_new_ref(hdl, ctrl, from_other_dev);
+		ret = handler_new_ref(hdl, ctrl, NULL, from_other_dev, false);
 		if (ret)
 			break;
 	}
-- 
2.18.0

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

* [PATCHv17 13/34] v4l2-ctrls: use ref in helper instead of ctrl
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (11 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 12/34] v4l2-ctrls: alloc memory for p_req Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-09 20:22   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 14/34] v4l2-ctrls: add core request support Hans Verkuil
                   ` (20 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

The next patch needs the reference to a control instead of the
control itself, so change struct v4l2_ctrl_helper accordingly.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/v4l2-core/v4l2-ctrls.c | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index 171ab389afdd..570b6f8ae46a 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -37,8 +37,8 @@
 struct v4l2_ctrl_helper {
 	/* Pointer to the control reference of the master control */
 	struct v4l2_ctrl_ref *mref;
-	/* The control corresponding to the v4l2_ext_control ID field. */
-	struct v4l2_ctrl *ctrl;
+	/* The control ref corresponding to the v4l2_ext_control ID field. */
+	struct v4l2_ctrl_ref *ref;
 	/* v4l2_ext_control index of the next control belonging to the
 	   same cluster, or 0 if there isn't any. */
 	u32 next;
@@ -2908,6 +2908,7 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
 		ref = find_ref_lock(hdl, id);
 		if (ref == NULL)
 			return -EINVAL;
+		h->ref = ref;
 		ctrl = ref->ctrl;
 		if (ctrl->flags & V4L2_CTRL_FLAG_DISABLED)
 			return -EINVAL;
@@ -2930,7 +2931,6 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
 		}
 		/* Store the ref to the master control of the cluster */
 		h->mref = ref;
-		h->ctrl = ctrl;
 		/* Initially set next to 0, meaning that there is no other
 		   control in this helper array belonging to the same
 		   cluster */
@@ -3015,7 +3015,7 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
 	cs->error_idx = cs->count;
 
 	for (i = 0; !ret && i < cs->count; i++)
-		if (helpers[i].ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
+		if (helpers[i].ref->ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
 			ret = -EACCES;
 
 	for (i = 0; !ret && i < cs->count; i++) {
@@ -3050,7 +3050,7 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
 
 			do {
 				ret = ctrl_to_user(cs->controls + idx,
-						   helpers[idx].ctrl);
+						   helpers[idx].ref->ctrl);
 				idx = helpers[idx].next;
 			} while (!ret && idx);
 		}
@@ -3202,7 +3202,7 @@ static int validate_ctrls(struct v4l2_ext_controls *cs,
 
 	cs->error_idx = cs->count;
 	for (i = 0; i < cs->count; i++) {
-		struct v4l2_ctrl *ctrl = helpers[i].ctrl;
+		struct v4l2_ctrl *ctrl = helpers[i].ref->ctrl;
 		union v4l2_ctrl_ptr p_new;
 
 		cs->error_idx = i;
@@ -3314,7 +3314,7 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
 			do {
 				/* Check if the auto control is part of the
 				   list, and remember the new value. */
-				if (helpers[tmp_idx].ctrl == master)
+				if (helpers[tmp_idx].ref->ctrl == master)
 					new_auto_val = cs->controls[tmp_idx].value;
 				tmp_idx = helpers[tmp_idx].next;
 			} while (tmp_idx);
@@ -3327,7 +3327,7 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
 		/* Copy the new caller-supplied control values.
 		   user_to_new() sets 'is_new' to 1. */
 		do {
-			struct v4l2_ctrl *ctrl = helpers[idx].ctrl;
+			struct v4l2_ctrl *ctrl = helpers[idx].ref->ctrl;
 
 			ret = user_to_new(cs->controls + idx, ctrl);
 			if (!ret && ctrl->is_ptr)
@@ -3343,7 +3343,7 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
 			idx = i;
 			do {
 				ret = new_to_user(cs->controls + idx,
-						helpers[idx].ctrl);
+						helpers[idx].ref->ctrl);
 				idx = helpers[idx].next;
 			} while (!ret && idx);
 		}
-- 
2.18.0

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

* [PATCHv17 14/34] v4l2-ctrls: add core request support
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (12 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 13/34] v4l2-ctrls: use ref in helper instead of ctrl Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-13 10:55   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 15/34] v4l2-ctrls: support g/s_ext_ctrls for requests Hans Verkuil
                   ` (19 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

Integrate the request support. This adds the v4l2_ctrl_request_complete
and v4l2_ctrl_request_setup functions to complete a request and (as a
helper function) to apply a request to the hardware.

It takes care of queuing requests and correctly chaining control values
in the request queue.

Note that when a request is marked completed it will copy control values
to the internal request state. This can be optimized in the future since
this is sub-optimal when dealing with large compound and/or array controls.

For the initial 'stateless codec' use-case the current implementation is
sufficient.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/v4l2-core/v4l2-ctrls.c | 326 ++++++++++++++++++++++++++-
 include/media/v4l2-ctrls.h           |  51 +++++
 2 files changed, 371 insertions(+), 6 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index 570b6f8ae46a..b8ff6d6b14cd 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -1668,6 +1668,13 @@ static int new_to_user(struct v4l2_ext_control *c,
 	return ptr_to_user(c, ctrl, ctrl->p_new);
 }
 
+/* Helper function: copy the request value back to the caller */
+static int req_to_user(struct v4l2_ext_control *c,
+		       struct v4l2_ctrl_ref *ref)
+{
+	return ptr_to_user(c, ref->ctrl, ref->p_req);
+}
+
 /* Helper function: copy the initial control value back to the caller */
 static int def_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
 {
@@ -1787,6 +1794,26 @@ static void cur_to_new(struct v4l2_ctrl *ctrl)
 	ptr_to_ptr(ctrl, ctrl->p_cur, ctrl->p_new);
 }
 
+/* Copy the new value to the request value */
+static void new_to_req(struct v4l2_ctrl_ref *ref)
+{
+	if (!ref)
+		return;
+	ptr_to_ptr(ref->ctrl, ref->ctrl->p_new, ref->p_req);
+	ref->req = ref;
+}
+
+/* Copy the request value to the new value */
+static void req_to_new(struct v4l2_ctrl_ref *ref)
+{
+	if (!ref)
+		return;
+	if (ref->req)
+		ptr_to_ptr(ref->ctrl, ref->req->p_req, ref->ctrl->p_new);
+	else
+		ptr_to_ptr(ref->ctrl, ref->ctrl->p_cur, ref->ctrl->p_new);
+}
+
 /* Return non-zero if one or more of the controls in the cluster has a new
    value that differs from the current value. */
 static int cluster_changed(struct v4l2_ctrl *master)
@@ -1896,6 +1923,9 @@ int v4l2_ctrl_handler_init_class(struct v4l2_ctrl_handler *hdl,
 	lockdep_set_class_and_name(hdl->lock, key, name);
 	INIT_LIST_HEAD(&hdl->ctrls);
 	INIT_LIST_HEAD(&hdl->ctrl_refs);
+	INIT_LIST_HEAD(&hdl->requests);
+	INIT_LIST_HEAD(&hdl->requests_queued);
+	hdl->request_is_queued = false;
 	hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8;
 	hdl->buckets = kvmalloc_array(hdl->nr_of_buckets,
 				      sizeof(hdl->buckets[0]),
@@ -1916,6 +1946,14 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
 	if (hdl == NULL || hdl->buckets == NULL)
 		return;
 
+	if (!hdl->req_obj.req && !list_empty(&hdl->requests)) {
+		struct v4l2_ctrl_handler *req, *next_req;
+
+		list_for_each_entry_safe(req, next_req, &hdl->requests, requests) {
+			media_request_object_unbind(&req->req_obj);
+			media_request_object_put(&req->req_obj);
+		}
+	}
 	mutex_lock(hdl->lock);
 	/* Free all nodes */
 	list_for_each_entry_safe(ref, next_ref, &hdl->ctrl_refs, node) {
@@ -2837,6 +2875,123 @@ int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm)
 }
 EXPORT_SYMBOL(v4l2_querymenu);
 
+static int v4l2_ctrl_request_clone(struct v4l2_ctrl_handler *hdl,
+				   const struct v4l2_ctrl_handler *from)
+{
+	struct v4l2_ctrl_ref *ref;
+	int err;
+
+	if (WARN_ON(!hdl || hdl == from))
+		return -EINVAL;
+
+	if (hdl->error)
+		return hdl->error;
+
+	WARN_ON(hdl->lock != &hdl->_lock);
+
+	mutex_lock(from->lock);
+	list_for_each_entry(ref, &from->ctrl_refs, node) {
+		struct v4l2_ctrl *ctrl = ref->ctrl;
+		struct v4l2_ctrl_ref *new_ref;
+
+		/* Skip refs inherited from other devices */
+		if (ref->from_other_dev)
+			continue;
+		/* And buttons */
+		if (ctrl->type == V4L2_CTRL_TYPE_BUTTON)
+			continue;
+		err = handler_new_ref(hdl, ctrl, &new_ref, false, true);
+		if (err)
+			break;
+	}
+	mutex_unlock(from->lock);
+	return err;
+}
+
+static void v4l2_ctrl_request_queue(struct media_request_object *obj)
+{
+	struct v4l2_ctrl_handler *hdl =
+		container_of(obj, struct v4l2_ctrl_handler, req_obj);
+	struct v4l2_ctrl_handler *main_hdl = obj->priv;
+	struct v4l2_ctrl_handler *prev_hdl = NULL;
+	struct v4l2_ctrl_ref *ref_ctrl, *ref_ctrl_prev = NULL;
+
+	if (list_empty(&main_hdl->requests_queued))
+		goto queue;
+
+	prev_hdl = list_last_entry(&main_hdl->requests_queued,
+				   struct v4l2_ctrl_handler, requests_queued);
+	/*
+	 * Note: prev_hdl and hdl must contain the same list of control
+	 * references, so if any differences are detected then that is a
+	 * driver bug and the WARN_ON is triggered.
+	 */
+	mutex_lock(prev_hdl->lock);
+	ref_ctrl_prev = list_first_entry(&prev_hdl->ctrl_refs,
+					 struct v4l2_ctrl_ref, node);
+	list_for_each_entry(ref_ctrl, &hdl->ctrl_refs, node) {
+		if (ref_ctrl->req)
+			continue;
+		while (ref_ctrl_prev->ctrl->id < ref_ctrl->ctrl->id) {
+			/* Should never happen, but just in case... */
+			if (list_is_last(&ref_ctrl_prev->node,
+					 &prev_hdl->ctrl_refs))
+				break;
+			ref_ctrl_prev = list_next_entry(ref_ctrl_prev, node);
+		}
+		if (WARN_ON(ref_ctrl_prev->ctrl->id != ref_ctrl->ctrl->id))
+			break;
+		ref_ctrl->req = ref_ctrl_prev->req;
+	}
+	mutex_unlock(prev_hdl->lock);
+queue:
+	list_add_tail(&hdl->requests_queued, &main_hdl->requests_queued);
+	hdl->request_is_queued = true;
+}
+
+static void v4l2_ctrl_request_unbind(struct media_request_object *obj)
+{
+	struct v4l2_ctrl_handler *hdl =
+		container_of(obj, struct v4l2_ctrl_handler, req_obj);
+
+	list_del_init(&hdl->requests);
+	if (hdl->request_is_queued) {
+		list_del_init(&hdl->requests_queued);
+		hdl->request_is_queued = false;
+	}
+}
+
+static void v4l2_ctrl_request_release(struct media_request_object *obj)
+{
+	struct v4l2_ctrl_handler *hdl =
+		container_of(obj, struct v4l2_ctrl_handler, req_obj);
+
+	v4l2_ctrl_handler_free(hdl);
+	kfree(hdl);
+}
+
+static const struct media_request_object_ops req_ops = {
+	.queue = v4l2_ctrl_request_queue,
+	.unbind = v4l2_ctrl_request_unbind,
+	.release = v4l2_ctrl_request_release,
+};
+
+static int v4l2_ctrl_request_bind(struct media_request *req,
+			   struct v4l2_ctrl_handler *hdl,
+			   struct v4l2_ctrl_handler *from)
+{
+	int ret;
+
+	ret = v4l2_ctrl_request_clone(hdl, from);
+
+	if (!ret) {
+		ret = media_request_object_bind(req, &req_ops,
+						from, &hdl->req_obj);
+		if (!ret)
+			list_add_tail(&hdl->requests, &from->requests);
+	}
+	return ret;
+}
 
 /* Some general notes on the atomic requirements of VIDIOC_G/TRY/S_EXT_CTRLS:
 
@@ -2898,6 +3053,7 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
 
 		if (cs->which &&
 		    cs->which != V4L2_CTRL_WHICH_DEF_VAL &&
+		    cs->which != V4L2_CTRL_WHICH_REQUEST_VAL &&
 		    V4L2_CTRL_ID2WHICH(id) != cs->which)
 			return -EINVAL;
 
@@ -2977,13 +3133,12 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
    whether there are any controls at all. */
 static int class_check(struct v4l2_ctrl_handler *hdl, u32 which)
 {
-	if (which == 0 || which == V4L2_CTRL_WHICH_DEF_VAL)
+	if (which == 0 || which == V4L2_CTRL_WHICH_DEF_VAL ||
+	    which == V4L2_CTRL_WHICH_REQUEST_VAL)
 		return 0;
 	return find_ref_lock(hdl, which | 1) ? 0 : -EINVAL;
 }
 
-
-
 /* Get extended controls. Allocates the helpers array if needed. */
 int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
 {
@@ -3049,8 +3204,12 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
 			u32 idx = i;
 
 			do {
-				ret = ctrl_to_user(cs->controls + idx,
-						   helpers[idx].ref->ctrl);
+				if (helpers[idx].ref->req)
+					ret = req_to_user(cs->controls + idx,
+						helpers[idx].ref->req);
+				else
+					ret = ctrl_to_user(cs->controls + idx,
+						helpers[idx].ref->ctrl);
 				idx = helpers[idx].next;
 			} while (!ret && idx);
 		}
@@ -3336,7 +3495,16 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
 		} while (!ret && idx);
 
 		if (!ret)
-			ret = try_or_set_cluster(fh, master, set, 0);
+			ret = try_or_set_cluster(fh, master,
+						 !hdl->req_obj.req && set, 0);
+		if (!ret && hdl->req_obj.req && set) {
+			for (j = 0; j < master->ncontrols; j++) {
+				struct v4l2_ctrl_ref *ref =
+					find_ref(hdl, master->cluster[j]->id);
+
+				new_to_req(ref);
+			}
+		}
 
 		/* Copy the new values back to userspace. */
 		if (!ret) {
@@ -3463,6 +3631,152 @@ int __v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s)
 }
 EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_string);
 
+void v4l2_ctrl_request_complete(struct media_request *req,
+				struct v4l2_ctrl_handler *main_hdl)
+{
+	struct media_request_object *obj;
+	struct v4l2_ctrl_handler *hdl;
+	struct v4l2_ctrl_ref *ref;
+
+	if (!req || !main_hdl)
+		return;
+
+	obj = media_request_object_find(req, &req_ops, main_hdl);
+	if (!obj)
+		return;
+	hdl = container_of(obj, struct v4l2_ctrl_handler, req_obj);
+
+	list_for_each_entry(ref, &hdl->ctrl_refs, node) {
+		struct v4l2_ctrl *ctrl = ref->ctrl;
+		struct v4l2_ctrl *master = ctrl->cluster[0];
+		unsigned int i;
+
+		if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE) {
+			ref->req = ref;
+
+			v4l2_ctrl_lock(master);
+			/* g_volatile_ctrl will update the current control values */
+			for (i = 0; i < master->ncontrols; i++)
+				cur_to_new(master->cluster[i]);
+			call_op(master, g_volatile_ctrl);
+			new_to_req(ref);
+			v4l2_ctrl_unlock(master);
+			continue;
+		}
+		if (ref->req == ref)
+			continue;
+
+		v4l2_ctrl_lock(ctrl);
+		if (ref->req)
+			ptr_to_ptr(ctrl, ref->req->p_req, ref->p_req);
+		else
+			ptr_to_ptr(ctrl, ctrl->p_cur, ref->p_req);
+		v4l2_ctrl_unlock(ctrl);
+	}
+
+	WARN_ON(!hdl->request_is_queued);
+	list_del_init(&hdl->requests_queued);
+	hdl->request_is_queued = false;
+	media_request_object_complete(obj);
+	media_request_object_put(obj);
+}
+EXPORT_SYMBOL(v4l2_ctrl_request_complete);
+
+void v4l2_ctrl_request_setup(struct media_request *req,
+			     struct v4l2_ctrl_handler *main_hdl)
+{
+	struct media_request_object *obj;
+	struct v4l2_ctrl_handler *hdl;
+	struct v4l2_ctrl_ref *ref;
+
+	if (!req || !main_hdl)
+		return;
+
+	if (WARN_ON(req->state != MEDIA_REQUEST_STATE_QUEUED))
+		return;
+
+	obj = media_request_object_find(req, &req_ops, main_hdl);
+	if (!obj)
+		return;
+	if (obj->completed) {
+		media_request_object_put(obj);
+		return;
+	}
+	hdl = container_of(obj, struct v4l2_ctrl_handler, req_obj);
+
+	list_for_each_entry(ref, &hdl->ctrl_refs, node)
+		ref->req_done = false;
+
+	list_for_each_entry(ref, &hdl->ctrl_refs, node) {
+		struct v4l2_ctrl *ctrl = ref->ctrl;
+		struct v4l2_ctrl *master = ctrl->cluster[0];
+		bool have_new_data = false;
+		int i;
+
+		/*
+		 * Skip if this control was already handled by a cluster.
+		 * Skip button controls and read-only controls.
+		 */
+		if (ref->req_done || ctrl->type == V4L2_CTRL_TYPE_BUTTON ||
+		    (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY))
+			continue;
+
+		v4l2_ctrl_lock(master);
+		for (i = 0; i < master->ncontrols; i++) {
+			if (master->cluster[i]) {
+				struct v4l2_ctrl_ref *r =
+					find_ref(hdl, master->cluster[i]->id);
+
+				if (r->req && r == r->req) {
+					have_new_data = true;
+					break;
+				}
+			}
+		}
+		if (!have_new_data) {
+			v4l2_ctrl_unlock(master);
+			continue;
+		}
+
+		for (i = 0; i < master->ncontrols; i++) {
+			if (master->cluster[i]) {
+				struct v4l2_ctrl_ref *r =
+					find_ref(hdl, master->cluster[i]->id);
+
+				req_to_new(r);
+				master->cluster[i]->is_new = 1;
+				r->req_done = true;
+			}
+		}
+		/*
+		 * For volatile autoclusters that are currently in auto mode
+		 * we need to discover if it will be set to manual mode.
+		 * If so, then we have to copy the current volatile values
+		 * first since those will become the new manual values (which
+		 * may be overwritten by explicit new values from this set
+		 * of controls).
+		 */
+		if (master->is_auto && master->has_volatiles &&
+		    !is_cur_manual(master)) {
+			s32 new_auto_val = *master->p_new.p_s32;
+
+			/*
+			 * If the new value == the manual value, then copy
+			 * the current volatile values.
+			 */
+			if (new_auto_val == master->manual_mode_value)
+				update_from_auto_cluster(master);
+		}
+
+		try_or_set_cluster(NULL, master, true, 0);
+
+		v4l2_ctrl_unlock(master);
+	}
+
+	media_request_object_put(obj);
+}
+EXPORT_SYMBOL(v4l2_ctrl_request_setup);
+
 void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void *priv)
 {
 	if (ctrl == NULL)
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index 3f4e062d4e3d..3617d5077b19 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -250,6 +250,12 @@ struct v4l2_ctrl {
  *		``prepare_ext_ctrls`` function at ``v4l2-ctrl.c``.
  * @from_other_dev: If true, then @ctrl was defined in another
  *		device than the &struct v4l2_ctrl_handler.
+ * @req_done:	Internal flag: if the control handler containing this control
+ *		reference is bound to a media request, then this is set when
+ *		the control has been applied. This prevents applying controls
+ *		from a cluster with multiple controls twice (when the first
+ *		control of a cluster is applied, they all are).
+ * @req:	If set, this refers to another request that sets this control.
  * @p_req:	If the control handler containing this control reference
  *		is bound to a media request, then this points to the
  *		value of the control that should be applied when the request
@@ -266,6 +272,8 @@ struct v4l2_ctrl_ref {
 	struct v4l2_ctrl *ctrl;
 	struct v4l2_ctrl_helper *helper;
 	bool from_other_dev;
+	bool req_done;
+	struct v4l2_ctrl_ref *req;
 	union v4l2_ctrl_ptr p_req;
 };
 
@@ -290,6 +298,15 @@ struct v4l2_ctrl_ref {
  * @notify_priv: Passed as argument to the v4l2_ctrl notify callback.
  * @nr_of_buckets: Total number of buckets in the array.
  * @error:	The error code of the first failed control addition.
+ * @request_is_queued: True if the request was queued.
+ * @requests:	List to keep track of open control handler request objects.
+ *		For the parent control handler (@req_obj.req == NULL) this
+ *		is the list header. When the parent control handler is
+ *		removed, it has to unbind and put all these requests since
+ *		they refer to the parent.
+ * @requests_queued: List of the queued requests. This determines the order
+ *		in which these controls are applied. Once the request is
+ *		completed it is removed from this list.
  * @req_obj:	The &struct media_request_object, used to link into a
  *		&struct media_request. This request object has a refcount.
  */
@@ -304,6 +321,9 @@ struct v4l2_ctrl_handler {
 	void *notify_priv;
 	u16 nr_of_buckets;
 	int error;
+	bool request_is_queued;
+	struct list_head requests;
+	struct list_head requests_queued;
 	struct media_request_object req_obj;
 };
 
@@ -1062,6 +1082,37 @@ int v4l2_ctrl_subscribe_event(struct v4l2_fh *fh,
  */
 __poll_t v4l2_ctrl_poll(struct file *file, struct poll_table_struct *wait);
 
+/**
+ * v4l2_ctrl_request_setup - helper function to apply control values in a request
+ *
+ * @req: The request
+ * @hdl: The control handler
+ *
+ * This is a helper function to call the control handler's s_ctrl callback with
+ * the control values contained in the request. Do note that this approach of
+ * applying control values in a request is only applicable to memory-to-memory
+ * devices.
+ */
+void v4l2_ctrl_request_setup(struct media_request *req,
+			     struct v4l2_ctrl_handler *hdl);
+
+/**
+ * v4l2_ctrl_request_complete - Complete a control handler request object
+ *
+ * @req: The request
+ * @hdl: The control handler
+ *
+ * This function is to be called on each control handler that may have had a
+ * request object associated with it, i.e. control handlers of a driver that
+ * supports requests.
+ *
+ * The function first obtains the values of any volatile controls in the control
+ * handler and attach them to the request. Then, the function completes the
+ * request object.
+ */
+void v4l2_ctrl_request_complete(struct media_request *req,
+				struct v4l2_ctrl_handler *hdl);
+
 /* Helpers for ioctl_ops */
 
 /**
-- 
2.18.0

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

* [PATCHv17 15/34] v4l2-ctrls: support g/s_ext_ctrls for requests
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (13 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 14/34] v4l2-ctrls: add core request support Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-13 11:05   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 16/34] v4l2-ctrls: add v4l2_ctrl_request_hdl_find/put/ctrl_find functions Hans Verkuil
                   ` (18 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

The v4l2_g/s_ext_ctrls functions now support control handlers that
represent requests.

The v4l2_ctrls_find_req_obj() function is responsible for finding the
request from the fd.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/platform/omap3isp/ispvideo.c |   2 +-
 drivers/media/v4l2-core/v4l2-ctrls.c       | 138 +++++++++++++++++++--
 drivers/media/v4l2-core/v4l2-ioctl.c       |  12 +-
 drivers/media/v4l2-core/v4l2-subdev.c      |   9 +-
 include/media/v4l2-ctrls.h                 |   7 +-
 5 files changed, 149 insertions(+), 19 deletions(-)

diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c
index 9d228eac24ea..674e7fd3ad99 100644
--- a/drivers/media/platform/omap3isp/ispvideo.c
+++ b/drivers/media/platform/omap3isp/ispvideo.c
@@ -1028,7 +1028,7 @@ static int isp_video_check_external_subdevs(struct isp_video *video,
 	ctrls.count = 1;
 	ctrls.controls = &ctrl;
 
-	ret = v4l2_g_ext_ctrls(pipe->external->ctrl_handler, &ctrls);
+	ret = v4l2_g_ext_ctrls(pipe->external->ctrl_handler, NULL, &ctrls);
 	if (ret < 0) {
 		dev_warn(isp->dev, "no pixel rate control in subdev %s\n",
 			 pipe->external->name);
diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index b8ff6d6b14cd..86a6ae54ccaa 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -3140,7 +3140,8 @@ static int class_check(struct v4l2_ctrl_handler *hdl, u32 which)
 }
 
 /* Get extended controls. Allocates the helpers array if needed. */
-int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
+int __v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl,
+		       struct v4l2_ext_controls *cs)
 {
 	struct v4l2_ctrl_helper helper[4];
 	struct v4l2_ctrl_helper *helpers = helper;
@@ -3220,6 +3221,83 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
 		kvfree(helpers);
 	return ret;
 }
+
+static struct media_request_object *
+v4l2_ctrls_find_req_obj(struct v4l2_ctrl_handler *hdl,
+			struct media_request *req, bool set)
+{
+	struct media_request_object *obj;
+	struct v4l2_ctrl_handler *new_hdl;
+	int ret;
+
+	if (IS_ERR(req))
+		return ERR_CAST(req);
+
+	if (set && WARN_ON(req->state != MEDIA_REQUEST_STATE_UPDATING))
+		return ERR_PTR(-EBUSY);
+
+	obj = media_request_object_find(req, &req_ops, hdl);
+	if (obj)
+		return obj;
+	if (!set)
+		return ERR_PTR(-ENOENT);
+
+	new_hdl = kzalloc(sizeof(*new_hdl), GFP_KERNEL);
+	if (!new_hdl)
+		return ERR_PTR(-ENOMEM);
+
+	obj = &new_hdl->req_obj;
+	ret = v4l2_ctrl_handler_init(new_hdl, (hdl->nr_of_buckets - 1) * 8);
+	if (!ret)
+		ret = v4l2_ctrl_request_bind(req, new_hdl, hdl);
+	if (ret) {
+		kfree(new_hdl);
+
+		return ERR_PTR(ret);
+	}
+
+	media_request_object_get(obj);
+	return obj;
+}
+
+int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct media_device *mdev,
+		     struct v4l2_ext_controls *cs)
+{
+	struct media_request_object *obj = NULL;
+	int ret;
+
+	if (cs->which == V4L2_CTRL_WHICH_REQUEST_VAL) {
+		struct media_request *req;
+
+		if (!mdev || cs->request_fd < 0)
+			return -EINVAL;
+
+		req = media_request_get_by_fd(mdev, cs->request_fd);
+		if (IS_ERR(req))
+			return PTR_ERR(req);
+
+		if (req->state != MEDIA_REQUEST_STATE_IDLE &&
+		    req->state != MEDIA_REQUEST_STATE_COMPLETE) {
+			media_request_put(req);
+			return -EBUSY;
+		}
+
+		obj = v4l2_ctrls_find_req_obj(hdl, req, false);
+		/* Reference to the request held through obj */
+		media_request_put(req);
+		if (IS_ERR(obj))
+			return PTR_ERR(obj);
+
+		hdl = container_of(obj, struct v4l2_ctrl_handler,
+				   req_obj);
+	}
+
+	ret = __v4l2_g_ext_ctrls(hdl, cs);
+
+	if (obj)
+		media_request_object_put(obj);
+	return ret;
+}
 EXPORT_SYMBOL(v4l2_g_ext_ctrls);
 
 /* Helper function to get a single control */
@@ -3408,9 +3486,9 @@ static void update_from_auto_cluster(struct v4l2_ctrl *master)
 }
 
 /* Try or try-and-set controls */
-static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
-			     struct v4l2_ext_controls *cs,
-			     bool set)
+static int __try_set_ext_ctrls(struct v4l2_fh *fh,
+			       struct v4l2_ctrl_handler *hdl,
+			       struct v4l2_ext_controls *cs, bool set)
 {
 	struct v4l2_ctrl_helper helper[4];
 	struct v4l2_ctrl_helper *helpers = helper;
@@ -3523,16 +3601,60 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
 	return ret;
 }
 
-int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
+static int try_set_ext_ctrls(struct v4l2_fh *fh,
+			     struct v4l2_ctrl_handler *hdl, struct media_device *mdev,
+			     struct v4l2_ext_controls *cs, bool set)
+{
+	struct media_request_object *obj = NULL;
+	struct media_request *req = NULL;
+	int ret;
+
+	if (cs->which == V4L2_CTRL_WHICH_REQUEST_VAL) {
+		if (!mdev || cs->request_fd < 0)
+			return -EINVAL;
+
+		req = media_request_get_by_fd(mdev, cs->request_fd);
+		if (IS_ERR(req))
+			return PTR_ERR(req);
+
+		ret = media_request_lock_for_update(req);
+		if (ret) {
+			media_request_put(req);
+			return ret;
+		}
+
+		obj = v4l2_ctrls_find_req_obj(hdl, req, set);
+		/* Reference to the request held through obj */
+		media_request_put(req);
+		if (IS_ERR(obj)) {
+			media_request_unlock_for_update(req);
+			return PTR_ERR(obj);
+		}
+		hdl = container_of(obj, struct v4l2_ctrl_handler,
+				   req_obj);
+	}
+
+	ret = __try_set_ext_ctrls(fh, hdl, cs, set);
+
+	if (obj) {
+		media_request_unlock_for_update(obj->req);
+		media_request_object_put(obj);
+	}
+
+	return ret;
+}
+
+int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct media_device *mdev,
+		       struct v4l2_ext_controls *cs)
 {
-	return try_set_ext_ctrls(NULL, hdl, cs, false);
+	return try_set_ext_ctrls(NULL, hdl, mdev, cs, false);
 }
 EXPORT_SYMBOL(v4l2_try_ext_ctrls);
 
 int v4l2_s_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
-					struct v4l2_ext_controls *cs)
+		     struct media_device *mdev, struct v4l2_ext_controls *cs)
 {
-	return try_set_ext_ctrls(fh, hdl, cs, true);
+	return try_set_ext_ctrls(fh, hdl, mdev, cs, true);
 }
 EXPORT_SYMBOL(v4l2_s_ext_ctrls);
 
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 03241d6b7ef8..20b5145a5254 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -2109,9 +2109,9 @@ static int v4l_g_ext_ctrls(const struct v4l2_ioctl_ops *ops,
 
 	p->error_idx = p->count;
 	if (vfh && vfh->ctrl_handler)
-		return v4l2_g_ext_ctrls(vfh->ctrl_handler, p);
+		return v4l2_g_ext_ctrls(vfh->ctrl_handler, vfd->v4l2_dev->mdev, p);
 	if (vfd->ctrl_handler)
-		return v4l2_g_ext_ctrls(vfd->ctrl_handler, p);
+		return v4l2_g_ext_ctrls(vfd->ctrl_handler, vfd->v4l2_dev->mdev, p);
 	if (ops->vidioc_g_ext_ctrls == NULL)
 		return -ENOTTY;
 	return check_ext_ctrls(p, 0) ? ops->vidioc_g_ext_ctrls(file, fh, p) :
@@ -2128,9 +2128,9 @@ static int v4l_s_ext_ctrls(const struct v4l2_ioctl_ops *ops,
 
 	p->error_idx = p->count;
 	if (vfh && vfh->ctrl_handler)
-		return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, p);
+		return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, vfd->v4l2_dev->mdev, p);
 	if (vfd->ctrl_handler)
-		return v4l2_s_ext_ctrls(NULL, vfd->ctrl_handler, p);
+		return v4l2_s_ext_ctrls(NULL, vfd->ctrl_handler, vfd->v4l2_dev->mdev, p);
 	if (ops->vidioc_s_ext_ctrls == NULL)
 		return -ENOTTY;
 	return check_ext_ctrls(p, 0) ? ops->vidioc_s_ext_ctrls(file, fh, p) :
@@ -2147,9 +2147,9 @@ static int v4l_try_ext_ctrls(const struct v4l2_ioctl_ops *ops,
 
 	p->error_idx = p->count;
 	if (vfh && vfh->ctrl_handler)
-		return v4l2_try_ext_ctrls(vfh->ctrl_handler, p);
+		return v4l2_try_ext_ctrls(vfh->ctrl_handler, vfd->v4l2_dev->mdev, p);
 	if (vfd->ctrl_handler)
-		return v4l2_try_ext_ctrls(vfd->ctrl_handler, p);
+		return v4l2_try_ext_ctrls(vfd->ctrl_handler, vfd->v4l2_dev->mdev, p);
 	if (ops->vidioc_try_ext_ctrls == NULL)
 		return -ENOTTY;
 	return check_ext_ctrls(p, 0) ? ops->vidioc_try_ext_ctrls(file, fh, p) :
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index 2b63fa6b6fc9..0fdbd85532dd 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -222,17 +222,20 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
 	case VIDIOC_G_EXT_CTRLS:
 		if (!vfh->ctrl_handler)
 			return -ENOTTY;
-		return v4l2_g_ext_ctrls(vfh->ctrl_handler, arg);
+		return v4l2_g_ext_ctrls(vfh->ctrl_handler,
+					sd->v4l2_dev->mdev, arg);
 
 	case VIDIOC_S_EXT_CTRLS:
 		if (!vfh->ctrl_handler)
 			return -ENOTTY;
-		return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, arg);
+		return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler,
+					sd->v4l2_dev->mdev, arg);
 
 	case VIDIOC_TRY_EXT_CTRLS:
 		if (!vfh->ctrl_handler)
 			return -ENOTTY;
-		return v4l2_try_ext_ctrls(vfh->ctrl_handler, arg);
+		return v4l2_try_ext_ctrls(vfh->ctrl_handler,
+					  sd->v4l2_dev->mdev, arg);
 
 	case VIDIOC_DQEVENT:
 		if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index 3617d5077b19..98b1e70a4a46 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -1179,11 +1179,12 @@ int v4l2_s_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
  *	:ref:`VIDIOC_G_EXT_CTRLS <vidioc_g_ext_ctrls>` ioctl
  *
  * @hdl: pointer to &struct v4l2_ctrl_handler
+ * @mdev: pointer to &struct media_device
  * @c: pointer to &struct v4l2_ext_controls
  *
  * If hdl == NULL then they will all return -EINVAL.
  */
-int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl,
+int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct media_device *mdev,
 		     struct v4l2_ext_controls *c);
 
 /**
@@ -1191,11 +1192,13 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl,
  *	:ref:`VIDIOC_TRY_EXT_CTRLS <vidioc_g_ext_ctrls>` ioctl
  *
  * @hdl: pointer to &struct v4l2_ctrl_handler
+ * @mdev: pointer to &struct media_device
  * @c: pointer to &struct v4l2_ext_controls
  *
  * If hdl == NULL then they will all return -EINVAL.
  */
 int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl,
+		       struct media_device *mdev,
 		       struct v4l2_ext_controls *c);
 
 /**
@@ -1204,11 +1207,13 @@ int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl,
  *
  * @fh: pointer to &struct v4l2_fh
  * @hdl: pointer to &struct v4l2_ctrl_handler
+ * @mdev: pointer to &struct media_device
  * @c: pointer to &struct v4l2_ext_controls
  *
  * If hdl == NULL then they will all return -EINVAL.
  */
 int v4l2_s_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
+		     struct media_device *mdev,
 		     struct v4l2_ext_controls *c);
 
 /**
-- 
2.18.0

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

* [PATCHv17 16/34] v4l2-ctrls: add v4l2_ctrl_request_hdl_find/put/ctrl_find functions
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (14 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 15/34] v4l2-ctrls: support g/s_ext_ctrls for requests Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-13 11:07   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 17/34] vb2: store userspace data in vb2_v4l2_buffer Hans Verkuil
                   ` (17 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

If a driver needs to find/inspect the controls set in a request then
it can use these functions.

E.g. to check if a required control is set in a request use this in the
req_validate() implementation:

	int res = -EINVAL;

	hdl = v4l2_ctrl_request_hdl_find(req, parent_hdl);
	if (hdl) {
		if (v4l2_ctrl_request_hdl_ctrl_find(hdl, ctrl_id))
			res = 0;
		v4l2_ctrl_request_hdl_put(hdl);
	}
	return res;

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/v4l2-core/v4l2-ctrls.c | 25 ++++++++++++++++
 include/media/v4l2-ctrls.h           | 44 +++++++++++++++++++++++++++-
 2 files changed, 68 insertions(+), 1 deletion(-)

diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index 86a6ae54ccaa..2a30be824491 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -2976,6 +2976,31 @@ static const struct media_request_object_ops req_ops = {
 	.release = v4l2_ctrl_request_release,
 };
 
+struct v4l2_ctrl_handler *v4l2_ctrl_request_hdl_find(struct media_request *req,
+					struct v4l2_ctrl_handler *parent)
+{
+	struct media_request_object *obj;
+
+	if (WARN_ON(req->state != MEDIA_REQUEST_STATE_VALIDATING &&
+		    req->state != MEDIA_REQUEST_STATE_QUEUED))
+		return NULL;
+
+	obj = media_request_object_find(req, &req_ops, parent);
+	if (obj)
+		return container_of(obj, struct v4l2_ctrl_handler, req_obj);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(v4l2_ctrl_request_hdl_find);
+
+struct v4l2_ctrl *
+v4l2_ctrl_request_hdl_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id)
+{
+	struct v4l2_ctrl_ref *ref = find_ref_lock(hdl, id);
+
+	return (ref && ref->req == ref) ? ref->ctrl : NULL;
+}
+EXPORT_SYMBOL_GPL(v4l2_ctrl_request_hdl_ctrl_find);
+
 static int v4l2_ctrl_request_bind(struct media_request *req,
 			   struct v4l2_ctrl_handler *hdl,
 			   struct v4l2_ctrl_handler *from)
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index 98b1e70a4a46..aeb7f3c24ef7 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -1111,7 +1111,49 @@ void v4l2_ctrl_request_setup(struct media_request *req,
  * request object.
  */
 void v4l2_ctrl_request_complete(struct media_request *req,
-				struct v4l2_ctrl_handler *hdl);
+				struct v4l2_ctrl_handler *parent);
+
+/**
+ * v4l2_ctrl_request_hdl_find - Find the control handler in the request
+ *
+ * @req: The request
+ * @parent: The parent control handler ('priv' in media_request_object_find())
+ *
+ * This function finds the control handler in the request. It may return
+ * NULL if not found. When done, you must call v4l2_ctrl_request_put_hdl()
+ * with the returned handler pointer.
+ *
+ * If the request is not in state VALIDATING or QUEUED, then this function
+ * will always return NULL.
+ */
+struct v4l2_ctrl_handler *v4l2_ctrl_request_hdl_find(struct media_request *req,
+					struct v4l2_ctrl_handler *parent);
+
+/**
+ * v4l2_ctrl_request_hdl_put - Put the control handler
+ *
+ * @hdl: Put this control handler
+ *
+ * This function released the control handler previously obtained from'
+ * v4l2_ctrl_request_hdl_find().
+ */
+static inline void v4l2_ctrl_request_hdl_put(struct v4l2_ctrl_handler *hdl)
+{
+	if (hdl)
+		media_request_object_put(&hdl->req_obj);
+}
+
+/**
+ * v4l2_ctrl_request_ctrl_find() - Find a control with the given ID.
+ *
+ * @hdl: The control handler from the request.
+ * @id: The ID of the control to find.
+ *
+ * This function returns a pointer to the control if this control is
+ * part of the request or NULL otherwise.
+ */
+struct v4l2_ctrl *
+v4l2_ctrl_request_hdl_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id);
 
 /* Helpers for ioctl_ops */
 
-- 
2.18.0

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

* [PATCHv17 17/34] vb2: store userspace data in vb2_v4l2_buffer
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (15 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 16/34] v4l2-ctrls: add v4l2_ctrl_request_hdl_find/put/ctrl_find functions Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-13 11:15   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 18/34] davinci_vpfe: remove bogus vb2->state check Hans Verkuil
                   ` (16 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

The userspace-provided plane data needs to be stored in
vb2_v4l2_buffer. Currently this information is applied by
__fill_vb2_buffer() which is called by the core prepare_buf
and qbuf functions, but when using requests these functions
aren't called yet since the buffer won't be prepared until
the media request is actually queued.

In the meantime this information has to be stored somewhere
and vb2_v4l2_buffer is a good place for it.

The __fill_vb2_buffer callback now just copies the relevant
information from vb2_v4l2_buffer into the planes array.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 .../media/common/videobuf2/videobuf2-core.c   |  43 +--
 .../media/common/videobuf2/videobuf2-v4l2.c   | 327 ++++++++++--------
 drivers/media/dvb-core/dvb_vb2.c              |   3 +-
 include/media/videobuf2-core.h                |   3 +-
 include/media/videobuf2-v4l2.h                |   2 +
 5 files changed, 209 insertions(+), 169 deletions(-)

diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
index 5653e8eebe2b..7401a17c80ca 100644
--- a/drivers/media/common/videobuf2/videobuf2-core.c
+++ b/drivers/media/common/videobuf2/videobuf2-core.c
@@ -967,20 +967,19 @@ EXPORT_SYMBOL_GPL(vb2_discard_done);
 /*
  * __prepare_mmap() - prepare an MMAP buffer
  */
-static int __prepare_mmap(struct vb2_buffer *vb, const void *pb)
+static int __prepare_mmap(struct vb2_buffer *vb)
 {
 	int ret = 0;
 
-	if (pb)
-		ret = call_bufop(vb->vb2_queue, fill_vb2_buffer,
-				 vb, pb, vb->planes);
+	ret = call_bufop(vb->vb2_queue, fill_vb2_buffer,
+			 vb, vb->planes);
 	return ret ? ret : call_vb_qop(vb, buf_prepare, vb);
 }
 
 /*
  * __prepare_userptr() - prepare a USERPTR buffer
  */
-static int __prepare_userptr(struct vb2_buffer *vb, const void *pb)
+static int __prepare_userptr(struct vb2_buffer *vb)
 {
 	struct vb2_plane planes[VB2_MAX_PLANES];
 	struct vb2_queue *q = vb->vb2_queue;
@@ -991,12 +990,10 @@ static int __prepare_userptr(struct vb2_buffer *vb, const void *pb)
 
 	memset(planes, 0, sizeof(planes[0]) * vb->num_planes);
 	/* Copy relevant information provided by the userspace */
-	if (pb) {
-		ret = call_bufop(vb->vb2_queue, fill_vb2_buffer,
-				 vb, pb, planes);
-		if (ret)
-			return ret;
-	}
+	ret = call_bufop(vb->vb2_queue, fill_vb2_buffer,
+			 vb, planes);
+	if (ret)
+		return ret;
 
 	for (plane = 0; plane < vb->num_planes; ++plane) {
 		/* Skip the plane if already verified */
@@ -1096,7 +1093,7 @@ static int __prepare_userptr(struct vb2_buffer *vb, const void *pb)
 /*
  * __prepare_dmabuf() - prepare a DMABUF buffer
  */
-static int __prepare_dmabuf(struct vb2_buffer *vb, const void *pb)
+static int __prepare_dmabuf(struct vb2_buffer *vb)
 {
 	struct vb2_plane planes[VB2_MAX_PLANES];
 	struct vb2_queue *q = vb->vb2_queue;
@@ -1107,12 +1104,10 @@ static int __prepare_dmabuf(struct vb2_buffer *vb, const void *pb)
 
 	memset(planes, 0, sizeof(planes[0]) * vb->num_planes);
 	/* Copy relevant information provided by the userspace */
-	if (pb) {
-		ret = call_bufop(vb->vb2_queue, fill_vb2_buffer,
-				 vb, pb, planes);
-		if (ret)
-			return ret;
-	}
+	ret = call_bufop(vb->vb2_queue, fill_vb2_buffer,
+			 vb, planes);
+	if (ret)
+		return ret;
 
 	for (plane = 0; plane < vb->num_planes; ++plane) {
 		struct dma_buf *dbuf = dma_buf_get(planes[plane].m.fd);
@@ -1241,7 +1236,7 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
 	call_void_vb_qop(vb, buf_queue, vb);
 }
 
-static int __buf_prepare(struct vb2_buffer *vb, const void *pb)
+static int __buf_prepare(struct vb2_buffer *vb)
 {
 	struct vb2_queue *q = vb->vb2_queue;
 	unsigned int plane;
@@ -1256,13 +1251,13 @@ static int __buf_prepare(struct vb2_buffer *vb, const void *pb)
 
 	switch (q->memory) {
 	case VB2_MEMORY_MMAP:
-		ret = __prepare_mmap(vb, pb);
+		ret = __prepare_mmap(vb);
 		break;
 	case VB2_MEMORY_USERPTR:
-		ret = __prepare_userptr(vb, pb);
+		ret = __prepare_userptr(vb);
 		break;
 	case VB2_MEMORY_DMABUF:
-		ret = __prepare_dmabuf(vb, pb);
+		ret = __prepare_dmabuf(vb);
 		break;
 	default:
 		WARN(1, "Invalid queue type\n");
@@ -1296,7 +1291,7 @@ int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb)
 		return -EINVAL;
 	}
 
-	ret = __buf_prepare(vb, pb);
+	ret = __buf_prepare(vb);
 	if (ret)
 		return ret;
 
@@ -1386,7 +1381,7 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb)
 
 	switch (vb->state) {
 	case VB2_BUF_STATE_DEQUEUED:
-		ret = __buf_prepare(vb, pb);
+		ret = __buf_prepare(vb);
 		if (ret)
 			return ret;
 		break;
diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c
index 886a2d8d5c6c..360dc4e7d413 100644
--- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
+++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
@@ -154,9 +154,177 @@ static void vb2_warn_zero_bytesused(struct vb2_buffer *vb)
 		pr_warn("use the actual size instead.\n");
 }
 
+static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
+{
+	struct vb2_queue *q = vb->vb2_queue;
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct vb2_plane *planes = vbuf->planes;
+	unsigned int plane;
+	int ret;
+
+	ret = __verify_length(vb, b);
+	if (ret < 0) {
+		dprintk(1, "plane parameters verification failed: %d\n", ret);
+		return ret;
+	}
+	if (b->field == V4L2_FIELD_ALTERNATE && q->is_output) {
+		/*
+		 * If the format's field is ALTERNATE, then the buffer's field
+		 * should be either TOP or BOTTOM, not ALTERNATE since that
+		 * makes no sense. The driver has to know whether the
+		 * buffer represents a top or a bottom field in order to
+		 * program any DMA correctly. Using ALTERNATE is wrong, since
+		 * that just says that it is either a top or a bottom field,
+		 * but not which of the two it is.
+		 */
+		dprintk(1, "the field is incorrectly set to ALTERNATE for an output buffer\n");
+		return -EINVAL;
+	}
+	vbuf->sequence = 0;
+
+	if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
+		switch (b->memory) {
+		case VB2_MEMORY_USERPTR:
+			for (plane = 0; plane < vb->num_planes; ++plane) {
+				planes[plane].m.userptr =
+					b->m.planes[plane].m.userptr;
+				planes[plane].length =
+					b->m.planes[plane].length;
+			}
+			break;
+		case VB2_MEMORY_DMABUF:
+			for (plane = 0; plane < vb->num_planes; ++plane) {
+				planes[plane].m.fd =
+					b->m.planes[plane].m.fd;
+				planes[plane].length =
+					b->m.planes[plane].length;
+			}
+			break;
+		default:
+			for (plane = 0; plane < vb->num_planes; ++plane) {
+				planes[plane].m.offset =
+					vb->planes[plane].m.offset;
+				planes[plane].length =
+					vb->planes[plane].length;
+			}
+			break;
+		}
+
+		/* Fill in driver-provided information for OUTPUT types */
+		if (V4L2_TYPE_IS_OUTPUT(b->type)) {
+			/*
+			 * Will have to go up to b->length when API starts
+			 * accepting variable number of planes.
+			 *
+			 * If bytesused == 0 for the output buffer, then fall
+			 * back to the full buffer size. In that case
+			 * userspace clearly never bothered to set it and
+			 * it's a safe assumption that they really meant to
+			 * use the full plane sizes.
+			 *
+			 * Some drivers, e.g. old codec drivers, use bytesused == 0
+			 * as a way to indicate that streaming is finished.
+			 * In that case, the driver should use the
+			 * allow_zero_bytesused flag to keep old userspace
+			 * applications working.
+			 */
+			for (plane = 0; plane < vb->num_planes; ++plane) {
+				struct vb2_plane *pdst = &planes[plane];
+				struct v4l2_plane *psrc = &b->m.planes[plane];
+
+				if (psrc->bytesused == 0)
+					vb2_warn_zero_bytesused(vb);
+
+				if (vb->vb2_queue->allow_zero_bytesused)
+					pdst->bytesused = psrc->bytesused;
+				else
+					pdst->bytesused = psrc->bytesused ?
+						psrc->bytesused : pdst->length;
+				pdst->data_offset = psrc->data_offset;
+			}
+		}
+	} else {
+		/*
+		 * Single-planar buffers do not use planes array,
+		 * so fill in relevant v4l2_buffer struct fields instead.
+		 * In videobuf we use our internal V4l2_planes struct for
+		 * single-planar buffers as well, for simplicity.
+		 *
+		 * If bytesused == 0 for the output buffer, then fall back
+		 * to the full buffer size as that's a sensible default.
+		 *
+		 * Some drivers, e.g. old codec drivers, use bytesused == 0 as
+		 * a way to indicate that streaming is finished. In that case,
+		 * the driver should use the allow_zero_bytesused flag to keep
+		 * old userspace applications working.
+		 */
+		switch (b->memory) {
+		case VB2_MEMORY_USERPTR:
+			planes[0].m.userptr = b->m.userptr;
+			planes[0].length = b->length;
+			break;
+		case VB2_MEMORY_DMABUF:
+			planes[0].m.fd = b->m.fd;
+			planes[0].length = b->length;
+			break;
+		default:
+			planes[0].m.offset = vb->planes[0].m.offset;
+			planes[0].length = vb->planes[0].length;
+			break;
+		}
+
+		planes[0].data_offset = 0;
+		if (V4L2_TYPE_IS_OUTPUT(b->type)) {
+			if (b->bytesused == 0)
+				vb2_warn_zero_bytesused(vb);
+
+			if (vb->vb2_queue->allow_zero_bytesused)
+				planes[0].bytesused = b->bytesused;
+			else
+				planes[0].bytesused = b->bytesused ?
+					b->bytesused : planes[0].length;
+		} else
+			planes[0].bytesused = 0;
+
+	}
+
+	/* Zero flags that we handle */
+	vbuf->flags = b->flags & ~V4L2_BUFFER_MASK_FLAGS;
+	if (!vb->vb2_queue->copy_timestamp || !V4L2_TYPE_IS_OUTPUT(b->type)) {
+		/*
+		 * Non-COPY timestamps and non-OUTPUT queues will get
+		 * their timestamp and timestamp source flags from the
+		 * queue.
+		 */
+		vbuf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+	}
+
+	if (V4L2_TYPE_IS_OUTPUT(b->type)) {
+		/*
+		 * For output buffers mask out the timecode flag:
+		 * this will be handled later in vb2_qbuf().
+		 * The 'field' is valid metadata for this output buffer
+		 * and so that needs to be copied here.
+		 */
+		vbuf->flags &= ~V4L2_BUF_FLAG_TIMECODE;
+		vbuf->field = b->field;
+	} else {
+		/* Zero any output buffer flags as this is a capture buffer */
+		vbuf->flags &= ~V4L2_BUFFER_OUT_FLAGS;
+		/* Zero last flag, this is a signal from driver to userspace */
+		vbuf->flags &= ~V4L2_BUF_FLAG_LAST;
+	}
+
+	return 0;
+}
+
 static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b,
 				    const char *opname)
 {
+	struct vb2_v4l2_buffer *vbuf;
+	struct vb2_buffer *vb;
+	int ret;
+
 	if (b->type != q->type) {
 		dprintk(1, "%s: invalid buffer type\n", opname);
 		return -EINVAL;
@@ -178,7 +346,15 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b,
 		return -EINVAL;
 	}
 
-	return __verify_planes_array(q->bufs[b->index], b);
+	vb = q->bufs[b->index];
+	vbuf = to_vb2_v4l2_buffer(vb);
+	ret = __verify_planes_array(vb, b);
+	if (ret)
+		return ret;
+
+	/* Copy relevant information provided by the userspace */
+	memset(vbuf->planes, 0, sizeof(vbuf->planes[0]) * vb->num_planes);
+	return vb2_fill_vb2_v4l2_buffer(vb, b);
 }
 
 /*
@@ -291,153 +467,22 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
  * v4l2_buffer by the userspace. It also verifies that struct
  * v4l2_buffer has a valid number of planes.
  */
-static int __fill_vb2_buffer(struct vb2_buffer *vb,
-		const void *pb, struct vb2_plane *planes)
+static int __fill_vb2_buffer(struct vb2_buffer *vb, struct vb2_plane *planes)
 {
-	struct vb2_queue *q = vb->vb2_queue;
-	const struct v4l2_buffer *b = pb;
 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
 	unsigned int plane;
-	int ret;
-
-	ret = __verify_length(vb, b);
-	if (ret < 0) {
-		dprintk(1, "plane parameters verification failed: %d\n", ret);
-		return ret;
-	}
-	if (b->field == V4L2_FIELD_ALTERNATE && q->is_output) {
-		/*
-		 * If the format's field is ALTERNATE, then the buffer's field
-		 * should be either TOP or BOTTOM, not ALTERNATE since that
-		 * makes no sense. The driver has to know whether the
-		 * buffer represents a top or a bottom field in order to
-		 * program any DMA correctly. Using ALTERNATE is wrong, since
-		 * that just says that it is either a top or a bottom field,
-		 * but not which of the two it is.
-		 */
-		dprintk(1, "the field is incorrectly set to ALTERNATE for an output buffer\n");
-		return -EINVAL;
-	}
-	vb->timestamp = 0;
-	vbuf->sequence = 0;
-
-	if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
-		if (b->memory == VB2_MEMORY_USERPTR) {
-			for (plane = 0; plane < vb->num_planes; ++plane) {
-				planes[plane].m.userptr =
-					b->m.planes[plane].m.userptr;
-				planes[plane].length =
-					b->m.planes[plane].length;
-			}
-		}
-		if (b->memory == VB2_MEMORY_DMABUF) {
-			for (plane = 0; plane < vb->num_planes; ++plane) {
-				planes[plane].m.fd =
-					b->m.planes[plane].m.fd;
-				planes[plane].length =
-					b->m.planes[plane].length;
-			}
-		}
-
-		/* Fill in driver-provided information for OUTPUT types */
-		if (V4L2_TYPE_IS_OUTPUT(b->type)) {
-			/*
-			 * Will have to go up to b->length when API starts
-			 * accepting variable number of planes.
-			 *
-			 * If bytesused == 0 for the output buffer, then fall
-			 * back to the full buffer size. In that case
-			 * userspace clearly never bothered to set it and
-			 * it's a safe assumption that they really meant to
-			 * use the full plane sizes.
-			 *
-			 * Some drivers, e.g. old codec drivers, use bytesused == 0
-			 * as a way to indicate that streaming is finished.
-			 * In that case, the driver should use the
-			 * allow_zero_bytesused flag to keep old userspace
-			 * applications working.
-			 */
-			for (plane = 0; plane < vb->num_planes; ++plane) {
-				struct vb2_plane *pdst = &planes[plane];
-				struct v4l2_plane *psrc = &b->m.planes[plane];
-
-				if (psrc->bytesused == 0)
-					vb2_warn_zero_bytesused(vb);
 
-				if (vb->vb2_queue->allow_zero_bytesused)
-					pdst->bytesused = psrc->bytesused;
-				else
-					pdst->bytesused = psrc->bytesused ?
-						psrc->bytesused : pdst->length;
-				pdst->data_offset = psrc->data_offset;
-			}
-		}
-	} else {
-		/*
-		 * Single-planar buffers do not use planes array,
-		 * so fill in relevant v4l2_buffer struct fields instead.
-		 * In videobuf we use our internal V4l2_planes struct for
-		 * single-planar buffers as well, for simplicity.
-		 *
-		 * If bytesused == 0 for the output buffer, then fall back
-		 * to the full buffer size as that's a sensible default.
-		 *
-		 * Some drivers, e.g. old codec drivers, use bytesused == 0 as
-		 * a way to indicate that streaming is finished. In that case,
-		 * the driver should use the allow_zero_bytesused flag to keep
-		 * old userspace applications working.
-		 */
-		if (b->memory == VB2_MEMORY_USERPTR) {
-			planes[0].m.userptr = b->m.userptr;
-			planes[0].length = b->length;
-		}
+	if (!vb->vb2_queue->is_output || !vb->vb2_queue->copy_timestamp)
+		vb->timestamp = 0;
 
-		if (b->memory == VB2_MEMORY_DMABUF) {
-			planes[0].m.fd = b->m.fd;
-			planes[0].length = b->length;
+	for (plane = 0; plane < vb->num_planes; ++plane) {
+		if (vb->vb2_queue->memory != VB2_MEMORY_MMAP) {
+			planes[plane].m = vbuf->planes[plane].m;
+			planes[plane].length = vbuf->planes[plane].length;
 		}
-
-		if (V4L2_TYPE_IS_OUTPUT(b->type)) {
-			if (b->bytesused == 0)
-				vb2_warn_zero_bytesused(vb);
-
-			if (vb->vb2_queue->allow_zero_bytesused)
-				planes[0].bytesused = b->bytesused;
-			else
-				planes[0].bytesused = b->bytesused ?
-					b->bytesused : planes[0].length;
-		} else
-			planes[0].bytesused = 0;
-
-	}
-
-	/* Zero flags that the vb2 core handles */
-	vbuf->flags = b->flags & ~V4L2_BUFFER_MASK_FLAGS;
-	if (!vb->vb2_queue->copy_timestamp || !V4L2_TYPE_IS_OUTPUT(b->type)) {
-		/*
-		 * Non-COPY timestamps and non-OUTPUT queues will get
-		 * their timestamp and timestamp source flags from the
-		 * queue.
-		 */
-		vbuf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+		planes[plane].bytesused = vbuf->planes[plane].bytesused;
+		planes[plane].data_offset = vbuf->planes[plane].data_offset;
 	}
-
-	if (V4L2_TYPE_IS_OUTPUT(b->type)) {
-		/*
-		 * For output buffers mask out the timecode flag:
-		 * this will be handled later in vb2_qbuf().
-		 * The 'field' is valid metadata for this output buffer
-		 * and so that needs to be copied here.
-		 */
-		vbuf->flags &= ~V4L2_BUF_FLAG_TIMECODE;
-		vbuf->field = b->field;
-	} else {
-		/* Zero any output buffer flags as this is a capture buffer */
-		vbuf->flags &= ~V4L2_BUFFER_OUT_FLAGS;
-		/* Zero last flag, this is a signal from driver to userspace */
-		vbuf->flags &= ~V4L2_BUF_FLAG_LAST;
-	}
-
 	return 0;
 }
 
diff --git a/drivers/media/dvb-core/dvb_vb2.c b/drivers/media/dvb-core/dvb_vb2.c
index b811adf88afa..da6a8cec7d42 100644
--- a/drivers/media/dvb-core/dvb_vb2.c
+++ b/drivers/media/dvb-core/dvb_vb2.c
@@ -146,8 +146,7 @@ static void _fill_dmx_buffer(struct vb2_buffer *vb, void *pb)
 	dprintk(3, "[%s]\n", ctx->name);
 }
 
-static int _fill_vb2_buffer(struct vb2_buffer *vb,
-			    const void *pb, struct vb2_plane *planes)
+static int _fill_vb2_buffer(struct vb2_buffer *vb, struct vb2_plane *planes)
 {
 	struct dvb_vb2_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
 
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index f6818f732f34..224c4820a044 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -417,8 +417,7 @@ struct vb2_ops {
 struct vb2_buf_ops {
 	int (*verify_planes_array)(struct vb2_buffer *vb, const void *pb);
 	void (*fill_user_buffer)(struct vb2_buffer *vb, void *pb);
-	int (*fill_vb2_buffer)(struct vb2_buffer *vb, const void *pb,
-				struct vb2_plane *planes);
+	int (*fill_vb2_buffer)(struct vb2_buffer *vb, struct vb2_plane *planes);
 	void (*copy_timestamp)(struct vb2_buffer *vb, const void *pb);
 };
 
diff --git a/include/media/videobuf2-v4l2.h b/include/media/videobuf2-v4l2.h
index 3d5e2d739f05..097bf3e6951d 100644
--- a/include/media/videobuf2-v4l2.h
+++ b/include/media/videobuf2-v4l2.h
@@ -32,6 +32,7 @@
  *		&enum v4l2_field.
  * @timecode:	frame timecode.
  * @sequence:	sequence count of this frame.
+ * @planes:	plane information (userptr/fd, length, bytesused, data_offset).
  *
  * Should contain enough information to be able to cover all the fields
  * of &struct v4l2_buffer at ``videodev2.h``.
@@ -43,6 +44,7 @@ struct vb2_v4l2_buffer {
 	__u32			field;
 	struct v4l2_timecode	timecode;
 	__u32			sequence;
+	struct vb2_plane	planes[VB2_MAX_PLANES];
 };
 
 /*
-- 
2.18.0

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

* [PATCHv17 18/34] davinci_vpfe: remove bogus vb2->state check
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (16 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 17/34] vb2: store userspace data in vb2_v4l2_buffer Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-13 11:17   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 19/34] vb2: drop VB2_BUF_STATE_PREPARED, use bool prepared/synced instead Hans Verkuil
                   ` (15 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

There is no need to check the vb2 state in the buf_prepare
callback: it can never be wrong.

Since VB2_BUF_STATE_PREPARED will be removed in the next patch
we'll remove this unnecessary check (and use of that state) first.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/staging/media/davinci_vpfe/vpfe_video.c | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c
index 1269a983455e..4e3ec7fdc90d 100644
--- a/drivers/staging/media/davinci_vpfe/vpfe_video.c
+++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c
@@ -1135,10 +1135,6 @@ static int vpfe_buffer_prepare(struct vb2_buffer *vb)
 
 	v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_prepare\n");
 
-	if (vb->state != VB2_BUF_STATE_ACTIVE &&
-	    vb->state != VB2_BUF_STATE_PREPARED)
-		return 0;
-
 	/* Initialize buffer */
 	vb2_set_plane_payload(vb, 0, video->fmt.fmt.pix.sizeimage);
 	if (vb2_plane_vaddr(vb, 0) &&
-- 
2.18.0

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

* [PATCHv17 19/34] vb2: drop VB2_BUF_STATE_PREPARED, use bool prepared/synced instead
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (17 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 18/34] davinci_vpfe: remove bogus vb2->state check Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-13 11:30   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 20/34] videodev2.h: Add request_fd field to v4l2_buffer Hans Verkuil
                   ` (14 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

The PREPARED state becomes a problem with the request API: a buffer
could be PREPARED but dequeued, or PREPARED and in state IN_REQUEST.

PREPARED is really not a state as such, but more a property of the
buffer. So make new 'prepared' and 'synced' bools instead to remember
whether the buffer is prepared and/or synced or not.

V4L2_BUF_FLAG_PREPARED is only set if the buffer is both synced and
prepared and in the DEQUEUED state.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 .../media/common/videobuf2/videobuf2-core.c   | 38 +++++++++++++------
 .../media/common/videobuf2/videobuf2-v4l2.c   | 16 +++++---
 include/media/videobuf2-core.h                |  6 ++-
 3 files changed, 40 insertions(+), 20 deletions(-)

diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
index 7401a17c80ca..eead693ba619 100644
--- a/drivers/media/common/videobuf2/videobuf2-core.c
+++ b/drivers/media/common/videobuf2/videobuf2-core.c
@@ -682,7 +682,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
 		}
 
 		/*
-		 * Call queue_cancel to clean up any buffers in the PREPARED or
+		 * Call queue_cancel to clean up any buffers in the
 		 * QUEUED state which is possible if buffers were prepared or
 		 * queued without ever calling STREAMON.
 		 */
@@ -921,6 +921,7 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
 		/* sync buffers */
 		for (plane = 0; plane < vb->num_planes; ++plane)
 			call_void_memop(vb, finish, vb->planes[plane].mem_priv);
+		vb->synced = false;
 	}
 
 	spin_lock_irqsave(&q->done_lock, flags);
@@ -1239,6 +1240,7 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
 static int __buf_prepare(struct vb2_buffer *vb)
 {
 	struct vb2_queue *q = vb->vb2_queue;
+	enum vb2_buffer_state orig_state = vb->state;
 	unsigned int plane;
 	int ret;
 
@@ -1247,6 +1249,10 @@ static int __buf_prepare(struct vb2_buffer *vb)
 		return -EIO;
 	}
 
+	if (vb->prepared)
+		return 0;
+	WARN_ON(vb->synced);
+
 	vb->state = VB2_BUF_STATE_PREPARING;
 
 	switch (q->memory) {
@@ -1262,11 +1268,12 @@ static int __buf_prepare(struct vb2_buffer *vb)
 	default:
 		WARN(1, "Invalid queue type\n");
 		ret = -EINVAL;
+		break;
 	}
 
 	if (ret) {
 		dprintk(1, "buffer preparation failed: %d\n", ret);
-		vb->state = VB2_BUF_STATE_DEQUEUED;
+		vb->state = orig_state;
 		return ret;
 	}
 
@@ -1274,7 +1281,9 @@ static int __buf_prepare(struct vb2_buffer *vb)
 	for (plane = 0; plane < vb->num_planes; ++plane)
 		call_void_memop(vb, prepare, vb->planes[plane].mem_priv);
 
-	vb->state = VB2_BUF_STATE_PREPARED;
+	vb->synced = true;
+	vb->prepared = true;
+	vb->state = orig_state;
 
 	return 0;
 }
@@ -1290,6 +1299,10 @@ int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb)
 			vb->state);
 		return -EINVAL;
 	}
+	if (vb->prepared) {
+		dprintk(1, "buffer already prepared\n");
+		return -EINVAL;
+	}
 
 	ret = __buf_prepare(vb);
 	if (ret)
@@ -1381,11 +1394,11 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb)
 
 	switch (vb->state) {
 	case VB2_BUF_STATE_DEQUEUED:
-		ret = __buf_prepare(vb);
-		if (ret)
-			return ret;
-		break;
-	case VB2_BUF_STATE_PREPARED:
+		if (!vb->prepared) {
+			ret = __buf_prepare(vb);
+			if (ret)
+				return ret;
+		}
 		break;
 	case VB2_BUF_STATE_PREPARING:
 		dprintk(1, "buffer still being prepared\n");
@@ -1611,6 +1624,7 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb,
 	}
 
 	call_void_vb_qop(vb, buf_finish, vb);
+	vb->prepared = false;
 
 	if (pindex)
 		*pindex = vb->index;
@@ -1699,18 +1713,18 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
 	for (i = 0; i < q->num_buffers; ++i) {
 		struct vb2_buffer *vb = q->bufs[i];
 
-		if (vb->state == VB2_BUF_STATE_PREPARED ||
-		    vb->state == VB2_BUF_STATE_QUEUED) {
+		if (vb->synced) {
 			unsigned int plane;
 
 			for (plane = 0; plane < vb->num_planes; ++plane)
 				call_void_memop(vb, finish,
 						vb->planes[plane].mem_priv);
+			vb->synced = false;
 		}
 
-		if (vb->state != VB2_BUF_STATE_DEQUEUED) {
-			vb->state = VB2_BUF_STATE_PREPARED;
+		if (vb->prepared) {
 			call_void_vb_qop(vb, buf_finish, vb);
+			vb->prepared = false;
 		}
 		__vb2_dqbuf(vb);
 	}
diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c
index 360dc4e7d413..a677e2c26247 100644
--- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
+++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
@@ -352,9 +352,13 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b,
 	if (ret)
 		return ret;
 
-	/* Copy relevant information provided by the userspace */
-	memset(vbuf->planes, 0, sizeof(vbuf->planes[0]) * vb->num_planes);
-	return vb2_fill_vb2_v4l2_buffer(vb, b);
+	if (!vb->prepared) {
+		/* Copy relevant information provided by the userspace */
+		memset(vbuf->planes, 0,
+		       sizeof(vbuf->planes[0]) * vb->num_planes);
+		ret = vb2_fill_vb2_v4l2_buffer(vb, b);
+	}
+	return ret;
 }
 
 /*
@@ -443,9 +447,6 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
 	case VB2_BUF_STATE_DONE:
 		b->flags |= V4L2_BUF_FLAG_DONE;
 		break;
-	case VB2_BUF_STATE_PREPARED:
-		b->flags |= V4L2_BUF_FLAG_PREPARED;
-		break;
 	case VB2_BUF_STATE_PREPARING:
 	case VB2_BUF_STATE_DEQUEUED:
 	case VB2_BUF_STATE_REQUEUEING:
@@ -453,6 +454,9 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
 		break;
 	}
 
+	if (vb->state == VB2_BUF_STATE_DEQUEUED && vb->synced && vb->prepared)
+		b->flags |= V4L2_BUF_FLAG_PREPARED;
+
 	if (vb2_buffer_in_use(q, vb))
 		b->flags |= V4L2_BUF_FLAG_MAPPED;
 
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index 224c4820a044..5e760d5f280a 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -204,7 +204,6 @@ enum vb2_io_modes {
  * enum vb2_buffer_state - current video buffer state.
  * @VB2_BUF_STATE_DEQUEUED:	buffer under userspace control.
  * @VB2_BUF_STATE_PREPARING:	buffer is being prepared in videobuf.
- * @VB2_BUF_STATE_PREPARED:	buffer prepared in videobuf and by the driver.
  * @VB2_BUF_STATE_QUEUED:	buffer queued in videobuf, but not in driver.
  * @VB2_BUF_STATE_REQUEUEING:	re-queue a buffer to the driver.
  * @VB2_BUF_STATE_ACTIVE:	buffer queued in driver and possibly used
@@ -218,7 +217,6 @@ enum vb2_io_modes {
 enum vb2_buffer_state {
 	VB2_BUF_STATE_DEQUEUED,
 	VB2_BUF_STATE_PREPARING,
-	VB2_BUF_STATE_PREPARED,
 	VB2_BUF_STATE_QUEUED,
 	VB2_BUF_STATE_REQUEUEING,
 	VB2_BUF_STATE_ACTIVE,
@@ -250,6 +248,8 @@ struct vb2_buffer {
 	/* private: internal use only
 	 *
 	 * state:		current buffer state; do not change
+	 * synced:		this buffer has been synced
+	 * prepared:		this buffer has been prepared
 	 * queued_entry:	entry on the queued buffers list, which holds
 	 *			all buffers queued from userspace
 	 * done_entry:		entry on the list that stores all buffers ready
@@ -257,6 +257,8 @@ struct vb2_buffer {
 	 * vb2_plane:		per-plane information; do not change
 	 */
 	enum vb2_buffer_state	state;
+	bool			synced;
+	bool			prepared;
 
 	struct vb2_plane	planes[VB2_MAX_PLANES];
 	struct list_head	queued_entry;
-- 
2.18.0

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

* [PATCHv17 20/34] videodev2.h: Add request_fd field to v4l2_buffer
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (18 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 19/34] vb2: drop VB2_BUF_STATE_PREPARED, use bool prepared/synced instead Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-13 11:41   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 21/34] vb2: add init_buffer buffer op Hans Verkuil
                   ` (13 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

When queuing buffers allow for passing the request that should
be associated with this buffer.

If V4L2_BUF_FLAG_REQUEST_FD is set, then request_fd is used as
the file descriptor.

If a buffer is stored in a request, but not yet queued to the
driver, then V4L2_BUF_FLAG_IN_REQUEST is set.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/common/videobuf2/videobuf2-v4l2.c |  2 +-
 drivers/media/usb/cpia2/cpia2_v4l.c             |  2 +-
 drivers/media/v4l2-core/v4l2-compat-ioctl32.c   |  9 ++++++---
 drivers/media/v4l2-core/v4l2-ioctl.c            |  4 ++--
 include/uapi/linux/videodev2.h                  | 10 +++++++++-
 5 files changed, 19 insertions(+), 8 deletions(-)

diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c
index a677e2c26247..64905d87465c 100644
--- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
+++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
@@ -384,7 +384,7 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
 	b->timecode = vbuf->timecode;
 	b->sequence = vbuf->sequence;
 	b->reserved2 = 0;
-	b->reserved = 0;
+	b->request_fd = 0;
 
 	if (q->is_multiplanar) {
 		/*
diff --git a/drivers/media/usb/cpia2/cpia2_v4l.c b/drivers/media/usb/cpia2/cpia2_v4l.c
index 99f106b13280..13aee9f67d05 100644
--- a/drivers/media/usb/cpia2/cpia2_v4l.c
+++ b/drivers/media/usb/cpia2/cpia2_v4l.c
@@ -949,7 +949,7 @@ static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
 	buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer;
 	buf->length = cam->frame_size;
 	buf->reserved2 = 0;
-	buf->reserved = 0;
+	buf->request_fd = 0;
 	memset(&buf->timecode, 0, sizeof(buf->timecode));
 
 	DBG("DQBUF #%d status:%d seq:%d length:%d\n", buf->index,
diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index dcce86c1fe40..633465d21d04 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -482,7 +482,7 @@ struct v4l2_buffer32 {
 	} m;
 	__u32			length;
 	__u32			reserved2;
-	__u32			reserved;
+	__s32			request_fd;
 };
 
 static int get_v4l2_plane32(struct v4l2_plane __user *p64,
@@ -581,6 +581,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer __user *p64,
 {
 	u32 type;
 	u32 length;
+	s32 request_fd;
 	enum v4l2_memory memory;
 	struct v4l2_plane32 __user *uplane32;
 	struct v4l2_plane __user *uplane;
@@ -595,7 +596,9 @@ static int get_v4l2_buffer32(struct v4l2_buffer __user *p64,
 	    get_user(memory, &p32->memory) ||
 	    put_user(memory, &p64->memory) ||
 	    get_user(length, &p32->length) ||
-	    put_user(length, &p64->length))
+	    put_user(length, &p64->length) ||
+	    get_user(request_fd, &p32->request_fd) ||
+	    put_user(request_fd, &p64->request_fd))
 		return -EFAULT;
 
 	if (V4L2_TYPE_IS_OUTPUT(type))
@@ -699,7 +702,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer __user *p64,
 	    copy_in_user(&p32->timecode, &p64->timecode, sizeof(p64->timecode)) ||
 	    assign_in_user(&p32->sequence, &p64->sequence) ||
 	    assign_in_user(&p32->reserved2, &p64->reserved2) ||
-	    assign_in_user(&p32->reserved, &p64->reserved) ||
+	    assign_in_user(&p32->request_fd, &p64->request_fd) ||
 	    get_user(length, &p64->length) ||
 	    put_user(length, &p32->length))
 		return -EFAULT;
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 20b5145a5254..2a84ca9e328a 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -474,13 +474,13 @@ static void v4l_print_buffer(const void *arg, bool write_only)
 	const struct v4l2_plane *plane;
 	int i;
 
-	pr_cont("%02ld:%02d:%02d.%08ld index=%d, type=%s, flags=0x%08x, field=%s, sequence=%d, memory=%s",
+	pr_cont("%02ld:%02d:%02d.%08ld index=%d, type=%s, request_fd=%d, flags=0x%08x, field=%s, sequence=%d, memory=%s",
 			p->timestamp.tv_sec / 3600,
 			(int)(p->timestamp.tv_sec / 60) % 60,
 			(int)(p->timestamp.tv_sec % 60),
 			(long)p->timestamp.tv_usec,
 			p->index,
-			prt_names(p->type, v4l2_type_names),
+			prt_names(p->type, v4l2_type_names), p->request_fd,
 			p->flags, prt_names(p->field, v4l2_field_names),
 			p->sequence, prt_names(p->memory, v4l2_memory_names));
 
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 1df0fa983db6..91126b7312f8 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -917,6 +917,7 @@ struct v4l2_plane {
  * @length:	size in bytes of the buffer (NOT its payload) for single-plane
  *		buffers (when type != *_MPLANE); number of elements in the
  *		planes array for multi-plane buffers
+ * @request_fd: fd of the request that this buffer should use
  *
  * Contains data exchanged by application and driver using one of the Streaming
  * I/O methods.
@@ -941,7 +942,10 @@ struct v4l2_buffer {
 	} m;
 	__u32			length;
 	__u32			reserved2;
-	__u32			reserved;
+	union {
+		__s32		request_fd;
+		__u32		reserved;
+	};
 };
 
 /*  Flags for 'flags' field */
@@ -959,6 +963,8 @@ struct v4l2_buffer {
 #define V4L2_BUF_FLAG_BFRAME			0x00000020
 /* Buffer is ready, but the data contained within is corrupted. */
 #define V4L2_BUF_FLAG_ERROR			0x00000040
+/* Buffer is added to an unqueued request */
+#define V4L2_BUF_FLAG_IN_REQUEST		0x00000080
 /* timecode field is valid */
 #define V4L2_BUF_FLAG_TIMECODE			0x00000100
 /* Buffer is prepared for queuing */
@@ -977,6 +983,8 @@ struct v4l2_buffer {
 #define V4L2_BUF_FLAG_TSTAMP_SRC_SOE		0x00010000
 /* mem2mem encoder/decoder */
 #define V4L2_BUF_FLAG_LAST			0x00100000
+/* request_fd is valid */
+#define V4L2_BUF_FLAG_REQUEST_FD		0x00800000
 
 /**
  * struct v4l2_exportbuffer - export of video buffer as DMABUF file descriptor
-- 
2.18.0

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

* [PATCHv17 21/34] vb2: add init_buffer buffer op
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (19 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 20/34] videodev2.h: Add request_fd field to v4l2_buffer Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-13 11:56   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 22/34] videobuf2-core: embed media_request_object Hans Verkuil
                   ` (12 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

We need to initialize the request_fd field in struct vb2_v4l2_buffer
to -1 instead of the default of 0. So we need to add a new op that
is called when struct vb2_v4l2_buffer is allocated.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/common/videobuf2/videobuf2-core.c | 2 ++
 include/media/videobuf2-core.h                  | 4 ++++
 2 files changed, 6 insertions(+)

diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
index eead693ba619..230f83d6d094 100644
--- a/drivers/media/common/videobuf2/videobuf2-core.c
+++ b/drivers/media/common/videobuf2/videobuf2-core.c
@@ -356,6 +356,8 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory,
 			vb->planes[plane].length = plane_sizes[plane];
 			vb->planes[plane].min_length = plane_sizes[plane];
 		}
+		call_void_bufop(q, init_buffer, vb);
+
 		q->bufs[vb->index] = vb;
 
 		/* Allocate video buffer memory for the MMAP type */
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index 5e760d5f280a..cbda3968d018 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -408,6 +408,9 @@ struct vb2_ops {
  * @verify_planes_array: Verify that a given user space structure contains
  *			enough planes for the buffer. This is called
  *			for each dequeued buffer.
+ * @init_buffer:	given a &vb2_buffer initialize the extra data after
+ *			struct vb2_buffer.
+ *			For V4L2 this is a &struct vb2_v4l2_buffer.
  * @fill_user_buffer:	given a &vb2_buffer fill in the userspace structure.
  *			For V4L2 this is a &struct v4l2_buffer.
  * @fill_vb2_buffer:	given a userspace structure, fill in the &vb2_buffer.
@@ -418,6 +421,7 @@ struct vb2_ops {
  */
 struct vb2_buf_ops {
 	int (*verify_planes_array)(struct vb2_buffer *vb, const void *pb);
+	void (*init_buffer)(struct vb2_buffer *vb);
 	void (*fill_user_buffer)(struct vb2_buffer *vb, void *pb);
 	int (*fill_vb2_buffer)(struct vb2_buffer *vb, struct vb2_plane *planes);
 	void (*copy_timestamp)(struct vb2_buffer *vb, const void *pb);
-- 
2.18.0

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

* [PATCHv17 22/34] videobuf2-core: embed media_request_object
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (20 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 21/34] vb2: add init_buffer buffer op Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-13 11:58   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 23/34] videobuf2-core: integrate with media requests Hans Verkuil
                   ` (11 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

Make vb2_buffer a request object.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 include/media/videobuf2-core.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index cbda3968d018..df92dcdeabb3 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -17,6 +17,7 @@
 #include <linux/poll.h>
 #include <linux/dma-buf.h>
 #include <linux/bitops.h>
+#include <media/media-request.h>
 
 #define VB2_MAX_FRAME	(32)
 #define VB2_MAX_PLANES	(8)
@@ -236,6 +237,8 @@ struct vb2_queue;
  * @num_planes:		number of planes in the buffer
  *			on an internal driver queue.
  * @timestamp:		frame timestamp in ns.
+ * @req_obj:		used to bind this buffer to a request. This
+ *			request object has a refcount.
  */
 struct vb2_buffer {
 	struct vb2_queue	*vb2_queue;
@@ -244,6 +247,7 @@ struct vb2_buffer {
 	unsigned int		memory;
 	unsigned int		num_planes;
 	u64			timestamp;
+	struct media_request_object	req_obj;
 
 	/* private: internal use only
 	 *
-- 
2.18.0

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

* [PATCHv17 23/34] videobuf2-core: integrate with media requests
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (21 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 22/34] videobuf2-core: embed media_request_object Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-13 12:09   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 24/34] videobuf2-v4l2: " Hans Verkuil
                   ` (10 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

Buffers can now be prepared or queued for a request.

A buffer is unbound from the request at vb2_buffer_done time or
when the queue is cancelled.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 .../media/common/videobuf2/videobuf2-core.c   | 133 +++++++++++++++++-
 .../media/common/videobuf2/videobuf2-v4l2.c   |   4 +-
 drivers/media/dvb-core/dvb_vb2.c              |   2 +-
 include/media/videobuf2-core.h                |  18 ++-
 4 files changed, 147 insertions(+), 10 deletions(-)

diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
index 230f83d6d094..1efb9c8b359d 100644
--- a/drivers/media/common/videobuf2/videobuf2-core.c
+++ b/drivers/media/common/videobuf2/videobuf2-core.c
@@ -499,8 +499,9 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
 			pr_info("     buf_init: %u buf_cleanup: %u buf_prepare: %u buf_finish: %u\n",
 				vb->cnt_buf_init, vb->cnt_buf_cleanup,
 				vb->cnt_buf_prepare, vb->cnt_buf_finish);
-			pr_info("     buf_queue: %u buf_done: %u\n",
-				vb->cnt_buf_queue, vb->cnt_buf_done);
+			pr_info("     buf_queue: %u buf_done: %u buf_request_complete: %u\n",
+				vb->cnt_buf_queue, vb->cnt_buf_done,
+				vb->cnt_buf_request_complete);
 			pr_info("     alloc: %u put: %u prepare: %u finish: %u mmap: %u\n",
 				vb->cnt_mem_alloc, vb->cnt_mem_put,
 				vb->cnt_mem_prepare, vb->cnt_mem_finish,
@@ -936,6 +937,14 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
 		vb->state = state;
 	}
 	atomic_dec(&q->owned_by_drv_count);
+
+	if (vb->req_obj.req) {
+		/* This is not supported at the moment */
+		WARN_ON(state == VB2_BUF_STATE_REQUEUEING);
+		media_request_object_unbind(&vb->req_obj);
+		media_request_object_put(&vb->req_obj);
+	}
+
 	spin_unlock_irqrestore(&q->done_lock, flags);
 
 	trace_vb2_buf_done(q, vb);
@@ -1290,6 +1299,60 @@ static int __buf_prepare(struct vb2_buffer *vb)
 	return 0;
 }
 
+static int vb2_req_prepare(struct media_request_object *obj)
+{
+	struct vb2_buffer *vb = container_of(obj, struct vb2_buffer, req_obj);
+	int ret;
+
+	if (WARN_ON(vb->state != VB2_BUF_STATE_IN_REQUEST))
+		return -EINVAL;
+
+	mutex_lock(vb->vb2_queue->lock);
+	ret = __buf_prepare(vb);
+	mutex_unlock(vb->vb2_queue->lock);
+	return ret;
+}
+
+static void __vb2_dqbuf(struct vb2_buffer *vb);
+
+static void vb2_req_unprepare(struct media_request_object *obj)
+{
+	struct vb2_buffer *vb = container_of(obj, struct vb2_buffer, req_obj);
+
+	mutex_lock(vb->vb2_queue->lock);
+	__vb2_dqbuf(vb);
+	vb->state = VB2_BUF_STATE_IN_REQUEST;
+	mutex_unlock(vb->vb2_queue->lock);
+	WARN_ON(!vb->req_obj.req);
+}
+
+int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb,
+		  struct media_request *req);
+
+static void vb2_req_queue(struct media_request_object *obj)
+{
+	struct vb2_buffer *vb = container_of(obj, struct vb2_buffer, req_obj);
+
+	mutex_lock(vb->vb2_queue->lock);
+	vb2_core_qbuf(vb->vb2_queue, vb->index, NULL, NULL);
+	mutex_unlock(vb->vb2_queue->lock);
+}
+
+static void vb2_req_release(struct media_request_object *obj)
+{
+	struct vb2_buffer *vb = container_of(obj, struct vb2_buffer, req_obj);
+
+	if (vb->state == VB2_BUF_STATE_IN_REQUEST)
+		vb->state = VB2_BUF_STATE_DEQUEUED;
+}
+
+static const struct media_request_object_ops vb2_core_req_ops = {
+	.prepare = vb2_req_prepare,
+	.unprepare = vb2_req_unprepare,
+	.queue = vb2_req_queue,
+	.release = vb2_req_release,
+};
+
 int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb)
 {
 	struct vb2_buffer *vb;
@@ -1315,7 +1378,7 @@ int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb)
 
 	dprintk(2, "prepare of buffer %d succeeded\n", vb->index);
 
-	return ret;
+	return 0;
 }
 EXPORT_SYMBOL_GPL(vb2_core_prepare_buf);
 
@@ -1382,7 +1445,8 @@ static int vb2_start_streaming(struct vb2_queue *q)
 	return ret;
 }
 
-int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb)
+int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb,
+		  struct media_request *req)
 {
 	struct vb2_buffer *vb;
 	int ret;
@@ -1394,8 +1458,39 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb)
 
 	vb = q->bufs[index];
 
+	if (req) {
+		int ret;
+
+		if (vb->state != VB2_BUF_STATE_DEQUEUED) {
+			dprintk(1, "buffer %d not in dequeued state\n",
+				vb->index);
+			return -EINVAL;
+		}
+
+		media_request_object_init(&vb->req_obj);
+
+		/* Make sure the request is in a safe state for updating. */
+		ret = media_request_lock_for_update(req);
+		if (ret)
+			return ret;
+		ret = media_request_object_bind(req, &vb2_core_req_ops,
+						q, &vb->req_obj);
+		media_request_unlock_for_update(req);
+		if (ret)
+			return ret;
+
+		vb->state = VB2_BUF_STATE_IN_REQUEST;
+		/* Fill buffer information for the userspace */
+		if (pb)
+			call_void_bufop(q, fill_user_buffer, vb, pb);
+
+		dprintk(2, "qbuf of buffer %d succeeded\n", vb->index);
+		return 0;
+	}
+
 	switch (vb->state) {
 	case VB2_BUF_STATE_DEQUEUED:
+	case VB2_BUF_STATE_IN_REQUEST:
 		if (!vb->prepared) {
 			ret = __buf_prepare(vb);
 			if (ret)
@@ -1601,6 +1696,11 @@ static void __vb2_dqbuf(struct vb2_buffer *vb)
 			call_void_memop(vb, unmap_dmabuf, vb->planes[i].mem_priv);
 			vb->planes[i].dbuf_mapped = 0;
 		}
+	if (vb->req_obj.req) {
+		media_request_object_unbind(&vb->req_obj);
+		media_request_object_put(&vb->req_obj);
+	}
+	call_void_bufop(q, init_buffer, vb);
 }
 
 int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb,
@@ -1714,6 +1814,25 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
 	 */
 	for (i = 0; i < q->num_buffers; ++i) {
 		struct vb2_buffer *vb = q->bufs[i];
+		struct media_request *req = vb->req_obj.req;
+
+		/*
+		 * If a request is associated with this buffer, then
+		 * call buf_request_cancel() to give the driver to complete()
+		 * related request objects. Otherwise those objects would
+		 * never complete.
+		 */
+		if (req) {
+			enum media_request_state state;
+			unsigned long flags;
+
+			spin_lock_irqsave(&req->lock, flags);
+			state = req->state;
+			spin_unlock_irqrestore(&req->lock, flags);
+
+			if (state == MEDIA_REQUEST_STATE_QUEUED)
+				call_void_vb_qop(vb, buf_request_complete, vb);
+		}
 
 		if (vb->synced) {
 			unsigned int plane;
@@ -2283,7 +2402,7 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read)
 		 * Queue all buffers.
 		 */
 		for (i = 0; i < q->num_buffers; i++) {
-			ret = vb2_core_qbuf(q, i, NULL);
+			ret = vb2_core_qbuf(q, i, NULL, NULL);
 			if (ret)
 				goto err_reqbufs;
 			fileio->bufs[i].queued = 1;
@@ -2462,7 +2581,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_
 
 		if (copy_timestamp)
 			b->timestamp = ktime_get_ns();
-		ret = vb2_core_qbuf(q, index, NULL);
+		ret = vb2_core_qbuf(q, index, NULL, NULL);
 		dprintk(5, "vb2_dbuf result: %d\n", ret);
 		if (ret)
 			return ret;
@@ -2565,7 +2684,7 @@ static int vb2_thread(void *data)
 		if (copy_timestamp)
 			vb->timestamp = ktime_get_ns();
 		if (!threadio->stop)
-			ret = vb2_core_qbuf(q, vb->index, NULL);
+			ret = vb2_core_qbuf(q, vb->index, NULL, NULL);
 		call_void_qop(q, wait_prepare, q);
 		if (ret || threadio->stop)
 			break;
diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c
index 64905d87465c..ea9db4b3f59a 100644
--- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
+++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
@@ -441,6 +441,8 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
 	case VB2_BUF_STATE_ACTIVE:
 		b->flags |= V4L2_BUF_FLAG_QUEUED;
 		break;
+	case VB2_BUF_STATE_IN_REQUEST:
+		break;
 	case VB2_BUF_STATE_ERROR:
 		b->flags |= V4L2_BUF_FLAG_ERROR;
 		/* fall through */
@@ -619,7 +621,7 @@ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
 	}
 
 	ret = vb2_queue_or_prepare_buf(q, b, "qbuf");
-	return ret ? ret : vb2_core_qbuf(q, b->index, b);
+	return ret ? ret : vb2_core_qbuf(q, b->index, b, NULL);
 }
 EXPORT_SYMBOL_GPL(vb2_qbuf);
 
diff --git a/drivers/media/dvb-core/dvb_vb2.c b/drivers/media/dvb-core/dvb_vb2.c
index da6a8cec7d42..f1e7f0536028 100644
--- a/drivers/media/dvb-core/dvb_vb2.c
+++ b/drivers/media/dvb-core/dvb_vb2.c
@@ -384,7 +384,7 @@ int dvb_vb2_qbuf(struct dvb_vb2_ctx *ctx, struct dmx_buffer *b)
 {
 	int ret;
 
-	ret = vb2_core_qbuf(&ctx->vb_q, b->index, b);
+	ret = vb2_core_qbuf(&ctx->vb_q, b->index, b, NULL);
 	if (ret) {
 		dprintk(1, "[%s] index=%d errno=%d\n", ctx->name,
 			b->index, ret);
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index df92dcdeabb3..8a8d7732d182 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -204,6 +204,7 @@ enum vb2_io_modes {
 /**
  * enum vb2_buffer_state - current video buffer state.
  * @VB2_BUF_STATE_DEQUEUED:	buffer under userspace control.
+ * @VB2_BUF_STATE_IN_REQUEST:	buffer is queued in media request.
  * @VB2_BUF_STATE_PREPARING:	buffer is being prepared in videobuf.
  * @VB2_BUF_STATE_QUEUED:	buffer queued in videobuf, but not in driver.
  * @VB2_BUF_STATE_REQUEUEING:	re-queue a buffer to the driver.
@@ -217,6 +218,7 @@ enum vb2_io_modes {
  */
 enum vb2_buffer_state {
 	VB2_BUF_STATE_DEQUEUED,
+	VB2_BUF_STATE_IN_REQUEST,
 	VB2_BUF_STATE_PREPARING,
 	VB2_BUF_STATE_QUEUED,
 	VB2_BUF_STATE_REQUEUEING,
@@ -293,6 +295,7 @@ struct vb2_buffer {
 	u32		cnt_buf_finish;
 	u32		cnt_buf_cleanup;
 	u32		cnt_buf_queue;
+	u32		cnt_buf_request_complete;
 
 	/* This counts the number of calls to vb2_buffer_done() */
 	u32		cnt_buf_done;
@@ -386,6 +389,11 @@ struct vb2_buffer {
  *			ioctl; might be called before @start_streaming callback
  *			if user pre-queued buffers before calling
  *			VIDIOC_STREAMON().
+ * @buf_request_complete: a buffer that was never queued to the driver but is
+ *			associated with a queued request was canceled.
+ *			The driver will have to mark associated objects in the
+ *			request as completed; required if requests are
+ *			supported.
  */
 struct vb2_ops {
 	int (*queue_setup)(struct vb2_queue *q,
@@ -404,6 +412,8 @@ struct vb2_ops {
 	void (*stop_streaming)(struct vb2_queue *q);
 
 	void (*buf_queue)(struct vb2_buffer *vb);
+
+	void (*buf_request_complete)(struct vb2_buffer *vb);
 };
 
 /**
@@ -761,12 +771,17 @@ int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb);
  * @index:	id number of the buffer
  * @pb:		buffer structure passed from userspace to
  *		v4l2_ioctl_ops->vidioc_qbuf handler in driver
+ * @req:	pointer to &struct media_request, may be NULL.
  *
  * Videobuf2 core helper to implement VIDIOC_QBUF() operation. It is called
  * internally by VB2 by an API-specific handler, like ``videobuf2-v4l2.h``.
  *
  * This function:
  *
+ * #) If @req is non-NULL, then the buffer will be bound to this
+ *    media request and it returns. The buffer will be prepared and
+ *    queued to the driver (i.e. the next two steps) when the request
+ *    itself is queued.
  * #) if necessary, calls &vb2_ops->buf_prepare callback in the driver
  *    (if provided), in which driver-specific buffer initialization can
  *    be performed;
@@ -775,7 +790,8 @@ int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb);
  *
  * Return: returns zero on success; an error code otherwise.
  */
-int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb);
+int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb,
+		  struct media_request *req);
 
 /**
  * vb2_core_dqbuf() - Dequeue a buffer to the userspace
-- 
2.18.0

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

* [PATCHv17 24/34] videobuf2-v4l2: integrate with media requests
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (22 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 23/34] videobuf2-core: integrate with media requests Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-13 14:30   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 25/34] videobuf2-core: add request helper functions Hans Verkuil
                   ` (9 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

This implements the V4L2 part of the request support. The main
change is that vb2_qbuf and vb2_prepare_buf now have a new
media_device pointer. This required changes to several drivers
that did not use the vb2_ioctl_qbuf/prepare_buf helper functions.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 .../media/common/videobuf2/videobuf2-core.c   |  13 +-
 .../media/common/videobuf2/videobuf2-v4l2.c   | 112 ++++++++++++++++--
 drivers/media/platform/omap3isp/ispvideo.c    |   2 +-
 .../media/platform/s3c-camif/camif-capture.c  |   4 +-
 drivers/media/platform/s5p-mfc/s5p_mfc_dec.c  |   4 +-
 drivers/media/platform/s5p-mfc/s5p_mfc_enc.c  |   4 +-
 .../media/platform/soc_camera/soc_camera.c    |   4 +-
 drivers/media/usb/uvc/uvc_queue.c             |   5 +-
 drivers/media/usb/uvc/uvc_v4l2.c              |   3 +-
 drivers/media/usb/uvc/uvcvideo.h              |   1 +
 drivers/media/v4l2-core/v4l2-mem2mem.c        |   6 +-
 .../staging/media/davinci_vpfe/vpfe_video.c   |   3 +-
 drivers/staging/media/omap4iss/iss_video.c    |   3 +-
 drivers/usb/gadget/function/uvc_queue.c       |   2 +-
 include/media/videobuf2-v4l2.h                |  14 ++-
 15 files changed, 148 insertions(+), 32 deletions(-)

diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
index 1efb9c8b359d..3e6db7d30989 100644
--- a/drivers/media/common/videobuf2/videobuf2-core.c
+++ b/drivers/media/common/videobuf2/videobuf2-core.c
@@ -1338,6 +1338,14 @@ static void vb2_req_queue(struct media_request_object *obj)
 	mutex_unlock(vb->vb2_queue->lock);
 }
 
+static void vb2_req_unbind(struct media_request_object *obj)
+{
+	struct vb2_buffer *vb = container_of(obj, struct vb2_buffer, req_obj);
+
+	if (vb->state == VB2_BUF_STATE_IN_REQUEST)
+		call_void_bufop(vb->vb2_queue, init_buffer, vb);
+}
+
 static void vb2_req_release(struct media_request_object *obj)
 {
 	struct vb2_buffer *vb = container_of(obj, struct vb2_buffer, req_obj);
@@ -1350,6 +1358,7 @@ static const struct media_request_object_ops vb2_core_req_ops = {
 	.prepare = vb2_req_prepare,
 	.unprepare = vb2_req_unprepare,
 	.queue = vb2_req_queue,
+	.unbind = vb2_req_unbind,
 	.release = vb2_req_release,
 };
 
@@ -1481,8 +1490,10 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb,
 
 		vb->state = VB2_BUF_STATE_IN_REQUEST;
 		/* Fill buffer information for the userspace */
-		if (pb)
+		if (pb) {
+			call_void_bufop(q, copy_timestamp, vb, pb);
 			call_void_bufop(q, fill_user_buffer, vb, pb);
+		}
 
 		dprintk(2, "qbuf of buffer %d succeeded\n", vb->index);
 		return 0;
diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c
index ea9db4b3f59a..9c652afa62ab 100644
--- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
+++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
@@ -25,6 +25,7 @@
 #include <linux/kthread.h>
 
 #include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
 #include <media/v4l2-fh.h>
 #include <media/v4l2-event.h>
 #include <media/v4l2-common.h>
@@ -40,10 +41,12 @@ module_param(debug, int, 0644);
 			pr_info("vb2-v4l2: %s: " fmt, __func__, ## arg); \
 	} while (0)
 
-/* Flags that are set by the vb2 core */
+/* Flags that are set by us */
 #define V4L2_BUFFER_MASK_FLAGS	(V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \
 				 V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR | \
 				 V4L2_BUF_FLAG_PREPARED | \
+				 V4L2_BUF_FLAG_IN_REQUEST | \
+				 V4L2_BUF_FLAG_REQUEST_FD | \
 				 V4L2_BUF_FLAG_TIMESTAMP_MASK)
 /* Output buffer flags that should be passed on to the driver */
 #define V4L2_BUFFER_OUT_FLAGS	(V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME | \
@@ -118,6 +121,16 @@ static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 	return 0;
 }
 
+/*
+ * __init_v4l2_vb2_buffer() - initialize the v4l2_vb2_buffer struct
+ */
+static void __init_v4l2_vb2_buffer(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+	vbuf->request_fd = -1;
+}
+
 static void __copy_timestamp(struct vb2_buffer *vb, const void *pb)
 {
 	const struct v4l2_buffer *b = pb;
@@ -181,6 +194,7 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b
 		return -EINVAL;
 	}
 	vbuf->sequence = 0;
+	vbuf->request_fd = -1;
 
 	if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
 		switch (b->memory) {
@@ -318,9 +332,12 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b
 	return 0;
 }
 
-static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b,
-				    const char *opname)
+static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *mdev,
+				    struct v4l2_buffer *b,
+				    const char *opname,
+				    struct media_request **p_req)
 {
+	struct media_request *req;
 	struct vb2_v4l2_buffer *vbuf;
 	struct vb2_buffer *vb;
 	int ret;
@@ -357,8 +374,60 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b,
 		memset(vbuf->planes, 0,
 		       sizeof(vbuf->planes[0]) * vb->num_planes);
 		ret = vb2_fill_vb2_v4l2_buffer(vb, b);
+		if (ret)
+			return ret;
 	}
-	return ret;
+
+	if (!(b->flags & V4L2_BUF_FLAG_REQUEST_FD))
+		return 0;
+
+	/*
+	 * For proper locking when queueing a request you need to be able
+	 * to lock access to the vb2 queue, so check that there is a lock
+	 * that we can use. In addition p_req must be non-NULL.
+	 */
+	if (WARN_ON(!q->lock || !p_req))
+		return -EINVAL;
+
+	/*
+	 * Make sure this op is implemented by the driver. It's easy to forget
+	 * this callback, but is it important when canceling a buffer in a
+	 * queued request.
+	 */
+	if (WARN_ON(!q->ops->buf_request_complete))
+		return -EINVAL;
+
+	if (vb->state != VB2_BUF_STATE_DEQUEUED) {
+		dprintk(1, "%s: buffer is not in dequeued state\n", opname);
+		return -EINVAL;
+	}
+
+	if (b->request_fd < 0) {
+		dprintk(1, "%s: request_fd < 0\n", opname);
+		return -EINVAL;
+	}
+
+	req = media_request_get_by_fd(mdev, b->request_fd);
+	if (IS_ERR(req)) {
+		dprintk(1, "%s: invalid request_fd\n", opname);
+		return PTR_ERR(req);
+	}
+
+	/*
+	 * Early sanity check. This is checked again when the buffer
+	 * is bound to the request in vb2_core_qbuf().
+	 */
+	if (req->state != MEDIA_REQUEST_STATE_IDLE &&
+	    req->state != MEDIA_REQUEST_STATE_UPDATING) {
+		dprintk(1, "%s: request is not idle\n", opname);
+		media_request_put(req);
+		return -EBUSY;
+	}
+
+	*p_req = req;
+	vbuf->request_fd = b->request_fd;
+
+	return 0;
 }
 
 /*
@@ -442,6 +511,7 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
 		b->flags |= V4L2_BUF_FLAG_QUEUED;
 		break;
 	case VB2_BUF_STATE_IN_REQUEST:
+		b->flags |= V4L2_BUF_FLAG_IN_REQUEST;
 		break;
 	case VB2_BUF_STATE_ERROR:
 		b->flags |= V4L2_BUF_FLAG_ERROR;
@@ -456,11 +526,17 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
 		break;
 	}
 
-	if (vb->state == VB2_BUF_STATE_DEQUEUED && vb->synced && vb->prepared)
+	if ((vb->state == VB2_BUF_STATE_DEQUEUED ||
+	     vb->state == VB2_BUF_STATE_IN_REQUEST) &&
+	    vb->synced && vb->prepared)
 		b->flags |= V4L2_BUF_FLAG_PREPARED;
 
 	if (vb2_buffer_in_use(q, vb))
 		b->flags |= V4L2_BUF_FLAG_MAPPED;
+	if (vbuf->request_fd >= 0) {
+		b->flags |= V4L2_BUF_FLAG_REQUEST_FD;
+		b->request_fd = vbuf->request_fd;
+	}
 
 	if (!q->is_output &&
 		b->flags & V4L2_BUF_FLAG_DONE &&
@@ -494,6 +570,7 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb, struct vb2_plane *planes)
 
 static const struct vb2_buf_ops v4l2_buf_ops = {
 	.verify_planes_array	= __verify_planes_array_core,
+	.init_buffer		= __init_v4l2_vb2_buffer,
 	.fill_user_buffer	= __fill_v4l2_buffer,
 	.fill_vb2_buffer	= __fill_vb2_buffer,
 	.copy_timestamp		= __copy_timestamp,
@@ -542,7 +619,8 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
 }
 EXPORT_SYMBOL_GPL(vb2_reqbufs);
 
-int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b)
+int vb2_prepare_buf(struct vb2_queue *q, struct media_device *mdev,
+		    struct v4l2_buffer *b)
 {
 	int ret;
 
@@ -551,7 +629,10 @@ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b)
 		return -EBUSY;
 	}
 
-	ret = vb2_queue_or_prepare_buf(q, b, "prepare_buf");
+	if (b->flags & V4L2_BUF_FLAG_REQUEST_FD)
+		return -EINVAL;
+
+	ret = vb2_queue_or_prepare_buf(q, mdev, b, "prepare_buf", NULL);
 
 	return ret ? ret : vb2_core_prepare_buf(q, b->index, b);
 }
@@ -611,8 +692,10 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
 }
 EXPORT_SYMBOL_GPL(vb2_create_bufs);
 
-int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
+int vb2_qbuf(struct vb2_queue *q, struct media_device *mdev,
+	     struct v4l2_buffer *b)
 {
+	struct media_request *req = NULL;
 	int ret;
 
 	if (vb2_fileio_is_active(q)) {
@@ -620,8 +703,13 @@ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
 		return -EBUSY;
 	}
 
-	ret = vb2_queue_or_prepare_buf(q, b, "qbuf");
-	return ret ? ret : vb2_core_qbuf(q, b->index, b, NULL);
+	ret = vb2_queue_or_prepare_buf(q, mdev, b, "qbuf", &req);
+	if (ret)
+		return ret;
+	ret = vb2_core_qbuf(q, b->index, b, req);
+	if (req)
+		media_request_put(req);
+	return ret;
 }
 EXPORT_SYMBOL_GPL(vb2_qbuf);
 
@@ -811,7 +899,7 @@ int vb2_ioctl_prepare_buf(struct file *file, void *priv,
 
 	if (vb2_queue_is_busy(vdev, file))
 		return -EBUSY;
-	return vb2_prepare_buf(vdev->queue, p);
+	return vb2_prepare_buf(vdev->queue, vdev->v4l2_dev->mdev, p);
 }
 EXPORT_SYMBOL_GPL(vb2_ioctl_prepare_buf);
 
@@ -830,7 +918,7 @@ int vb2_ioctl_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
 
 	if (vb2_queue_is_busy(vdev, file))
 		return -EBUSY;
-	return vb2_qbuf(vdev->queue, p);
+	return vb2_qbuf(vdev->queue, vdev->v4l2_dev->mdev, p);
 }
 EXPORT_SYMBOL_GPL(vb2_ioctl_qbuf);
 
diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c
index 674e7fd3ad99..1de7b6b13f67 100644
--- a/drivers/media/platform/omap3isp/ispvideo.c
+++ b/drivers/media/platform/omap3isp/ispvideo.c
@@ -940,7 +940,7 @@ isp_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
 	int ret;
 
 	mutex_lock(&video->queue_lock);
-	ret = vb2_qbuf(&vfh->queue, b);
+	ret = vb2_qbuf(&vfh->queue, video->video.v4l2_dev->mdev, b);
 	mutex_unlock(&video->queue_lock);
 
 	return ret;
diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c
index b1d9f3857d3d..60b62ec6e9cd 100644
--- a/drivers/media/platform/s3c-camif/camif-capture.c
+++ b/drivers/media/platform/s3c-camif/camif-capture.c
@@ -943,7 +943,7 @@ static int s3c_camif_qbuf(struct file *file, void *priv,
 	if (vp->owner && vp->owner != priv)
 		return -EBUSY;
 
-	return vb2_qbuf(&vp->vb_queue, buf);
+	return vb2_qbuf(&vp->vb_queue, vp->vdev.v4l2_dev->mdev, buf);
 }
 
 static int s3c_camif_dqbuf(struct file *file, void *priv,
@@ -981,7 +981,7 @@ static int s3c_camif_prepare_buf(struct file *file, void *priv,
 				 struct v4l2_buffer *b)
 {
 	struct camif_vp *vp = video_drvdata(file);
-	return vb2_prepare_buf(&vp->vb_queue, b);
+	return vb2_prepare_buf(&vp->vb_queue, vp->vdev.v4l2_dev->mdev, b);
 }
 
 static int s3c_camif_g_selection(struct file *file, void *priv,
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
index 6a3cc4f86c5d..fc0b61f1b91d 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
@@ -632,9 +632,9 @@ static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
 		return -EIO;
 	}
 	if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
-		return vb2_qbuf(&ctx->vq_src, buf);
+		return vb2_qbuf(&ctx->vq_src, NULL, buf);
 	else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
-		return vb2_qbuf(&ctx->vq_dst, buf);
+		return vb2_qbuf(&ctx->vq_dst, NULL, buf);
 	return -EINVAL;
 }
 
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
index 3ad4f5073002..9208c83aa377 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
@@ -1621,9 +1621,9 @@ static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
 			mfc_err("Call on QBUF after EOS command\n");
 			return -EIO;
 		}
-		return vb2_qbuf(&ctx->vq_src, buf);
+		return vb2_qbuf(&ctx->vq_src, NULL, buf);
 	} else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
-		return vb2_qbuf(&ctx->vq_dst, buf);
+		return vb2_qbuf(&ctx->vq_dst, NULL, buf);
 	}
 	return -EINVAL;
 }
diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index 901c07f49351..4572fea38d0b 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -394,7 +394,7 @@ static int soc_camera_qbuf(struct file *file, void *priv,
 	if (icd->streamer != file)
 		return -EBUSY;
 
-	return vb2_qbuf(&icd->vb2_vidq, p);
+	return vb2_qbuf(&icd->vb2_vidq, NULL, p);
 }
 
 static int soc_camera_dqbuf(struct file *file, void *priv,
@@ -430,7 +430,7 @@ static int soc_camera_prepare_buf(struct file *file, void *priv,
 {
 	struct soc_camera_device *icd = file->private_data;
 
-	return vb2_prepare_buf(&icd->vb2_vidq, b);
+	return vb2_prepare_buf(&icd->vb2_vidq, NULL, b);
 }
 
 static int soc_camera_expbuf(struct file *file, void *priv,
diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
index fecccb5e7628..8964e16f2b22 100644
--- a/drivers/media/usb/uvc/uvc_queue.c
+++ b/drivers/media/usb/uvc/uvc_queue.c
@@ -300,12 +300,13 @@ int uvc_create_buffers(struct uvc_video_queue *queue,
 	return ret;
 }
 
-int uvc_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
+int uvc_queue_buffer(struct uvc_video_queue *queue,
+		     struct media_device *mdev, struct v4l2_buffer *buf)
 {
 	int ret;
 
 	mutex_lock(&queue->mutex);
-	ret = vb2_qbuf(&queue->queue, buf);
+	ret = vb2_qbuf(&queue->queue, mdev, buf);
 	mutex_unlock(&queue->mutex);
 
 	return ret;
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 18a7384b50ee..0a2b8ea8a4ff 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -751,7 +751,8 @@ static int uvc_ioctl_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
 	if (!uvc_has_privileges(handle))
 		return -EBUSY;
 
-	return uvc_queue_buffer(&stream->queue, buf);
+	return uvc_queue_buffer(&stream->queue,
+				stream->vdev.v4l2_dev->mdev, buf);
 }
 
 static int uvc_ioctl_expbuf(struct file *file, void *fh,
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index e5f5d84f1d1d..8b7bb89f12df 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -694,6 +694,7 @@ int uvc_query_buffer(struct uvc_video_queue *queue,
 int uvc_create_buffers(struct uvc_video_queue *queue,
 		       struct v4l2_create_buffers *v4l2_cb);
 int uvc_queue_buffer(struct uvc_video_queue *queue,
+		     struct media_device *mdev,
 		     struct v4l2_buffer *v4l2_buf);
 int uvc_export_buffer(struct uvc_video_queue *queue,
 		      struct v4l2_exportbuffer *exp);
diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c
index 0a93c5b173c2..b09494174eb4 100644
--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
+++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
@@ -455,11 +455,12 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_querybuf);
 int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
 		  struct v4l2_buffer *buf)
 {
+	struct video_device *vdev = video_devdata(file);
 	struct vb2_queue *vq;
 	int ret;
 
 	vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
-	ret = vb2_qbuf(vq, buf);
+	ret = vb2_qbuf(vq, vdev->v4l2_dev->mdev, buf);
 	if (!ret)
 		v4l2_m2m_try_schedule(m2m_ctx);
 
@@ -480,11 +481,12 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_dqbuf);
 int v4l2_m2m_prepare_buf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
 			 struct v4l2_buffer *buf)
 {
+	struct video_device *vdev = video_devdata(file);
 	struct vb2_queue *vq;
 	int ret;
 
 	vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
-	ret = vb2_prepare_buf(vq, buf);
+	ret = vb2_prepare_buf(vq, vdev->v4l2_dev->mdev, buf);
 	if (!ret)
 		v4l2_m2m_try_schedule(m2m_ctx);
 
diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c
index 4e3ec7fdc90d..6d693067a251 100644
--- a/drivers/staging/media/davinci_vpfe/vpfe_video.c
+++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c
@@ -1425,7 +1425,8 @@ static int vpfe_qbuf(struct file *file, void *priv,
 		return -EACCES;
 	}
 
-	return vb2_qbuf(&video->buffer_queue, p);
+	return vb2_qbuf(&video->buffer_queue,
+			video->video_dev.v4l2_dev->mdev, p);
 }
 
 /*
diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c
index a3a83424a926..a35d1004b522 100644
--- a/drivers/staging/media/omap4iss/iss_video.c
+++ b/drivers/staging/media/omap4iss/iss_video.c
@@ -805,9 +805,10 @@ iss_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
 static int
 iss_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
 {
+	struct iss_video *video = video_drvdata(file);
 	struct iss_video_fh *vfh = to_iss_video_fh(fh);
 
-	return vb2_qbuf(&vfh->queue, b);
+	return vb2_qbuf(&vfh->queue, video->video.v4l2_dev->mdev, b);
 }
 
 static int
diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c
index 9e33d5206d54..f2497cb96abb 100644
--- a/drivers/usb/gadget/function/uvc_queue.c
+++ b/drivers/usb/gadget/function/uvc_queue.c
@@ -166,7 +166,7 @@ int uvcg_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
 	unsigned long flags;
 	int ret;
 
-	ret = vb2_qbuf(&queue->queue, buf);
+	ret = vb2_qbuf(&queue->queue, NULL, buf);
 	if (ret < 0)
 		return ret;
 
diff --git a/include/media/videobuf2-v4l2.h b/include/media/videobuf2-v4l2.h
index 097bf3e6951d..91a2b3e1a642 100644
--- a/include/media/videobuf2-v4l2.h
+++ b/include/media/videobuf2-v4l2.h
@@ -32,6 +32,7 @@
  *		&enum v4l2_field.
  * @timecode:	frame timecode.
  * @sequence:	sequence count of this frame.
+ * @request_fd:	the request_fd associated with this buffer
  * @planes:	plane information (userptr/fd, length, bytesused, data_offset).
  *
  * Should contain enough information to be able to cover all the fields
@@ -44,6 +45,7 @@ struct vb2_v4l2_buffer {
 	__u32			field;
 	struct v4l2_timecode	timecode;
 	__u32			sequence;
+	__s32			request_fd;
 	struct vb2_plane	planes[VB2_MAX_PLANES];
 };
 
@@ -79,6 +81,7 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create);
  * vb2_prepare_buf() - Pass ownership of a buffer from userspace to the kernel
  *
  * @q:		pointer to &struct vb2_queue with videobuf2 queue.
+ * @mdev:	pointer to &struct media_device, may be NULL.
  * @b:		buffer structure passed from userspace to
  *		&v4l2_ioctl_ops->vidioc_prepare_buf handler in driver
  *
@@ -90,15 +93,19 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create);
  * #) verifies the passed buffer,
  * #) calls &vb2_ops->buf_prepare callback in the driver (if provided),
  *    in which driver-specific buffer initialization can be performed.
+ * #) if @b->request_fd is non-zero and @mdev->ops->req_queue is set,
+ *    then bind the prepared buffer to the request.
  *
  * The return values from this function are intended to be directly returned
  * from &v4l2_ioctl_ops->vidioc_prepare_buf handler in driver.
  */
-int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b);
+int vb2_prepare_buf(struct vb2_queue *q, struct media_device *mdev,
+		    struct v4l2_buffer *b);
 
 /**
  * vb2_qbuf() - Queue a buffer from userspace
  * @q:		pointer to &struct vb2_queue with videobuf2 queue.
+ * @mdev:	pointer to &struct media_device, may be NULL.
  * @b:		buffer structure passed from userspace to
  *		&v4l2_ioctl_ops->vidioc_qbuf handler in driver
  *
@@ -107,6 +114,8 @@ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b);
  * This function:
  *
  * #) verifies the passed buffer;
+ * #) if @b->request_fd is non-zero and @mdev->ops->req_queue is set,
+ *    then bind the buffer to the request.
  * #) if necessary, calls &vb2_ops->buf_prepare callback in the driver
  *    (if provided), in which driver-specific buffer initialization can
  *    be performed;
@@ -116,7 +125,8 @@ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b);
  * The return values from this function are intended to be directly returned
  * from &v4l2_ioctl_ops->vidioc_qbuf handler in driver.
  */
-int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b);
+int vb2_qbuf(struct vb2_queue *q, struct media_device *mdev,
+	     struct v4l2_buffer *b);
 
 /**
  * vb2_expbuf() - Export a buffer as a file descriptor
-- 
2.18.0

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

* [PATCHv17 25/34] videobuf2-core: add request helper functions
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (23 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 24/34] videobuf2-v4l2: " Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-13 14:50   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 26/34] videobuf2-v4l2: add vb2_request_queue/validate helpers Hans Verkuil
                   ` (8 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

Add a new helper function to tell if a request object is a buffer.

Add a new helper function that returns true if a media_request
contains at least one buffer.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 .../media/common/videobuf2/videobuf2-core.c   | 24 +++++++++++++++++++
 include/media/videobuf2-core.h                | 15 ++++++++++++
 2 files changed, 39 insertions(+)

diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
index 3e6db7d30989..f8af7add35ab 100644
--- a/drivers/media/common/videobuf2/videobuf2-core.c
+++ b/drivers/media/common/videobuf2/videobuf2-core.c
@@ -1362,6 +1362,30 @@ static const struct media_request_object_ops vb2_core_req_ops = {
 	.release = vb2_req_release,
 };
 
+bool vb2_request_object_is_buffer(struct media_request_object *obj)
+{
+	return obj->ops == &vb2_core_req_ops;
+}
+EXPORT_SYMBOL_GPL(vb2_request_object_is_buffer);
+
+bool vb2_request_has_buffers(struct media_request *req)
+{
+	struct media_request_object *obj;
+	unsigned long flags;
+	bool has_buffers = false;
+
+	spin_lock_irqsave(&req->lock, flags);
+	list_for_each_entry(obj, &req->objects, list) {
+		if (vb2_request_object_is_buffer(obj)) {
+			has_buffers = true;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&req->lock, flags);
+	return has_buffers;
+}
+EXPORT_SYMBOL_GPL(vb2_request_has_buffers);
+
 int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb)
 {
 	struct vb2_buffer *vb;
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index 8a8d7732d182..cad712403d14 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -1168,4 +1168,19 @@ bool vb2_buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb);
  */
 int vb2_verify_memory_type(struct vb2_queue *q,
 		enum vb2_memory memory, unsigned int type);
+
+/**
+ * vb2_request_object_is_buffer() - return true if the object is a buffer
+ *
+ * @obj:	the request object.
+ */
+bool vb2_request_object_is_buffer(struct media_request_object *obj);
+
+/**
+ * vb2_request_has_buffers() - return true if the request contains buffers
+ *
+ * @req:	the request.
+ */
+bool vb2_request_has_buffers(struct media_request *req);
+
 #endif /* _MEDIA_VIDEOBUF2_CORE_H */
-- 
2.18.0

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

* [PATCHv17 26/34] videobuf2-v4l2: add vb2_request_queue/validate helpers
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (24 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 25/34] videobuf2-core: add request helper functions Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-13 14:53   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 27/34] videobuf2-core: add uses_requests/qbuf flags Hans Verkuil
                   ` (7 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

The generic vb2_request_validate helper function checks if
there are buffers in the request and if so, prepares (validates)
all objects in the request.

The generic vb2_request_queue helper function queues all buffer
objects in the validated request.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 .../media/common/videobuf2/videobuf2-v4l2.c   | 44 +++++++++++++++++++
 include/media/videobuf2-v4l2.h                |  4 ++
 2 files changed, 48 insertions(+)

diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c
index 9c652afa62ab..88d8f60c742b 100644
--- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
+++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
@@ -1100,6 +1100,50 @@ void vb2_ops_wait_finish(struct vb2_queue *vq)
 }
 EXPORT_SYMBOL_GPL(vb2_ops_wait_finish);
 
+int vb2_request_validate(struct media_request *req)
+{
+	struct media_request_object *obj;
+	int ret = 0;
+
+	if (!vb2_request_has_buffers(req))
+		return -ENOENT;
+
+	list_for_each_entry(obj, &req->objects, list) {
+		if (!obj->ops->prepare)
+			continue;
+
+		ret = obj->ops->prepare(obj);
+		if (ret)
+			break;
+	}
+
+	if (ret) {
+		list_for_each_entry_continue_reverse(obj, &req->objects, list)
+			if (obj->ops->unprepare)
+				obj->ops->unprepare(obj);
+		return ret;
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_request_validate);
+
+void vb2_request_queue(struct media_request *req)
+{
+	struct media_request_object *obj, *obj_safe;
+
+	/* Queue all non-buffer objects */
+	list_for_each_entry_safe(obj, obj_safe, &req->objects, list)
+		if (obj->ops->queue && !vb2_request_object_is_buffer(obj))
+			obj->ops->queue(obj);
+
+	/* Queue all buffer objects */
+	list_for_each_entry_safe(obj, obj_safe, &req->objects, list) {
+		if (obj->ops->queue && vb2_request_object_is_buffer(obj))
+			obj->ops->queue(obj);
+	}
+}
+EXPORT_SYMBOL_GPL(vb2_request_queue);
+
 MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2");
 MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>, Marek Szyprowski");
 MODULE_LICENSE("GPL");
diff --git a/include/media/videobuf2-v4l2.h b/include/media/videobuf2-v4l2.h
index 91a2b3e1a642..727855463838 100644
--- a/include/media/videobuf2-v4l2.h
+++ b/include/media/videobuf2-v4l2.h
@@ -303,4 +303,8 @@ void vb2_ops_wait_prepare(struct vb2_queue *vq);
  */
 void vb2_ops_wait_finish(struct vb2_queue *vq);
 
+struct media_request;
+int vb2_request_validate(struct media_request *req);
+void vb2_request_queue(struct media_request *req);
+
 #endif /* _MEDIA_VIDEOBUF2_V4L2_H */
-- 
2.18.0

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

* [PATCHv17 27/34] videobuf2-core: add uses_requests/qbuf flags
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (25 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 26/34] videobuf2-v4l2: add vb2_request_queue/validate helpers Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-13 14:55   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 28/34] videobuf2-v4l2: refuse qbuf if queue uses requests or vv Hans Verkuil
                   ` (6 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

Set the first time a buffer from a request is queued to vb2
(uses_requests) or directly queued (uses_qbuf).
Cleared when the queue is canceled.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/common/videobuf2/videobuf2-core.c | 13 +++++++++++++
 include/media/videobuf2-core.h                  |  8 ++++++++
 2 files changed, 21 insertions(+)

diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
index f8af7add35ab..5d7946ec80d8 100644
--- a/drivers/media/common/videobuf2/videobuf2-core.c
+++ b/drivers/media/common/videobuf2/videobuf2-core.c
@@ -1491,9 +1491,17 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb,
 
 	vb = q->bufs[index];
 
+	if ((req && q->uses_qbuf) ||
+	    (!req && vb->state != VB2_BUF_STATE_IN_REQUEST &&
+	     q->uses_requests)) {
+		dprintk(1, "queue in wrong mode (qbuf vs requests)\n");
+		return -EPERM;
+	}
+
 	if (req) {
 		int ret;
 
+		q->uses_requests = 1;
 		if (vb->state != VB2_BUF_STATE_DEQUEUED) {
 			dprintk(1, "buffer %d not in dequeued state\n",
 				vb->index);
@@ -1523,6 +1531,9 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb,
 		return 0;
 	}
 
+	if (vb->state != VB2_BUF_STATE_IN_REQUEST)
+		q->uses_qbuf = 1;
+
 	switch (vb->state) {
 	case VB2_BUF_STATE_DEQUEUED:
 	case VB2_BUF_STATE_IN_REQUEST:
@@ -1825,6 +1836,8 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
 	q->start_streaming_called = 0;
 	q->queued_count = 0;
 	q->error = 0;
+	q->uses_requests = 0;
+	q->uses_qbuf = 0;
 
 	/*
 	 * Remove all buffers from videobuf's list...
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index cad712403d14..daffdf259fce 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -468,6 +468,12 @@ struct vb2_buf_ops {
  * @quirk_poll_must_check_waiting_for_buffers: Return %EPOLLERR at poll when QBUF
  *              has not been called. This is a vb1 idiom that has been adopted
  *              also by vb2.
+ * @uses_qbuf:	qbuf was used directly for this queue. Set to 1 the first
+ *		time this is called. Set to 0 when the queue is canceled.
+ *		If this is 1, then you cannot queue buffers from a request.
+ * @uses_requests: requests are used for this queue. Set to 1 the first time
+ *		a request is queued. Set to 0 when the queue is canceled.
+ *		If this is 1, then you cannot queue buffers directly.
  * @lock:	pointer to a mutex that protects the &struct vb2_queue. The
  *		driver can set this to a mutex to let the v4l2 core serialize
  *		the queuing ioctls. If the driver wants to handle locking
@@ -535,6 +541,8 @@ struct vb2_queue {
 	unsigned			fileio_write_immediately:1;
 	unsigned			allow_zero_bytesused:1;
 	unsigned		   quirk_poll_must_check_waiting_for_buffers:1;
+	unsigned			uses_qbuf:1;
+	unsigned			uses_requests:1;
 
 	struct mutex			*lock;
 	void				*owner;
-- 
2.18.0

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

* [PATCHv17 28/34] videobuf2-v4l2: refuse qbuf if queue uses requests or vv.
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (26 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 27/34] videobuf2-core: add uses_requests/qbuf flags Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-13 14:56   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 29/34] v4l2-mem2mem: add vb2_m2m_request_queue Hans Verkuil
                   ` (5 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

Check if the vb2 queue uses requests, and if so refuse to
add buffers that are not part of a request. Also check for
the reverse: a vb2 queue did not use requests, and an attempt
was made to queue a buffer to a request.

We might relax this in the future, but for now just return
-EPERM in that case.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/common/videobuf2/videobuf2-v4l2.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c
index 88d8f60c742b..1b2351986230 100644
--- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
+++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
@@ -378,8 +378,16 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md
 			return ret;
 	}
 
-	if (!(b->flags & V4L2_BUF_FLAG_REQUEST_FD))
+	if (!(b->flags & V4L2_BUF_FLAG_REQUEST_FD)) {
+		if (q->uses_requests) {
+			dprintk(1, "%s: queue uses requests\n", opname);
+			return -EPERM;
+		}
 		return 0;
+	} else if (q->uses_qbuf) {
+		dprintk(1, "%s: queue does not use requests\n", opname);
+		return -EPERM;
+	}
 
 	/*
 	 * For proper locking when queueing a request you need to be able
-- 
2.18.0

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

* [PATCHv17 29/34] v4l2-mem2mem: add vb2_m2m_request_queue
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (27 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 28/34] videobuf2-v4l2: refuse qbuf if queue uses requests or vv Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-13 15:02   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 30/34] vim2m: use workqueue Hans Verkuil
                   ` (4 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

For mem2mem devices we have to make sure that v4l2_m2m_try_schedule()
is called whenever a request is queued.

We do that by creating a vb2_m2m_request_queue() helper that should
be used instead of the 'normal' vb2_request_queue() helper. The m2m
helper function will call v4l2_m2m_try_schedule() as needed.

In addition we also avoid calling v4l2_m2m_try_schedule() when preparing
or queueing a buffer for a request since that is no longer needed.
Instead this helper function will do that when the request is actually
queued.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/v4l2-core/v4l2-mem2mem.c | 59 ++++++++++++++++++++++----
 include/media/v4l2-mem2mem.h           |  4 ++
 2 files changed, 55 insertions(+), 8 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c
index b09494174eb4..56a16cfef6f8 100644
--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
+++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
@@ -369,7 +369,7 @@ static void v4l2_m2m_cancel_job(struct v4l2_m2m_ctx *m2m_ctx)
 		spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
 		if (m2m_dev->m2m_ops->job_abort)
 			m2m_dev->m2m_ops->job_abort(m2m_ctx->priv);
-		dprintk("m2m_ctx %p running, will wait to complete", m2m_ctx);
+		dprintk("m2m_ctx %p running, will wait to complete\n", m2m_ctx);
 		wait_event(m2m_ctx->finished,
 				!(m2m_ctx->job_flags & TRANS_RUNNING));
 	} else if (m2m_ctx->job_flags & TRANS_QUEUED) {
@@ -460,8 +460,14 @@ int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
 	int ret;
 
 	vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
+	if (!V4L2_TYPE_IS_OUTPUT(vq->type) &&
+	    (buf->flags & V4L2_BUF_FLAG_REQUEST_FD)) {
+		dprintk("%s: requests cannot be used with capture buffers\n",
+			__func__);
+		return -EPERM;
+	}
 	ret = vb2_qbuf(vq, vdev->v4l2_dev->mdev, buf);
-	if (!ret)
+	if (!ret && !(buf->flags & V4L2_BUF_FLAG_IN_REQUEST))
 		v4l2_m2m_try_schedule(m2m_ctx);
 
 	return ret;
@@ -483,14 +489,9 @@ int v4l2_m2m_prepare_buf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
 {
 	struct video_device *vdev = video_devdata(file);
 	struct vb2_queue *vq;
-	int ret;
 
 	vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
-	ret = vb2_prepare_buf(vq, vdev->v4l2_dev->mdev, buf);
-	if (!ret)
-		v4l2_m2m_try_schedule(m2m_ctx);
-
-	return ret;
+	return vb2_prepare_buf(vq, vdev->v4l2_dev->mdev, buf);
 }
 EXPORT_SYMBOL_GPL(v4l2_m2m_prepare_buf);
 
@@ -934,6 +935,48 @@ void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx,
 }
 EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue);
 
+void vb2_m2m_request_queue(struct media_request *req)
+{
+	struct media_request_object *obj, *obj_safe;
+	struct v4l2_m2m_ctx *m2m_ctx = NULL;
+
+	/* Queue all non-buffer objects */
+	list_for_each_entry_safe(obj, obj_safe, &req->objects, list)
+		if (obj->ops->queue && !vb2_request_object_is_buffer(obj))
+			obj->ops->queue(obj);
+
+	/* Queue all buffer objects */
+	list_for_each_entry_safe(obj, obj_safe, &req->objects, list) {
+		struct v4l2_m2m_ctx *m2m_ctx_obj;
+		struct vb2_buffer *vb;
+
+		if (!obj->ops->queue || !vb2_request_object_is_buffer(obj))
+			continue;
+
+		/* Sanity checks */
+		vb = container_of(obj, struct vb2_buffer, req_obj);
+		WARN_ON(!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type));
+		m2m_ctx_obj = container_of(vb->vb2_queue,
+					   struct v4l2_m2m_ctx,
+					   out_q_ctx.q);
+		WARN_ON(m2m_ctx && m2m_ctx_obj != m2m_ctx);
+		m2m_ctx = m2m_ctx_obj;
+
+		/*
+		 * The buffer we queue here can in theory be immediately
+		 * unbound, hence the use of list_for_each_entry_safe()
+		 * above and why we call the queue op last.
+		 */
+		obj->ops->queue(obj);
+	}
+
+	WARN_ON(!m2m_ctx);
+
+	if (m2m_ctx)
+		v4l2_m2m_try_schedule(m2m_ctx);
+}
+EXPORT_SYMBOL_GPL(vb2_m2m_request_queue);
+
 /* Videobuf2 ioctl helpers */
 
 int v4l2_m2m_ioctl_reqbufs(struct file *file, void *priv,
diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h
index d655720e16a1..58c1ecf3d648 100644
--- a/include/media/v4l2-mem2mem.h
+++ b/include/media/v4l2-mem2mem.h
@@ -622,6 +622,10 @@ v4l2_m2m_dst_buf_remove_by_idx(struct v4l2_m2m_ctx *m2m_ctx, unsigned int idx)
 	return v4l2_m2m_buf_remove_by_idx(&m2m_ctx->cap_q_ctx, idx);
 }
 
+/* v4l2 request helper */
+
+void vb2_m2m_request_queue(struct media_request *req);
+
 /* v4l2 ioctl helpers */
 
 int v4l2_m2m_ioctl_reqbufs(struct file *file, void *priv,
-- 
2.18.0

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

* [PATCHv17 30/34] vim2m: use workqueue
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (28 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 29/34] v4l2-mem2mem: add vb2_m2m_request_queue Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-13 15:05   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 31/34] vim2m: support requests Hans Verkuil
                   ` (3 subsequent siblings)
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

v4l2_ctrl uses mutexes, so we can't setup a ctrl_handler in
interrupt context. Switch to a workqueue instead and drop the timer.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/platform/vim2m.c | 25 ++++++++++---------------
 1 file changed, 10 insertions(+), 15 deletions(-)

diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c
index 462099a141e4..6f87ef025ff1 100644
--- a/drivers/media/platform/vim2m.c
+++ b/drivers/media/platform/vim2m.c
@@ -3,7 +3,8 @@
  *
  * This is a virtual device driver for testing mem-to-mem videobuf framework.
  * It simulates a device that uses memory buffers for both source and
- * destination, processes the data and issues an "irq" (simulated by a timer).
+ * destination, processes the data and issues an "irq" (simulated by a delayed
+ * workqueue).
  * The device is capable of multi-instance, multi-buffer-per-transaction
  * operation (via the mem2mem framework).
  *
@@ -19,7 +20,6 @@
 #include <linux/module.h>
 #include <linux/delay.h>
 #include <linux/fs.h>
-#include <linux/timer.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
 
@@ -148,7 +148,7 @@ struct vim2m_dev {
 	struct mutex		dev_mutex;
 	spinlock_t		irqlock;
 
-	struct timer_list	timer;
+	struct delayed_work	work_run;
 
 	struct v4l2_m2m_dev	*m2m_dev;
 };
@@ -336,12 +336,6 @@ static int device_process(struct vim2m_ctx *ctx,
 	return 0;
 }
 
-static void schedule_irq(struct vim2m_dev *dev, int msec_timeout)
-{
-	dprintk(dev, "Scheduling a simulated irq\n");
-	mod_timer(&dev->timer, jiffies + msecs_to_jiffies(msec_timeout));
-}
-
 /*
  * mem2mem callbacks
  */
@@ -387,13 +381,14 @@ static void device_run(void *priv)
 
 	device_process(ctx, src_buf, dst_buf);
 
-	/* Run a timer, which simulates a hardware irq  */
-	schedule_irq(dev, ctx->transtime);
+	/* Run delayed work, which simulates a hardware irq  */
+	schedule_delayed_work(&dev->work_run, msecs_to_jiffies(ctx->transtime));
 }
 
-static void device_isr(struct timer_list *t)
+static void device_work(struct work_struct *w)
 {
-	struct vim2m_dev *vim2m_dev = from_timer(vim2m_dev, t, timer);
+	struct vim2m_dev *vim2m_dev =
+		container_of(w, struct vim2m_dev, work_run.work);
 	struct vim2m_ctx *curr_ctx;
 	struct vb2_v4l2_buffer *src_vb, *dst_vb;
 	unsigned long flags;
@@ -805,6 +800,7 @@ static void vim2m_stop_streaming(struct vb2_queue *q)
 	struct vb2_v4l2_buffer *vbuf;
 	unsigned long flags;
 
+	flush_scheduled_work();
 	for (;;) {
 		if (V4L2_TYPE_IS_OUTPUT(q->type))
 			vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
@@ -1015,6 +1011,7 @@ static int vim2m_probe(struct platform_device *pdev)
 	vfd = &dev->vfd;
 	vfd->lock = &dev->dev_mutex;
 	vfd->v4l2_dev = &dev->v4l2_dev;
+	INIT_DELAYED_WORK(&dev->work_run, device_work);
 
 	ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
 	if (ret) {
@@ -1026,7 +1023,6 @@ static int vim2m_probe(struct platform_device *pdev)
 	v4l2_info(&dev->v4l2_dev,
 			"Device registered as /dev/video%d\n", vfd->num);
 
-	timer_setup(&dev->timer, device_isr, 0);
 	platform_set_drvdata(pdev, dev);
 
 	dev->m2m_dev = v4l2_m2m_init(&m2m_ops);
@@ -1083,7 +1079,6 @@ static int vim2m_remove(struct platform_device *pdev)
 	media_device_cleanup(&dev->mdev);
 #endif
 	v4l2_m2m_release(dev->m2m_dev);
-	del_timer_sync(&dev->timer);
 	video_unregister_device(&dev->vfd);
 	v4l2_device_unregister(&dev->v4l2_dev);
 
-- 
2.18.0

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

* [PATCHv17 31/34] vim2m: support requests
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (29 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 30/34] vim2m: use workqueue Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-06 21:02   ` Ezequiel Garcia
  2018-08-13 15:08   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 32/34] vivid: add mc Hans Verkuil
                   ` (2 subsequent siblings)
  33 siblings, 2 replies; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

Add support for requests to vim2m.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/platform/vim2m.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c
index 6f87ef025ff1..3b8df2c9d24a 100644
--- a/drivers/media/platform/vim2m.c
+++ b/drivers/media/platform/vim2m.c
@@ -379,8 +379,18 @@ static void device_run(void *priv)
 	src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
 	dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
 
+	/* Apply request controls if needed */
+	if (src_buf->vb2_buf.req_obj.req)
+		v4l2_ctrl_request_setup(src_buf->vb2_buf.req_obj.req,
+					&ctx->hdl);
+
 	device_process(ctx, src_buf, dst_buf);
 
+	/* Complete request controls if needed */
+	if (src_buf->vb2_buf.req_obj.req)
+		v4l2_ctrl_request_complete(src_buf->vb2_buf.req_obj.req,
+					&ctx->hdl);
+
 	/* Run delayed work, which simulates a hardware irq  */
 	schedule_delayed_work(&dev->work_run, msecs_to_jiffies(ctx->transtime));
 }
@@ -808,12 +818,21 @@ static void vim2m_stop_streaming(struct vb2_queue *q)
 			vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
 		if (vbuf == NULL)
 			return;
+		v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req,
+					   &ctx->hdl);
 		spin_lock_irqsave(&ctx->dev->irqlock, flags);
 		v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
 		spin_unlock_irqrestore(&ctx->dev->irqlock, flags);
 	}
 }
 
+static void vim2m_buf_request_complete(struct vb2_buffer *vb)
+{
+	struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+	v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl);
+}
+
 static const struct vb2_ops vim2m_qops = {
 	.queue_setup	 = vim2m_queue_setup,
 	.buf_prepare	 = vim2m_buf_prepare,
@@ -822,6 +841,7 @@ static const struct vb2_ops vim2m_qops = {
 	.stop_streaming  = vim2m_stop_streaming,
 	.wait_prepare	 = vb2_ops_wait_prepare,
 	.wait_finish	 = vb2_ops_wait_finish,
+	.buf_request_complete = vim2m_buf_request_complete,
 };
 
 static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
@@ -988,6 +1008,11 @@ static const struct v4l2_m2m_ops m2m_ops = {
 	.job_abort	= job_abort,
 };
 
+static const struct media_device_ops m2m_media_ops = {
+	.req_validate = vb2_request_validate,
+	.req_queue = vb2_m2m_request_queue,
+};
+
 static int vim2m_probe(struct platform_device *pdev)
 {
 	struct vim2m_dev *dev;
@@ -1036,6 +1061,7 @@ static int vim2m_probe(struct platform_device *pdev)
 	dev->mdev.dev = &pdev->dev;
 	strlcpy(dev->mdev.model, "vim2m", sizeof(dev->mdev.model));
 	media_device_init(&dev->mdev);
+	dev->mdev.ops = &m2m_media_ops;
 	dev->v4l2_dev.mdev = &dev->mdev;
 
 	ret = v4l2_m2m_register_media_controller(dev->m2m_dev,
-- 
2.18.0

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

* [PATCHv17 32/34] vivid: add mc
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (30 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 31/34] vim2m: support requests Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-13 15:09   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 33/34] vivid: add request support Hans Verkuil
  2018-08-04 12:45 ` [PATCHv17 34/34] RFC: media-requests: add debugfs node Hans Verkuil
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

Add support for the media_device to vivid. This is a prerequisite
for request support.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/platform/vivid/vivid-core.c | 61 +++++++++++++++++++++++
 drivers/media/platform/vivid/vivid-core.h |  8 +++
 2 files changed, 69 insertions(+)

diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c
index 31db363602e5..1c448529be04 100644
--- a/drivers/media/platform/vivid/vivid-core.c
+++ b/drivers/media/platform/vivid/vivid-core.c
@@ -657,6 +657,15 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
 
 	dev->inst = inst;
 
+#ifdef CONFIG_MEDIA_CONTROLLER
+	dev->v4l2_dev.mdev = &dev->mdev;
+
+	/* Initialize media device */
+	strlcpy(dev->mdev.model, VIVID_MODULE_NAME, sizeof(dev->mdev.model));
+	dev->mdev.dev = &pdev->dev;
+	media_device_init(&dev->mdev);
+#endif
+
 	/* register v4l2_device */
 	snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
 			"%s-%03d", VIVID_MODULE_NAME, inst);
@@ -1174,6 +1183,13 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
 		vfd->lock = &dev->mutex;
 		video_set_drvdata(vfd, dev);
 
+#ifdef CONFIG_MEDIA_CONTROLLER
+		dev->vid_cap_pad.flags = MEDIA_PAD_FL_SINK;
+		ret = media_entity_pads_init(&vfd->entity, 1, &dev->vid_cap_pad);
+		if (ret)
+			goto unreg_dev;
+#endif
+
 #ifdef CONFIG_VIDEO_VIVID_CEC
 		if (in_type_counter[HDMI]) {
 			struct cec_adapter *adap;
@@ -1226,6 +1242,13 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
 		vfd->lock = &dev->mutex;
 		video_set_drvdata(vfd, dev);
 
+#ifdef CONFIG_MEDIA_CONTROLLER
+		dev->vid_out_pad.flags = MEDIA_PAD_FL_SOURCE;
+		ret = media_entity_pads_init(&vfd->entity, 1, &dev->vid_out_pad);
+		if (ret)
+			goto unreg_dev;
+#endif
+
 #ifdef CONFIG_VIDEO_VIVID_CEC
 		for (i = 0; i < dev->num_outputs; i++) {
 			struct cec_adapter *adap;
@@ -1275,6 +1298,13 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
 		vfd->tvnorms = tvnorms_cap;
 		video_set_drvdata(vfd, dev);
 
+#ifdef CONFIG_MEDIA_CONTROLLER
+		dev->vbi_cap_pad.flags = MEDIA_PAD_FL_SINK;
+		ret = media_entity_pads_init(&vfd->entity, 1, &dev->vbi_cap_pad);
+		if (ret)
+			goto unreg_dev;
+#endif
+
 		ret = video_register_device(vfd, VFL_TYPE_VBI, vbi_cap_nr[inst]);
 		if (ret < 0)
 			goto unreg_dev;
@@ -1300,6 +1330,13 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
 		vfd->tvnorms = tvnorms_out;
 		video_set_drvdata(vfd, dev);
 
+#ifdef CONFIG_MEDIA_CONTROLLER
+		dev->vbi_out_pad.flags = MEDIA_PAD_FL_SOURCE;
+		ret = media_entity_pads_init(&vfd->entity, 1, &dev->vbi_out_pad);
+		if (ret)
+			goto unreg_dev;
+#endif
+
 		ret = video_register_device(vfd, VFL_TYPE_VBI, vbi_out_nr[inst]);
 		if (ret < 0)
 			goto unreg_dev;
@@ -1323,6 +1360,13 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
 		vfd->lock = &dev->mutex;
 		video_set_drvdata(vfd, dev);
 
+#ifdef CONFIG_MEDIA_CONTROLLER
+		dev->sdr_cap_pad.flags = MEDIA_PAD_FL_SINK;
+		ret = media_entity_pads_init(&vfd->entity, 1, &dev->sdr_cap_pad);
+		if (ret)
+			goto unreg_dev;
+#endif
+
 		ret = video_register_device(vfd, VFL_TYPE_SDR, sdr_cap_nr[inst]);
 		if (ret < 0)
 			goto unreg_dev;
@@ -1369,12 +1413,25 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
 					  video_device_node_name(vfd));
 	}
 
+#ifdef CONFIG_MEDIA_CONTROLLER
+	/* Register the media device */
+	ret = media_device_register(&dev->mdev);
+	if (ret) {
+		dev_err(dev->mdev.dev,
+			"media device register failed (err=%d)\n", ret);
+		goto unreg_dev;
+	}
+#endif
+
 	/* Now that everything is fine, let's add it to device list */
 	vivid_devs[inst] = dev;
 
 	return 0;
 
 unreg_dev:
+#ifdef CONFIG_MEDIA_CONTROLLER
+	media_device_unregister(&dev->mdev);
+#endif
 	video_unregister_device(&dev->radio_tx_dev);
 	video_unregister_device(&dev->radio_rx_dev);
 	video_unregister_device(&dev->sdr_cap_dev);
@@ -1445,6 +1502,10 @@ static int vivid_remove(struct platform_device *pdev)
 		if (!dev)
 			continue;
 
+#ifdef CONFIG_MEDIA_CONTROLLER
+		media_device_unregister(&dev->mdev);
+#endif
+
 		if (dev->has_vid_cap) {
 			v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
 				video_device_node_name(&dev->vid_cap_dev));
diff --git a/drivers/media/platform/vivid/vivid-core.h b/drivers/media/platform/vivid/vivid-core.h
index 477c80a4d44c..6ccd1f5c1d91 100644
--- a/drivers/media/platform/vivid/vivid-core.h
+++ b/drivers/media/platform/vivid/vivid-core.h
@@ -136,6 +136,14 @@ struct vivid_cec_work {
 struct vivid_dev {
 	unsigned			inst;
 	struct v4l2_device		v4l2_dev;
+#ifdef CONFIG_MEDIA_CONTROLLER
+	struct media_device		mdev;
+	struct media_pad		vid_cap_pad;
+	struct media_pad		vid_out_pad;
+	struct media_pad		vbi_cap_pad;
+	struct media_pad		vbi_out_pad;
+	struct media_pad		sdr_cap_pad;
+#endif
 	struct v4l2_ctrl_handler	ctrl_hdl_user_gen;
 	struct v4l2_ctrl_handler	ctrl_hdl_user_vid;
 	struct v4l2_ctrl_handler	ctrl_hdl_user_aud;
-- 
2.18.0

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

* [PATCHv17 33/34] vivid: add request support
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (31 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 32/34] vivid: add mc Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-13 15:11   ` Mauro Carvalho Chehab
  2018-08-04 12:45 ` [PATCHv17 34/34] RFC: media-requests: add debugfs node Hans Verkuil
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

Add support for requests to vivid.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/platform/vivid/vivid-core.c        |  8 ++++++++
 drivers/media/platform/vivid/vivid-kthread-cap.c | 12 ++++++++++++
 drivers/media/platform/vivid/vivid-kthread-out.c | 12 ++++++++++++
 drivers/media/platform/vivid/vivid-sdr-cap.c     | 16 ++++++++++++++++
 drivers/media/platform/vivid/vivid-vbi-cap.c     | 10 ++++++++++
 drivers/media/platform/vivid/vivid-vbi-out.c     | 10 ++++++++++
 drivers/media/platform/vivid/vivid-vid-cap.c     | 10 ++++++++++
 drivers/media/platform/vivid/vivid-vid-out.c     | 10 ++++++++++
 8 files changed, 88 insertions(+)

diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c
index 1c448529be04..3f6f5cbe1b60 100644
--- a/drivers/media/platform/vivid/vivid-core.c
+++ b/drivers/media/platform/vivid/vivid-core.c
@@ -627,6 +627,13 @@ static void vivid_dev_release(struct v4l2_device *v4l2_dev)
 	kfree(dev);
 }
 
+#ifdef CONFIG_MEDIA_CONTROLLER
+static const struct media_device_ops vivid_media_ops = {
+	.req_validate = vb2_request_validate,
+	.req_queue = vb2_request_queue,
+};
+#endif
+
 static int vivid_create_instance(struct platform_device *pdev, int inst)
 {
 	static const struct v4l2_dv_timings def_dv_timings =
@@ -664,6 +671,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
 	strlcpy(dev->mdev.model, VIVID_MODULE_NAME, sizeof(dev->mdev.model));
 	dev->mdev.dev = &pdev->dev;
 	media_device_init(&dev->mdev);
+	dev->mdev.ops = &vivid_media_ops;
 #endif
 
 	/* register v4l2_device */
diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.c b/drivers/media/platform/vivid/vivid-kthread-cap.c
index f06003bb8e42..eebfff2126be 100644
--- a/drivers/media/platform/vivid/vivid-kthread-cap.c
+++ b/drivers/media/platform/vivid/vivid-kthread-cap.c
@@ -703,6 +703,8 @@ static void vivid_thread_vid_cap_tick(struct vivid_dev *dev, int dropped_bufs)
 		goto update_mv;
 
 	if (vid_cap_buf) {
+		v4l2_ctrl_request_setup(vid_cap_buf->vb.vb2_buf.req_obj.req,
+					&dev->ctrl_hdl_vid_cap);
 		/* Fill buffer */
 		vivid_fillbuff(dev, vid_cap_buf);
 		dprintk(dev, 1, "filled buffer %d\n",
@@ -713,6 +715,8 @@ static void vivid_thread_vid_cap_tick(struct vivid_dev *dev, int dropped_bufs)
 			dev->fb_cap.fmt.pixelformat == dev->fmt_cap->fourcc)
 			vivid_overlay(dev, vid_cap_buf);
 
+		v4l2_ctrl_request_complete(vid_cap_buf->vb.vb2_buf.req_obj.req,
+					   &dev->ctrl_hdl_vid_cap);
 		vb2_buffer_done(&vid_cap_buf->vb.vb2_buf, dev->dqbuf_error ?
 				VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
 		dprintk(dev, 2, "vid_cap buffer %d done\n",
@@ -720,10 +724,14 @@ static void vivid_thread_vid_cap_tick(struct vivid_dev *dev, int dropped_bufs)
 	}
 
 	if (vbi_cap_buf) {
+		v4l2_ctrl_request_setup(vbi_cap_buf->vb.vb2_buf.req_obj.req,
+					&dev->ctrl_hdl_vbi_cap);
 		if (dev->stream_sliced_vbi_cap)
 			vivid_sliced_vbi_cap_process(dev, vbi_cap_buf);
 		else
 			vivid_raw_vbi_cap_process(dev, vbi_cap_buf);
+		v4l2_ctrl_request_complete(vbi_cap_buf->vb.vb2_buf.req_obj.req,
+					   &dev->ctrl_hdl_vbi_cap);
 		vb2_buffer_done(&vbi_cap_buf->vb.vb2_buf, dev->dqbuf_error ?
 				VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
 		dprintk(dev, 2, "vbi_cap %d done\n",
@@ -891,6 +899,8 @@ void vivid_stop_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming)
 			buf = list_entry(dev->vid_cap_active.next,
 					 struct vivid_buffer, list);
 			list_del(&buf->list);
+			v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
+						   &dev->ctrl_hdl_vid_cap);
 			vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
 			dprintk(dev, 2, "vid_cap buffer %d done\n",
 				buf->vb.vb2_buf.index);
@@ -904,6 +914,8 @@ void vivid_stop_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming)
 			buf = list_entry(dev->vbi_cap_active.next,
 					 struct vivid_buffer, list);
 			list_del(&buf->list);
+			v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
+						   &dev->ctrl_hdl_vbi_cap);
 			vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
 			dprintk(dev, 2, "vbi_cap buffer %d done\n",
 				buf->vb.vb2_buf.index);
diff --git a/drivers/media/platform/vivid/vivid-kthread-out.c b/drivers/media/platform/vivid/vivid-kthread-out.c
index 9981e7548019..5a14810eeb69 100644
--- a/drivers/media/platform/vivid/vivid-kthread-out.c
+++ b/drivers/media/platform/vivid/vivid-kthread-out.c
@@ -75,6 +75,10 @@ static void vivid_thread_vid_out_tick(struct vivid_dev *dev)
 		return;
 
 	if (vid_out_buf) {
+		v4l2_ctrl_request_setup(vid_out_buf->vb.vb2_buf.req_obj.req,
+					&dev->ctrl_hdl_vid_out);
+		v4l2_ctrl_request_complete(vid_out_buf->vb.vb2_buf.req_obj.req,
+					   &dev->ctrl_hdl_vid_out);
 		vid_out_buf->vb.sequence = dev->vid_out_seq_count;
 		if (dev->field_out == V4L2_FIELD_ALTERNATE) {
 			/*
@@ -92,6 +96,10 @@ static void vivid_thread_vid_out_tick(struct vivid_dev *dev)
 	}
 
 	if (vbi_out_buf) {
+		v4l2_ctrl_request_setup(vbi_out_buf->vb.vb2_buf.req_obj.req,
+					&dev->ctrl_hdl_vbi_out);
+		v4l2_ctrl_request_complete(vbi_out_buf->vb.vb2_buf.req_obj.req,
+					   &dev->ctrl_hdl_vbi_out);
 		if (dev->stream_sliced_vbi_out)
 			vivid_sliced_vbi_out_process(dev, vbi_out_buf);
 
@@ -262,6 +270,8 @@ void vivid_stop_generating_vid_out(struct vivid_dev *dev, bool *pstreaming)
 			buf = list_entry(dev->vid_out_active.next,
 					 struct vivid_buffer, list);
 			list_del(&buf->list);
+			v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
+						   &dev->ctrl_hdl_vid_out);
 			vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
 			dprintk(dev, 2, "vid_out buffer %d done\n",
 				buf->vb.vb2_buf.index);
@@ -275,6 +285,8 @@ void vivid_stop_generating_vid_out(struct vivid_dev *dev, bool *pstreaming)
 			buf = list_entry(dev->vbi_out_active.next,
 					 struct vivid_buffer, list);
 			list_del(&buf->list);
+			v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
+						   &dev->ctrl_hdl_vbi_out);
 			vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
 			dprintk(dev, 2, "vbi_out buffer %d done\n",
 				buf->vb.vb2_buf.index);
diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.c b/drivers/media/platform/vivid/vivid-sdr-cap.c
index cfb7cb4d37a8..76cf8810a974 100644
--- a/drivers/media/platform/vivid/vivid-sdr-cap.c
+++ b/drivers/media/platform/vivid/vivid-sdr-cap.c
@@ -102,6 +102,10 @@ static void vivid_thread_sdr_cap_tick(struct vivid_dev *dev)
 
 	if (sdr_cap_buf) {
 		sdr_cap_buf->vb.sequence = dev->sdr_cap_seq_count;
+		v4l2_ctrl_request_setup(sdr_cap_buf->vb.vb2_buf.req_obj.req,
+					&dev->ctrl_hdl_sdr_cap);
+		v4l2_ctrl_request_complete(sdr_cap_buf->vb.vb2_buf.req_obj.req,
+					   &dev->ctrl_hdl_sdr_cap);
 		vivid_sdr_cap_process(dev, sdr_cap_buf);
 		sdr_cap_buf->vb.vb2_buf.timestamp =
 			ktime_get_ns() + dev->time_wrap_offset;
@@ -272,6 +276,8 @@ static int sdr_cap_start_streaming(struct vb2_queue *vq, unsigned count)
 
 		list_for_each_entry_safe(buf, tmp, &dev->sdr_cap_active, list) {
 			list_del(&buf->list);
+			v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
+						   &dev->ctrl_hdl_sdr_cap);
 			vb2_buffer_done(&buf->vb.vb2_buf,
 					VB2_BUF_STATE_QUEUED);
 		}
@@ -293,6 +299,8 @@ static void sdr_cap_stop_streaming(struct vb2_queue *vq)
 		buf = list_entry(dev->sdr_cap_active.next,
 				struct vivid_buffer, list);
 		list_del(&buf->list);
+		v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
+					   &dev->ctrl_hdl_sdr_cap);
 		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
 	}
 
@@ -303,12 +311,20 @@ static void sdr_cap_stop_streaming(struct vb2_queue *vq)
 	mutex_lock(&dev->mutex);
 }
 
+static void sdr_cap_buf_request_complete(struct vb2_buffer *vb)
+{
+	struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+
+	v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_sdr_cap);
+}
+
 const struct vb2_ops vivid_sdr_cap_qops = {
 	.queue_setup		= sdr_cap_queue_setup,
 	.buf_prepare		= sdr_cap_buf_prepare,
 	.buf_queue		= sdr_cap_buf_queue,
 	.start_streaming	= sdr_cap_start_streaming,
 	.stop_streaming		= sdr_cap_stop_streaming,
+	.buf_request_complete	= sdr_cap_buf_request_complete,
 	.wait_prepare		= vb2_ops_wait_prepare,
 	.wait_finish		= vb2_ops_wait_finish,
 };
diff --git a/drivers/media/platform/vivid/vivid-vbi-cap.c b/drivers/media/platform/vivid/vivid-vbi-cap.c
index 92a852955173..903cebeb5ce5 100644
--- a/drivers/media/platform/vivid/vivid-vbi-cap.c
+++ b/drivers/media/platform/vivid/vivid-vbi-cap.c
@@ -204,6 +204,8 @@ static int vbi_cap_start_streaming(struct vb2_queue *vq, unsigned count)
 
 		list_for_each_entry_safe(buf, tmp, &dev->vbi_cap_active, list) {
 			list_del(&buf->list);
+			v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
+						   &dev->ctrl_hdl_vbi_cap);
 			vb2_buffer_done(&buf->vb.vb2_buf,
 					VB2_BUF_STATE_QUEUED);
 		}
@@ -220,12 +222,20 @@ static void vbi_cap_stop_streaming(struct vb2_queue *vq)
 	vivid_stop_generating_vid_cap(dev, &dev->vbi_cap_streaming);
 }
 
+static void vbi_cap_buf_request_complete(struct vb2_buffer *vb)
+{
+	struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+
+	v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_vbi_cap);
+}
+
 const struct vb2_ops vivid_vbi_cap_qops = {
 	.queue_setup		= vbi_cap_queue_setup,
 	.buf_prepare		= vbi_cap_buf_prepare,
 	.buf_queue		= vbi_cap_buf_queue,
 	.start_streaming	= vbi_cap_start_streaming,
 	.stop_streaming		= vbi_cap_stop_streaming,
+	.buf_request_complete	= vbi_cap_buf_request_complete,
 	.wait_prepare		= vb2_ops_wait_prepare,
 	.wait_finish		= vb2_ops_wait_finish,
 };
diff --git a/drivers/media/platform/vivid/vivid-vbi-out.c b/drivers/media/platform/vivid/vivid-vbi-out.c
index 69486c130a7e..9357c07e30d6 100644
--- a/drivers/media/platform/vivid/vivid-vbi-out.c
+++ b/drivers/media/platform/vivid/vivid-vbi-out.c
@@ -96,6 +96,8 @@ static int vbi_out_start_streaming(struct vb2_queue *vq, unsigned count)
 
 		list_for_each_entry_safe(buf, tmp, &dev->vbi_out_active, list) {
 			list_del(&buf->list);
+			v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
+						   &dev->ctrl_hdl_vbi_out);
 			vb2_buffer_done(&buf->vb.vb2_buf,
 					VB2_BUF_STATE_QUEUED);
 		}
@@ -115,12 +117,20 @@ static void vbi_out_stop_streaming(struct vb2_queue *vq)
 	dev->vbi_out_have_cc[1] = false;
 }
 
+static void vbi_out_buf_request_complete(struct vb2_buffer *vb)
+{
+	struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+
+	v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_vbi_out);
+}
+
 const struct vb2_ops vivid_vbi_out_qops = {
 	.queue_setup		= vbi_out_queue_setup,
 	.buf_prepare		= vbi_out_buf_prepare,
 	.buf_queue		= vbi_out_buf_queue,
 	.start_streaming	= vbi_out_start_streaming,
 	.stop_streaming		= vbi_out_stop_streaming,
+	.buf_request_complete	= vbi_out_buf_request_complete,
 	.wait_prepare		= vb2_ops_wait_prepare,
 	.wait_finish		= vb2_ops_wait_finish,
 };
diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c
index 1599159f2574..b2aad441a071 100644
--- a/drivers/media/platform/vivid/vivid-vid-cap.c
+++ b/drivers/media/platform/vivid/vivid-vid-cap.c
@@ -240,6 +240,8 @@ static int vid_cap_start_streaming(struct vb2_queue *vq, unsigned count)
 
 		list_for_each_entry_safe(buf, tmp, &dev->vid_cap_active, list) {
 			list_del(&buf->list);
+			v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
+						   &dev->ctrl_hdl_vid_cap);
 			vb2_buffer_done(&buf->vb.vb2_buf,
 					VB2_BUF_STATE_QUEUED);
 		}
@@ -257,6 +259,13 @@ static void vid_cap_stop_streaming(struct vb2_queue *vq)
 	dev->can_loop_video = false;
 }
 
+static void vid_cap_buf_request_complete(struct vb2_buffer *vb)
+{
+	struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+
+	v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_vid_cap);
+}
+
 const struct vb2_ops vivid_vid_cap_qops = {
 	.queue_setup		= vid_cap_queue_setup,
 	.buf_prepare		= vid_cap_buf_prepare,
@@ -264,6 +273,7 @@ const struct vb2_ops vivid_vid_cap_qops = {
 	.buf_queue		= vid_cap_buf_queue,
 	.start_streaming	= vid_cap_start_streaming,
 	.stop_streaming		= vid_cap_stop_streaming,
+	.buf_request_complete	= vid_cap_buf_request_complete,
 	.wait_prepare		= vb2_ops_wait_prepare,
 	.wait_finish		= vb2_ops_wait_finish,
 };
diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c
index 51fec66d8d45..423a67133f28 100644
--- a/drivers/media/platform/vivid/vivid-vid-out.c
+++ b/drivers/media/platform/vivid/vivid-vid-out.c
@@ -162,6 +162,8 @@ static int vid_out_start_streaming(struct vb2_queue *vq, unsigned count)
 
 		list_for_each_entry_safe(buf, tmp, &dev->vid_out_active, list) {
 			list_del(&buf->list);
+			v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
+						   &dev->ctrl_hdl_vid_out);
 			vb2_buffer_done(&buf->vb.vb2_buf,
 					VB2_BUF_STATE_QUEUED);
 		}
@@ -179,12 +181,20 @@ static void vid_out_stop_streaming(struct vb2_queue *vq)
 	dev->can_loop_video = false;
 }
 
+static void vid_out_buf_request_complete(struct vb2_buffer *vb)
+{
+	struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+
+	v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_vid_out);
+}
+
 const struct vb2_ops vivid_vid_out_qops = {
 	.queue_setup		= vid_out_queue_setup,
 	.buf_prepare		= vid_out_buf_prepare,
 	.buf_queue		= vid_out_buf_queue,
 	.start_streaming	= vid_out_start_streaming,
 	.stop_streaming		= vid_out_stop_streaming,
+	.buf_request_complete	= vid_out_buf_request_complete,
 	.wait_prepare		= vb2_ops_wait_prepare,
 	.wait_finish		= vb2_ops_wait_finish,
 };
-- 
2.18.0

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

* [PATCHv17 34/34] RFC: media-requests: add debugfs node
  2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
                   ` (32 preceding siblings ...)
  2018-08-04 12:45 ` [PATCHv17 33/34] vivid: add request support Hans Verkuil
@ 2018-08-04 12:45 ` Hans Verkuil
  2018-08-13 15:15   ` Mauro Carvalho Chehab
  33 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-04 12:45 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

Keep track of the number of requests and request objects of a media
device. Helps to verify that all request-related memory is freed.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/media-device.c  | 41 +++++++++++++++++++++++++++++++++++
 drivers/media/media-devnode.c | 17 +++++++++++++++
 drivers/media/media-request.c |  5 +++++
 include/media/media-device.h  | 11 ++++++++++
 include/media/media-devnode.h |  4 ++++
 include/media/media-request.h |  2 ++
 6 files changed, 80 insertions(+)

diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
index 4b9a8de05562..28a891b53886 100644
--- a/drivers/media/media-device.c
+++ b/drivers/media/media-device.c
@@ -691,6 +691,23 @@ void media_device_unregister_entity(struct media_entity *entity)
 }
 EXPORT_SYMBOL_GPL(media_device_unregister_entity);
 
+#ifdef CONFIG_DEBUG_FS
+/*
+ * Log the state of media requests.
+ * Very useful for debugging.
+ */
+static int media_device_requests(struct seq_file *file, void *priv)
+{
+	struct media_device *dev = dev_get_drvdata(file->private);
+
+	seq_printf(file, "number of requests: %d\n",
+		   atomic_read(&dev->num_requests));
+	seq_printf(file, "number of request objects: %d\n",
+		   atomic_read(&dev->num_request_objects));
+	return 0;
+}
+#endif
+
 /**
  * media_device_init() - initialize a media device
  * @mdev:	The media device
@@ -713,6 +730,9 @@ void media_device_init(struct media_device *mdev)
 	mutex_init(&mdev->graph_mutex);
 	ida_init(&mdev->entity_internal_idx);
 
+	atomic_set(&mdev->num_requests, 0);
+	atomic_set(&mdev->num_request_objects, 0);
+
 	dev_dbg(mdev->dev, "Media device initialized\n");
 }
 EXPORT_SYMBOL_GPL(media_device_init);
@@ -764,6 +784,26 @@ int __must_check __media_device_register(struct media_device *mdev,
 
 	dev_dbg(mdev->dev, "Media device registered\n");
 
+#ifdef CONFIG_DEBUG_FS
+	if (!media_top_dir)
+		return 0;
+
+	mdev->media_dir = debugfs_create_dir(dev_name(&devnode->dev),
+					     media_top_dir);
+	if (IS_ERR_OR_NULL(mdev->media_dir)) {
+		dev_warn(mdev->dev, "Failed to create debugfs dir\n");
+		return 0;
+	}
+	mdev->requests_file = debugfs_create_devm_seqfile(&devnode->dev,
+		"requests", mdev->media_dir, media_device_requests);
+	if (IS_ERR_OR_NULL(mdev->requests_file)) {
+		dev_warn(mdev->dev, "Failed to create requests file\n");
+		debugfs_remove_recursive(mdev->media_dir);
+		mdev->media_dir = NULL;
+		return 0;
+	}
+#endif
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(__media_device_register);
@@ -841,6 +881,7 @@ void media_device_unregister(struct media_device *mdev)
 
 	dev_dbg(mdev->dev, "Media device unregistered\n");
 
+	debugfs_remove_recursive(mdev->media_dir);
 	device_remove_file(&mdev->devnode->dev, &dev_attr_model);
 	media_devnode_unregister(mdev->devnode);
 	/* devnode free is handled in media_devnode_*() */
diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c
index 6b87a721dc49..4358ed22f208 100644
--- a/drivers/media/media-devnode.c
+++ b/drivers/media/media-devnode.c
@@ -53,6 +53,12 @@ static dev_t media_dev_t;
 static DEFINE_MUTEX(media_devnode_lock);
 static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES);
 
+/*
+ * debugfs
+ */
+struct dentry *media_top_dir;
+
+
 /* Called when the last user of the media device exits. */
 static void media_devnode_release(struct device *cd)
 {
@@ -259,6 +265,8 @@ int __must_check media_devnode_register(struct media_device *mdev,
 		goto cdev_add_error;
 	}
 
+	dev_set_drvdata(&devnode->dev, mdev);
+
 	/* Part 4: Activate this minor. The char device can now be used. */
 	set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
 
@@ -310,6 +318,14 @@ static int __init media_devnode_init(void)
 		return ret;
 	}
 
+#ifdef CONFIG_DEBUG_FS
+	media_top_dir = debugfs_create_dir("media", NULL);
+	if (IS_ERR_OR_NULL(media_top_dir)) {
+		pr_warn("Failed to create debugfs media dir\n");
+		media_top_dir = NULL;
+	}
+#endif
+
 	ret = bus_register(&media_bus_type);
 	if (ret < 0) {
 		unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
@@ -322,6 +338,7 @@ static int __init media_devnode_init(void)
 
 static void __exit media_devnode_exit(void)
 {
+	debugfs_remove_recursive(media_top_dir);
 	bus_unregister(&media_bus_type);
 	unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
 }
diff --git a/drivers/media/media-request.c b/drivers/media/media-request.c
index a5b70a4e613b..8ba97a9c4bf1 100644
--- a/drivers/media/media-request.c
+++ b/drivers/media/media-request.c
@@ -72,6 +72,7 @@ static void media_request_release(struct kref *kref)
 		mdev->ops->req_free(req);
 	else
 		kfree(req);
+	atomic_dec(&mdev->num_requests);
 }
 
 void media_request_put(struct media_request *req)
@@ -318,6 +319,7 @@ int media_request_alloc(struct media_device *mdev,
 	get_task_comm(comm, current);
 	snprintf(req->debug_str, sizeof(req->debug_str), "%s:%d",
 		 comm, fd);
+	atomic_inc(&mdev->num_requests);
 	dev_dbg(mdev->dev, "request: allocated %s\n", req->debug_str);
 
 	fd_install(fd, filp);
@@ -342,6 +344,7 @@ static void media_request_object_release(struct kref *kref)
 	if (WARN_ON(req))
 		media_request_object_unbind(obj);
 	obj->ops->release(obj);
+	atomic_dec(&obj->mdev->num_request_objects);
 }
 
 struct media_request_object *
@@ -405,10 +408,12 @@ int media_request_object_bind(struct media_request *req,
 	obj->req = req;
 	obj->ops = ops;
 	obj->priv = priv;
+	obj->mdev = req->mdev;
 
 	list_add_tail(&obj->list, &req->objects);
 	req->num_incomplete_objects++;
 	ret = 0;
+	atomic_inc(&obj->mdev->num_request_objects);
 
 unlock:
 	spin_unlock_irqrestore(&req->lock, flags);
diff --git a/include/media/media-device.h b/include/media/media-device.h
index 110b89567671..2de0606938d4 100644
--- a/include/media/media-device.h
+++ b/include/media/media-device.h
@@ -21,6 +21,7 @@
 
 #include <linux/list.h>
 #include <linux/mutex.h>
+#include <linux/atomic.h>
 
 #include <media/media-devnode.h>
 #include <media/media-entity.h>
@@ -107,6 +108,10 @@ struct media_device_ops {
  * @ops:	Operation handler callbacks
  * @req_queue_mutex: Serialise the MEDIA_REQUEST_IOC_QUEUE ioctl w.r.t.
  *		     other operations that stop or start streaming.
+ * @num_requests: number of associated requests
+ * @num_request_objects: number of associated request objects
+ * @media_dir:	DebugFS media directory
+ * @requests_file: DebugFS requests file
  *
  * This structure represents an abstract high-level media device. It allows easy
  * access to entities and provides basic media device-level support. The
@@ -179,6 +184,12 @@ struct media_device {
 	const struct media_device_ops *ops;
 
 	struct mutex req_queue_mutex;
+	atomic_t num_requests;
+	atomic_t num_request_objects;
+
+	/* debugfs */
+	struct dentry *media_dir;
+	struct dentry *requests_file;
 };
 
 /* We don't need to include pci.h or usb.h here */
diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
index dc2f64e1b08f..984b62b634d3 100644
--- a/include/media/media-devnode.h
+++ b/include/media/media-devnode.h
@@ -28,9 +28,13 @@
 #include <linux/fs.h>
 #include <linux/device.h>
 #include <linux/cdev.h>
+#include <linux/debugfs.h>
 
 struct media_device;
 
+/* DebugFS top-level media directory */
+extern struct dentry *media_top_dir;
+
 /*
  * Flag to mark the media_devnode struct as registered. Drivers must not touch
  * this flag directly, it will be set and cleared by media_devnode_register and
diff --git a/include/media/media-request.h b/include/media/media-request.h
index fd08d7a431a1..76727d4a89c3 100644
--- a/include/media/media-request.h
+++ b/include/media/media-request.h
@@ -210,6 +210,7 @@ struct media_request_object_ops {
  * struct media_request_object - An opaque object that belongs to a media
  *				 request
  *
+ * @mdev: Media device this object belongs to
  * @ops: object's operations
  * @priv: object's priv pointer
  * @req: the request this object belongs to (can be NULL)
@@ -221,6 +222,7 @@ struct media_request_object_ops {
  * another struct that contains the actual data for this request object.
  */
 struct media_request_object {
+	struct media_device *mdev;
 	const struct media_request_object_ops *ops;
 	void *priv;
 	struct media_request *req;
-- 
2.18.0

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

* Re: [PATCHv17 31/34] vim2m: support requests
  2018-08-04 12:45 ` [PATCHv17 31/34] vim2m: support requests Hans Verkuil
@ 2018-08-06 21:02   ` Ezequiel Garcia
  2018-08-13 15:08   ` Mauro Carvalho Chehab
  1 sibling, 0 replies; 106+ messages in thread
From: Ezequiel Garcia @ 2018-08-06 21:02 UTC (permalink / raw)
  To: Hans Verkuil, linux-media; +Cc: Hans Verkuil

Hey Hans,

Just a small nit.

On Sat, 2018-08-04 at 14:45 +0200, Hans Verkuil wrote:
> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Add support for requests to vim2m.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  drivers/media/platform/vim2m.c | 26 ++++++++++++++++++++++++++
>  1 file changed, 26 insertions(+)
> 
> diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c
> index 6f87ef025ff1..3b8df2c9d24a 100644
> --- a/drivers/media/platform/vim2m.c
> +++ b/drivers/media/platform/vim2m.c
> @@ -379,8 +379,18 @@ static void device_run(void *priv)
>  	src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
>  	dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
>  
> +	/* Apply request controls if needed */
> +	if (src_buf->vb2_buf.req_obj.req)

Seems v4l2_ctrl_request_setup checks for null parameters,
so the check is not needed.

> +		v4l2_ctrl_request_setup(src_buf->vb2_buf.req_obj.req,
> +					&ctx->hdl);
> +
>  	device_process(ctx, src_buf, dst_buf);
>  
> +	/* Complete request controls if needed */
> +	if (src_buf->vb2_buf.req_obj.req)
> +		v4l2_ctrl_request_complete(src_buf->vb2_buf.req_obj.req,
> +					&ctx->hdl);
> +
> 

Ditto.

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

* Re: [PATCHv17 01/34] Documentation: v4l: document request API
  2018-08-04 12:44 ` [PATCHv17 01/34] Documentation: v4l: document request API Hans Verkuil
@ 2018-08-06 23:44   ` Pavel Machek
  2018-08-09 17:43   ` Mauro Carvalho Chehab
  1 sibling, 0 replies; 106+ messages in thread
From: Pavel Machek @ 2018-08-06 23:44 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Alexandre Courbot, Hans Verkuil

Hi!

> Document the request API for V4L2 devices, and amend the documentation
> of system calls influenced by it.
> 
> Signed-off-by: Alexandre Courbot <acourbot@chromium.org>
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>

Cc documentation people?

> +Synopsis
> +========
> +
> +.. c:function:: int ioctl( int request_fd, MEDIA_REQUEST_IOC_QUEUE )
> +    :name: MEDIA_REQUEST_IOC_QUEUE
> +
> +
> +Arguments
> +=========
> +
> +``request_fd``
> +    File descriptor returned by :ref:`MEDIA_IOC_REQUEST_ALLOC`.
> +
> +
> +Description
> +===========
> +
> +If the media device supports :ref:`requests <media-request-api>`, then
> +this request ioctl can be used to queue a previously allocated request.
> +
> +If the request was successfully queued, then the file descriptor can be
> +:ref:`polled <request-func-poll>` to wait for the request to complete.

> +
> +If the request was already queued before, then ``EBUSY`` is returned.

I'd expect -1 to be returned and errno set to EBUSY?

> +============
> +
> +On success 0 is returned, on error -1 and the ``errno`` variable is set
> +appropriately. The generic error codes are described at the
> +:ref:`Generic Error Codes <gen-errors>` chapter.
> +
> +EBUSY
> +    The request was already queued.
> +EPERM
> +    The application queued the first buffer directly, but later attempted
> +    to use a request. It is not permitted to mix the two APIs.
> +ENOENT
> +    The request did not contain any buffers. All requests are required
> +    to have at least one buffer. This can also be returned if required
> +    controls are missing.
> +ENOMEM
> +    Out of memory when allocating internal data structures for this
> +    request.
> +EINVAL
> +    The request has invalid data.
> +EIO
> +    The hardware is in a bad state. To recover, the application needs to
> +    stop streaming to reset the hardware state and then try to restart
> +    streaming.

										Pavel

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

* Re: [PATCHv17 01/34] Documentation: v4l: document request API
  2018-08-04 12:44 ` [PATCHv17 01/34] Documentation: v4l: document request API Hans Verkuil
  2018-08-06 23:44   ` Pavel Machek
@ 2018-08-09 17:43   ` Mauro Carvalho Chehab
  2018-08-10  7:20     ` Hans Verkuil
  2018-08-14  7:57     ` Hans Verkuil
  1 sibling, 2 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-09 17:43 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Alexandre Courbot, Hans Verkuil

Em Sat,  4 Aug 2018 14:44:53 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Alexandre Courbot <acourbot@chromium.org>
> 
> Document the request API for V4L2 devices, and amend the documentation
> of system calls influenced by it.

It follows some comments. Most are nitpicks. There are just two ones
that aren't:
	- a problem at the tables changes on Documentation/
	- a question with regards to MEDIA_IOC_REQUEST_ALLOC ioctl.

I'll keep reviewing this patch series.

PS.: I lost entirely my first review to this doc... I hope I didn't
forget anything when re-typing my comments.

> 
> Signed-off-by: Alexandre Courbot <acourbot@chromium.org>
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  .../media/uapi/mediactl/media-controller.rst  |   1 +
>  .../media/uapi/mediactl/media-funcs.rst       |   6 +
>  .../uapi/mediactl/media-ioc-request-alloc.rst |  78 ++++++
>  .../uapi/mediactl/media-request-ioc-queue.rst |  82 ++++++
>  .../mediactl/media-request-ioc-reinit.rst     |  51 ++++
>  .../media/uapi/mediactl/request-api.rst       | 247 ++++++++++++++++++
>  .../uapi/mediactl/request-func-close.rst      |  49 ++++
>  .../uapi/mediactl/request-func-ioctl.rst      |  68 +++++
>  .../media/uapi/mediactl/request-func-poll.rst |  77 ++++++
>  Documentation/media/uapi/v4l/buffer.rst       |  21 +-
>  .../media/uapi/v4l/vidioc-g-ext-ctrls.rst     |  94 ++++---
>  Documentation/media/uapi/v4l/vidioc-qbuf.rst  |  32 ++-
>  .../media/videodev2.h.rst.exceptions          |   1 +
>  13 files changed, 771 insertions(+), 36 deletions(-)
>  create mode 100644 Documentation/media/uapi/mediactl/media-ioc-request-alloc.rst
>  create mode 100644 Documentation/media/uapi/mediactl/media-request-ioc-queue.rst
>  create mode 100644 Documentation/media/uapi/mediactl/media-request-ioc-reinit.rst
>  create mode 100644 Documentation/media/uapi/mediactl/request-api.rst
>  create mode 100644 Documentation/media/uapi/mediactl/request-func-close.rst
>  create mode 100644 Documentation/media/uapi/mediactl/request-func-ioctl.rst
>  create mode 100644 Documentation/media/uapi/mediactl/request-func-poll.rst
> 
> diff --git a/Documentation/media/uapi/mediactl/media-controller.rst b/Documentation/media/uapi/mediactl/media-controller.rst
> index 0eea4f9a07d5..66aff38cd499 100644
> --- a/Documentation/media/uapi/mediactl/media-controller.rst
> +++ b/Documentation/media/uapi/mediactl/media-controller.rst
> @@ -21,6 +21,7 @@ Part IV - Media Controller API
>      media-controller-intro
>      media-controller-model
>      media-types
> +    request-api
>      media-funcs
>      media-header
>  
> diff --git a/Documentation/media/uapi/mediactl/media-funcs.rst b/Documentation/media/uapi/mediactl/media-funcs.rst
> index 076856501cdb..260f9dcadcde 100644
> --- a/Documentation/media/uapi/mediactl/media-funcs.rst
> +++ b/Documentation/media/uapi/mediactl/media-funcs.rst
> @@ -16,3 +16,9 @@ Function Reference
>      media-ioc-enum-entities
>      media-ioc-enum-links
>      media-ioc-setup-link
> +    media-ioc-request-alloc
> +    request-func-close
> +    request-func-ioctl
> +    request-func-poll
> +    media-request-ioc-queue
> +    media-request-ioc-reinit
> diff --git a/Documentation/media/uapi/mediactl/media-ioc-request-alloc.rst b/Documentation/media/uapi/mediactl/media-ioc-request-alloc.rst
> new file mode 100644
> index 000000000000..11dcc14ca0bd
> --- /dev/null
> +++ b/Documentation/media/uapi/mediactl/media-ioc-request-alloc.rst
> @@ -0,0 +1,78 @@
> +.. SPDX-License-Identifier: GPL-2.0

Better to use SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-or-later WITH no-invariant-sections

E. g. dual-licensing it with FDL and add a LICENSES/exceptions for
no-invariant-sections.

> +
> +.. _media_ioc_request_alloc:
> +
> +*****************************
> +ioctl MEDIA_IOC_REQUEST_ALLOC
> +*****************************
> +
> +Name
> +====
> +
> +MEDIA_IOC_REQUEST_ALLOC - Allocate a request
> +
> +
> +Synopsis
> +========
> +
> +.. c:function:: int ioctl( int fd, MEDIA_IOC_REQUEST_ALLOC, struct media_request_alloc *argp )
> +    :name: MEDIA_IOC_REQUEST_ALLOC
> +
> +
> +Arguments
> +=========
> +
> +``fd``
> +    File descriptor returned by :ref:`open() <media-func-open>`.
> +
> +``argp``
> +    Pointer to struct :c:type:`media_request_alloc`.
> +
> +
> +Description
> +===========
> +
> +If the media device supports :ref:`requests <media-request-api>`, then
> +this ioctl can be used to allocate a request. If it is not supported, then
> +``errno`` is set to ``ENOTTY``. A request is accessed through a file descriptor
> +that is returned in struct :c:type:`media_request_alloc`.
> +
> +If the request was successfully allocated, then the request file descriptor
> +can be passed to the :ref:`VIDIOC_QBUF <VIDIOC_QBUF>`,
> +:ref:`VIDIOC_G_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>`,
> +:ref:`VIDIOC_S_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` and
> +:ref:`VIDIOC_TRY_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` ioctls.
> +
> +In addition, the request can be queued by calling
> +:ref:`MEDIA_REQUEST_IOC_QUEUE` and re-initialized by calling
> +:ref:`MEDIA_REQUEST_IOC_REINIT`.
> +
> +Finally, the file descriptor can be :ref:`polled <request-func-poll>` to wait
> +for the request to complete.
> +
> +The request will remain allocated until all the file descriptors associated
> +with it are closed by :ref:`close() <request-func-close>` and the driver no
> +longer uses the request internally.
> +
> +.. c:type:: media_request_alloc
> +
> +.. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.7cm}|
> +
> +.. flat-table:: struct media_request_alloc
> +    :header-rows:  0
> +    :stub-columns: 0
> +    :widths:       1 1 2
> +
> +    *  -  __s32
> +       -  ``fd``
> +       -  The file descriptor of the request.

It should be mentioned that the struct should be zeroed before calling
the Kernel, but I is overkill to have a struct to pass just one value.

I mean, if this has just one value inside the struct, it is a way better
to declare it as:

.. c:function:: int ioctl( int fd, MEDIA_IOC_REQUEST_ALLOC, s32 &argp )

Even if we later need more stuff, the size of a new MEDIA_IOC_REQUEST_ALLOC
will be bigger, and then (and only then) we can add extra stuff.

Or are you foreseen any new fields there in short term?



> +
> +Return Value
> +============
> +
> +On success 0 is returned, on error -1 and the ``errno`` variable is set
> +appropriately. The generic error codes are described at the
> +:ref:`Generic Error Codes <gen-errors>` chapter.
> +
> +ENOTTY
> +    The driver has no support for requests.
> diff --git a/Documentation/media/uapi/mediactl/media-request-ioc-queue.rst b/Documentation/media/uapi/mediactl/media-request-ioc-queue.rst
> new file mode 100644
> index 000000000000..86a341cbc59e
> --- /dev/null
> +++ b/Documentation/media/uapi/mediactl/media-request-ioc-queue.rst
> @@ -0,0 +1,82 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +.. _media_request_ioc_queue:
> +
> +*****************************
> +ioctl MEDIA_REQUEST_IOC_QUEUE
> +*****************************
> +
> +Name
> +====
> +
> +MEDIA_REQUEST_IOC_QUEUE - Queue a request
> +
> +
> +Synopsis
> +========
> +
> +.. c:function:: int ioctl( int request_fd, MEDIA_REQUEST_IOC_QUEUE )
> +    :name: MEDIA_REQUEST_IOC_QUEUE
> +
> +
> +Arguments
> +=========
> +
> +``request_fd``
> +    File descriptor returned by :ref:`MEDIA_IOC_REQUEST_ALLOC`.
> +
> +
> +Description
> +===========
> +
> +If the media device supports :ref:`requests <media-request-api>`, then
> +this request ioctl can be used to queue a previously allocated request.
> +
> +If the request was successfully queued, then the file descriptor can be
> +:ref:`polled <request-func-poll>` to wait for the request to complete.
> +
> +If the request was already queued before, then ``EBUSY`` is returned.
> +Other errors can be returned if the contents of the request contained
> +invalid or inconsistent data, see the next section for a list of
> +common error codes. On error both the request and driver state are unchanged.
> +
> +Typically if you get an error here, then that means that the application
> +did something wrong and you have to fix the application.
> +
> +Once a request is queued, then the driver is required to gracefully handle
> +errors that occur when the request is applied to the hardware. The
> +exception is the ``EIO`` error which signals a fatal error that requires
> +the application to stop streaming to reset the hardware state.
> +
> +It is not allowed to mix queuing requests with queuing buffers directly
> +(without a request). ``EPERM`` will be returned if the first buffer was
> +queued directly and you next try to queue a request, or vice versa.
> +
> +A request must contain at least one buffer, otherwise this ioctl will
> +return an ``ENOENT`` error.
> +
> +Return Value
> +============
> +
> +On success 0 is returned, on error -1 and the ``errno`` variable is set
> +appropriately. The generic error codes are described at the
> +:ref:`Generic Error Codes <gen-errors>` chapter.
> +
> +EBUSY
> +    The request was already queued.
> +EPERM
> +    The application queued the first buffer directly, but later attempted
> +    to use a request. It is not permitted to mix the two APIs.
> +ENOENT
> +    The request did not contain any buffers. All requests are required
> +    to have at least one buffer. This can also be returned if required
> +    controls are missing.
> +ENOMEM
> +    Out of memory when allocating internal data structures for this
> +    request.
> +EINVAL
> +    The request has invalid data.
> +EIO
> +    The hardware is in a bad state. To recover, the application needs to
> +    stop streaming to reset the hardware state and then try to restart
> +    streaming.
> diff --git a/Documentation/media/uapi/mediactl/media-request-ioc-reinit.rst b/Documentation/media/uapi/mediactl/media-request-ioc-reinit.rst
> new file mode 100644
> index 000000000000..7ede0584a0d7
> --- /dev/null
> +++ b/Documentation/media/uapi/mediactl/media-request-ioc-reinit.rst
> @@ -0,0 +1,51 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +.. _media_request_ioc_reinit:
> +
> +******************************
> +ioctl MEDIA_REQUEST_IOC_REINIT
> +******************************
> +
> +Name
> +====
> +
> +MEDIA_REQUEST_IOC_REINIT - Re-initialize a request
> +
> +
> +Synopsis
> +========
> +
> +.. c:function:: int ioctl( int request_fd, MEDIA_REQUEST_IOC_REINIT )
> +    :name: MEDIA_REQUEST_IOC_REINIT
> +
> +
> +Arguments
> +=========
> +
> +``request_fd``
> +    File descriptor returned by :ref:`MEDIA_IOC_REQUEST_ALLOC`.
> +
> +Description
> +===========
> +
> +If the media device supports :ref:`requests <media-request-api>`, then
> +this request ioctl can be used to re-initialize a previously allocated
> +request.
> +
> +Re-initializing a request will clear any existing data from the request.
> +This avoids having to :ref:`close() <request-func-close>` a completed
> +request and allocate a new request. Instead the completed request can just
> +be re-initialized and it is ready to be used again.
> +
> +A request can only be re-initialized if it either has not been queued
> +yet, or if it was queued and completed. Otherwise it will set ``errno``
> +to ``EBUSY``. No other error codes can be returned.
> +
> +Return Value
> +============
> +
> +On success 0 is returned, on error -1 and the ``errno`` variable is set
> +appropriately.
> +
> +EBUSY
> +    The request is queued but not yet completed.
> diff --git a/Documentation/media/uapi/mediactl/request-api.rst b/Documentation/media/uapi/mediactl/request-api.rst
> new file mode 100644
> index 000000000000..5d1c4674d4bb
> --- /dev/null
> +++ b/Documentation/media/uapi/mediactl/request-api.rst
> @@ -0,0 +1,247 @@


> +.. -*- coding: utf-8; mode: rst -*-

I guess this is not needed.

Also, SPDX header is missing here.

> +
> +.. _media-request-api:
> +
> +Request API
> +===========
> +
> +The Request API has been designed to allow V4L2 to deal with requirements of
> +modern devices (stateless codecs, complex camera pipelines, ...) and APIs
> +(Android Codec v2). One such requirement is the ability for devices belonging to
> +the same pipeline to reconfigure and collaborate closely on a per-frame basis.
> +Another is support of stateless codecs, which require controls to be applied
> +to specific frames (aka 'per-frame controls') in order to be used efficiently.
> +
> +Supporting these features without the Request API is not always possible and if
> +it is, it is terribly inefficient: user-space would have to flush all activity
> +on the media pipeline, reconfigure it for the next frame, queue the buffers to
> +be processed with that configuration, and wait until they are all available for
> +dequeuing before considering the next frame. This defeats the purpose of having
> +buffer queues since in practice only one buffer would be queued at a time.
> +
> +The Request API allows a specific configuration of the pipeline (media
> +controller topology + controls for each media entity) to be associated with
> +specific buffers. The parameters are applied by each participating device as
> +buffers associated to a request flow in. This allows user-space to schedule
> +several tasks ("requests") with different parameters in advance, knowing that
> +the parameters will be applied when needed to get the expected result. Control
> +values at the time of request completion are also available for reading.
> +
> +Usage
> +=====
> +
> +The Request API is used on top of standard media controller and V4L2 calls,
> +which are augmented with an extra ``request_fd`` parameter. Requests themselves
> +are allocated from the supporting media controller node.
> +
> +Request Allocation
> +------------------
> +
> +User-space allocates requests using :ref:`MEDIA_IOC_REQUEST_ALLOC`
> +for the media device node. This returns a file descriptor representing the
> +request. Typically, several such requests will be allocated.
> +
> +Request Preparation
> +-------------------
> +
> +Standard V4L2 ioctls can then receive a request file descriptor to express the
> +fact that the ioctl is part of said request, and is not to be applied
> +immediately. See :ref:`MEDIA_IOC_REQUEST_ALLOC` for a list of ioctls that
> +support this. Controls set with a ``request_fd`` parameter are stored instead
> +of being immediately applied, and buffers queued to a request do not enter the
> +regular buffer queue until the request itself is queued.
> +
> +Request Submission
> +------------------
> +
> +Once the parameters and buffers of the request are specified, it can be
> +queued by calling :ref:`MEDIA_REQUEST_IOC_QUEUE` on the request FD. A request
> +must contain at least one buffer, otherwise ``ENOENT`` is returned.
> +This will make the buffers associated to the request available to their driver,
> +which can then apply the associated controls as buffers are processed. A queued
> +request cannot be modified anymore.
> +
> +.. caution::
> +   For :ref:`memory-to-memory devices <codec>` you can use requests only for
> +   output buffers, not for capture buffers. Attempting to add a capture buffer
> +   to a request will result in an ``EPERM`` error.
> +
> +If the request contains parameters for multiple entities, individual drivers may
> +synchronize so the requested pipeline's topology is applied before the buffers
> +are processed. Media controller drivers do a best effort implementation since
> +perfect atomicity may not be possible due to hardware limitations.
> +
> +.. caution::
> +
> +   It is not allowed to mix queuing requests with directly queuing buffers: whichever
> +   method is used first locks this in place until :ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>`
> +   is called or the device is :ref:`closed <func-close>`. Attempts to
> +   directly queue a buffer when earlier a buffer was queued via a request or
> +   vice versa will result in an ``EPERM`` error.

Please don't write past 80 columns.

When writing docs, I actually prefer to end each line with a soft limit
of ~72 characters, as it makes easier if we later need to change the text
inside it, for small changes.

> +
> +Controls can still be set without a request and are applied immediately,
> +regardless of whether a request is in use or not.
> +
> +.. caution::
> +
> +   Setting the same control through a request and also directly can lead to
> +   undefined behavior!
> +
> +User-space can :ref:`poll() <request-func-poll>` a request FD in order to

This is a generic comment, as this happens several times inside this patch:
let's not use abbreviations without a very good reason why. In this specific
case, if "FD" means a ``fd`` argument, please write it as such. Otherwise:

	FD -> file descriptor

(and be sure to not go past 80 chars per line when doing such change)

> +wait until the request completes. A request is considered complete once all its
> +associated buffers are available for dequeuing and all the associated controls
> +have been updated with the values at the time of completion. Note that user-space
> +does not need to wait for the request to complete to dequeue its buffers: buffers
> +that are available halfway through a request can be dequeued independently of the
> +request's state.
> +
> +A completed request contains the state of the request at the time of the
> +request completion. User-space can query that state by calling
> +:ref:`ioctl VIDIOC_G_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` with the request FD.
> +Calling :ref:`ioctl VIDIOC_G_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` for a
> +request that has been queued but not yet completed will return ``EBUSY``
> +since the control values might be changed at any time by the driver while the
> +request is in flight.
> +
> +Recycling and Destruction
> +-------------------------
> +
> +Finally, a completed request can either be discarded or be reused. Calling
> +:ref:`close() <request-func-close>` on a request FD will make that FD unusable
> +and the request will be freed once it is no longer in use by the kernel. That
> +is, if the request is queued and then the FD is closed, then it won't be freed
> +until the driver completed the request.
> +
> +The :ref:`MEDIA_REQUEST_IOC_REINIT` will clear a request's state and make it
> +available again. No state is retained by this operation: the request is as
> +if it had just been allocated.
> +
> +Example for a Codec Device
> +--------------------------
> +
> +For use-cases such as :ref:`codecs <codec>`, the request API can be used
> +to associate specific controls to
> +be applied by the driver for the OUTPUT buffer, allowing user-space
> +to queue many such buffers in advance. It can also take advantage of requests'
> +ability to capture the state of controls when the request completes to read back
> +information that may be subject to change.
> +
> +Put into code, after obtaining a request, user-space can assign controls and one
> +OUTPUT buffer to it:
> +
> +.. code-block:: c
> +
> +	struct v4l2_buffer buf;
> +	struct v4l2_ext_controls ctrls;
> +	struct media_request_alloc alloc = { 0 };
> +	int req_fd;
> +	...
> +	if (ioctl(media_fd, MEDIA_IOC_REQUEST_ALLOC, &alloc))
> +		return errno;
> +	req_fd = alloc.fd;
> +	...
> +	ctrls.which = V4L2_CTRL_WHICH_REQUEST_VAL;
> +	ctrls.request_fd = req_fd;
> +	if (ioctl(codec_fd, VIDIOC_S_EXT_CTRLS, &ctrls))
> +		return errno;
> +	...
> +	buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
> +	buf.flags |= V4L2_BUF_FLAG_REQUEST_FD;
> +	buf.request_fd = req_fd;
> +	if (ioctl(codec_fd, VIDIOC_QBUF, &buf))
> +		return errno;
> +
> +Note that there is typically no need to use the Request API for CAPTURE buffers
> +since there are no per-frame settings to report there.
> +
> +Once the request is fully prepared, it can be queued to the driver:
> +
> +.. code-block:: c
> +
> +	if (ioctl(req_fd, MEDIA_REQUEST_IOC_QUEUE))
> +		return errno;
> +
> +User-space can then either wait for the request to complete by calling poll() on
> +its file descriptor, or start dequeuing CAPTURE buffers. Most likely, it will
> +want to get CAPTURE buffers as soon as possible and this can be done using a
> +regular DQBUF:

	DQBUF -> :ref:`VIDIOC_DQBUF <VIDIOC_QBUF>`

> +
> +.. code-block:: c
> +
> +	struct v4l2_buffer buf;
> +
> +	memset(&buf, 0, sizeof(buf));
> +	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	if (ioctl(codec_fd, VIDIOC_DQBUF, &buf))
> +		return errno;
> +
> +Note that this example assumes for simplicity that for every OUTPUT buffer
> +there will be one CAPTURE buffer, but this does not have to be the case.
> +
> +We can then, after ensuring that the request is completed via polling the
> +request FD, query control values at the time of its completion via a
> +call to :ref:`VIDIOC_G_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>`.
> +This is particularly useful for volatile controls for which we want to
> +query values as soon as the capture buffer is produced.
> +
> +.. code-block:: c
> +
> +	struct pollfd pfd = { .events = POLLPRI, .fd = req_fd };
> +	poll(&pfd, 1, -1);
> +	...
> +	ctrls.which = V4L2_CTRL_WHICH_REQUEST_VAL;
> +	ctrls.request_fd = req_fd;
> +	if (ioctl(codec_fd, VIDIOC_G_EXT_CTRLS, &ctrls))
> +		return errno;
> +
> +Once we don't need the request anymore, we can either recycle it for reuse with
> +:ref:`MEDIA_REQUEST_IOC_REINIT`...
> +
> +.. code-block:: c
> +
> +	if (ioctl(req_fd, MEDIA_REQUEST_IOC_REINIT))
> +		return errno;
> +
> +... or close its file descriptor to completely dispose of it.
> +
> +.. code-block:: c
> +
> +	close(req_fd);
> +
> +Example for a Simple Capture Device
> +-----------------------------------
> +
> +With a simple capture device, requests can be used to specify controls to apply
> +for a given CAPTURE buffer.
> +
> +.. code-block:: c
> +
> +	struct v4l2_buffer buf;
> +	struct v4l2_ext_controls ctrls;
> +	struct media_request_alloc alloc = { 0 };
> +	int req_fd;
> +	...
> +	if (ioctl(media_fd, MEDIA_IOC_REQUEST_ALLOC, &alloc))
> +		return errno;
> +	req_fd = alloc.fd;
> +	...
> +	ctrls.which = V4L2_CTRL_WHICH_REQUEST_VAL;
> +	ctrls.request_fd = req_fd;
> +	if (ioctl(camera_fd, VIDIOC_S_EXT_CTRLS, &ctrls))
> +		return errno;
> +	...
> +	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	buf.flags |= V4L2_BUF_FLAG_REQUEST_FD;
> +	buf.request_fd = req_fd;
> +	if (ioctl(camera_fd, VIDIOC_QBUF, &buf))
> +		return errno;
> +
> +Once the request is fully prepared, it can be queued to the driver:
> +
> +.. code-block:: c
> +
> +	if (ioctl(req_fd, MEDIA_REQUEST_IOC_QUEUE))
> +		return errno;
> +
> +User-space can then dequeue buffers, wait for the request completion, query
> +controls and recycle the request as in the M2M example above.
> diff --git a/Documentation/media/uapi/mediactl/request-func-close.rst b/Documentation/media/uapi/mediactl/request-func-close.rst
> new file mode 100644
> index 000000000000..2dd9958f9b0e
> --- /dev/null
> +++ b/Documentation/media/uapi/mediactl/request-func-close.rst
> @@ -0,0 +1,49 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +.. -*- coding: utf-8; mode: rst -*-
> +
> +.. _request-func-close:
> +
> +***************
> +request close()
> +***************
> +
> +Name
> +====
> +
> +request-close - Close a request file descriptor
> +
> +
> +Synopsis
> +========
> +
> +.. code-block:: c
> +
> +    #include <unistd.h>
> +
> +
> +.. c:function:: int close( int fd )
> +    :name: req-close
> +
> +Arguments
> +=========
> +
> +``fd``
> +    File descriptor returned by :ref:`MEDIA_IOC_REQUEST_ALLOC`.
> +
> +
> +Description
> +===========
> +
> +Closes the request file descriptor. Resources associated with the request
> +are freed once all file descriptors associated with the request are closed
> +and the driver has completed the request.
> +
> +
> +Return Value
> +============
> +
> +:ref:`close() <request-func-close>` returns 0 on success. On error, -1 is
> +returned, and ``errno`` is set appropriately. Possible error codes are:
> +
> +EBADF
> +    ``fd`` is not a valid open file descriptor.
> diff --git a/Documentation/media/uapi/mediactl/request-func-ioctl.rst b/Documentation/media/uapi/mediactl/request-func-ioctl.rst
> new file mode 100644
> index 000000000000..33cadfd6a90b
> --- /dev/null
> +++ b/Documentation/media/uapi/mediactl/request-func-ioctl.rst
> @@ -0,0 +1,68 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +.. -*- coding: utf-8; mode: rst -*-
> +
> +.. _request-func-ioctl:
> +
> +***************
> +request ioctl()
> +***************
> +
> +Name
> +====
> +
> +request-ioctl - Control a request file descriptor
> +
> +
> +Synopsis
> +========
> +
> +.. code-block:: c
> +
> +    #include <sys/ioctl.h>
> +
> +
> +.. c:function:: int ioctl( int fd, int cmd, void *argp )
> +    :name: req-ioctl
> +
> +Arguments
> +=========
> +
> +``fd``
> +    File descriptor returned by :ref:`MEDIA_IOC_REQUEST_ALLOC`.
> +
> +``cmd``
> +    The request ioctl command code as defined in the media.h header file, for
> +    example :ref:`MEDIA_REQUEST_IOC_QUEUE`.
> +
> +``argp``
> +    Pointer to a request-specific structure.
> +
> +
> +Description
> +===========
> +
> +The :ref:`ioctl() <request-func-ioctl>` function manipulates request
> +parameters. The argument ``fd`` must be an open file descriptor.
> +
> +The ioctl ``cmd`` code specifies the request function to be called. It
> +has encoded in it whether the argument is an input, output or read/write
> +parameter, and the size of the argument ``argp`` in bytes.
> +
> +Macros and structures definitions specifying request ioctl commands and
> +their parameters are located in the media.h header file. All request ioctl
> +commands, their respective function and parameters are specified in
> +:ref:`media-user-func`.
> +
> +
> +Return Value
> +============
> +
> +On success 0 is returned, on error -1 and the ``errno`` variable is set
> +appropriately. The generic error codes are described at the
> +:ref:`Generic Error Codes <gen-errors>` chapter.
> +
> +Command-specific error codes are listed in the individual command
> +descriptions.
> +
> +When an ioctl that takes an output or read/write parameter fails, the
> +parameter remains unmodified.
> diff --git a/Documentation/media/uapi/mediactl/request-func-poll.rst b/Documentation/media/uapi/mediactl/request-func-poll.rst
> new file mode 100644
> index 000000000000..206d8660d25a
> --- /dev/null
> +++ b/Documentation/media/uapi/mediactl/request-func-poll.rst
> @@ -0,0 +1,77 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +.. -*- coding: utf-8; mode: rst -*-

Same comments as before. Won't repeat myself about license and
that extra utf-8 coding line.

> +
> +.. _request-func-poll:
> +
> +**************
> +request poll()
> +**************
> +
> +Name
> +====
> +
> +request-poll - Wait for some event on a file descriptor
> +
> +
> +Synopsis
> +========
> +
> +.. code-block:: c
> +
> +    #include <sys/poll.h>
> +
> +
> +.. c:function:: int poll( struct pollfd *ufds, unsigned int nfds, int timeout )
> +   :name: request-poll
> +
> +Arguments
> +=========
> +
> +``ufds``
> +   List of FD events to be watched
> +
> +``nfds``
> +   Number of FD events at the \*ufds array
> +
> +``timeout``
> +   Timeout to wait for events
> +
> +
> +Description
> +===========
> +
> +With the :c:func:`poll() <request-func-poll>` function applications can wait
> +for a request to complete.
> +
> +On success :c:func:`poll() <request-func-poll>` returns the number of file
> +descriptors that have been selected (that is, file descriptors for which the
> +``revents`` field of the respective struct :c:type:`pollfd`
> +is non-zero). Request file descriptor set the ``POLLPRI`` flag in ``revents``
> +when the request was completed.  When the function times out it returns
> +a value of zero, on failure it returns -1 and the ``errno`` variable is
> +set appropriately.
> +
> +Attempting to poll for a request that is completed or not yet queued will
> +set the ``POLLERR`` flag in ``revents``.
> +
> +
> +Return Value
> +============
> +
> +On success, :c:func:`poll() <request-func-poll>` returns the number of
> +structures which have non-zero ``revents`` fields, or zero if the call
> +timed out. On error -1 is returned, and the ``errno`` variable is set
> +appropriately:
> +
> +``EBADF``
> +    One or more of the ``ufds`` members specify an invalid file
> +    descriptor.
> +
> +``EFAULT``
> +    ``ufds`` references an inaccessible memory area.
> +
> +``EINTR``
> +    The call was interrupted by a signal.
> +
> +``EINVAL``
> +    The ``nfds`` argument is greater than ``OPEN_MAX``.

That's wrong. OPEN_MAX is the maximum hard limit for root
users. The actual limit given by RLIMIT is usually 1/4th:

	$ulimit -n
	1024

The poll manpage wrote it as:

       EINVAL The nfds value exceeds the RLIMIT_NOFILE value.

Yet, RLIMIT_NOFILE requires description. Btw, the same wrong
text is also present at V4L2 and CEC docs.

> diff --git a/Documentation/media/uapi/v4l/buffer.rst b/Documentation/media/uapi/v4l/buffer.rst
> index e2c85ddc990b..dd0065a95ea0 100644
> --- a/Documentation/media/uapi/v4l/buffer.rst
> +++ b/Documentation/media/uapi/v4l/buffer.rst
> @@ -306,10 +306,15 @@ struct v4l2_buffer
>        - A place holder for future extensions. Drivers and applications
>  	must set this to 0.
>      * - __u32
> -      - ``reserved``
> +      - ``request_fd``
>        -
> -      - A place holder for future extensions. Drivers and applications
> -	must set this to 0.
> +      - The file descriptor of the request to queue the buffer to. If specified
> +        and flag ``V4L2_BUF_FLAG_REQUEST_FD`` is set, then the buffer will be
> +	queued to that request. This is set by the user when calling
> +	:ref:`ioctl VIDIOC_QBUF <VIDIOC_QBUF>` and ignored by other ioctls.
> +	If the device does not support requests, then ``EPERM`` will be returned.
> +	If requests are supported but an invalid request FD is given, then
> +	``ENOENT`` will be returned.
>  
>  
>  
> @@ -514,6 +519,11 @@ Buffer Flags
>  	streaming may continue as normal and the buffer may be reused
>  	normally. Drivers set this flag when the ``VIDIOC_DQBUF`` ioctl is
>  	called.
> +    * .. _`V4L2-BUF-FLAG-IN-REQUEST`:
> +
> +      - ``V4L2_BUF_FLAG_IN_REQUEST``
> +      - 0x00000080
> +      - This buffer is part of a request that hasn't been queued yet.
>      * .. _`V4L2-BUF-FLAG-KEYFRAME`:
>  
>        - ``V4L2_BUF_FLAG_KEYFRAME``
> @@ -589,6 +599,11 @@ Buffer Flags
>  	the format. Any Any subsequent call to the
>  	:ref:`VIDIOC_DQBUF <VIDIOC_QBUF>` ioctl will not block anymore,
>  	but return an ``EPIPE`` error code.
> +    * .. _`V4L2-BUF-FLAG-REQUEST-FD`:
> +
> +      - ``V4L2_BUF_FLAG_REQUEST_FD``
> +      - 0x00800000
> +      - The ``request_fd`` field contains a valid file descriptor.
>      * .. _`V4L2-BUF-FLAG-TIMESTAMP-MASK`:
>  
>        - ``V4L2_BUF_FLAG_TIMESTAMP_MASK``
> diff --git a/Documentation/media/uapi/v4l/vidioc-g-ext-ctrls.rst b/Documentation/media/uapi/v4l/vidioc-g-ext-ctrls.rst
> index 2011c2b2ee67..c592074be273 100644
> --- a/Documentation/media/uapi/v4l/vidioc-g-ext-ctrls.rst
> +++ b/Documentation/media/uapi/v4l/vidioc-g-ext-ctrls.rst
> @@ -95,6 +95,26 @@ appropriate. In the first case the new value is set in struct
>  is inappropriate (e.g. the given menu index is not supported by the menu
>  control), then this will also result in an ``EINVAL`` error code error.
>  
> +If ``request_fd`` is set to a not-yet-queued :ref:`request <media-request-api>`
> +file descriptor and ``which`` is set to ``V4L2_CTRL_WHICH_REQUEST_VAL``,
> +then the controls are not applied immediately when calling
> +:ref:`VIDIOC_S_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>`, but instead are applied by
> +the driver for the buffer associated with the same request.
> +If the device does not support requests, then ``EPERM`` will be returned.
> +If requests are supported but an invalid request FD is given, then
> +``ENOENT`` will be returned.
> +
> +An attempt to call :ref:`VIDIOC_S_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` for a
> +request that has already been queued will result in an ``EBUSY`` error.
> +
> +If ``request_fd`` is specified and ``which`` is set to ``V4L2_CTRL_WHICH_REQUEST_VAL``
> +during a call to :ref:`VIDIOC_G_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>`, then the
> +returned values will be the values currently set for the request (or the
> +hardware value if none is set) if the request has not yet been queued, or the
> +values of the controls at the time of request completion if it has already
> +completed. Attempting to get controls while the request has been queued but
> +not yet completed will result in an ``EBUSY`` error.
> +
>  The driver will only set/get these controls if all control values are
>  correct. This prevents the situation where only some of the controls
>  were set/get. Only low-level errors (e. g. a failed i2c command) can
> @@ -110,15 +130,13 @@ still cause this situation.
>  .. flat-table:: struct v4l2_ext_control
>      :header-rows:  0
>      :stub-columns: 0
> -    :widths:       1 1 1 2
> +    :widths:       1 1 3

This is wrong: you can't change widths without changing the preceeding
.. tabularcolumns tag.

You probably didn't test PDF generation for this table.

Also, the change is this table doesn't belong to this patch. It is
a (doubtful) optimization at the table, not related to requests API.

>  
>      * - __u32
>        - ``id``
> -      -
>        - Identifies the control, set by the application.
>      * - __u32
>        - ``size``
> -      -
>        - The total size in bytes of the payload of this control. This is
>  	normally 0, but for pointer controls this should be set to the
>  	size of the memory containing the payload, or that will receive
> @@ -135,51 +153,43 @@ still cause this situation.
>  	   *length* of the string may well be much smaller.
>      * - __u32
>        - ``reserved2``\ [1]
> -      -
>        - Reserved for future extensions. Drivers and applications must set
>  	the array to zero.
> -    * - union
> +    * - union {

Adding { and } at the end sounds ok...

>        - (anonymous)
> -    * -
> -      - __s32
> +    * - __s32
>        - ``value``
>        - New value or current value. Valid if this control is not of type
>  	``V4L2_CTRL_TYPE_INTEGER64`` and ``V4L2_CTRL_FLAG_HAS_PAYLOAD`` is
>  	not set.
> -    * -
> -      - __s64
> +    * - __s64
>        - ``value64``
>        - New value or current value. Valid if this control is of type
>  	``V4L2_CTRL_TYPE_INTEGER64`` and ``V4L2_CTRL_FLAG_HAS_PAYLOAD`` is
>  	not set.
> -    * -
> -      - char *
> +    * - char *
>        - ``string``
>        - A pointer to a string. Valid if this control is of type
>  	``V4L2_CTRL_TYPE_STRING``.
> -    * -
> -      - __u8 *
> +    * - __u8 *
>        - ``p_u8``
>        - A pointer to a matrix control of unsigned 8-bit values. Valid if
>  	this control is of type ``V4L2_CTRL_TYPE_U8``.
> -    * -
> -      - __u16 *
> +    * - __u16 *
>        - ``p_u16``
>        - A pointer to a matrix control of unsigned 16-bit values. Valid if
>  	this control is of type ``V4L2_CTRL_TYPE_U16``.
> -    * -
> -      - __u32 *
> +    * - __u32 *
>        - ``p_u32``
>        - A pointer to a matrix control of unsigned 32-bit values. Valid if
>  	this control is of type ``V4L2_CTRL_TYPE_U32``.
> -    * -
> -      - void *
> +    * - void *
>        - ``ptr``
>        - A pointer to a compound type which can be an N-dimensional array
>  	and/or a compound type (the control's type is >=
>  	``V4L2_CTRL_COMPOUND_TYPES``). Valid if
>  	``V4L2_CTRL_FLAG_HAS_PAYLOAD`` is set for this control.
> -
> +    * - }

... however, removing the extra cell is not, because it will break the
indent inside the union's elements. The best way to keep them indented
is to use a separate cell (at least on DocBook). 

You could come with some other solution that would add a fixed amount
of spaces for all the elements inside the union, but I guess the
easiest way to do that is by having a separate column.

>  
>  .. tabularcolumns:: |p{4.0cm}|p{2.2cm}|p{2.1cm}|p{8.2cm}|
>  
> @@ -190,12 +200,11 @@ still cause this situation.
>  .. flat-table:: struct v4l2_ext_controls
>      :header-rows:  0
>      :stub-columns: 0
> -    :widths:       1 1 2 1
> +    :widths:       1 1 3

Same comments I made for the past table apply here as well.

>  
> -    * - union
> +    * - union {
>        - (anonymous)
> -    * -
> -      - __u32
> +    * - __u32
>        - ``ctrl_class``
>        - The control class to which all controls belong, see
>  	:ref:`ctrl-class`. Drivers that use a kernel framework for
> @@ -204,18 +213,21 @@ still cause this situation.
>  	support this can be tested by setting ``ctrl_class`` to 0 and
>  	calling :ref:`VIDIOC_TRY_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` with a ``count`` of 0. If that
>  	succeeds, then the driver supports this feature.
> -    * -
> -      - __u32
> +    * - __u32
>        - ``which``
>        - Which value of the control to get/set/try.
>  	``V4L2_CTRL_WHICH_CUR_VAL`` will return the current value of the
> -	control and ``V4L2_CTRL_WHICH_DEF_VAL`` will return the default
> -	value of the control.
> +	control, ``V4L2_CTRL_WHICH_DEF_VAL`` will return the default
> +	value of the control and ``V4L2_CTRL_WHICH_REQUEST_VAL`` indicates that
> +	these controls have to be retrieved from a request or tried/set for
> +	a request. In the latter case the ``request_fd`` field contains the
> +	file descriptor of the request that should be used. If the device
> +	does not support requests, then ``EPERM`` will be returned.
>  
>  	.. note::
>  
> -	   You can only get the default value of the control,
> -	   you cannot set or try it.
> +	   When using ``V4L2_CTRL_WHICH_DEF_VAL`` note that You can only
> +	   get the default value of the control, you cannot set or try it.
>  
>  	For backwards compatibility you can also use a control class here
>  	(see :ref:`ctrl-class`). In that case all controls have to
> @@ -226,6 +238,7 @@ still cause this situation.
>  	by setting ctrl_class to ``V4L2_CTRL_WHICH_CUR_VAL`` and calling
>  	VIDIOC_TRY_EXT_CTRLS with a count of 0. If that fails, then the
>  	driver does not support ``V4L2_CTRL_WHICH_CUR_VAL``.
> +    * - }
>      * - __u32
>        - ``count``
>        - The number of controls in the controls array. May also be zero.
> @@ -272,8 +285,15 @@ still cause this situation.
>  	then you can call :ref:`VIDIOC_TRY_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` to try to discover the
>  	actual control that failed the validation step. Unfortunately,
>  	there is no ``TRY`` equivalent for :ref:`VIDIOC_G_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>`.
> +    * - __s32
> +      - ``request_fd``
> +      - File descriptor of the request to be used by this operation. Only
> +	valid if ``which`` is set to ``V4L2_CTRL_WHICH_REQUEST_VAL``.
> +	If the device does not support requests, then ``EPERM`` will be returned.
> +	If requests are supported but an invalid request FD is given, then
> +	``ENOENT`` will be returned.
>      * - __u32
> -      - ``reserved``\ [2]
> +      - ``reserved``\ [1]
>        - Reserved for future extensions.
>  
>  	Drivers and applications must set the array to zero.
> @@ -362,7 +382,9 @@ ERANGE
>  EBUSY
>      The control is temporarily not changeable, possibly because another
>      applications took over control of the device function this control
> -    belongs to.
> +    belongs to, or (if the ``which`` field was set to
> +    ``V4L2_CTRL_WHICH_REQUEST_VAL``) the request was queued but not yet
> +    completed.
>  
>  ENOSPC
>      The space reserved for the control's payload is insufficient. The
> @@ -372,3 +394,11 @@ ENOSPC
>  EACCES
>      Attempt to try or set a read-only control or to get a write-only
>      control.
> +
> +EPERM
> +    The ``which`` field was set to ``V4L2_CTRL_WHICH_REQUEST_VAL`` but the
> +    device does not support requests.
> +
> +ENOENT
> +    The ``which`` field was set to ``V4L2_CTRL_WHICH_REQUEST_VAL`` but the
> +    the given ``request_fd`` was invalid.
> diff --git a/Documentation/media/uapi/v4l/vidioc-qbuf.rst b/Documentation/media/uapi/v4l/vidioc-qbuf.rst
> index 9e448a4aa3aa..0e415f2551b2 100644
> --- a/Documentation/media/uapi/v4l/vidioc-qbuf.rst
> +++ b/Documentation/media/uapi/v4l/vidioc-qbuf.rst
> @@ -65,7 +65,7 @@ To enqueue a :ref:`memory mapped <mmap>` buffer applications set the
>  with a pointer to this structure the driver sets the
>  ``V4L2_BUF_FLAG_MAPPED`` and ``V4L2_BUF_FLAG_QUEUED`` flags and clears
>  the ``V4L2_BUF_FLAG_DONE`` flag in the ``flags`` field, or it returns an
> -EINVAL error code.
> +``EINVAL`` error code.

Side note: we should likely do a similar replacement on all other places
inside the media uAPI docs.

>  
>  To enqueue a :ref:`user pointer <userp>` buffer applications set the
>  ``memory`` field to ``V4L2_MEMORY_USERPTR``, the ``m.userptr`` field to
> @@ -98,6 +98,25 @@ dequeued, until the :ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` or
>  :ref:`VIDIOC_REQBUFS` ioctl is called, or until the
>  device is closed.
>  
> +The ``request_fd`` field can be used with the ``VIDIOC_QBUF`` ioctl to specify

Please prefer using :ref: for QBUF too, e. g.: 
	:ref:`ioctl VIDIOC_QBUF <VIDIOC_QBUF>`

Please double-check this at the docs, as this is the second case I
got with the similar issue.

> +the file descriptor of a :ref:`request <media-request-api>`, if requests are
> +in use. Setting it means that the buffer will not be passed to the driver
> +until the request itself is queued. Also, the driver will apply any
> +settings associated with the request for this buffer. This field will
> +be ignored unless the ``V4L2_BUF_FLAG_REQUEST_FD`` flag is set.
> +If the device does not support requests, then ``EPERM`` will be returned.
> +If requests are supported but an invalid request FD is given, then
> +``ENOENT`` will be returned.
> +
> +.. caution::
> +   It is not allowed to mix queuing requests with queuing buffers directly.
> +   ``EPERM`` will be returned if the first buffer was queued directly and
> +   then the application tries to queue a request, or vice versa.
> +
> +   For :ref:`memory-to-memory devices <codec>` you can specify the
> +   ``request_fd`` only for output buffers, not for capture buffers. Attempting
> +   to specify this for a capture buffer will result in an ``EPERM`` error.
> +
>  Applications call the ``VIDIOC_DQBUF`` ioctl to dequeue a filled
>  (capturing) or displayed (output) buffer from the driver's outgoing
>  queue. They just set the ``type``, ``memory`` and ``reserved`` fields of
> @@ -153,3 +172,14 @@ EPIPE
>      ``VIDIOC_DQBUF`` returns this on an empty capture queue for mem2mem
>      codecs if a buffer with the ``V4L2_BUF_FLAG_LAST`` was already
>      dequeued and no new buffers are expected to become available.
> +
> +EPERM
> +    The ``V4L2_BUF_FLAG_REQUEST_FD`` flag was set but the device does not
> +    support requests. Or the first buffer was queued via a request, but
> +    the application now tries to queue it directly, or vice versa (it is
> +    not permitted to mix the two APIs). Or an attempt is made to queue a
> +    CAPTURE buffer to a request for a :ref:`memory-to-memory device <codec>`.
> +
> +ENOENT
> +    The ``V4L2_BUF_FLAG_REQUEST_FD`` flag was set but the the given
> +    ``request_fd`` was invalid.
> diff --git a/Documentation/media/videodev2.h.rst.exceptions b/Documentation/media/videodev2.h.rst.exceptions
> index ca9f0edc579e..99256a2c003e 100644
> --- a/Documentation/media/videodev2.h.rst.exceptions
> +++ b/Documentation/media/videodev2.h.rst.exceptions
> @@ -514,6 +514,7 @@ ignore define V4L2_CTRL_DRIVER_PRIV
>  ignore define V4L2_CTRL_MAX_DIMS
>  ignore define V4L2_CTRL_WHICH_CUR_VAL
>  ignore define V4L2_CTRL_WHICH_DEF_VAL
> +ignore define V4L2_CTRL_WHICH_REQUEST_VAL
>  ignore define V4L2_OUT_CAP_CUSTOM_TIMINGS
>  ignore define V4L2_CID_MAX_CTRLS
>  



Thanks,
Mauro

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

* Re: [PATCHv17 02/34] uapi/linux/media.h: add request API
  2018-08-04 12:44 ` [PATCHv17 02/34] uapi/linux/media.h: add " Hans Verkuil
@ 2018-08-09 17:53   ` Mauro Carvalho Chehab
  2018-08-10  7:21     ` Hans Verkuil
  0 siblings, 1 reply; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-09 17:53 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:44:54 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Define the public request API.
> 
> This adds the new MEDIA_IOC_REQUEST_ALLOC ioctl to allocate a request
> and two ioctls that operate on a request in order to queue the
> contents of the request to the driver and to re-initialize the
> request.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  include/uapi/linux/media.h | 12 ++++++++++++
>  1 file changed, 12 insertions(+)
> 
> diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h
> index 36f76e777ef9..cf77f00a0f2d 100644
> --- a/include/uapi/linux/media.h
> +++ b/include/uapi/linux/media.h
> @@ -364,11 +364,23 @@ struct media_v2_topology {
>  
>  /* ioctls */
>  
> +struct __attribute__ ((packed)) media_request_alloc {
> +	__s32 fd;
> +};
> +
>  #define MEDIA_IOC_DEVICE_INFO	_IOWR('|', 0x00, struct media_device_info)
>  #define MEDIA_IOC_ENUM_ENTITIES	_IOWR('|', 0x01, struct media_entity_desc)
>  #define MEDIA_IOC_ENUM_LINKS	_IOWR('|', 0x02, struct media_links_enum)
>  #define MEDIA_IOC_SETUP_LINK	_IOWR('|', 0x03, struct media_link_desc)
>  #define MEDIA_IOC_G_TOPOLOGY	_IOWR('|', 0x04, struct media_v2_topology)
> +#define MEDIA_IOC_REQUEST_ALLOC	_IOWR('|', 0x05, struct media_request_alloc)

Same comment as in patch 1: keep it simpler: just pass a s32 * as the
argument for this ioctl.

> +
> +/*
> + * These ioctls are called on the request file descriptor as returned
> + * by MEDIA_IOC_REQUEST_ALLOC.
> + */
> +#define MEDIA_REQUEST_IOC_QUEUE		_IO('|',  0x80)
> +#define MEDIA_REQUEST_IOC_REINIT	_IO('|',  0x81)
>  
>  #ifndef __KERNEL__
>  



Thanks,
Mauro

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

* Re: [PATCHv17 03/34] media-request: implement media requests
  2018-08-04 12:44 ` [PATCHv17 03/34] media-request: implement media requests Hans Verkuil
@ 2018-08-09 18:37   ` Mauro Carvalho Chehab
  2018-08-10  7:28     ` Hans Verkuil
  2018-08-14 13:33   ` Hans Verkuil
  1 sibling, 1 reply; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-09 18:37 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil, Sakari Ailus

Em Sat,  4 Aug 2018 14:44:55 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Add initial media request support:
> 
> 1) Add MEDIA_IOC_REQUEST_ALLOC ioctl support to media-device.c
> 2) Add struct media_request to store request objects.
> 3) Add struct media_request_object to represent a request object.
> 4) Add MEDIA_REQUEST_IOC_QUEUE/REINIT ioctl support.
> 
> Basic lifecycle: the application allocates a request, adds
> objects to it, queues the request, polls until it is completed
> and can then read the final values of the objects at the time
> of completion. When it closes the file descriptor the request
> memory will be freed (actually, when the last user of that request
> releases the request).
> 
> Drivers will bind an object to a request (the 'adds objects to it'
> phase), when MEDIA_REQUEST_IOC_QUEUE is called the request is
> validated (req_validate op), then queued (the req_queue op).
> 
> When done with an object it can either be unbound from the request
> (e.g. when the driver has finished with a vb2 buffer) or marked as
> completed (e.g. for controls associated with a buffer). When all
> objects in the request are completed (or unbound), then the request
> fd will signal an exception (poll).
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> Co-developed-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> Co-developed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Co-developed-by: Alexandre Courbot <acourbot@chromium.org>
> ---
>  drivers/media/Makefile        |   3 +-
>  drivers/media/media-device.c  |  14 ++
>  drivers/media/media-request.c | 423 ++++++++++++++++++++++++++++++++++
>  include/media/media-device.h  |  21 ++
>  include/media/media-request.h | 327 ++++++++++++++++++++++++++
>  5 files changed, 787 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/media/media-request.c
>  create mode 100644 include/media/media-request.h
> 
> diff --git a/drivers/media/Makefile b/drivers/media/Makefile
> index 594b462ddf0e..985d35ec6b29 100644
> --- a/drivers/media/Makefile
> +++ b/drivers/media/Makefile
> @@ -3,7 +3,8 @@
>  # Makefile for the kernel multimedia device drivers.
>  #
>  
> -media-objs	:= media-device.o media-devnode.o media-entity.o
> +media-objs	:= media-device.o media-devnode.o media-entity.o \
> +		   media-request.o
>  
>  #
>  # I2C drivers should come before other drivers, otherwise they'll fail
> diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
> index fcdf3d5dc4b6..4b9a8de05562 100644
> --- a/drivers/media/media-device.c
> +++ b/drivers/media/media-device.c
> @@ -30,6 +30,7 @@
>  #include <media/media-device.h>
>  #include <media/media-devnode.h>
>  #include <media/media-entity.h>
> +#include <media/media-request.h>
>  
>  #ifdef CONFIG_MEDIA_CONTROLLER
>  
> @@ -377,6 +378,15 @@ static long media_device_get_topology(struct media_device *mdev, void *arg)
>  	return ret;
>  }
>  
> +static long media_device_request_alloc(struct media_device *mdev,
> +				       struct media_request_alloc *alloc)
> +{
> +	if (!mdev->ops || !mdev->ops->req_validate || !mdev->ops->req_queue)
> +		return -ENOTTY;
> +
> +	return media_request_alloc(mdev, alloc);
> +}
> +
>  static long copy_arg_from_user(void *karg, void __user *uarg, unsigned int cmd)
>  {
>  	/* All media IOCTLs are _IOWR() */
> @@ -425,6 +435,7 @@ static const struct media_ioctl_info ioctl_info[] = {
>  	MEDIA_IOC(ENUM_LINKS, media_device_enum_links, MEDIA_IOC_FL_GRAPH_MUTEX),
>  	MEDIA_IOC(SETUP_LINK, media_device_setup_link, MEDIA_IOC_FL_GRAPH_MUTEX),
>  	MEDIA_IOC(G_TOPOLOGY, media_device_get_topology, MEDIA_IOC_FL_GRAPH_MUTEX),
> +	MEDIA_IOC(REQUEST_ALLOC, media_device_request_alloc, 0),
>  };
>  
>  static long media_device_ioctl(struct file *filp, unsigned int cmd,
> @@ -697,6 +708,8 @@ void media_device_init(struct media_device *mdev)
>  	INIT_LIST_HEAD(&mdev->pads);
>  	INIT_LIST_HEAD(&mdev->links);
>  	INIT_LIST_HEAD(&mdev->entity_notify);
> +
> +	mutex_init(&mdev->req_queue_mutex);
>  	mutex_init(&mdev->graph_mutex);
>  	ida_init(&mdev->entity_internal_idx);
>  
> @@ -710,6 +723,7 @@ void media_device_cleanup(struct media_device *mdev)
>  	mdev->entity_internal_idx_max = 0;
>  	media_graph_walk_cleanup(&mdev->pm_count_walk);
>  	mutex_destroy(&mdev->graph_mutex);
> +	mutex_destroy(&mdev->req_queue_mutex);
>  }
>  EXPORT_SYMBOL_GPL(media_device_cleanup);
>  
> diff --git a/drivers/media/media-request.c b/drivers/media/media-request.c
> new file mode 100644
> index 000000000000..253068f51a1f
> --- /dev/null
> +++ b/drivers/media/media-request.c
> @@ -0,0 +1,423 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Media device request objects
> + *
> + * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
> + * Copyright (C) 2018 Intel Corporation
> + * Copyright (C) 2018 Google, Inc.
> + *
> + * Author: Hans Verkuil <hans.verkuil@cisco.com>
> + * Author: Sakari Ailus <sakari.ailus@linux.intel.com>
> + */
> +
> +#include <linux/anon_inodes.h>
> +#include <linux/file.h>
> +#include <linux/refcount.h>
> +
> +#include <media/media-device.h>
> +#include <media/media-request.h>
> +
> +static const char * const request_state[] = {
> +	[MEDIA_REQUEST_STATE_IDLE]	 = "idle",
> +	[MEDIA_REQUEST_STATE_VALIDATING] = "validating",
> +	[MEDIA_REQUEST_STATE_QUEUED]	 = "queued",
> +	[MEDIA_REQUEST_STATE_COMPLETE]	 = "complete",
> +	[MEDIA_REQUEST_STATE_CLEANING]	 = "cleaning",
> +	[MEDIA_REQUEST_STATE_UPDATING]	 = "updating",
> +};
> +
> +static const char *
> +media_request_state_str(enum media_request_state state)
> +{
> +	BUILD_BUG_ON(ARRAY_SIZE(request_state) != NR_OF_MEDIA_REQUEST_STATE);
> +
> +	if (WARN_ON(state >= ARRAY_SIZE(request_state)))
> +		return "invalid";
> +	return request_state[state];
> +}
> +
> +static void media_request_clean(struct media_request *req)
> +{
> +	struct media_request_object *obj, *obj_safe;
> +
> +	/* Just a sanity check. No other code path is allowed to change this. */
> +	WARN_ON(req->state != MEDIA_REQUEST_STATE_CLEANING);
> +	WARN_ON(req->updating_count);
> +
> +	list_for_each_entry_safe(obj, obj_safe, &req->objects, list) {
> +		media_request_object_unbind(obj);
> +		media_request_object_put(obj);
> +	}
> +
> +	req->updating_count = 0;
> +	WARN_ON(req->num_incomplete_objects);
> +	req->num_incomplete_objects = 0;
> +	wake_up_interruptible_all(&req->poll_wait);
> +}
> +
> +static void media_request_release(struct kref *kref)
> +{
> +	struct media_request *req =
> +		container_of(kref, struct media_request, kref);
> +	struct media_device *mdev = req->mdev;
> +
> +	dev_dbg(mdev->dev, "request: release %s\n", req->debug_str);
> +
> +	/* No other users, no need for a spinlock */
> +	req->state = MEDIA_REQUEST_STATE_CLEANING;
> +
> +	media_request_clean(req);
> +
> +	if (mdev->ops->req_free)
> +		mdev->ops->req_free(req);
> +	else
> +		kfree(req);
> +}
> +
> +void media_request_put(struct media_request *req)
> +{
> +	kref_put(&req->kref, media_request_release);
> +}
> +EXPORT_SYMBOL_GPL(media_request_put);
> +
> +static int media_request_close(struct inode *inode, struct file *filp)
> +{
> +	struct media_request *req = filp->private_data;
> +
> +	media_request_put(req);
> +	return 0;
> +}
> +
> +static unsigned int media_request_poll(struct file *filp,
> +				       struct poll_table_struct *wait)
> +{
> +	struct media_request *req = filp->private_data;
> +	unsigned long flags;
> +	unsigned int ret = 0;
> +
> +	if (!(poll_requested_events(wait) & POLLPRI))
> +		return 0;
> +
> +	spin_lock_irqsave(&req->lock, flags);
> +	if (req->state == MEDIA_REQUEST_STATE_COMPLETE) {
> +		ret = POLLPRI;
> +		goto unlock;
> +	}
> +	if (req->state != MEDIA_REQUEST_STATE_QUEUED) {
> +		ret = POLLERR;
> +		goto unlock;
> +	}
> +
> +	poll_wait(filp, &req->poll_wait, wait);
> +
> +unlock:
> +	spin_unlock_irqrestore(&req->lock, flags);
> +	return ret;
> +}
> +
> +static long media_request_ioctl_queue(struct media_request *req)
> +{
> +	struct media_device *mdev = req->mdev;
> +	enum media_request_state state;
> +	unsigned long flags;
> +	int ret;
> +
> +	dev_dbg(mdev->dev, "request: queue %s\n", req->debug_str);
> +
> +	/*
> +	 * Ensure the request that is validated will be the one that gets queued
> +	 * next by serialising the queueing process. This mutex is also used
> +	 * to serialize with canceling a vb2 queue and with setting values such
> +	 * as controls in a request.
> +	 */
> +	mutex_lock(&mdev->req_queue_mutex);
> +
> +	media_request_get(req);
> +
> +	spin_lock_irqsave(&req->lock, flags);
> +	if (req->state == MEDIA_REQUEST_STATE_IDLE)
> +		req->state = MEDIA_REQUEST_STATE_VALIDATING;
> +	state = req->state;
> +	spin_unlock_irqrestore(&req->lock, flags);
> +	if (state != MEDIA_REQUEST_STATE_VALIDATING) {
> +		dev_dbg(mdev->dev,
> +			"request: unable to queue %s, request in state %s\n",
> +			req->debug_str, media_request_state_str(state));
> +		mutex_unlock(&mdev->req_queue_mutex);
> +		return -EBUSY;
> +	}
> +
> +	ret = mdev->ops->req_validate(req);
> +
> +	/*
> +	 * If the req_validate was successful, then we mark the state as QUEUED
> +	 * and call req_queue. The reason we set the state first is that this
> +	 * allows req_queue to unbind or complete the queued objects in case
> +	 * they are immediately 'consumed'. State changes from QUEUED to another
> +	 * state can only happen if either the driver changes the state or if
> +	 * the user cancels the vb2 queue. The driver can only change the state
> +	 * after each object is queued through the req_queue op (and note that
> +	 * that op cannot fail), so setting the state to QUEUED up front is
> +	 * safe.
> +	 *
> +	 * The other reason for changing the state is if the vb2 queue is
> +	 * canceled, and that uses the req_queue_mutex which is still locked
> +	 * while req_queue is called, so that's safe as well.
> +	 */
> +	spin_lock_irqsave(&req->lock, flags);
> +	req->state = ret ? MEDIA_REQUEST_STATE_IDLE
> +			 : MEDIA_REQUEST_STATE_QUEUED;
> +	spin_unlock_irqrestore(&req->lock, flags);
> +
> +	if (!ret)
> +		mdev->ops->req_queue(req);
> +
> +	mutex_unlock(&mdev->req_queue_mutex);
> +
> +	if (ret) {
> +		dev_dbg(mdev->dev, "request: can't queue %s (%d)\n",
> +			req->debug_str, ret);
> +		media_request_put(req);
> +	}
> +
> +	return ret;
> +}
> +
> +static long media_request_ioctl_reinit(struct media_request *req)
> +{
> +	struct media_device *mdev = req->mdev;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&req->lock, flags);
> +	if (req->state != MEDIA_REQUEST_STATE_IDLE &&
> +	    req->state != MEDIA_REQUEST_STATE_COMPLETE) {
> +		dev_dbg(mdev->dev,
> +			"request: %s not in idle or complete state, cannot reinit\n",
> +			req->debug_str);
> +		spin_unlock_irqrestore(&req->lock, flags);
> +		return -EBUSY;
> +	}
> +	req->state = MEDIA_REQUEST_STATE_CLEANING;
> +	spin_unlock_irqrestore(&req->lock, flags);
> +
> +	media_request_clean(req);
> +
> +	spin_lock_irqsave(&req->lock, flags);
> +	req->state = MEDIA_REQUEST_STATE_IDLE;
> +	spin_unlock_irqrestore(&req->lock, flags);
> +
> +	return 0;
> +}
> +
> +static long media_request_ioctl(struct file *filp, unsigned int cmd,
> +				unsigned long arg)
> +{
> +	struct media_request *req = filp->private_data;
> +
> +	switch (cmd) {
> +	case MEDIA_REQUEST_IOC_QUEUE:
> +		return media_request_ioctl_queue(req);
> +	case MEDIA_REQUEST_IOC_REINIT:
> +		return media_request_ioctl_reinit(req);
> +	default:
> +		return -ENOIOCTLCMD;
> +	}
> +}
> +
> +static const struct file_operations request_fops = {
> +	.owner = THIS_MODULE,
> +	.poll = media_request_poll,
> +	.unlocked_ioctl = media_request_ioctl,
> +	.release = media_request_close,
> +};
> +
> +int media_request_alloc(struct media_device *mdev,
> +			struct media_request_alloc *alloc)
> +{
> +	struct media_request *req;
> +	struct file *filp;
> +	char comm[TASK_COMM_LEN];
> +	int fd;
> +	int ret;
> +
> +	/* Either both are NULL or both are non-NULL */
> +	if (WARN_ON(!mdev->ops->req_alloc ^ !mdev->ops->req_free))
> +		return -ENOMEM;
> +
> +	fd = get_unused_fd_flags(O_CLOEXEC);
> +	if (fd < 0)
> +		return fd;
> +
> +	filp = anon_inode_getfile("request", &request_fops, NULL, O_CLOEXEC);
> +	if (IS_ERR(filp)) {
> +		ret = PTR_ERR(filp);
> +		goto err_put_fd;
> +	}
> +
> +	if (mdev->ops->req_alloc)
> +		req = mdev->ops->req_alloc(mdev);
> +	else
> +		req = kzalloc(sizeof(*req), GFP_KERNEL);
> +	if (!req) {
> +		ret = -ENOMEM;
> +		goto err_fput;
> +	}
> +
> +	filp->private_data = req;
> +	req->mdev = mdev;
> +	req->state = MEDIA_REQUEST_STATE_IDLE;
> +	req->num_incomplete_objects = 0;
> +	kref_init(&req->kref);
> +	INIT_LIST_HEAD(&req->objects);
> +	spin_lock_init(&req->lock);
> +	init_waitqueue_head(&req->poll_wait);
> +	req->updating_count = 0;
> +
> +	alloc->fd = fd;
> +
> +	get_task_comm(comm, current);
> +	snprintf(req->debug_str, sizeof(req->debug_str), "%s:%d",
> +		 comm, fd);

I'm pretty sure we've discussed about get_task_comm(). I don't think 
we should use it, as the task with queues can be different than
the one with dequeues. Instead, just give an unique ID for each
request. That will allow tracking it in a better way, no matter how
the userspace app is encoded.

Also, for dynamic debugs, the task ID can easily be obtained by
passing a parameter to it, with +t:

	echo "file media_request.c +t" > /sys/kernel/debug/dynamic_debug/control

or, at the boot time with:
	dyndbg="file media_request.c +t"

So, Kernel shouldn't be bothered by having such hacks hardcoded
at the code.

> +	dev_dbg(mdev->dev, "request: allocated %s\n", req->debug_str);
> +
> +	fd_install(fd, filp);
> +
> +	return 0;
> +
> +err_fput:
> +	fput(filp);
> +
> +err_put_fd:
> +	put_unused_fd(fd);
> +
> +	return ret;
> +}
> +
> +static void media_request_object_release(struct kref *kref)
> +{
> +	struct media_request_object *obj =
> +		container_of(kref, struct media_request_object, kref);
> +	struct media_request *req = obj->req;
> +
> +	if (WARN_ON(req))
> +		media_request_object_unbind(obj);
> +	obj->ops->release(obj);
> +}
> +
> +void media_request_object_put(struct media_request_object *obj)
> +{
> +	kref_put(&obj->kref, media_request_object_release);
> +}
> +EXPORT_SYMBOL_GPL(media_request_object_put);
> +
> +void media_request_object_init(struct media_request_object *obj)
> +{
> +	obj->ops = NULL;
> +	obj->req = NULL;
> +	obj->priv = NULL;
> +	obj->completed = false;
> +	INIT_LIST_HEAD(&obj->list);
> +	kref_init(&obj->kref);
> +}
> +EXPORT_SYMBOL_GPL(media_request_object_init);
> +
> +int media_request_object_bind(struct media_request *req,
> +			      const struct media_request_object_ops *ops,
> +			      void *priv,
> +			      struct media_request_object *obj)
> +{
> +	unsigned long flags;
> +	int ret = -EBUSY;
> +
> +	if (WARN_ON(!ops->release))
> +		return -EPERM;
> +
> +	spin_lock_irqsave(&req->lock, flags);
> +
> +	if (WARN_ON(req->state != MEDIA_REQUEST_STATE_UPDATING))
> +		goto unlock;
> +
> +	obj->req = req;
> +	obj->ops = ops;
> +	obj->priv = priv;
> +
> +	list_add_tail(&obj->list, &req->objects);
> +	req->num_incomplete_objects++;
> +	ret = 0;
> +
> +unlock:
> +	spin_unlock_irqrestore(&req->lock, flags);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(media_request_object_bind);
> +
> +void media_request_object_unbind(struct media_request_object *obj)
> +{
> +	struct media_request *req = obj->req;
> +	unsigned long flags;
> +	bool completed = false;
> +
> +	if (WARN_ON(!req))
> +		return;
> +
> +	spin_lock_irqsave(&req->lock, flags);
> +	list_del(&obj->list);
> +	obj->req = NULL;
> +
> +	if (req->state == MEDIA_REQUEST_STATE_COMPLETE)
> +		goto unlock;
> +
> +	if (WARN_ON(req->state == MEDIA_REQUEST_STATE_VALIDATING))
> +		goto unlock;
> +
> +	if (req->state == MEDIA_REQUEST_STATE_CLEANING) {
> +		if (!obj->completed)
> +			req->num_incomplete_objects--;
> +		goto unlock;
> +	}
> +
> +	if (WARN_ON(!req->num_incomplete_objects))
> +		goto unlock;
> +
> +	req->num_incomplete_objects--;
> +	if (req->state == MEDIA_REQUEST_STATE_QUEUED &&
> +	    !req->num_incomplete_objects) {
> +		req->state = MEDIA_REQUEST_STATE_COMPLETE;
> +		completed = true;
> +		wake_up_interruptible_all(&req->poll_wait);
> +	}
> +
> +unlock:
> +	spin_unlock_irqrestore(&req->lock, flags);
> +	if (obj->ops->unbind)
> +		obj->ops->unbind(obj);
> +	if (completed)
> +		media_request_put(req);
> +}
> +EXPORT_SYMBOL_GPL(media_request_object_unbind);
> +
> +void media_request_object_complete(struct media_request_object *obj)
> +{
> +	struct media_request *req = obj->req;
> +	unsigned long flags;
> +	bool completed = false;
> +
> +	spin_lock_irqsave(&req->lock, flags);
> +	if (obj->completed)
> +		goto unlock;
> +	obj->completed = true;
> +	if (WARN_ON(!req->num_incomplete_objects) ||
> +	    WARN_ON(req->state != MEDIA_REQUEST_STATE_QUEUED))
> +		goto unlock;
> +
> +	if (!--req->num_incomplete_objects) {
> +		req->state = MEDIA_REQUEST_STATE_COMPLETE;
> +		wake_up_interruptible_all(&req->poll_wait);
> +		completed = true;
> +	}
> +unlock:
> +	spin_unlock_irqrestore(&req->lock, flags);
> +	if (completed)
> +		media_request_put(req);
> +}
> +EXPORT_SYMBOL_GPL(media_request_object_complete);
> diff --git a/include/media/media-device.h b/include/media/media-device.h
> index bcc6ec434f1f..110b89567671 100644
> --- a/include/media/media-device.h
> +++ b/include/media/media-device.h
> @@ -27,6 +27,7 @@
>  
>  struct ida;
>  struct device;
> +struct media_device;
>  
>  /**
>   * struct media_entity_notify - Media Entity Notify
> @@ -50,10 +51,26 @@ struct media_entity_notify {
>   * struct media_device_ops - Media device operations
>   * @link_notify: Link state change notification callback. This callback is
>   *		 called with the graph_mutex held.
> + * @req_alloc: Allocate a request. Set this if you need to allocate a struct
> + *	       larger then struct media_request. @req_alloc and @req_free must
> + *	       either both be set or both be NULL.
> + * @req_free: Free a request. Set this if @req_alloc was set as well, leave
> + *	      to NULL otherwise.
> + * @req_validate: Validate a request, but do not queue yet. The req_queue_mutex
> + *	          lock is held when this op is called.
> + * @req_queue: Queue a validated request, cannot fail. If something goes
> + *	       wrong when queueing this request then it should be marked
> + *	       as such internally in the driver and any related buffers
> + *	       must eventually return to vb2 with state VB2_BUF_STATE_ERROR.
> + *	       The req_queue_mutex lock is held when this op is called.
>   */
>  struct media_device_ops {
>  	int (*link_notify)(struct media_link *link, u32 flags,
>  			   unsigned int notification);
> +	struct media_request *(*req_alloc)(struct media_device *mdev);
> +	void (*req_free)(struct media_request *req);
> +	int (*req_validate)(struct media_request *req);
> +	void (*req_queue)(struct media_request *req);
>  };
>  
>  /**
> @@ -88,6 +105,8 @@ struct media_device_ops {
>   * @disable_source: Disable Source Handler function pointer
>   *
>   * @ops:	Operation handler callbacks
> + * @req_queue_mutex: Serialise the MEDIA_REQUEST_IOC_QUEUE ioctl w.r.t.
> + *		     other operations that stop or start streaming.
>   *
>   * This structure represents an abstract high-level media device. It allows easy
>   * access to entities and provides basic media device-level support. The
> @@ -158,6 +177,8 @@ struct media_device {
>  	void (*disable_source)(struct media_entity *entity);
>  
>  	const struct media_device_ops *ops;
> +
> +	struct mutex req_queue_mutex;
>  };
>  
>  /* We don't need to include pci.h or usb.h here */
> diff --git a/include/media/media-request.h b/include/media/media-request.h
> new file mode 100644
> index 000000000000..fe5a04fb970c
> --- /dev/null
> +++ b/include/media/media-request.h
> @@ -0,0 +1,327 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Media device request objects
> + *
> + * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
> + * Copyright (C) 2018 Intel Corporation
> + *
> + * Author: Hans Verkuil <hans.verkuil@cisco.com>
> + * Author: Sakari Ailus <sakari.ailus@linux.intel.com>
> + */
> +
> +#ifndef MEDIA_REQUEST_H
> +#define MEDIA_REQUEST_H
> +
> +#include <linux/list.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/refcount.h>
> +
> +#include <media/media-device.h>
> +
> +/**
> + * enum media_request_state - media request state
> + *
> + * @MEDIA_REQUEST_STATE_IDLE:		Idle
> + * @MEDIA_REQUEST_STATE_VALIDATING:	Validating the request, no state changes
> + *					allowed
> + * @MEDIA_REQUEST_STATE_QUEUED:		Queued
> + * @MEDIA_REQUEST_STATE_COMPLETE:	Completed, the request is done
> + * @MEDIA_REQUEST_STATE_CLEANING:	Cleaning, the request is being re-inited
> + * @MEDIA_REQUEST_STATE_UPDATING:	The request is being updated, i.e.
> + *					request objects are being added,
> + *					modified or removed
> + * @NR_OF_MEDIA_REQUEST_STATE:		The number of media request states, used
> + *					internally for sanity check purposes
> + */
> +enum media_request_state {
> +	MEDIA_REQUEST_STATE_IDLE,
> +	MEDIA_REQUEST_STATE_VALIDATING,
> +	MEDIA_REQUEST_STATE_QUEUED,
> +	MEDIA_REQUEST_STATE_COMPLETE,
> +	MEDIA_REQUEST_STATE_CLEANING,
> +	MEDIA_REQUEST_STATE_UPDATING,
> +	NR_OF_MEDIA_REQUEST_STATE,
> +};
> +
> +struct media_request_object;
> +
> +/**
> + * struct media_request - Media device request
> + * @mdev: Media device this request belongs to
> + * @kref: Reference count
> + * @debug_str: Prefix for debug messages (process name:fd)
> + * @state: The state of the request
> + * @updating_count: count the number of request updates that are in progress
> + * @objects: List of @struct media_request_object request objects
> + * @num_incomplete_objects: The number of incomplete objects in the request
> + * @poll_wait: Wait queue for poll
> + * @lock: Serializes access to this struct
> + */
> +struct media_request {
> +	struct media_device *mdev;
> +	struct kref kref;
> +	char debug_str[TASK_COMM_LEN + 11];
> +	enum media_request_state state;
> +	unsigned int updating_count;
> +	struct list_head objects;
> +	unsigned int num_incomplete_objects;
> +	struct wait_queue_head poll_wait;
> +	spinlock_t lock;
> +};
> +
> +#ifdef CONFIG_MEDIA_CONTROLLER
> +
> +/**
> + * media_request_lock_for_update - Lock the request for updating its objects
> + *
> + * @req: The media request
> + *
> + * Use before updating a request, i.e. adding, modifying or removing a request
> + * object in it. A reference to the request must be held during the update. This
> + * usually takes place automatically through a file handle. Use
> + * @media_request_unlock_for_update when done.
> + */
> +static inline int __must_check
> +media_request_lock_for_update(struct media_request *req)
> +{
> +	unsigned long flags;
> +	int ret = 0;
> +
> +	spin_lock_irqsave(&req->lock, flags);
> +	if (req->state == MEDIA_REQUEST_STATE_IDLE ||
> +	    req->state == MEDIA_REQUEST_STATE_UPDATING) {
> +		req->state = MEDIA_REQUEST_STATE_UPDATING;
> +		req->updating_count++;
> +	} else {
> +		ret = -EBUSY;
> +	}
> +	spin_unlock_irqrestore(&req->lock, flags);
> +
> +	return ret;
> +}
> +
> +/**
> + * media_request_unlock_for_update - Unlock a request previously locked for
> + *				     update
> + *
> + * @req: The media request
> + *
> + * Unlock a request that has previously been locked using
> + * @media_request_lock_for_update.
> + */
> +static inline void media_request_unlock_for_update(struct media_request *req)
> +{
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&req->lock, flags);
> +	WARN_ON(req->updating_count <= 0);
> +	if (!--req->updating_count)
> +		req->state = MEDIA_REQUEST_STATE_IDLE;
> +	spin_unlock_irqrestore(&req->lock, flags);
> +}
> +
> +/**
> + * media_request_get - Get the media request
> + *
> + * @req: The media request
> + *
> + * Get the media request.
> + */
> +static inline void media_request_get(struct media_request *req)
> +{
> +	kref_get(&req->kref);
> +}
> +
> +/**
> + * media_request_put - Put the media request
> + *
> + * @req: The media request
> + *
> + * Put the media request. The media request will be released
> + * when the refcount reaches 0.
> + */
> +void media_request_put(struct media_request *req);
> +
> +/**
> + * media_request_alloc - Allocate the media request
> + *
> + * @mdev: Media device this request belongs to
> + * @alloc: Store the request's file descriptor in this struct
> + *
> + * Allocated the media request and put the fd in @alloc->fd.
> + */
> +int media_request_alloc(struct media_device *mdev,
> +			struct media_request_alloc *alloc);
> +
> +#else
> +
> +static inline void media_request_get(struct media_request *req)
> +{
> +}
> +
> +static inline void media_request_put(struct media_request *req)
> +{
> +}
> +
> +#endif
> +
> +/**
> + * struct media_request_object_ops - Media request object operations
> + * @prepare: Validate and prepare the request object, optional.
> + * @unprepare: Unprepare the request object, optional.
> + * @queue: Queue the request object, optional.
> + * @unbind: Unbind the request object, optional.
> + * @release: Release the request object, required.
> + */
> +struct media_request_object_ops {
> +	int (*prepare)(struct media_request_object *object);
> +	void (*unprepare)(struct media_request_object *object);
> +	void (*queue)(struct media_request_object *object);
> +	void (*unbind)(struct media_request_object *object);
> +	void (*release)(struct media_request_object *object);
> +};
> +
> +/**
> + * struct media_request_object - An opaque object that belongs to a media
> + *				 request
> + *
> + * @ops: object's operations
> + * @priv: object's priv pointer
> + * @req: the request this object belongs to (can be NULL)
> + * @list: List entry of the object for @struct media_request
> + * @kref: Reference count of the object, acquire before releasing req->lock
> + * @completed: If true, then this object was completed.
> + *
> + * An object related to the request. This struct is always embedded in
> + * another struct that contains the actual data for this request object.
> + */
> +struct media_request_object {
> +	const struct media_request_object_ops *ops;
> +	void *priv;
> +	struct media_request *req;
> +	struct list_head list;
> +	struct kref kref;
> +	bool completed;
> +};
> +
> +#ifdef CONFIG_MEDIA_CONTROLLER
> +
> +/**
> + * media_request_object_get - Get a media request object
> + *
> + * @obj: The object
> + *
> + * Get a media request object.
> + */
> +static inline void media_request_object_get(struct media_request_object *obj)
> +{
> +	kref_get(&obj->kref);
> +}
> +
> +/**
> + * media_request_object_put - Put a media request object
> + *
> + * @obj: The object
> + *
> + * Put a media request object. Once all references are gone, the
> + * object's memory is released.
> + */
> +void media_request_object_put(struct media_request_object *obj);
> +
> +/**
> + * media_request_object_init - Initialise a media request object
> + *
> + * @obj: The object
> + *
> + * Initialise a media request object. The object will be released using the
> + * release callback of the ops once it has no references (this function
> + * initialises references to one).
> + */
> +void media_request_object_init(struct media_request_object *obj);
> +
> +/**
> + * media_request_object_bind - Bind a media request object to a request
> + *
> + * @req: The media request
> + * @ops: The object ops for this object
> + * @priv: A driver-specific priv pointer associated with this object
> + * @obj: The object
> + *
> + * Bind this object to the request and set the ops and priv values of
> + * the object so it can be found later with media_request_object_find().
> + *
> + * Every bound object must be unbound or completed by the kernel at some
> + * point in time, otherwise the request will never complete. When the
> + * request is released all completed objects will be unbound by the
> + * request core code.
> + */
> +int media_request_object_bind(struct media_request *req,
> +			      const struct media_request_object_ops *ops,
> +			      void *priv,
> +			      struct media_request_object *obj);
> +
> +/**
> + * media_request_object_unbind - Unbind a media request object
> + *
> + * @obj: The object
> + *
> + * Unbind the media request object from the request.
> + */
> +void media_request_object_unbind(struct media_request_object *obj);
> +
> +/**
> + * media_request_object_complete - Mark the media request object as complete
> + *
> + * @obj: The object
> + *
> + * Mark the media request object as complete. Only bound objects can
> + * be completed.
> + */
> +void media_request_object_complete(struct media_request_object *obj);
> +
> +#else
> +
> +static inline int __must_check
> +media_request_lock_for_update(struct media_request *req)
> +{
> +	return -EINVAL;
> +}
> +
> +static inline void media_request_unlock_for_update(struct media_request *req)
> +{
> +}
> +
> +static inline void media_request_object_get(struct media_request_object *obj)
> +{
> +}
> +
> +static inline void media_request_object_put(struct media_request_object *obj)
> +{
> +}
> +
> +static inline void media_request_object_init(struct media_request_object *obj)
> +{
> +	obj->ops = NULL;
> +	obj->req = NULL;
> +}
> +
> +static inline int media_request_object_bind(struct media_request *req,
> +			       const struct media_request_object_ops *ops,
> +			       void *priv,
> +			       struct media_request_object *obj)
> +{
> +	return 0;
> +}
> +
> +static inline void media_request_object_unbind(struct media_request_object *obj)
> +{
> +}
> +
> +static inline void media_request_object_complete(struct media_request_object *obj)
> +{
> +}
> +
> +#endif
> +
> +#endif



Thanks,
Mauro

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

* Re: [PATCHv17 04/34] media: doc: Add media-request.h header to documentation build
  2018-08-04 12:44 ` [PATCHv17 04/34] media: doc: Add media-request.h header to documentation build Hans Verkuil
@ 2018-08-09 18:43   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-09 18:43 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Sakari Ailus, Hans Verkuil

Em Sat,  4 Aug 2018 14:44:56 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Sakari Ailus <sakari.ailus@linux.intel.com>
> 
> media-request.h has been recently added; add it to the documentation build
> as well.

Looks good. Please add my reviewed-by:

Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>

(from now on, if I find a patch without anything to comment in this series,
I'll add my reviewed by - please remove it if you do changes that aren't
simply trivial rebase conflict solving).


> 
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  Documentation/media/kapi/mc-core.rst | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/Documentation/media/kapi/mc-core.rst b/Documentation/media/kapi/mc-core.rst
> index 0c05503eaf1f..69362b3135c2 100644
> --- a/Documentation/media/kapi/mc-core.rst
> +++ b/Documentation/media/kapi/mc-core.rst
> @@ -262,3 +262,5 @@ in the end provide a way to use driver-specific callbacks.
>  .. kernel-doc:: include/media/media-devnode.h
>  
>  .. kernel-doc:: include/media/media-entity.h
> +
> +.. kernel-doc:: include/media/media-request.h



Thanks,
Mauro

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

* Re: [PATCHv17 05/34] media-request: add media_request_get_by_fd
  2018-08-04 12:44 ` [PATCHv17 05/34] media-request: add media_request_get_by_fd Hans Verkuil
@ 2018-08-09 19:55   ` Mauro Carvalho Chehab
  2018-08-10  7:32     ` Hans Verkuil
  0 siblings, 1 reply; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-09 19:55 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:44:57 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Add media_request_get_by_fd() to find a request based on the file
> descriptor.
> 
> The caller has to call media_request_put() for the returned
> request since this function increments the refcount.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> ---
>  drivers/media/media-request.c | 40 +++++++++++++++++++++++++++++++++++
>  include/media/media-request.h | 24 +++++++++++++++++++++
>  2 files changed, 64 insertions(+)
> 
> diff --git a/drivers/media/media-request.c b/drivers/media/media-request.c
> index 253068f51a1f..4b523f3a03a3 100644
> --- a/drivers/media/media-request.c
> +++ b/drivers/media/media-request.c
> @@ -231,6 +231,46 @@ static const struct file_operations request_fops = {
>  	.release = media_request_close,
>  };
>  
> +struct media_request *
> +media_request_get_by_fd(struct media_device *mdev, int request_fd)
> +{
> +	struct file *filp;
> +	struct media_request *req;
> +
> +	if (!mdev || !mdev->ops ||
> +	    !mdev->ops->req_validate || !mdev->ops->req_queue)
> +		return ERR_PTR(-EPERM);

EPERM? I guess ENOTTY would be better.

Any reason why using EPERM?

> +
> +	filp = fget(request_fd);
> +	if (!filp)
> +		return ERR_PTR(-ENOENT);
> +
> +	if (filp->f_op != &request_fops)
> +		goto err_fput;
> +	req = filp->private_data;
> +	if (req->mdev != mdev)
> +		goto err_fput;
> +
> +	/*
> +	 * Note: as long as someone has an open filehandle of the request,
> +	 * the request can never be released. The fget() above ensures that
> +	 * even if userspace closes the request filehandle, the release()
> +	 * fop won't be called, so the media_request_get() always succeeds
> +	 * and there is no race condition where the request was released
> +	 * before media_request_get() is called.
> +	 */
> +	media_request_get(req);
> +	fput(filp);
> +
> +	return req;
> +
> +err_fput:
> +	fput(filp);
> +
> +	return ERR_PTR(-ENOENT);
> +}
> +EXPORT_SYMBOL_GPL(media_request_get_by_fd);
> +
>  int media_request_alloc(struct media_device *mdev,
>  			struct media_request_alloc *alloc)
>  {
> diff --git a/include/media/media-request.h b/include/media/media-request.h
> index fe5a04fb970c..66ec9d09fcd8 100644
> --- a/include/media/media-request.h
> +++ b/include/media/media-request.h
> @@ -143,6 +143,24 @@ static inline void media_request_get(struct media_request *req)
>   */
>  void media_request_put(struct media_request *req);
>  
> +/**
> + * media_request_get_by_fd - Get a media request by fd
> + *
> + * @mdev: Media device this request belongs to
> + * @request_fd: The file descriptor of the request
> + *
> + * Get the request represented by @request_fd that is owned
> + * by the media device.
> + *
> + * Return a -EPERM error pointer if requests are not supported
> + * by this driver. Return -ENOENT if the request was not found.
> + * Return the pointer to the request if found: the caller will
> + * have to call @media_request_put when it finished using the
> + * request.
> + */
> +struct media_request *
> +media_request_get_by_fd(struct media_device *mdev, int request_fd);
> +
>  /**
>   * media_request_alloc - Allocate the media request
>   *
> @@ -164,6 +182,12 @@ static inline void media_request_put(struct media_request *req)
>  {
>  }
>  
> +static inline struct media_request *
> +media_request_get_by_fd(struct media_device *mdev, int request_fd)
> +{
> +	return ERR_PTR(-EPERM);

Again -ENOTTY sounds better.

> +}
> +
>  #endif
>  
>  /**

Feel free to add my reviewed-by if you change the error code to
ENOTTY:

Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>


Thanks,
Mauro

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

* Re: [PATCHv17 06/34] media-request: add media_request_object_find
  2018-08-04 12:44 ` [PATCHv17 06/34] media-request: add media_request_object_find Hans Verkuil
@ 2018-08-09 19:56   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-09 19:56 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:44:58 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Add media_request_object_find to find a request object inside a
> request based on ops and priv values.
> 
> Objects of the same type (vb2 buffer, control handler) will have
> the same ops value. And objects that refer to the same 'parent'
> object (e.g. the v4l2_ctrl_handler that has the current driver
> state) will have the same priv value.
> 
> The caller has to call media_request_object_put() for the returned
> object since this function increments the refcount.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>

Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
> ---
>  drivers/media/media-request.c | 25 +++++++++++++++++++++++++
>  include/media/media-request.h | 28 ++++++++++++++++++++++++++++
>  2 files changed, 53 insertions(+)
> 
> diff --git a/drivers/media/media-request.c b/drivers/media/media-request.c
> index 4b523f3a03a3..a5b70a4e613b 100644
> --- a/drivers/media/media-request.c
> +++ b/drivers/media/media-request.c
> @@ -344,6 +344,31 @@ static void media_request_object_release(struct kref *kref)
>  	obj->ops->release(obj);
>  }
>  
> +struct media_request_object *
> +media_request_object_find(struct media_request *req,
> +			  const struct media_request_object_ops *ops,
> +			  void *priv)
> +{
> +	struct media_request_object *obj;
> +	struct media_request_object *found = NULL;
> +	unsigned long flags;
> +
> +	if (WARN_ON(!ops || !priv))
> +		return NULL;
> +
> +	spin_lock_irqsave(&req->lock, flags);
> +	list_for_each_entry(obj, &req->objects, list) {
> +		if (obj->ops == ops && obj->priv == priv) {
> +			media_request_object_get(obj);
> +			found = obj;
> +			break;
> +		}
> +	}
> +	spin_unlock_irqrestore(&req->lock, flags);
> +	return found;
> +}
> +EXPORT_SYMBOL_GPL(media_request_object_find);
> +
>  void media_request_object_put(struct media_request_object *obj)
>  {
>  	kref_put(&obj->kref, media_request_object_release);
> diff --git a/include/media/media-request.h b/include/media/media-request.h
> index 66ec9d09fcd8..fd08d7a431a1 100644
> --- a/include/media/media-request.h
> +++ b/include/media/media-request.h
> @@ -253,6 +253,26 @@ static inline void media_request_object_get(struct media_request_object *obj)
>   */
>  void media_request_object_put(struct media_request_object *obj);
>  
> +/**
> + * media_request_object_find - Find an object in a request
> + *
> + * @req: The media request
> + * @ops: Find an object with this ops value
> + * @priv: Find an object with this priv value
> + *
> + * Both @ops and @priv must be non-NULL.
> + *
> + * Returns the object pointer or NULL if not found. The caller must
> + * call media_request_object_put() once it finished using the object.
> + *
> + * Since this function needs to walk the list of objects it takes
> + * the @req->lock spin lock to make this safe.
> + */
> +struct media_request_object *
> +media_request_object_find(struct media_request *req,
> +			  const struct media_request_object_ops *ops,
> +			  void *priv);
> +
>  /**
>   * media_request_object_init - Initialise a media request object
>   *
> @@ -324,6 +344,14 @@ static inline void media_request_object_put(struct media_request_object *obj)
>  {
>  }
>  
> +static inline struct media_request_object *
> +media_request_object_find(struct media_request *req,
> +			  const struct media_request_object_ops *ops,
> +			  void *priv)
> +{
> +	return NULL;
> +}
> +
>  static inline void media_request_object_init(struct media_request_object *obj)
>  {
>  	obj->ops = NULL;



Thanks,
Mauro

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

* Re: [PATCHv17 07/34] v4l2-device.h: add v4l2_device_supports_requests() helper
  2018-08-04 12:44 ` [PATCHv17 07/34] v4l2-device.h: add v4l2_device_supports_requests() helper Hans Verkuil
@ 2018-08-09 19:57   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-09 19:57 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:44:59 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Add a simple helper function that tests if the driver supports
> the request API.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>

Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>

> ---
>  include/media/v4l2-device.h | 11 +++++++++++
>  1 file changed, 11 insertions(+)
> 
> diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h
> index b330e4a08a6b..ac7677a183ff 100644
> --- a/include/media/v4l2-device.h
> +++ b/include/media/v4l2-device.h
> @@ -211,6 +211,17 @@ static inline void v4l2_subdev_notify(struct v4l2_subdev *sd,
>  		sd->v4l2_dev->notify(sd, notification, arg);
>  }
>  
> +/**
> + * v4l2_device_supports_requests - Test if requests are supported.
> + *
> + * @v4l2_dev: pointer to struct v4l2_device
> + */
> +static inline bool v4l2_device_supports_requests(struct v4l2_device *v4l2_dev)
> +{
> +	return v4l2_dev->mdev && v4l2_dev->mdev->ops &&
> +	       v4l2_dev->mdev->ops->req_queue;
> +}
> +
>  /* Helper macros to iterate over all subdevs. */
>  
>  /**



Thanks,
Mauro

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

* Re: [PATCHv17 08/34] v4l2-dev: lock req_queue_mutex
  2018-08-04 12:45 ` [PATCHv17 08/34] v4l2-dev: lock req_queue_mutex Hans Verkuil
@ 2018-08-09 20:03   ` Mauro Carvalho Chehab
  2018-08-10  7:39     ` Hans Verkuil
  2018-08-14 12:00     ` Hans Verkuil
  0 siblings, 2 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-09 20:03 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil, Sakari Ailus

Em Sat,  4 Aug 2018 14:45:00 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> We need to serialize streamon/off with queueing new requests.
> These ioctls may trigger the cancellation of a streaming
> operation, and that should not be mixed with queuing a new
> request at the same time.
> 
> Finally close() needs this lock since that too can trigger the
> cancellation of a streaming operation.
> 
> We take the req_queue_mutex here before any other locks since
> it is a very high-level lock.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> ---
>  drivers/media/v4l2-core/v4l2-dev.c   | 13 +++++++++++++
>  drivers/media/v4l2-core/v4l2-ioctl.c | 22 +++++++++++++++++++++-
>  2 files changed, 34 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
> index 69e775930fc4..53018e4a4c78 100644
> --- a/drivers/media/v4l2-core/v4l2-dev.c
> +++ b/drivers/media/v4l2-core/v4l2-dev.c
> @@ -444,8 +444,21 @@ static int v4l2_release(struct inode *inode, struct file *filp)
>  	struct video_device *vdev = video_devdata(filp);
>  	int ret = 0;
>  
> +	/*
> +	 * We need to serialize the release() with queueing new requests.
> +	 * The release() may trigger the cancellation of a streaming
> +	 * operation, and that should not be mixed with queueing a new
> +	 * request at the same time.
> +	 */
> +	if (v4l2_device_supports_requests(vdev->v4l2_dev))
> +		mutex_lock(&vdev->v4l2_dev->mdev->req_queue_mutex);
> +
>  	if (vdev->fops->release)
>  		ret = vdev->fops->release(filp);
> +
> +	if (v4l2_device_supports_requests(vdev->v4l2_dev))
> +		mutex_unlock(&vdev->v4l2_dev->mdev->req_queue_mutex);
> +

This will very likely generate sparse warnings. See my discussions
with that regards with Linus.

The only way to avoid it would be to do something like:

	if (v4l2_device_supports_requests(vdev->v4l2_dev)) {
		mutex_lock(&vdev->v4l2_dev->mdev->req_queue_mutex);
	 	if (vdev->fops->release)
			ret = vdev->fops->release(filp);
		mutex_unlock(&vdev->v4l2_dev->mdev->req_queue_mutex);
	} else {
	 	if (vdev->fops->release)
			ret = vdev->fops->release(filp);
	}

>  	if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
>  		dprintk("%s: release\n",
>  			video_device_node_name(vdev));
> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
> index 54afc9c7ee6e..ea475d833dd6 100644
> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> @@ -2780,6 +2780,7 @@ static long __video_do_ioctl(struct file *file,
>  		unsigned int cmd, void *arg)
>  {
>  	struct video_device *vfd = video_devdata(file);
> +	struct mutex *req_queue_lock = NULL;
>  	struct mutex *lock; /* ioctl serialization mutex */
>  	const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
>  	bool write_only = false;
> @@ -2799,10 +2800,27 @@ static long __video_do_ioctl(struct file *file,
>  	if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags))
>  		vfh = file->private_data;
>  
> +	/*
> +	 * We need to serialize streamon/off with queueing new requests.
> +	 * These ioctls may trigger the cancellation of a streaming
> +	 * operation, and that should not be mixed with queueing a new
> +	 * request at the same time.
> +	 */
> +	if (v4l2_device_supports_requests(vfd->v4l2_dev) &&
> +	    (cmd == VIDIOC_STREAMON || cmd == VIDIOC_STREAMOFF)) {
> +		req_queue_lock = &vfd->v4l2_dev->mdev->req_queue_mutex;
> +
> +		if (mutex_lock_interruptible(req_queue_lock))
> +			return -ERESTARTSYS;
> +	}
> +
>  	lock = v4l2_ioctl_get_lock(vfd, vfh, cmd, arg);
>  
> -	if (lock && mutex_lock_interruptible(lock))
> +	if (lock && mutex_lock_interruptible(lock)) {
> +		if (req_queue_lock)
> +			mutex_unlock(req_queue_lock);
>  		return -ERESTARTSYS;
> +	}

Same applies here.

>  
>  	if (!video_is_registered(vfd)) {
>  		ret = -ENODEV;
> @@ -2861,6 +2879,8 @@ static long __video_do_ioctl(struct file *file,
>  unlock:
>  	if (lock)
>  		mutex_unlock(lock);
> +	if (req_queue_lock)
> +		mutex_unlock(req_queue_lock);

This code looks really weird! are you locking in order to get a
lock pointer?

That seems broken by design.

>  	return ret;
>  }
>  



Thanks,
Mauro

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

* Re: [PATCHv17 09/34] videodev2.h: add request_fd field to v4l2_ext_controls
  2018-08-04 12:45 ` [PATCHv17 09/34] videodev2.h: add request_fd field to v4l2_ext_controls Hans Verkuil
@ 2018-08-09 20:04   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-09 20:04 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Alexandre Courbot, Hans Verkuil

Em Sat,  4 Aug 2018 14:45:01 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Alexandre Courbot <acourbot@chromium.org>
> 
> If 'which' is V4L2_CTRL_WHICH_REQUEST_VAL, then the 'request_fd' field
> can be used to specify a request for the G/S/TRY_EXT_CTRLS ioctls.
> 
> Signed-off-by: Alexandre Courbot <acourbot@chromium.org>
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>

Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>

> ---
>  drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 5 ++++-
>  drivers/media/v4l2-core/v4l2-ioctl.c          | 6 +++---
>  include/uapi/linux/videodev2.h                | 4 +++-
>  3 files changed, 10 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> index 6481212fda77..dcce86c1fe40 100644
> --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> @@ -834,7 +834,8 @@ struct v4l2_ext_controls32 {
>  	__u32 which;
>  	__u32 count;
>  	__u32 error_idx;
> -	__u32 reserved[2];
> +	__s32 request_fd;
> +	__u32 reserved[1];
>  	compat_caddr_t controls; /* actually struct v4l2_ext_control32 * */
>  };
>  
> @@ -909,6 +910,7 @@ static int get_v4l2_ext_controls32(struct file *file,
>  	    get_user(count, &p32->count) ||
>  	    put_user(count, &p64->count) ||
>  	    assign_in_user(&p64->error_idx, &p32->error_idx) ||
> +	    assign_in_user(&p64->request_fd, &p32->request_fd) ||
>  	    copy_in_user(p64->reserved, p32->reserved, sizeof(p64->reserved)))
>  		return -EFAULT;
>  
> @@ -974,6 +976,7 @@ static int put_v4l2_ext_controls32(struct file *file,
>  	    get_user(count, &p64->count) ||
>  	    put_user(count, &p32->count) ||
>  	    assign_in_user(&p32->error_idx, &p64->error_idx) ||
> +	    assign_in_user(&p32->request_fd, &p64->request_fd) ||
>  	    copy_in_user(p32->reserved, p64->reserved, sizeof(p32->reserved)) ||
>  	    get_user(kcontrols, &p64->controls))
>  		return -EFAULT;
> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
> index ea475d833dd6..03241d6b7ef8 100644
> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> @@ -590,8 +590,8 @@ static void v4l_print_ext_controls(const void *arg, bool write_only)
>  	const struct v4l2_ext_controls *p = arg;
>  	int i;
>  
> -	pr_cont("which=0x%x, count=%d, error_idx=%d",
> -			p->which, p->count, p->error_idx);
> +	pr_cont("which=0x%x, count=%d, error_idx=%d, request_fd=%d",
> +			p->which, p->count, p->error_idx, p->request_fd);
>  	for (i = 0; i < p->count; i++) {
>  		if (!p->controls[i].size)
>  			pr_cont(", id/val=0x%x/0x%x",
> @@ -907,7 +907,7 @@ static int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv)
>  	__u32 i;
>  
>  	/* zero the reserved fields */
> -	c->reserved[0] = c->reserved[1] = 0;
> +	c->reserved[0] = 0;
>  	for (i = 0; i < c->count; i++)
>  		c->controls[i].reserved2[0] = 0;
>  
> diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> index 5d1a3685bea9..1df0fa983db6 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -1599,7 +1599,8 @@ struct v4l2_ext_controls {
>  	};
>  	__u32 count;
>  	__u32 error_idx;
> -	__u32 reserved[2];
> +	__s32 request_fd;
> +	__u32 reserved[1];
>  	struct v4l2_ext_control *controls;
>  };
>  
> @@ -1612,6 +1613,7 @@ struct v4l2_ext_controls {
>  #define V4L2_CTRL_MAX_DIMS	  (4)
>  #define V4L2_CTRL_WHICH_CUR_VAL   0
>  #define V4L2_CTRL_WHICH_DEF_VAL   0x0f000000
> +#define V4L2_CTRL_WHICH_REQUEST_VAL 0x0f010000
>  
>  enum v4l2_ctrl_type {
>  	V4L2_CTRL_TYPE_INTEGER	     = 1,



Thanks,
Mauro

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

* Re: [PATCHv17 10/34] v4l2-ctrls: v4l2_ctrl_add_handler: add from_other_dev
  2018-08-04 12:45 ` [PATCHv17 10/34] v4l2-ctrls: v4l2_ctrl_add_handler: add from_other_dev Hans Verkuil
@ 2018-08-09 20:10   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-09 20:10 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil, Alexandre Courbot

Em Sat,  4 Aug 2018 14:45:02 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Add a 'bool from_other_dev' argument: set to true if the two
> handlers refer to different devices (e.g. it is true when
> inheriting controls from a subdev into a main v4l2 bridge
> driver).
> 
> This will be used later when implementing support for the
> request API since we need to skip such controls.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> Signed-off-by: Alexandre Courbot <acourbot@chromium.org>

Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>

> ---
>  drivers/media/dvb-frontends/rtl2832_sdr.c     |  5 +-
>  drivers/media/pci/bt8xx/bttv-driver.c         |  2 +-
>  drivers/media/pci/cx23885/cx23885-417.c       |  2 +-
>  drivers/media/pci/cx88/cx88-blackbird.c       |  2 +-
>  drivers/media/pci/cx88/cx88-video.c           |  2 +-
>  drivers/media/pci/saa7134/saa7134-empress.c   |  4 +-
>  drivers/media/pci/saa7134/saa7134-video.c     |  2 +-
>  .../media/platform/exynos4-is/fimc-capture.c  |  2 +-
>  drivers/media/platform/rcar-vin/rcar-core.c   |  2 +-
>  drivers/media/platform/rcar_drif.c            |  2 +-
>  .../media/platform/soc_camera/soc_camera.c    |  3 +-
>  drivers/media/platform/vivid/vivid-ctrls.c    | 46 +++++++++----------
>  drivers/media/usb/cx231xx/cx231xx-417.c       |  2 +-
>  drivers/media/usb/cx231xx/cx231xx-video.c     |  4 +-
>  drivers/media/usb/msi2500/msi2500.c           |  2 +-
>  drivers/media/usb/tm6000/tm6000-video.c       |  2 +-
>  drivers/media/v4l2-core/v4l2-ctrls.c          | 11 +++--
>  drivers/media/v4l2-core/v4l2-device.c         |  3 +-
>  drivers/staging/media/imx/imx-media-dev.c     |  2 +-
>  drivers/staging/media/imx/imx-media-fim.c     |  2 +-
>  include/media/v4l2-ctrls.h                    |  8 +++-
>  21 files changed, 61 insertions(+), 49 deletions(-)
> 
> diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c
> index d448d9d4879c..7d0c89e269ab 100644
> --- a/drivers/media/dvb-frontends/rtl2832_sdr.c
> +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c
> @@ -1394,7 +1394,8 @@ static int rtl2832_sdr_probe(struct platform_device *pdev)
>  	case RTL2832_SDR_TUNER_E4000:
>  		v4l2_ctrl_handler_init(&dev->hdl, 9);
>  		if (subdev)
> -			v4l2_ctrl_add_handler(&dev->hdl, subdev->ctrl_handler, NULL);
> +			v4l2_ctrl_add_handler(&dev->hdl, subdev->ctrl_handler,
> +					      NULL, true);
>  		break;
>  	case RTL2832_SDR_TUNER_R820T:
>  	case RTL2832_SDR_TUNER_R828D:
> @@ -1423,7 +1424,7 @@ static int rtl2832_sdr_probe(struct platform_device *pdev)
>  		v4l2_ctrl_handler_init(&dev->hdl, 2);
>  		if (subdev)
>  			v4l2_ctrl_add_handler(&dev->hdl, subdev->ctrl_handler,
> -					      NULL);
> +					      NULL, true);
>  		break;
>  	default:
>  		v4l2_ctrl_handler_init(&dev->hdl, 0);
> diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c
> index cf05e11da01b..e86154092558 100644
> --- a/drivers/media/pci/bt8xx/bttv-driver.c
> +++ b/drivers/media/pci/bt8xx/bttv-driver.c
> @@ -4211,7 +4211,7 @@ static int bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id)
>  	/* register video4linux + input */
>  	if (!bttv_tvcards[btv->c.type].no_video) {
>  		v4l2_ctrl_add_handler(&btv->radio_ctrl_handler, hdl,
> -				v4l2_ctrl_radio_filter);
> +				v4l2_ctrl_radio_filter, false);
>  		if (btv->radio_ctrl_handler.error) {
>  			result = btv->radio_ctrl_handler.error;
>  			goto fail2;
> diff --git a/drivers/media/pci/cx23885/cx23885-417.c b/drivers/media/pci/cx23885/cx23885-417.c
> index a71f3c7569ce..762823871c78 100644
> --- a/drivers/media/pci/cx23885/cx23885-417.c
> +++ b/drivers/media/pci/cx23885/cx23885-417.c
> @@ -1527,7 +1527,7 @@ int cx23885_417_register(struct cx23885_dev *dev)
>  	dev->cxhdl.priv = dev;
>  	dev->cxhdl.func = cx23885_api_func;
>  	cx2341x_handler_set_50hz(&dev->cxhdl, tsport->height == 576);
> -	v4l2_ctrl_add_handler(&dev->ctrl_handler, &dev->cxhdl.hdl, NULL);
> +	v4l2_ctrl_add_handler(&dev->ctrl_handler, &dev->cxhdl.hdl, NULL, false);
>  
>  	/* Allocate and initialize V4L video device */
>  	dev->v4l_device = cx23885_video_dev_alloc(tsport,
> diff --git a/drivers/media/pci/cx88/cx88-blackbird.c b/drivers/media/pci/cx88/cx88-blackbird.c
> index 7a4876cf9f08..722dd101c9b0 100644
> --- a/drivers/media/pci/cx88/cx88-blackbird.c
> +++ b/drivers/media/pci/cx88/cx88-blackbird.c
> @@ -1183,7 +1183,7 @@ static int cx8802_blackbird_probe(struct cx8802_driver *drv)
>  	err = cx2341x_handler_init(&dev->cxhdl, 36);
>  	if (err)
>  		goto fail_core;
> -	v4l2_ctrl_add_handler(&dev->cxhdl.hdl, &core->video_hdl, NULL);
> +	v4l2_ctrl_add_handler(&dev->cxhdl.hdl, &core->video_hdl, NULL, false);
>  
>  	/* blackbird stuff */
>  	pr_info("cx23416 based mpeg encoder (blackbird reference design)\n");
> diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c
> index 7b113bad70d2..85e2b6c9fb1c 100644
> --- a/drivers/media/pci/cx88/cx88-video.c
> +++ b/drivers/media/pci/cx88/cx88-video.c
> @@ -1378,7 +1378,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev,
>  		if (vc->id == V4L2_CID_CHROMA_AGC)
>  			core->chroma_agc = vc;
>  	}
> -	v4l2_ctrl_add_handler(&core->video_hdl, &core->audio_hdl, NULL);
> +	v4l2_ctrl_add_handler(&core->video_hdl, &core->audio_hdl, NULL, false);
>  
>  	/* load and configure helper modules */
>  
> diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c
> index 66acfd35ffc6..fc75ce00dbf8 100644
> --- a/drivers/media/pci/saa7134/saa7134-empress.c
> +++ b/drivers/media/pci/saa7134/saa7134-empress.c
> @@ -265,9 +265,9 @@ static int empress_init(struct saa7134_dev *dev)
>  		 "%s empress (%s)", dev->name,
>  		 saa7134_boards[dev->board].name);
>  	v4l2_ctrl_handler_init(hdl, 21);
> -	v4l2_ctrl_add_handler(hdl, &dev->ctrl_handler, empress_ctrl_filter);
> +	v4l2_ctrl_add_handler(hdl, &dev->ctrl_handler, empress_ctrl_filter, false);
>  	if (dev->empress_sd)
> -		v4l2_ctrl_add_handler(hdl, dev->empress_sd->ctrl_handler, NULL);
> +		v4l2_ctrl_add_handler(hdl, dev->empress_sd->ctrl_handler, NULL, true);
>  	if (hdl->error) {
>  		video_device_release(dev->empress_dev);
>  		return hdl->error;
> diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c
> index 1a50ec9d084f..41d46488d22e 100644
> --- a/drivers/media/pci/saa7134/saa7134-video.c
> +++ b/drivers/media/pci/saa7134/saa7134-video.c
> @@ -2136,7 +2136,7 @@ int saa7134_video_init1(struct saa7134_dev *dev)
>  		hdl = &dev->radio_ctrl_handler;
>  		v4l2_ctrl_handler_init(hdl, 2);
>  		v4l2_ctrl_add_handler(hdl, &dev->ctrl_handler,
> -				v4l2_ctrl_radio_filter);
> +				v4l2_ctrl_radio_filter, false);
>  		if (hdl->error)
>  			return hdl->error;
>  	}
> diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c
> index a3cdac188190..2164375f0ee0 100644
> --- a/drivers/media/platform/exynos4-is/fimc-capture.c
> +++ b/drivers/media/platform/exynos4-is/fimc-capture.c
> @@ -1424,7 +1424,7 @@ static int fimc_link_setup(struct media_entity *entity,
>  		return 0;
>  
>  	return v4l2_ctrl_add_handler(&vc->ctx->ctrls.handler,
> -				     sensor->ctrl_handler, NULL);
> +				     sensor->ctrl_handler, NULL, true);
>  }
>  
>  static const struct media_entity_operations fimc_sd_media_ops = {
> diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c
> index ce09799976ef..42f1084bedef 100644
> --- a/drivers/media/platform/rcar-vin/rcar-core.c
> +++ b/drivers/media/platform/rcar-vin/rcar-core.c
> @@ -476,7 +476,7 @@ static int rvin_parallel_subdevice_attach(struct rvin_dev *vin,
>  		return ret;
>  
>  	ret = v4l2_ctrl_add_handler(&vin->ctrl_handler, subdev->ctrl_handler,
> -				    NULL);
> +				    NULL, true);
>  	if (ret < 0) {
>  		v4l2_ctrl_handler_free(&vin->ctrl_handler);
>  		return ret;
> diff --git a/drivers/media/platform/rcar_drif.c b/drivers/media/platform/rcar_drif.c
> index 81413ab52475..8c3388b9f5bd 100644
> --- a/drivers/media/platform/rcar_drif.c
> +++ b/drivers/media/platform/rcar_drif.c
> @@ -1164,7 +1164,7 @@ static int rcar_drif_notify_complete(struct v4l2_async_notifier *notifier)
>  	}
>  
>  	ret = v4l2_ctrl_add_handler(&sdr->ctrl_hdl,
> -				    sdr->ep.subdev->ctrl_handler, NULL);
> +				    sdr->ep.subdev->ctrl_handler, NULL, true);
>  	if (ret) {
>  		rdrif_err(sdr, "failed: ctrl add hdlr ret %d\n", ret);
>  		goto error;
> diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
> index 66d613629167..901c07f49351 100644
> --- a/drivers/media/platform/soc_camera/soc_camera.c
> +++ b/drivers/media/platform/soc_camera/soc_camera.c
> @@ -1181,7 +1181,8 @@ static int soc_camera_probe_finish(struct soc_camera_device *icd)
>  
>  	v4l2_subdev_call(sd, video, g_tvnorms, &icd->vdev->tvnorms);
>  
> -	ret = v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler, NULL);
> +	ret = v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler,
> +				    NULL, true);
>  	if (ret < 0)
>  		return ret;
>  
> diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/platform/vivid/vivid-ctrls.c
> index 5429193fbb91..5655f39d8e76 100644
> --- a/drivers/media/platform/vivid/vivid-ctrls.c
> +++ b/drivers/media/platform/vivid/vivid-ctrls.c
> @@ -1662,59 +1662,59 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap,
>  		v4l2_ctrl_auto_cluster(2, &dev->autogain, 0, true);
>  
>  	if (dev->has_vid_cap) {
> -		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_gen, NULL);
> -		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_vid, NULL);
> -		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_aud, NULL);
> -		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_streaming, NULL);
> -		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_sdtv_cap, NULL);
> -		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_loop_cap, NULL);
> -		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_fb, NULL);
> +		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_gen, NULL, false);
> +		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_vid, NULL, false);
> +		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_user_aud, NULL, false);
> +		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_streaming, NULL, false);
> +		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_sdtv_cap, NULL, false);
> +		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_loop_cap, NULL, false);
> +		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_fb, NULL, false);
>  		if (hdl_vid_cap->error)
>  			return hdl_vid_cap->error;
>  		dev->vid_cap_dev.ctrl_handler = hdl_vid_cap;
>  	}
>  	if (dev->has_vid_out) {
> -		v4l2_ctrl_add_handler(hdl_vid_out, hdl_user_gen, NULL);
> -		v4l2_ctrl_add_handler(hdl_vid_out, hdl_user_aud, NULL);
> -		v4l2_ctrl_add_handler(hdl_vid_out, hdl_streaming, NULL);
> -		v4l2_ctrl_add_handler(hdl_vid_out, hdl_fb, NULL);
> +		v4l2_ctrl_add_handler(hdl_vid_out, hdl_user_gen, NULL, false);
> +		v4l2_ctrl_add_handler(hdl_vid_out, hdl_user_aud, NULL, false);
> +		v4l2_ctrl_add_handler(hdl_vid_out, hdl_streaming, NULL, false);
> +		v4l2_ctrl_add_handler(hdl_vid_out, hdl_fb, NULL, false);
>  		if (hdl_vid_out->error)
>  			return hdl_vid_out->error;
>  		dev->vid_out_dev.ctrl_handler = hdl_vid_out;
>  	}
>  	if (dev->has_vbi_cap) {
> -		v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_user_gen, NULL);
> -		v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_streaming, NULL);
> -		v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_sdtv_cap, NULL);
> -		v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_loop_cap, NULL);
> +		v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_user_gen, NULL, false);
> +		v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_streaming, NULL, false);
> +		v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_sdtv_cap, NULL, false);
> +		v4l2_ctrl_add_handler(hdl_vbi_cap, hdl_loop_cap, NULL, false);
>  		if (hdl_vbi_cap->error)
>  			return hdl_vbi_cap->error;
>  		dev->vbi_cap_dev.ctrl_handler = hdl_vbi_cap;
>  	}
>  	if (dev->has_vbi_out) {
> -		v4l2_ctrl_add_handler(hdl_vbi_out, hdl_user_gen, NULL);
> -		v4l2_ctrl_add_handler(hdl_vbi_out, hdl_streaming, NULL);
> +		v4l2_ctrl_add_handler(hdl_vbi_out, hdl_user_gen, NULL, false);
> +		v4l2_ctrl_add_handler(hdl_vbi_out, hdl_streaming, NULL, false);
>  		if (hdl_vbi_out->error)
>  			return hdl_vbi_out->error;
>  		dev->vbi_out_dev.ctrl_handler = hdl_vbi_out;
>  	}
>  	if (dev->has_radio_rx) {
> -		v4l2_ctrl_add_handler(hdl_radio_rx, hdl_user_gen, NULL);
> -		v4l2_ctrl_add_handler(hdl_radio_rx, hdl_user_aud, NULL);
> +		v4l2_ctrl_add_handler(hdl_radio_rx, hdl_user_gen, NULL, false);
> +		v4l2_ctrl_add_handler(hdl_radio_rx, hdl_user_aud, NULL, false);
>  		if (hdl_radio_rx->error)
>  			return hdl_radio_rx->error;
>  		dev->radio_rx_dev.ctrl_handler = hdl_radio_rx;
>  	}
>  	if (dev->has_radio_tx) {
> -		v4l2_ctrl_add_handler(hdl_radio_tx, hdl_user_gen, NULL);
> -		v4l2_ctrl_add_handler(hdl_radio_tx, hdl_user_aud, NULL);
> +		v4l2_ctrl_add_handler(hdl_radio_tx, hdl_user_gen, NULL, false);
> +		v4l2_ctrl_add_handler(hdl_radio_tx, hdl_user_aud, NULL, false);
>  		if (hdl_radio_tx->error)
>  			return hdl_radio_tx->error;
>  		dev->radio_tx_dev.ctrl_handler = hdl_radio_tx;
>  	}
>  	if (dev->has_sdr_cap) {
> -		v4l2_ctrl_add_handler(hdl_sdr_cap, hdl_user_gen, NULL);
> -		v4l2_ctrl_add_handler(hdl_sdr_cap, hdl_streaming, NULL);
> +		v4l2_ctrl_add_handler(hdl_sdr_cap, hdl_user_gen, NULL, false);
> +		v4l2_ctrl_add_handler(hdl_sdr_cap, hdl_streaming, NULL, false);
>  		if (hdl_sdr_cap->error)
>  			return hdl_sdr_cap->error;
>  		dev->sdr_cap_dev.ctrl_handler = hdl_sdr_cap;
> diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c
> index 2f3b0564d676..e3cb9eefd36a 100644
> --- a/drivers/media/usb/cx231xx/cx231xx-417.c
> +++ b/drivers/media/usb/cx231xx/cx231xx-417.c
> @@ -1992,7 +1992,7 @@ int cx231xx_417_register(struct cx231xx *dev)
>  	dev->mpeg_ctrl_handler.ops = &cx231xx_ops;
>  	if (dev->sd_cx25840)
>  		v4l2_ctrl_add_handler(&dev->mpeg_ctrl_handler.hdl,
> -				dev->sd_cx25840->ctrl_handler, NULL);
> +				dev->sd_cx25840->ctrl_handler, NULL, false);
>  	if (dev->mpeg_ctrl_handler.hdl.error) {
>  		err = dev->mpeg_ctrl_handler.hdl.error;
>  		dprintk(3, "%s: can't add cx25840 controls\n", dev->name);
> diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c
> index f7fcd733a2ca..2dedb18f63a0 100644
> --- a/drivers/media/usb/cx231xx/cx231xx-video.c
> +++ b/drivers/media/usb/cx231xx/cx231xx-video.c
> @@ -2204,10 +2204,10 @@ int cx231xx_register_analog_devices(struct cx231xx *dev)
>  
>  	if (dev->sd_cx25840) {
>  		v4l2_ctrl_add_handler(&dev->ctrl_handler,
> -				dev->sd_cx25840->ctrl_handler, NULL);
> +				dev->sd_cx25840->ctrl_handler, NULL, true);
>  		v4l2_ctrl_add_handler(&dev->radio_ctrl_handler,
>  				dev->sd_cx25840->ctrl_handler,
> -				v4l2_ctrl_radio_filter);
> +				v4l2_ctrl_radio_filter, true);
>  	}
>  
>  	if (dev->ctrl_handler.error)
> diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c
> index 65ef755adfdc..4aacd77a5d58 100644
> --- a/drivers/media/usb/msi2500/msi2500.c
> +++ b/drivers/media/usb/msi2500/msi2500.c
> @@ -1278,7 +1278,7 @@ static int msi2500_probe(struct usb_interface *intf,
>  	}
>  
>  	/* currently all controls are from subdev */
> -	v4l2_ctrl_add_handler(&dev->hdl, sd->ctrl_handler, NULL);
> +	v4l2_ctrl_add_handler(&dev->hdl, sd->ctrl_handler, NULL, true);
>  
>  	dev->v4l2_dev.ctrl_handler = &dev->hdl;
>  	dev->vdev.v4l2_dev = &dev->v4l2_dev;
> diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c
> index 96055de6e8ce..176abbf5fbba 100644
> --- a/drivers/media/usb/tm6000/tm6000-video.c
> +++ b/drivers/media/usb/tm6000/tm6000-video.c
> @@ -1625,7 +1625,7 @@ int tm6000_v4l2_register(struct tm6000_core *dev)
>  	v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops,
>  			V4L2_CID_HUE, -128, 127, 1, 0);
>  	v4l2_ctrl_add_handler(&dev->ctrl_handler,
> -			&dev->radio_ctrl_handler, NULL);
> +			&dev->radio_ctrl_handler, NULL, false);
>  
>  	if (dev->radio_ctrl_handler.error)
>  		ret = dev->radio_ctrl_handler.error;
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
> index 599c1cbff3b9..404291f00715 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls.c
> @@ -2016,7 +2016,8 @@ EXPORT_SYMBOL(v4l2_ctrl_find);
>  
>  /* Allocate a new v4l2_ctrl_ref and hook it into the handler. */
>  static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
> -			   struct v4l2_ctrl *ctrl)
> +			   struct v4l2_ctrl *ctrl,
> +			   bool from_other_dev)
>  {
>  	struct v4l2_ctrl_ref *ref;
>  	struct v4l2_ctrl_ref *new_ref;
> @@ -2040,6 +2041,7 @@ static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
>  	if (!new_ref)
>  		return handler_set_err(hdl, -ENOMEM);
>  	new_ref->ctrl = ctrl;
> +	new_ref->from_other_dev = from_other_dev;
>  	if (ctrl->handler == hdl) {
>  		/* By default each control starts in a cluster of its own.
>  		   new_ref->ctrl is basically a cluster array with one
> @@ -2220,7 +2222,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>  		ctrl->type_ops->init(ctrl, idx, ctrl->p_new);
>  	}
>  
> -	if (handler_new_ref(hdl, ctrl)) {
> +	if (handler_new_ref(hdl, ctrl, false)) {
>  		kvfree(ctrl);
>  		return NULL;
>  	}
> @@ -2389,7 +2391,8 @@ EXPORT_SYMBOL(v4l2_ctrl_new_int_menu);
>  /* Add the controls from another handler to our own. */
>  int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
>  			  struct v4l2_ctrl_handler *add,
> -			  bool (*filter)(const struct v4l2_ctrl *ctrl))
> +			  bool (*filter)(const struct v4l2_ctrl *ctrl),
> +			  bool from_other_dev)
>  {
>  	struct v4l2_ctrl_ref *ref;
>  	int ret = 0;
> @@ -2412,7 +2415,7 @@ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
>  		/* Filter any unwanted controls */
>  		if (filter && !filter(ctrl))
>  			continue;
> -		ret = handler_new_ref(hdl, ctrl);
> +		ret = handler_new_ref(hdl, ctrl, from_other_dev);
>  		if (ret)
>  			break;
>  	}
> diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c
> index 3940e55c72f1..5189fb9f741f 100644
> --- a/drivers/media/v4l2-core/v4l2-device.c
> +++ b/drivers/media/v4l2-core/v4l2-device.c
> @@ -178,7 +178,8 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
>  
>  	sd->v4l2_dev = v4l2_dev;
>  	/* This just returns 0 if either of the two args is NULL */
> -	err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler, NULL);
> +	err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler,
> +				    NULL, true);
>  	if (err)
>  		goto error_module;
>  
> diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c
> index b0be80f05767..b03a4b7bb769 100644
> --- a/drivers/staging/media/imx/imx-media-dev.c
> +++ b/drivers/staging/media/imx/imx-media-dev.c
> @@ -391,7 +391,7 @@ static int imx_media_inherit_controls(struct imx_media_dev *imxmd,
>  
>  		ret = v4l2_ctrl_add_handler(vfd->ctrl_handler,
>  					    sd->ctrl_handler,
> -					    NULL);
> +					    NULL, true);
>  		if (ret)
>  			return ret;
>  	}
> diff --git a/drivers/staging/media/imx/imx-media-fim.c b/drivers/staging/media/imx/imx-media-fim.c
> index 6df189135db8..8cf773eef9da 100644
> --- a/drivers/staging/media/imx/imx-media-fim.c
> +++ b/drivers/staging/media/imx/imx-media-fim.c
> @@ -463,7 +463,7 @@ int imx_media_fim_add_controls(struct imx_media_fim *fim)
>  {
>  	/* add the FIM controls to the calling subdev ctrl handler */
>  	return v4l2_ctrl_add_handler(fim->sd->ctrl_handler,
> -				     &fim->ctrl_handler, NULL);
> +				     &fim->ctrl_handler, NULL, false);
>  }
>  EXPORT_SYMBOL_GPL(imx_media_fim_add_controls);
>  
> diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
> index f615ba1b29dd..192e31c21faf 100644
> --- a/include/media/v4l2-ctrls.h
> +++ b/include/media/v4l2-ctrls.h
> @@ -247,6 +247,8 @@ struct v4l2_ctrl {
>   * @ctrl:	The actual control information.
>   * @helper:	Pointer to helper struct. Used internally in
>   *		``prepare_ext_ctrls`` function at ``v4l2-ctrl.c``.
> + * @from_other_dev: If true, then @ctrl was defined in another
> + *		device than the &struct v4l2_ctrl_handler.
>   *
>   * Each control handler has a list of these refs. The list_head is used to
>   * keep a sorted-by-control-ID list of all controls, while the next pointer
> @@ -257,6 +259,7 @@ struct v4l2_ctrl_ref {
>  	struct v4l2_ctrl_ref *next;
>  	struct v4l2_ctrl *ctrl;
>  	struct v4l2_ctrl_helper *helper;
> +	bool from_other_dev;
>  };
>  
>  /**
> @@ -633,6 +636,8 @@ typedef bool (*v4l2_ctrl_filter)(const struct v4l2_ctrl *ctrl);
>   * @add:	The control handler whose controls you want to add to
>   *		the @hdl control handler.
>   * @filter:	This function will filter which controls should be added.
> + * @from_other_dev: If true, then the controls in @add were defined in another
> + *		device than @hdl.
>   *
>   * Does nothing if either of the two handlers is a NULL pointer.
>   * If @filter is NULL, then all controls are added. Otherwise only those
> @@ -642,7 +647,8 @@ typedef bool (*v4l2_ctrl_filter)(const struct v4l2_ctrl *ctrl);
>   */
>  int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
>  			  struct v4l2_ctrl_handler *add,
> -			  v4l2_ctrl_filter filter);
> +			  v4l2_ctrl_filter filter,
> +			  bool from_other_dev);
>  
>  /**
>   * v4l2_ctrl_radio_filter() - Standard filter for radio controls.



Thanks,
Mauro

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

* Re: [PATCHv17 11/34] v4l2-ctrls: prepare internal structs for request API
  2018-08-04 12:45 ` [PATCHv17 11/34] v4l2-ctrls: prepare internal structs for request API Hans Verkuil
@ 2018-08-09 20:16   ` Mauro Carvalho Chehab
  2018-08-10  7:41     ` Hans Verkuil
  0 siblings, 1 reply; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-09 20:16 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil, Alexandre Courbot

Em Sat,  4 Aug 2018 14:45:03 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Embed and initialize a media_request_object in struct v4l2_ctrl_handler.
> 
> Add a p_req field to struct v4l2_ctrl_ref that will store the
> request value.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> Signed-off-by: Alexandre Courbot <acourbot@chromium.org>

Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>

> ---
>  drivers/media/v4l2-core/v4l2-ctrls.c |  1 +
>  include/media/v4l2-ctrls.h           | 10 ++++++++++
>  2 files changed, 11 insertions(+)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
> index 404291f00715..b33a8bee82b0 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls.c
> @@ -1901,6 +1901,7 @@ int v4l2_ctrl_handler_init_class(struct v4l2_ctrl_handler *hdl,
>  				      sizeof(hdl->buckets[0]),
>  				      GFP_KERNEL | __GFP_ZERO);
>  	hdl->error = hdl->buckets ? 0 : -ENOMEM;
> +	media_request_object_init(&hdl->req_obj);

I don't like very much the idea of initializing it even when the
request API won't work, e. g. :

	if (!mdev->ops || !mdev->ops->req_validate || !mdev->ops->req_queue)

but I guess it would be too early to check it here, right?

>  	return hdl->error;
>  }
>  EXPORT_SYMBOL(v4l2_ctrl_handler_init_class);
> diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
> index 192e31c21faf..3f4e062d4e3d 100644
> --- a/include/media/v4l2-ctrls.h
> +++ b/include/media/v4l2-ctrls.h
> @@ -20,6 +20,7 @@
>  #include <linux/list.h>
>  #include <linux/mutex.h>
>  #include <linux/videodev2.h>
> +#include <media/media-request.h>
>  
>  /* forward references */
>  struct file;
> @@ -249,6 +250,11 @@ struct v4l2_ctrl {
>   *		``prepare_ext_ctrls`` function at ``v4l2-ctrl.c``.
>   * @from_other_dev: If true, then @ctrl was defined in another
>   *		device than the &struct v4l2_ctrl_handler.
> + * @p_req:	If the control handler containing this control reference
> + *		is bound to a media request, then this points to the
> + *		value of the control that should be applied when the request
> + *		is executed, or to the value of the control at the time
> + *		that the request was completed.
>   *
>   * Each control handler has a list of these refs. The list_head is used to
>   * keep a sorted-by-control-ID list of all controls, while the next pointer
> @@ -260,6 +266,7 @@ struct v4l2_ctrl_ref {
>  	struct v4l2_ctrl *ctrl;
>  	struct v4l2_ctrl_helper *helper;
>  	bool from_other_dev;
> +	union v4l2_ctrl_ptr p_req;
>  };
>  
>  /**
> @@ -283,6 +290,8 @@ struct v4l2_ctrl_ref {
>   * @notify_priv: Passed as argument to the v4l2_ctrl notify callback.
>   * @nr_of_buckets: Total number of buckets in the array.
>   * @error:	The error code of the first failed control addition.
> + * @req_obj:	The &struct media_request_object, used to link into a
> + *		&struct media_request. This request object has a refcount.
>   */
>  struct v4l2_ctrl_handler {
>  	struct mutex _lock;
> @@ -295,6 +304,7 @@ struct v4l2_ctrl_handler {
>  	void *notify_priv;
>  	u16 nr_of_buckets;
>  	int error;
> +	struct media_request_object req_obj;
>  };
>  
>  /**



Thanks,
Mauro

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

* Re: [PATCHv17 12/34] v4l2-ctrls: alloc memory for p_req
  2018-08-04 12:45 ` [PATCHv17 12/34] v4l2-ctrls: alloc memory for p_req Hans Verkuil
@ 2018-08-09 20:19   ` Mauro Carvalho Chehab
  2018-08-10  7:41     ` Hans Verkuil
  0 siblings, 1 reply; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-09 20:19 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:45:04 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> To store request data the handler_new_ref() allocates memory
> for it if needed.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  drivers/media/v4l2-core/v4l2-ctrls.c | 20 ++++++++++++++++----
>  1 file changed, 16 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
> index b33a8bee82b0..171ab389afdd 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls.c
> @@ -2018,13 +2018,18 @@ EXPORT_SYMBOL(v4l2_ctrl_find);
>  /* Allocate a new v4l2_ctrl_ref and hook it into the handler. */
>  static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
>  			   struct v4l2_ctrl *ctrl,
> -			   bool from_other_dev)
> +			   struct v4l2_ctrl_ref **ctrl_ref,
> +			   bool from_other_dev, bool allocate_req)
>  {
>  	struct v4l2_ctrl_ref *ref;
>  	struct v4l2_ctrl_ref *new_ref;
>  	u32 id = ctrl->id;
>  	u32 class_ctrl = V4L2_CTRL_ID2WHICH(id) | 1;
>  	int bucket = id % hdl->nr_of_buckets;	/* which bucket to use */
> +	unsigned int sz_extra = 0;

Nitpick: I would name it size_extra_req, to make clear its usage.

Once renamed:

Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>

> +
> +	if (ctrl_ref)
> +		*ctrl_ref = NULL;
>  
>  	/*
>  	 * Automatically add the control class if it is not yet present and
> @@ -2038,11 +2043,16 @@ static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
>  	if (hdl->error)
>  		return hdl->error;
>  
> -	new_ref = kzalloc(sizeof(*new_ref), GFP_KERNEL);
> +	if (allocate_req)
> +		sz_extra = ctrl->elems * ctrl->elem_size;
> +	new_ref = kzalloc(sizeof(*new_ref) + sz_extra, GFP_KERNEL);
>  	if (!new_ref)
>  		return handler_set_err(hdl, -ENOMEM);
>  	new_ref->ctrl = ctrl;
>  	new_ref->from_other_dev = from_other_dev;
> +	if (sz_extra)
> +		new_ref->p_req.p = &new_ref[1];
> +
>  	if (ctrl->handler == hdl) {
>  		/* By default each control starts in a cluster of its own.
>  		   new_ref->ctrl is basically a cluster array with one
> @@ -2082,6 +2092,8 @@ static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
>  	/* Insert the control node in the hash */
>  	new_ref->next = hdl->buckets[bucket];
>  	hdl->buckets[bucket] = new_ref;
> +	if (ctrl_ref)
> +		*ctrl_ref = new_ref;
>  
>  unlock:
>  	mutex_unlock(hdl->lock);
> @@ -2223,7 +2235,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>  		ctrl->type_ops->init(ctrl, idx, ctrl->p_new);
>  	}
>  
> -	if (handler_new_ref(hdl, ctrl, false)) {
> +	if (handler_new_ref(hdl, ctrl, NULL, false, false)) {
>  		kvfree(ctrl);
>  		return NULL;
>  	}
> @@ -2416,7 +2428,7 @@ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
>  		/* Filter any unwanted controls */
>  		if (filter && !filter(ctrl))
>  			continue;
> -		ret = handler_new_ref(hdl, ctrl, from_other_dev);
> +		ret = handler_new_ref(hdl, ctrl, NULL, from_other_dev, false);
>  		if (ret)
>  			break;
>  	}



Thanks,
Mauro

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

* Re: [PATCHv17 13/34] v4l2-ctrls: use ref in helper instead of ctrl
  2018-08-04 12:45 ` [PATCHv17 13/34] v4l2-ctrls: use ref in helper instead of ctrl Hans Verkuil
@ 2018-08-09 20:22   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-09 20:22 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:45:05 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> The next patch needs the reference to a control instead of the
> control itself, so change struct v4l2_ctrl_helper accordingly.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>

Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
> ---
>  drivers/media/v4l2-core/v4l2-ctrls.c | 18 +++++++++---------
>  1 file changed, 9 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
> index 171ab389afdd..570b6f8ae46a 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls.c
> @@ -37,8 +37,8 @@
>  struct v4l2_ctrl_helper {
>  	/* Pointer to the control reference of the master control */
>  	struct v4l2_ctrl_ref *mref;
> -	/* The control corresponding to the v4l2_ext_control ID field. */
> -	struct v4l2_ctrl *ctrl;
> +	/* The control ref corresponding to the v4l2_ext_control ID field. */
> +	struct v4l2_ctrl_ref *ref;
>  	/* v4l2_ext_control index of the next control belonging to the
>  	   same cluster, or 0 if there isn't any. */

It sounds a good idea to convert the documentation of this struct to the
kernel-doc syntax. Suggestion for a future patch (out of this series).

>  	u32 next;
> @@ -2908,6 +2908,7 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
>  		ref = find_ref_lock(hdl, id);
>  		if (ref == NULL)
>  			return -EINVAL;
> +		h->ref = ref;
>  		ctrl = ref->ctrl;
>  		if (ctrl->flags & V4L2_CTRL_FLAG_DISABLED)
>  			return -EINVAL;
> @@ -2930,7 +2931,6 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
>  		}
>  		/* Store the ref to the master control of the cluster */
>  		h->mref = ref;
> -		h->ctrl = ctrl;
>  		/* Initially set next to 0, meaning that there is no other
>  		   control in this helper array belonging to the same
>  		   cluster */
> @@ -3015,7 +3015,7 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
>  	cs->error_idx = cs->count;
>  
>  	for (i = 0; !ret && i < cs->count; i++)
> -		if (helpers[i].ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
> +		if (helpers[i].ref->ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
>  			ret = -EACCES;
>  
>  	for (i = 0; !ret && i < cs->count; i++) {
> @@ -3050,7 +3050,7 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
>  
>  			do {
>  				ret = ctrl_to_user(cs->controls + idx,
> -						   helpers[idx].ctrl);
> +						   helpers[idx].ref->ctrl);
>  				idx = helpers[idx].next;
>  			} while (!ret && idx);
>  		}
> @@ -3202,7 +3202,7 @@ static int validate_ctrls(struct v4l2_ext_controls *cs,
>  
>  	cs->error_idx = cs->count;
>  	for (i = 0; i < cs->count; i++) {
> -		struct v4l2_ctrl *ctrl = helpers[i].ctrl;
> +		struct v4l2_ctrl *ctrl = helpers[i].ref->ctrl;
>  		union v4l2_ctrl_ptr p_new;
>  
>  		cs->error_idx = i;
> @@ -3314,7 +3314,7 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
>  			do {
>  				/* Check if the auto control is part of the
>  				   list, and remember the new value. */
> -				if (helpers[tmp_idx].ctrl == master)
> +				if (helpers[tmp_idx].ref->ctrl == master)
>  					new_auto_val = cs->controls[tmp_idx].value;
>  				tmp_idx = helpers[tmp_idx].next;
>  			} while (tmp_idx);
> @@ -3327,7 +3327,7 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
>  		/* Copy the new caller-supplied control values.
>  		   user_to_new() sets 'is_new' to 1. */
>  		do {
> -			struct v4l2_ctrl *ctrl = helpers[idx].ctrl;
> +			struct v4l2_ctrl *ctrl = helpers[idx].ref->ctrl;
>  
>  			ret = user_to_new(cs->controls + idx, ctrl);
>  			if (!ret && ctrl->is_ptr)
> @@ -3343,7 +3343,7 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
>  			idx = i;
>  			do {
>  				ret = new_to_user(cs->controls + idx,
> -						helpers[idx].ctrl);
> +						helpers[idx].ref->ctrl);
>  				idx = helpers[idx].next;
>  			} while (!ret && idx);
>  		}



Thanks,
Mauro

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

* Re: [PATCHv17 01/34] Documentation: v4l: document request API
  2018-08-09 17:43   ` Mauro Carvalho Chehab
@ 2018-08-10  7:20     ` Hans Verkuil
  2018-08-14  8:21       ` Mauro Carvalho Chehab
  2018-08-14  7:57     ` Hans Verkuil
  1 sibling, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-10  7:20 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media, Alexandre Courbot, Hans Verkuil

On 08/09/2018 07:43 PM, Mauro Carvalho Chehab wrote:
> Em Sat,  4 Aug 2018 14:44:53 +0200
> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> 
>> From: Alexandre Courbot <acourbot@chromium.org>
>>
>> Document the request API for V4L2 devices, and amend the documentation
>> of system calls influenced by it.
> 
> It follows some comments. Most are nitpicks. There are just two ones
> that aren't:
> 	- a problem at the tables changes on Documentation/
> 	- a question with regards to MEDIA_IOC_REQUEST_ALLOC ioctl.

I'll fix all the smaller comments and in this reply only address these
two topics.

> 
> I'll keep reviewing this patch series.
> 
> PS.: I lost entirely my first review to this doc... I hope I didn't
> forget anything when re-typing my comments.
> 
>>
>> Signed-off-by: Alexandre Courbot <acourbot@chromium.org>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> ---
>>  .../media/uapi/mediactl/media-controller.rst  |   1 +
>>  .../media/uapi/mediactl/media-funcs.rst       |   6 +
>>  .../uapi/mediactl/media-ioc-request-alloc.rst |  78 ++++++
>>  .../uapi/mediactl/media-request-ioc-queue.rst |  82 ++++++
>>  .../mediactl/media-request-ioc-reinit.rst     |  51 ++++
>>  .../media/uapi/mediactl/request-api.rst       | 247 ++++++++++++++++++
>>  .../uapi/mediactl/request-func-close.rst      |  49 ++++
>>  .../uapi/mediactl/request-func-ioctl.rst      |  68 +++++
>>  .../media/uapi/mediactl/request-func-poll.rst |  77 ++++++
>>  Documentation/media/uapi/v4l/buffer.rst       |  21 +-
>>  .../media/uapi/v4l/vidioc-g-ext-ctrls.rst     |  94 ++++---
>>  Documentation/media/uapi/v4l/vidioc-qbuf.rst  |  32 ++-
>>  .../media/videodev2.h.rst.exceptions          |   1 +
>>  13 files changed, 771 insertions(+), 36 deletions(-)
>>  create mode 100644 Documentation/media/uapi/mediactl/media-ioc-request-alloc.rst
>>  create mode 100644 Documentation/media/uapi/mediactl/media-request-ioc-queue.rst
>>  create mode 100644 Documentation/media/uapi/mediactl/media-request-ioc-reinit.rst
>>  create mode 100644 Documentation/media/uapi/mediactl/request-api.rst
>>  create mode 100644 Documentation/media/uapi/mediactl/request-func-close.rst
>>  create mode 100644 Documentation/media/uapi/mediactl/request-func-ioctl.rst
>>  create mode 100644 Documentation/media/uapi/mediactl/request-func-poll.rst
>>

<snip>

>> +.. c:type:: media_request_alloc
>> +
>> +.. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.7cm}|
>> +
>> +.. flat-table:: struct media_request_alloc
>> +    :header-rows:  0
>> +    :stub-columns: 0
>> +    :widths:       1 1 2
>> +
>> +    *  -  __s32
>> +       -  ``fd``
>> +       -  The file descriptor of the request.
> 
> It should be mentioned that the struct should be zeroed before calling
> the Kernel, but I is overkill to have a struct to pass just one value.
> 
> I mean, if this has just one value inside the struct, it is a way better
> to declare it as:
> 
> .. c:function:: int ioctl( int fd, MEDIA_IOC_REQUEST_ALLOC, s32 &argp )
> 
> Even if we later need more stuff, the size of a new MEDIA_IOC_REQUEST_ALLOC
> will be bigger, and then (and only then) we can add extra stuff.
> 
> Or are you foreseen any new fields there in short term?

The first version just had a s32 argument, not a struct. The main reason for
going back to a struct was indeed to make it easier to add new fields in the
future. I don't foresee any, but then, you never do.

I don't have a particularly strong opinion on this one way or another, but
if we change it back to a s32 argument, then I want the opinion of others as
well.

<snip>

>> @@ -110,15 +130,13 @@ still cause this situation.
>>  .. flat-table:: struct v4l2_ext_control
>>      :header-rows:  0
>>      :stub-columns: 0
>> -    :widths:       1 1 1 2
>> +    :widths:       1 1 3
> 
> This is wrong: you can't change widths without changing the preceeding
> .. tabularcolumns tag.
> 
> You probably didn't test PDF generation for this table.
> 
> Also, the change is this table doesn't belong to this patch. It is
> a (doubtful) optimization at the table, not related to requests API.
> 
>>  
>>      * - __u32
>>        - ``id``
>> -      -
>>        - Identifies the control, set by the application.
>>      * - __u32
>>        - ``size``
>> -      -
>>        - The total size in bytes of the payload of this control. This is
>>  	normally 0, but for pointer controls this should be set to the
>>  	size of the memory containing the payload, or that will receive
>> @@ -135,51 +153,43 @@ still cause this situation.
>>  	   *length* of the string may well be much smaller.
>>      * - __u32
>>        - ``reserved2``\ [1]
>> -      -
>>        - Reserved for future extensions. Drivers and applications must set
>>  	the array to zero.
>> -    * - union
>> +    * - union {
> 
> Adding { and } at the end sounds ok...
> 
>>        - (anonymous)
>> -    * -
>> -      - __s32
>> +    * - __s32
>>        - ``value``
>>        - New value or current value. Valid if this control is not of type
>>  	``V4L2_CTRL_TYPE_INTEGER64`` and ``V4L2_CTRL_FLAG_HAS_PAYLOAD`` is
>>  	not set.
>> -    * -
>> -      - __s64
>> +    * - __s64
>>        - ``value64``
>>        - New value or current value. Valid if this control is of type
>>  	``V4L2_CTRL_TYPE_INTEGER64`` and ``V4L2_CTRL_FLAG_HAS_PAYLOAD`` is
>>  	not set.
>> -    * -
>> -      - char *
>> +    * - char *
>>        - ``string``
>>        - A pointer to a string. Valid if this control is of type
>>  	``V4L2_CTRL_TYPE_STRING``.
>> -    * -
>> -      - __u8 *
>> +    * - __u8 *
>>        - ``p_u8``
>>        - A pointer to a matrix control of unsigned 8-bit values. Valid if
>>  	this control is of type ``V4L2_CTRL_TYPE_U8``.
>> -    * -
>> -      - __u16 *
>> +    * - __u16 *
>>        - ``p_u16``
>>        - A pointer to a matrix control of unsigned 16-bit values. Valid if
>>  	this control is of type ``V4L2_CTRL_TYPE_U16``.
>> -    * -
>> -      - __u32 *
>> +    * - __u32 *
>>        - ``p_u32``
>>        - A pointer to a matrix control of unsigned 32-bit values. Valid if
>>  	this control is of type ``V4L2_CTRL_TYPE_U32``.
>> -    * -
>> -      - void *
>> +    * - void *
>>        - ``ptr``
>>        - A pointer to a compound type which can be an N-dimensional array
>>  	and/or a compound type (the control's type is >=
>>  	``V4L2_CTRL_COMPOUND_TYPES``). Valid if
>>  	``V4L2_CTRL_FLAG_HAS_PAYLOAD`` is set for this control.
>> -
>> +    * - }
> 
> ... however, removing the extra cell is not, because it will break the
> indent inside the union's elements. The best way to keep them indented
> is to use a separate cell (at least on DocBook). 
> 
> You could come with some other solution that would add a fixed amount
> of spaces for all the elements inside the union, but I guess the
> easiest way to do that is by having a separate column.

The problem is that that extra cell makes the table hard to read: the last column
with the actual description gets squashed leading to narrow hard to read columns.

The only reason for doing this is a stupid union.

I'll experiment a bit more with this.

> 
>>  
>>  .. tabularcolumns:: |p{4.0cm}|p{2.2cm}|p{2.1cm}|p{8.2cm}|
>>  
>> @@ -190,12 +200,11 @@ still cause this situation.
>>  .. flat-table:: struct v4l2_ext_controls
>>      :header-rows:  0
>>      :stub-columns: 0
>> -    :widths:       1 1 2 1
>> +    :widths:       1 1 3
> 
> Same comments I made for the past table apply here as well.

Regards,

	Hans

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

* Re: [PATCHv17 02/34] uapi/linux/media.h: add request API
  2018-08-09 17:53   ` Mauro Carvalho Chehab
@ 2018-08-10  7:21     ` Hans Verkuil
  2018-08-14  8:46       ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-10  7:21 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media, Hans Verkuil

On 08/09/2018 07:53 PM, Mauro Carvalho Chehab wrote:
> Em Sat,  4 Aug 2018 14:44:54 +0200
> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> 
>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>
>> Define the public request API.
>>
>> This adds the new MEDIA_IOC_REQUEST_ALLOC ioctl to allocate a request
>> and two ioctls that operate on a request in order to queue the
>> contents of the request to the driver and to re-initialize the
>> request.
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>> ---
>>  include/uapi/linux/media.h | 12 ++++++++++++
>>  1 file changed, 12 insertions(+)
>>
>> diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h
>> index 36f76e777ef9..cf77f00a0f2d 100644
>> --- a/include/uapi/linux/media.h
>> +++ b/include/uapi/linux/media.h
>> @@ -364,11 +364,23 @@ struct media_v2_topology {
>>  
>>  /* ioctls */
>>  
>> +struct __attribute__ ((packed)) media_request_alloc {
>> +	__s32 fd;
>> +};
>> +
>>  #define MEDIA_IOC_DEVICE_INFO	_IOWR('|', 0x00, struct media_device_info)
>>  #define MEDIA_IOC_ENUM_ENTITIES	_IOWR('|', 0x01, struct media_entity_desc)
>>  #define MEDIA_IOC_ENUM_LINKS	_IOWR('|', 0x02, struct media_links_enum)
>>  #define MEDIA_IOC_SETUP_LINK	_IOWR('|', 0x03, struct media_link_desc)
>>  #define MEDIA_IOC_G_TOPOLOGY	_IOWR('|', 0x04, struct media_v2_topology)
>> +#define MEDIA_IOC_REQUEST_ALLOC	_IOWR('|', 0x05, struct media_request_alloc)
> 
> Same comment as in patch 1: keep it simpler: just pass a s32 * as the
> argument for this ioctl.

Same comment as in patch 1: I have no strong opinion, but I want the input from others
as well.

Regards,

	Hans

> 
>> +
>> +/*
>> + * These ioctls are called on the request file descriptor as returned
>> + * by MEDIA_IOC_REQUEST_ALLOC.
>> + */
>> +#define MEDIA_REQUEST_IOC_QUEUE		_IO('|',  0x80)
>> +#define MEDIA_REQUEST_IOC_REINIT	_IO('|',  0x81)
>>  
>>  #ifndef __KERNEL__
>>  
> 
> 
> 
> Thanks,
> Mauro
> 

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

* Re: [PATCHv17 03/34] media-request: implement media requests
  2018-08-09 18:37   ` Mauro Carvalho Chehab
@ 2018-08-10  7:28     ` Hans Verkuil
  0 siblings, 0 replies; 106+ messages in thread
From: Hans Verkuil @ 2018-08-10  7:28 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media, Hans Verkuil, Sakari Ailus

On 08/09/2018 08:37 PM, Mauro Carvalho Chehab wrote:
>> +	get_task_comm(comm, current);
>> +	snprintf(req->debug_str, sizeof(req->debug_str), "%s:%d",
>> +		 comm, fd);
> 
> I'm pretty sure we've discussed about get_task_comm(). I don't think 
> we should use it, as the task with queues can be different than
> the one with dequeues. Instead, just give an unique ID for each
> request. That will allow tracking it in a better way, no matter how
> the userspace app is encoded.

The original discussion went back-and-forth a bit, but I'll just
replace it with a unique ID.

> Also, for dynamic debugs, the task ID can easily be obtained by
> passing a parameter to it, with +t:
> 
> 	echo "file media_request.c +t" > /sys/kernel/debug/dynamic_debug/control
> 
> or, at the boot time with:
> 	dyndbg="file media_request.c +t"
> 
> So, Kernel shouldn't be bothered by having such hacks hardcoded
> at the code.

Regards,

	Hans

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

* Re: [PATCHv17 05/34] media-request: add media_request_get_by_fd
  2018-08-09 19:55   ` Mauro Carvalho Chehab
@ 2018-08-10  7:32     ` Hans Verkuil
  2018-08-14  9:00       ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-10  7:32 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media, Hans Verkuil

On 08/09/2018 09:55 PM, Mauro Carvalho Chehab wrote:
> Em Sat,  4 Aug 2018 14:44:57 +0200
> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> 
>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>
>> Add media_request_get_by_fd() to find a request based on the file
>> descriptor.
>>
>> The caller has to call media_request_put() for the returned
>> request since this function increments the refcount.
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
>> ---
>>  drivers/media/media-request.c | 40 +++++++++++++++++++++++++++++++++++
>>  include/media/media-request.h | 24 +++++++++++++++++++++
>>  2 files changed, 64 insertions(+)
>>
>> diff --git a/drivers/media/media-request.c b/drivers/media/media-request.c
>> index 253068f51a1f..4b523f3a03a3 100644
>> --- a/drivers/media/media-request.c
>> +++ b/drivers/media/media-request.c
>> @@ -231,6 +231,46 @@ static const struct file_operations request_fops = {
>>  	.release = media_request_close,
>>  };
>>  
>> +struct media_request *
>> +media_request_get_by_fd(struct media_device *mdev, int request_fd)
>> +{
>> +	struct file *filp;
>> +	struct media_request *req;
>> +
>> +	if (!mdev || !mdev->ops ||
>> +	    !mdev->ops->req_validate || !mdev->ops->req_queue)
>> +		return ERR_PTR(-EPERM);
> 
> EPERM? I guess ENOTTY would be better.
> 
> Any reason why using EPERM?

This is called by e.g. VIDIOC_QBUF or VIDIOC_S/G/TRY_EXT_CTRLS where someone
sets request_fd. Then this function is called to obtain the corresponding
media_request struct. If requests are not supported, then EPERM is returned.

Returning ENOTTY would be wrong, since VIDIOC_QBUF etc. are definitely implemented,
instead they just do not permit the use of requests.

Let me know if I can add your Reviewed-by after this explanation.

Regards,

	Hans

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

* Re: [PATCHv17 08/34] v4l2-dev: lock req_queue_mutex
  2018-08-09 20:03   ` Mauro Carvalho Chehab
@ 2018-08-10  7:39     ` Hans Verkuil
  2018-08-14  8:09       ` Mauro Carvalho Chehab
  2018-08-14 12:00     ` Hans Verkuil
  1 sibling, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-10  7:39 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media, Hans Verkuil, Sakari Ailus

On 08/09/2018 10:03 PM, Mauro Carvalho Chehab wrote:
> Em Sat,  4 Aug 2018 14:45:00 +0200
> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> 
>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>
>> We need to serialize streamon/off with queueing new requests.
>> These ioctls may trigger the cancellation of a streaming
>> operation, and that should not be mixed with queuing a new
>> request at the same time.
>>
>> Finally close() needs this lock since that too can trigger the
>> cancellation of a streaming operation.
>>
>> We take the req_queue_mutex here before any other locks since
>> it is a very high-level lock.
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
>> ---
>>  drivers/media/v4l2-core/v4l2-dev.c   | 13 +++++++++++++
>>  drivers/media/v4l2-core/v4l2-ioctl.c | 22 +++++++++++++++++++++-
>>  2 files changed, 34 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
>> index 69e775930fc4..53018e4a4c78 100644
>> --- a/drivers/media/v4l2-core/v4l2-dev.c
>> +++ b/drivers/media/v4l2-core/v4l2-dev.c
>> @@ -444,8 +444,21 @@ static int v4l2_release(struct inode *inode, struct file *filp)
>>  	struct video_device *vdev = video_devdata(filp);
>>  	int ret = 0;
>>  
>> +	/*
>> +	 * We need to serialize the release() with queueing new requests.
>> +	 * The release() may trigger the cancellation of a streaming
>> +	 * operation, and that should not be mixed with queueing a new
>> +	 * request at the same time.
>> +	 */
>> +	if (v4l2_device_supports_requests(vdev->v4l2_dev))
>> +		mutex_lock(&vdev->v4l2_dev->mdev->req_queue_mutex);
>> +
>>  	if (vdev->fops->release)
>>  		ret = vdev->fops->release(filp);
>> +
>> +	if (v4l2_device_supports_requests(vdev->v4l2_dev))
>> +		mutex_unlock(&vdev->v4l2_dev->mdev->req_queue_mutex);
>> +
> 
> This will very likely generate sparse warnings. See my discussions
> with that regards with Linus.
> 
> The only way to avoid it would be to do something like:
> 
> 	if (v4l2_device_supports_requests(vdev->v4l2_dev)) {
> 		mutex_lock(&vdev->v4l2_dev->mdev->req_queue_mutex);
> 	 	if (vdev->fops->release)
> 			ret = vdev->fops->release(filp);
> 		mutex_unlock(&vdev->v4l2_dev->mdev->req_queue_mutex);
> 	} else {
> 	 	if (vdev->fops->release)
> 			ret = vdev->fops->release(filp);
> 	}

I'll check what sparse says and make this change if needed (I hate
working around sparse warnings).

> 
>>  	if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
>>  		dprintk("%s: release\n",
>>  			video_device_node_name(vdev));
>> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
>> index 54afc9c7ee6e..ea475d833dd6 100644
>> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
>> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
>> @@ -2780,6 +2780,7 @@ static long __video_do_ioctl(struct file *file,
>>  		unsigned int cmd, void *arg)
>>  {
>>  	struct video_device *vfd = video_devdata(file);
>> +	struct mutex *req_queue_lock = NULL;
>>  	struct mutex *lock; /* ioctl serialization mutex */
>>  	const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
>>  	bool write_only = false;
>> @@ -2799,10 +2800,27 @@ static long __video_do_ioctl(struct file *file,
>>  	if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags))
>>  		vfh = file->private_data;
>>  
>> +	/*
>> +	 * We need to serialize streamon/off with queueing new requests.
>> +	 * These ioctls may trigger the cancellation of a streaming
>> +	 * operation, and that should not be mixed with queueing a new
>> +	 * request at the same time.
>> +	 */
>> +	if (v4l2_device_supports_requests(vfd->v4l2_dev) &&
>> +	    (cmd == VIDIOC_STREAMON || cmd == VIDIOC_STREAMOFF)) {
>> +		req_queue_lock = &vfd->v4l2_dev->mdev->req_queue_mutex;
>> +
>> +		if (mutex_lock_interruptible(req_queue_lock))
>> +			return -ERESTARTSYS;
>> +	}
>> +
>>  	lock = v4l2_ioctl_get_lock(vfd, vfh, cmd, arg);
>>  
>> -	if (lock && mutex_lock_interruptible(lock))
>> +	if (lock && mutex_lock_interruptible(lock)) {
>> +		if (req_queue_lock)
>> +			mutex_unlock(req_queue_lock);
>>  		return -ERESTARTSYS;
>> +	}
> 
> Same applies here.

I'm not sure there is much that can be done here without making the
code hard to read. I'll see.

> 
>>  
>>  	if (!video_is_registered(vfd)) {
>>  		ret = -ENODEV;
>> @@ -2861,6 +2879,8 @@ static long __video_do_ioctl(struct file *file,
>>  unlock:
>>  	if (lock)
>>  		mutex_unlock(lock);
>> +	if (req_queue_lock)
>> +		mutex_unlock(req_queue_lock);
> 
> This code looks really weird! are you locking in order to get a
> lock pointer?
> 
> That seems broken by design.

I've no idea what you mean. Both 'lock' and 'req_queue_lock' are pointers to
a struct mutex. If NULL, don't unlock, otherwise you need to unlock the mutex
here since it was locked earlier.

Did you misread this or should the lock/req_queue_lock names be changed to e.g.
mutex_ptr/req_queue_mutex_ptr?

Regards,

	Hans

> 
>>  	return ret;
>>  }
>>  
> 
> 
> 
> Thanks,
> Mauro
> 

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

* Re: [PATCHv17 11/34] v4l2-ctrls: prepare internal structs for request API
  2018-08-09 20:16   ` Mauro Carvalho Chehab
@ 2018-08-10  7:41     ` Hans Verkuil
  0 siblings, 0 replies; 106+ messages in thread
From: Hans Verkuil @ 2018-08-10  7:41 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media, Hans Verkuil, Alexandre Courbot

On 08/09/2018 10:16 PM, Mauro Carvalho Chehab wrote:
> Em Sat,  4 Aug 2018 14:45:03 +0200
> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> 
>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>
>> Embed and initialize a media_request_object in struct v4l2_ctrl_handler.
>>
>> Add a p_req field to struct v4l2_ctrl_ref that will store the
>> request value.
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> Signed-off-by: Alexandre Courbot <acourbot@chromium.org>
> 
> Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
> 
>> ---
>>  drivers/media/v4l2-core/v4l2-ctrls.c |  1 +
>>  include/media/v4l2-ctrls.h           | 10 ++++++++++
>>  2 files changed, 11 insertions(+)
>>
>> diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
>> index 404291f00715..b33a8bee82b0 100644
>> --- a/drivers/media/v4l2-core/v4l2-ctrls.c
>> +++ b/drivers/media/v4l2-core/v4l2-ctrls.c
>> @@ -1901,6 +1901,7 @@ int v4l2_ctrl_handler_init_class(struct v4l2_ctrl_handler *hdl,
>>  				      sizeof(hdl->buckets[0]),
>>  				      GFP_KERNEL | __GFP_ZERO);
>>  	hdl->error = hdl->buckets ? 0 : -ENOMEM;
>> +	media_request_object_init(&hdl->req_obj);
> 
> I don't like very much the idea of initializing it even when the
> request API won't work, e. g. :
> 
> 	if (!mdev->ops || !mdev->ops->req_validate || !mdev->ops->req_queue)
> 
> but I guess it would be too early to check it here, right?

Correct.

Regards,

	Hans

> 
>>  	return hdl->error;
>>  }
>>  EXPORT_SYMBOL(v4l2_ctrl_handler_init_class);
>> diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
>> index 192e31c21faf..3f4e062d4e3d 100644
>> --- a/include/media/v4l2-ctrls.h
>> +++ b/include/media/v4l2-ctrls.h
>> @@ -20,6 +20,7 @@
>>  #include <linux/list.h>
>>  #include <linux/mutex.h>
>>  #include <linux/videodev2.h>
>> +#include <media/media-request.h>
>>  
>>  /* forward references */
>>  struct file;
>> @@ -249,6 +250,11 @@ struct v4l2_ctrl {
>>   *		``prepare_ext_ctrls`` function at ``v4l2-ctrl.c``.
>>   * @from_other_dev: If true, then @ctrl was defined in another
>>   *		device than the &struct v4l2_ctrl_handler.
>> + * @p_req:	If the control handler containing this control reference
>> + *		is bound to a media request, then this points to the
>> + *		value of the control that should be applied when the request
>> + *		is executed, or to the value of the control at the time
>> + *		that the request was completed.
>>   *
>>   * Each control handler has a list of these refs. The list_head is used to
>>   * keep a sorted-by-control-ID list of all controls, while the next pointer
>> @@ -260,6 +266,7 @@ struct v4l2_ctrl_ref {
>>  	struct v4l2_ctrl *ctrl;
>>  	struct v4l2_ctrl_helper *helper;
>>  	bool from_other_dev;
>> +	union v4l2_ctrl_ptr p_req;
>>  };
>>  
>>  /**
>> @@ -283,6 +290,8 @@ struct v4l2_ctrl_ref {
>>   * @notify_priv: Passed as argument to the v4l2_ctrl notify callback.
>>   * @nr_of_buckets: Total number of buckets in the array.
>>   * @error:	The error code of the first failed control addition.
>> + * @req_obj:	The &struct media_request_object, used to link into a
>> + *		&struct media_request. This request object has a refcount.
>>   */
>>  struct v4l2_ctrl_handler {
>>  	struct mutex _lock;
>> @@ -295,6 +304,7 @@ struct v4l2_ctrl_handler {
>>  	void *notify_priv;
>>  	u16 nr_of_buckets;
>>  	int error;
>> +	struct media_request_object req_obj;
>>  };
>>  
>>  /**
> 
> 
> 
> Thanks,
> Mauro
> 

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

* Re: [PATCHv17 12/34] v4l2-ctrls: alloc memory for p_req
  2018-08-09 20:19   ` Mauro Carvalho Chehab
@ 2018-08-10  7:41     ` Hans Verkuil
  0 siblings, 0 replies; 106+ messages in thread
From: Hans Verkuil @ 2018-08-10  7:41 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media, Hans Verkuil

On 08/09/2018 10:19 PM, Mauro Carvalho Chehab wrote:
> Em Sat,  4 Aug 2018 14:45:04 +0200
> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> 
>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>
>> To store request data the handler_new_ref() allocates memory
>> for it if needed.
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> ---
>>  drivers/media/v4l2-core/v4l2-ctrls.c | 20 ++++++++++++++++----
>>  1 file changed, 16 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
>> index b33a8bee82b0..171ab389afdd 100644
>> --- a/drivers/media/v4l2-core/v4l2-ctrls.c
>> +++ b/drivers/media/v4l2-core/v4l2-ctrls.c
>> @@ -2018,13 +2018,18 @@ EXPORT_SYMBOL(v4l2_ctrl_find);
>>  /* Allocate a new v4l2_ctrl_ref and hook it into the handler. */
>>  static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
>>  			   struct v4l2_ctrl *ctrl,
>> -			   bool from_other_dev)
>> +			   struct v4l2_ctrl_ref **ctrl_ref,
>> +			   bool from_other_dev, bool allocate_req)
>>  {
>>  	struct v4l2_ctrl_ref *ref;
>>  	struct v4l2_ctrl_ref *new_ref;
>>  	u32 id = ctrl->id;
>>  	u32 class_ctrl = V4L2_CTRL_ID2WHICH(id) | 1;
>>  	int bucket = id % hdl->nr_of_buckets;	/* which bucket to use */
>> +	unsigned int sz_extra = 0;
> 
> Nitpick: I would name it size_extra_req, to make clear its usage.

OK, makes sense.

Regards,

	Hans

> Once renamed:
> 
> Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
> 
>> +
>> +	if (ctrl_ref)
>> +		*ctrl_ref = NULL;
>>  
>>  	/*
>>  	 * Automatically add the control class if it is not yet present and
>> @@ -2038,11 +2043,16 @@ static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
>>  	if (hdl->error)
>>  		return hdl->error;
>>  
>> -	new_ref = kzalloc(sizeof(*new_ref), GFP_KERNEL);
>> +	if (allocate_req)
>> +		sz_extra = ctrl->elems * ctrl->elem_size;
>> +	new_ref = kzalloc(sizeof(*new_ref) + sz_extra, GFP_KERNEL);
>>  	if (!new_ref)
>>  		return handler_set_err(hdl, -ENOMEM);
>>  	new_ref->ctrl = ctrl;
>>  	new_ref->from_other_dev = from_other_dev;
>> +	if (sz_extra)
>> +		new_ref->p_req.p = &new_ref[1];
>> +
>>  	if (ctrl->handler == hdl) {
>>  		/* By default each control starts in a cluster of its own.
>>  		   new_ref->ctrl is basically a cluster array with one
>> @@ -2082,6 +2092,8 @@ static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
>>  	/* Insert the control node in the hash */
>>  	new_ref->next = hdl->buckets[bucket];
>>  	hdl->buckets[bucket] = new_ref;
>> +	if (ctrl_ref)
>> +		*ctrl_ref = new_ref;
>>  
>>  unlock:
>>  	mutex_unlock(hdl->lock);
>> @@ -2223,7 +2235,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>>  		ctrl->type_ops->init(ctrl, idx, ctrl->p_new);
>>  	}
>>  
>> -	if (handler_new_ref(hdl, ctrl, false)) {
>> +	if (handler_new_ref(hdl, ctrl, NULL, false, false)) {
>>  		kvfree(ctrl);
>>  		return NULL;
>>  	}
>> @@ -2416,7 +2428,7 @@ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
>>  		/* Filter any unwanted controls */
>>  		if (filter && !filter(ctrl))
>>  			continue;
>> -		ret = handler_new_ref(hdl, ctrl, from_other_dev);
>> +		ret = handler_new_ref(hdl, ctrl, NULL, from_other_dev, false);
>>  		if (ret)
>>  			break;
>>  	}
> 
> 
> 
> Thanks,
> Mauro
> 

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

* Re: [PATCHv17 14/34] v4l2-ctrls: add core request support
  2018-08-04 12:45 ` [PATCHv17 14/34] v4l2-ctrls: add core request support Hans Verkuil
@ 2018-08-13 10:55   ` Mauro Carvalho Chehab
  2018-08-14  8:34     ` Hans Verkuil
  0 siblings, 1 reply; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-13 10:55 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:45:06 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Integrate the request support. This adds the v4l2_ctrl_request_complete
> and v4l2_ctrl_request_setup functions to complete a request and (as a
> helper function) to apply a request to the hardware.
> 
> It takes care of queuing requests and correctly chaining control values
> in the request queue.
> 
> Note that when a request is marked completed it will copy control values
> to the internal request state. This can be optimized in the future since
> this is sub-optimal when dealing with large compound and/or array controls.
> 
> For the initial 'stateless codec' use-case the current implementation is
> sufficient.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  drivers/media/v4l2-core/v4l2-ctrls.c | 326 ++++++++++++++++++++++++++-
>  include/media/v4l2-ctrls.h           |  51 +++++
>  2 files changed, 371 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
> index 570b6f8ae46a..b8ff6d6b14cd 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls.c
> @@ -1668,6 +1668,13 @@ static int new_to_user(struct v4l2_ext_control *c,
>  	return ptr_to_user(c, ctrl, ctrl->p_new);
>  }
>  
> +/* Helper function: copy the request value back to the caller */
> +static int req_to_user(struct v4l2_ext_control *c,
> +		       struct v4l2_ctrl_ref *ref)
> +{
> +	return ptr_to_user(c, ref->ctrl, ref->p_req);
> +}
> +
>  /* Helper function: copy the initial control value back to the caller */
>  static int def_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
>  {
> @@ -1787,6 +1794,26 @@ static void cur_to_new(struct v4l2_ctrl *ctrl)
>  	ptr_to_ptr(ctrl, ctrl->p_cur, ctrl->p_new);
>  }
>  
> +/* Copy the new value to the request value */
> +static void new_to_req(struct v4l2_ctrl_ref *ref)
> +{
> +	if (!ref)
> +		return;
> +	ptr_to_ptr(ref->ctrl, ref->ctrl->p_new, ref->p_req);
> +	ref->req = ref;
> +}
> +
> +/* Copy the request value to the new value */
> +static void req_to_new(struct v4l2_ctrl_ref *ref)
> +{
> +	if (!ref)
> +		return;
> +	if (ref->req)
> +		ptr_to_ptr(ref->ctrl, ref->req->p_req, ref->ctrl->p_new);
> +	else
> +		ptr_to_ptr(ref->ctrl, ref->ctrl->p_cur, ref->ctrl->p_new);
> +}
> +
>  /* Return non-zero if one or more of the controls in the cluster has a new
>     value that differs from the current value. */
>  static int cluster_changed(struct v4l2_ctrl *master)
> @@ -1896,6 +1923,9 @@ int v4l2_ctrl_handler_init_class(struct v4l2_ctrl_handler *hdl,
>  	lockdep_set_class_and_name(hdl->lock, key, name);
>  	INIT_LIST_HEAD(&hdl->ctrls);
>  	INIT_LIST_HEAD(&hdl->ctrl_refs);
> +	INIT_LIST_HEAD(&hdl->requests);
> +	INIT_LIST_HEAD(&hdl->requests_queued);
> +	hdl->request_is_queued = false;
>  	hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8;
>  	hdl->buckets = kvmalloc_array(hdl->nr_of_buckets,
>  				      sizeof(hdl->buckets[0]),
> @@ -1916,6 +1946,14 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
>  	if (hdl == NULL || hdl->buckets == NULL)
>  		return;
>  
> +	if (!hdl->req_obj.req && !list_empty(&hdl->requests)) {
> +		struct v4l2_ctrl_handler *req, *next_req;
> +
> +		list_for_each_entry_safe(req, next_req, &hdl->requests, requests) {
> +			media_request_object_unbind(&req->req_obj);
> +			media_request_object_put(&req->req_obj);

Hmm... while this would work for the trivial case where object_put()
would just drop the object from the list if nobody else is using it,
nothing prevents that, if v4l2_ctrl_handler_free() is called twice,
it would do the wrong thing... as the only test here is if req_obj.reg 
is not NULL, and not if the control framework is already done with the
object.

> +		}
> +	}
>  	mutex_lock(hdl->lock);
>  	/* Free all nodes */
>  	list_for_each_entry_safe(ref, next_ref, &hdl->ctrl_refs, node) {
> @@ -2837,6 +2875,123 @@ int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm)
>  }
>  EXPORT_SYMBOL(v4l2_querymenu);
>  
> +static int v4l2_ctrl_request_clone(struct v4l2_ctrl_handler *hdl,
> +				   const struct v4l2_ctrl_handler *from)
> +{
> +	struct v4l2_ctrl_ref *ref;
> +	int err;
> +
> +	if (WARN_ON(!hdl || hdl == from))
> +		return -EINVAL;
> +
> +	if (hdl->error)
> +		return hdl->error;
> +
> +	WARN_ON(hdl->lock != &hdl->_lock);
> +
> +	mutex_lock(from->lock);
> +	list_for_each_entry(ref, &from->ctrl_refs, node) {
> +		struct v4l2_ctrl *ctrl = ref->ctrl;
> +		struct v4l2_ctrl_ref *new_ref;
> +
> +		/* Skip refs inherited from other devices */
> +		if (ref->from_other_dev)
> +			continue;
> +		/* And buttons */
> +		if (ctrl->type == V4L2_CTRL_TYPE_BUTTON)
> +			continue;
> +		err = handler_new_ref(hdl, ctrl, &new_ref, false, true);
> +		if (err)
> +			break;
> +	}
> +	mutex_unlock(from->lock);
> +	return err;
> +}
> +
> +static void v4l2_ctrl_request_queue(struct media_request_object *obj)
> +{
> +	struct v4l2_ctrl_handler *hdl =
> +		container_of(obj, struct v4l2_ctrl_handler, req_obj);
> +	struct v4l2_ctrl_handler *main_hdl = obj->priv;
> +	struct v4l2_ctrl_handler *prev_hdl = NULL;
> +	struct v4l2_ctrl_ref *ref_ctrl, *ref_ctrl_prev = NULL;
> +
> +	if (list_empty(&main_hdl->requests_queued))
> +		goto queue;
> +
> +	prev_hdl = list_last_entry(&main_hdl->requests_queued,
> +				   struct v4l2_ctrl_handler, requests_queued);
> +	/*
> +	 * Note: prev_hdl and hdl must contain the same list of control
> +	 * references, so if any differences are detected then that is a
> +	 * driver bug and the WARN_ON is triggered.
> +	 */
> +	mutex_lock(prev_hdl->lock);
> +	ref_ctrl_prev = list_first_entry(&prev_hdl->ctrl_refs,
> +					 struct v4l2_ctrl_ref, node);
> +	list_for_each_entry(ref_ctrl, &hdl->ctrl_refs, node) {
> +		if (ref_ctrl->req)
> +			continue;
> +		while (ref_ctrl_prev->ctrl->id < ref_ctrl->ctrl->id) {
> +			/* Should never happen, but just in case... */
> +			if (list_is_last(&ref_ctrl_prev->node,
> +					 &prev_hdl->ctrl_refs))

If should never happen, please use unlikely(). That makes clearer
while doing some speed optimization).

> +				break;
> +			ref_ctrl_prev = list_next_entry(ref_ctrl_prev, node);
> +		}
> +		if (WARN_ON(ref_ctrl_prev->ctrl->id != ref_ctrl->ctrl->id))
> +			break;
> +		ref_ctrl->req = ref_ctrl_prev->req;
> +	}
> +	mutex_unlock(prev_hdl->lock);
> +queue:
> +	list_add_tail(&hdl->requests_queued, &main_hdl->requests_queued);

Shouldn't list changes be serialized? Ok, the ioctls are serialized, but
device removal/unbind can happen any time for hot-plugged devices[1].

[1] yeah, I know that, right now, the framework is meant to be used only
by codecs that are on SoC, but I'm pretty sure we'll end by using it on
other use cases in the future. As this is core code, we should be sure 
that it will not cause troubles due to the lack of a proper locking, as
I doubt we'll review the locks when adding other use cases.

> +	hdl->request_is_queued = true;
> +}
> +
> +static void v4l2_ctrl_request_unbind(struct media_request_object *obj)
> +{
> +	struct v4l2_ctrl_handler *hdl =
> +		container_of(obj, struct v4l2_ctrl_handler, req_obj);
> +
> +	list_del_init(&hdl->requests);
> +	if (hdl->request_is_queued) {
> +		list_del_init(&hdl->requests_queued);
> +		hdl->request_is_queued = false;
> +	}
> +}
> +
> +static void v4l2_ctrl_request_release(struct media_request_object *obj)
> +{
> +	struct v4l2_ctrl_handler *hdl =
> +		container_of(obj, struct v4l2_ctrl_handler, req_obj);
> +
> +	v4l2_ctrl_handler_free(hdl);
> +	kfree(hdl);
> +}
> +
> +static const struct media_request_object_ops req_ops = {
> +	.queue = v4l2_ctrl_request_queue,
> +	.unbind = v4l2_ctrl_request_unbind,
> +	.release = v4l2_ctrl_request_release,
> +};
> +
> +static int v4l2_ctrl_request_bind(struct media_request *req,
> +			   struct v4l2_ctrl_handler *hdl,
> +			   struct v4l2_ctrl_handler *from)
> +{
> +	int ret;
> +
> +	ret = v4l2_ctrl_request_clone(hdl, from);
> +
> +	if (!ret) {
> +		ret = media_request_object_bind(req, &req_ops,
> +						from, &hdl->req_obj);
> +		if (!ret)
> +			list_add_tail(&hdl->requests, &from->requests);
> +	}
> +	return ret;
> +}
>  
>  /* Some general notes on the atomic requirements of VIDIOC_G/TRY/S_EXT_CTRLS:
>  
> @@ -2898,6 +3053,7 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
>  
>  		if (cs->which &&
>  		    cs->which != V4L2_CTRL_WHICH_DEF_VAL &&
> +		    cs->which != V4L2_CTRL_WHICH_REQUEST_VAL &&
>  		    V4L2_CTRL_ID2WHICH(id) != cs->which)
>  			return -EINVAL;
>  
> @@ -2977,13 +3133,12 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
>     whether there are any controls at all. */
>  static int class_check(struct v4l2_ctrl_handler *hdl, u32 which)
>  {
> -	if (which == 0 || which == V4L2_CTRL_WHICH_DEF_VAL)
> +	if (which == 0 || which == V4L2_CTRL_WHICH_DEF_VAL ||
> +	    which == V4L2_CTRL_WHICH_REQUEST_VAL)
>  		return 0;
>  	return find_ref_lock(hdl, which | 1) ? 0 : -EINVAL;
>  }
>  
> -
> -
>  /* Get extended controls. Allocates the helpers array if needed. */
>  int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
>  {
> @@ -3049,8 +3204,12 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
>  			u32 idx = i;
>  
>  			do {
> -				ret = ctrl_to_user(cs->controls + idx,
> -						   helpers[idx].ref->ctrl);
> +				if (helpers[idx].ref->req)
> +					ret = req_to_user(cs->controls + idx,
> +						helpers[idx].ref->req);
> +				else
> +					ret = ctrl_to_user(cs->controls + idx,
> +						helpers[idx].ref->ctrl);
>  				idx = helpers[idx].next;
>  			} while (!ret && idx);
>  		}
> @@ -3336,7 +3495,16 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
>  		} while (!ret && idx);
>  
>  		if (!ret)
> -			ret = try_or_set_cluster(fh, master, set, 0);
> +			ret = try_or_set_cluster(fh, master,
> +						 !hdl->req_obj.req && set, 0);
> +		if (!ret && hdl->req_obj.req && set) {
> +			for (j = 0; j < master->ncontrols; j++) {
> +				struct v4l2_ctrl_ref *ref =
> +					find_ref(hdl, master->cluster[j]->id);
> +
> +				new_to_req(ref);
> +			}
> +		}
>  
>  		/* Copy the new values back to userspace. */
>  		if (!ret) {
> @@ -3463,6 +3631,152 @@ int __v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s)
>  }
>  EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_string);
>  
> +void v4l2_ctrl_request_complete(struct media_request *req,
> +				struct v4l2_ctrl_handler *main_hdl)
> +{
> +	struct media_request_object *obj;
> +	struct v4l2_ctrl_handler *hdl;
> +	struct v4l2_ctrl_ref *ref;
> +
> +	if (!req || !main_hdl)
> +		return;
> +
> +	obj = media_request_object_find(req, &req_ops, main_hdl);
> +	if (!obj)
> +		return;
> +	hdl = container_of(obj, struct v4l2_ctrl_handler, req_obj);
> +
> +	list_for_each_entry(ref, &hdl->ctrl_refs, node) {
> +		struct v4l2_ctrl *ctrl = ref->ctrl;
> +		struct v4l2_ctrl *master = ctrl->cluster[0];
> +		unsigned int i;
> +
> +		if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE) {
> +			ref->req = ref;
> +
> +			v4l2_ctrl_lock(master);
> +			/* g_volatile_ctrl will update the current control values */
> +			for (i = 0; i < master->ncontrols; i++)
> +				cur_to_new(master->cluster[i]);
> +			call_op(master, g_volatile_ctrl);
> +			new_to_req(ref);
> +			v4l2_ctrl_unlock(master);
> +			continue;
> +		}
> +		if (ref->req == ref)
> +			continue;
> +
> +		v4l2_ctrl_lock(ctrl);
> +		if (ref->req)
> +			ptr_to_ptr(ctrl, ref->req->p_req, ref->p_req);
> +		else
> +			ptr_to_ptr(ctrl, ctrl->p_cur, ref->p_req);
> +		v4l2_ctrl_unlock(ctrl);
> +	}
> +
> +	WARN_ON(!hdl->request_is_queued);
> +	list_del_init(&hdl->requests_queued);
> +	hdl->request_is_queued = false;
> +	media_request_object_complete(obj);
> +	media_request_object_put(obj);

Hmm... nothing prevents that this would race with v4l2_ctrl_handler_free()
and cause double-free (actually double object_put).

> +}
> +EXPORT_SYMBOL(v4l2_ctrl_request_complete);
> +
> +void v4l2_ctrl_request_setup(struct media_request *req,
> +			     struct v4l2_ctrl_handler *main_hdl)
> +{
> +	struct media_request_object *obj;
> +	struct v4l2_ctrl_handler *hdl;
> +	struct v4l2_ctrl_ref *ref;
> +
> +	if (!req || !main_hdl)
> +		return;
> +
> +	if (WARN_ON(req->state != MEDIA_REQUEST_STATE_QUEUED))
> +		return;
> +
> +	obj = media_request_object_find(req, &req_ops, main_hdl);
> +	if (!obj)
> +		return;

Shouldn't the above checks produce an error or print something at
the logs?

> +	if (obj->completed) {
> +		media_request_object_put(obj);
> +		return;
> +	}
> +	hdl = container_of(obj, struct v4l2_ctrl_handler, req_obj);
> +
> +	list_for_each_entry(ref, &hdl->ctrl_refs, node)
> +		ref->req_done = false;
> +
> +	list_for_each_entry(ref, &hdl->ctrl_refs, node) {
> +		struct v4l2_ctrl *ctrl = ref->ctrl;
> +		struct v4l2_ctrl *master = ctrl->cluster[0];
> +		bool have_new_data = false;
> +		int i;
> +
> +		/*
> +		 * Skip if this control was already handled by a cluster.
> +		 * Skip button controls and read-only controls.
> +		 */
> +		if (ref->req_done || ctrl->type == V4L2_CTRL_TYPE_BUTTON ||
> +		    (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY))
> +			continue;
> +
> +		v4l2_ctrl_lock(master);
> +		for (i = 0; i < master->ncontrols; i++) {
> +			if (master->cluster[i]) {
> +				struct v4l2_ctrl_ref *r =
> +					find_ref(hdl, master->cluster[i]->id);
> +
> +				if (r->req && r == r->req) {
> +					have_new_data = true;
> +					break;
> +				}
> +			}
> +		}
> +		if (!have_new_data) {
> +			v4l2_ctrl_unlock(master);
> +			continue;
> +		}
> +
> +		for (i = 0; i < master->ncontrols; i++) {
> +			if (master->cluster[i]) {
> +				struct v4l2_ctrl_ref *r =
> +					find_ref(hdl, master->cluster[i]->id);
> +
> +				req_to_new(r);
> +				master->cluster[i]->is_new = 1;
> +				r->req_done = true;
> +			}
> +		}
> +		/*
> +		 * For volatile autoclusters that are currently in auto mode
> +		 * we need to discover if it will be set to manual mode.
> +		 * If so, then we have to copy the current volatile values
> +		 * first since those will become the new manual values (which
> +		 * may be overwritten by explicit new values from this set
> +		 * of controls).
> +		 */
> +		if (master->is_auto && master->has_volatiles &&
> +		    !is_cur_manual(master)) {
> +			s32 new_auto_val = *master->p_new.p_s32;
> +
> +			/*
> +			 * If the new value == the manual value, then copy
> +			 * the current volatile values.
> +			 */
> +			if (new_auto_val == master->manual_mode_value)
> +				update_from_auto_cluster(master);
> +		}
> +
> +		try_or_set_cluster(NULL, master, true, 0);
> +
> +		v4l2_ctrl_unlock(master);
> +	}
> +
> +	media_request_object_put(obj);
> +}
> +EXPORT_SYMBOL(v4l2_ctrl_request_setup);
> +
>  void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void *priv)
>  {
>  	if (ctrl == NULL)
> diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
> index 3f4e062d4e3d..3617d5077b19 100644
> --- a/include/media/v4l2-ctrls.h
> +++ b/include/media/v4l2-ctrls.h
> @@ -250,6 +250,12 @@ struct v4l2_ctrl {
>   *		``prepare_ext_ctrls`` function at ``v4l2-ctrl.c``.
>   * @from_other_dev: If true, then @ctrl was defined in another
>   *		device than the &struct v4l2_ctrl_handler.
> + * @req_done:	Internal flag: if the control handler containing this control
> + *		reference is bound to a media request, then this is set when
> + *		the control has been applied. This prevents applying controls
> + *		from a cluster with multiple controls twice (when the first
> + *		control of a cluster is applied, they all are).
> + * @req:	If set, this refers to another request that sets this control.
>   * @p_req:	If the control handler containing this control reference
>   *		is bound to a media request, then this points to the
>   *		value of the control that should be applied when the request
> @@ -266,6 +272,8 @@ struct v4l2_ctrl_ref {
>  	struct v4l2_ctrl *ctrl;
>  	struct v4l2_ctrl_helper *helper;
>  	bool from_other_dev;
> +	bool req_done;
> +	struct v4l2_ctrl_ref *req;
>  	union v4l2_ctrl_ptr p_req;
>  };
>  
> @@ -290,6 +298,15 @@ struct v4l2_ctrl_ref {
>   * @notify_priv: Passed as argument to the v4l2_ctrl notify callback.
>   * @nr_of_buckets: Total number of buckets in the array.
>   * @error:	The error code of the first failed control addition.
> + * @request_is_queued: True if the request was queued.
> + * @requests:	List to keep track of open control handler request objects.
> + *		For the parent control handler (@req_obj.req == NULL) this
> + *		is the list header. When the parent control handler is
> + *		removed, it has to unbind and put all these requests since
> + *		they refer to the parent.
> + * @requests_queued: List of the queued requests. This determines the order
> + *		in which these controls are applied. Once the request is
> + *		completed it is removed from this list.
>   * @req_obj:	The &struct media_request_object, used to link into a
>   *		&struct media_request. This request object has a refcount.
>   */
> @@ -304,6 +321,9 @@ struct v4l2_ctrl_handler {
>  	void *notify_priv;
>  	u16 nr_of_buckets;
>  	int error;
> +	bool request_is_queued;
> +	struct list_head requests;
> +	struct list_head requests_queued;
>  	struct media_request_object req_obj;
>  };
>  
> @@ -1062,6 +1082,37 @@ int v4l2_ctrl_subscribe_event(struct v4l2_fh *fh,
>   */
>  __poll_t v4l2_ctrl_poll(struct file *file, struct poll_table_struct *wait);
>  
> +/**
> + * v4l2_ctrl_request_setup - helper function to apply control values in a request
> + *
> + * @req: The request
> + * @hdl: The control handler
> + *
> + * This is a helper function to call the control handler's s_ctrl callback with
> + * the control values contained in the request. Do note that this approach of
> + * applying control values in a request is only applicable to memory-to-memory
> + * devices.
> + */
> +void v4l2_ctrl_request_setup(struct media_request *req,
> +			     struct v4l2_ctrl_handler *hdl);
> +
> +/**
> + * v4l2_ctrl_request_complete - Complete a control handler request object
> + *
> + * @req: The request
> + * @hdl: The control handler
> + *
> + * This function is to be called on each control handler that may have had a
> + * request object associated with it, i.e. control handlers of a driver that
> + * supports requests.
> + *
> + * The function first obtains the values of any volatile controls in the control
> + * handler and attach them to the request. Then, the function completes the
> + * request object.
> + */
> +void v4l2_ctrl_request_complete(struct media_request *req,
> +				struct v4l2_ctrl_handler *hdl);
> +
>  /* Helpers for ioctl_ops */
>  
>  /**



Thanks,
Mauro

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

* Re: [PATCHv17 15/34] v4l2-ctrls: support g/s_ext_ctrls for requests
  2018-08-04 12:45 ` [PATCHv17 15/34] v4l2-ctrls: support g/s_ext_ctrls for requests Hans Verkuil
@ 2018-08-13 11:05   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-13 11:05 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:45:07 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> The v4l2_g/s_ext_ctrls functions now support control handlers that
> represent requests.
> 
> The v4l2_ctrls_find_req_obj() function is responsible for finding the
> request from the fd.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  drivers/media/platform/omap3isp/ispvideo.c |   2 +-
>  drivers/media/v4l2-core/v4l2-ctrls.c       | 138 +++++++++++++++++++--
>  drivers/media/v4l2-core/v4l2-ioctl.c       |  12 +-
>  drivers/media/v4l2-core/v4l2-subdev.c      |   9 +-
>  include/media/v4l2-ctrls.h                 |   7 +-
>  5 files changed, 149 insertions(+), 19 deletions(-)
> 
> diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c
> index 9d228eac24ea..674e7fd3ad99 100644
> --- a/drivers/media/platform/omap3isp/ispvideo.c
> +++ b/drivers/media/platform/omap3isp/ispvideo.c
> @@ -1028,7 +1028,7 @@ static int isp_video_check_external_subdevs(struct isp_video *video,
>  	ctrls.count = 1;
>  	ctrls.controls = &ctrl;
>  
> -	ret = v4l2_g_ext_ctrls(pipe->external->ctrl_handler, &ctrls);
> +	ret = v4l2_g_ext_ctrls(pipe->external->ctrl_handler, NULL, &ctrls);
>  	if (ret < 0) {
>  		dev_warn(isp->dev, "no pixel rate control in subdev %s\n",
>  			 pipe->external->name);
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
> index b8ff6d6b14cd..86a6ae54ccaa 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls.c
> @@ -3140,7 +3140,8 @@ static int class_check(struct v4l2_ctrl_handler *hdl, u32 which)
>  }
>  
>  /* Get extended controls. Allocates the helpers array if needed. */
> -int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
> +int __v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl,
> +		       struct v4l2_ext_controls *cs)

I don't like the idea of just adding a __ before the name here. We
generally use __foo() for a non-locked version of foo(). This is not
the case here.

The non-standard convention is even worse here, as this is not a
static function.

Perhaps it could be called something like v4l2_g_ext_ctrls_no_req(),
v4l2_g_ext_ctrls_common() or something similar.

>  {
>  	struct v4l2_ctrl_helper helper[4];
>  	struct v4l2_ctrl_helper *helpers = helper;
> @@ -3220,6 +3221,83 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
>  		kvfree(helpers);
>  	return ret;
>  }
> +
> +static struct media_request_object *
> +v4l2_ctrls_find_req_obj(struct v4l2_ctrl_handler *hdl,
> +			struct media_request *req, bool set)
> +{
> +	struct media_request_object *obj;
> +	struct v4l2_ctrl_handler *new_hdl;
> +	int ret;
> +
> +	if (IS_ERR(req))
> +		return ERR_CAST(req);
> +
> +	if (set && WARN_ON(req->state != MEDIA_REQUEST_STATE_UPDATING))
> +		return ERR_PTR(-EBUSY);
> +
> +	obj = media_request_object_find(req, &req_ops, hdl);
> +	if (obj)
> +		return obj;
> +	if (!set)
> +		return ERR_PTR(-ENOENT);
> +
> +	new_hdl = kzalloc(sizeof(*new_hdl), GFP_KERNEL);
> +	if (!new_hdl)
> +		return ERR_PTR(-ENOMEM);
> +
> +	obj = &new_hdl->req_obj;
> +	ret = v4l2_ctrl_handler_init(new_hdl, (hdl->nr_of_buckets - 1) * 8);
> +	if (!ret)
> +		ret = v4l2_ctrl_request_bind(req, new_hdl, hdl);
> +	if (ret) {
> +		kfree(new_hdl);
> +
> +		return ERR_PTR(ret);
> +	}
> +
> +	media_request_object_get(obj);
> +	return obj;
> +}
> +
> +int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct media_device *mdev,
> +		     struct v4l2_ext_controls *cs)
> +{
> +	struct media_request_object *obj = NULL;
> +	int ret;
> +
> +	if (cs->which == V4L2_CTRL_WHICH_REQUEST_VAL) {
> +		struct media_request *req;
> +
> +		if (!mdev || cs->request_fd < 0)
> +			return -EINVAL;
> +
> +		req = media_request_get_by_fd(mdev, cs->request_fd);
> +		if (IS_ERR(req))
> +			return PTR_ERR(req);
> +
> +		if (req->state != MEDIA_REQUEST_STATE_IDLE &&
> +		    req->state != MEDIA_REQUEST_STATE_COMPLETE) {
> +			media_request_put(req);
> +			return -EBUSY;
> +		}
> +
> +		obj = v4l2_ctrls_find_req_obj(hdl, req, false);
> +		/* Reference to the request held through obj */
> +		media_request_put(req);
> +		if (IS_ERR(obj))
> +			return PTR_ERR(obj);
> +
> +		hdl = container_of(obj, struct v4l2_ctrl_handler,
> +				   req_obj);
> +	}
> +
> +	ret = __v4l2_g_ext_ctrls(hdl, cs);
> +
> +	if (obj)
> +		media_request_object_put(obj);
> +	return ret;
> +}
>  EXPORT_SYMBOL(v4l2_g_ext_ctrls);
>  
>  /* Helper function to get a single control */
> @@ -3408,9 +3486,9 @@ static void update_from_auto_cluster(struct v4l2_ctrl *master)
>  }
>  
>  /* Try or try-and-set controls */
> -static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
> -			     struct v4l2_ext_controls *cs,
> -			     bool set)
> +static int __try_set_ext_ctrls(struct v4l2_fh *fh,
> +			       struct v4l2_ctrl_handler *hdl,
> +			       struct v4l2_ext_controls *cs, bool set)

Same comment about naming convention applies here... please call it as
try_set_ext_ctrls_no_req() or similar.

>  {
>  	struct v4l2_ctrl_helper helper[4];
>  	struct v4l2_ctrl_helper *helpers = helper;
> @@ -3523,16 +3601,60 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
>  	return ret;
>  }
>  
> -int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
> +static int try_set_ext_ctrls(struct v4l2_fh *fh,
> +			     struct v4l2_ctrl_handler *hdl, struct media_device *mdev,
> +			     struct v4l2_ext_controls *cs, bool set)
> +{
> +	struct media_request_object *obj = NULL;
> +	struct media_request *req = NULL;
> +	int ret;
> +
> +	if (cs->which == V4L2_CTRL_WHICH_REQUEST_VAL) {
> +		if (!mdev || cs->request_fd < 0)
> +			return -EINVAL;
> +
> +		req = media_request_get_by_fd(mdev, cs->request_fd);
> +		if (IS_ERR(req))
> +			return PTR_ERR(req);
> +
> +		ret = media_request_lock_for_update(req);
> +		if (ret) {
> +			media_request_put(req);
> +			return ret;
> +		}
> +
> +		obj = v4l2_ctrls_find_req_obj(hdl, req, set);
> +		/* Reference to the request held through obj */
> +		media_request_put(req);
> +		if (IS_ERR(obj)) {
> +			media_request_unlock_for_update(req);
> +			return PTR_ERR(obj);
> +		}
> +		hdl = container_of(obj, struct v4l2_ctrl_handler,
> +				   req_obj);
> +	}
> +
> +	ret = __try_set_ext_ctrls(fh, hdl, cs, set);
> +
> +	if (obj) {
> +		media_request_unlock_for_update(obj->req);
> +		media_request_object_put(obj);
> +	}
> +
> +	return ret;
> +}
> +
> +int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct media_device *mdev,
> +		       struct v4l2_ext_controls *cs)
>  {
> -	return try_set_ext_ctrls(NULL, hdl, cs, false);
> +	return try_set_ext_ctrls(NULL, hdl, mdev, cs, false);
>  }
>  EXPORT_SYMBOL(v4l2_try_ext_ctrls);
>  
>  int v4l2_s_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
> -					struct v4l2_ext_controls *cs)
> +		     struct media_device *mdev, struct v4l2_ext_controls *cs)
>  {
> -	return try_set_ext_ctrls(fh, hdl, cs, true);
> +	return try_set_ext_ctrls(fh, hdl, mdev, cs, true);
>  }
>  EXPORT_SYMBOL(v4l2_s_ext_ctrls);
>  
> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
> index 03241d6b7ef8..20b5145a5254 100644
> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> @@ -2109,9 +2109,9 @@ static int v4l_g_ext_ctrls(const struct v4l2_ioctl_ops *ops,
>  
>  	p->error_idx = p->count;
>  	if (vfh && vfh->ctrl_handler)
> -		return v4l2_g_ext_ctrls(vfh->ctrl_handler, p);
> +		return v4l2_g_ext_ctrls(vfh->ctrl_handler, vfd->v4l2_dev->mdev, p);
>  	if (vfd->ctrl_handler)
> -		return v4l2_g_ext_ctrls(vfd->ctrl_handler, p);
> +		return v4l2_g_ext_ctrls(vfd->ctrl_handler, vfd->v4l2_dev->mdev, p);
>  	if (ops->vidioc_g_ext_ctrls == NULL)
>  		return -ENOTTY;
>  	return check_ext_ctrls(p, 0) ? ops->vidioc_g_ext_ctrls(file, fh, p) :
> @@ -2128,9 +2128,9 @@ static int v4l_s_ext_ctrls(const struct v4l2_ioctl_ops *ops,
>  
>  	p->error_idx = p->count;
>  	if (vfh && vfh->ctrl_handler)
> -		return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, p);
> +		return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, vfd->v4l2_dev->mdev, p);
>  	if (vfd->ctrl_handler)
> -		return v4l2_s_ext_ctrls(NULL, vfd->ctrl_handler, p);
> +		return v4l2_s_ext_ctrls(NULL, vfd->ctrl_handler, vfd->v4l2_dev->mdev, p);
>  	if (ops->vidioc_s_ext_ctrls == NULL)
>  		return -ENOTTY;
>  	return check_ext_ctrls(p, 0) ? ops->vidioc_s_ext_ctrls(file, fh, p) :
> @@ -2147,9 +2147,9 @@ static int v4l_try_ext_ctrls(const struct v4l2_ioctl_ops *ops,
>  
>  	p->error_idx = p->count;
>  	if (vfh && vfh->ctrl_handler)
> -		return v4l2_try_ext_ctrls(vfh->ctrl_handler, p);
> +		return v4l2_try_ext_ctrls(vfh->ctrl_handler, vfd->v4l2_dev->mdev, p);
>  	if (vfd->ctrl_handler)
> -		return v4l2_try_ext_ctrls(vfd->ctrl_handler, p);
> +		return v4l2_try_ext_ctrls(vfd->ctrl_handler, vfd->v4l2_dev->mdev, p);
>  	if (ops->vidioc_try_ext_ctrls == NULL)
>  		return -ENOTTY;
>  	return check_ext_ctrls(p, 0) ? ops->vidioc_try_ext_ctrls(file, fh, p) :
> diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
> index 2b63fa6b6fc9..0fdbd85532dd 100644
> --- a/drivers/media/v4l2-core/v4l2-subdev.c
> +++ b/drivers/media/v4l2-core/v4l2-subdev.c
> @@ -222,17 +222,20 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
>  	case VIDIOC_G_EXT_CTRLS:
>  		if (!vfh->ctrl_handler)
>  			return -ENOTTY;
> -		return v4l2_g_ext_ctrls(vfh->ctrl_handler, arg);
> +		return v4l2_g_ext_ctrls(vfh->ctrl_handler,
> +					sd->v4l2_dev->mdev, arg);
>  
>  	case VIDIOC_S_EXT_CTRLS:
>  		if (!vfh->ctrl_handler)
>  			return -ENOTTY;
> -		return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, arg);
> +		return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler,
> +					sd->v4l2_dev->mdev, arg);
>  
>  	case VIDIOC_TRY_EXT_CTRLS:
>  		if (!vfh->ctrl_handler)
>  			return -ENOTTY;
> -		return v4l2_try_ext_ctrls(vfh->ctrl_handler, arg);
> +		return v4l2_try_ext_ctrls(vfh->ctrl_handler,
> +					  sd->v4l2_dev->mdev, arg);
>  
>  	case VIDIOC_DQEVENT:
>  		if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
> diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
> index 3617d5077b19..98b1e70a4a46 100644
> --- a/include/media/v4l2-ctrls.h
> +++ b/include/media/v4l2-ctrls.h
> @@ -1179,11 +1179,12 @@ int v4l2_s_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
>   *	:ref:`VIDIOC_G_EXT_CTRLS <vidioc_g_ext_ctrls>` ioctl
>   *
>   * @hdl: pointer to &struct v4l2_ctrl_handler
> + * @mdev: pointer to &struct media_device
>   * @c: pointer to &struct v4l2_ext_controls
>   *
>   * If hdl == NULL then they will all return -EINVAL.
>   */
> -int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl,
> +int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct media_device *mdev,
>  		     struct v4l2_ext_controls *c);
>  
>  /**
> @@ -1191,11 +1192,13 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl,
>   *	:ref:`VIDIOC_TRY_EXT_CTRLS <vidioc_g_ext_ctrls>` ioctl
>   *
>   * @hdl: pointer to &struct v4l2_ctrl_handler
> + * @mdev: pointer to &struct media_device
>   * @c: pointer to &struct v4l2_ext_controls
>   *
>   * If hdl == NULL then they will all return -EINVAL.
>   */
>  int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl,
> +		       struct media_device *mdev,
>  		       struct v4l2_ext_controls *c);
>  
>  /**
> @@ -1204,11 +1207,13 @@ int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl,
>   *
>   * @fh: pointer to &struct v4l2_fh
>   * @hdl: pointer to &struct v4l2_ctrl_handler
> + * @mdev: pointer to &struct media_device
>   * @c: pointer to &struct v4l2_ext_controls
>   *
>   * If hdl == NULL then they will all return -EINVAL.
>   */
>  int v4l2_s_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
> +		     struct media_device *mdev,
>  		     struct v4l2_ext_controls *c);
>  
>  /**



Thanks,
Mauro

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

* Re: [PATCHv17 16/34] v4l2-ctrls: add v4l2_ctrl_request_hdl_find/put/ctrl_find functions
  2018-08-04 12:45 ` [PATCHv17 16/34] v4l2-ctrls: add v4l2_ctrl_request_hdl_find/put/ctrl_find functions Hans Verkuil
@ 2018-08-13 11:07   ` Mauro Carvalho Chehab
  2018-08-14  8:45     ` Hans Verkuil
  0 siblings, 1 reply; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-13 11:07 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:45:08 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> If a driver needs to find/inspect the controls set in a request then
> it can use these functions.
> 
> E.g. to check if a required control is set in a request use this in the
> req_validate() implementation:
> 
> 	int res = -EINVAL;
> 
> 	hdl = v4l2_ctrl_request_hdl_find(req, parent_hdl);
> 	if (hdl) {
> 		if (v4l2_ctrl_request_hdl_ctrl_find(hdl, ctrl_id))
> 			res = 0;
> 		v4l2_ctrl_request_hdl_put(hdl);
> 	}
> 	return res;
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  drivers/media/v4l2-core/v4l2-ctrls.c | 25 ++++++++++++++++
>  include/media/v4l2-ctrls.h           | 44 +++++++++++++++++++++++++++-
>  2 files changed, 68 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
> index 86a6ae54ccaa..2a30be824491 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls.c
> @@ -2976,6 +2976,31 @@ static const struct media_request_object_ops req_ops = {
>  	.release = v4l2_ctrl_request_release,
>  };
>  
> +struct v4l2_ctrl_handler *v4l2_ctrl_request_hdl_find(struct media_request *req,
> +					struct v4l2_ctrl_handler *parent)
> +{
> +	struct media_request_object *obj;
> +
> +	if (WARN_ON(req->state != MEDIA_REQUEST_STATE_VALIDATING &&
> +		    req->state != MEDIA_REQUEST_STATE_QUEUED))
> +		return NULL;
> +
> +	obj = media_request_object_find(req, &req_ops, parent);
> +	if (obj)
> +		return container_of(obj, struct v4l2_ctrl_handler, req_obj);
> +	return NULL;
> +}
> +EXPORT_SYMBOL_GPL(v4l2_ctrl_request_hdl_find);
> +
> +struct v4l2_ctrl *
> +v4l2_ctrl_request_hdl_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id)
> +{
> +	struct v4l2_ctrl_ref *ref = find_ref_lock(hdl, id);
> +
> +	return (ref && ref->req == ref) ? ref->ctrl : NULL;

Doesn't those helper functions (including this one) be serialized?

> +}
> +EXPORT_SYMBOL_GPL(v4l2_ctrl_request_hdl_ctrl_find);
> +
>  static int v4l2_ctrl_request_bind(struct media_request *req,
>  			   struct v4l2_ctrl_handler *hdl,
>  			   struct v4l2_ctrl_handler *from)
> diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
> index 98b1e70a4a46..aeb7f3c24ef7 100644
> --- a/include/media/v4l2-ctrls.h
> +++ b/include/media/v4l2-ctrls.h
> @@ -1111,7 +1111,49 @@ void v4l2_ctrl_request_setup(struct media_request *req,
>   * request object.
>   */
>  void v4l2_ctrl_request_complete(struct media_request *req,
> -				struct v4l2_ctrl_handler *hdl);
> +				struct v4l2_ctrl_handler *parent);
> +
> +/**
> + * v4l2_ctrl_request_hdl_find - Find the control handler in the request
> + *
> + * @req: The request
> + * @parent: The parent control handler ('priv' in media_request_object_find())
> + *
> + * This function finds the control handler in the request. It may return
> + * NULL if not found. When done, you must call v4l2_ctrl_request_put_hdl()
> + * with the returned handler pointer.
> + *
> + * If the request is not in state VALIDATING or QUEUED, then this function
> + * will always return NULL.
> + */
> +struct v4l2_ctrl_handler *v4l2_ctrl_request_hdl_find(struct media_request *req,
> +					struct v4l2_ctrl_handler *parent);
> +
> +/**
> + * v4l2_ctrl_request_hdl_put - Put the control handler
> + *
> + * @hdl: Put this control handler
> + *
> + * This function released the control handler previously obtained from'
> + * v4l2_ctrl_request_hdl_find().
> + */
> +static inline void v4l2_ctrl_request_hdl_put(struct v4l2_ctrl_handler *hdl)
> +{
> +	if (hdl)
> +		media_request_object_put(&hdl->req_obj);
> +}
> +
> +/**
> + * v4l2_ctrl_request_ctrl_find() - Find a control with the given ID.
> + *
> + * @hdl: The control handler from the request.
> + * @id: The ID of the control to find.
> + *
> + * This function returns a pointer to the control if this control is
> + * part of the request or NULL otherwise.
> + */
> +struct v4l2_ctrl *
> +v4l2_ctrl_request_hdl_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id);
>  
>  /* Helpers for ioctl_ops */
>  



Thanks,
Mauro

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

* Re: [PATCHv17 17/34] vb2: store userspace data in vb2_v4l2_buffer
  2018-08-04 12:45 ` [PATCHv17 17/34] vb2: store userspace data in vb2_v4l2_buffer Hans Verkuil
@ 2018-08-13 11:15   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-13 11:15 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:45:09 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> The userspace-provided plane data needs to be stored in
> vb2_v4l2_buffer. Currently this information is applied by
> __fill_vb2_buffer() which is called by the core prepare_buf
> and qbuf functions, but when using requests these functions
> aren't called yet since the buffer won't be prepared until
> the media request is actually queued.
> 
> In the meantime this information has to be stored somewhere
> and vb2_v4l2_buffer is a good place for it.
> 
> The __fill_vb2_buffer callback now just copies the relevant
> information from vb2_v4l2_buffer into the planes array.

Reviewing this patch is very hard, as it does more than one
thing at a time. Also, if something goes bad and we need to revisit,
people will lose precious time trying to understand what are the
actual changes done here.

So, could you please split this patch into a preparation one that
would be just splitting the __fill_vb2_buffer() into two functions
(without any other change), and another one doing the changes required
for req buffers?

> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  .../media/common/videobuf2/videobuf2-core.c   |  43 +--
>  .../media/common/videobuf2/videobuf2-v4l2.c   | 327 ++++++++++--------
>  drivers/media/dvb-core/dvb_vb2.c              |   3 +-
>  include/media/videobuf2-core.h                |   3 +-
>  include/media/videobuf2-v4l2.h                |   2 +
>  5 files changed, 209 insertions(+), 169 deletions(-)
> 
> diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
> index 5653e8eebe2b..7401a17c80ca 100644
> --- a/drivers/media/common/videobuf2/videobuf2-core.c
> +++ b/drivers/media/common/videobuf2/videobuf2-core.c
> @@ -967,20 +967,19 @@ EXPORT_SYMBOL_GPL(vb2_discard_done);
>  /*
>   * __prepare_mmap() - prepare an MMAP buffer
>   */
> -static int __prepare_mmap(struct vb2_buffer *vb, const void *pb)
> +static int __prepare_mmap(struct vb2_buffer *vb)
>  {
>  	int ret = 0;
>  
> -	if (pb)
> -		ret = call_bufop(vb->vb2_queue, fill_vb2_buffer,
> -				 vb, pb, vb->planes);
> +	ret = call_bufop(vb->vb2_queue, fill_vb2_buffer,
> +			 vb, vb->planes);
>  	return ret ? ret : call_vb_qop(vb, buf_prepare, vb);
>  }
>  
>  /*
>   * __prepare_userptr() - prepare a USERPTR buffer
>   */
> -static int __prepare_userptr(struct vb2_buffer *vb, const void *pb)
> +static int __prepare_userptr(struct vb2_buffer *vb)
>  {
>  	struct vb2_plane planes[VB2_MAX_PLANES];
>  	struct vb2_queue *q = vb->vb2_queue;
> @@ -991,12 +990,10 @@ static int __prepare_userptr(struct vb2_buffer *vb, const void *pb)
>  
>  	memset(planes, 0, sizeof(planes[0]) * vb->num_planes);
>  	/* Copy relevant information provided by the userspace */
> -	if (pb) {
> -		ret = call_bufop(vb->vb2_queue, fill_vb2_buffer,
> -				 vb, pb, planes);
> -		if (ret)
> -			return ret;
> -	}
> +	ret = call_bufop(vb->vb2_queue, fill_vb2_buffer,
> +			 vb, planes);
> +	if (ret)
> +		return ret;
>  
>  	for (plane = 0; plane < vb->num_planes; ++plane) {
>  		/* Skip the plane if already verified */
> @@ -1096,7 +1093,7 @@ static int __prepare_userptr(struct vb2_buffer *vb, const void *pb)
>  /*
>   * __prepare_dmabuf() - prepare a DMABUF buffer
>   */
> -static int __prepare_dmabuf(struct vb2_buffer *vb, const void *pb)
> +static int __prepare_dmabuf(struct vb2_buffer *vb)
>  {
>  	struct vb2_plane planes[VB2_MAX_PLANES];
>  	struct vb2_queue *q = vb->vb2_queue;
> @@ -1107,12 +1104,10 @@ static int __prepare_dmabuf(struct vb2_buffer *vb, const void *pb)
>  
>  	memset(planes, 0, sizeof(planes[0]) * vb->num_planes);
>  	/* Copy relevant information provided by the userspace */
> -	if (pb) {
> -		ret = call_bufop(vb->vb2_queue, fill_vb2_buffer,
> -				 vb, pb, planes);
> -		if (ret)
> -			return ret;
> -	}
> +	ret = call_bufop(vb->vb2_queue, fill_vb2_buffer,
> +			 vb, planes);
> +	if (ret)
> +		return ret;
>  
>  	for (plane = 0; plane < vb->num_planes; ++plane) {
>  		struct dma_buf *dbuf = dma_buf_get(planes[plane].m.fd);
> @@ -1241,7 +1236,7 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
>  	call_void_vb_qop(vb, buf_queue, vb);
>  }
>  
> -static int __buf_prepare(struct vb2_buffer *vb, const void *pb)
> +static int __buf_prepare(struct vb2_buffer *vb)
>  {
>  	struct vb2_queue *q = vb->vb2_queue;
>  	unsigned int plane;
> @@ -1256,13 +1251,13 @@ static int __buf_prepare(struct vb2_buffer *vb, const void *pb)
>  
>  	switch (q->memory) {
>  	case VB2_MEMORY_MMAP:
> -		ret = __prepare_mmap(vb, pb);
> +		ret = __prepare_mmap(vb);
>  		break;
>  	case VB2_MEMORY_USERPTR:
> -		ret = __prepare_userptr(vb, pb);
> +		ret = __prepare_userptr(vb);
>  		break;
>  	case VB2_MEMORY_DMABUF:
> -		ret = __prepare_dmabuf(vb, pb);
> +		ret = __prepare_dmabuf(vb);
>  		break;
>  	default:
>  		WARN(1, "Invalid queue type\n");
> @@ -1296,7 +1291,7 @@ int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb)
>  		return -EINVAL;
>  	}
>  
> -	ret = __buf_prepare(vb, pb);
> +	ret = __buf_prepare(vb);
>  	if (ret)
>  		return ret;
>  
> @@ -1386,7 +1381,7 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb)
>  
>  	switch (vb->state) {
>  	case VB2_BUF_STATE_DEQUEUED:
> -		ret = __buf_prepare(vb, pb);
> +		ret = __buf_prepare(vb);
>  		if (ret)
>  			return ret;
>  		break;
> diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c
> index 886a2d8d5c6c..360dc4e7d413 100644
> --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
> +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
> @@ -154,9 +154,177 @@ static void vb2_warn_zero_bytesused(struct vb2_buffer *vb)
>  		pr_warn("use the actual size instead.\n");
>  }
>  
> +static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
> +{
> +	struct vb2_queue *q = vb->vb2_queue;
> +	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> +	struct vb2_plane *planes = vbuf->planes;
> +	unsigned int plane;
> +	int ret;
> +
> +	ret = __verify_length(vb, b);
> +	if (ret < 0) {
> +		dprintk(1, "plane parameters verification failed: %d\n", ret);
> +		return ret;
> +	}
> +	if (b->field == V4L2_FIELD_ALTERNATE && q->is_output) {
> +		/*
> +		 * If the format's field is ALTERNATE, then the buffer's field
> +		 * should be either TOP or BOTTOM, not ALTERNATE since that
> +		 * makes no sense. The driver has to know whether the
> +		 * buffer represents a top or a bottom field in order to
> +		 * program any DMA correctly. Using ALTERNATE is wrong, since
> +		 * that just says that it is either a top or a bottom field,
> +		 * but not which of the two it is.
> +		 */
> +		dprintk(1, "the field is incorrectly set to ALTERNATE for an output buffer\n");
> +		return -EINVAL;
> +	}
> +	vbuf->sequence = 0;
> +
> +	if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
> +		switch (b->memory) {
> +		case VB2_MEMORY_USERPTR:
> +			for (plane = 0; plane < vb->num_planes; ++plane) {
> +				planes[plane].m.userptr =
> +					b->m.planes[plane].m.userptr;
> +				planes[plane].length =
> +					b->m.planes[plane].length;
> +			}
> +			break;
> +		case VB2_MEMORY_DMABUF:
> +			for (plane = 0; plane < vb->num_planes; ++plane) {
> +				planes[plane].m.fd =
> +					b->m.planes[plane].m.fd;
> +				planes[plane].length =
> +					b->m.planes[plane].length;
> +			}
> +			break;
> +		default:
> +			for (plane = 0; plane < vb->num_planes; ++plane) {
> +				planes[plane].m.offset =
> +					vb->planes[plane].m.offset;
> +				planes[plane].length =
> +					vb->planes[plane].length;
> +			}
> +			break;
> +		}
> +
> +		/* Fill in driver-provided information for OUTPUT types */
> +		if (V4L2_TYPE_IS_OUTPUT(b->type)) {
> +			/*
> +			 * Will have to go up to b->length when API starts
> +			 * accepting variable number of planes.
> +			 *
> +			 * If bytesused == 0 for the output buffer, then fall
> +			 * back to the full buffer size. In that case
> +			 * userspace clearly never bothered to set it and
> +			 * it's a safe assumption that they really meant to
> +			 * use the full plane sizes.
> +			 *
> +			 * Some drivers, e.g. old codec drivers, use bytesused == 0
> +			 * as a way to indicate that streaming is finished.
> +			 * In that case, the driver should use the
> +			 * allow_zero_bytesused flag to keep old userspace
> +			 * applications working.
> +			 */
> +			for (plane = 0; plane < vb->num_planes; ++plane) {
> +				struct vb2_plane *pdst = &planes[plane];
> +				struct v4l2_plane *psrc = &b->m.planes[plane];
> +
> +				if (psrc->bytesused == 0)
> +					vb2_warn_zero_bytesused(vb);
> +
> +				if (vb->vb2_queue->allow_zero_bytesused)
> +					pdst->bytesused = psrc->bytesused;
> +				else
> +					pdst->bytesused = psrc->bytesused ?
> +						psrc->bytesused : pdst->length;
> +				pdst->data_offset = psrc->data_offset;
> +			}
> +		}
> +	} else {
> +		/*
> +		 * Single-planar buffers do not use planes array,
> +		 * so fill in relevant v4l2_buffer struct fields instead.
> +		 * In videobuf we use our internal V4l2_planes struct for
> +		 * single-planar buffers as well, for simplicity.
> +		 *
> +		 * If bytesused == 0 for the output buffer, then fall back
> +		 * to the full buffer size as that's a sensible default.
> +		 *
> +		 * Some drivers, e.g. old codec drivers, use bytesused == 0 as
> +		 * a way to indicate that streaming is finished. In that case,
> +		 * the driver should use the allow_zero_bytesused flag to keep
> +		 * old userspace applications working.
> +		 */
> +		switch (b->memory) {
> +		case VB2_MEMORY_USERPTR:
> +			planes[0].m.userptr = b->m.userptr;
> +			planes[0].length = b->length;
> +			break;
> +		case VB2_MEMORY_DMABUF:
> +			planes[0].m.fd = b->m.fd;
> +			planes[0].length = b->length;
> +			break;
> +		default:
> +			planes[0].m.offset = vb->planes[0].m.offset;
> +			planes[0].length = vb->planes[0].length;
> +			break;
> +		}
> +
> +		planes[0].data_offset = 0;
> +		if (V4L2_TYPE_IS_OUTPUT(b->type)) {
> +			if (b->bytesused == 0)
> +				vb2_warn_zero_bytesused(vb);
> +
> +			if (vb->vb2_queue->allow_zero_bytesused)
> +				planes[0].bytesused = b->bytesused;
> +			else
> +				planes[0].bytesused = b->bytesused ?
> +					b->bytesused : planes[0].length;
> +		} else
> +			planes[0].bytesused = 0;
> +
> +	}
> +
> +	/* Zero flags that we handle */
> +	vbuf->flags = b->flags & ~V4L2_BUFFER_MASK_FLAGS;
> +	if (!vb->vb2_queue->copy_timestamp || !V4L2_TYPE_IS_OUTPUT(b->type)) {
> +		/*
> +		 * Non-COPY timestamps and non-OUTPUT queues will get
> +		 * their timestamp and timestamp source flags from the
> +		 * queue.
> +		 */
> +		vbuf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
> +	}
> +
> +	if (V4L2_TYPE_IS_OUTPUT(b->type)) {
> +		/*
> +		 * For output buffers mask out the timecode flag:
> +		 * this will be handled later in vb2_qbuf().
> +		 * The 'field' is valid metadata for this output buffer
> +		 * and so that needs to be copied here.
> +		 */
> +		vbuf->flags &= ~V4L2_BUF_FLAG_TIMECODE;
> +		vbuf->field = b->field;
> +	} else {
> +		/* Zero any output buffer flags as this is a capture buffer */
> +		vbuf->flags &= ~V4L2_BUFFER_OUT_FLAGS;
> +		/* Zero last flag, this is a signal from driver to userspace */
> +		vbuf->flags &= ~V4L2_BUF_FLAG_LAST;
> +	}
> +
> +	return 0;
> +}
> +
>  static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b,
>  				    const char *opname)
>  {
> +	struct vb2_v4l2_buffer *vbuf;
> +	struct vb2_buffer *vb;
> +	int ret;
> +
>  	if (b->type != q->type) {
>  		dprintk(1, "%s: invalid buffer type\n", opname);
>  		return -EINVAL;
> @@ -178,7 +346,15 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b,
>  		return -EINVAL;
>  	}
>  
> -	return __verify_planes_array(q->bufs[b->index], b);
> +	vb = q->bufs[b->index];
> +	vbuf = to_vb2_v4l2_buffer(vb);
> +	ret = __verify_planes_array(vb, b);
> +	if (ret)
> +		return ret;
> +
> +	/* Copy relevant information provided by the userspace */
> +	memset(vbuf->planes, 0, sizeof(vbuf->planes[0]) * vb->num_planes);
> +	return vb2_fill_vb2_v4l2_buffer(vb, b);
>  }
>  
>  /*
> @@ -291,153 +467,22 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
>   * v4l2_buffer by the userspace. It also verifies that struct
>   * v4l2_buffer has a valid number of planes.
>   */
> -static int __fill_vb2_buffer(struct vb2_buffer *vb,
> -		const void *pb, struct vb2_plane *planes)
> +static int __fill_vb2_buffer(struct vb2_buffer *vb, struct vb2_plane *planes)
>  {
> -	struct vb2_queue *q = vb->vb2_queue;
> -	const struct v4l2_buffer *b = pb;
>  	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
>  	unsigned int plane;
> -	int ret;
> -
> -	ret = __verify_length(vb, b);
> -	if (ret < 0) {
> -		dprintk(1, "plane parameters verification failed: %d\n", ret);
> -		return ret;
> -	}
> -	if (b->field == V4L2_FIELD_ALTERNATE && q->is_output) {
> -		/*
> -		 * If the format's field is ALTERNATE, then the buffer's field
> -		 * should be either TOP or BOTTOM, not ALTERNATE since that
> -		 * makes no sense. The driver has to know whether the
> -		 * buffer represents a top or a bottom field in order to
> -		 * program any DMA correctly. Using ALTERNATE is wrong, since
> -		 * that just says that it is either a top or a bottom field,
> -		 * but not which of the two it is.
> -		 */
> -		dprintk(1, "the field is incorrectly set to ALTERNATE for an output buffer\n");
> -		return -EINVAL;
> -	}
> -	vb->timestamp = 0;
> -	vbuf->sequence = 0;
> -
> -	if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
> -		if (b->memory == VB2_MEMORY_USERPTR) {
> -			for (plane = 0; plane < vb->num_planes; ++plane) {
> -				planes[plane].m.userptr =
> -					b->m.planes[plane].m.userptr;
> -				planes[plane].length =
> -					b->m.planes[plane].length;
> -			}
> -		}
> -		if (b->memory == VB2_MEMORY_DMABUF) {
> -			for (plane = 0; plane < vb->num_planes; ++plane) {
> -				planes[plane].m.fd =
> -					b->m.planes[plane].m.fd;
> -				planes[plane].length =
> -					b->m.planes[plane].length;
> -			}
> -		}
> -
> -		/* Fill in driver-provided information for OUTPUT types */
> -		if (V4L2_TYPE_IS_OUTPUT(b->type)) {
> -			/*
> -			 * Will have to go up to b->length when API starts
> -			 * accepting variable number of planes.
> -			 *
> -			 * If bytesused == 0 for the output buffer, then fall
> -			 * back to the full buffer size. In that case
> -			 * userspace clearly never bothered to set it and
> -			 * it's a safe assumption that they really meant to
> -			 * use the full plane sizes.
> -			 *
> -			 * Some drivers, e.g. old codec drivers, use bytesused == 0
> -			 * as a way to indicate that streaming is finished.
> -			 * In that case, the driver should use the
> -			 * allow_zero_bytesused flag to keep old userspace
> -			 * applications working.
> -			 */
> -			for (plane = 0; plane < vb->num_planes; ++plane) {
> -				struct vb2_plane *pdst = &planes[plane];
> -				struct v4l2_plane *psrc = &b->m.planes[plane];
> -
> -				if (psrc->bytesused == 0)
> -					vb2_warn_zero_bytesused(vb);
>  
> -				if (vb->vb2_queue->allow_zero_bytesused)
> -					pdst->bytesused = psrc->bytesused;
> -				else
> -					pdst->bytesused = psrc->bytesused ?
> -						psrc->bytesused : pdst->length;
> -				pdst->data_offset = psrc->data_offset;
> -			}
> -		}
> -	} else {
> -		/*
> -		 * Single-planar buffers do not use planes array,
> -		 * so fill in relevant v4l2_buffer struct fields instead.
> -		 * In videobuf we use our internal V4l2_planes struct for
> -		 * single-planar buffers as well, for simplicity.
> -		 *
> -		 * If bytesused == 0 for the output buffer, then fall back
> -		 * to the full buffer size as that's a sensible default.
> -		 *
> -		 * Some drivers, e.g. old codec drivers, use bytesused == 0 as
> -		 * a way to indicate that streaming is finished. In that case,
> -		 * the driver should use the allow_zero_bytesused flag to keep
> -		 * old userspace applications working.
> -		 */
> -		if (b->memory == VB2_MEMORY_USERPTR) {
> -			planes[0].m.userptr = b->m.userptr;
> -			planes[0].length = b->length;
> -		}
> +	if (!vb->vb2_queue->is_output || !vb->vb2_queue->copy_timestamp)
> +		vb->timestamp = 0;
>  
> -		if (b->memory == VB2_MEMORY_DMABUF) {
> -			planes[0].m.fd = b->m.fd;
> -			planes[0].length = b->length;
> +	for (plane = 0; plane < vb->num_planes; ++plane) {
> +		if (vb->vb2_queue->memory != VB2_MEMORY_MMAP) {
> +			planes[plane].m = vbuf->planes[plane].m;
> +			planes[plane].length = vbuf->planes[plane].length;
>  		}
> -
> -		if (V4L2_TYPE_IS_OUTPUT(b->type)) {
> -			if (b->bytesused == 0)
> -				vb2_warn_zero_bytesused(vb);
> -
> -			if (vb->vb2_queue->allow_zero_bytesused)
> -				planes[0].bytesused = b->bytesused;
> -			else
> -				planes[0].bytesused = b->bytesused ?
> -					b->bytesused : planes[0].length;
> -		} else
> -			planes[0].bytesused = 0;
> -
> -	}
> -
> -	/* Zero flags that the vb2 core handles */
> -	vbuf->flags = b->flags & ~V4L2_BUFFER_MASK_FLAGS;
> -	if (!vb->vb2_queue->copy_timestamp || !V4L2_TYPE_IS_OUTPUT(b->type)) {
> -		/*
> -		 * Non-COPY timestamps and non-OUTPUT queues will get
> -		 * their timestamp and timestamp source flags from the
> -		 * queue.
> -		 */
> -		vbuf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
> +		planes[plane].bytesused = vbuf->planes[plane].bytesused;
> +		planes[plane].data_offset = vbuf->planes[plane].data_offset;
>  	}
> -
> -	if (V4L2_TYPE_IS_OUTPUT(b->type)) {
> -		/*
> -		 * For output buffers mask out the timecode flag:
> -		 * this will be handled later in vb2_qbuf().
> -		 * The 'field' is valid metadata for this output buffer
> -		 * and so that needs to be copied here.
> -		 */
> -		vbuf->flags &= ~V4L2_BUF_FLAG_TIMECODE;
> -		vbuf->field = b->field;
> -	} else {
> -		/* Zero any output buffer flags as this is a capture buffer */
> -		vbuf->flags &= ~V4L2_BUFFER_OUT_FLAGS;
> -		/* Zero last flag, this is a signal from driver to userspace */
> -		vbuf->flags &= ~V4L2_BUF_FLAG_LAST;
> -	}
> -
>  	return 0;
>  }
>  
> diff --git a/drivers/media/dvb-core/dvb_vb2.c b/drivers/media/dvb-core/dvb_vb2.c
> index b811adf88afa..da6a8cec7d42 100644
> --- a/drivers/media/dvb-core/dvb_vb2.c
> +++ b/drivers/media/dvb-core/dvb_vb2.c
> @@ -146,8 +146,7 @@ static void _fill_dmx_buffer(struct vb2_buffer *vb, void *pb)
>  	dprintk(3, "[%s]\n", ctx->name);
>  }
>  
> -static int _fill_vb2_buffer(struct vb2_buffer *vb,
> -			    const void *pb, struct vb2_plane *planes)
> +static int _fill_vb2_buffer(struct vb2_buffer *vb, struct vb2_plane *planes)
>  {
>  	struct dvb_vb2_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
>  
> diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
> index f6818f732f34..224c4820a044 100644
> --- a/include/media/videobuf2-core.h
> +++ b/include/media/videobuf2-core.h
> @@ -417,8 +417,7 @@ struct vb2_ops {
>  struct vb2_buf_ops {
>  	int (*verify_planes_array)(struct vb2_buffer *vb, const void *pb);
>  	void (*fill_user_buffer)(struct vb2_buffer *vb, void *pb);
> -	int (*fill_vb2_buffer)(struct vb2_buffer *vb, const void *pb,
> -				struct vb2_plane *planes);
> +	int (*fill_vb2_buffer)(struct vb2_buffer *vb, struct vb2_plane *planes);
>  	void (*copy_timestamp)(struct vb2_buffer *vb, const void *pb);
>  };
>  
> diff --git a/include/media/videobuf2-v4l2.h b/include/media/videobuf2-v4l2.h
> index 3d5e2d739f05..097bf3e6951d 100644
> --- a/include/media/videobuf2-v4l2.h
> +++ b/include/media/videobuf2-v4l2.h
> @@ -32,6 +32,7 @@
>   *		&enum v4l2_field.
>   * @timecode:	frame timecode.
>   * @sequence:	sequence count of this frame.
> + * @planes:	plane information (userptr/fd, length, bytesused, data_offset).
>   *
>   * Should contain enough information to be able to cover all the fields
>   * of &struct v4l2_buffer at ``videodev2.h``.
> @@ -43,6 +44,7 @@ struct vb2_v4l2_buffer {
>  	__u32			field;
>  	struct v4l2_timecode	timecode;
>  	__u32			sequence;
> +	struct vb2_plane	planes[VB2_MAX_PLANES];
>  };
>  
>  /*



Thanks,
Mauro

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

* Re: [PATCHv17 18/34] davinci_vpfe: remove bogus vb2->state check
  2018-08-04 12:45 ` [PATCHv17 18/34] davinci_vpfe: remove bogus vb2->state check Hans Verkuil
@ 2018-08-13 11:17   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-13 11:17 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:45:10 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> There is no need to check the vb2 state in the buf_prepare
> callback: it can never be wrong.
> 
> Since VB2_BUF_STATE_PREPARED will be removed in the next patch
> we'll remove this unnecessary check (and use of that state) first.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>

Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>

> ---
>  drivers/staging/media/davinci_vpfe/vpfe_video.c | 4 ----
>  1 file changed, 4 deletions(-)
> 
> diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c
> index 1269a983455e..4e3ec7fdc90d 100644
> --- a/drivers/staging/media/davinci_vpfe/vpfe_video.c
> +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c
> @@ -1135,10 +1135,6 @@ static int vpfe_buffer_prepare(struct vb2_buffer *vb)
>  
>  	v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_prepare\n");
>  
> -	if (vb->state != VB2_BUF_STATE_ACTIVE &&
> -	    vb->state != VB2_BUF_STATE_PREPARED)
> -		return 0;
> -
>  	/* Initialize buffer */
>  	vb2_set_plane_payload(vb, 0, video->fmt.fmt.pix.sizeimage);
>  	if (vb2_plane_vaddr(vb, 0) &&



Thanks,
Mauro

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

* Re: [PATCHv17 19/34] vb2: drop VB2_BUF_STATE_PREPARED, use bool prepared/synced instead
  2018-08-04 12:45 ` [PATCHv17 19/34] vb2: drop VB2_BUF_STATE_PREPARED, use bool prepared/synced instead Hans Verkuil
@ 2018-08-13 11:30   ` Mauro Carvalho Chehab
  2018-08-14  8:58     ` Hans Verkuil
  0 siblings, 1 reply; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-13 11:30 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:45:11 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> The PREPARED state becomes a problem with the request API: a buffer
> could be PREPARED but dequeued, or PREPARED and in state IN_REQUEST.
> 
> PREPARED is really not a state as such, but more a property of the
> buffer. So make new 'prepared' and 'synced' bools instead to remember
> whether the buffer is prepared and/or synced or not.
> 
> V4L2_BUF_FLAG_PREPARED is only set if the buffer is both synced and
> prepared and in the DEQUEUED state.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  .../media/common/videobuf2/videobuf2-core.c   | 38 +++++++++++++------
>  .../media/common/videobuf2/videobuf2-v4l2.c   | 16 +++++---
>  include/media/videobuf2-core.h                |  6 ++-
>  3 files changed, 40 insertions(+), 20 deletions(-)
> 
> diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
> index 7401a17c80ca..eead693ba619 100644
> --- a/drivers/media/common/videobuf2/videobuf2-core.c
> +++ b/drivers/media/common/videobuf2/videobuf2-core.c
> @@ -682,7 +682,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
>  		}
>  
>  		/*
> -		 * Call queue_cancel to clean up any buffers in the PREPARED or
> +		 * Call queue_cancel to clean up any buffers in the
>  		 * QUEUED state which is possible if buffers were prepared or
>  		 * queued without ever calling STREAMON.
>  		 */
> @@ -921,6 +921,7 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
>  		/* sync buffers */
>  		for (plane = 0; plane < vb->num_planes; ++plane)
>  			call_void_memop(vb, finish, vb->planes[plane].mem_priv);
> +		vb->synced = false;

Shouldn't be prepared cleaned as well on reqbufs?

>  	}
>  
>  	spin_lock_irqsave(&q->done_lock, flags);
> @@ -1239,6 +1240,7 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
>  static int __buf_prepare(struct vb2_buffer *vb)
>  {
>  	struct vb2_queue *q = vb->vb2_queue;
> +	enum vb2_buffer_state orig_state = vb->state;
>  	unsigned int plane;
>  	int ret;
>  
> @@ -1247,6 +1249,10 @@ static int __buf_prepare(struct vb2_buffer *vb)
>  		return -EIO;
>  	}
>  
> +	if (vb->prepared)
> +		return 0;
> +	WARN_ON(vb->synced);
> +
>  	vb->state = VB2_BUF_STATE_PREPARING;
>  
>  	switch (q->memory) {

> @@ -1262,11 +1268,12 @@ static int __buf_prepare(struct vb2_buffer *vb)
>  	default:
>  		WARN(1, "Invalid queue type\n");
>  		ret = -EINVAL;
> +		break;
>  	}

Hmm... is this hunk a bug fix? if so, please split into a separate patch
and add a c/c stable.

>  
>  	if (ret) {
>  		dprintk(1, "buffer preparation failed: %d\n", ret);
> -		vb->state = VB2_BUF_STATE_DEQUEUED;
> +		vb->state = orig_state;
>  		return ret;
>  	}
>  
> @@ -1274,7 +1281,9 @@ static int __buf_prepare(struct vb2_buffer *vb)
>  	for (plane = 0; plane < vb->num_planes; ++plane)
>  		call_void_memop(vb, prepare, vb->planes[plane].mem_priv);
>  
> -	vb->state = VB2_BUF_STATE_PREPARED;
> +	vb->synced = true;
> +	vb->prepared = true;
> +	vb->state = orig_state;
>  
>  	return 0;
>  }
> @@ -1290,6 +1299,10 @@ int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb)
>  			vb->state);
>  		return -EINVAL;
>  	}
> +	if (vb->prepared) {
> +		dprintk(1, "buffer already prepared\n");
> +		return -EINVAL;
> +	}
>  
>  	ret = __buf_prepare(vb);
>  	if (ret)
> @@ -1381,11 +1394,11 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb)
>  
>  	switch (vb->state) {
>  	case VB2_BUF_STATE_DEQUEUED:
> -		ret = __buf_prepare(vb);
> -		if (ret)
> -			return ret;
> -		break;
> -	case VB2_BUF_STATE_PREPARED:
> +		if (!vb->prepared) {
> +			ret = __buf_prepare(vb);
> +			if (ret)
> +				return ret;
> +		}
>  		break;
>  	case VB2_BUF_STATE_PREPARING:
>  		dprintk(1, "buffer still being prepared\n");
> @@ -1611,6 +1624,7 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb,
>  	}
>  
>  	call_void_vb_qop(vb, buf_finish, vb);
> +	vb->prepared = false;
>  
>  	if (pindex)
>  		*pindex = vb->index;
> @@ -1699,18 +1713,18 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
>  	for (i = 0; i < q->num_buffers; ++i) {
>  		struct vb2_buffer *vb = q->bufs[i];
>  
> -		if (vb->state == VB2_BUF_STATE_PREPARED ||
> -		    vb->state == VB2_BUF_STATE_QUEUED) {
> +		if (vb->synced) {
>  			unsigned int plane;
>  
>  			for (plane = 0; plane < vb->num_planes; ++plane)
>  				call_void_memop(vb, finish,
>  						vb->planes[plane].mem_priv);
> +			vb->synced = false;
>  		}
>  
> -		if (vb->state != VB2_BUF_STATE_DEQUEUED) {
> -			vb->state = VB2_BUF_STATE_PREPARED;
> +		if (vb->prepared) {
>  			call_void_vb_qop(vb, buf_finish, vb);
> +			vb->prepared = false;
>  		}
>  		__vb2_dqbuf(vb);
>  	}
> diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c
> index 360dc4e7d413..a677e2c26247 100644
> --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
> +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
> @@ -352,9 +352,13 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b,
>  	if (ret)
>  		return ret;
>  
> -	/* Copy relevant information provided by the userspace */
> -	memset(vbuf->planes, 0, sizeof(vbuf->planes[0]) * vb->num_planes);
> -	return vb2_fill_vb2_v4l2_buffer(vb, b);
> +	if (!vb->prepared) {
> +		/* Copy relevant information provided by the userspace */
> +		memset(vbuf->planes, 0,
> +		       sizeof(vbuf->planes[0]) * vb->num_planes);
> +		ret = vb2_fill_vb2_v4l2_buffer(vb, b);
> +	}
> +	return ret;
>  }
>  
>  /*
> @@ -443,9 +447,6 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
>  	case VB2_BUF_STATE_DONE:
>  		b->flags |= V4L2_BUF_FLAG_DONE;
>  		break;
> -	case VB2_BUF_STATE_PREPARED:
> -		b->flags |= V4L2_BUF_FLAG_PREPARED;
> -		break;
>  	case VB2_BUF_STATE_PREPARING:
>  	case VB2_BUF_STATE_DEQUEUED:
>  	case VB2_BUF_STATE_REQUEUEING:
> @@ -453,6 +454,9 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
>  		break;
>  	}
>  
> +	if (vb->state == VB2_BUF_STATE_DEQUEUED && vb->synced && vb->prepared)
> +		b->flags |= V4L2_BUF_FLAG_PREPARED;
> +
>  	if (vb2_buffer_in_use(q, vb))
>  		b->flags |= V4L2_BUF_FLAG_MAPPED;
>  
> diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
> index 224c4820a044..5e760d5f280a 100644
> --- a/include/media/videobuf2-core.h
> +++ b/include/media/videobuf2-core.h
> @@ -204,7 +204,6 @@ enum vb2_io_modes {
>   * enum vb2_buffer_state - current video buffer state.
>   * @VB2_BUF_STATE_DEQUEUED:	buffer under userspace control.
>   * @VB2_BUF_STATE_PREPARING:	buffer is being prepared in videobuf.
> - * @VB2_BUF_STATE_PREPARED:	buffer prepared in videobuf and by the driver.
>   * @VB2_BUF_STATE_QUEUED:	buffer queued in videobuf, but not in driver.
>   * @VB2_BUF_STATE_REQUEUEING:	re-queue a buffer to the driver.
>   * @VB2_BUF_STATE_ACTIVE:	buffer queued in driver and possibly used
> @@ -218,7 +217,6 @@ enum vb2_io_modes {
>  enum vb2_buffer_state {
>  	VB2_BUF_STATE_DEQUEUED,
>  	VB2_BUF_STATE_PREPARING,
> -	VB2_BUF_STATE_PREPARED,
>  	VB2_BUF_STATE_QUEUED,
>  	VB2_BUF_STATE_REQUEUEING,
>  	VB2_BUF_STATE_ACTIVE,
> @@ -250,6 +248,8 @@ struct vb2_buffer {
>  	/* private: internal use only
>  	 *
>  	 * state:		current buffer state; do not change
> +	 * synced:		this buffer has been synced
> +	 * prepared:		this buffer has been prepared

Please describe better what "prepared" and "synced" means, as
the above doesn't really help.

From what I got:

	- "prepared" means that buf_prepare() was called and
	  vidioc_dqbuf() (nor cancel) was not called;
	- "sync" means that the buf_prepare() was called
	  and the v4l2_buffer_done() (nor cancel)

right?


>  	 * queued_entry:	entry on the queued buffers list, which holds
>  	 *			all buffers queued from userspace
>  	 * done_entry:		entry on the list that stores all buffers ready
> @@ -257,6 +257,8 @@ struct vb2_buffer {
>  	 * vb2_plane:		per-plane information; do not change
>  	 */
>  	enum vb2_buffer_state	state;
> +	bool			synced;
> +	bool			prepared;
>  
>  	struct vb2_plane	planes[VB2_MAX_PLANES];
>  	struct list_head	queued_entry;



Thanks,
Mauro

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

* Re: [PATCHv17 20/34] videodev2.h: Add request_fd field to v4l2_buffer
  2018-08-04 12:45 ` [PATCHv17 20/34] videodev2.h: Add request_fd field to v4l2_buffer Hans Verkuil
@ 2018-08-13 11:41   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-13 11:41 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:45:12 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> When queuing buffers allow for passing the request that should
> be associated with this buffer.
> 
> If V4L2_BUF_FLAG_REQUEST_FD is set, then request_fd is used as
> the file descriptor.
> 
> If a buffer is stored in a request, but not yet queued to the
> driver, then V4L2_BUF_FLAG_IN_REQUEST is set.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>

> ---
>  drivers/media/common/videobuf2/videobuf2-v4l2.c |  2 +-
>  drivers/media/usb/cpia2/cpia2_v4l.c             |  2 +-
>  drivers/media/v4l2-core/v4l2-compat-ioctl32.c   |  9 ++++++---
>  drivers/media/v4l2-core/v4l2-ioctl.c            |  4 ++--
>  include/uapi/linux/videodev2.h                  | 10 +++++++++-
>  5 files changed, 19 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c
> index a677e2c26247..64905d87465c 100644
> --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
> +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
> @@ -384,7 +384,7 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
>  	b->timecode = vbuf->timecode;
>  	b->sequence = vbuf->sequence;
>  	b->reserved2 = 0;
> -	b->reserved = 0;
> +	b->request_fd = 0;
>  
>  	if (q->is_multiplanar) {
>  		/*
> diff --git a/drivers/media/usb/cpia2/cpia2_v4l.c b/drivers/media/usb/cpia2/cpia2_v4l.c
> index 99f106b13280..13aee9f67d05 100644
> --- a/drivers/media/usb/cpia2/cpia2_v4l.c
> +++ b/drivers/media/usb/cpia2/cpia2_v4l.c
> @@ -949,7 +949,7 @@ static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
>  	buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer;
>  	buf->length = cam->frame_size;
>  	buf->reserved2 = 0;
> -	buf->reserved = 0;
> +	buf->request_fd = 0;
>  	memset(&buf->timecode, 0, sizeof(buf->timecode));
>  
>  	DBG("DQBUF #%d status:%d seq:%d length:%d\n", buf->index,
> diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> index dcce86c1fe40..633465d21d04 100644
> --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> @@ -482,7 +482,7 @@ struct v4l2_buffer32 {
>  	} m;
>  	__u32			length;
>  	__u32			reserved2;
> -	__u32			reserved;
> +	__s32			request_fd;
>  };
>  
>  static int get_v4l2_plane32(struct v4l2_plane __user *p64,
> @@ -581,6 +581,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer __user *p64,
>  {
>  	u32 type;
>  	u32 length;
> +	s32 request_fd;
>  	enum v4l2_memory memory;
>  	struct v4l2_plane32 __user *uplane32;
>  	struct v4l2_plane __user *uplane;
> @@ -595,7 +596,9 @@ static int get_v4l2_buffer32(struct v4l2_buffer __user *p64,
>  	    get_user(memory, &p32->memory) ||
>  	    put_user(memory, &p64->memory) ||
>  	    get_user(length, &p32->length) ||
> -	    put_user(length, &p64->length))
> +	    put_user(length, &p64->length) ||
> +	    get_user(request_fd, &p32->request_fd) ||
> +	    put_user(request_fd, &p64->request_fd))
>  		return -EFAULT;
>  
>  	if (V4L2_TYPE_IS_OUTPUT(type))
> @@ -699,7 +702,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer __user *p64,
>  	    copy_in_user(&p32->timecode, &p64->timecode, sizeof(p64->timecode)) ||
>  	    assign_in_user(&p32->sequence, &p64->sequence) ||
>  	    assign_in_user(&p32->reserved2, &p64->reserved2) ||
> -	    assign_in_user(&p32->reserved, &p64->reserved) ||
> +	    assign_in_user(&p32->request_fd, &p64->request_fd) ||
>  	    get_user(length, &p64->length) ||
>  	    put_user(length, &p32->length))
>  		return -EFAULT;
> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
> index 20b5145a5254..2a84ca9e328a 100644
> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> @@ -474,13 +474,13 @@ static void v4l_print_buffer(const void *arg, bool write_only)
>  	const struct v4l2_plane *plane;
>  	int i;
>  
> -	pr_cont("%02ld:%02d:%02d.%08ld index=%d, type=%s, flags=0x%08x, field=%s, sequence=%d, memory=%s",
> +	pr_cont("%02ld:%02d:%02d.%08ld index=%d, type=%s, request_fd=%d, flags=0x%08x, field=%s, sequence=%d, memory=%s",
>  			p->timestamp.tv_sec / 3600,
>  			(int)(p->timestamp.tv_sec / 60) % 60,
>  			(int)(p->timestamp.tv_sec % 60),
>  			(long)p->timestamp.tv_usec,
>  			p->index,
> -			prt_names(p->type, v4l2_type_names),
> +			prt_names(p->type, v4l2_type_names), p->request_fd,
>  			p->flags, prt_names(p->field, v4l2_field_names),
>  			p->sequence, prt_names(p->memory, v4l2_memory_names));
>  
> diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> index 1df0fa983db6..91126b7312f8 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -917,6 +917,7 @@ struct v4l2_plane {
>   * @length:	size in bytes of the buffer (NOT its payload) for single-plane
>   *		buffers (when type != *_MPLANE); number of elements in the
>   *		planes array for multi-plane buffers
> + * @request_fd: fd of the request that this buffer should use
>   *
>   * Contains data exchanged by application and driver using one of the Streaming
>   * I/O methods.
> @@ -941,7 +942,10 @@ struct v4l2_buffer {
>  	} m;
>  	__u32			length;
>  	__u32			reserved2;
> -	__u32			reserved;
> +	union {
> +		__s32		request_fd;
> +		__u32		reserved;
> +	};
>  };
>  
>  /*  Flags for 'flags' field */
> @@ -959,6 +963,8 @@ struct v4l2_buffer {
>  #define V4L2_BUF_FLAG_BFRAME			0x00000020
>  /* Buffer is ready, but the data contained within is corrupted. */
>  #define V4L2_BUF_FLAG_ERROR			0x00000040
> +/* Buffer is added to an unqueued request */
> +#define V4L2_BUF_FLAG_IN_REQUEST		0x00000080
>  /* timecode field is valid */
>  #define V4L2_BUF_FLAG_TIMECODE			0x00000100
>  /* Buffer is prepared for queuing */
> @@ -977,6 +983,8 @@ struct v4l2_buffer {
>  #define V4L2_BUF_FLAG_TSTAMP_SRC_SOE		0x00010000
>  /* mem2mem encoder/decoder */
>  #define V4L2_BUF_FLAG_LAST			0x00100000
> +/* request_fd is valid */
> +#define V4L2_BUF_FLAG_REQUEST_FD		0x00800000
>  
>  /**
>   * struct v4l2_exportbuffer - export of video buffer as DMABUF file descriptor



Thanks,
Mauro

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

* Re: [PATCHv17 21/34] vb2: add init_buffer buffer op
  2018-08-04 12:45 ` [PATCHv17 21/34] vb2: add init_buffer buffer op Hans Verkuil
@ 2018-08-13 11:56   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-13 11:56 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:45:13 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> We need to initialize the request_fd field in struct vb2_v4l2_buffer
> to -1 instead of the default of 0. So we need to add a new op that
> is called when struct vb2_v4l2_buffer is allocated.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>

Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>

> ---
>  drivers/media/common/videobuf2/videobuf2-core.c | 2 ++
>  include/media/videobuf2-core.h                  | 4 ++++
>  2 files changed, 6 insertions(+)
> 
> diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
> index eead693ba619..230f83d6d094 100644
> --- a/drivers/media/common/videobuf2/videobuf2-core.c
> +++ b/drivers/media/common/videobuf2/videobuf2-core.c
> @@ -356,6 +356,8 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory,
>  			vb->planes[plane].length = plane_sizes[plane];
>  			vb->planes[plane].min_length = plane_sizes[plane];
>  		}
> +		call_void_bufop(q, init_buffer, vb);
> +
>  		q->bufs[vb->index] = vb;
>  
>  		/* Allocate video buffer memory for the MMAP type */
> diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
> index 5e760d5f280a..cbda3968d018 100644
> --- a/include/media/videobuf2-core.h
> +++ b/include/media/videobuf2-core.h
> @@ -408,6 +408,9 @@ struct vb2_ops {
>   * @verify_planes_array: Verify that a given user space structure contains
>   *			enough planes for the buffer. This is called
>   *			for each dequeued buffer.
> + * @init_buffer:	given a &vb2_buffer initialize the extra data after
> + *			struct vb2_buffer.
> + *			For V4L2 this is a &struct vb2_v4l2_buffer.
>   * @fill_user_buffer:	given a &vb2_buffer fill in the userspace structure.
>   *			For V4L2 this is a &struct v4l2_buffer.
>   * @fill_vb2_buffer:	given a userspace structure, fill in the &vb2_buffer.
> @@ -418,6 +421,7 @@ struct vb2_ops {
>   */
>  struct vb2_buf_ops {
>  	int (*verify_planes_array)(struct vb2_buffer *vb, const void *pb);
> +	void (*init_buffer)(struct vb2_buffer *vb);
>  	void (*fill_user_buffer)(struct vb2_buffer *vb, void *pb);
>  	int (*fill_vb2_buffer)(struct vb2_buffer *vb, struct vb2_plane *planes);
>  	void (*copy_timestamp)(struct vb2_buffer *vb, const void *pb);



Thanks,
Mauro

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

* Re: [PATCHv17 22/34] videobuf2-core: embed media_request_object
  2018-08-04 12:45 ` [PATCHv17 22/34] videobuf2-core: embed media_request_object Hans Verkuil
@ 2018-08-13 11:58   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-13 11:58 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:45:14 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Make vb2_buffer a request object.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>

Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
> ---
>  include/media/videobuf2-core.h | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
> index cbda3968d018..df92dcdeabb3 100644
> --- a/include/media/videobuf2-core.h
> +++ b/include/media/videobuf2-core.h
> @@ -17,6 +17,7 @@
>  #include <linux/poll.h>
>  #include <linux/dma-buf.h>
>  #include <linux/bitops.h>
> +#include <media/media-request.h>
>  
>  #define VB2_MAX_FRAME	(32)
>  #define VB2_MAX_PLANES	(8)
> @@ -236,6 +237,8 @@ struct vb2_queue;
>   * @num_planes:		number of planes in the buffer
>   *			on an internal driver queue.
>   * @timestamp:		frame timestamp in ns.
> + * @req_obj:		used to bind this buffer to a request. This
> + *			request object has a refcount.
>   */
>  struct vb2_buffer {
>  	struct vb2_queue	*vb2_queue;
> @@ -244,6 +247,7 @@ struct vb2_buffer {
>  	unsigned int		memory;
>  	unsigned int		num_planes;
>  	u64			timestamp;
> +	struct media_request_object	req_obj;
>  
>  	/* private: internal use only
>  	 *



Thanks,
Mauro

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

* Re: [PATCHv17 23/34] videobuf2-core: integrate with media requests
  2018-08-04 12:45 ` [PATCHv17 23/34] videobuf2-core: integrate with media requests Hans Verkuil
@ 2018-08-13 12:09   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-13 12:09 UTC (permalink / raw)
  To: Hans Verkuil, linux-media; +Cc: Hans Verkuil

Em Sat,  4 Aug 2018 14:45:15 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Buffers can now be prepared or queued for a request.
> 
> A buffer is unbound from the request at vb2_buffer_done time or
> when the queue is cancelled.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>

> ---
>  .../media/common/videobuf2/videobuf2-core.c   | 133 +++++++++++++++++-
>  .../media/common/videobuf2/videobuf2-v4l2.c   |   4 +-
>  drivers/media/dvb-core/dvb_vb2.c              |   2 +-
>  include/media/videobuf2-core.h                |  18 ++-
>  4 files changed, 147 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
> index 230f83d6d094..1efb9c8b359d 100644
> --- a/drivers/media/common/videobuf2/videobuf2-core.c
> +++ b/drivers/media/common/videobuf2/videobuf2-core.c
> @@ -499,8 +499,9 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
>  			pr_info("     buf_init: %u buf_cleanup: %u buf_prepare: %u buf_finish: %u\n",
>  				vb->cnt_buf_init, vb->cnt_buf_cleanup,
>  				vb->cnt_buf_prepare, vb->cnt_buf_finish);
> -			pr_info("     buf_queue: %u buf_done: %u\n",
> -				vb->cnt_buf_queue, vb->cnt_buf_done);
> +			pr_info("     buf_queue: %u buf_done: %u buf_request_complete: %u\n",
> +				vb->cnt_buf_queue, vb->cnt_buf_done,
> +				vb->cnt_buf_request_complete);
>  			pr_info("     alloc: %u put: %u prepare: %u finish: %u mmap: %u\n",
>  				vb->cnt_mem_alloc, vb->cnt_mem_put,
>  				vb->cnt_mem_prepare, vb->cnt_mem_finish,
> @@ -936,6 +937,14 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
>  		vb->state = state;
>  	}
>  	atomic_dec(&q->owned_by_drv_count);
> +
> +	if (vb->req_obj.req) {
> +		/* This is not supported at the moment */
> +		WARN_ON(state == VB2_BUF_STATE_REQUEUEING);
> +		media_request_object_unbind(&vb->req_obj);
> +		media_request_object_put(&vb->req_obj);
> +	}
> +
>  	spin_unlock_irqrestore(&q->done_lock, flags);
>  
>  	trace_vb2_buf_done(q, vb);
> @@ -1290,6 +1299,60 @@ static int __buf_prepare(struct vb2_buffer *vb)
>  	return 0;
>  }
>  
> +static int vb2_req_prepare(struct media_request_object *obj)
> +{
> +	struct vb2_buffer *vb = container_of(obj, struct vb2_buffer, req_obj);
> +	int ret;
> +
> +	if (WARN_ON(vb->state != VB2_BUF_STATE_IN_REQUEST))
> +		return -EINVAL;
> +
> +	mutex_lock(vb->vb2_queue->lock);
> +	ret = __buf_prepare(vb);
> +	mutex_unlock(vb->vb2_queue->lock);
> +	return ret;
> +}
> +
> +static void __vb2_dqbuf(struct vb2_buffer *vb);
> +
> +static void vb2_req_unprepare(struct media_request_object *obj)
> +{
> +	struct vb2_buffer *vb = container_of(obj, struct vb2_buffer, req_obj);
> +
> +	mutex_lock(vb->vb2_queue->lock);
> +	__vb2_dqbuf(vb);
> +	vb->state = VB2_BUF_STATE_IN_REQUEST;
> +	mutex_unlock(vb->vb2_queue->lock);
> +	WARN_ON(!vb->req_obj.req);
> +}
> +
> +int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb,
> +		  struct media_request *req);
> +
> +static void vb2_req_queue(struct media_request_object *obj)
> +{
> +	struct vb2_buffer *vb = container_of(obj, struct vb2_buffer, req_obj);
> +
> +	mutex_lock(vb->vb2_queue->lock);
> +	vb2_core_qbuf(vb->vb2_queue, vb->index, NULL, NULL);
> +	mutex_unlock(vb->vb2_queue->lock);
> +}
> +
> +static void vb2_req_release(struct media_request_object *obj)
> +{
> +	struct vb2_buffer *vb = container_of(obj, struct vb2_buffer, req_obj);
> +
> +	if (vb->state == VB2_BUF_STATE_IN_REQUEST)
> +		vb->state = VB2_BUF_STATE_DEQUEUED;

Shouldn't it be protected by the lock?

> +}
> +
> +static const struct media_request_object_ops vb2_core_req_ops = {
> +	.prepare = vb2_req_prepare,
> +	.unprepare = vb2_req_unprepare,
> +	.queue = vb2_req_queue,
> +	.release = vb2_req_release,
> +};
> +
>  int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb)
>  {
>  	struct vb2_buffer *vb;
> @@ -1315,7 +1378,7 @@ int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb)
>  
>  	dprintk(2, "prepare of buffer %d succeeded\n", vb->index);
>  
> -	return ret;
> +	return 0;
>  }
>  EXPORT_SYMBOL_GPL(vb2_core_prepare_buf);
>  
> @@ -1382,7 +1445,8 @@ static int vb2_start_streaming(struct vb2_queue *q)
>  	return ret;
>  }
>  
> -int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb)
> +int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb,
> +		  struct media_request *req)
>  {
>  	struct vb2_buffer *vb;
>  	int ret;
> @@ -1394,8 +1458,39 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb)
>  
>  	vb = q->bufs[index];
>  
> +	if (req) {
> +		int ret;
> +
> +		if (vb->state != VB2_BUF_STATE_DEQUEUED) {
> +			dprintk(1, "buffer %d not in dequeued state\n",
> +				vb->index);
> +			return -EINVAL;
> +		}
> +
> +		media_request_object_init(&vb->req_obj);
> +
> +		/* Make sure the request is in a safe state for updating. */
> +		ret = media_request_lock_for_update(req);
> +		if (ret)
> +			return ret;
> +		ret = media_request_object_bind(req, &vb2_core_req_ops,
> +						q, &vb->req_obj);
> +		media_request_unlock_for_update(req);
> +		if (ret)
> +			return ret;
> +
> +		vb->state = VB2_BUF_STATE_IN_REQUEST;
> +		/* Fill buffer information for the userspace */
> +		if (pb)
> +			call_void_bufop(q, fill_user_buffer, vb, pb);
> +
> +		dprintk(2, "qbuf of buffer %d succeeded\n", vb->index);
> +		return 0;
> +	}
> +
>  	switch (vb->state) {
>  	case VB2_BUF_STATE_DEQUEUED:
> +	case VB2_BUF_STATE_IN_REQUEST:
>  		if (!vb->prepared) {
>  			ret = __buf_prepare(vb);
>  			if (ret)
> @@ -1601,6 +1696,11 @@ static void __vb2_dqbuf(struct vb2_buffer *vb)
>  			call_void_memop(vb, unmap_dmabuf, vb->planes[i].mem_priv);
>  			vb->planes[i].dbuf_mapped = 0;
>  		}
> +	if (vb->req_obj.req) {
> +		media_request_object_unbind(&vb->req_obj);
> +		media_request_object_put(&vb->req_obj);
> +	}
> +	call_void_bufop(q, init_buffer, vb);
>  }
>  
>  int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb,
> @@ -1714,6 +1814,25 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
>  	 */
>  	for (i = 0; i < q->num_buffers; ++i) {
>  		struct vb2_buffer *vb = q->bufs[i];
> +		struct media_request *req = vb->req_obj.req;
> +
> +		/*
> +		 * If a request is associated with this buffer, then
> +		 * call buf_request_cancel() to give the driver to complete()
> +		 * related request objects. Otherwise those objects would
> +		 * never complete.
> +		 */
> +		if (req) {
> +			enum media_request_state state;
> +			unsigned long flags;
> +
> +			spin_lock_irqsave(&req->lock, flags);
> +			state = req->state;
> +			spin_unlock_irqrestore(&req->lock, flags);
> +
> +			if (state == MEDIA_REQUEST_STATE_QUEUED)
> +				call_void_vb_qop(vb, buf_request_complete, vb);
> +		}
>  
>  		if (vb->synced) {
>  			unsigned int plane;
> @@ -2283,7 +2402,7 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read)
>  		 * Queue all buffers.
>  		 */
>  		for (i = 0; i < q->num_buffers; i++) {
> -			ret = vb2_core_qbuf(q, i, NULL);
> +			ret = vb2_core_qbuf(q, i, NULL, NULL);
>  			if (ret)
>  				goto err_reqbufs;
>  			fileio->bufs[i].queued = 1;
> @@ -2462,7 +2581,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_
>  
>  		if (copy_timestamp)
>  			b->timestamp = ktime_get_ns();
> -		ret = vb2_core_qbuf(q, index, NULL);
> +		ret = vb2_core_qbuf(q, index, NULL, NULL);
>  		dprintk(5, "vb2_dbuf result: %d\n", ret);
>  		if (ret)
>  			return ret;
> @@ -2565,7 +2684,7 @@ static int vb2_thread(void *data)
>  		if (copy_timestamp)
>  			vb->timestamp = ktime_get_ns();
>  		if (!threadio->stop)
> -			ret = vb2_core_qbuf(q, vb->index, NULL);
> +			ret = vb2_core_qbuf(q, vb->index, NULL, NULL);
>  		call_void_qop(q, wait_prepare, q);
>  		if (ret || threadio->stop)
>  			break;
> diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c
> index 64905d87465c..ea9db4b3f59a 100644
> --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
> +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
> @@ -441,6 +441,8 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
>  	case VB2_BUF_STATE_ACTIVE:
>  		b->flags |= V4L2_BUF_FLAG_QUEUED;
>  		break;
> +	case VB2_BUF_STATE_IN_REQUEST:
> +		break;
>  	case VB2_BUF_STATE_ERROR:
>  		b->flags |= V4L2_BUF_FLAG_ERROR;
>  		/* fall through */
> @@ -619,7 +621,7 @@ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
>  	}
>  
>  	ret = vb2_queue_or_prepare_buf(q, b, "qbuf");
> -	return ret ? ret : vb2_core_qbuf(q, b->index, b);
> +	return ret ? ret : vb2_core_qbuf(q, b->index, b, NULL);
>  }
>  EXPORT_SYMBOL_GPL(vb2_qbuf);
>  
> diff --git a/drivers/media/dvb-core/dvb_vb2.c b/drivers/media/dvb-core/dvb_vb2.c
> index da6a8cec7d42..f1e7f0536028 100644
> --- a/drivers/media/dvb-core/dvb_vb2.c
> +++ b/drivers/media/dvb-core/dvb_vb2.c
> @@ -384,7 +384,7 @@ int dvb_vb2_qbuf(struct dvb_vb2_ctx *ctx, struct dmx_buffer *b)
>  {
>  	int ret;
>  
> -	ret = vb2_core_qbuf(&ctx->vb_q, b->index, b);
> +	ret = vb2_core_qbuf(&ctx->vb_q, b->index, b, NULL);
>  	if (ret) {
>  		dprintk(1, "[%s] index=%d errno=%d\n", ctx->name,
>  			b->index, ret);
> diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
> index df92dcdeabb3..8a8d7732d182 100644
> --- a/include/media/videobuf2-core.h
> +++ b/include/media/videobuf2-core.h
> @@ -204,6 +204,7 @@ enum vb2_io_modes {
>  /**
>   * enum vb2_buffer_state - current video buffer state.
>   * @VB2_BUF_STATE_DEQUEUED:	buffer under userspace control.
> + * @VB2_BUF_STATE_IN_REQUEST:	buffer is queued in media request.
>   * @VB2_BUF_STATE_PREPARING:	buffer is being prepared in videobuf.
>   * @VB2_BUF_STATE_QUEUED:	buffer queued in videobuf, but not in driver.
>   * @VB2_BUF_STATE_REQUEUEING:	re-queue a buffer to the driver.
> @@ -217,6 +218,7 @@ enum vb2_io_modes {
>   */
>  enum vb2_buffer_state {
>  	VB2_BUF_STATE_DEQUEUED,
> +	VB2_BUF_STATE_IN_REQUEST,
>  	VB2_BUF_STATE_PREPARING,
>  	VB2_BUF_STATE_QUEUED,
>  	VB2_BUF_STATE_REQUEUEING,
> @@ -293,6 +295,7 @@ struct vb2_buffer {
>  	u32		cnt_buf_finish;
>  	u32		cnt_buf_cleanup;
>  	u32		cnt_buf_queue;
> +	u32		cnt_buf_request_complete;
>  
>  	/* This counts the number of calls to vb2_buffer_done() */
>  	u32		cnt_buf_done;
> @@ -386,6 +389,11 @@ struct vb2_buffer {
>   *			ioctl; might be called before @start_streaming callback
>   *			if user pre-queued buffers before calling
>   *			VIDIOC_STREAMON().
> + * @buf_request_complete: a buffer that was never queued to the driver but is
> + *			associated with a queued request was canceled.
> + *			The driver will have to mark associated objects in the
> + *			request as completed; required if requests are
> + *			supported.
>   */
>  struct vb2_ops {
>  	int (*queue_setup)(struct vb2_queue *q,
> @@ -404,6 +412,8 @@ struct vb2_ops {
>  	void (*stop_streaming)(struct vb2_queue *q);
>  
>  	void (*buf_queue)(struct vb2_buffer *vb);
> +
> +	void (*buf_request_complete)(struct vb2_buffer *vb);
>  };
>  
>  /**
> @@ -761,12 +771,17 @@ int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb);
>   * @index:	id number of the buffer
>   * @pb:		buffer structure passed from userspace to
>   *		v4l2_ioctl_ops->vidioc_qbuf handler in driver
> + * @req:	pointer to &struct media_request, may be NULL.
>   *
>   * Videobuf2 core helper to implement VIDIOC_QBUF() operation. It is called
>   * internally by VB2 by an API-specific handler, like ``videobuf2-v4l2.h``.
>   *
>   * This function:
>   *
> + * #) If @req is non-NULL, then the buffer will be bound to this
> + *    media request and it returns. The buffer will be prepared and
> + *    queued to the driver (i.e. the next two steps) when the request
> + *    itself is queued.
>   * #) if necessary, calls &vb2_ops->buf_prepare callback in the driver
>   *    (if provided), in which driver-specific buffer initialization can
>   *    be performed;
> @@ -775,7 +790,8 @@ int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb);
>   *
>   * Return: returns zero on success; an error code otherwise.
>   */
> -int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb);
> +int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb,
> +		  struct media_request *req);
>  
>  /**
>   * vb2_core_dqbuf() - Dequeue a buffer to the userspace



Thanks,
Mauro

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

* Re: [PATCHv17 24/34] videobuf2-v4l2: integrate with media requests
  2018-08-04 12:45 ` [PATCHv17 24/34] videobuf2-v4l2: " Hans Verkuil
@ 2018-08-13 14:30   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-13 14:30 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:45:16 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> This implements the V4L2 part of the request support. The main
> change is that vb2_qbuf and vb2_prepare_buf now have a new
> media_device pointer. This required changes to several drivers
> that did not use the vb2_ioctl_qbuf/prepare_buf helper functions.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>

Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>

> ---
>  .../media/common/videobuf2/videobuf2-core.c   |  13 +-
>  .../media/common/videobuf2/videobuf2-v4l2.c   | 112 ++++++++++++++++--
>  drivers/media/platform/omap3isp/ispvideo.c    |   2 +-
>  .../media/platform/s3c-camif/camif-capture.c  |   4 +-
>  drivers/media/platform/s5p-mfc/s5p_mfc_dec.c  |   4 +-
>  drivers/media/platform/s5p-mfc/s5p_mfc_enc.c  |   4 +-
>  .../media/platform/soc_camera/soc_camera.c    |   4 +-
>  drivers/media/usb/uvc/uvc_queue.c             |   5 +-
>  drivers/media/usb/uvc/uvc_v4l2.c              |   3 +-
>  drivers/media/usb/uvc/uvcvideo.h              |   1 +
>  drivers/media/v4l2-core/v4l2-mem2mem.c        |   6 +-
>  .../staging/media/davinci_vpfe/vpfe_video.c   |   3 +-
>  drivers/staging/media/omap4iss/iss_video.c    |   3 +-
>  drivers/usb/gadget/function/uvc_queue.c       |   2 +-
>  include/media/videobuf2-v4l2.h                |  14 ++-
>  15 files changed, 148 insertions(+), 32 deletions(-)
> 
> diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
> index 1efb9c8b359d..3e6db7d30989 100644
> --- a/drivers/media/common/videobuf2/videobuf2-core.c
> +++ b/drivers/media/common/videobuf2/videobuf2-core.c
> @@ -1338,6 +1338,14 @@ static void vb2_req_queue(struct media_request_object *obj)
>  	mutex_unlock(vb->vb2_queue->lock);
>  }
>  
> +static void vb2_req_unbind(struct media_request_object *obj)
> +{
> +	struct vb2_buffer *vb = container_of(obj, struct vb2_buffer, req_obj);
> +
> +	if (vb->state == VB2_BUF_STATE_IN_REQUEST)
> +		call_void_bufop(vb->vb2_queue, init_buffer, vb);
> +}
> +
>  static void vb2_req_release(struct media_request_object *obj)
>  {
>  	struct vb2_buffer *vb = container_of(obj, struct vb2_buffer, req_obj);
> @@ -1350,6 +1358,7 @@ static const struct media_request_object_ops vb2_core_req_ops = {
>  	.prepare = vb2_req_prepare,
>  	.unprepare = vb2_req_unprepare,
>  	.queue = vb2_req_queue,
> +	.unbind = vb2_req_unbind,
>  	.release = vb2_req_release,
>  };
>  
> @@ -1481,8 +1490,10 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb,
>  
>  		vb->state = VB2_BUF_STATE_IN_REQUEST;
>  		/* Fill buffer information for the userspace */
> -		if (pb)
> +		if (pb) {
> +			call_void_bufop(q, copy_timestamp, vb, pb);
>  			call_void_bufop(q, fill_user_buffer, vb, pb);
> +		}
>  
>  		dprintk(2, "qbuf of buffer %d succeeded\n", vb->index);
>  		return 0;
> diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c
> index ea9db4b3f59a..9c652afa62ab 100644
> --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
> +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
> @@ -25,6 +25,7 @@
>  #include <linux/kthread.h>
>  
>  #include <media/v4l2-dev.h>
> +#include <media/v4l2-device.h>
>  #include <media/v4l2-fh.h>
>  #include <media/v4l2-event.h>
>  #include <media/v4l2-common.h>
> @@ -40,10 +41,12 @@ module_param(debug, int, 0644);
>  			pr_info("vb2-v4l2: %s: " fmt, __func__, ## arg); \
>  	} while (0)
>  
> -/* Flags that are set by the vb2 core */
> +/* Flags that are set by us */
>  #define V4L2_BUFFER_MASK_FLAGS	(V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \
>  				 V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR | \
>  				 V4L2_BUF_FLAG_PREPARED | \
> +				 V4L2_BUF_FLAG_IN_REQUEST | \
> +				 V4L2_BUF_FLAG_REQUEST_FD | \
>  				 V4L2_BUF_FLAG_TIMESTAMP_MASK)
>  /* Output buffer flags that should be passed on to the driver */
>  #define V4L2_BUFFER_OUT_FLAGS	(V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME | \
> @@ -118,6 +121,16 @@ static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b)
>  	return 0;
>  }
>  
> +/*
> + * __init_v4l2_vb2_buffer() - initialize the v4l2_vb2_buffer struct
> + */
> +static void __init_v4l2_vb2_buffer(struct vb2_buffer *vb)
> +{
> +	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> +
> +	vbuf->request_fd = -1;
> +}
> +
>  static void __copy_timestamp(struct vb2_buffer *vb, const void *pb)
>  {
>  	const struct v4l2_buffer *b = pb;
> @@ -181,6 +194,7 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b
>  		return -EINVAL;
>  	}
>  	vbuf->sequence = 0;
> +	vbuf->request_fd = -1;
>  
>  	if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
>  		switch (b->memory) {
> @@ -318,9 +332,12 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b
>  	return 0;
>  }
>  
> -static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b,
> -				    const char *opname)
> +static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *mdev,
> +				    struct v4l2_buffer *b,
> +				    const char *opname,
> +				    struct media_request **p_req)
>  {
> +	struct media_request *req;
>  	struct vb2_v4l2_buffer *vbuf;
>  	struct vb2_buffer *vb;
>  	int ret;
> @@ -357,8 +374,60 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b,
>  		memset(vbuf->planes, 0,
>  		       sizeof(vbuf->planes[0]) * vb->num_planes);
>  		ret = vb2_fill_vb2_v4l2_buffer(vb, b);
> +		if (ret)
> +			return ret;
>  	}
> -	return ret;
> +
> +	if (!(b->flags & V4L2_BUF_FLAG_REQUEST_FD))
> +		return 0;
> +
> +	/*
> +	 * For proper locking when queueing a request you need to be able
> +	 * to lock access to the vb2 queue, so check that there is a lock
> +	 * that we can use. In addition p_req must be non-NULL.
> +	 */
> +	if (WARN_ON(!q->lock || !p_req))
> +		return -EINVAL;
> +
> +	/*
> +	 * Make sure this op is implemented by the driver. It's easy to forget
> +	 * this callback, but is it important when canceling a buffer in a
> +	 * queued request.
> +	 */
> +	if (WARN_ON(!q->ops->buf_request_complete))
> +		return -EINVAL;
> +
> +	if (vb->state != VB2_BUF_STATE_DEQUEUED) {
> +		dprintk(1, "%s: buffer is not in dequeued state\n", opname);
> +		return -EINVAL;
> +	}
> +
> +	if (b->request_fd < 0) {
> +		dprintk(1, "%s: request_fd < 0\n", opname);
> +		return -EINVAL;
> +	}
> +
> +	req = media_request_get_by_fd(mdev, b->request_fd);
> +	if (IS_ERR(req)) {
> +		dprintk(1, "%s: invalid request_fd\n", opname);
> +		return PTR_ERR(req);
> +	}
> +
> +	/*
> +	 * Early sanity check. This is checked again when the buffer
> +	 * is bound to the request in vb2_core_qbuf().
> +	 */
> +	if (req->state != MEDIA_REQUEST_STATE_IDLE &&
> +	    req->state != MEDIA_REQUEST_STATE_UPDATING) {
> +		dprintk(1, "%s: request is not idle\n", opname);
> +		media_request_put(req);
> +		return -EBUSY;
> +	}
> +
> +	*p_req = req;
> +	vbuf->request_fd = b->request_fd;
> +
> +	return 0;
>  }
>  
>  /*
> @@ -442,6 +511,7 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
>  		b->flags |= V4L2_BUF_FLAG_QUEUED;
>  		break;
>  	case VB2_BUF_STATE_IN_REQUEST:
> +		b->flags |= V4L2_BUF_FLAG_IN_REQUEST;
>  		break;
>  	case VB2_BUF_STATE_ERROR:
>  		b->flags |= V4L2_BUF_FLAG_ERROR;
> @@ -456,11 +526,17 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
>  		break;
>  	}
>  
> -	if (vb->state == VB2_BUF_STATE_DEQUEUED && vb->synced && vb->prepared)
> +	if ((vb->state == VB2_BUF_STATE_DEQUEUED ||
> +	     vb->state == VB2_BUF_STATE_IN_REQUEST) &&
> +	    vb->synced && vb->prepared)
>  		b->flags |= V4L2_BUF_FLAG_PREPARED;
>  
>  	if (vb2_buffer_in_use(q, vb))
>  		b->flags |= V4L2_BUF_FLAG_MAPPED;
> +	if (vbuf->request_fd >= 0) {
> +		b->flags |= V4L2_BUF_FLAG_REQUEST_FD;
> +		b->request_fd = vbuf->request_fd;
> +	}
>  
>  	if (!q->is_output &&
>  		b->flags & V4L2_BUF_FLAG_DONE &&
> @@ -494,6 +570,7 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb, struct vb2_plane *planes)
>  
>  static const struct vb2_buf_ops v4l2_buf_ops = {
>  	.verify_planes_array	= __verify_planes_array_core,
> +	.init_buffer		= __init_v4l2_vb2_buffer,
>  	.fill_user_buffer	= __fill_v4l2_buffer,
>  	.fill_vb2_buffer	= __fill_vb2_buffer,
>  	.copy_timestamp		= __copy_timestamp,
> @@ -542,7 +619,8 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
>  }
>  EXPORT_SYMBOL_GPL(vb2_reqbufs);
>  
> -int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b)
> +int vb2_prepare_buf(struct vb2_queue *q, struct media_device *mdev,
> +		    struct v4l2_buffer *b)
>  {
>  	int ret;
>  
> @@ -551,7 +629,10 @@ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b)
>  		return -EBUSY;
>  	}
>  
> -	ret = vb2_queue_or_prepare_buf(q, b, "prepare_buf");
> +	if (b->flags & V4L2_BUF_FLAG_REQUEST_FD)
> +		return -EINVAL;
> +
> +	ret = vb2_queue_or_prepare_buf(q, mdev, b, "prepare_buf", NULL);
>  
>  	return ret ? ret : vb2_core_prepare_buf(q, b->index, b);
>  }
> @@ -611,8 +692,10 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
>  }
>  EXPORT_SYMBOL_GPL(vb2_create_bufs);
>  
> -int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
> +int vb2_qbuf(struct vb2_queue *q, struct media_device *mdev,
> +	     struct v4l2_buffer *b)
>  {
> +	struct media_request *req = NULL;
>  	int ret;
>  
>  	if (vb2_fileio_is_active(q)) {
> @@ -620,8 +703,13 @@ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
>  		return -EBUSY;
>  	}
>  
> -	ret = vb2_queue_or_prepare_buf(q, b, "qbuf");
> -	return ret ? ret : vb2_core_qbuf(q, b->index, b, NULL);
> +	ret = vb2_queue_or_prepare_buf(q, mdev, b, "qbuf", &req);
> +	if (ret)
> +		return ret;
> +	ret = vb2_core_qbuf(q, b->index, b, req);
> +	if (req)
> +		media_request_put(req);
> +	return ret;
>  }
>  EXPORT_SYMBOL_GPL(vb2_qbuf);
>  
> @@ -811,7 +899,7 @@ int vb2_ioctl_prepare_buf(struct file *file, void *priv,
>  
>  	if (vb2_queue_is_busy(vdev, file))
>  		return -EBUSY;
> -	return vb2_prepare_buf(vdev->queue, p);
> +	return vb2_prepare_buf(vdev->queue, vdev->v4l2_dev->mdev, p);
>  }
>  EXPORT_SYMBOL_GPL(vb2_ioctl_prepare_buf);
>  
> @@ -830,7 +918,7 @@ int vb2_ioctl_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
>  
>  	if (vb2_queue_is_busy(vdev, file))
>  		return -EBUSY;
> -	return vb2_qbuf(vdev->queue, p);
> +	return vb2_qbuf(vdev->queue, vdev->v4l2_dev->mdev, p);
>  }
>  EXPORT_SYMBOL_GPL(vb2_ioctl_qbuf);
>  
> diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c
> index 674e7fd3ad99..1de7b6b13f67 100644
> --- a/drivers/media/platform/omap3isp/ispvideo.c
> +++ b/drivers/media/platform/omap3isp/ispvideo.c
> @@ -940,7 +940,7 @@ isp_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
>  	int ret;
>  
>  	mutex_lock(&video->queue_lock);
> -	ret = vb2_qbuf(&vfh->queue, b);
> +	ret = vb2_qbuf(&vfh->queue, video->video.v4l2_dev->mdev, b);
>  	mutex_unlock(&video->queue_lock);
>  
>  	return ret;
> diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c
> index b1d9f3857d3d..60b62ec6e9cd 100644
> --- a/drivers/media/platform/s3c-camif/camif-capture.c
> +++ b/drivers/media/platform/s3c-camif/camif-capture.c
> @@ -943,7 +943,7 @@ static int s3c_camif_qbuf(struct file *file, void *priv,
>  	if (vp->owner && vp->owner != priv)
>  		return -EBUSY;
>  
> -	return vb2_qbuf(&vp->vb_queue, buf);
> +	return vb2_qbuf(&vp->vb_queue, vp->vdev.v4l2_dev->mdev, buf);
>  }
>  
>  static int s3c_camif_dqbuf(struct file *file, void *priv,
> @@ -981,7 +981,7 @@ static int s3c_camif_prepare_buf(struct file *file, void *priv,
>  				 struct v4l2_buffer *b)
>  {
>  	struct camif_vp *vp = video_drvdata(file);
> -	return vb2_prepare_buf(&vp->vb_queue, b);
> +	return vb2_prepare_buf(&vp->vb_queue, vp->vdev.v4l2_dev->mdev, b);
>  }
>  
>  static int s3c_camif_g_selection(struct file *file, void *priv,
> diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
> index 6a3cc4f86c5d..fc0b61f1b91d 100644
> --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
> +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
> @@ -632,9 +632,9 @@ static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
>  		return -EIO;
>  	}
>  	if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> -		return vb2_qbuf(&ctx->vq_src, buf);
> +		return vb2_qbuf(&ctx->vq_src, NULL, buf);
>  	else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> -		return vb2_qbuf(&ctx->vq_dst, buf);
> +		return vb2_qbuf(&ctx->vq_dst, NULL, buf);
>  	return -EINVAL;
>  }
>  
> diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
> index 3ad4f5073002..9208c83aa377 100644
> --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
> +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
> @@ -1621,9 +1621,9 @@ static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
>  			mfc_err("Call on QBUF after EOS command\n");
>  			return -EIO;
>  		}
> -		return vb2_qbuf(&ctx->vq_src, buf);
> +		return vb2_qbuf(&ctx->vq_src, NULL, buf);
>  	} else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> -		return vb2_qbuf(&ctx->vq_dst, buf);
> +		return vb2_qbuf(&ctx->vq_dst, NULL, buf);
>  	}
>  	return -EINVAL;
>  }
> diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
> index 901c07f49351..4572fea38d0b 100644
> --- a/drivers/media/platform/soc_camera/soc_camera.c
> +++ b/drivers/media/platform/soc_camera/soc_camera.c
> @@ -394,7 +394,7 @@ static int soc_camera_qbuf(struct file *file, void *priv,
>  	if (icd->streamer != file)
>  		return -EBUSY;
>  
> -	return vb2_qbuf(&icd->vb2_vidq, p);
> +	return vb2_qbuf(&icd->vb2_vidq, NULL, p);
>  }
>  
>  static int soc_camera_dqbuf(struct file *file, void *priv,
> @@ -430,7 +430,7 @@ static int soc_camera_prepare_buf(struct file *file, void *priv,
>  {
>  	struct soc_camera_device *icd = file->private_data;
>  
> -	return vb2_prepare_buf(&icd->vb2_vidq, b);
> +	return vb2_prepare_buf(&icd->vb2_vidq, NULL, b);
>  }
>  
>  static int soc_camera_expbuf(struct file *file, void *priv,
> diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
> index fecccb5e7628..8964e16f2b22 100644
> --- a/drivers/media/usb/uvc/uvc_queue.c
> +++ b/drivers/media/usb/uvc/uvc_queue.c
> @@ -300,12 +300,13 @@ int uvc_create_buffers(struct uvc_video_queue *queue,
>  	return ret;
>  }
>  
> -int uvc_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
> +int uvc_queue_buffer(struct uvc_video_queue *queue,
> +		     struct media_device *mdev, struct v4l2_buffer *buf)
>  {
>  	int ret;
>  
>  	mutex_lock(&queue->mutex);
> -	ret = vb2_qbuf(&queue->queue, buf);
> +	ret = vb2_qbuf(&queue->queue, mdev, buf);
>  	mutex_unlock(&queue->mutex);
>  
>  	return ret;
> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> index 18a7384b50ee..0a2b8ea8a4ff 100644
> --- a/drivers/media/usb/uvc/uvc_v4l2.c
> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> @@ -751,7 +751,8 @@ static int uvc_ioctl_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
>  	if (!uvc_has_privileges(handle))
>  		return -EBUSY;
>  
> -	return uvc_queue_buffer(&stream->queue, buf);
> +	return uvc_queue_buffer(&stream->queue,
> +				stream->vdev.v4l2_dev->mdev, buf);
>  }
>  
>  static int uvc_ioctl_expbuf(struct file *file, void *fh,
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index e5f5d84f1d1d..8b7bb89f12df 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -694,6 +694,7 @@ int uvc_query_buffer(struct uvc_video_queue *queue,
>  int uvc_create_buffers(struct uvc_video_queue *queue,
>  		       struct v4l2_create_buffers *v4l2_cb);
>  int uvc_queue_buffer(struct uvc_video_queue *queue,
> +		     struct media_device *mdev,
>  		     struct v4l2_buffer *v4l2_buf);
>  int uvc_export_buffer(struct uvc_video_queue *queue,
>  		      struct v4l2_exportbuffer *exp);
> diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c
> index 0a93c5b173c2..b09494174eb4 100644
> --- a/drivers/media/v4l2-core/v4l2-mem2mem.c
> +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
> @@ -455,11 +455,12 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_querybuf);
>  int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
>  		  struct v4l2_buffer *buf)
>  {
> +	struct video_device *vdev = video_devdata(file);
>  	struct vb2_queue *vq;
>  	int ret;
>  
>  	vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
> -	ret = vb2_qbuf(vq, buf);
> +	ret = vb2_qbuf(vq, vdev->v4l2_dev->mdev, buf);
>  	if (!ret)
>  		v4l2_m2m_try_schedule(m2m_ctx);
>  
> @@ -480,11 +481,12 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_dqbuf);
>  int v4l2_m2m_prepare_buf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
>  			 struct v4l2_buffer *buf)
>  {
> +	struct video_device *vdev = video_devdata(file);
>  	struct vb2_queue *vq;
>  	int ret;
>  
>  	vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
> -	ret = vb2_prepare_buf(vq, buf);
> +	ret = vb2_prepare_buf(vq, vdev->v4l2_dev->mdev, buf);
>  	if (!ret)
>  		v4l2_m2m_try_schedule(m2m_ctx);
>  
> diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c
> index 4e3ec7fdc90d..6d693067a251 100644
> --- a/drivers/staging/media/davinci_vpfe/vpfe_video.c
> +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c
> @@ -1425,7 +1425,8 @@ static int vpfe_qbuf(struct file *file, void *priv,
>  		return -EACCES;
>  	}
>  
> -	return vb2_qbuf(&video->buffer_queue, p);
> +	return vb2_qbuf(&video->buffer_queue,
> +			video->video_dev.v4l2_dev->mdev, p);
>  }
>  
>  /*
> diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c
> index a3a83424a926..a35d1004b522 100644
> --- a/drivers/staging/media/omap4iss/iss_video.c
> +++ b/drivers/staging/media/omap4iss/iss_video.c
> @@ -805,9 +805,10 @@ iss_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
>  static int
>  iss_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
>  {
> +	struct iss_video *video = video_drvdata(file);
>  	struct iss_video_fh *vfh = to_iss_video_fh(fh);
>  
> -	return vb2_qbuf(&vfh->queue, b);
> +	return vb2_qbuf(&vfh->queue, video->video.v4l2_dev->mdev, b);
>  }
>  
>  static int
> diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c
> index 9e33d5206d54..f2497cb96abb 100644
> --- a/drivers/usb/gadget/function/uvc_queue.c
> +++ b/drivers/usb/gadget/function/uvc_queue.c
> @@ -166,7 +166,7 @@ int uvcg_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
>  	unsigned long flags;
>  	int ret;
>  
> -	ret = vb2_qbuf(&queue->queue, buf);
> +	ret = vb2_qbuf(&queue->queue, NULL, buf);
>  	if (ret < 0)
>  		return ret;
>  
> diff --git a/include/media/videobuf2-v4l2.h b/include/media/videobuf2-v4l2.h
> index 097bf3e6951d..91a2b3e1a642 100644
> --- a/include/media/videobuf2-v4l2.h
> +++ b/include/media/videobuf2-v4l2.h
> @@ -32,6 +32,7 @@
>   *		&enum v4l2_field.
>   * @timecode:	frame timecode.
>   * @sequence:	sequence count of this frame.
> + * @request_fd:	the request_fd associated with this buffer
>   * @planes:	plane information (userptr/fd, length, bytesused, data_offset).
>   *
>   * Should contain enough information to be able to cover all the fields
> @@ -44,6 +45,7 @@ struct vb2_v4l2_buffer {
>  	__u32			field;
>  	struct v4l2_timecode	timecode;
>  	__u32			sequence;
> +	__s32			request_fd;
>  	struct vb2_plane	planes[VB2_MAX_PLANES];
>  };
>  
> @@ -79,6 +81,7 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create);
>   * vb2_prepare_buf() - Pass ownership of a buffer from userspace to the kernel
>   *
>   * @q:		pointer to &struct vb2_queue with videobuf2 queue.
> + * @mdev:	pointer to &struct media_device, may be NULL.
>   * @b:		buffer structure passed from userspace to
>   *		&v4l2_ioctl_ops->vidioc_prepare_buf handler in driver
>   *
> @@ -90,15 +93,19 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create);
>   * #) verifies the passed buffer,
>   * #) calls &vb2_ops->buf_prepare callback in the driver (if provided),
>   *    in which driver-specific buffer initialization can be performed.
> + * #) if @b->request_fd is non-zero and @mdev->ops->req_queue is set,
> + *    then bind the prepared buffer to the request.
>   *
>   * The return values from this function are intended to be directly returned
>   * from &v4l2_ioctl_ops->vidioc_prepare_buf handler in driver.
>   */
> -int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b);
> +int vb2_prepare_buf(struct vb2_queue *q, struct media_device *mdev,
> +		    struct v4l2_buffer *b);
>  
>  /**
>   * vb2_qbuf() - Queue a buffer from userspace
>   * @q:		pointer to &struct vb2_queue with videobuf2 queue.
> + * @mdev:	pointer to &struct media_device, may be NULL.
>   * @b:		buffer structure passed from userspace to
>   *		&v4l2_ioctl_ops->vidioc_qbuf handler in driver
>   *
> @@ -107,6 +114,8 @@ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b);
>   * This function:
>   *
>   * #) verifies the passed buffer;
> + * #) if @b->request_fd is non-zero and @mdev->ops->req_queue is set,
> + *    then bind the buffer to the request.
>   * #) if necessary, calls &vb2_ops->buf_prepare callback in the driver
>   *    (if provided), in which driver-specific buffer initialization can
>   *    be performed;
> @@ -116,7 +125,8 @@ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b);
>   * The return values from this function are intended to be directly returned
>   * from &v4l2_ioctl_ops->vidioc_qbuf handler in driver.
>   */
> -int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b);
> +int vb2_qbuf(struct vb2_queue *q, struct media_device *mdev,
> +	     struct v4l2_buffer *b);
>  
>  /**
>   * vb2_expbuf() - Export a buffer as a file descriptor



Thanks,
Mauro

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

* Re: [PATCHv17 25/34] videobuf2-core: add request helper functions
  2018-08-04 12:45 ` [PATCHv17 25/34] videobuf2-core: add request helper functions Hans Verkuil
@ 2018-08-13 14:50   ` Mauro Carvalho Chehab
  2018-08-14  7:22     ` Hans Verkuil
  0 siblings, 1 reply; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-13 14:50 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:45:17 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Add a new helper function to tell if a request object is a buffer.
> 
> Add a new helper function that returns true if a media_request
> contains at least one buffer.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  .../media/common/videobuf2/videobuf2-core.c   | 24 +++++++++++++++++++
>  include/media/videobuf2-core.h                | 15 ++++++++++++
>  2 files changed, 39 insertions(+)
> 
> diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
> index 3e6db7d30989..f8af7add35ab 100644
> --- a/drivers/media/common/videobuf2/videobuf2-core.c
> +++ b/drivers/media/common/videobuf2/videobuf2-core.c
> @@ -1362,6 +1362,30 @@ static const struct media_request_object_ops vb2_core_req_ops = {
>  	.release = vb2_req_release,
>  };
>  
> +bool vb2_request_object_is_buffer(struct media_request_object *obj)
> +{
> +	return obj->ops == &vb2_core_req_ops;
> +}
> +EXPORT_SYMBOL_GPL(vb2_request_object_is_buffer);
> +
> +bool vb2_request_has_buffers(struct media_request *req)
> +{
> +	struct media_request_object *obj;
> +	unsigned long flags;
> +	bool has_buffers = false;
> +
> +	spin_lock_irqsave(&req->lock, flags);
> +	list_for_each_entry(obj, &req->objects, list) {
> +		if (vb2_request_object_is_buffer(obj)) {
> +			has_buffers = true;
> +			break;
> +		}
> +	}
> +	spin_unlock_irqrestore(&req->lock, flags);
> +	return has_buffers;
> +}
> +EXPORT_SYMBOL_GPL(vb2_request_has_buffers);
> +
>  int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb)
>  {
>  	struct vb2_buffer *vb;
> diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
> index 8a8d7732d182..cad712403d14 100644
> --- a/include/media/videobuf2-core.h
> +++ b/include/media/videobuf2-core.h
> @@ -1168,4 +1168,19 @@ bool vb2_buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb);
>   */
>  int vb2_verify_memory_type(struct vb2_queue *q,
>  		enum vb2_memory memory, unsigned int type);
> +
> +/**
> + * vb2_request_object_is_buffer() - return true if the object is a buffer
> + *
> + * @obj:	the request object.

It should be mentioned that it should be called with req->lock locked.

With such change:

Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>

> + */
> +bool vb2_request_object_is_buffer(struct media_request_object *obj);
> +
> +/**
> + * vb2_request_has_buffers() - return true if the request contains buffers
> + *
> + * @req:	the request.
> + */
> +bool vb2_request_has_buffers(struct media_request *req);
> +
>  #endif /* _MEDIA_VIDEOBUF2_CORE_H */



Thanks,
Mauro

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

* Re: [PATCHv17 26/34] videobuf2-v4l2: add vb2_request_queue/validate helpers
  2018-08-04 12:45 ` [PATCHv17 26/34] videobuf2-v4l2: add vb2_request_queue/validate helpers Hans Verkuil
@ 2018-08-13 14:53   ` Mauro Carvalho Chehab
  2018-08-14  7:19     ` Hans Verkuil
  0 siblings, 1 reply; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-13 14:53 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:45:18 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> The generic vb2_request_validate helper function checks if
> there are buffers in the request and if so, prepares (validates)
> all objects in the request.
> 
> The generic vb2_request_queue helper function queues all buffer
> objects in the validated request.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  .../media/common/videobuf2/videobuf2-v4l2.c   | 44 +++++++++++++++++++
>  include/media/videobuf2-v4l2.h                |  4 ++
>  2 files changed, 48 insertions(+)
> 
> diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c
> index 9c652afa62ab..88d8f60c742b 100644
> --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
> +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
> @@ -1100,6 +1100,50 @@ void vb2_ops_wait_finish(struct vb2_queue *vq)
>  }
>  EXPORT_SYMBOL_GPL(vb2_ops_wait_finish);
>  
> +int vb2_request_validate(struct media_request *req)
> +{
> +	struct media_request_object *obj;
> +	int ret = 0;
> +
> +	if (!vb2_request_has_buffers(req))
> +		return -ENOENT;

This holds the spinlock...

> +
> +	list_for_each_entry(obj, &req->objects, list) {
> +		if (!obj->ops->prepare)
> +			continue;
> +
> +		ret = obj->ops->prepare(obj);
> +		if (ret)
> +			break;
> +	}
> +

Shouldn't this logic hold it too?

> +	if (ret) {
> +		list_for_each_entry_continue_reverse(obj, &req->objects, list)
> +			if (obj->ops->unprepare)
> +				obj->ops->unprepare(obj);
> +		return ret;
> +	}
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(vb2_request_validate);
> +
> +void vb2_request_queue(struct media_request *req)
> +{
> +	struct media_request_object *obj, *obj_safe;
> +
> +	/* Queue all non-buffer objects */
> +	list_for_each_entry_safe(obj, obj_safe, &req->objects, list)
> +		if (obj->ops->queue && !vb2_request_object_is_buffer(obj))
> +			obj->ops->queue(obj);
> +
> +	/* Queue all buffer objects */
> +	list_for_each_entry_safe(obj, obj_safe, &req->objects, list) {
> +		if (obj->ops->queue && vb2_request_object_is_buffer(obj))
> +			obj->ops->queue(obj);
> +	}
> +}
> +EXPORT_SYMBOL_GPL(vb2_request_queue);
> +
>  MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2");
>  MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>, Marek Szyprowski");
>  MODULE_LICENSE("GPL");
> diff --git a/include/media/videobuf2-v4l2.h b/include/media/videobuf2-v4l2.h
> index 91a2b3e1a642..727855463838 100644
> --- a/include/media/videobuf2-v4l2.h
> +++ b/include/media/videobuf2-v4l2.h
> @@ -303,4 +303,8 @@ void vb2_ops_wait_prepare(struct vb2_queue *vq);
>   */
>  void vb2_ops_wait_finish(struct vb2_queue *vq);
>  
> +struct media_request;
> +int vb2_request_validate(struct media_request *req);
> +void vb2_request_queue(struct media_request *req);
> +
>  #endif /* _MEDIA_VIDEOBUF2_V4L2_H */



Thanks,
Mauro

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

* Re: [PATCHv17 27/34] videobuf2-core: add uses_requests/qbuf flags
  2018-08-04 12:45 ` [PATCHv17 27/34] videobuf2-core: add uses_requests/qbuf flags Hans Verkuil
@ 2018-08-13 14:55   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-13 14:55 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:45:19 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Set the first time a buffer from a request is queued to vb2
> (uses_requests) or directly queued (uses_qbuf).
> Cleared when the queue is canceled.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
> ---
>  drivers/media/common/videobuf2/videobuf2-core.c | 13 +++++++++++++
>  include/media/videobuf2-core.h                  |  8 ++++++++
>  2 files changed, 21 insertions(+)
> 
> diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
> index f8af7add35ab..5d7946ec80d8 100644
> --- a/drivers/media/common/videobuf2/videobuf2-core.c
> +++ b/drivers/media/common/videobuf2/videobuf2-core.c
> @@ -1491,9 +1491,17 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb,
>  
>  	vb = q->bufs[index];
>  
> +	if ((req && q->uses_qbuf) ||
> +	    (!req && vb->state != VB2_BUF_STATE_IN_REQUEST &&
> +	     q->uses_requests)) {
> +		dprintk(1, "queue in wrong mode (qbuf vs requests)\n");
> +		return -EPERM;
> +	}
> +
>  	if (req) {
>  		int ret;
>  
> +		q->uses_requests = 1;
>  		if (vb->state != VB2_BUF_STATE_DEQUEUED) {
>  			dprintk(1, "buffer %d not in dequeued state\n",
>  				vb->index);
> @@ -1523,6 +1531,9 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb,
>  		return 0;
>  	}
>  
> +	if (vb->state != VB2_BUF_STATE_IN_REQUEST)
> +		q->uses_qbuf = 1;
> +
>  	switch (vb->state) {
>  	case VB2_BUF_STATE_DEQUEUED:
>  	case VB2_BUF_STATE_IN_REQUEST:
> @@ -1825,6 +1836,8 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
>  	q->start_streaming_called = 0;
>  	q->queued_count = 0;
>  	q->error = 0;
> +	q->uses_requests = 0;
> +	q->uses_qbuf = 0;
>  
>  	/*
>  	 * Remove all buffers from videobuf's list...
> diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
> index cad712403d14..daffdf259fce 100644
> --- a/include/media/videobuf2-core.h
> +++ b/include/media/videobuf2-core.h
> @@ -468,6 +468,12 @@ struct vb2_buf_ops {
>   * @quirk_poll_must_check_waiting_for_buffers: Return %EPOLLERR at poll when QBUF
>   *              has not been called. This is a vb1 idiom that has been adopted
>   *              also by vb2.
> + * @uses_qbuf:	qbuf was used directly for this queue. Set to 1 the first
> + *		time this is called. Set to 0 when the queue is canceled.
> + *		If this is 1, then you cannot queue buffers from a request.
> + * @uses_requests: requests are used for this queue. Set to 1 the first time
> + *		a request is queued. Set to 0 when the queue is canceled.
> + *		If this is 1, then you cannot queue buffers directly.
>   * @lock:	pointer to a mutex that protects the &struct vb2_queue. The
>   *		driver can set this to a mutex to let the v4l2 core serialize
>   *		the queuing ioctls. If the driver wants to handle locking
> @@ -535,6 +541,8 @@ struct vb2_queue {
>  	unsigned			fileio_write_immediately:1;
>  	unsigned			allow_zero_bytesused:1;
>  	unsigned		   quirk_poll_must_check_waiting_for_buffers:1;
> +	unsigned			uses_qbuf:1;
> +	unsigned			uses_requests:1;
>  
>  	struct mutex			*lock;
>  	void				*owner;



Thanks,
Mauro

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

* Re: [PATCHv17 28/34] videobuf2-v4l2: refuse qbuf if queue uses requests or vv.
  2018-08-04 12:45 ` [PATCHv17 28/34] videobuf2-v4l2: refuse qbuf if queue uses requests or vv Hans Verkuil
@ 2018-08-13 14:56   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-13 14:56 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:45:20 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Check if the vb2 queue uses requests, and if so refuse to
> add buffers that are not part of a request. Also check for
> the reverse: a vb2 queue did not use requests, and an attempt
> was made to queue a buffer to a request.
> 
> We might relax this in the future, but for now just return
> -EPERM in that case.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
> ---
>  drivers/media/common/videobuf2/videobuf2-v4l2.c | 10 +++++++++-
>  1 file changed, 9 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c
> index 88d8f60c742b..1b2351986230 100644
> --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
> +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
> @@ -378,8 +378,16 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md
>  			return ret;
>  	}
>  
> -	if (!(b->flags & V4L2_BUF_FLAG_REQUEST_FD))
> +	if (!(b->flags & V4L2_BUF_FLAG_REQUEST_FD)) {
> +		if (q->uses_requests) {
> +			dprintk(1, "%s: queue uses requests\n", opname);
> +			return -EPERM;
> +		}
>  		return 0;
> +	} else if (q->uses_qbuf) {
> +		dprintk(1, "%s: queue does not use requests\n", opname);
> +		return -EPERM;
> +	}
>  
>  	/*
>  	 * For proper locking when queueing a request you need to be able



Thanks,
Mauro

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

* Re: [PATCHv17 29/34] v4l2-mem2mem: add vb2_m2m_request_queue
  2018-08-04 12:45 ` [PATCHv17 29/34] v4l2-mem2mem: add vb2_m2m_request_queue Hans Verkuil
@ 2018-08-13 15:02   ` Mauro Carvalho Chehab
  2018-08-14  7:26     ` Hans Verkuil
  0 siblings, 1 reply; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-13 15:02 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:45:21 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> For mem2mem devices we have to make sure that v4l2_m2m_try_schedule()
> is called whenever a request is queued.
> 
> We do that by creating a vb2_m2m_request_queue() helper that should
> be used instead of the 'normal' vb2_request_queue() helper. The m2m
> helper function will call v4l2_m2m_try_schedule() as needed.
> 
> In addition we also avoid calling v4l2_m2m_try_schedule() when preparing
> or queueing a buffer for a request since that is no longer needed.
> Instead this helper function will do that when the request is actually
> queued.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>

Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>

But please notice below that there's another change on patch 1/34 due
to this patchset.

> ---
>  drivers/media/v4l2-core/v4l2-mem2mem.c | 59 ++++++++++++++++++++++----
>  include/media/v4l2-mem2mem.h           |  4 ++
>  2 files changed, 55 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c
> index b09494174eb4..56a16cfef6f8 100644
> --- a/drivers/media/v4l2-core/v4l2-mem2mem.c
> +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
> @@ -369,7 +369,7 @@ static void v4l2_m2m_cancel_job(struct v4l2_m2m_ctx *m2m_ctx)
>  		spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
>  		if (m2m_dev->m2m_ops->job_abort)
>  			m2m_dev->m2m_ops->job_abort(m2m_ctx->priv);
> -		dprintk("m2m_ctx %p running, will wait to complete", m2m_ctx);
> +		dprintk("m2m_ctx %p running, will wait to complete\n", m2m_ctx);
>  		wait_event(m2m_ctx->finished,
>  				!(m2m_ctx->job_flags & TRANS_RUNNING));
>  	} else if (m2m_ctx->job_flags & TRANS_QUEUED) {
> @@ -460,8 +460,14 @@ int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
>  	int ret;
>  
>  	vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
> +	if (!V4L2_TYPE_IS_OUTPUT(vq->type) &&
> +	    (buf->flags & V4L2_BUF_FLAG_REQUEST_FD)) {
> +		dprintk("%s: requests cannot be used with capture buffers\n",
> +			__func__);

I had to double-check the documentation at patch 01/34. While on one
part is says the same, saying that -EPERM is the error code, on
another part, it says:

	+Note that there is typically no need to use the Request API for CAPTURE buffers
	+since there are no per-frame settings to report there.

"typically" means that the normal usecase is to now allow, but gives
it open to implementations doing it.

Please fix that paragraph on patch 01/34 to reflect that no CAPTURE
buffers should be used with request API for m2m, in order to
reflect the code's implementation.

> +		return -EPERM;
> +	}
>  	ret = vb2_qbuf(vq, vdev->v4l2_dev->mdev, buf);
> -	if (!ret)
> +	if (!ret && !(buf->flags & V4L2_BUF_FLAG_IN_REQUEST))
>  		v4l2_m2m_try_schedule(m2m_ctx);
>  
>  	return ret;
> @@ -483,14 +489,9 @@ int v4l2_m2m_prepare_buf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
>  {
>  	struct video_device *vdev = video_devdata(file);
>  	struct vb2_queue *vq;
> -	int ret;
>  
>  	vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
> -	ret = vb2_prepare_buf(vq, vdev->v4l2_dev->mdev, buf);
> -	if (!ret)
> -		v4l2_m2m_try_schedule(m2m_ctx);
> -
> -	return ret;
> +	return vb2_prepare_buf(vq, vdev->v4l2_dev->mdev, buf);
>  }
>  EXPORT_SYMBOL_GPL(v4l2_m2m_prepare_buf);
>  
> @@ -934,6 +935,48 @@ void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx,
>  }
>  EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue);
>  
> +void vb2_m2m_request_queue(struct media_request *req)
> +{
> +	struct media_request_object *obj, *obj_safe;
> +	struct v4l2_m2m_ctx *m2m_ctx = NULL;
> +
> +	/* Queue all non-buffer objects */
> +	list_for_each_entry_safe(obj, obj_safe, &req->objects, list)
> +		if (obj->ops->queue && !vb2_request_object_is_buffer(obj))
> +			obj->ops->queue(obj);
> +
> +	/* Queue all buffer objects */
> +	list_for_each_entry_safe(obj, obj_safe, &req->objects, list) {
> +		struct v4l2_m2m_ctx *m2m_ctx_obj;
> +		struct vb2_buffer *vb;
> +
> +		if (!obj->ops->queue || !vb2_request_object_is_buffer(obj))
> +			continue;
> +
> +		/* Sanity checks */
> +		vb = container_of(obj, struct vb2_buffer, req_obj);
> +		WARN_ON(!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type));
> +		m2m_ctx_obj = container_of(vb->vb2_queue,
> +					   struct v4l2_m2m_ctx,
> +					   out_q_ctx.q);
> +		WARN_ON(m2m_ctx && m2m_ctx_obj != m2m_ctx);
> +		m2m_ctx = m2m_ctx_obj;
> +
> +		/*
> +		 * The buffer we queue here can in theory be immediately
> +		 * unbound, hence the use of list_for_each_entry_safe()
> +		 * above and why we call the queue op last.
> +		 */
> +		obj->ops->queue(obj);
> +	}
> +
> +	WARN_ON(!m2m_ctx);
> +
> +	if (m2m_ctx)
> +		v4l2_m2m_try_schedule(m2m_ctx);
> +}
> +EXPORT_SYMBOL_GPL(vb2_m2m_request_queue);
> +
>  /* Videobuf2 ioctl helpers */
>  
>  int v4l2_m2m_ioctl_reqbufs(struct file *file, void *priv,
> diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h
> index d655720e16a1..58c1ecf3d648 100644
> --- a/include/media/v4l2-mem2mem.h
> +++ b/include/media/v4l2-mem2mem.h
> @@ -622,6 +622,10 @@ v4l2_m2m_dst_buf_remove_by_idx(struct v4l2_m2m_ctx *m2m_ctx, unsigned int idx)
>  	return v4l2_m2m_buf_remove_by_idx(&m2m_ctx->cap_q_ctx, idx);
>  }
>  
> +/* v4l2 request helper */
> +
> +void vb2_m2m_request_queue(struct media_request *req);
> +
>  /* v4l2 ioctl helpers */
>  
>  int v4l2_m2m_ioctl_reqbufs(struct file *file, void *priv,



Thanks,
Mauro

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

* Re: [PATCHv17 30/34] vim2m: use workqueue
  2018-08-04 12:45 ` [PATCHv17 30/34] vim2m: use workqueue Hans Verkuil
@ 2018-08-13 15:05   ` Mauro Carvalho Chehab
  2018-08-14  7:28     ` Hans Verkuil
  0 siblings, 1 reply; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-13 15:05 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:45:22 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> v4l2_ctrl uses mutexes, so we can't setup a ctrl_handler in
> interrupt context. Switch to a workqueue instead and drop the timer.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>

Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>

Shouldn't this come earlier at the series (before adding request API
support to m2m) in order to avoid regressions?

> ---
>  drivers/media/platform/vim2m.c | 25 ++++++++++---------------
>  1 file changed, 10 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c
> index 462099a141e4..6f87ef025ff1 100644
> --- a/drivers/media/platform/vim2m.c
> +++ b/drivers/media/platform/vim2m.c
> @@ -3,7 +3,8 @@
>   *
>   * This is a virtual device driver for testing mem-to-mem videobuf framework.
>   * It simulates a device that uses memory buffers for both source and
> - * destination, processes the data and issues an "irq" (simulated by a timer).
> + * destination, processes the data and issues an "irq" (simulated by a delayed
> + * workqueue).
>   * The device is capable of multi-instance, multi-buffer-per-transaction
>   * operation (via the mem2mem framework).
>   *
> @@ -19,7 +20,6 @@
>  #include <linux/module.h>
>  #include <linux/delay.h>
>  #include <linux/fs.h>
> -#include <linux/timer.h>
>  #include <linux/sched.h>
>  #include <linux/slab.h>
>  
> @@ -148,7 +148,7 @@ struct vim2m_dev {
>  	struct mutex		dev_mutex;
>  	spinlock_t		irqlock;
>  
> -	struct timer_list	timer;
> +	struct delayed_work	work_run;
>  
>  	struct v4l2_m2m_dev	*m2m_dev;
>  };
> @@ -336,12 +336,6 @@ static int device_process(struct vim2m_ctx *ctx,
>  	return 0;
>  }
>  
> -static void schedule_irq(struct vim2m_dev *dev, int msec_timeout)
> -{
> -	dprintk(dev, "Scheduling a simulated irq\n");
> -	mod_timer(&dev->timer, jiffies + msecs_to_jiffies(msec_timeout));
> -}
> -
>  /*
>   * mem2mem callbacks
>   */
> @@ -387,13 +381,14 @@ static void device_run(void *priv)
>  
>  	device_process(ctx, src_buf, dst_buf);
>  
> -	/* Run a timer, which simulates a hardware irq  */
> -	schedule_irq(dev, ctx->transtime);
> +	/* Run delayed work, which simulates a hardware irq  */
> +	schedule_delayed_work(&dev->work_run, msecs_to_jiffies(ctx->transtime));
>  }
>  
> -static void device_isr(struct timer_list *t)
> +static void device_work(struct work_struct *w)
>  {
> -	struct vim2m_dev *vim2m_dev = from_timer(vim2m_dev, t, timer);
> +	struct vim2m_dev *vim2m_dev =
> +		container_of(w, struct vim2m_dev, work_run.work);
>  	struct vim2m_ctx *curr_ctx;
>  	struct vb2_v4l2_buffer *src_vb, *dst_vb;
>  	unsigned long flags;
> @@ -805,6 +800,7 @@ static void vim2m_stop_streaming(struct vb2_queue *q)
>  	struct vb2_v4l2_buffer *vbuf;
>  	unsigned long flags;
>  
> +	flush_scheduled_work();
>  	for (;;) {
>  		if (V4L2_TYPE_IS_OUTPUT(q->type))
>  			vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> @@ -1015,6 +1011,7 @@ static int vim2m_probe(struct platform_device *pdev)
>  	vfd = &dev->vfd;
>  	vfd->lock = &dev->dev_mutex;
>  	vfd->v4l2_dev = &dev->v4l2_dev;
> +	INIT_DELAYED_WORK(&dev->work_run, device_work);
>  
>  	ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
>  	if (ret) {
> @@ -1026,7 +1023,6 @@ static int vim2m_probe(struct platform_device *pdev)
>  	v4l2_info(&dev->v4l2_dev,
>  			"Device registered as /dev/video%d\n", vfd->num);
>  
> -	timer_setup(&dev->timer, device_isr, 0);
>  	platform_set_drvdata(pdev, dev);
>  
>  	dev->m2m_dev = v4l2_m2m_init(&m2m_ops);
> @@ -1083,7 +1079,6 @@ static int vim2m_remove(struct platform_device *pdev)
>  	media_device_cleanup(&dev->mdev);
>  #endif
>  	v4l2_m2m_release(dev->m2m_dev);
> -	del_timer_sync(&dev->timer);
>  	video_unregister_device(&dev->vfd);
>  	v4l2_device_unregister(&dev->v4l2_dev);
>  



Thanks,
Mauro

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

* Re: [PATCHv17 31/34] vim2m: support requests
  2018-08-04 12:45 ` [PATCHv17 31/34] vim2m: support requests Hans Verkuil
  2018-08-06 21:02   ` Ezequiel Garcia
@ 2018-08-13 15:08   ` Mauro Carvalho Chehab
  1 sibling, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-13 15:08 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:45:23 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Add support for requests to vim2m.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>

Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>

> ---
>  drivers/media/platform/vim2m.c | 26 ++++++++++++++++++++++++++
>  1 file changed, 26 insertions(+)
> 
> diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c
> index 6f87ef025ff1..3b8df2c9d24a 100644
> --- a/drivers/media/platform/vim2m.c
> +++ b/drivers/media/platform/vim2m.c
> @@ -379,8 +379,18 @@ static void device_run(void *priv)
>  	src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
>  	dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
>  
> +	/* Apply request controls if needed */
> +	if (src_buf->vb2_buf.req_obj.req)
> +		v4l2_ctrl_request_setup(src_buf->vb2_buf.req_obj.req,
> +					&ctx->hdl);
> +
>  	device_process(ctx, src_buf, dst_buf);
>  
> +	/* Complete request controls if needed */
> +	if (src_buf->vb2_buf.req_obj.req)
> +		v4l2_ctrl_request_complete(src_buf->vb2_buf.req_obj.req,
> +					&ctx->hdl);
> +
>  	/* Run delayed work, which simulates a hardware irq  */
>  	schedule_delayed_work(&dev->work_run, msecs_to_jiffies(ctx->transtime));
>  }
> @@ -808,12 +818,21 @@ static void vim2m_stop_streaming(struct vb2_queue *q)
>  			vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
>  		if (vbuf == NULL)
>  			return;
> +		v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req,
> +					   &ctx->hdl);
>  		spin_lock_irqsave(&ctx->dev->irqlock, flags);
>  		v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
>  		spin_unlock_irqrestore(&ctx->dev->irqlock, flags);
>  	}
>  }
>  
> +static void vim2m_buf_request_complete(struct vb2_buffer *vb)
> +{
> +	struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> +
> +	v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl);
> +}
> +
>  static const struct vb2_ops vim2m_qops = {
>  	.queue_setup	 = vim2m_queue_setup,
>  	.buf_prepare	 = vim2m_buf_prepare,
> @@ -822,6 +841,7 @@ static const struct vb2_ops vim2m_qops = {
>  	.stop_streaming  = vim2m_stop_streaming,
>  	.wait_prepare	 = vb2_ops_wait_prepare,
>  	.wait_finish	 = vb2_ops_wait_finish,
> +	.buf_request_complete = vim2m_buf_request_complete,
>  };
>  
>  static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
> @@ -988,6 +1008,11 @@ static const struct v4l2_m2m_ops m2m_ops = {
>  	.job_abort	= job_abort,
>  };
>  
> +static const struct media_device_ops m2m_media_ops = {
> +	.req_validate = vb2_request_validate,
> +	.req_queue = vb2_m2m_request_queue,
> +};
> +
>  static int vim2m_probe(struct platform_device *pdev)
>  {
>  	struct vim2m_dev *dev;
> @@ -1036,6 +1061,7 @@ static int vim2m_probe(struct platform_device *pdev)
>  	dev->mdev.dev = &pdev->dev;
>  	strlcpy(dev->mdev.model, "vim2m", sizeof(dev->mdev.model));
>  	media_device_init(&dev->mdev);
> +	dev->mdev.ops = &m2m_media_ops;
>  	dev->v4l2_dev.mdev = &dev->mdev;
>  
>  	ret = v4l2_m2m_register_media_controller(dev->m2m_dev,



Thanks,
Mauro

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

* Re: [PATCHv17 32/34] vivid: add mc
  2018-08-04 12:45 ` [PATCHv17 32/34] vivid: add mc Hans Verkuil
@ 2018-08-13 15:09   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-13 15:09 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:45:24 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Add support for the media_device to vivid. This is a prerequisite
> for request support.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>

Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>

> ---
>  drivers/media/platform/vivid/vivid-core.c | 61 +++++++++++++++++++++++
>  drivers/media/platform/vivid/vivid-core.h |  8 +++
>  2 files changed, 69 insertions(+)
> 
> diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c
> index 31db363602e5..1c448529be04 100644
> --- a/drivers/media/platform/vivid/vivid-core.c
> +++ b/drivers/media/platform/vivid/vivid-core.c
> @@ -657,6 +657,15 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
>  
>  	dev->inst = inst;
>  
> +#ifdef CONFIG_MEDIA_CONTROLLER
> +	dev->v4l2_dev.mdev = &dev->mdev;
> +
> +	/* Initialize media device */
> +	strlcpy(dev->mdev.model, VIVID_MODULE_NAME, sizeof(dev->mdev.model));
> +	dev->mdev.dev = &pdev->dev;
> +	media_device_init(&dev->mdev);
> +#endif
> +
>  	/* register v4l2_device */
>  	snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
>  			"%s-%03d", VIVID_MODULE_NAME, inst);
> @@ -1174,6 +1183,13 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
>  		vfd->lock = &dev->mutex;
>  		video_set_drvdata(vfd, dev);
>  
> +#ifdef CONFIG_MEDIA_CONTROLLER
> +		dev->vid_cap_pad.flags = MEDIA_PAD_FL_SINK;
> +		ret = media_entity_pads_init(&vfd->entity, 1, &dev->vid_cap_pad);
> +		if (ret)
> +			goto unreg_dev;
> +#endif
> +
>  #ifdef CONFIG_VIDEO_VIVID_CEC
>  		if (in_type_counter[HDMI]) {
>  			struct cec_adapter *adap;
> @@ -1226,6 +1242,13 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
>  		vfd->lock = &dev->mutex;
>  		video_set_drvdata(vfd, dev);
>  
> +#ifdef CONFIG_MEDIA_CONTROLLER
> +		dev->vid_out_pad.flags = MEDIA_PAD_FL_SOURCE;
> +		ret = media_entity_pads_init(&vfd->entity, 1, &dev->vid_out_pad);
> +		if (ret)
> +			goto unreg_dev;
> +#endif
> +
>  #ifdef CONFIG_VIDEO_VIVID_CEC
>  		for (i = 0; i < dev->num_outputs; i++) {
>  			struct cec_adapter *adap;
> @@ -1275,6 +1298,13 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
>  		vfd->tvnorms = tvnorms_cap;
>  		video_set_drvdata(vfd, dev);
>  
> +#ifdef CONFIG_MEDIA_CONTROLLER
> +		dev->vbi_cap_pad.flags = MEDIA_PAD_FL_SINK;
> +		ret = media_entity_pads_init(&vfd->entity, 1, &dev->vbi_cap_pad);
> +		if (ret)
> +			goto unreg_dev;
> +#endif
> +
>  		ret = video_register_device(vfd, VFL_TYPE_VBI, vbi_cap_nr[inst]);
>  		if (ret < 0)
>  			goto unreg_dev;
> @@ -1300,6 +1330,13 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
>  		vfd->tvnorms = tvnorms_out;
>  		video_set_drvdata(vfd, dev);
>  
> +#ifdef CONFIG_MEDIA_CONTROLLER
> +		dev->vbi_out_pad.flags = MEDIA_PAD_FL_SOURCE;
> +		ret = media_entity_pads_init(&vfd->entity, 1, &dev->vbi_out_pad);
> +		if (ret)
> +			goto unreg_dev;
> +#endif
> +
>  		ret = video_register_device(vfd, VFL_TYPE_VBI, vbi_out_nr[inst]);
>  		if (ret < 0)
>  			goto unreg_dev;
> @@ -1323,6 +1360,13 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
>  		vfd->lock = &dev->mutex;
>  		video_set_drvdata(vfd, dev);
>  
> +#ifdef CONFIG_MEDIA_CONTROLLER
> +		dev->sdr_cap_pad.flags = MEDIA_PAD_FL_SINK;
> +		ret = media_entity_pads_init(&vfd->entity, 1, &dev->sdr_cap_pad);
> +		if (ret)
> +			goto unreg_dev;
> +#endif
> +
>  		ret = video_register_device(vfd, VFL_TYPE_SDR, sdr_cap_nr[inst]);
>  		if (ret < 0)
>  			goto unreg_dev;
> @@ -1369,12 +1413,25 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
>  					  video_device_node_name(vfd));
>  	}
>  
> +#ifdef CONFIG_MEDIA_CONTROLLER
> +	/* Register the media device */
> +	ret = media_device_register(&dev->mdev);
> +	if (ret) {
> +		dev_err(dev->mdev.dev,
> +			"media device register failed (err=%d)\n", ret);
> +		goto unreg_dev;
> +	}
> +#endif
> +
>  	/* Now that everything is fine, let's add it to device list */
>  	vivid_devs[inst] = dev;
>  
>  	return 0;
>  
>  unreg_dev:
> +#ifdef CONFIG_MEDIA_CONTROLLER
> +	media_device_unregister(&dev->mdev);
> +#endif
>  	video_unregister_device(&dev->radio_tx_dev);
>  	video_unregister_device(&dev->radio_rx_dev);
>  	video_unregister_device(&dev->sdr_cap_dev);
> @@ -1445,6 +1502,10 @@ static int vivid_remove(struct platform_device *pdev)
>  		if (!dev)
>  			continue;
>  
> +#ifdef CONFIG_MEDIA_CONTROLLER
> +		media_device_unregister(&dev->mdev);
> +#endif
> +
>  		if (dev->has_vid_cap) {
>  			v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
>  				video_device_node_name(&dev->vid_cap_dev));
> diff --git a/drivers/media/platform/vivid/vivid-core.h b/drivers/media/platform/vivid/vivid-core.h
> index 477c80a4d44c..6ccd1f5c1d91 100644
> --- a/drivers/media/platform/vivid/vivid-core.h
> +++ b/drivers/media/platform/vivid/vivid-core.h
> @@ -136,6 +136,14 @@ struct vivid_cec_work {
>  struct vivid_dev {
>  	unsigned			inst;
>  	struct v4l2_device		v4l2_dev;
> +#ifdef CONFIG_MEDIA_CONTROLLER
> +	struct media_device		mdev;
> +	struct media_pad		vid_cap_pad;
> +	struct media_pad		vid_out_pad;
> +	struct media_pad		vbi_cap_pad;
> +	struct media_pad		vbi_out_pad;
> +	struct media_pad		sdr_cap_pad;
> +#endif
>  	struct v4l2_ctrl_handler	ctrl_hdl_user_gen;
>  	struct v4l2_ctrl_handler	ctrl_hdl_user_vid;
>  	struct v4l2_ctrl_handler	ctrl_hdl_user_aud;



Thanks,
Mauro

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

* Re: [PATCHv17 33/34] vivid: add request support
  2018-08-04 12:45 ` [PATCHv17 33/34] vivid: add request support Hans Verkuil
@ 2018-08-13 15:11   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-13 15:11 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:45:25 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Add support for requests to vivid.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>

Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
> ---
>  drivers/media/platform/vivid/vivid-core.c        |  8 ++++++++
>  drivers/media/platform/vivid/vivid-kthread-cap.c | 12 ++++++++++++
>  drivers/media/platform/vivid/vivid-kthread-out.c | 12 ++++++++++++
>  drivers/media/platform/vivid/vivid-sdr-cap.c     | 16 ++++++++++++++++
>  drivers/media/platform/vivid/vivid-vbi-cap.c     | 10 ++++++++++
>  drivers/media/platform/vivid/vivid-vbi-out.c     | 10 ++++++++++
>  drivers/media/platform/vivid/vivid-vid-cap.c     | 10 ++++++++++
>  drivers/media/platform/vivid/vivid-vid-out.c     | 10 ++++++++++
>  8 files changed, 88 insertions(+)
> 
> diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c
> index 1c448529be04..3f6f5cbe1b60 100644
> --- a/drivers/media/platform/vivid/vivid-core.c
> +++ b/drivers/media/platform/vivid/vivid-core.c
> @@ -627,6 +627,13 @@ static void vivid_dev_release(struct v4l2_device *v4l2_dev)
>  	kfree(dev);
>  }
>  
> +#ifdef CONFIG_MEDIA_CONTROLLER
> +static const struct media_device_ops vivid_media_ops = {
> +	.req_validate = vb2_request_validate,
> +	.req_queue = vb2_request_queue,
> +};
> +#endif
> +
>  static int vivid_create_instance(struct platform_device *pdev, int inst)
>  {
>  	static const struct v4l2_dv_timings def_dv_timings =
> @@ -664,6 +671,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
>  	strlcpy(dev->mdev.model, VIVID_MODULE_NAME, sizeof(dev->mdev.model));
>  	dev->mdev.dev = &pdev->dev;
>  	media_device_init(&dev->mdev);
> +	dev->mdev.ops = &vivid_media_ops;
>  #endif
>  
>  	/* register v4l2_device */
> diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.c b/drivers/media/platform/vivid/vivid-kthread-cap.c
> index f06003bb8e42..eebfff2126be 100644
> --- a/drivers/media/platform/vivid/vivid-kthread-cap.c
> +++ b/drivers/media/platform/vivid/vivid-kthread-cap.c
> @@ -703,6 +703,8 @@ static void vivid_thread_vid_cap_tick(struct vivid_dev *dev, int dropped_bufs)
>  		goto update_mv;
>  
>  	if (vid_cap_buf) {
> +		v4l2_ctrl_request_setup(vid_cap_buf->vb.vb2_buf.req_obj.req,
> +					&dev->ctrl_hdl_vid_cap);
>  		/* Fill buffer */
>  		vivid_fillbuff(dev, vid_cap_buf);
>  		dprintk(dev, 1, "filled buffer %d\n",
> @@ -713,6 +715,8 @@ static void vivid_thread_vid_cap_tick(struct vivid_dev *dev, int dropped_bufs)
>  			dev->fb_cap.fmt.pixelformat == dev->fmt_cap->fourcc)
>  			vivid_overlay(dev, vid_cap_buf);
>  
> +		v4l2_ctrl_request_complete(vid_cap_buf->vb.vb2_buf.req_obj.req,
> +					   &dev->ctrl_hdl_vid_cap);
>  		vb2_buffer_done(&vid_cap_buf->vb.vb2_buf, dev->dqbuf_error ?
>  				VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
>  		dprintk(dev, 2, "vid_cap buffer %d done\n",
> @@ -720,10 +724,14 @@ static void vivid_thread_vid_cap_tick(struct vivid_dev *dev, int dropped_bufs)
>  	}
>  
>  	if (vbi_cap_buf) {
> +		v4l2_ctrl_request_setup(vbi_cap_buf->vb.vb2_buf.req_obj.req,
> +					&dev->ctrl_hdl_vbi_cap);
>  		if (dev->stream_sliced_vbi_cap)
>  			vivid_sliced_vbi_cap_process(dev, vbi_cap_buf);
>  		else
>  			vivid_raw_vbi_cap_process(dev, vbi_cap_buf);
> +		v4l2_ctrl_request_complete(vbi_cap_buf->vb.vb2_buf.req_obj.req,
> +					   &dev->ctrl_hdl_vbi_cap);
>  		vb2_buffer_done(&vbi_cap_buf->vb.vb2_buf, dev->dqbuf_error ?
>  				VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
>  		dprintk(dev, 2, "vbi_cap %d done\n",
> @@ -891,6 +899,8 @@ void vivid_stop_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming)
>  			buf = list_entry(dev->vid_cap_active.next,
>  					 struct vivid_buffer, list);
>  			list_del(&buf->list);
> +			v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
> +						   &dev->ctrl_hdl_vid_cap);
>  			vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
>  			dprintk(dev, 2, "vid_cap buffer %d done\n",
>  				buf->vb.vb2_buf.index);
> @@ -904,6 +914,8 @@ void vivid_stop_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming)
>  			buf = list_entry(dev->vbi_cap_active.next,
>  					 struct vivid_buffer, list);
>  			list_del(&buf->list);
> +			v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
> +						   &dev->ctrl_hdl_vbi_cap);
>  			vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
>  			dprintk(dev, 2, "vbi_cap buffer %d done\n",
>  				buf->vb.vb2_buf.index);
> diff --git a/drivers/media/platform/vivid/vivid-kthread-out.c b/drivers/media/platform/vivid/vivid-kthread-out.c
> index 9981e7548019..5a14810eeb69 100644
> --- a/drivers/media/platform/vivid/vivid-kthread-out.c
> +++ b/drivers/media/platform/vivid/vivid-kthread-out.c
> @@ -75,6 +75,10 @@ static void vivid_thread_vid_out_tick(struct vivid_dev *dev)
>  		return;
>  
>  	if (vid_out_buf) {
> +		v4l2_ctrl_request_setup(vid_out_buf->vb.vb2_buf.req_obj.req,
> +					&dev->ctrl_hdl_vid_out);
> +		v4l2_ctrl_request_complete(vid_out_buf->vb.vb2_buf.req_obj.req,
> +					   &dev->ctrl_hdl_vid_out);
>  		vid_out_buf->vb.sequence = dev->vid_out_seq_count;
>  		if (dev->field_out == V4L2_FIELD_ALTERNATE) {
>  			/*
> @@ -92,6 +96,10 @@ static void vivid_thread_vid_out_tick(struct vivid_dev *dev)
>  	}
>  
>  	if (vbi_out_buf) {
> +		v4l2_ctrl_request_setup(vbi_out_buf->vb.vb2_buf.req_obj.req,
> +					&dev->ctrl_hdl_vbi_out);
> +		v4l2_ctrl_request_complete(vbi_out_buf->vb.vb2_buf.req_obj.req,
> +					   &dev->ctrl_hdl_vbi_out);
>  		if (dev->stream_sliced_vbi_out)
>  			vivid_sliced_vbi_out_process(dev, vbi_out_buf);
>  
> @@ -262,6 +270,8 @@ void vivid_stop_generating_vid_out(struct vivid_dev *dev, bool *pstreaming)
>  			buf = list_entry(dev->vid_out_active.next,
>  					 struct vivid_buffer, list);
>  			list_del(&buf->list);
> +			v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
> +						   &dev->ctrl_hdl_vid_out);
>  			vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
>  			dprintk(dev, 2, "vid_out buffer %d done\n",
>  				buf->vb.vb2_buf.index);
> @@ -275,6 +285,8 @@ void vivid_stop_generating_vid_out(struct vivid_dev *dev, bool *pstreaming)
>  			buf = list_entry(dev->vbi_out_active.next,
>  					 struct vivid_buffer, list);
>  			list_del(&buf->list);
> +			v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
> +						   &dev->ctrl_hdl_vbi_out);
>  			vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
>  			dprintk(dev, 2, "vbi_out buffer %d done\n",
>  				buf->vb.vb2_buf.index);
> diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.c b/drivers/media/platform/vivid/vivid-sdr-cap.c
> index cfb7cb4d37a8..76cf8810a974 100644
> --- a/drivers/media/platform/vivid/vivid-sdr-cap.c
> +++ b/drivers/media/platform/vivid/vivid-sdr-cap.c
> @@ -102,6 +102,10 @@ static void vivid_thread_sdr_cap_tick(struct vivid_dev *dev)
>  
>  	if (sdr_cap_buf) {
>  		sdr_cap_buf->vb.sequence = dev->sdr_cap_seq_count;
> +		v4l2_ctrl_request_setup(sdr_cap_buf->vb.vb2_buf.req_obj.req,
> +					&dev->ctrl_hdl_sdr_cap);
> +		v4l2_ctrl_request_complete(sdr_cap_buf->vb.vb2_buf.req_obj.req,
> +					   &dev->ctrl_hdl_sdr_cap);
>  		vivid_sdr_cap_process(dev, sdr_cap_buf);
>  		sdr_cap_buf->vb.vb2_buf.timestamp =
>  			ktime_get_ns() + dev->time_wrap_offset;
> @@ -272,6 +276,8 @@ static int sdr_cap_start_streaming(struct vb2_queue *vq, unsigned count)
>  
>  		list_for_each_entry_safe(buf, tmp, &dev->sdr_cap_active, list) {
>  			list_del(&buf->list);
> +			v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
> +						   &dev->ctrl_hdl_sdr_cap);
>  			vb2_buffer_done(&buf->vb.vb2_buf,
>  					VB2_BUF_STATE_QUEUED);
>  		}
> @@ -293,6 +299,8 @@ static void sdr_cap_stop_streaming(struct vb2_queue *vq)
>  		buf = list_entry(dev->sdr_cap_active.next,
>  				struct vivid_buffer, list);
>  		list_del(&buf->list);
> +		v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
> +					   &dev->ctrl_hdl_sdr_cap);
>  		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
>  	}
>  
> @@ -303,12 +311,20 @@ static void sdr_cap_stop_streaming(struct vb2_queue *vq)
>  	mutex_lock(&dev->mutex);
>  }
>  
> +static void sdr_cap_buf_request_complete(struct vb2_buffer *vb)
> +{
> +	struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
> +
> +	v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_sdr_cap);
> +}
> +
>  const struct vb2_ops vivid_sdr_cap_qops = {
>  	.queue_setup		= sdr_cap_queue_setup,
>  	.buf_prepare		= sdr_cap_buf_prepare,
>  	.buf_queue		= sdr_cap_buf_queue,
>  	.start_streaming	= sdr_cap_start_streaming,
>  	.stop_streaming		= sdr_cap_stop_streaming,
> +	.buf_request_complete	= sdr_cap_buf_request_complete,
>  	.wait_prepare		= vb2_ops_wait_prepare,
>  	.wait_finish		= vb2_ops_wait_finish,
>  };
> diff --git a/drivers/media/platform/vivid/vivid-vbi-cap.c b/drivers/media/platform/vivid/vivid-vbi-cap.c
> index 92a852955173..903cebeb5ce5 100644
> --- a/drivers/media/platform/vivid/vivid-vbi-cap.c
> +++ b/drivers/media/platform/vivid/vivid-vbi-cap.c
> @@ -204,6 +204,8 @@ static int vbi_cap_start_streaming(struct vb2_queue *vq, unsigned count)
>  
>  		list_for_each_entry_safe(buf, tmp, &dev->vbi_cap_active, list) {
>  			list_del(&buf->list);
> +			v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
> +						   &dev->ctrl_hdl_vbi_cap);
>  			vb2_buffer_done(&buf->vb.vb2_buf,
>  					VB2_BUF_STATE_QUEUED);
>  		}
> @@ -220,12 +222,20 @@ static void vbi_cap_stop_streaming(struct vb2_queue *vq)
>  	vivid_stop_generating_vid_cap(dev, &dev->vbi_cap_streaming);
>  }
>  
> +static void vbi_cap_buf_request_complete(struct vb2_buffer *vb)
> +{
> +	struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
> +
> +	v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_vbi_cap);
> +}
> +
>  const struct vb2_ops vivid_vbi_cap_qops = {
>  	.queue_setup		= vbi_cap_queue_setup,
>  	.buf_prepare		= vbi_cap_buf_prepare,
>  	.buf_queue		= vbi_cap_buf_queue,
>  	.start_streaming	= vbi_cap_start_streaming,
>  	.stop_streaming		= vbi_cap_stop_streaming,
> +	.buf_request_complete	= vbi_cap_buf_request_complete,
>  	.wait_prepare		= vb2_ops_wait_prepare,
>  	.wait_finish		= vb2_ops_wait_finish,
>  };
> diff --git a/drivers/media/platform/vivid/vivid-vbi-out.c b/drivers/media/platform/vivid/vivid-vbi-out.c
> index 69486c130a7e..9357c07e30d6 100644
> --- a/drivers/media/platform/vivid/vivid-vbi-out.c
> +++ b/drivers/media/platform/vivid/vivid-vbi-out.c
> @@ -96,6 +96,8 @@ static int vbi_out_start_streaming(struct vb2_queue *vq, unsigned count)
>  
>  		list_for_each_entry_safe(buf, tmp, &dev->vbi_out_active, list) {
>  			list_del(&buf->list);
> +			v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
> +						   &dev->ctrl_hdl_vbi_out);
>  			vb2_buffer_done(&buf->vb.vb2_buf,
>  					VB2_BUF_STATE_QUEUED);
>  		}
> @@ -115,12 +117,20 @@ static void vbi_out_stop_streaming(struct vb2_queue *vq)
>  	dev->vbi_out_have_cc[1] = false;
>  }
>  
> +static void vbi_out_buf_request_complete(struct vb2_buffer *vb)
> +{
> +	struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
> +
> +	v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_vbi_out);
> +}
> +
>  const struct vb2_ops vivid_vbi_out_qops = {
>  	.queue_setup		= vbi_out_queue_setup,
>  	.buf_prepare		= vbi_out_buf_prepare,
>  	.buf_queue		= vbi_out_buf_queue,
>  	.start_streaming	= vbi_out_start_streaming,
>  	.stop_streaming		= vbi_out_stop_streaming,
> +	.buf_request_complete	= vbi_out_buf_request_complete,
>  	.wait_prepare		= vb2_ops_wait_prepare,
>  	.wait_finish		= vb2_ops_wait_finish,
>  };
> diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c
> index 1599159f2574..b2aad441a071 100644
> --- a/drivers/media/platform/vivid/vivid-vid-cap.c
> +++ b/drivers/media/platform/vivid/vivid-vid-cap.c
> @@ -240,6 +240,8 @@ static int vid_cap_start_streaming(struct vb2_queue *vq, unsigned count)
>  
>  		list_for_each_entry_safe(buf, tmp, &dev->vid_cap_active, list) {
>  			list_del(&buf->list);
> +			v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
> +						   &dev->ctrl_hdl_vid_cap);
>  			vb2_buffer_done(&buf->vb.vb2_buf,
>  					VB2_BUF_STATE_QUEUED);
>  		}
> @@ -257,6 +259,13 @@ static void vid_cap_stop_streaming(struct vb2_queue *vq)
>  	dev->can_loop_video = false;
>  }
>  
> +static void vid_cap_buf_request_complete(struct vb2_buffer *vb)
> +{
> +	struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
> +
> +	v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_vid_cap);
> +}
> +
>  const struct vb2_ops vivid_vid_cap_qops = {
>  	.queue_setup		= vid_cap_queue_setup,
>  	.buf_prepare		= vid_cap_buf_prepare,
> @@ -264,6 +273,7 @@ const struct vb2_ops vivid_vid_cap_qops = {
>  	.buf_queue		= vid_cap_buf_queue,
>  	.start_streaming	= vid_cap_start_streaming,
>  	.stop_streaming		= vid_cap_stop_streaming,
> +	.buf_request_complete	= vid_cap_buf_request_complete,
>  	.wait_prepare		= vb2_ops_wait_prepare,
>  	.wait_finish		= vb2_ops_wait_finish,
>  };
> diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c
> index 51fec66d8d45..423a67133f28 100644
> --- a/drivers/media/platform/vivid/vivid-vid-out.c
> +++ b/drivers/media/platform/vivid/vivid-vid-out.c
> @@ -162,6 +162,8 @@ static int vid_out_start_streaming(struct vb2_queue *vq, unsigned count)
>  
>  		list_for_each_entry_safe(buf, tmp, &dev->vid_out_active, list) {
>  			list_del(&buf->list);
> +			v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req,
> +						   &dev->ctrl_hdl_vid_out);
>  			vb2_buffer_done(&buf->vb.vb2_buf,
>  					VB2_BUF_STATE_QUEUED);
>  		}
> @@ -179,12 +181,20 @@ static void vid_out_stop_streaming(struct vb2_queue *vq)
>  	dev->can_loop_video = false;
>  }
>  
> +static void vid_out_buf_request_complete(struct vb2_buffer *vb)
> +{
> +	struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
> +
> +	v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_vid_out);
> +}
> +
>  const struct vb2_ops vivid_vid_out_qops = {
>  	.queue_setup		= vid_out_queue_setup,
>  	.buf_prepare		= vid_out_buf_prepare,
>  	.buf_queue		= vid_out_buf_queue,
>  	.start_streaming	= vid_out_start_streaming,
>  	.stop_streaming		= vid_out_stop_streaming,
> +	.buf_request_complete	= vid_out_buf_request_complete,
>  	.wait_prepare		= vb2_ops_wait_prepare,
>  	.wait_finish		= vb2_ops_wait_finish,
>  };



Thanks,
Mauro

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

* Re: [PATCHv17 34/34] RFC: media-requests: add debugfs node
  2018-08-04 12:45 ` [PATCHv17 34/34] RFC: media-requests: add debugfs node Hans Verkuil
@ 2018-08-13 15:15   ` Mauro Carvalho Chehab
  2018-08-14  7:33     ` Hans Verkuil
  0 siblings, 1 reply; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-13 15:15 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Sat,  4 Aug 2018 14:45:26 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Keep track of the number of requests and request objects of a media
> device. Helps to verify that all request-related memory is freed.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>



> ---
>  drivers/media/media-device.c  | 41 +++++++++++++++++++++++++++++++++++
>  drivers/media/media-devnode.c | 17 +++++++++++++++
>  drivers/media/media-request.c |  5 +++++
>  include/media/media-device.h  | 11 ++++++++++
>  include/media/media-devnode.h |  4 ++++
>  include/media/media-request.h |  2 ++
>  6 files changed, 80 insertions(+)
> 
> diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
> index 4b9a8de05562..28a891b53886 100644
> --- a/drivers/media/media-device.c
> +++ b/drivers/media/media-device.c
> @@ -691,6 +691,23 @@ void media_device_unregister_entity(struct media_entity *entity)
>  }
>  EXPORT_SYMBOL_GPL(media_device_unregister_entity);
>  
> +#ifdef CONFIG_DEBUG_FS

Patch itself looks good. Yet, perhaps we could request both
CONFIG_DEBUG_FS and CONFIG_VIDEO_ADV_DEBUG.

Also, instead of ifdef, please use IS_ENABLED for DEBUG_FS. That tends
to be safer long term.

With that:

Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>

> +/*
> + * Log the state of media requests.
> + * Very useful for debugging.
> + */
> +static int media_device_requests(struct seq_file *file, void *priv)
> +{
> +	struct media_device *dev = dev_get_drvdata(file->private);
> +
> +	seq_printf(file, "number of requests: %d\n",
> +		   atomic_read(&dev->num_requests));
> +	seq_printf(file, "number of request objects: %d\n",
> +		   atomic_read(&dev->num_request_objects));
> +	return 0;
> +}
> +#endif
> +
>  /**
>   * media_device_init() - initialize a media device
>   * @mdev:	The media device
> @@ -713,6 +730,9 @@ void media_device_init(struct media_device *mdev)
>  	mutex_init(&mdev->graph_mutex);
>  	ida_init(&mdev->entity_internal_idx);
>  
> +	atomic_set(&mdev->num_requests, 0);
> +	atomic_set(&mdev->num_request_objects, 0);
> +
>  	dev_dbg(mdev->dev, "Media device initialized\n");
>  }
>  EXPORT_SYMBOL_GPL(media_device_init);
> @@ -764,6 +784,26 @@ int __must_check __media_device_register(struct media_device *mdev,
>  
>  	dev_dbg(mdev->dev, "Media device registered\n");
>  
> +#ifdef CONFIG_DEBUG_FS
> +	if (!media_top_dir)
> +		return 0;
> +
> +	mdev->media_dir = debugfs_create_dir(dev_name(&devnode->dev),
> +					     media_top_dir);
> +	if (IS_ERR_OR_NULL(mdev->media_dir)) {
> +		dev_warn(mdev->dev, "Failed to create debugfs dir\n");
> +		return 0;
> +	}
> +	mdev->requests_file = debugfs_create_devm_seqfile(&devnode->dev,
> +		"requests", mdev->media_dir, media_device_requests);
> +	if (IS_ERR_OR_NULL(mdev->requests_file)) {
> +		dev_warn(mdev->dev, "Failed to create requests file\n");
> +		debugfs_remove_recursive(mdev->media_dir);
> +		mdev->media_dir = NULL;
> +		return 0;
> +	}
> +#endif
> +
>  	return 0;
>  }
>  EXPORT_SYMBOL_GPL(__media_device_register);
> @@ -841,6 +881,7 @@ void media_device_unregister(struct media_device *mdev)
>  
>  	dev_dbg(mdev->dev, "Media device unregistered\n");
>  
> +	debugfs_remove_recursive(mdev->media_dir);
>  	device_remove_file(&mdev->devnode->dev, &dev_attr_model);
>  	media_devnode_unregister(mdev->devnode);
>  	/* devnode free is handled in media_devnode_*() */
> diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c
> index 6b87a721dc49..4358ed22f208 100644
> --- a/drivers/media/media-devnode.c
> +++ b/drivers/media/media-devnode.c
> @@ -53,6 +53,12 @@ static dev_t media_dev_t;
>  static DEFINE_MUTEX(media_devnode_lock);
>  static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES);
>  
> +/*
> + * debugfs
> + */
> +struct dentry *media_top_dir;
> +
> +
>  /* Called when the last user of the media device exits. */
>  static void media_devnode_release(struct device *cd)
>  {
> @@ -259,6 +265,8 @@ int __must_check media_devnode_register(struct media_device *mdev,
>  		goto cdev_add_error;
>  	}
>  
> +	dev_set_drvdata(&devnode->dev, mdev);
> +
>  	/* Part 4: Activate this minor. The char device can now be used. */
>  	set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
>  
> @@ -310,6 +318,14 @@ static int __init media_devnode_init(void)
>  		return ret;
>  	}
>  
> +#ifdef CONFIG_DEBUG_FS
> +	media_top_dir = debugfs_create_dir("media", NULL);
> +	if (IS_ERR_OR_NULL(media_top_dir)) {
> +		pr_warn("Failed to create debugfs media dir\n");
> +		media_top_dir = NULL;
> +	}
> +#endif
> +
>  	ret = bus_register(&media_bus_type);
>  	if (ret < 0) {
>  		unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
> @@ -322,6 +338,7 @@ static int __init media_devnode_init(void)
>  
>  static void __exit media_devnode_exit(void)
>  {
> +	debugfs_remove_recursive(media_top_dir);
>  	bus_unregister(&media_bus_type);
>  	unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
>  }
> diff --git a/drivers/media/media-request.c b/drivers/media/media-request.c
> index a5b70a4e613b..8ba97a9c4bf1 100644
> --- a/drivers/media/media-request.c
> +++ b/drivers/media/media-request.c
> @@ -72,6 +72,7 @@ static void media_request_release(struct kref *kref)
>  		mdev->ops->req_free(req);
>  	else
>  		kfree(req);
> +	atomic_dec(&mdev->num_requests);
>  }
>  
>  void media_request_put(struct media_request *req)
> @@ -318,6 +319,7 @@ int media_request_alloc(struct media_device *mdev,
>  	get_task_comm(comm, current);
>  	snprintf(req->debug_str, sizeof(req->debug_str), "%s:%d",
>  		 comm, fd);
> +	atomic_inc(&mdev->num_requests);
>  	dev_dbg(mdev->dev, "request: allocated %s\n", req->debug_str);
>  
>  	fd_install(fd, filp);
> @@ -342,6 +344,7 @@ static void media_request_object_release(struct kref *kref)
>  	if (WARN_ON(req))
>  		media_request_object_unbind(obj);
>  	obj->ops->release(obj);
> +	atomic_dec(&obj->mdev->num_request_objects);
>  }
>  
>  struct media_request_object *
> @@ -405,10 +408,12 @@ int media_request_object_bind(struct media_request *req,
>  	obj->req = req;
>  	obj->ops = ops;
>  	obj->priv = priv;
> +	obj->mdev = req->mdev;
>  
>  	list_add_tail(&obj->list, &req->objects);
>  	req->num_incomplete_objects++;
>  	ret = 0;
> +	atomic_inc(&obj->mdev->num_request_objects);
>  
>  unlock:
>  	spin_unlock_irqrestore(&req->lock, flags);
> diff --git a/include/media/media-device.h b/include/media/media-device.h
> index 110b89567671..2de0606938d4 100644
> --- a/include/media/media-device.h
> +++ b/include/media/media-device.h
> @@ -21,6 +21,7 @@
>  
>  #include <linux/list.h>
>  #include <linux/mutex.h>
> +#include <linux/atomic.h>
>  
>  #include <media/media-devnode.h>
>  #include <media/media-entity.h>
> @@ -107,6 +108,10 @@ struct media_device_ops {
>   * @ops:	Operation handler callbacks
>   * @req_queue_mutex: Serialise the MEDIA_REQUEST_IOC_QUEUE ioctl w.r.t.
>   *		     other operations that stop or start streaming.
> + * @num_requests: number of associated requests
> + * @num_request_objects: number of associated request objects
> + * @media_dir:	DebugFS media directory
> + * @requests_file: DebugFS requests file
>   *
>   * This structure represents an abstract high-level media device. It allows easy
>   * access to entities and provides basic media device-level support. The
> @@ -179,6 +184,12 @@ struct media_device {
>  	const struct media_device_ops *ops;
>  
>  	struct mutex req_queue_mutex;
> +	atomic_t num_requests;
> +	atomic_t num_request_objects;
> +
> +	/* debugfs */
> +	struct dentry *media_dir;
> +	struct dentry *requests_file;
>  };
>  
>  /* We don't need to include pci.h or usb.h here */
> diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
> index dc2f64e1b08f..984b62b634d3 100644
> --- a/include/media/media-devnode.h
> +++ b/include/media/media-devnode.h
> @@ -28,9 +28,13 @@
>  #include <linux/fs.h>
>  #include <linux/device.h>
>  #include <linux/cdev.h>
> +#include <linux/debugfs.h>
>  
>  struct media_device;
>  
> +/* DebugFS top-level media directory */
> +extern struct dentry *media_top_dir;
> +
>  /*
>   * Flag to mark the media_devnode struct as registered. Drivers must not touch
>   * this flag directly, it will be set and cleared by media_devnode_register and
> diff --git a/include/media/media-request.h b/include/media/media-request.h
> index fd08d7a431a1..76727d4a89c3 100644
> --- a/include/media/media-request.h
> +++ b/include/media/media-request.h
> @@ -210,6 +210,7 @@ struct media_request_object_ops {
>   * struct media_request_object - An opaque object that belongs to a media
>   *				 request
>   *
> + * @mdev: Media device this object belongs to
>   * @ops: object's operations
>   * @priv: object's priv pointer
>   * @req: the request this object belongs to (can be NULL)
> @@ -221,6 +222,7 @@ struct media_request_object_ops {
>   * another struct that contains the actual data for this request object.
>   */
>  struct media_request_object {
> +	struct media_device *mdev;
>  	const struct media_request_object_ops *ops;
>  	void *priv;
>  	struct media_request *req;



Thanks,
Mauro

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

* Re: [PATCHv17 26/34] videobuf2-v4l2: add vb2_request_queue/validate helpers
  2018-08-13 14:53   ` Mauro Carvalho Chehab
@ 2018-08-14  7:19     ` Hans Verkuil
  2018-08-14  7:49       ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-14  7:19 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media, Hans Verkuil

On 13/08/18 16:53, Mauro Carvalho Chehab wrote:
> Em Sat,  4 Aug 2018 14:45:18 +0200
> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> 
>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>
>> The generic vb2_request_validate helper function checks if
>> there are buffers in the request and if so, prepares (validates)
>> all objects in the request.
>>
>> The generic vb2_request_queue helper function queues all buffer
>> objects in the validated request.
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> ---
>>  .../media/common/videobuf2/videobuf2-v4l2.c   | 44 +++++++++++++++++++
>>  include/media/videobuf2-v4l2.h                |  4 ++
>>  2 files changed, 48 insertions(+)
>>
>> diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c
>> index 9c652afa62ab..88d8f60c742b 100644
>> --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
>> +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
>> @@ -1100,6 +1100,50 @@ void vb2_ops_wait_finish(struct vb2_queue *vq)
>>  }
>>  EXPORT_SYMBOL_GPL(vb2_ops_wait_finish);
>>  
>> +int vb2_request_validate(struct media_request *req)
>> +{
>> +	struct media_request_object *obj;
>> +	int ret = 0;
>> +
>> +	if (!vb2_request_has_buffers(req))
>> +		return -ENOENT;
> 
> This holds the spinlock...

The spinlock in vb2_request_has_buffers() is not needed if vb2_request_has_buffers()
is called from this validate function. While validating no new objects can be
added to the request, and since nothing has been queued yet objects cannot be
deleted either. That's true for this whole vb2_request_validate() function.

But should vb2_request_has_buffers() ever be called from a non-validate context,
then the spinlock would be needed.

I want to keep the spinlock in that function for now as it is more robust.

Regards,

	Hans

> 
>> +
>> +	list_for_each_entry(obj, &req->objects, list) {
>> +		if (!obj->ops->prepare)
>> +			continue;
>> +
>> +		ret = obj->ops->prepare(obj);
>> +		if (ret)
>> +			break;
>> +	}
>> +
> 
> Shouldn't this logic hold it too?
> 
>> +	if (ret) {
>> +		list_for_each_entry_continue_reverse(obj, &req->objects, list)
>> +			if (obj->ops->unprepare)
>> +				obj->ops->unprepare(obj);
>> +		return ret;
>> +	}
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(vb2_request_validate);
>> +
>> +void vb2_request_queue(struct media_request *req)
>> +{
>> +	struct media_request_object *obj, *obj_safe;
>> +
>> +	/* Queue all non-buffer objects */
>> +	list_for_each_entry_safe(obj, obj_safe, &req->objects, list)
>> +		if (obj->ops->queue && !vb2_request_object_is_buffer(obj))
>> +			obj->ops->queue(obj);
>> +
>> +	/* Queue all buffer objects */
>> +	list_for_each_entry_safe(obj, obj_safe, &req->objects, list) {
>> +		if (obj->ops->queue && vb2_request_object_is_buffer(obj))
>> +			obj->ops->queue(obj);
>> +	}
>> +}
>> +EXPORT_SYMBOL_GPL(vb2_request_queue);
>> +
>>  MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2");
>>  MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>, Marek Szyprowski");
>>  MODULE_LICENSE("GPL");
>> diff --git a/include/media/videobuf2-v4l2.h b/include/media/videobuf2-v4l2.h
>> index 91a2b3e1a642..727855463838 100644
>> --- a/include/media/videobuf2-v4l2.h
>> +++ b/include/media/videobuf2-v4l2.h
>> @@ -303,4 +303,8 @@ void vb2_ops_wait_prepare(struct vb2_queue *vq);
>>   */
>>  void vb2_ops_wait_finish(struct vb2_queue *vq);
>>  
>> +struct media_request;
>> +int vb2_request_validate(struct media_request *req);
>> +void vb2_request_queue(struct media_request *req);
>> +
>>  #endif /* _MEDIA_VIDEOBUF2_V4L2_H */
> 
> 
> 
> Thanks,
> Mauro
> 

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

* Re: [PATCHv17 25/34] videobuf2-core: add request helper functions
  2018-08-13 14:50   ` Mauro Carvalho Chehab
@ 2018-08-14  7:22     ` Hans Verkuil
  0 siblings, 0 replies; 106+ messages in thread
From: Hans Verkuil @ 2018-08-14  7:22 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media, Hans Verkuil

On 13/08/18 16:50, Mauro Carvalho Chehab wrote:
> Em Sat,  4 Aug 2018 14:45:17 +0200
> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> 
>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>
>> Add a new helper function to tell if a request object is a buffer.
>>
>> Add a new helper function that returns true if a media_request
>> contains at least one buffer.
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> ---
>>  .../media/common/videobuf2/videobuf2-core.c   | 24 +++++++++++++++++++
>>  include/media/videobuf2-core.h                | 15 ++++++++++++
>>  2 files changed, 39 insertions(+)
>>
>> diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
>> index 3e6db7d30989..f8af7add35ab 100644
>> --- a/drivers/media/common/videobuf2/videobuf2-core.c
>> +++ b/drivers/media/common/videobuf2/videobuf2-core.c
>> @@ -1362,6 +1362,30 @@ static const struct media_request_object_ops vb2_core_req_ops = {
>>  	.release = vb2_req_release,
>>  };
>>  
>> +bool vb2_request_object_is_buffer(struct media_request_object *obj)
>> +{
>> +	return obj->ops == &vb2_core_req_ops;
>> +}
>> +EXPORT_SYMBOL_GPL(vb2_request_object_is_buffer);
>> +
>> +bool vb2_request_has_buffers(struct media_request *req)
>> +{
>> +	struct media_request_object *obj;
>> +	unsigned long flags;
>> +	bool has_buffers = false;
>> +
>> +	spin_lock_irqsave(&req->lock, flags);
>> +	list_for_each_entry(obj, &req->objects, list) {
>> +		if (vb2_request_object_is_buffer(obj)) {
>> +			has_buffers = true;
>> +			break;
>> +		}
>> +	}
>> +	spin_unlock_irqrestore(&req->lock, flags);
>> +	return has_buffers;
>> +}
>> +EXPORT_SYMBOL_GPL(vb2_request_has_buffers);
>> +
>>  int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb)
>>  {
>>  	struct vb2_buffer *vb;
>> diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
>> index 8a8d7732d182..cad712403d14 100644
>> --- a/include/media/videobuf2-core.h
>> +++ b/include/media/videobuf2-core.h
>> @@ -1168,4 +1168,19 @@ bool vb2_buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb);
>>   */
>>  int vb2_verify_memory_type(struct vb2_queue *q,
>>  		enum vb2_memory memory, unsigned int type);
>> +
>> +/**
>> + * vb2_request_object_is_buffer() - return true if the object is a buffer
>> + *
>> + * @obj:	the request object.
> 
> It should be mentioned that it should be called with req->lock locked.

No, it doesn't.

All it does is:

bool vb2_request_object_is_buffer(struct media_request_object *obj)
{
        return obj->ops == &vb2_core_req_ops;
}
EXPORT_SYMBOL_GPL(vb2_request_object_is_buffer);

That test doesn't require req->lock to be locked.

Regards,

	Hans

> 
> With such change:
> 
> Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
> 
>> + */
>> +bool vb2_request_object_is_buffer(struct media_request_object *obj);
>> +
>> +/**
>> + * vb2_request_has_buffers() - return true if the request contains buffers
>> + *
>> + * @req:	the request.
>> + */
>> +bool vb2_request_has_buffers(struct media_request *req);
>> +
>>  #endif /* _MEDIA_VIDEOBUF2_CORE_H */
> 
> 
> 
> Thanks,
> Mauro
> 

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

* Re: [PATCHv17 29/34] v4l2-mem2mem: add vb2_m2m_request_queue
  2018-08-13 15:02   ` Mauro Carvalho Chehab
@ 2018-08-14  7:26     ` Hans Verkuil
  0 siblings, 0 replies; 106+ messages in thread
From: Hans Verkuil @ 2018-08-14  7:26 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media, Hans Verkuil

On 13/08/18 17:02, Mauro Carvalho Chehab wrote:
> Em Sat,  4 Aug 2018 14:45:21 +0200
> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> 
>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>
>> For mem2mem devices we have to make sure that v4l2_m2m_try_schedule()
>> is called whenever a request is queued.
>>
>> We do that by creating a vb2_m2m_request_queue() helper that should
>> be used instead of the 'normal' vb2_request_queue() helper. The m2m
>> helper function will call v4l2_m2m_try_schedule() as needed.
>>
>> In addition we also avoid calling v4l2_m2m_try_schedule() when preparing
>> or queueing a buffer for a request since that is no longer needed.
>> Instead this helper function will do that when the request is actually
>> queued.
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
> 
> But please notice below that there's another change on patch 1/34 due
> to this patchset.
> 
>> ---
>>  drivers/media/v4l2-core/v4l2-mem2mem.c | 59 ++++++++++++++++++++++----
>>  include/media/v4l2-mem2mem.h           |  4 ++
>>  2 files changed, 55 insertions(+), 8 deletions(-)
>>
>> diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c
>> index b09494174eb4..56a16cfef6f8 100644
>> --- a/drivers/media/v4l2-core/v4l2-mem2mem.c
>> +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
>> @@ -369,7 +369,7 @@ static void v4l2_m2m_cancel_job(struct v4l2_m2m_ctx *m2m_ctx)
>>  		spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
>>  		if (m2m_dev->m2m_ops->job_abort)
>>  			m2m_dev->m2m_ops->job_abort(m2m_ctx->priv);
>> -		dprintk("m2m_ctx %p running, will wait to complete", m2m_ctx);
>> +		dprintk("m2m_ctx %p running, will wait to complete\n", m2m_ctx);
>>  		wait_event(m2m_ctx->finished,
>>  				!(m2m_ctx->job_flags & TRANS_RUNNING));
>>  	} else if (m2m_ctx->job_flags & TRANS_QUEUED) {
>> @@ -460,8 +460,14 @@ int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
>>  	int ret;
>>  
>>  	vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
>> +	if (!V4L2_TYPE_IS_OUTPUT(vq->type) &&
>> +	    (buf->flags & V4L2_BUF_FLAG_REQUEST_FD)) {
>> +		dprintk("%s: requests cannot be used with capture buffers\n",
>> +			__func__);
> 
> I had to double-check the documentation at patch 01/34. While on one
> part is says the same, saying that -EPERM is the error code, on
> another part, it says:
> 
> 	+Note that there is typically no need to use the Request API for CAPTURE buffers
> 	+since there are no per-frame settings to report there.
> 
> "typically" means that the normal usecase is to now allow, but gives
> it open to implementations doing it.
> 
> Please fix that paragraph on patch 01/34 to reflect that no CAPTURE
> buffers should be used with request API for m2m, in order to
> reflect the code's implementation.

Good catch! That's a left-over from earlier versions of the documentation.
I'll update the doc text.

Regards,

	Hans

> 
>> +		return -EPERM;
>> +	}
>>  	ret = vb2_qbuf(vq, vdev->v4l2_dev->mdev, buf);
>> -	if (!ret)
>> +	if (!ret && !(buf->flags & V4L2_BUF_FLAG_IN_REQUEST))
>>  		v4l2_m2m_try_schedule(m2m_ctx);
>>  
>>  	return ret;
>> @@ -483,14 +489,9 @@ int v4l2_m2m_prepare_buf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
>>  {
>>  	struct video_device *vdev = video_devdata(file);
>>  	struct vb2_queue *vq;
>> -	int ret;
>>  
>>  	vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
>> -	ret = vb2_prepare_buf(vq, vdev->v4l2_dev->mdev, buf);
>> -	if (!ret)
>> -		v4l2_m2m_try_schedule(m2m_ctx);
>> -
>> -	return ret;
>> +	return vb2_prepare_buf(vq, vdev->v4l2_dev->mdev, buf);
>>  }
>>  EXPORT_SYMBOL_GPL(v4l2_m2m_prepare_buf);
>>  
>> @@ -934,6 +935,48 @@ void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx,
>>  }
>>  EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue);
>>  
>> +void vb2_m2m_request_queue(struct media_request *req)
>> +{
>> +	struct media_request_object *obj, *obj_safe;
>> +	struct v4l2_m2m_ctx *m2m_ctx = NULL;
>> +
>> +	/* Queue all non-buffer objects */
>> +	list_for_each_entry_safe(obj, obj_safe, &req->objects, list)
>> +		if (obj->ops->queue && !vb2_request_object_is_buffer(obj))
>> +			obj->ops->queue(obj);
>> +
>> +	/* Queue all buffer objects */
>> +	list_for_each_entry_safe(obj, obj_safe, &req->objects, list) {
>> +		struct v4l2_m2m_ctx *m2m_ctx_obj;
>> +		struct vb2_buffer *vb;
>> +
>> +		if (!obj->ops->queue || !vb2_request_object_is_buffer(obj))
>> +			continue;
>> +
>> +		/* Sanity checks */
>> +		vb = container_of(obj, struct vb2_buffer, req_obj);
>> +		WARN_ON(!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type));
>> +		m2m_ctx_obj = container_of(vb->vb2_queue,
>> +					   struct v4l2_m2m_ctx,
>> +					   out_q_ctx.q);
>> +		WARN_ON(m2m_ctx && m2m_ctx_obj != m2m_ctx);
>> +		m2m_ctx = m2m_ctx_obj;
>> +
>> +		/*
>> +		 * The buffer we queue here can in theory be immediately
>> +		 * unbound, hence the use of list_for_each_entry_safe()
>> +		 * above and why we call the queue op last.
>> +		 */
>> +		obj->ops->queue(obj);
>> +	}
>> +
>> +	WARN_ON(!m2m_ctx);
>> +
>> +	if (m2m_ctx)
>> +		v4l2_m2m_try_schedule(m2m_ctx);
>> +}
>> +EXPORT_SYMBOL_GPL(vb2_m2m_request_queue);
>> +
>>  /* Videobuf2 ioctl helpers */
>>  
>>  int v4l2_m2m_ioctl_reqbufs(struct file *file, void *priv,
>> diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h
>> index d655720e16a1..58c1ecf3d648 100644
>> --- a/include/media/v4l2-mem2mem.h
>> +++ b/include/media/v4l2-mem2mem.h
>> @@ -622,6 +622,10 @@ v4l2_m2m_dst_buf_remove_by_idx(struct v4l2_m2m_ctx *m2m_ctx, unsigned int idx)
>>  	return v4l2_m2m_buf_remove_by_idx(&m2m_ctx->cap_q_ctx, idx);
>>  }
>>  
>> +/* v4l2 request helper */
>> +
>> +void vb2_m2m_request_queue(struct media_request *req);
>> +
>>  /* v4l2 ioctl helpers */
>>  
>>  int v4l2_m2m_ioctl_reqbufs(struct file *file, void *priv,
> 
> 
> 
> Thanks,
> Mauro
> 

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

* Re: [PATCHv17 30/34] vim2m: use workqueue
  2018-08-13 15:05   ` Mauro Carvalho Chehab
@ 2018-08-14  7:28     ` Hans Verkuil
  2018-08-14  7:41       ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-14  7:28 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media, Hans Verkuil

On 13/08/18 17:05, Mauro Carvalho Chehab wrote:
> Em Sat,  4 Aug 2018 14:45:22 +0200
> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> 
>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>
>> v4l2_ctrl uses mutexes, so we can't setup a ctrl_handler in
>> interrupt context. Switch to a workqueue instead and drop the timer.
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
> 
> Shouldn't this come earlier at the series (before adding request API
> support to m2m) in order to avoid regressions?

??? At this stage vim2m doesn't support the request API yet. It's the next
patch that adds that (and that's when this patch is needed for it to work).

Regards,

	Hans

> 
>> ---
>>  drivers/media/platform/vim2m.c | 25 ++++++++++---------------
>>  1 file changed, 10 insertions(+), 15 deletions(-)
>>
>> diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c
>> index 462099a141e4..6f87ef025ff1 100644
>> --- a/drivers/media/platform/vim2m.c
>> +++ b/drivers/media/platform/vim2m.c
>> @@ -3,7 +3,8 @@
>>   *
>>   * This is a virtual device driver for testing mem-to-mem videobuf framework.
>>   * It simulates a device that uses memory buffers for both source and
>> - * destination, processes the data and issues an "irq" (simulated by a timer).
>> + * destination, processes the data and issues an "irq" (simulated by a delayed
>> + * workqueue).
>>   * The device is capable of multi-instance, multi-buffer-per-transaction
>>   * operation (via the mem2mem framework).
>>   *
>> @@ -19,7 +20,6 @@
>>  #include <linux/module.h>
>>  #include <linux/delay.h>
>>  #include <linux/fs.h>
>> -#include <linux/timer.h>
>>  #include <linux/sched.h>
>>  #include <linux/slab.h>
>>  
>> @@ -148,7 +148,7 @@ struct vim2m_dev {
>>  	struct mutex		dev_mutex;
>>  	spinlock_t		irqlock;
>>  
>> -	struct timer_list	timer;
>> +	struct delayed_work	work_run;
>>  
>>  	struct v4l2_m2m_dev	*m2m_dev;
>>  };
>> @@ -336,12 +336,6 @@ static int device_process(struct vim2m_ctx *ctx,
>>  	return 0;
>>  }
>>  
>> -static void schedule_irq(struct vim2m_dev *dev, int msec_timeout)
>> -{
>> -	dprintk(dev, "Scheduling a simulated irq\n");
>> -	mod_timer(&dev->timer, jiffies + msecs_to_jiffies(msec_timeout));
>> -}
>> -
>>  /*
>>   * mem2mem callbacks
>>   */
>> @@ -387,13 +381,14 @@ static void device_run(void *priv)
>>  
>>  	device_process(ctx, src_buf, dst_buf);
>>  
>> -	/* Run a timer, which simulates a hardware irq  */
>> -	schedule_irq(dev, ctx->transtime);
>> +	/* Run delayed work, which simulates a hardware irq  */
>> +	schedule_delayed_work(&dev->work_run, msecs_to_jiffies(ctx->transtime));
>>  }
>>  
>> -static void device_isr(struct timer_list *t)
>> +static void device_work(struct work_struct *w)
>>  {
>> -	struct vim2m_dev *vim2m_dev = from_timer(vim2m_dev, t, timer);
>> +	struct vim2m_dev *vim2m_dev =
>> +		container_of(w, struct vim2m_dev, work_run.work);
>>  	struct vim2m_ctx *curr_ctx;
>>  	struct vb2_v4l2_buffer *src_vb, *dst_vb;
>>  	unsigned long flags;
>> @@ -805,6 +800,7 @@ static void vim2m_stop_streaming(struct vb2_queue *q)
>>  	struct vb2_v4l2_buffer *vbuf;
>>  	unsigned long flags;
>>  
>> +	flush_scheduled_work();
>>  	for (;;) {
>>  		if (V4L2_TYPE_IS_OUTPUT(q->type))
>>  			vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
>> @@ -1015,6 +1011,7 @@ static int vim2m_probe(struct platform_device *pdev)
>>  	vfd = &dev->vfd;
>>  	vfd->lock = &dev->dev_mutex;
>>  	vfd->v4l2_dev = &dev->v4l2_dev;
>> +	INIT_DELAYED_WORK(&dev->work_run, device_work);
>>  
>>  	ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
>>  	if (ret) {
>> @@ -1026,7 +1023,6 @@ static int vim2m_probe(struct platform_device *pdev)
>>  	v4l2_info(&dev->v4l2_dev,
>>  			"Device registered as /dev/video%d\n", vfd->num);
>>  
>> -	timer_setup(&dev->timer, device_isr, 0);
>>  	platform_set_drvdata(pdev, dev);
>>  
>>  	dev->m2m_dev = v4l2_m2m_init(&m2m_ops);
>> @@ -1083,7 +1079,6 @@ static int vim2m_remove(struct platform_device *pdev)
>>  	media_device_cleanup(&dev->mdev);
>>  #endif
>>  	v4l2_m2m_release(dev->m2m_dev);
>> -	del_timer_sync(&dev->timer);
>>  	video_unregister_device(&dev->vfd);
>>  	v4l2_device_unregister(&dev->v4l2_dev);
>>  
> 
> 
> 
> Thanks,
> Mauro
> 

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

* Re: [PATCHv17 34/34] RFC: media-requests: add debugfs node
  2018-08-13 15:15   ` Mauro Carvalho Chehab
@ 2018-08-14  7:33     ` Hans Verkuil
  2018-08-14  7:43       ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-14  7:33 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media, Hans Verkuil

On 13/08/18 17:15, Mauro Carvalho Chehab wrote:
> Em Sat,  4 Aug 2018 14:45:26 +0200
> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> 
>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>
>> Keep track of the number of requests and request objects of a media
>> device. Helps to verify that all request-related memory is freed.
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> 
> 
> 
>> ---
>>  drivers/media/media-device.c  | 41 +++++++++++++++++++++++++++++++++++
>>  drivers/media/media-devnode.c | 17 +++++++++++++++
>>  drivers/media/media-request.c |  5 +++++
>>  include/media/media-device.h  | 11 ++++++++++
>>  include/media/media-devnode.h |  4 ++++
>>  include/media/media-request.h |  2 ++
>>  6 files changed, 80 insertions(+)
>>
>> diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
>> index 4b9a8de05562..28a891b53886 100644
>> --- a/drivers/media/media-device.c
>> +++ b/drivers/media/media-device.c
>> @@ -691,6 +691,23 @@ void media_device_unregister_entity(struct media_entity *entity)
>>  }
>>  EXPORT_SYMBOL_GPL(media_device_unregister_entity);
>>  
>> +#ifdef CONFIG_DEBUG_FS
> 
> Patch itself looks good. Yet, perhaps we could request both
> CONFIG_DEBUG_FS and CONFIG_VIDEO_ADV_DEBUG.
> 
> Also, instead of ifdef, please use IS_ENABLED for DEBUG_FS. That tends
> to be safer long term.
> 
> With that:
> 
> Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>

I don't intend to merge this patch yet. I'm not happy with it: I would
really like to count the objects per-request instead of globally, and
provide some more information about requests and their objects.

It was really a quick hack so that I could verify that all requests and
objects are properly freed.

Regards,

	Hans

> 
>> +/*
>> + * Log the state of media requests.
>> + * Very useful for debugging.
>> + */
>> +static int media_device_requests(struct seq_file *file, void *priv)
>> +{
>> +	struct media_device *dev = dev_get_drvdata(file->private);
>> +
>> +	seq_printf(file, "number of requests: %d\n",
>> +		   atomic_read(&dev->num_requests));
>> +	seq_printf(file, "number of request objects: %d\n",
>> +		   atomic_read(&dev->num_request_objects));
>> +	return 0;
>> +}
>> +#endif
>> +
>>  /**
>>   * media_device_init() - initialize a media device
>>   * @mdev:	The media device
>> @@ -713,6 +730,9 @@ void media_device_init(struct media_device *mdev)
>>  	mutex_init(&mdev->graph_mutex);
>>  	ida_init(&mdev->entity_internal_idx);
>>  
>> +	atomic_set(&mdev->num_requests, 0);
>> +	atomic_set(&mdev->num_request_objects, 0);
>> +
>>  	dev_dbg(mdev->dev, "Media device initialized\n");
>>  }
>>  EXPORT_SYMBOL_GPL(media_device_init);
>> @@ -764,6 +784,26 @@ int __must_check __media_device_register(struct media_device *mdev,
>>  
>>  	dev_dbg(mdev->dev, "Media device registered\n");
>>  
>> +#ifdef CONFIG_DEBUG_FS
>> +	if (!media_top_dir)
>> +		return 0;
>> +
>> +	mdev->media_dir = debugfs_create_dir(dev_name(&devnode->dev),
>> +					     media_top_dir);
>> +	if (IS_ERR_OR_NULL(mdev->media_dir)) {
>> +		dev_warn(mdev->dev, "Failed to create debugfs dir\n");
>> +		return 0;
>> +	}
>> +	mdev->requests_file = debugfs_create_devm_seqfile(&devnode->dev,
>> +		"requests", mdev->media_dir, media_device_requests);
>> +	if (IS_ERR_OR_NULL(mdev->requests_file)) {
>> +		dev_warn(mdev->dev, "Failed to create requests file\n");
>> +		debugfs_remove_recursive(mdev->media_dir);
>> +		mdev->media_dir = NULL;
>> +		return 0;
>> +	}
>> +#endif
>> +
>>  	return 0;
>>  }
>>  EXPORT_SYMBOL_GPL(__media_device_register);
>> @@ -841,6 +881,7 @@ void media_device_unregister(struct media_device *mdev)
>>  
>>  	dev_dbg(mdev->dev, "Media device unregistered\n");
>>  
>> +	debugfs_remove_recursive(mdev->media_dir);
>>  	device_remove_file(&mdev->devnode->dev, &dev_attr_model);
>>  	media_devnode_unregister(mdev->devnode);
>>  	/* devnode free is handled in media_devnode_*() */
>> diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c
>> index 6b87a721dc49..4358ed22f208 100644
>> --- a/drivers/media/media-devnode.c
>> +++ b/drivers/media/media-devnode.c
>> @@ -53,6 +53,12 @@ static dev_t media_dev_t;
>>  static DEFINE_MUTEX(media_devnode_lock);
>>  static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES);
>>  
>> +/*
>> + * debugfs
>> + */
>> +struct dentry *media_top_dir;
>> +
>> +
>>  /* Called when the last user of the media device exits. */
>>  static void media_devnode_release(struct device *cd)
>>  {
>> @@ -259,6 +265,8 @@ int __must_check media_devnode_register(struct media_device *mdev,
>>  		goto cdev_add_error;
>>  	}
>>  
>> +	dev_set_drvdata(&devnode->dev, mdev);
>> +
>>  	/* Part 4: Activate this minor. The char device can now be used. */
>>  	set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
>>  
>> @@ -310,6 +318,14 @@ static int __init media_devnode_init(void)
>>  		return ret;
>>  	}
>>  
>> +#ifdef CONFIG_DEBUG_FS
>> +	media_top_dir = debugfs_create_dir("media", NULL);
>> +	if (IS_ERR_OR_NULL(media_top_dir)) {
>> +		pr_warn("Failed to create debugfs media dir\n");
>> +		media_top_dir = NULL;
>> +	}
>> +#endif
>> +
>>  	ret = bus_register(&media_bus_type);
>>  	if (ret < 0) {
>>  		unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
>> @@ -322,6 +338,7 @@ static int __init media_devnode_init(void)
>>  
>>  static void __exit media_devnode_exit(void)
>>  {
>> +	debugfs_remove_recursive(media_top_dir);
>>  	bus_unregister(&media_bus_type);
>>  	unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
>>  }
>> diff --git a/drivers/media/media-request.c b/drivers/media/media-request.c
>> index a5b70a4e613b..8ba97a9c4bf1 100644
>> --- a/drivers/media/media-request.c
>> +++ b/drivers/media/media-request.c
>> @@ -72,6 +72,7 @@ static void media_request_release(struct kref *kref)
>>  		mdev->ops->req_free(req);
>>  	else
>>  		kfree(req);
>> +	atomic_dec(&mdev->num_requests);
>>  }
>>  
>>  void media_request_put(struct media_request *req)
>> @@ -318,6 +319,7 @@ int media_request_alloc(struct media_device *mdev,
>>  	get_task_comm(comm, current);
>>  	snprintf(req->debug_str, sizeof(req->debug_str), "%s:%d",
>>  		 comm, fd);
>> +	atomic_inc(&mdev->num_requests);
>>  	dev_dbg(mdev->dev, "request: allocated %s\n", req->debug_str);
>>  
>>  	fd_install(fd, filp);
>> @@ -342,6 +344,7 @@ static void media_request_object_release(struct kref *kref)
>>  	if (WARN_ON(req))
>>  		media_request_object_unbind(obj);
>>  	obj->ops->release(obj);
>> +	atomic_dec(&obj->mdev->num_request_objects);
>>  }
>>  
>>  struct media_request_object *
>> @@ -405,10 +408,12 @@ int media_request_object_bind(struct media_request *req,
>>  	obj->req = req;
>>  	obj->ops = ops;
>>  	obj->priv = priv;
>> +	obj->mdev = req->mdev;
>>  
>>  	list_add_tail(&obj->list, &req->objects);
>>  	req->num_incomplete_objects++;
>>  	ret = 0;
>> +	atomic_inc(&obj->mdev->num_request_objects);
>>  
>>  unlock:
>>  	spin_unlock_irqrestore(&req->lock, flags);
>> diff --git a/include/media/media-device.h b/include/media/media-device.h
>> index 110b89567671..2de0606938d4 100644
>> --- a/include/media/media-device.h
>> +++ b/include/media/media-device.h
>> @@ -21,6 +21,7 @@
>>  
>>  #include <linux/list.h>
>>  #include <linux/mutex.h>
>> +#include <linux/atomic.h>
>>  
>>  #include <media/media-devnode.h>
>>  #include <media/media-entity.h>
>> @@ -107,6 +108,10 @@ struct media_device_ops {
>>   * @ops:	Operation handler callbacks
>>   * @req_queue_mutex: Serialise the MEDIA_REQUEST_IOC_QUEUE ioctl w.r.t.
>>   *		     other operations that stop or start streaming.
>> + * @num_requests: number of associated requests
>> + * @num_request_objects: number of associated request objects
>> + * @media_dir:	DebugFS media directory
>> + * @requests_file: DebugFS requests file
>>   *
>>   * This structure represents an abstract high-level media device. It allows easy
>>   * access to entities and provides basic media device-level support. The
>> @@ -179,6 +184,12 @@ struct media_device {
>>  	const struct media_device_ops *ops;
>>  
>>  	struct mutex req_queue_mutex;
>> +	atomic_t num_requests;
>> +	atomic_t num_request_objects;
>> +
>> +	/* debugfs */
>> +	struct dentry *media_dir;
>> +	struct dentry *requests_file;
>>  };
>>  
>>  /* We don't need to include pci.h or usb.h here */
>> diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
>> index dc2f64e1b08f..984b62b634d3 100644
>> --- a/include/media/media-devnode.h
>> +++ b/include/media/media-devnode.h
>> @@ -28,9 +28,13 @@
>>  #include <linux/fs.h>
>>  #include <linux/device.h>
>>  #include <linux/cdev.h>
>> +#include <linux/debugfs.h>
>>  
>>  struct media_device;
>>  
>> +/* DebugFS top-level media directory */
>> +extern struct dentry *media_top_dir;
>> +
>>  /*
>>   * Flag to mark the media_devnode struct as registered. Drivers must not touch
>>   * this flag directly, it will be set and cleared by media_devnode_register and
>> diff --git a/include/media/media-request.h b/include/media/media-request.h
>> index fd08d7a431a1..76727d4a89c3 100644
>> --- a/include/media/media-request.h
>> +++ b/include/media/media-request.h
>> @@ -210,6 +210,7 @@ struct media_request_object_ops {
>>   * struct media_request_object - An opaque object that belongs to a media
>>   *				 request
>>   *
>> + * @mdev: Media device this object belongs to
>>   * @ops: object's operations
>>   * @priv: object's priv pointer
>>   * @req: the request this object belongs to (can be NULL)
>> @@ -221,6 +222,7 @@ struct media_request_object_ops {
>>   * another struct that contains the actual data for this request object.
>>   */
>>  struct media_request_object {
>> +	struct media_device *mdev;
>>  	const struct media_request_object_ops *ops;
>>  	void *priv;
>>  	struct media_request *req;
> 
> 
> 
> Thanks,
> Mauro
> 

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

* Re: [PATCHv17 30/34] vim2m: use workqueue
  2018-08-14  7:28     ` Hans Verkuil
@ 2018-08-14  7:41       ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-14  7:41 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Tue, 14 Aug 2018 09:28:42 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> On 13/08/18 17:05, Mauro Carvalho Chehab wrote:
> > Em Sat,  4 Aug 2018 14:45:22 +0200
> > Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> >   
> >> From: Hans Verkuil <hans.verkuil@cisco.com>
> >>
> >> v4l2_ctrl uses mutexes, so we can't setup a ctrl_handler in
> >> interrupt context. Switch to a workqueue instead and drop the timer.
> >>
> >> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>  
> > 
> > Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
> > 
> > Shouldn't this come earlier at the series (before adding request API
> > support to m2m) in order to avoid regressions?  
> 
> ??? At this stage vim2m doesn't support the request API yet. It's the next
> patch that adds that (and that's when this patch is needed for it to work).

Ah, OK!
> 
> Regards,
> 
> 	Hans
> 
> >   
> >> ---
> >>  drivers/media/platform/vim2m.c | 25 ++++++++++---------------
> >>  1 file changed, 10 insertions(+), 15 deletions(-)
> >>
> >> diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c
> >> index 462099a141e4..6f87ef025ff1 100644
> >> --- a/drivers/media/platform/vim2m.c
> >> +++ b/drivers/media/platform/vim2m.c
> >> @@ -3,7 +3,8 @@
> >>   *
> >>   * This is a virtual device driver for testing mem-to-mem videobuf framework.
> >>   * It simulates a device that uses memory buffers for both source and
> >> - * destination, processes the data and issues an "irq" (simulated by a timer).
> >> + * destination, processes the data and issues an "irq" (simulated by a delayed
> >> + * workqueue).
> >>   * The device is capable of multi-instance, multi-buffer-per-transaction
> >>   * operation (via the mem2mem framework).
> >>   *
> >> @@ -19,7 +20,6 @@
> >>  #include <linux/module.h>
> >>  #include <linux/delay.h>
> >>  #include <linux/fs.h>
> >> -#include <linux/timer.h>
> >>  #include <linux/sched.h>
> >>  #include <linux/slab.h>
> >>  
> >> @@ -148,7 +148,7 @@ struct vim2m_dev {
> >>  	struct mutex		dev_mutex;
> >>  	spinlock_t		irqlock;
> >>  
> >> -	struct timer_list	timer;
> >> +	struct delayed_work	work_run;
> >>  
> >>  	struct v4l2_m2m_dev	*m2m_dev;
> >>  };
> >> @@ -336,12 +336,6 @@ static int device_process(struct vim2m_ctx *ctx,
> >>  	return 0;
> >>  }
> >>  
> >> -static void schedule_irq(struct vim2m_dev *dev, int msec_timeout)
> >> -{
> >> -	dprintk(dev, "Scheduling a simulated irq\n");
> >> -	mod_timer(&dev->timer, jiffies + msecs_to_jiffies(msec_timeout));
> >> -}
> >> -
> >>  /*
> >>   * mem2mem callbacks
> >>   */
> >> @@ -387,13 +381,14 @@ static void device_run(void *priv)
> >>  
> >>  	device_process(ctx, src_buf, dst_buf);
> >>  
> >> -	/* Run a timer, which simulates a hardware irq  */
> >> -	schedule_irq(dev, ctx->transtime);
> >> +	/* Run delayed work, which simulates a hardware irq  */
> >> +	schedule_delayed_work(&dev->work_run, msecs_to_jiffies(ctx->transtime));
> >>  }
> >>  
> >> -static void device_isr(struct timer_list *t)
> >> +static void device_work(struct work_struct *w)
> >>  {
> >> -	struct vim2m_dev *vim2m_dev = from_timer(vim2m_dev, t, timer);
> >> +	struct vim2m_dev *vim2m_dev =
> >> +		container_of(w, struct vim2m_dev, work_run.work);
> >>  	struct vim2m_ctx *curr_ctx;
> >>  	struct vb2_v4l2_buffer *src_vb, *dst_vb;
> >>  	unsigned long flags;
> >> @@ -805,6 +800,7 @@ static void vim2m_stop_streaming(struct vb2_queue *q)
> >>  	struct vb2_v4l2_buffer *vbuf;
> >>  	unsigned long flags;
> >>  
> >> +	flush_scheduled_work();
> >>  	for (;;) {
> >>  		if (V4L2_TYPE_IS_OUTPUT(q->type))
> >>  			vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> >> @@ -1015,6 +1011,7 @@ static int vim2m_probe(struct platform_device *pdev)
> >>  	vfd = &dev->vfd;
> >>  	vfd->lock = &dev->dev_mutex;
> >>  	vfd->v4l2_dev = &dev->v4l2_dev;
> >> +	INIT_DELAYED_WORK(&dev->work_run, device_work);
> >>  
> >>  	ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
> >>  	if (ret) {
> >> @@ -1026,7 +1023,6 @@ static int vim2m_probe(struct platform_device *pdev)
> >>  	v4l2_info(&dev->v4l2_dev,
> >>  			"Device registered as /dev/video%d\n", vfd->num);
> >>  
> >> -	timer_setup(&dev->timer, device_isr, 0);
> >>  	platform_set_drvdata(pdev, dev);
> >>  
> >>  	dev->m2m_dev = v4l2_m2m_init(&m2m_ops);
> >> @@ -1083,7 +1079,6 @@ static int vim2m_remove(struct platform_device *pdev)
> >>  	media_device_cleanup(&dev->mdev);
> >>  #endif
> >>  	v4l2_m2m_release(dev->m2m_dev);
> >> -	del_timer_sync(&dev->timer);
> >>  	video_unregister_device(&dev->vfd);
> >>  	v4l2_device_unregister(&dev->v4l2_dev);
> >>    
> > 
> > 
> > 
> > Thanks,
> > Mauro
> >   
> 



Thanks,
Mauro

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

* Re: [PATCHv17 34/34] RFC: media-requests: add debugfs node
  2018-08-14  7:33     ` Hans Verkuil
@ 2018-08-14  7:43       ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-14  7:43 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Tue, 14 Aug 2018 09:33:16 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> On 13/08/18 17:15, Mauro Carvalho Chehab wrote:
> > Em Sat,  4 Aug 2018 14:45:26 +0200
> > Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> >   
> >> From: Hans Verkuil <hans.verkuil@cisco.com>
> >>
> >> Keep track of the number of requests and request objects of a media
> >> device. Helps to verify that all request-related memory is freed.

Ok. So, let's skip it on your next patchset. Debugfs can
be added any time after the requests API main patchset gets merged.

> >>
> >> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>  
> > 
> > 
> >   
> >> ---
> >>  drivers/media/media-device.c  | 41 +++++++++++++++++++++++++++++++++++
> >>  drivers/media/media-devnode.c | 17 +++++++++++++++
> >>  drivers/media/media-request.c |  5 +++++
> >>  include/media/media-device.h  | 11 ++++++++++
> >>  include/media/media-devnode.h |  4 ++++
> >>  include/media/media-request.h |  2 ++
> >>  6 files changed, 80 insertions(+)
> >>
> >> diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
> >> index 4b9a8de05562..28a891b53886 100644
> >> --- a/drivers/media/media-device.c
> >> +++ b/drivers/media/media-device.c
> >> @@ -691,6 +691,23 @@ void media_device_unregister_entity(struct media_entity *entity)
> >>  }
> >>  EXPORT_SYMBOL_GPL(media_device_unregister_entity);
> >>  
> >> +#ifdef CONFIG_DEBUG_FS  
> > 
> > Patch itself looks good. Yet, perhaps we could request both
> > CONFIG_DEBUG_FS and CONFIG_VIDEO_ADV_DEBUG.
> > 
> > Also, instead of ifdef, please use IS_ENABLED for DEBUG_FS. That tends
> > to be safer long term.
> > 
> > With that:
> > 
> > Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>  
> 
> I don't intend to merge this patch yet. I'm not happy with it: I would
> really like to count the objects per-request instead of globally, and
> provide some more information about requests and their objects.
> 
> It was really a quick hack so that I could verify that all requests and
> objects are properly freed.
> 
> Regards,
> 
> 	Hans
> 
> >   
> >> +/*
> >> + * Log the state of media requests.
> >> + * Very useful for debugging.
> >> + */
> >> +static int media_device_requests(struct seq_file *file, void *priv)
> >> +{
> >> +	struct media_device *dev = dev_get_drvdata(file->private);
> >> +
> >> +	seq_printf(file, "number of requests: %d\n",
> >> +		   atomic_read(&dev->num_requests));
> >> +	seq_printf(file, "number of request objects: %d\n",
> >> +		   atomic_read(&dev->num_request_objects));
> >> +	return 0;
> >> +}
> >> +#endif
> >> +
> >>  /**
> >>   * media_device_init() - initialize a media device
> >>   * @mdev:	The media device
> >> @@ -713,6 +730,9 @@ void media_device_init(struct media_device *mdev)
> >>  	mutex_init(&mdev->graph_mutex);
> >>  	ida_init(&mdev->entity_internal_idx);
> >>  
> >> +	atomic_set(&mdev->num_requests, 0);
> >> +	atomic_set(&mdev->num_request_objects, 0);
> >> +
> >>  	dev_dbg(mdev->dev, "Media device initialized\n");
> >>  }
> >>  EXPORT_SYMBOL_GPL(media_device_init);
> >> @@ -764,6 +784,26 @@ int __must_check __media_device_register(struct media_device *mdev,
> >>  
> >>  	dev_dbg(mdev->dev, "Media device registered\n");
> >>  
> >> +#ifdef CONFIG_DEBUG_FS
> >> +	if (!media_top_dir)
> >> +		return 0;
> >> +
> >> +	mdev->media_dir = debugfs_create_dir(dev_name(&devnode->dev),
> >> +					     media_top_dir);
> >> +	if (IS_ERR_OR_NULL(mdev->media_dir)) {
> >> +		dev_warn(mdev->dev, "Failed to create debugfs dir\n");
> >> +		return 0;
> >> +	}
> >> +	mdev->requests_file = debugfs_create_devm_seqfile(&devnode->dev,
> >> +		"requests", mdev->media_dir, media_device_requests);
> >> +	if (IS_ERR_OR_NULL(mdev->requests_file)) {
> >> +		dev_warn(mdev->dev, "Failed to create requests file\n");
> >> +		debugfs_remove_recursive(mdev->media_dir);
> >> +		mdev->media_dir = NULL;
> >> +		return 0;
> >> +	}
> >> +#endif
> >> +
> >>  	return 0;
> >>  }
> >>  EXPORT_SYMBOL_GPL(__media_device_register);
> >> @@ -841,6 +881,7 @@ void media_device_unregister(struct media_device *mdev)
> >>  
> >>  	dev_dbg(mdev->dev, "Media device unregistered\n");
> >>  
> >> +	debugfs_remove_recursive(mdev->media_dir);
> >>  	device_remove_file(&mdev->devnode->dev, &dev_attr_model);
> >>  	media_devnode_unregister(mdev->devnode);
> >>  	/* devnode free is handled in media_devnode_*() */
> >> diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c
> >> index 6b87a721dc49..4358ed22f208 100644
> >> --- a/drivers/media/media-devnode.c
> >> +++ b/drivers/media/media-devnode.c
> >> @@ -53,6 +53,12 @@ static dev_t media_dev_t;
> >>  static DEFINE_MUTEX(media_devnode_lock);
> >>  static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES);
> >>  
> >> +/*
> >> + * debugfs
> >> + */
> >> +struct dentry *media_top_dir;
> >> +
> >> +
> >>  /* Called when the last user of the media device exits. */
> >>  static void media_devnode_release(struct device *cd)
> >>  {
> >> @@ -259,6 +265,8 @@ int __must_check media_devnode_register(struct media_device *mdev,
> >>  		goto cdev_add_error;
> >>  	}
> >>  
> >> +	dev_set_drvdata(&devnode->dev, mdev);
> >> +
> >>  	/* Part 4: Activate this minor. The char device can now be used. */
> >>  	set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
> >>  
> >> @@ -310,6 +318,14 @@ static int __init media_devnode_init(void)
> >>  		return ret;
> >>  	}
> >>  
> >> +#ifdef CONFIG_DEBUG_FS
> >> +	media_top_dir = debugfs_create_dir("media", NULL);
> >> +	if (IS_ERR_OR_NULL(media_top_dir)) {
> >> +		pr_warn("Failed to create debugfs media dir\n");
> >> +		media_top_dir = NULL;
> >> +	}
> >> +#endif
> >> +
> >>  	ret = bus_register(&media_bus_type);
> >>  	if (ret < 0) {
> >>  		unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
> >> @@ -322,6 +338,7 @@ static int __init media_devnode_init(void)
> >>  
> >>  static void __exit media_devnode_exit(void)
> >>  {
> >> +	debugfs_remove_recursive(media_top_dir);
> >>  	bus_unregister(&media_bus_type);
> >>  	unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
> >>  }
> >> diff --git a/drivers/media/media-request.c b/drivers/media/media-request.c
> >> index a5b70a4e613b..8ba97a9c4bf1 100644
> >> --- a/drivers/media/media-request.c
> >> +++ b/drivers/media/media-request.c
> >> @@ -72,6 +72,7 @@ static void media_request_release(struct kref *kref)
> >>  		mdev->ops->req_free(req);
> >>  	else
> >>  		kfree(req);
> >> +	atomic_dec(&mdev->num_requests);
> >>  }
> >>  
> >>  void media_request_put(struct media_request *req)
> >> @@ -318,6 +319,7 @@ int media_request_alloc(struct media_device *mdev,
> >>  	get_task_comm(comm, current);
> >>  	snprintf(req->debug_str, sizeof(req->debug_str), "%s:%d",
> >>  		 comm, fd);
> >> +	atomic_inc(&mdev->num_requests);
> >>  	dev_dbg(mdev->dev, "request: allocated %s\n", req->debug_str);
> >>  
> >>  	fd_install(fd, filp);
> >> @@ -342,6 +344,7 @@ static void media_request_object_release(struct kref *kref)
> >>  	if (WARN_ON(req))
> >>  		media_request_object_unbind(obj);
> >>  	obj->ops->release(obj);
> >> +	atomic_dec(&obj->mdev->num_request_objects);
> >>  }
> >>  
> >>  struct media_request_object *
> >> @@ -405,10 +408,12 @@ int media_request_object_bind(struct media_request *req,
> >>  	obj->req = req;
> >>  	obj->ops = ops;
> >>  	obj->priv = priv;
> >> +	obj->mdev = req->mdev;
> >>  
> >>  	list_add_tail(&obj->list, &req->objects);
> >>  	req->num_incomplete_objects++;
> >>  	ret = 0;
> >> +	atomic_inc(&obj->mdev->num_request_objects);
> >>  
> >>  unlock:
> >>  	spin_unlock_irqrestore(&req->lock, flags);
> >> diff --git a/include/media/media-device.h b/include/media/media-device.h
> >> index 110b89567671..2de0606938d4 100644
> >> --- a/include/media/media-device.h
> >> +++ b/include/media/media-device.h
> >> @@ -21,6 +21,7 @@
> >>  
> >>  #include <linux/list.h>
> >>  #include <linux/mutex.h>
> >> +#include <linux/atomic.h>
> >>  
> >>  #include <media/media-devnode.h>
> >>  #include <media/media-entity.h>
> >> @@ -107,6 +108,10 @@ struct media_device_ops {
> >>   * @ops:	Operation handler callbacks
> >>   * @req_queue_mutex: Serialise the MEDIA_REQUEST_IOC_QUEUE ioctl w.r.t.
> >>   *		     other operations that stop or start streaming.
> >> + * @num_requests: number of associated requests
> >> + * @num_request_objects: number of associated request objects
> >> + * @media_dir:	DebugFS media directory
> >> + * @requests_file: DebugFS requests file
> >>   *
> >>   * This structure represents an abstract high-level media device. It allows easy
> >>   * access to entities and provides basic media device-level support. The
> >> @@ -179,6 +184,12 @@ struct media_device {
> >>  	const struct media_device_ops *ops;
> >>  
> >>  	struct mutex req_queue_mutex;
> >> +	atomic_t num_requests;
> >> +	atomic_t num_request_objects;
> >> +
> >> +	/* debugfs */
> >> +	struct dentry *media_dir;
> >> +	struct dentry *requests_file;
> >>  };
> >>  
> >>  /* We don't need to include pci.h or usb.h here */
> >> diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
> >> index dc2f64e1b08f..984b62b634d3 100644
> >> --- a/include/media/media-devnode.h
> >> +++ b/include/media/media-devnode.h
> >> @@ -28,9 +28,13 @@
> >>  #include <linux/fs.h>
> >>  #include <linux/device.h>
> >>  #include <linux/cdev.h>
> >> +#include <linux/debugfs.h>
> >>  
> >>  struct media_device;
> >>  
> >> +/* DebugFS top-level media directory */
> >> +extern struct dentry *media_top_dir;
> >> +
> >>  /*
> >>   * Flag to mark the media_devnode struct as registered. Drivers must not touch
> >>   * this flag directly, it will be set and cleared by media_devnode_register and
> >> diff --git a/include/media/media-request.h b/include/media/media-request.h
> >> index fd08d7a431a1..76727d4a89c3 100644
> >> --- a/include/media/media-request.h
> >> +++ b/include/media/media-request.h
> >> @@ -210,6 +210,7 @@ struct media_request_object_ops {
> >>   * struct media_request_object - An opaque object that belongs to a media
> >>   *				 request
> >>   *
> >> + * @mdev: Media device this object belongs to
> >>   * @ops: object's operations
> >>   * @priv: object's priv pointer
> >>   * @req: the request this object belongs to (can be NULL)
> >> @@ -221,6 +222,7 @@ struct media_request_object_ops {
> >>   * another struct that contains the actual data for this request object.
> >>   */
> >>  struct media_request_object {
> >> +	struct media_device *mdev;
> >>  	const struct media_request_object_ops *ops;
> >>  	void *priv;
> >>  	struct media_request *req;  
> > 
> > 
> > 
> > Thanks,
> > Mauro
> >   
> 



Thanks,
Mauro

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

* Re: [PATCHv17 26/34] videobuf2-v4l2: add vb2_request_queue/validate helpers
  2018-08-14  7:19     ` Hans Verkuil
@ 2018-08-14  7:49       ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-14  7:49 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Tue, 14 Aug 2018 09:19:08 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> On 13/08/18 16:53, Mauro Carvalho Chehab wrote:
> > Em Sat,  4 Aug 2018 14:45:18 +0200
> > Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> >   
> >> From: Hans Verkuil <hans.verkuil@cisco.com>
> >>
> >> The generic vb2_request_validate helper function checks if
> >> there are buffers in the request and if so, prepares (validates)
> >> all objects in the request.
> >>
> >> The generic vb2_request_queue helper function queues all buffer
> >> objects in the validated request.
> >>
> >> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> >> ---
> >>  .../media/common/videobuf2/videobuf2-v4l2.c   | 44 +++++++++++++++++++
> >>  include/media/videobuf2-v4l2.h                |  4 ++
> >>  2 files changed, 48 insertions(+)
> >>
> >> diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c
> >> index 9c652afa62ab..88d8f60c742b 100644
> >> --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
> >> +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
> >> @@ -1100,6 +1100,50 @@ void vb2_ops_wait_finish(struct vb2_queue *vq)
> >>  }
> >>  EXPORT_SYMBOL_GPL(vb2_ops_wait_finish);
> >>  
> >> +int vb2_request_validate(struct media_request *req)
> >> +{
> >> +	struct media_request_object *obj;
> >> +	int ret = 0;
> >> +
> >> +	if (!vb2_request_has_buffers(req))
> >> +		return -ENOENT;  
> > 
> > This holds the spinlock...  
> 
> The spinlock in vb2_request_has_buffers() is not needed if vb2_request_has_buffers()
> is called from this validate function. While validating no new objects can be
> added to the request, and since nothing has been queued yet objects cannot be
> deleted either. That's true for this whole vb2_request_validate() function.
> 
> But should vb2_request_has_buffers() ever be called from a non-validate context,
> then the spinlock would be needed.
> 
> I want to keep the spinlock in that function for now as it is more robust.

Now your comment to patch 25/34 makes sense, but please document it, as
it is confusing for anyone reviewing the code.

> 
> Regards,
> 
> 	Hans
> 
> >   
> >> +
> >> +	list_for_each_entry(obj, &req->objects, list) {
> >> +		if (!obj->ops->prepare)
> >> +			continue;
> >> +
> >> +		ret = obj->ops->prepare(obj);
> >> +		if (ret)
> >> +			break;
> >> +	}
> >> +  
> > 
> > Shouldn't this logic hold it too?
> >   
> >> +	if (ret) {
> >> +		list_for_each_entry_continue_reverse(obj, &req->objects, list)
> >> +			if (obj->ops->unprepare)
> >> +				obj->ops->unprepare(obj);
> >> +		return ret;
> >> +	}
> >> +	return 0;
> >> +}
> >> +EXPORT_SYMBOL_GPL(vb2_request_validate);
> >> +
> >> +void vb2_request_queue(struct media_request *req)
> >> +{
> >> +	struct media_request_object *obj, *obj_safe;
> >> +
> >> +	/* Queue all non-buffer objects */
> >> +	list_for_each_entry_safe(obj, obj_safe, &req->objects, list)
> >> +		if (obj->ops->queue && !vb2_request_object_is_buffer(obj))
> >> +			obj->ops->queue(obj);
> >> +
> >> +	/* Queue all buffer objects */
> >> +	list_for_each_entry_safe(obj, obj_safe, &req->objects, list) {
> >> +		if (obj->ops->queue && vb2_request_object_is_buffer(obj))
> >> +			obj->ops->queue(obj);
> >> +	}
> >> +}
> >> +EXPORT_SYMBOL_GPL(vb2_request_queue);
> >> +
> >>  MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2");
> >>  MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>, Marek Szyprowski");
> >>  MODULE_LICENSE("GPL");
> >> diff --git a/include/media/videobuf2-v4l2.h b/include/media/videobuf2-v4l2.h
> >> index 91a2b3e1a642..727855463838 100644
> >> --- a/include/media/videobuf2-v4l2.h
> >> +++ b/include/media/videobuf2-v4l2.h
> >> @@ -303,4 +303,8 @@ void vb2_ops_wait_prepare(struct vb2_queue *vq);
> >>   */
> >>  void vb2_ops_wait_finish(struct vb2_queue *vq);
> >>  
> >> +struct media_request;
> >> +int vb2_request_validate(struct media_request *req);
> >> +void vb2_request_queue(struct media_request *req);
> >> +
> >>  #endif /* _MEDIA_VIDEOBUF2_V4L2_H */  
> > 
> > 
> > 
> > Thanks,
> > Mauro
> >   
> 



Thanks,
Mauro

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

* Re: [PATCHv17 01/34] Documentation: v4l: document request API
  2018-08-09 17:43   ` Mauro Carvalho Chehab
  2018-08-10  7:20     ` Hans Verkuil
@ 2018-08-14  7:57     ` Hans Verkuil
  2018-08-14  8:48       ` Mauro Carvalho Chehab
  1 sibling, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-14  7:57 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media, Alexandre Courbot, Hans Verkuil

On 09/08/18 19:43, Mauro Carvalho Chehab wrote:
>> diff --git a/Documentation/media/uapi/v4l/vidioc-qbuf.rst b/Documentation/media/uapi/v4l/vidioc-qbuf.rst
>> index 9e448a4aa3aa..0e415f2551b2 100644
>> --- a/Documentation/media/uapi/v4l/vidioc-qbuf.rst
>> +++ b/Documentation/media/uapi/v4l/vidioc-qbuf.rst
>> @@ -65,7 +65,7 @@ To enqueue a :ref:`memory mapped <mmap>` buffer applications set the
>>  with a pointer to this structure the driver sets the
>>  ``V4L2_BUF_FLAG_MAPPED`` and ``V4L2_BUF_FLAG_QUEUED`` flags and clears
>>  the ``V4L2_BUF_FLAG_DONE`` flag in the ``flags`` field, or it returns an
>> -EINVAL error code.
>> +``EINVAL`` error code.
> 
> Side note: we should likely do a similar replacement on all other places
> inside the media uAPI docs.
> 
>>  
>>  To enqueue a :ref:`user pointer <userp>` buffer applications set the
>>  ``memory`` field to ``V4L2_MEMORY_USERPTR``, the ``m.userptr`` field to
>> @@ -98,6 +98,25 @@ dequeued, until the :ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` or
>>  :ref:`VIDIOC_REQBUFS` ioctl is called, or until the
>>  device is closed.
>>  
>> +The ``request_fd`` field can be used with the ``VIDIOC_QBUF`` ioctl to specify
> 
> Please prefer using :ref: for QBUF too, e. g.: 
> 	:ref:`ioctl VIDIOC_QBUF <VIDIOC_QBUF>`

Does this make sense when you are in the QBUF documentation itself? Using :ref: will
just link back to the same page.

We need some guidelines here. I personally don't think this makes sense.

Regards,

	Hans

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

* Re: [PATCHv17 08/34] v4l2-dev: lock req_queue_mutex
  2018-08-10  7:39     ` Hans Verkuil
@ 2018-08-14  8:09       ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-14  8:09 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil, Sakari Ailus

Em Fri, 10 Aug 2018 09:39:20 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> On 08/09/2018 10:03 PM, Mauro Carvalho Chehab wrote:
> > Em Sat,  4 Aug 2018 14:45:00 +0200
> > Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> >   
> >> From: Hans Verkuil <hans.verkuil@cisco.com>
> >>
> >> We need to serialize streamon/off with queueing new requests.
> >> These ioctls may trigger the cancellation of a streaming
> >> operation, and that should not be mixed with queuing a new
> >> request at the same time.
> >>
> >> Finally close() needs this lock since that too can trigger the
> >> cancellation of a streaming operation.
> >>
> >> We take the req_queue_mutex here before any other locks since
> >> it is a very high-level lock.
> >>
> >> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> >> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> >> ---
> >>  drivers/media/v4l2-core/v4l2-dev.c   | 13 +++++++++++++
> >>  drivers/media/v4l2-core/v4l2-ioctl.c | 22 +++++++++++++++++++++-
> >>  2 files changed, 34 insertions(+), 1 deletion(-)
> >>
> >> diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
> >> index 69e775930fc4..53018e4a4c78 100644
> >> --- a/drivers/media/v4l2-core/v4l2-dev.c
> >> +++ b/drivers/media/v4l2-core/v4l2-dev.c
> >> @@ -444,8 +444,21 @@ static int v4l2_release(struct inode *inode, struct file *filp)
> >>  	struct video_device *vdev = video_devdata(filp);
> >>  	int ret = 0;
> >>  
> >> +	/*
> >> +	 * We need to serialize the release() with queueing new requests.
> >> +	 * The release() may trigger the cancellation of a streaming
> >> +	 * operation, and that should not be mixed with queueing a new
> >> +	 * request at the same time.
> >> +	 */
> >> +	if (v4l2_device_supports_requests(vdev->v4l2_dev))
> >> +		mutex_lock(&vdev->v4l2_dev->mdev->req_queue_mutex);
> >> +
> >>  	if (vdev->fops->release)
> >>  		ret = vdev->fops->release(filp);
> >> +
> >> +	if (v4l2_device_supports_requests(vdev->v4l2_dev))
> >> +		mutex_unlock(&vdev->v4l2_dev->mdev->req_queue_mutex);
> >> +  
> > 
> > This will very likely generate sparse warnings. See my discussions
> > with that regards with Linus.
> > 
> > The only way to avoid it would be to do something like:
> > 
> > 	if (v4l2_device_supports_requests(vdev->v4l2_dev)) {
> > 		mutex_lock(&vdev->v4l2_dev->mdev->req_queue_mutex);
> > 	 	if (vdev->fops->release)
> > 			ret = vdev->fops->release(filp);
> > 		mutex_unlock(&vdev->v4l2_dev->mdev->req_queue_mutex);
> > 	} else {
> > 	 	if (vdev->fops->release)
> > 			ret = vdev->fops->release(filp);
> > 	}  
> 
> I'll check what sparse says and make this change if needed (I hate
> working around sparse warnings).

For reference, see the discussions I had with Linus and Christopher
about this at sparse ML:
	https://www.spinics.net/lists/linux-sparse/msg08069.html

In particular, see this:
	https://www.spinics.net/lists/linux-sparse/msg08071.html
	

(I thought I had c/c media, but it seems that only sparse ML was
c/c).

> 
> >   
> >>  	if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
> >>  		dprintk("%s: release\n",
> >>  			video_device_node_name(vdev));
> >> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
> >> index 54afc9c7ee6e..ea475d833dd6 100644
> >> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> >> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> >> @@ -2780,6 +2780,7 @@ static long __video_do_ioctl(struct file *file,
> >>  		unsigned int cmd, void *arg)
> >>  {
> >>  	struct video_device *vfd = video_devdata(file);
> >> +	struct mutex *req_queue_lock = NULL;
> >>  	struct mutex *lock; /* ioctl serialization mutex */
> >>  	const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
> >>  	bool write_only = false;
> >> @@ -2799,10 +2800,27 @@ static long __video_do_ioctl(struct file *file,
> >>  	if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags))
> >>  		vfh = file->private_data;
> >>  
> >> +	/*
> >> +	 * We need to serialize streamon/off with queueing new requests.
> >> +	 * These ioctls may trigger the cancellation of a streaming
> >> +	 * operation, and that should not be mixed with queueing a new
> >> +	 * request at the same time.
> >> +	 */
> >> +	if (v4l2_device_supports_requests(vfd->v4l2_dev) &&
> >> +	    (cmd == VIDIOC_STREAMON || cmd == VIDIOC_STREAMOFF)) {
> >> +		req_queue_lock = &vfd->v4l2_dev->mdev->req_queue_mutex;
> >> +
> >> +		if (mutex_lock_interruptible(req_queue_lock))
> >> +			return -ERESTARTSYS;
> >> +	}
> >> +
> >>  	lock = v4l2_ioctl_get_lock(vfd, vfh, cmd, arg);
> >>  
> >> -	if (lock && mutex_lock_interruptible(lock))
> >> +	if (lock && mutex_lock_interruptible(lock)) {
> >> +		if (req_queue_lock)
> >> +			mutex_unlock(req_queue_lock);
> >>  		return -ERESTARTSYS;
> >> +	}  
> > 
> > Same applies here.  
> 
> I'm not sure there is much that can be done here without making the
> code hard to read. I'll see.

You can have a lock-free routine called by another one with
would set the lock if req_queue_lock.

We ended by doing that at the ddbridge driver.

> 
> >   
> >>  
> >>  	if (!video_is_registered(vfd)) {
> >>  		ret = -ENODEV;
> >> @@ -2861,6 +2879,8 @@ static long __video_do_ioctl(struct file *file,
> >>  unlock:
> >>  	if (lock)
> >>  		mutex_unlock(lock);
> >> +	if (req_queue_lock)
> >> +		mutex_unlock(req_queue_lock);  
> > 
> > This code looks really weird! are you locking in order to get a
> > lock pointer?
> > 
> > That seems broken by design.  
> 
> I've no idea what you mean. Both 'lock' and 'req_queue_lock' are pointers to
> a struct mutex. If NULL, don't unlock, otherwise you need to unlock the mutex
> here since it was locked earlier.
> 
> Did you misread this or should the lock/req_queue_lock names be changed to e.g.
> mutex_ptr/req_queue_mutex_ptr?

Yeah, I misread it. Sorry for the noise.

IMO, it is worth to mention at the req_queue_lock field documentation that
all ioctls calls will hold the req_queue_lock.



> 
> Regards,
> 
> 	Hans
> 
> >   
> >>  	return ret;
> >>  }
> >>    
> > 
> > 
> > 
> > Thanks,
> > Mauro
> >   
> 



Thanks,
Mauro

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

* Re: [PATCHv17 01/34] Documentation: v4l: document request API
  2018-08-10  7:20     ` Hans Verkuil
@ 2018-08-14  8:21       ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-14  8:21 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Alexandre Courbot, Hans Verkuil

Em Fri, 10 Aug 2018 09:20:48 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> On 08/09/2018 07:43 PM, Mauro Carvalho Chehab wrote:
> > Em Sat,  4 Aug 2018 14:44:53 +0200
> > Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> >   
> >> From: Alexandre Courbot <acourbot@chromium.org>
> >>
> >> Document the request API for V4L2 devices, and amend the documentation
> >> of system calls influenced by it.  
> > 
> > It follows some comments. Most are nitpicks. There are just two ones
> > that aren't:
> > 	- a problem at the tables changes on Documentation/
> > 	- a question with regards to MEDIA_IOC_REQUEST_ALLOC ioctl.  
> 
> I'll fix all the smaller comments and in this reply only address these
> two topics.
> 
> > 
> > I'll keep reviewing this patch series.
> > 
> > PS.: I lost entirely my first review to this doc... I hope I didn't
> > forget anything when re-typing my comments.
> >   
> >>
> >> Signed-off-by: Alexandre Courbot <acourbot@chromium.org>
> >> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> >> ---
> >>  .../media/uapi/mediactl/media-controller.rst  |   1 +
> >>  .../media/uapi/mediactl/media-funcs.rst       |   6 +
> >>  .../uapi/mediactl/media-ioc-request-alloc.rst |  78 ++++++
> >>  .../uapi/mediactl/media-request-ioc-queue.rst |  82 ++++++
> >>  .../mediactl/media-request-ioc-reinit.rst     |  51 ++++
> >>  .../media/uapi/mediactl/request-api.rst       | 247 ++++++++++++++++++
> >>  .../uapi/mediactl/request-func-close.rst      |  49 ++++
> >>  .../uapi/mediactl/request-func-ioctl.rst      |  68 +++++
> >>  .../media/uapi/mediactl/request-func-poll.rst |  77 ++++++
> >>  Documentation/media/uapi/v4l/buffer.rst       |  21 +-
> >>  .../media/uapi/v4l/vidioc-g-ext-ctrls.rst     |  94 ++++---
> >>  Documentation/media/uapi/v4l/vidioc-qbuf.rst  |  32 ++-
> >>  .../media/videodev2.h.rst.exceptions          |   1 +
> >>  13 files changed, 771 insertions(+), 36 deletions(-)
> >>  create mode 100644 Documentation/media/uapi/mediactl/media-ioc-request-alloc.rst
> >>  create mode 100644 Documentation/media/uapi/mediactl/media-request-ioc-queue.rst
> >>  create mode 100644 Documentation/media/uapi/mediactl/media-request-ioc-reinit.rst
> >>  create mode 100644 Documentation/media/uapi/mediactl/request-api.rst
> >>  create mode 100644 Documentation/media/uapi/mediactl/request-func-close.rst
> >>  create mode 100644 Documentation/media/uapi/mediactl/request-func-ioctl.rst
> >>  create mode 100644 Documentation/media/uapi/mediactl/request-func-poll.rst
> >>  
> 
> <snip>
> 
> >> +.. c:type:: media_request_alloc
> >> +
> >> +.. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.7cm}|
> >> +
> >> +.. flat-table:: struct media_request_alloc
> >> +    :header-rows:  0
> >> +    :stub-columns: 0
> >> +    :widths:       1 1 2
> >> +
> >> +    *  -  __s32
> >> +       -  ``fd``
> >> +       -  The file descriptor of the request.  
> > 
> > It should be mentioned that the struct should be zeroed before calling
> > the Kernel, but I is overkill to have a struct to pass just one value.
> > 
> > I mean, if this has just one value inside the struct, it is a way better
> > to declare it as:
> > 
> > .. c:function:: int ioctl( int fd, MEDIA_IOC_REQUEST_ALLOC, s32 &argp )
> > 
> > Even if we later need more stuff, the size of a new MEDIA_IOC_REQUEST_ALLOC
> > will be bigger, and then (and only then) we can add extra stuff.
> > 
> > Or are you foreseen any new fields there in short term?  
> 
> The first version just had a s32 argument, not a struct. The main reason for
> going back to a struct was indeed to make it easier to add new fields in the
> future. I don't foresee any, but then, you never do.
> 
> I don't have a particularly strong opinion on this one way or another, but
> if we change it back to a s32 argument, then I want the opinion of others as
> well.

I'll comment it on patch 02/34.

> 
> <snip>
> 
> >> @@ -110,15 +130,13 @@ still cause this situation.
> >>  .. flat-table:: struct v4l2_ext_control
> >>      :header-rows:  0
> >>      :stub-columns: 0
> >> -    :widths:       1 1 1 2
> >> +    :widths:       1 1 3  
> > 
> > This is wrong: you can't change widths without changing the preceeding
> > .. tabularcolumns tag.
> > 
> > You probably didn't test PDF generation for this table.
> > 
> > Also, the change is this table doesn't belong to this patch. It is
> > a (doubtful) optimization at the table, not related to requests API.
> >   
> >>  
> >>      * - __u32
> >>        - ``id``
> >> -      -
> >>        - Identifies the control, set by the application.
> >>      * - __u32
> >>        - ``size``
> >> -      -
> >>        - The total size in bytes of the payload of this control. This is
> >>  	normally 0, but for pointer controls this should be set to the
> >>  	size of the memory containing the payload, or that will receive
> >> @@ -135,51 +153,43 @@ still cause this situation.
> >>  	   *length* of the string may well be much smaller.
> >>      * - __u32
> >>        - ``reserved2``\ [1]
> >> -      -
> >>        - Reserved for future extensions. Drivers and applications must set
> >>  	the array to zero.
> >> -    * - union
> >> +    * - union {  
> > 
> > Adding { and } at the end sounds ok...
> >   
> >>        - (anonymous)
> >> -    * -
> >> -      - __s32
> >> +    * - __s32
> >>        - ``value``
> >>        - New value or current value. Valid if this control is not of type
> >>  	``V4L2_CTRL_TYPE_INTEGER64`` and ``V4L2_CTRL_FLAG_HAS_PAYLOAD`` is
> >>  	not set.
> >> -    * -
> >> -      - __s64
> >> +    * - __s64
> >>        - ``value64``
> >>        - New value or current value. Valid if this control is of type
> >>  	``V4L2_CTRL_TYPE_INTEGER64`` and ``V4L2_CTRL_FLAG_HAS_PAYLOAD`` is
> >>  	not set.
> >> -    * -
> >> -      - char *
> >> +    * - char *
> >>        - ``string``
> >>        - A pointer to a string. Valid if this control is of type
> >>  	``V4L2_CTRL_TYPE_STRING``.
> >> -    * -
> >> -      - __u8 *
> >> +    * - __u8 *
> >>        - ``p_u8``
> >>        - A pointer to a matrix control of unsigned 8-bit values. Valid if
> >>  	this control is of type ``V4L2_CTRL_TYPE_U8``.
> >> -    * -
> >> -      - __u16 *
> >> +    * - __u16 *
> >>        - ``p_u16``
> >>        - A pointer to a matrix control of unsigned 16-bit values. Valid if
> >>  	this control is of type ``V4L2_CTRL_TYPE_U16``.
> >> -    * -
> >> -      - __u32 *
> >> +    * - __u32 *
> >>        - ``p_u32``
> >>        - A pointer to a matrix control of unsigned 32-bit values. Valid if
> >>  	this control is of type ``V4L2_CTRL_TYPE_U32``.
> >> -    * -
> >> -      - void *
> >> +    * - void *
> >>        - ``ptr``
> >>        - A pointer to a compound type which can be an N-dimensional array
> >>  	and/or a compound type (the control's type is >=
> >>  	``V4L2_CTRL_COMPOUND_TYPES``). Valid if
> >>  	``V4L2_CTRL_FLAG_HAS_PAYLOAD`` is set for this control.
> >> -
> >> +    * - }  
> > 
> > ... however, removing the extra cell is not, because it will break the
> > indent inside the union's elements. The best way to keep them indented
> > is to use a separate cell (at least on DocBook). 
> > 
> > You could come with some other solution that would add a fixed amount
> > of spaces for all the elements inside the union, but I guess the
> > easiest way to do that is by having a separate column.  
> 
> The problem is that that extra cell makes the table hard to read: the last column
> with the actual description gets squashed leading to narrow hard to read columns.
> 
> The only reason for doing this is a stupid union.
> 
> I'll experiment a bit more with this.

We used this since DocBook for all tables with unions. The conversion
kept this. I remember I did some experiments on that time trying to
do it on some other way, but didn't spend too much time on seeking
for an alternative, as this was not the top priority at the conversion.

I don't doubt that some other way to indent unions (and nested structs)
would be possible.

Anyway, this change is unrelated to Request API itself. For this patch
series, please keep the extra cell. Just touch what's required for the
request API itself.

Feel free to experiment some other options and submit a separate
patch series, if you find a better way. In this case, please double 
check the docs, as I guess there were a lot more tables using this 
trick to indent unions and nested structs.

> 
> >   
> >>  
> >>  .. tabularcolumns:: |p{4.0cm}|p{2.2cm}|p{2.1cm}|p{8.2cm}|
> >>  
> >> @@ -190,12 +200,11 @@ still cause this situation.
> >>  .. flat-table:: struct v4l2_ext_controls
> >>      :header-rows:  0
> >>      :stub-columns: 0
> >> -    :widths:       1 1 2 1
> >> +    :widths:       1 1 3  
> > 
> > Same comments I made for the past table apply here as well.  
> 
> Regards,
> 
> 	Hans



Thanks,
Mauro

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

* Re: [PATCHv17 14/34] v4l2-ctrls: add core request support
  2018-08-13 10:55   ` Mauro Carvalho Chehab
@ 2018-08-14  8:34     ` Hans Verkuil
  2018-08-14  8:59       ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-14  8:34 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media, Hans Verkuil

On 13/08/18 12:55, Mauro Carvalho Chehab wrote:
> Em Sat,  4 Aug 2018 14:45:06 +0200
> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> 
>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>
>> Integrate the request support. This adds the v4l2_ctrl_request_complete
>> and v4l2_ctrl_request_setup functions to complete a request and (as a
>> helper function) to apply a request to the hardware.
>>
>> It takes care of queuing requests and correctly chaining control values
>> in the request queue.
>>
>> Note that when a request is marked completed it will copy control values
>> to the internal request state. This can be optimized in the future since
>> this is sub-optimal when dealing with large compound and/or array controls.
>>
>> For the initial 'stateless codec' use-case the current implementation is
>> sufficient.
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> ---
>>  drivers/media/v4l2-core/v4l2-ctrls.c | 326 ++++++++++++++++++++++++++-
>>  include/media/v4l2-ctrls.h           |  51 +++++
>>  2 files changed, 371 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
>> index 570b6f8ae46a..b8ff6d6b14cd 100644
>> --- a/drivers/media/v4l2-core/v4l2-ctrls.c
>> +++ b/drivers/media/v4l2-core/v4l2-ctrls.c
>> @@ -1668,6 +1668,13 @@ static int new_to_user(struct v4l2_ext_control *c,
>>  	return ptr_to_user(c, ctrl, ctrl->p_new);
>>  }
>>  
>> +/* Helper function: copy the request value back to the caller */
>> +static int req_to_user(struct v4l2_ext_control *c,
>> +		       struct v4l2_ctrl_ref *ref)
>> +{
>> +	return ptr_to_user(c, ref->ctrl, ref->p_req);
>> +}
>> +
>>  /* Helper function: copy the initial control value back to the caller */
>>  static int def_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
>>  {
>> @@ -1787,6 +1794,26 @@ static void cur_to_new(struct v4l2_ctrl *ctrl)
>>  	ptr_to_ptr(ctrl, ctrl->p_cur, ctrl->p_new);
>>  }
>>  
>> +/* Copy the new value to the request value */
>> +static void new_to_req(struct v4l2_ctrl_ref *ref)
>> +{
>> +	if (!ref)
>> +		return;
>> +	ptr_to_ptr(ref->ctrl, ref->ctrl->p_new, ref->p_req);
>> +	ref->req = ref;
>> +}
>> +
>> +/* Copy the request value to the new value */
>> +static void req_to_new(struct v4l2_ctrl_ref *ref)
>> +{
>> +	if (!ref)
>> +		return;
>> +	if (ref->req)
>> +		ptr_to_ptr(ref->ctrl, ref->req->p_req, ref->ctrl->p_new);
>> +	else
>> +		ptr_to_ptr(ref->ctrl, ref->ctrl->p_cur, ref->ctrl->p_new);
>> +}
>> +
>>  /* Return non-zero if one or more of the controls in the cluster has a new
>>     value that differs from the current value. */
>>  static int cluster_changed(struct v4l2_ctrl *master)
>> @@ -1896,6 +1923,9 @@ int v4l2_ctrl_handler_init_class(struct v4l2_ctrl_handler *hdl,
>>  	lockdep_set_class_and_name(hdl->lock, key, name);
>>  	INIT_LIST_HEAD(&hdl->ctrls);
>>  	INIT_LIST_HEAD(&hdl->ctrl_refs);
>> +	INIT_LIST_HEAD(&hdl->requests);
>> +	INIT_LIST_HEAD(&hdl->requests_queued);
>> +	hdl->request_is_queued = false;
>>  	hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8;
>>  	hdl->buckets = kvmalloc_array(hdl->nr_of_buckets,
>>  				      sizeof(hdl->buckets[0]),
>> @@ -1916,6 +1946,14 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
>>  	if (hdl == NULL || hdl->buckets == NULL)
>>  		return;
>>  
>> +	if (!hdl->req_obj.req && !list_empty(&hdl->requests)) {
>> +		struct v4l2_ctrl_handler *req, *next_req;
>> +
>> +		list_for_each_entry_safe(req, next_req, &hdl->requests, requests) {
>> +			media_request_object_unbind(&req->req_obj);
>> +			media_request_object_put(&req->req_obj);
> 
> Hmm... while this would work for the trivial case where object_put()
> would just drop the object from the list if nobody else is using it,
> nothing prevents that, if v4l2_ctrl_handler_free() is called twice,
> it would do the wrong thing... as the only test here is if req_obj.reg 
> is not NULL, and not if the control framework is already done with the
> object.

v4l2_ctrl_handler_free sets hdl->buckets to NULL when done. And if it is
called twice it will detect that hdl->buckets == NULL and return.

So this isn't an issue.

> 
>> +		}
>> +	}
>>  	mutex_lock(hdl->lock);
>>  	/* Free all nodes */
>>  	list_for_each_entry_safe(ref, next_ref, &hdl->ctrl_refs, node) {
>> @@ -2837,6 +2875,123 @@ int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm)
>>  }
>>  EXPORT_SYMBOL(v4l2_querymenu);
>>  
>> +static int v4l2_ctrl_request_clone(struct v4l2_ctrl_handler *hdl,
>> +				   const struct v4l2_ctrl_handler *from)
>> +{
>> +	struct v4l2_ctrl_ref *ref;
>> +	int err;
>> +
>> +	if (WARN_ON(!hdl || hdl == from))
>> +		return -EINVAL;
>> +
>> +	if (hdl->error)
>> +		return hdl->error;
>> +
>> +	WARN_ON(hdl->lock != &hdl->_lock);
>> +
>> +	mutex_lock(from->lock);
>> +	list_for_each_entry(ref, &from->ctrl_refs, node) {
>> +		struct v4l2_ctrl *ctrl = ref->ctrl;
>> +		struct v4l2_ctrl_ref *new_ref;
>> +
>> +		/* Skip refs inherited from other devices */
>> +		if (ref->from_other_dev)
>> +			continue;
>> +		/* And buttons */
>> +		if (ctrl->type == V4L2_CTRL_TYPE_BUTTON)
>> +			continue;
>> +		err = handler_new_ref(hdl, ctrl, &new_ref, false, true);
>> +		if (err)
>> +			break;
>> +	}
>> +	mutex_unlock(from->lock);
>> +	return err;
>> +}
>> +
>> +static void v4l2_ctrl_request_queue(struct media_request_object *obj)
>> +{
>> +	struct v4l2_ctrl_handler *hdl =
>> +		container_of(obj, struct v4l2_ctrl_handler, req_obj);
>> +	struct v4l2_ctrl_handler *main_hdl = obj->priv;
>> +	struct v4l2_ctrl_handler *prev_hdl = NULL;
>> +	struct v4l2_ctrl_ref *ref_ctrl, *ref_ctrl_prev = NULL;
>> +
>> +	if (list_empty(&main_hdl->requests_queued))
>> +		goto queue;
>> +
>> +	prev_hdl = list_last_entry(&main_hdl->requests_queued,
>> +				   struct v4l2_ctrl_handler, requests_queued);
>> +	/*
>> +	 * Note: prev_hdl and hdl must contain the same list of control
>> +	 * references, so if any differences are detected then that is a
>> +	 * driver bug and the WARN_ON is triggered.
>> +	 */
>> +	mutex_lock(prev_hdl->lock);
>> +	ref_ctrl_prev = list_first_entry(&prev_hdl->ctrl_refs,
>> +					 struct v4l2_ctrl_ref, node);
>> +	list_for_each_entry(ref_ctrl, &hdl->ctrl_refs, node) {
>> +		if (ref_ctrl->req)
>> +			continue;
>> +		while (ref_ctrl_prev->ctrl->id < ref_ctrl->ctrl->id) {
>> +			/* Should never happen, but just in case... */
>> +			if (list_is_last(&ref_ctrl_prev->node,
>> +					 &prev_hdl->ctrl_refs))
> 
> If should never happen, please use unlikely(). That makes clearer
> while doing some speed optimization).

Sorry, I won't make this change. There is no optimization benefit from
using unlikely (there almost never is) and it just pollutes the code.

> 
>> +				break;
>> +			ref_ctrl_prev = list_next_entry(ref_ctrl_prev, node);
>> +		}
>> +		if (WARN_ON(ref_ctrl_prev->ctrl->id != ref_ctrl->ctrl->id))
>> +			break;
>> +		ref_ctrl->req = ref_ctrl_prev->req;
>> +	}
>> +	mutex_unlock(prev_hdl->lock);
>> +queue:
>> +	list_add_tail(&hdl->requests_queued, &main_hdl->requests_queued);
> 
> Shouldn't list changes be serialized? Ok, the ioctls are serialized, but
> device removal/unbind can happen any time for hot-plugged devices[1].

They are serialized. This function is (indirectly) called from
media_request_ioctl_queue() which has req_queue_mutex locked. This should all
be safe.

> 
> [1] yeah, I know that, right now, the framework is meant to be used only
> by codecs that are on SoC, but I'm pretty sure we'll end by using it on
> other use cases in the future. As this is core code, we should be sure 
> that it will not cause troubles due to the lack of a proper locking, as
> I doubt we'll review the locks when adding other use cases.
> 
>> +	hdl->request_is_queued = true;
>> +}
>> +
>> +static void v4l2_ctrl_request_unbind(struct media_request_object *obj)
>> +{
>> +	struct v4l2_ctrl_handler *hdl =
>> +		container_of(obj, struct v4l2_ctrl_handler, req_obj);
>> +
>> +	list_del_init(&hdl->requests);
>> +	if (hdl->request_is_queued) {
>> +		list_del_init(&hdl->requests_queued);
>> +		hdl->request_is_queued = false;
>> +	}
>> +}
>> +
>> +static void v4l2_ctrl_request_release(struct media_request_object *obj)
>> +{
>> +	struct v4l2_ctrl_handler *hdl =
>> +		container_of(obj, struct v4l2_ctrl_handler, req_obj);
>> +
>> +	v4l2_ctrl_handler_free(hdl);
>> +	kfree(hdl);
>> +}
>> +
>> +static const struct media_request_object_ops req_ops = {
>> +	.queue = v4l2_ctrl_request_queue,
>> +	.unbind = v4l2_ctrl_request_unbind,
>> +	.release = v4l2_ctrl_request_release,
>> +};
>> +
>> +static int v4l2_ctrl_request_bind(struct media_request *req,
>> +			   struct v4l2_ctrl_handler *hdl,
>> +			   struct v4l2_ctrl_handler *from)
>> +{
>> +	int ret;
>> +
>> +	ret = v4l2_ctrl_request_clone(hdl, from);
>> +
>> +	if (!ret) {
>> +		ret = media_request_object_bind(req, &req_ops,
>> +						from, &hdl->req_obj);
>> +		if (!ret)
>> +			list_add_tail(&hdl->requests, &from->requests);
>> +	}
>> +	return ret;
>> +}
>>  
>>  /* Some general notes on the atomic requirements of VIDIOC_G/TRY/S_EXT_CTRLS:
>>  
>> @@ -2898,6 +3053,7 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
>>  
>>  		if (cs->which &&
>>  		    cs->which != V4L2_CTRL_WHICH_DEF_VAL &&
>> +		    cs->which != V4L2_CTRL_WHICH_REQUEST_VAL &&
>>  		    V4L2_CTRL_ID2WHICH(id) != cs->which)
>>  			return -EINVAL;
>>  
>> @@ -2977,13 +3133,12 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
>>     whether there are any controls at all. */
>>  static int class_check(struct v4l2_ctrl_handler *hdl, u32 which)
>>  {
>> -	if (which == 0 || which == V4L2_CTRL_WHICH_DEF_VAL)
>> +	if (which == 0 || which == V4L2_CTRL_WHICH_DEF_VAL ||
>> +	    which == V4L2_CTRL_WHICH_REQUEST_VAL)
>>  		return 0;
>>  	return find_ref_lock(hdl, which | 1) ? 0 : -EINVAL;
>>  }
>>  
>> -
>> -
>>  /* Get extended controls. Allocates the helpers array if needed. */
>>  int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
>>  {
>> @@ -3049,8 +3204,12 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
>>  			u32 idx = i;
>>  
>>  			do {
>> -				ret = ctrl_to_user(cs->controls + idx,
>> -						   helpers[idx].ref->ctrl);
>> +				if (helpers[idx].ref->req)
>> +					ret = req_to_user(cs->controls + idx,
>> +						helpers[idx].ref->req);
>> +				else
>> +					ret = ctrl_to_user(cs->controls + idx,
>> +						helpers[idx].ref->ctrl);
>>  				idx = helpers[idx].next;
>>  			} while (!ret && idx);
>>  		}
>> @@ -3336,7 +3495,16 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
>>  		} while (!ret && idx);
>>  
>>  		if (!ret)
>> -			ret = try_or_set_cluster(fh, master, set, 0);
>> +			ret = try_or_set_cluster(fh, master,
>> +						 !hdl->req_obj.req && set, 0);
>> +		if (!ret && hdl->req_obj.req && set) {
>> +			for (j = 0; j < master->ncontrols; j++) {
>> +				struct v4l2_ctrl_ref *ref =
>> +					find_ref(hdl, master->cluster[j]->id);
>> +
>> +				new_to_req(ref);
>> +			}
>> +		}
>>  
>>  		/* Copy the new values back to userspace. */
>>  		if (!ret) {
>> @@ -3463,6 +3631,152 @@ int __v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s)
>>  }
>>  EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_string);
>>  
>> +void v4l2_ctrl_request_complete(struct media_request *req,
>> +				struct v4l2_ctrl_handler *main_hdl)
>> +{
>> +	struct media_request_object *obj;
>> +	struct v4l2_ctrl_handler *hdl;
>> +	struct v4l2_ctrl_ref *ref;
>> +
>> +	if (!req || !main_hdl)
>> +		return;
>> +
>> +	obj = media_request_object_find(req, &req_ops, main_hdl);
>> +	if (!obj)
>> +		return;
>> +	hdl = container_of(obj, struct v4l2_ctrl_handler, req_obj);
>> +
>> +	list_for_each_entry(ref, &hdl->ctrl_refs, node) {
>> +		struct v4l2_ctrl *ctrl = ref->ctrl;
>> +		struct v4l2_ctrl *master = ctrl->cluster[0];
>> +		unsigned int i;
>> +
>> +		if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE) {
>> +			ref->req = ref;
>> +
>> +			v4l2_ctrl_lock(master);
>> +			/* g_volatile_ctrl will update the current control values */
>> +			for (i = 0; i < master->ncontrols; i++)
>> +				cur_to_new(master->cluster[i]);
>> +			call_op(master, g_volatile_ctrl);
>> +			new_to_req(ref);
>> +			v4l2_ctrl_unlock(master);
>> +			continue;
>> +		}
>> +		if (ref->req == ref)
>> +			continue;
>> +
>> +		v4l2_ctrl_lock(ctrl);
>> +		if (ref->req)
>> +			ptr_to_ptr(ctrl, ref->req->p_req, ref->p_req);
>> +		else
>> +			ptr_to_ptr(ctrl, ctrl->p_cur, ref->p_req);
>> +		v4l2_ctrl_unlock(ctrl);
>> +	}
>> +
>> +	WARN_ON(!hdl->request_is_queued);
>> +	list_del_init(&hdl->requests_queued);
>> +	hdl->request_is_queued = false;
>> +	media_request_object_complete(obj);
>> +	media_request_object_put(obj);
> 
> Hmm... nothing prevents that this would race with v4l2_ctrl_handler_free()
> and cause double-free (actually double object_put).

That would be a driver bug: complete is called when completing a request, and if
this happens after v4l2_ctrl_handler_free is called, then the driver got the
cleanup sequence wrong (and this won't be the only problem that driver has!).

> 
>> +}
>> +EXPORT_SYMBOL(v4l2_ctrl_request_complete);
>> +
>> +void v4l2_ctrl_request_setup(struct media_request *req,
>> +			     struct v4l2_ctrl_handler *main_hdl)
>> +{
>> +	struct media_request_object *obj;
>> +	struct v4l2_ctrl_handler *hdl;
>> +	struct v4l2_ctrl_ref *ref;
>> +
>> +	if (!req || !main_hdl)
>> +		return;
>> +
>> +	if (WARN_ON(req->state != MEDIA_REQUEST_STATE_QUEUED))
>> +		return;
>> +
>> +	obj = media_request_object_find(req, &req_ops, main_hdl);
>> +	if (!obj)
>> +		return;
> 
> Shouldn't the above checks produce an error or print something at
> the logs?

Good question.

I think not. This situation would occur if the applications makes a request
with only a buffer but no controls, thus making no changes to the controls in
this request.

This is perfectly legal, so nothing needs to be logged here.

Regards,

	Hans

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

* Re: [PATCHv17 16/34] v4l2-ctrls: add v4l2_ctrl_request_hdl_find/put/ctrl_find functions
  2018-08-13 11:07   ` Mauro Carvalho Chehab
@ 2018-08-14  8:45     ` Hans Verkuil
  2018-08-14  8:55       ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-14  8:45 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media, Hans Verkuil

On 13/08/18 13:07, Mauro Carvalho Chehab wrote:
> Em Sat,  4 Aug 2018 14:45:08 +0200
> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> 
>> If a driver needs to find/inspect the controls set in a request then
>> it can use these functions.
>>
>> E.g. to check if a required control is set in a request use this in the
>> req_validate() implementation:
>>
>> 	int res = -EINVAL;
>>
>> 	hdl = v4l2_ctrl_request_hdl_find(req, parent_hdl);
>> 	if (hdl) {
>> 		if (v4l2_ctrl_request_hdl_ctrl_find(hdl, ctrl_id))
>> 			res = 0;
>> 		v4l2_ctrl_request_hdl_put(hdl);
>> 	}
>> 	return res;
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> ---
>>  drivers/media/v4l2-core/v4l2-ctrls.c | 25 ++++++++++++++++
>>  include/media/v4l2-ctrls.h           | 44 +++++++++++++++++++++++++++-
>>  2 files changed, 68 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
>> index 86a6ae54ccaa..2a30be824491 100644
>> --- a/drivers/media/v4l2-core/v4l2-ctrls.c
>> +++ b/drivers/media/v4l2-core/v4l2-ctrls.c
>> @@ -2976,6 +2976,31 @@ static const struct media_request_object_ops req_ops = {
>>  	.release = v4l2_ctrl_request_release,
>>  };
>>  
>> +struct v4l2_ctrl_handler *v4l2_ctrl_request_hdl_find(struct media_request *req,
>> +					struct v4l2_ctrl_handler *parent)
>> +{
>> +	struct media_request_object *obj;
>> +
>> +	if (WARN_ON(req->state != MEDIA_REQUEST_STATE_VALIDATING &&
>> +		    req->state != MEDIA_REQUEST_STATE_QUEUED))
>> +		return NULL;
>> +
>> +	obj = media_request_object_find(req, &req_ops, parent);
>> +	if (obj)
>> +		return container_of(obj, struct v4l2_ctrl_handler, req_obj);
>> +	return NULL;
>> +}
>> +EXPORT_SYMBOL_GPL(v4l2_ctrl_request_hdl_find);
>> +
>> +struct v4l2_ctrl *
>> +v4l2_ctrl_request_hdl_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id)
>> +{
>> +	struct v4l2_ctrl_ref *ref = find_ref_lock(hdl, id);
>> +
>> +	return (ref && ref->req == ref) ? ref->ctrl : NULL;
> 
> Doesn't those helper functions (including this one) be serialized?

v4l2_ctrl_request_hdl_find() checks the request state to ensure this:
it is either VALIDATING (then the req_queue_mutex is locked) or QUEUED
and then it is under control of the driver. Of course, in that case the
driver should make sure that it doesn't complete the request in the
middle of calling this function. If a driver does that, then it is a driver
bug.

Regards,

	Hans

> 
>> +}
>> +EXPORT_SYMBOL_GPL(v4l2_ctrl_request_hdl_ctrl_find);
>> +
>>  static int v4l2_ctrl_request_bind(struct media_request *req,
>>  			   struct v4l2_ctrl_handler *hdl,
>>  			   struct v4l2_ctrl_handler *from)
>> diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
>> index 98b1e70a4a46..aeb7f3c24ef7 100644
>> --- a/include/media/v4l2-ctrls.h
>> +++ b/include/media/v4l2-ctrls.h
>> @@ -1111,7 +1111,49 @@ void v4l2_ctrl_request_setup(struct media_request *req,
>>   * request object.
>>   */
>>  void v4l2_ctrl_request_complete(struct media_request *req,
>> -				struct v4l2_ctrl_handler *hdl);
>> +				struct v4l2_ctrl_handler *parent);
>> +
>> +/**
>> + * v4l2_ctrl_request_hdl_find - Find the control handler in the request
>> + *
>> + * @req: The request
>> + * @parent: The parent control handler ('priv' in media_request_object_find())
>> + *
>> + * This function finds the control handler in the request. It may return
>> + * NULL if not found. When done, you must call v4l2_ctrl_request_put_hdl()
>> + * with the returned handler pointer.
>> + *
>> + * If the request is not in state VALIDATING or QUEUED, then this function
>> + * will always return NULL.
>> + */
>> +struct v4l2_ctrl_handler *v4l2_ctrl_request_hdl_find(struct media_request *req,
>> +					struct v4l2_ctrl_handler *parent);
>> +
>> +/**
>> + * v4l2_ctrl_request_hdl_put - Put the control handler
>> + *
>> + * @hdl: Put this control handler
>> + *
>> + * This function released the control handler previously obtained from'
>> + * v4l2_ctrl_request_hdl_find().
>> + */
>> +static inline void v4l2_ctrl_request_hdl_put(struct v4l2_ctrl_handler *hdl)
>> +{
>> +	if (hdl)
>> +		media_request_object_put(&hdl->req_obj);
>> +}
>> +
>> +/**
>> + * v4l2_ctrl_request_ctrl_find() - Find a control with the given ID.
>> + *
>> + * @hdl: The control handler from the request.
>> + * @id: The ID of the control to find.
>> + *
>> + * This function returns a pointer to the control if this control is
>> + * part of the request or NULL otherwise.
>> + */
>> +struct v4l2_ctrl *
>> +v4l2_ctrl_request_hdl_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id);
>>  
>>  /* Helpers for ioctl_ops */
>>  
> 
> 
> 
> Thanks,
> Mauro
> 

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

* Re: [PATCHv17 02/34] uapi/linux/media.h: add request API
  2018-08-10  7:21     ` Hans Verkuil
@ 2018-08-14  8:46       ` Mauro Carvalho Chehab
  2018-08-14  9:57         ` Hans Verkuil
  0 siblings, 1 reply; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-14  8:46 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Fri, 10 Aug 2018 09:21:59 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> On 08/09/2018 07:53 PM, Mauro Carvalho Chehab wrote:
> > Em Sat,  4 Aug 2018 14:44:54 +0200
> > Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> >   
> >> From: Hans Verkuil <hans.verkuil@cisco.com>
> >>
> >> Define the public request API.
> >>
> >> This adds the new MEDIA_IOC_REQUEST_ALLOC ioctl to allocate a request
> >> and two ioctls that operate on a request in order to queue the
> >> contents of the request to the driver and to re-initialize the
> >> request.
> >>
> >> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> >> Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> >> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> >> ---
> >>  include/uapi/linux/media.h | 12 ++++++++++++
> >>  1 file changed, 12 insertions(+)
> >>
> >> diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h
> >> index 36f76e777ef9..cf77f00a0f2d 100644
> >> --- a/include/uapi/linux/media.h
> >> +++ b/include/uapi/linux/media.h
> >> @@ -364,11 +364,23 @@ struct media_v2_topology {
> >>  
> >>  /* ioctls */
> >>  
> >> +struct __attribute__ ((packed)) media_request_alloc {
> >> +	__s32 fd;
> >> +};
> >> +
> >>  #define MEDIA_IOC_DEVICE_INFO	_IOWR('|', 0x00, struct media_device_info)
> >>  #define MEDIA_IOC_ENUM_ENTITIES	_IOWR('|', 0x01, struct media_entity_desc)
> >>  #define MEDIA_IOC_ENUM_LINKS	_IOWR('|', 0x02, struct media_links_enum)
> >>  #define MEDIA_IOC_SETUP_LINK	_IOWR('|', 0x03, struct media_link_desc)
> >>  #define MEDIA_IOC_G_TOPOLOGY	_IOWR('|', 0x04, struct media_v2_topology)
> >> +#define MEDIA_IOC_REQUEST_ALLOC	_IOWR('|', 0x05, struct media_request_alloc)  

The definition here is wrong... the fd field is not R/W, it is just R, as no
fields inside this struct should be filled by userspace.
The right declaration for it would be:

	#define MEDIA_IOC_REQUEST_ALLOC	_IOR('|', 0x05, struct media_request_alloc)

I do have a strong opinion here: ioctls that just return stuff should use _IOR.

> > 
> > Same comment as in patch 1: keep it simpler: just pass a s32 * as the
> > argument for this ioctl.  
> 
> Same comment as in patch 1: I have no strong opinion, but I want the input from others
> as well.

I'm transcribing a comment you wrote on patch 01/34 here, for the sake of
keeping everything on a single thread:

> The first version just had a s32 argument, not a struct. The main reason for
> going back to a struct was indeed to make it easier to add new fields in the
> future. I don't foresee any, but then, you never do.

First of all, if we declare it as it should be, e. g.: 

#define MEDIA_IOC_REQUEST_ALLOC	_IOR('|', 0x05, int) 

If later find the need for some struct:

	struct media_request_alloc {
		__s32 fd;
		__s32 foo;
	} __packed;

Assuming that "foo" is a write argument, we'll then have:

	#define MEDIA_IOC_REQUEST_ALLOC		_IOR('|', 0x05, int) 
	#define MEDIA_IOC_REQUEST_ALLOC_V2	_IOWR('|', 0x05, struct media_request_alloc)  

The size of the ioctl will also be different, and also the direction.
So, the magic number will be different.

The Kernel can easily handle it on both ways, and, as 
MEDIA_IOC_REQUEST_ALLOC has only an integer output parameter, 
there's no need for compat32 or to keep any old struct.
The MEDIA_IOC_REQUEST_ALLOC code handler will still be very simple,
and backward compatible comes for free.

If, on the other hand, we declare it as:
	#define MEDIA_IOC_REQUEST_ALLOC	_IOR('|', 0x05, struct media_request_alloc_old)

And then we change it to:
	#define MEDIA_IOC_REQUEST_ALLOC	_IORW('|', 0x05, struct media_request_alloc_new)

Keeping backward compatible will be painful, and will require efforts for
no gain.

Thanks,
Mauro

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

* Re: [PATCHv17 01/34] Documentation: v4l: document request API
  2018-08-14  7:57     ` Hans Verkuil
@ 2018-08-14  8:48       ` Mauro Carvalho Chehab
  2018-08-14  9:51         ` Hans Verkuil
  0 siblings, 1 reply; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-14  8:48 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Alexandre Courbot, Hans Verkuil

Em Tue, 14 Aug 2018 09:57:27 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> On 09/08/18 19:43, Mauro Carvalho Chehab wrote:
> >> diff --git a/Documentation/media/uapi/v4l/vidioc-qbuf.rst b/Documentation/media/uapi/v4l/vidioc-qbuf.rst
> >> index 9e448a4aa3aa..0e415f2551b2 100644
> >> --- a/Documentation/media/uapi/v4l/vidioc-qbuf.rst
> >> +++ b/Documentation/media/uapi/v4l/vidioc-qbuf.rst
> >> @@ -65,7 +65,7 @@ To enqueue a :ref:`memory mapped <mmap>` buffer applications set the
> >>  with a pointer to this structure the driver sets the
> >>  ``V4L2_BUF_FLAG_MAPPED`` and ``V4L2_BUF_FLAG_QUEUED`` flags and clears
> >>  the ``V4L2_BUF_FLAG_DONE`` flag in the ``flags`` field, or it returns an
> >> -EINVAL error code.
> >> +``EINVAL`` error code.  
> > 
> > Side note: we should likely do a similar replacement on all other places
> > inside the media uAPI docs.
> >   
> >>  
> >>  To enqueue a :ref:`user pointer <userp>` buffer applications set the
> >>  ``memory`` field to ``V4L2_MEMORY_USERPTR``, the ``m.userptr`` field to
> >> @@ -98,6 +98,25 @@ dequeued, until the :ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` or
> >>  :ref:`VIDIOC_REQBUFS` ioctl is called, or until the
> >>  device is closed.
> >>  
> >> +The ``request_fd`` field can be used with the ``VIDIOC_QBUF`` ioctl to specify  
> > 
> > Please prefer using :ref: for QBUF too, e. g.: 
> > 	:ref:`ioctl VIDIOC_QBUF <VIDIOC_QBUF>`  
> 
> Does this make sense when you are in the QBUF documentation itself? Using :ref: will
> just link back to the same page.
> 
> We need some guidelines here. I personally don't think this makes sense.

I'm almost sure we're doing the same on every other place within media docs.

> 
> Regards,
> 
> 	Hans



Thanks,
Mauro

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

* Re: [PATCHv17 16/34] v4l2-ctrls: add v4l2_ctrl_request_hdl_find/put/ctrl_find functions
  2018-08-14  8:45     ` Hans Verkuil
@ 2018-08-14  8:55       ` Mauro Carvalho Chehab
  2018-08-14 10:50         ` Hans Verkuil
  0 siblings, 1 reply; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-14  8:55 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Tue, 14 Aug 2018 10:45:57 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> On 13/08/18 13:07, Mauro Carvalho Chehab wrote:
> > Em Sat,  4 Aug 2018 14:45:08 +0200
> > Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> >   
> >> If a driver needs to find/inspect the controls set in a request then
> >> it can use these functions.
> >>
> >> E.g. to check if a required control is set in a request use this in the
> >> req_validate() implementation:
> >>
> >> 	int res = -EINVAL;
> >>
> >> 	hdl = v4l2_ctrl_request_hdl_find(req, parent_hdl);
> >> 	if (hdl) {
> >> 		if (v4l2_ctrl_request_hdl_ctrl_find(hdl, ctrl_id))
> >> 			res = 0;
> >> 		v4l2_ctrl_request_hdl_put(hdl);
> >> 	}
> >> 	return res;
> >>
> >> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> >> ---
> >>  drivers/media/v4l2-core/v4l2-ctrls.c | 25 ++++++++++++++++
> >>  include/media/v4l2-ctrls.h           | 44 +++++++++++++++++++++++++++-
> >>  2 files changed, 68 insertions(+), 1 deletion(-)
> >>
> >> diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
> >> index 86a6ae54ccaa..2a30be824491 100644
> >> --- a/drivers/media/v4l2-core/v4l2-ctrls.c
> >> +++ b/drivers/media/v4l2-core/v4l2-ctrls.c
> >> @@ -2976,6 +2976,31 @@ static const struct media_request_object_ops req_ops = {
> >>  	.release = v4l2_ctrl_request_release,
> >>  };
> >>  
> >> +struct v4l2_ctrl_handler *v4l2_ctrl_request_hdl_find(struct media_request *req,
> >> +					struct v4l2_ctrl_handler *parent)
> >> +{
> >> +	struct media_request_object *obj;
> >> +
> >> +	if (WARN_ON(req->state != MEDIA_REQUEST_STATE_VALIDATING &&
> >> +		    req->state != MEDIA_REQUEST_STATE_QUEUED))
> >> +		return NULL;
> >> +
> >> +	obj = media_request_object_find(req, &req_ops, parent);
> >> +	if (obj)
> >> +		return container_of(obj, struct v4l2_ctrl_handler, req_obj);
> >> +	return NULL;
> >> +}
> >> +EXPORT_SYMBOL_GPL(v4l2_ctrl_request_hdl_find);
> >> +
> >> +struct v4l2_ctrl *
> >> +v4l2_ctrl_request_hdl_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id)
> >> +{
> >> +	struct v4l2_ctrl_ref *ref = find_ref_lock(hdl, id);
> >> +
> >> +	return (ref && ref->req == ref) ? ref->ctrl : NULL;  
> > 
> > Doesn't those helper functions (including this one) be serialized?  
> 
> v4l2_ctrl_request_hdl_find() checks the request state to ensure this:
> it is either VALIDATING (then the req_queue_mutex is locked) or QUEUED
> and then it is under control of the driver. Of course, in that case the
> driver should make sure that it doesn't complete the request in the
> middle of calling this function. If a driver does that, then it is a driver
> bug.

Please document it then, as I guess anyone that didn't worked at the
request API patchset wouldn't guess when the driver needs to take
the lock themselves.

From what I'm understanding, the driver needs to take the lock only
when it is running a code that it is not called from an ioctl.
right?

Thanks,
Mauro

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

* Re: [PATCHv17 19/34] vb2: drop VB2_BUF_STATE_PREPARED, use bool prepared/synced instead
  2018-08-13 11:30   ` Mauro Carvalho Chehab
@ 2018-08-14  8:58     ` Hans Verkuil
  2018-08-14  9:06       ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-14  8:58 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media, Hans Verkuil

On 13/08/18 13:30, Mauro Carvalho Chehab wrote:
> Em Sat,  4 Aug 2018 14:45:11 +0200
> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> 
>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>
>> The PREPARED state becomes a problem with the request API: a buffer
>> could be PREPARED but dequeued, or PREPARED and in state IN_REQUEST.
>>
>> PREPARED is really not a state as such, but more a property of the
>> buffer. So make new 'prepared' and 'synced' bools instead to remember
>> whether the buffer is prepared and/or synced or not.
>>
>> V4L2_BUF_FLAG_PREPARED is only set if the buffer is both synced and
>> prepared and in the DEQUEUED state.
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> ---
>>  .../media/common/videobuf2/videobuf2-core.c   | 38 +++++++++++++------
>>  .../media/common/videobuf2/videobuf2-v4l2.c   | 16 +++++---
>>  include/media/videobuf2-core.h                |  6 ++-
>>  3 files changed, 40 insertions(+), 20 deletions(-)
>>
>> diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
>> index 7401a17c80ca..eead693ba619 100644
>> --- a/drivers/media/common/videobuf2/videobuf2-core.c
>> +++ b/drivers/media/common/videobuf2/videobuf2-core.c
>> @@ -682,7 +682,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
>>  		}
>>  
>>  		/*
>> -		 * Call queue_cancel to clean up any buffers in the PREPARED or
>> +		 * Call queue_cancel to clean up any buffers in the
>>  		 * QUEUED state which is possible if buffers were prepared or
>>  		 * queued without ever calling STREAMON.
>>  		 */
>> @@ -921,6 +921,7 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
>>  		/* sync buffers */
>>  		for (plane = 0; plane < vb->num_planes; ++plane)
>>  			call_void_memop(vb, finish, vb->planes[plane].mem_priv);
>> +		vb->synced = false;
> 
> Shouldn't be prepared cleaned as well on reqbufs?

reqbufs calls queue_cancel which will in turn call the buf_finish op and sets
prepared to false.

> 
>>  	}
>>  
>>  	spin_lock_irqsave(&q->done_lock, flags);
>> @@ -1239,6 +1240,7 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
>>  static int __buf_prepare(struct vb2_buffer *vb)
>>  {
>>  	struct vb2_queue *q = vb->vb2_queue;
>> +	enum vb2_buffer_state orig_state = vb->state;
>>  	unsigned int plane;
>>  	int ret;
>>  
>> @@ -1247,6 +1249,10 @@ static int __buf_prepare(struct vb2_buffer *vb)
>>  		return -EIO;
>>  	}
>>  
>> +	if (vb->prepared)
>> +		return 0;
>> +	WARN_ON(vb->synced);
>> +
>>  	vb->state = VB2_BUF_STATE_PREPARING;
>>  
>>  	switch (q->memory) {
> 
>> @@ -1262,11 +1268,12 @@ static int __buf_prepare(struct vb2_buffer *vb)
>>  	default:
>>  		WARN(1, "Invalid queue type\n");
>>  		ret = -EINVAL;
>> +		break;
>>  	}
> 
> Hmm... is this hunk a bug fix? if so, please split into a separate patch
> and add a c/c stable.

No, just a clean up. I hate switch cases that do not end with a 'break'.
I can leave it our if you prefer.

> 
>>  
>>  	if (ret) {
>>  		dprintk(1, "buffer preparation failed: %d\n", ret);
>> -		vb->state = VB2_BUF_STATE_DEQUEUED;
>> +		vb->state = orig_state;
>>  		return ret;
>>  	}
>>  
>> @@ -1274,7 +1281,9 @@ static int __buf_prepare(struct vb2_buffer *vb)
>>  	for (plane = 0; plane < vb->num_planes; ++plane)
>>  		call_void_memop(vb, prepare, vb->planes[plane].mem_priv);
>>  
>> -	vb->state = VB2_BUF_STATE_PREPARED;
>> +	vb->synced = true;
>> +	vb->prepared = true;
>> +	vb->state = orig_state;
>>  
>>  	return 0;
>>  }
>> @@ -1290,6 +1299,10 @@ int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb)
>>  			vb->state);
>>  		return -EINVAL;
>>  	}
>> +	if (vb->prepared) {
>> +		dprintk(1, "buffer already prepared\n");
>> +		return -EINVAL;
>> +	}
>>  
>>  	ret = __buf_prepare(vb);
>>  	if (ret)
>> @@ -1381,11 +1394,11 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb)
>>  
>>  	switch (vb->state) {
>>  	case VB2_BUF_STATE_DEQUEUED:
>> -		ret = __buf_prepare(vb);
>> -		if (ret)
>> -			return ret;
>> -		break;
>> -	case VB2_BUF_STATE_PREPARED:
>> +		if (!vb->prepared) {
>> +			ret = __buf_prepare(vb);
>> +			if (ret)
>> +				return ret;
>> +		}
>>  		break;
>>  	case VB2_BUF_STATE_PREPARING:
>>  		dprintk(1, "buffer still being prepared\n");
>> @@ -1611,6 +1624,7 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb,
>>  	}
>>  
>>  	call_void_vb_qop(vb, buf_finish, vb);
>> +	vb->prepared = false;
>>  
>>  	if (pindex)
>>  		*pindex = vb->index;
>> @@ -1699,18 +1713,18 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
>>  	for (i = 0; i < q->num_buffers; ++i) {
>>  		struct vb2_buffer *vb = q->bufs[i];
>>  
>> -		if (vb->state == VB2_BUF_STATE_PREPARED ||
>> -		    vb->state == VB2_BUF_STATE_QUEUED) {
>> +		if (vb->synced) {
>>  			unsigned int plane;
>>  
>>  			for (plane = 0; plane < vb->num_planes; ++plane)
>>  				call_void_memop(vb, finish,
>>  						vb->planes[plane].mem_priv);
>> +			vb->synced = false;
>>  		}
>>  
>> -		if (vb->state != VB2_BUF_STATE_DEQUEUED) {
>> -			vb->state = VB2_BUF_STATE_PREPARED;
>> +		if (vb->prepared) {
>>  			call_void_vb_qop(vb, buf_finish, vb);
>> +			vb->prepared = false;
>>  		}
>>  		__vb2_dqbuf(vb);
>>  	}
>> diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c
>> index 360dc4e7d413..a677e2c26247 100644
>> --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
>> +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
>> @@ -352,9 +352,13 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b,
>>  	if (ret)
>>  		return ret;
>>  
>> -	/* Copy relevant information provided by the userspace */
>> -	memset(vbuf->planes, 0, sizeof(vbuf->planes[0]) * vb->num_planes);
>> -	return vb2_fill_vb2_v4l2_buffer(vb, b);
>> +	if (!vb->prepared) {
>> +		/* Copy relevant information provided by the userspace */
>> +		memset(vbuf->planes, 0,
>> +		       sizeof(vbuf->planes[0]) * vb->num_planes);
>> +		ret = vb2_fill_vb2_v4l2_buffer(vb, b);
>> +	}
>> +	return ret;
>>  }
>>  
>>  /*
>> @@ -443,9 +447,6 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
>>  	case VB2_BUF_STATE_DONE:
>>  		b->flags |= V4L2_BUF_FLAG_DONE;
>>  		break;
>> -	case VB2_BUF_STATE_PREPARED:
>> -		b->flags |= V4L2_BUF_FLAG_PREPARED;
>> -		break;
>>  	case VB2_BUF_STATE_PREPARING:
>>  	case VB2_BUF_STATE_DEQUEUED:
>>  	case VB2_BUF_STATE_REQUEUEING:
>> @@ -453,6 +454,9 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
>>  		break;
>>  	}
>>  
>> +	if (vb->state == VB2_BUF_STATE_DEQUEUED && vb->synced && vb->prepared)
>> +		b->flags |= V4L2_BUF_FLAG_PREPARED;
>> +
>>  	if (vb2_buffer_in_use(q, vb))
>>  		b->flags |= V4L2_BUF_FLAG_MAPPED;
>>  
>> diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
>> index 224c4820a044..5e760d5f280a 100644
>> --- a/include/media/videobuf2-core.h
>> +++ b/include/media/videobuf2-core.h
>> @@ -204,7 +204,6 @@ enum vb2_io_modes {
>>   * enum vb2_buffer_state - current video buffer state.
>>   * @VB2_BUF_STATE_DEQUEUED:	buffer under userspace control.
>>   * @VB2_BUF_STATE_PREPARING:	buffer is being prepared in videobuf.
>> - * @VB2_BUF_STATE_PREPARED:	buffer prepared in videobuf and by the driver.
>>   * @VB2_BUF_STATE_QUEUED:	buffer queued in videobuf, but not in driver.
>>   * @VB2_BUF_STATE_REQUEUEING:	re-queue a buffer to the driver.
>>   * @VB2_BUF_STATE_ACTIVE:	buffer queued in driver and possibly used
>> @@ -218,7 +217,6 @@ enum vb2_io_modes {
>>  enum vb2_buffer_state {
>>  	VB2_BUF_STATE_DEQUEUED,
>>  	VB2_BUF_STATE_PREPARING,
>> -	VB2_BUF_STATE_PREPARED,
>>  	VB2_BUF_STATE_QUEUED,
>>  	VB2_BUF_STATE_REQUEUEING,
>>  	VB2_BUF_STATE_ACTIVE,
>> @@ -250,6 +248,8 @@ struct vb2_buffer {
>>  	/* private: internal use only
>>  	 *
>>  	 * state:		current buffer state; do not change
>> +	 * synced:		this buffer has been synced
>> +	 * prepared:		this buffer has been prepared
> 
> Please describe better what "prepared" and "synced" means, as
> the above doesn't really help.
> 
> From what I got:
> 
> 	- "prepared" means that buf_prepare() was called and
> 	  vidioc_dqbuf() (nor cancel) was not called;

Set when the buf_prepare vb2_op is called, cleared after the buf_finish
vb2_op is called. I.e. this locks the buffer memory in place and makes
it ready for use by the driver.

> 	- "sync" means that the buf_prepare() was called
> 	  and the v4l2_buffer_done() (nor cancel)

Set when the 'prepare' memop is called, cleared when the 'finish' memop
is called. This syncs the memory for use with DMA (i.e. flushing or
invalidating the CPU cache).

> 
> right?

I updated the documentation:

         * synced:              this buffer has been synced for DMA, i.e. the
         *                      'prepare' memop was called. It is cleared again
         *                      after the 'finish' memop is called.
         * prepared:            this buffer has been prepared, i.e. the
         *                      buf_prepare op was called. It is cleared again
         *                      after the 'buf_finish' op is called.


> 
> 
>>  	 * queued_entry:	entry on the queued buffers list, which holds
>>  	 *			all buffers queued from userspace
>>  	 * done_entry:		entry on the list that stores all buffers ready
>> @@ -257,6 +257,8 @@ struct vb2_buffer {
>>  	 * vb2_plane:		per-plane information; do not change
>>  	 */
>>  	enum vb2_buffer_state	state;
>> +	bool			synced;
>> +	bool			prepared;
>>  
>>  	struct vb2_plane	planes[VB2_MAX_PLANES];
>>  	struct list_head	queued_entry;
> 
> 
> 
> Thanks,
> Mauro
> 

Regards,

	Hans

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

* Re: [PATCHv17 14/34] v4l2-ctrls: add core request support
  2018-08-14  8:34     ` Hans Verkuil
@ 2018-08-14  8:59       ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-14  8:59 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Tue, 14 Aug 2018 10:34:47 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> >> +void v4l2_ctrl_request_setup(struct media_request *req,
> >> +			     struct v4l2_ctrl_handler *main_hdl)
> >> +{
> >> +	struct media_request_object *obj;
> >> +	struct v4l2_ctrl_handler *hdl;
> >> +	struct v4l2_ctrl_ref *ref;
> >> +
> >> +	if (!req || !main_hdl)
> >> +		return;
> >> +
> >> +	if (WARN_ON(req->state != MEDIA_REQUEST_STATE_QUEUED))
> >> +		return;
> >> +
> >> +	obj = media_request_object_find(req, &req_ops, main_hdl);
> >> +	if (!obj)
> >> +		return;  
> > 
> > Shouldn't the above checks produce an error or print something at
> > the logs?  
> 
> Good question.
> 
> I think not. This situation would occur if the applications makes a request
> with only a buffer but no controls, thus making no changes to the controls in
> this request.
> 
> This is perfectly legal, so nothing needs to be logged here.

Ok, makes sense.

Please add a note at the source code explaining that, as this is
not obvious for a casual code reviewer.


Thanks,
Mauro

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

* Re: [PATCHv17 05/34] media-request: add media_request_get_by_fd
  2018-08-10  7:32     ` Hans Verkuil
@ 2018-08-14  9:00       ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-14  9:00 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Fri, 10 Aug 2018 09:32:53 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> On 08/09/2018 09:55 PM, Mauro Carvalho Chehab wrote:
> > Em Sat,  4 Aug 2018 14:44:57 +0200
> > Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> >   
> >> From: Hans Verkuil <hans.verkuil@cisco.com>
> >>
> >> Add media_request_get_by_fd() to find a request based on the file
> >> descriptor.
> >>
> >> The caller has to call media_request_put() for the returned
> >> request since this function increments the refcount.
> >>
> >> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> >> Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> >> ---
> >>  drivers/media/media-request.c | 40 +++++++++++++++++++++++++++++++++++
> >>  include/media/media-request.h | 24 +++++++++++++++++++++
> >>  2 files changed, 64 insertions(+)
> >>
> >> diff --git a/drivers/media/media-request.c b/drivers/media/media-request.c
> >> index 253068f51a1f..4b523f3a03a3 100644
> >> --- a/drivers/media/media-request.c
> >> +++ b/drivers/media/media-request.c
> >> @@ -231,6 +231,46 @@ static const struct file_operations request_fops = {
> >>  	.release = media_request_close,
> >>  };
> >>  
> >> +struct media_request *
> >> +media_request_get_by_fd(struct media_device *mdev, int request_fd)
> >> +{
> >> +	struct file *filp;
> >> +	struct media_request *req;
> >> +
> >> +	if (!mdev || !mdev->ops ||
> >> +	    !mdev->ops->req_validate || !mdev->ops->req_queue)
> >> +		return ERR_PTR(-EPERM);  
> > 
> > EPERM? I guess ENOTTY would be better.
> > 
> > Any reason why using EPERM?  
> 
> This is called by e.g. VIDIOC_QBUF or VIDIOC_S/G/TRY_EXT_CTRLS where someone
> sets request_fd. Then this function is called to obtain the corresponding
> media_request struct. If requests are not supported, then EPERM is returned.
> 
> Returning ENOTTY would be wrong, since VIDIOC_QBUF etc. are definitely implemented,
> instead they just do not permit the use of requests.
> 
> Let me know if I can add your Reviewed-by after this explanation.

Reviewed-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>

> 
> Regards,
> 
> 	Hans



Thanks,
Mauro

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

* Re: [PATCHv17 19/34] vb2: drop VB2_BUF_STATE_PREPARED, use bool prepared/synced instead
  2018-08-14  8:58     ` Hans Verkuil
@ 2018-08-14  9:06       ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-14  9:06 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Tue, 14 Aug 2018 10:58:59 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> On 13/08/18 13:30, Mauro Carvalho Chehab wrote:
> > Em Sat,  4 Aug 2018 14:45:11 +0200
> > Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> >   
> >> From: Hans Verkuil <hans.verkuil@cisco.com>
> >>
> >> The PREPARED state becomes a problem with the request API: a buffer
> >> could be PREPARED but dequeued, or PREPARED and in state IN_REQUEST.
> >>
> >> PREPARED is really not a state as such, but more a property of the
> >> buffer. So make new 'prepared' and 'synced' bools instead to remember
> >> whether the buffer is prepared and/or synced or not.
> >>
> >> V4L2_BUF_FLAG_PREPARED is only set if the buffer is both synced and
> >> prepared and in the DEQUEUED state.
> >>
> >> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> >> ---
> >>  .../media/common/videobuf2/videobuf2-core.c   | 38 +++++++++++++------
> >>  .../media/common/videobuf2/videobuf2-v4l2.c   | 16 +++++---
> >>  include/media/videobuf2-core.h                |  6 ++-
> >>  3 files changed, 40 insertions(+), 20 deletions(-)
> >>
> >> diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
> >> index 7401a17c80ca..eead693ba619 100644
> >> --- a/drivers/media/common/videobuf2/videobuf2-core.c
> >> +++ b/drivers/media/common/videobuf2/videobuf2-core.c
> >> @@ -682,7 +682,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
> >>  		}
> >>  
> >>  		/*
> >> -		 * Call queue_cancel to clean up any buffers in the PREPARED or
> >> +		 * Call queue_cancel to clean up any buffers in the
> >>  		 * QUEUED state which is possible if buffers were prepared or
> >>  		 * queued without ever calling STREAMON.
> >>  		 */
> >> @@ -921,6 +921,7 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
> >>  		/* sync buffers */
> >>  		for (plane = 0; plane < vb->num_planes; ++plane)
> >>  			call_void_memop(vb, finish, vb->planes[plane].mem_priv);
> >> +		vb->synced = false;  
> > 
> > Shouldn't be prepared cleaned as well on reqbufs?  
> 
> reqbufs calls queue_cancel which will in turn call the buf_finish op and sets
> prepared to false.

Ok. Not sure if I like to have it implicit somewhere.

> 
> >   
> >>  	}
> >>  
> >>  	spin_lock_irqsave(&q->done_lock, flags);
> >> @@ -1239,6 +1240,7 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
> >>  static int __buf_prepare(struct vb2_buffer *vb)
> >>  {
> >>  	struct vb2_queue *q = vb->vb2_queue;
> >> +	enum vb2_buffer_state orig_state = vb->state;
> >>  	unsigned int plane;
> >>  	int ret;
> >>  
> >> @@ -1247,6 +1249,10 @@ static int __buf_prepare(struct vb2_buffer *vb)
> >>  		return -EIO;
> >>  	}
> >>  
> >> +	if (vb->prepared)
> >> +		return 0;
> >> +	WARN_ON(vb->synced);
> >> +
> >>  	vb->state = VB2_BUF_STATE_PREPARING;
> >>  
> >>  	switch (q->memory) {  
> >   
> >> @@ -1262,11 +1268,12 @@ static int __buf_prepare(struct vb2_buffer *vb)
> >>  	default:
> >>  		WARN(1, "Invalid queue type\n");
> >>  		ret = -EINVAL;
> >> +		break;
> >>  	}  
> > 
> > Hmm... is this hunk a bug fix? if so, please split into a separate patch
> > and add a c/c stable.  
> 
> No, just a clean up. I hate switch cases that do not end with a 'break'.
> I can leave it our if you prefer.

Ah, this is at the default clause. I don't personally add a break
after default: when it is the last clause. No need for that, and
very unlikely that it would ever cause problems.

Also, as now gcc complains about missing breaks, at the very unlikely
case where someone would add something after the default: clause,
gcc would warn.

So, for my taste, this is overkill.

> 
> >   
> >>  
> >>  	if (ret) {
> >>  		dprintk(1, "buffer preparation failed: %d\n", ret);
> >> -		vb->state = VB2_BUF_STATE_DEQUEUED;
> >> +		vb->state = orig_state;
> >>  		return ret;
> >>  	}
> >>  
> >> @@ -1274,7 +1281,9 @@ static int __buf_prepare(struct vb2_buffer *vb)
> >>  	for (plane = 0; plane < vb->num_planes; ++plane)
> >>  		call_void_memop(vb, prepare, vb->planes[plane].mem_priv);
> >>  
> >> -	vb->state = VB2_BUF_STATE_PREPARED;
> >> +	vb->synced = true;
> >> +	vb->prepared = true;
> >> +	vb->state = orig_state;
> >>  
> >>  	return 0;
> >>  }
> >> @@ -1290,6 +1299,10 @@ int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb)
> >>  			vb->state);
> >>  		return -EINVAL;
> >>  	}
> >> +	if (vb->prepared) {
> >> +		dprintk(1, "buffer already prepared\n");
> >> +		return -EINVAL;
> >> +	}
> >>  
> >>  	ret = __buf_prepare(vb);
> >>  	if (ret)
> >> @@ -1381,11 +1394,11 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb)
> >>  
> >>  	switch (vb->state) {
> >>  	case VB2_BUF_STATE_DEQUEUED:
> >> -		ret = __buf_prepare(vb);
> >> -		if (ret)
> >> -			return ret;
> >> -		break;
> >> -	case VB2_BUF_STATE_PREPARED:
> >> +		if (!vb->prepared) {
> >> +			ret = __buf_prepare(vb);
> >> +			if (ret)
> >> +				return ret;
> >> +		}
> >>  		break;
> >>  	case VB2_BUF_STATE_PREPARING:
> >>  		dprintk(1, "buffer still being prepared\n");
> >> @@ -1611,6 +1624,7 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb,
> >>  	}
> >>  
> >>  	call_void_vb_qop(vb, buf_finish, vb);
> >> +	vb->prepared = false;
> >>  
> >>  	if (pindex)
> >>  		*pindex = vb->index;
> >> @@ -1699,18 +1713,18 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
> >>  	for (i = 0; i < q->num_buffers; ++i) {
> >>  		struct vb2_buffer *vb = q->bufs[i];
> >>  
> >> -		if (vb->state == VB2_BUF_STATE_PREPARED ||
> >> -		    vb->state == VB2_BUF_STATE_QUEUED) {
> >> +		if (vb->synced) {
> >>  			unsigned int plane;
> >>  
> >>  			for (plane = 0; plane < vb->num_planes; ++plane)
> >>  				call_void_memop(vb, finish,
> >>  						vb->planes[plane].mem_priv);
> >> +			vb->synced = false;
> >>  		}
> >>  
> >> -		if (vb->state != VB2_BUF_STATE_DEQUEUED) {
> >> -			vb->state = VB2_BUF_STATE_PREPARED;
> >> +		if (vb->prepared) {
> >>  			call_void_vb_qop(vb, buf_finish, vb);
> >> +			vb->prepared = false;
> >>  		}
> >>  		__vb2_dqbuf(vb);
> >>  	}
> >> diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c
> >> index 360dc4e7d413..a677e2c26247 100644
> >> --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
> >> +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
> >> @@ -352,9 +352,13 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b,
> >>  	if (ret)
> >>  		return ret;
> >>  
> >> -	/* Copy relevant information provided by the userspace */
> >> -	memset(vbuf->planes, 0, sizeof(vbuf->planes[0]) * vb->num_planes);
> >> -	return vb2_fill_vb2_v4l2_buffer(vb, b);
> >> +	if (!vb->prepared) {
> >> +		/* Copy relevant information provided by the userspace */
> >> +		memset(vbuf->planes, 0,
> >> +		       sizeof(vbuf->planes[0]) * vb->num_planes);
> >> +		ret = vb2_fill_vb2_v4l2_buffer(vb, b);
> >> +	}
> >> +	return ret;
> >>  }
> >>  
> >>  /*
> >> @@ -443,9 +447,6 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
> >>  	case VB2_BUF_STATE_DONE:
> >>  		b->flags |= V4L2_BUF_FLAG_DONE;
> >>  		break;
> >> -	case VB2_BUF_STATE_PREPARED:
> >> -		b->flags |= V4L2_BUF_FLAG_PREPARED;
> >> -		break;
> >>  	case VB2_BUF_STATE_PREPARING:
> >>  	case VB2_BUF_STATE_DEQUEUED:
> >>  	case VB2_BUF_STATE_REQUEUEING:
> >> @@ -453,6 +454,9 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
> >>  		break;
> >>  	}
> >>  
> >> +	if (vb->state == VB2_BUF_STATE_DEQUEUED && vb->synced && vb->prepared)
> >> +		b->flags |= V4L2_BUF_FLAG_PREPARED;
> >> +
> >>  	if (vb2_buffer_in_use(q, vb))
> >>  		b->flags |= V4L2_BUF_FLAG_MAPPED;
> >>  
> >> diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
> >> index 224c4820a044..5e760d5f280a 100644
> >> --- a/include/media/videobuf2-core.h
> >> +++ b/include/media/videobuf2-core.h
> >> @@ -204,7 +204,6 @@ enum vb2_io_modes {
> >>   * enum vb2_buffer_state - current video buffer state.
> >>   * @VB2_BUF_STATE_DEQUEUED:	buffer under userspace control.
> >>   * @VB2_BUF_STATE_PREPARING:	buffer is being prepared in videobuf.
> >> - * @VB2_BUF_STATE_PREPARED:	buffer prepared in videobuf and by the driver.
> >>   * @VB2_BUF_STATE_QUEUED:	buffer queued in videobuf, but not in driver.
> >>   * @VB2_BUF_STATE_REQUEUEING:	re-queue a buffer to the driver.
> >>   * @VB2_BUF_STATE_ACTIVE:	buffer queued in driver and possibly used
> >> @@ -218,7 +217,6 @@ enum vb2_io_modes {
> >>  enum vb2_buffer_state {
> >>  	VB2_BUF_STATE_DEQUEUED,
> >>  	VB2_BUF_STATE_PREPARING,
> >> -	VB2_BUF_STATE_PREPARED,
> >>  	VB2_BUF_STATE_QUEUED,
> >>  	VB2_BUF_STATE_REQUEUEING,
> >>  	VB2_BUF_STATE_ACTIVE,
> >> @@ -250,6 +248,8 @@ struct vb2_buffer {
> >>  	/* private: internal use only
> >>  	 *
> >>  	 * state:		current buffer state; do not change
> >> +	 * synced:		this buffer has been synced
> >> +	 * prepared:		this buffer has been prepared  
> > 
> > Please describe better what "prepared" and "synced" means, as
> > the above doesn't really help.
> > 
> > From what I got:
> > 
> > 	- "prepared" means that buf_prepare() was called and
> > 	  vidioc_dqbuf() (nor cancel) was not called;  
> 
> Set when the buf_prepare vb2_op is called, cleared after the buf_finish
> vb2_op is called. I.e. this locks the buffer memory in place and makes
> it ready for use by the driver.
> 
> > 	- "sync" means that the buf_prepare() was called
> > 	  and the v4l2_buffer_done() (nor cancel)  
> 
> Set when the 'prepare' memop is called, cleared when the 'finish' memop
> is called. This syncs the memory for use with DMA (i.e. flushing or
> invalidating the CPU cache).
> 
> > 
> > right?  
> 
> I updated the documentation:
> 
>          * synced:              this buffer has been synced for DMA, i.e. the
>          *                      'prepare' memop was called. It is cleared again
>          *                      after the 'finish' memop is called.
>          * prepared:            this buffer has been prepared, i.e. the
>          *                      buf_prepare op was called. It is cleared again
>          *                      after the 'buf_finish' op is called.

A way better!

> 
> > 
> >   
> >>  	 * queued_entry:	entry on the queued buffers list, which holds
> >>  	 *			all buffers queued from userspace
> >>  	 * done_entry:		entry on the list that stores all buffers ready
> >> @@ -257,6 +257,8 @@ struct vb2_buffer {
> >>  	 * vb2_plane:		per-plane information; do not change
> >>  	 */
> >>  	enum vb2_buffer_state	state;
> >> +	bool			synced;
> >> +	bool			prepared;
> >>  
> >>  	struct vb2_plane	planes[VB2_MAX_PLANES];
> >>  	struct list_head	queued_entry;  
> > 
> > 
> > 
> > Thanks,
> > Mauro
> >   
> 
> Regards,
> 
> 	Hans



Thanks,
Mauro

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

* Re: [PATCHv17 01/34] Documentation: v4l: document request API
  2018-08-14  8:48       ` Mauro Carvalho Chehab
@ 2018-08-14  9:51         ` Hans Verkuil
  2018-08-14 12:18           ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-14  9:51 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media, Alexandre Courbot, Hans Verkuil

On 14/08/18 10:48, Mauro Carvalho Chehab wrote:
> Em Tue, 14 Aug 2018 09:57:27 +0200
> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> 
>> On 09/08/18 19:43, Mauro Carvalho Chehab wrote:
>>>> diff --git a/Documentation/media/uapi/v4l/vidioc-qbuf.rst b/Documentation/media/uapi/v4l/vidioc-qbuf.rst
>>>> index 9e448a4aa3aa..0e415f2551b2 100644
>>>> --- a/Documentation/media/uapi/v4l/vidioc-qbuf.rst
>>>> +++ b/Documentation/media/uapi/v4l/vidioc-qbuf.rst
>>>> @@ -65,7 +65,7 @@ To enqueue a :ref:`memory mapped <mmap>` buffer applications set the
>>>>  with a pointer to this structure the driver sets the
>>>>  ``V4L2_BUF_FLAG_MAPPED`` and ``V4L2_BUF_FLAG_QUEUED`` flags and clears
>>>>  the ``V4L2_BUF_FLAG_DONE`` flag in the ``flags`` field, or it returns an
>>>> -EINVAL error code.
>>>> +``EINVAL`` error code.  
>>>
>>> Side note: we should likely do a similar replacement on all other places
>>> inside the media uAPI docs.
>>>   
>>>>  
>>>>  To enqueue a :ref:`user pointer <userp>` buffer applications set the
>>>>  ``memory`` field to ``V4L2_MEMORY_USERPTR``, the ``m.userptr`` field to
>>>> @@ -98,6 +98,25 @@ dequeued, until the :ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` or
>>>>  :ref:`VIDIOC_REQBUFS` ioctl is called, or until the
>>>>  device is closed.
>>>>  
>>>> +The ``request_fd`` field can be used with the ``VIDIOC_QBUF`` ioctl to specify  
>>>
>>> Please prefer using :ref: for QBUF too, e. g.: 
>>> 	:ref:`ioctl VIDIOC_QBUF <VIDIOC_QBUF>`  
>>
>> Does this make sense when you are in the QBUF documentation itself? Using :ref: will
>> just link back to the same page.
>>
>> We need some guidelines here. I personally don't think this makes sense.
> 
> I'm almost sure we're doing the same on every other place within media docs.

Not in vidioc-qbuf.rst: there you never use :ref: to refer to a QBUF/DQBUF.
I want to keep this as-is. If we really want to change this, then it should
be done for all vidioc-*.rst files.

A quick grep shows that this is very common:

git grep '``VIDIOC_' Documentation/media/uapi/v4l/vidioc-*

I honestly think it is silly and even confusing to use a :ref: to the page
you are already on.

I keep this as-is since this is consistent with the usage elsewhere in vidioc-qbuuf.rst.
If we want to change this, then that's something we should do separately from this
patch.

Regards,

	Hans

> 
>>
>> Regards,
>>
>> 	Hans
> 
> 
> 
> Thanks,
> Mauro
> 

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

* Re: [PATCHv17 02/34] uapi/linux/media.h: add request API
  2018-08-14  8:46       ` Mauro Carvalho Chehab
@ 2018-08-14  9:57         ` Hans Verkuil
  2018-08-14 12:34           ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-14  9:57 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media, Hans Verkuil

On 14/08/18 10:46, Mauro Carvalho Chehab wrote:
> Em Fri, 10 Aug 2018 09:21:59 +0200
> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> 
>> On 08/09/2018 07:53 PM, Mauro Carvalho Chehab wrote:
>>> Em Sat,  4 Aug 2018 14:44:54 +0200
>>> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
>>>   
>>>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>>>
>>>> Define the public request API.
>>>>
>>>> This adds the new MEDIA_IOC_REQUEST_ALLOC ioctl to allocate a request
>>>> and two ioctls that operate on a request in order to queue the
>>>> contents of the request to the driver and to re-initialize the
>>>> request.
>>>>
>>>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>>>> Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
>>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>>>> ---
>>>>  include/uapi/linux/media.h | 12 ++++++++++++
>>>>  1 file changed, 12 insertions(+)
>>>>
>>>> diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h
>>>> index 36f76e777ef9..cf77f00a0f2d 100644
>>>> --- a/include/uapi/linux/media.h
>>>> +++ b/include/uapi/linux/media.h
>>>> @@ -364,11 +364,23 @@ struct media_v2_topology {
>>>>  
>>>>  /* ioctls */
>>>>  
>>>> +struct __attribute__ ((packed)) media_request_alloc {
>>>> +	__s32 fd;
>>>> +};
>>>> +
>>>>  #define MEDIA_IOC_DEVICE_INFO	_IOWR('|', 0x00, struct media_device_info)
>>>>  #define MEDIA_IOC_ENUM_ENTITIES	_IOWR('|', 0x01, struct media_entity_desc)
>>>>  #define MEDIA_IOC_ENUM_LINKS	_IOWR('|', 0x02, struct media_links_enum)
>>>>  #define MEDIA_IOC_SETUP_LINK	_IOWR('|', 0x03, struct media_link_desc)
>>>>  #define MEDIA_IOC_G_TOPOLOGY	_IOWR('|', 0x04, struct media_v2_topology)
>>>> +#define MEDIA_IOC_REQUEST_ALLOC	_IOWR('|', 0x05, struct media_request_alloc)  
> 
> The definition here is wrong... the fd field is not R/W, it is just R, as no
> fields inside this struct should be filled by userspace.
> The right declaration for it would be:
> 
> 	#define MEDIA_IOC_REQUEST_ALLOC	_IOR('|', 0x05, struct media_request_alloc)
> 
> I do have a strong opinion here: ioctls that just return stuff should use _IOR.

You are right, this should be _IOR.

> 
>>>
>>> Same comment as in patch 1: keep it simpler: just pass a s32 * as the
>>> argument for this ioctl.  
>>
>> Same comment as in patch 1: I have no strong opinion, but I want the input from others
>> as well.
> 
> I'm transcribing a comment you wrote on patch 01/34 here, for the sake of
> keeping everything on a single thread:
> 
>> The first version just had a s32 argument, not a struct. The main reason for
>> going back to a struct was indeed to make it easier to add new fields in the
>> future. I don't foresee any, but then, you never do.
> 
> First of all, if we declare it as it should be, e. g.: 
> 
> #define MEDIA_IOC_REQUEST_ALLOC	_IOR('|', 0x05, int) 
> 
> If later find the need for some struct:
> 
> 	struct media_request_alloc {
> 		__s32 fd;
> 		__s32 foo;
> 	} __packed;
> 
> Assuming that "foo" is a write argument, we'll then have:
> 
> 	#define MEDIA_IOC_REQUEST_ALLOC		_IOR('|', 0x05, int) 
> 	#define MEDIA_IOC_REQUEST_ALLOC_V2	_IOWR('|', 0x05, struct media_request_alloc)  
> 
> The size of the ioctl will also be different, and also the direction.
> So, the magic number will be different.
> 
> The Kernel can easily handle it on both ways, and, as 
> MEDIA_IOC_REQUEST_ALLOC has only an integer output parameter, 
> there's no need for compat32 or to keep any old struct.
> The MEDIA_IOC_REQUEST_ALLOC code handler will still be very simple,
> and backward compatible comes for free.
> 
> If, on the other hand, we declare it as:
> 	#define MEDIA_IOC_REQUEST_ALLOC	_IOR('|', 0x05, struct media_request_alloc_old)
> 
> And then we change it to:
> 	#define MEDIA_IOC_REQUEST_ALLOC	_IORW('|', 0x05, struct media_request_alloc_new)
> 
> Keeping backward compatible will be painful, and will require efforts for
> no gain.

In the kernel it doesn't matter much, I agree. But applications that have to
be able to handle both old and new ioctls will have to switch between either
an fd or a struct. Whereas if we use a struct from the beginning, then the
only difference between the old and new ioctls are whether or not additional
fields beyond the first fd field are set.

As mentioned before, I don't have very strong feelings about this, but I
do want input from others on this before making this change.

Frankly, I think it is unlikely that we will need more fields beyond just
the fd, but I've been wrong about such things before...

Regards,

	Hans

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

* Re: [PATCHv17 16/34] v4l2-ctrls: add v4l2_ctrl_request_hdl_find/put/ctrl_find functions
  2018-08-14  8:55       ` Mauro Carvalho Chehab
@ 2018-08-14 10:50         ` Hans Verkuil
  0 siblings, 0 replies; 106+ messages in thread
From: Hans Verkuil @ 2018-08-14 10:50 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media, Hans Verkuil

On 14/08/18 10:55, Mauro Carvalho Chehab wrote:
> Em Tue, 14 Aug 2018 10:45:57 +0200
> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> 
>> On 13/08/18 13:07, Mauro Carvalho Chehab wrote:
>>> Em Sat,  4 Aug 2018 14:45:08 +0200
>>> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
>>>   
>>>> If a driver needs to find/inspect the controls set in a request then
>>>> it can use these functions.
>>>>
>>>> E.g. to check if a required control is set in a request use this in the
>>>> req_validate() implementation:
>>>>
>>>> 	int res = -EINVAL;
>>>>
>>>> 	hdl = v4l2_ctrl_request_hdl_find(req, parent_hdl);
>>>> 	if (hdl) {
>>>> 		if (v4l2_ctrl_request_hdl_ctrl_find(hdl, ctrl_id))
>>>> 			res = 0;
>>>> 		v4l2_ctrl_request_hdl_put(hdl);
>>>> 	}
>>>> 	return res;
>>>>
>>>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>>>> ---
>>>>  drivers/media/v4l2-core/v4l2-ctrls.c | 25 ++++++++++++++++
>>>>  include/media/v4l2-ctrls.h           | 44 +++++++++++++++++++++++++++-
>>>>  2 files changed, 68 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
>>>> index 86a6ae54ccaa..2a30be824491 100644
>>>> --- a/drivers/media/v4l2-core/v4l2-ctrls.c
>>>> +++ b/drivers/media/v4l2-core/v4l2-ctrls.c
>>>> @@ -2976,6 +2976,31 @@ static const struct media_request_object_ops req_ops = {
>>>>  	.release = v4l2_ctrl_request_release,
>>>>  };
>>>>  
>>>> +struct v4l2_ctrl_handler *v4l2_ctrl_request_hdl_find(struct media_request *req,
>>>> +					struct v4l2_ctrl_handler *parent)
>>>> +{
>>>> +	struct media_request_object *obj;
>>>> +
>>>> +	if (WARN_ON(req->state != MEDIA_REQUEST_STATE_VALIDATING &&
>>>> +		    req->state != MEDIA_REQUEST_STATE_QUEUED))
>>>> +		return NULL;
>>>> +
>>>> +	obj = media_request_object_find(req, &req_ops, parent);
>>>> +	if (obj)
>>>> +		return container_of(obj, struct v4l2_ctrl_handler, req_obj);
>>>> +	return NULL;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(v4l2_ctrl_request_hdl_find);
>>>> +
>>>> +struct v4l2_ctrl *
>>>> +v4l2_ctrl_request_hdl_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id)
>>>> +{
>>>> +	struct v4l2_ctrl_ref *ref = find_ref_lock(hdl, id);
>>>> +
>>>> +	return (ref && ref->req == ref) ? ref->ctrl : NULL;  
>>>
>>> Doesn't those helper functions (including this one) be serialized?  
>>
>> v4l2_ctrl_request_hdl_find() checks the request state to ensure this:
>> it is either VALIDATING (then the req_queue_mutex is locked) or QUEUED
>> and then it is under control of the driver. Of course, in that case the
>> driver should make sure that it doesn't complete the request in the
>> middle of calling this function. If a driver does that, then it is a driver
>> bug.
> 
> Please document it then, as I guess anyone that didn't worked at the
> request API patchset wouldn't guess when the driver needs to take
> the lock themselves.
> 
> From what I'm understanding, the driver needs to take the lock only
> when it is running a code that it is not called from an ioctl.
> right?

Drivers never take req_queue_mutex themselves. If they call this function
when in state QUEUED, then they might need to take a driver-specific mutex
or spinlock that protects against the request being completed by an
interrupt handler.

I very much doubt that this function is ever called when in QUEUED state,
the typical use-case is to call this during validation or when queuing
a validated request. In both cases there is no need to do any locking
since it is guaranteed that no control objects will be added/deleted during
that phase.

I've extended the req_queue documentation in media-device.h with
two extra sentences at the end:

 * @req_queue: Queue a validated request, cannot fail. If something goes
 *             wrong when queueing this request then it should be marked
 *             as such internally in the driver and any related buffers
 *             must eventually return to vb2 with state VB2_BUF_STATE_ERROR.
 *             The req_queue_mutex lock is held when this op is called.
 *             It is important that vb2 buffer objects are queued last after
 *             all other object types are queued: queueing a buffer kickstarts
 *             the request processing, so all other objects related to the
 *             request (and thus the buffer) must be available to the driver.
 *             And once a buffer is queued, then the driver can complete
 *             or delete objects from the request before req_queue exits.

This is important information that wasn't documented.

So nobody can add/remove objects from a request while in the req_validate()
callback. And nobody can add/remove objects from a request while in req_queue()
and non-buffer objects are queued.

Only once buffer objects are queued can objects be completed or removed from
the request and you have to be careful about that.

vb2_request_queue() does the right thing there: notice the list_for_each_entry_safe()
when iterating over the objects for buffers to queue.

Now, that said it is possible that the objects list contains non-buffer objects
after a buffer object: let's say that the objects list has four objects:

N1 B2 N3 N4

Where the Nx objects are non-buffer objects and B2 is a buffer object.

First N1/3/4 are queued to the driver, then the list is walked again using
list_for_each_entry_safe() and B2 is queued to the driver which immediately
deletes the buffer. list_for_each_entry_safe() only works if only B2 can
be deleted by the driver. If the driver would also delete e.g. N3, then
list_for_each_entry_safe() would fail.

This is OK in the current implementation since control objects are never
deleted, only completed (i.e. they stay in the object list until the request
is deleted by the application).

Still, we might get other object types where this is not the case anymore.

I have modified the code to add buffer objects to the end of the list and
non-buffer objects to the beginning of the list (an extra argument to
the media_request_object_bind function).

This avoids this corner case altogether, and it also avoids having to
walk the list twice.

So in the example above the objects list will now look like this:

N1 N3 N4 B2

Regards,

	Hans

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

* Re: [PATCHv17 08/34] v4l2-dev: lock req_queue_mutex
  2018-08-09 20:03   ` Mauro Carvalho Chehab
  2018-08-10  7:39     ` Hans Verkuil
@ 2018-08-14 12:00     ` Hans Verkuil
  2018-08-14 12:39       ` Mauro Carvalho Chehab
  1 sibling, 1 reply; 106+ messages in thread
From: Hans Verkuil @ 2018-08-14 12:00 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media, Hans Verkuil, Sakari Ailus

On 09/08/18 22:03, Mauro Carvalho Chehab wrote:
> Em Sat,  4 Aug 2018 14:45:00 +0200
> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> 
>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>
>> We need to serialize streamon/off with queueing new requests.
>> These ioctls may trigger the cancellation of a streaming
>> operation, and that should not be mixed with queuing a new
>> request at the same time.
>>
>> Finally close() needs this lock since that too can trigger the
>> cancellation of a streaming operation.
>>
>> We take the req_queue_mutex here before any other locks since
>> it is a very high-level lock.
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
>> ---
>>  drivers/media/v4l2-core/v4l2-dev.c   | 13 +++++++++++++
>>  drivers/media/v4l2-core/v4l2-ioctl.c | 22 +++++++++++++++++++++-
>>  2 files changed, 34 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
>> index 69e775930fc4..53018e4a4c78 100644
>> --- a/drivers/media/v4l2-core/v4l2-dev.c
>> +++ b/drivers/media/v4l2-core/v4l2-dev.c
>> @@ -444,8 +444,21 @@ static int v4l2_release(struct inode *inode, struct file *filp)
>>  	struct video_device *vdev = video_devdata(filp);
>>  	int ret = 0;
>>  
>> +	/*
>> +	 * We need to serialize the release() with queueing new requests.
>> +	 * The release() may trigger the cancellation of a streaming
>> +	 * operation, and that should not be mixed with queueing a new
>> +	 * request at the same time.
>> +	 */
>> +	if (v4l2_device_supports_requests(vdev->v4l2_dev))
>> +		mutex_lock(&vdev->v4l2_dev->mdev->req_queue_mutex);
>> +
>>  	if (vdev->fops->release)
>>  		ret = vdev->fops->release(filp);
>> +
>> +	if (v4l2_device_supports_requests(vdev->v4l2_dev))
>> +		mutex_unlock(&vdev->v4l2_dev->mdev->req_queue_mutex);
>> +
> 
> This will very likely generate sparse warnings. See my discussions
> with that regards with Linus.
> 
> The only way to avoid it would be to do something like:
> 
> 	if (v4l2_device_supports_requests(vdev->v4l2_dev)) {
> 		mutex_lock(&vdev->v4l2_dev->mdev->req_queue_mutex);
> 	 	if (vdev->fops->release)
> 			ret = vdev->fops->release(filp);
> 		mutex_unlock(&vdev->v4l2_dev->mdev->req_queue_mutex);
> 	} else {
> 	 	if (vdev->fops->release)
> 			ret = vdev->fops->release(filp);
> 	}

I've changed this.

> 
>>  	if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
>>  		dprintk("%s: release\n",
>>  			video_device_node_name(vdev));
>> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
>> index 54afc9c7ee6e..ea475d833dd6 100644
>> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
>> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
>> @@ -2780,6 +2780,7 @@ static long __video_do_ioctl(struct file *file,
>>  		unsigned int cmd, void *arg)
>>  {
>>  	struct video_device *vfd = video_devdata(file);
>> +	struct mutex *req_queue_lock = NULL;
>>  	struct mutex *lock; /* ioctl serialization mutex */
>>  	const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
>>  	bool write_only = false;
>> @@ -2799,10 +2800,27 @@ static long __video_do_ioctl(struct file *file,
>>  	if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags))
>>  		vfh = file->private_data;
>>  
>> +	/*
>> +	 * We need to serialize streamon/off with queueing new requests.
>> +	 * These ioctls may trigger the cancellation of a streaming
>> +	 * operation, and that should not be mixed with queueing a new
>> +	 * request at the same time.
>> +	 */
>> +	if (v4l2_device_supports_requests(vfd->v4l2_dev) &&
>> +	    (cmd == VIDIOC_STREAMON || cmd == VIDIOC_STREAMOFF)) {
>> +		req_queue_lock = &vfd->v4l2_dev->mdev->req_queue_mutex;
>> +
>> +		if (mutex_lock_interruptible(req_queue_lock))
>> +			return -ERESTARTSYS;
>> +	}
>> +
>>  	lock = v4l2_ioctl_get_lock(vfd, vfh, cmd, arg);
>>  
>> -	if (lock && mutex_lock_interruptible(lock))
>> +	if (lock && mutex_lock_interruptible(lock)) {
>> +		if (req_queue_lock)
>> +			mutex_unlock(req_queue_lock);
>>  		return -ERESTARTSYS;
>> +	}
> 
> Same applies here.

I've kept this. I don't get any sparse warnings for this, but if they appear
then the only way to fix it is likely to split this function into two,
one that takes the lock(s) and one unlocked variant that does the actual
job.

If we need that, then that's a separate patch on top of this patch series.

Regards,

	Hans

> 
>>  
>>  	if (!video_is_registered(vfd)) {
>>  		ret = -ENODEV;
>> @@ -2861,6 +2879,8 @@ static long __video_do_ioctl(struct file *file,
>>  unlock:
>>  	if (lock)
>>  		mutex_unlock(lock);
>> +	if (req_queue_lock)
>> +		mutex_unlock(req_queue_lock);
> 
> This code looks really weird! are you locking in order to get a
> lock pointer?
> 
> That seems broken by design.
> 
>>  	return ret;
>>  }
>>  
> 
> 
> 
> Thanks,
> Mauro
> 

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

* Re: [PATCHv17 01/34] Documentation: v4l: document request API
  2018-08-14  9:51         ` Hans Verkuil
@ 2018-08-14 12:18           ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-14 12:18 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Alexandre Courbot, Hans Verkuil

Em Tue, 14 Aug 2018 11:51:30 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> On 14/08/18 10:48, Mauro Carvalho Chehab wrote:
> > Em Tue, 14 Aug 2018 09:57:27 +0200
> > Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> >   
> >> On 09/08/18 19:43, Mauro Carvalho Chehab wrote:  
> >>>> diff --git a/Documentation/media/uapi/v4l/vidioc-qbuf.rst b/Documentation/media/uapi/v4l/vidioc-qbuf.rst
> >>>> index 9e448a4aa3aa..0e415f2551b2 100644
> >>>> --- a/Documentation/media/uapi/v4l/vidioc-qbuf.rst
> >>>> +++ b/Documentation/media/uapi/v4l/vidioc-qbuf.rst
> >>>> @@ -65,7 +65,7 @@ To enqueue a :ref:`memory mapped <mmap>` buffer applications set the
> >>>>  with a pointer to this structure the driver sets the
> >>>>  ``V4L2_BUF_FLAG_MAPPED`` and ``V4L2_BUF_FLAG_QUEUED`` flags and clears
> >>>>  the ``V4L2_BUF_FLAG_DONE`` flag in the ``flags`` field, or it returns an
> >>>> -EINVAL error code.
> >>>> +``EINVAL`` error code.    
> >>>
> >>> Side note: we should likely do a similar replacement on all other places
> >>> inside the media uAPI docs.
> >>>     
> >>>>  
> >>>>  To enqueue a :ref:`user pointer <userp>` buffer applications set the
> >>>>  ``memory`` field to ``V4L2_MEMORY_USERPTR``, the ``m.userptr`` field to
> >>>> @@ -98,6 +98,25 @@ dequeued, until the :ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` or
> >>>>  :ref:`VIDIOC_REQBUFS` ioctl is called, or until the
> >>>>  device is closed.
> >>>>  
> >>>> +The ``request_fd`` field can be used with the ``VIDIOC_QBUF`` ioctl to specify    
> >>>
> >>> Please prefer using :ref: for QBUF too, e. g.: 
> >>> 	:ref:`ioctl VIDIOC_QBUF <VIDIOC_QBUF>`    
> >>
> >> Does this make sense when you are in the QBUF documentation itself? Using :ref: will
> >> just link back to the same page.
> >>
> >> We need some guidelines here. I personally don't think this makes sense.  
> > 
> > I'm almost sure we're doing the same on every other place within media docs.  
> 
> Not in vidioc-qbuf.rst: there you never use :ref: to refer to a QBUF/DQBUF.
> I want to keep this as-is. If we really want to change this, then it should
> be done for all vidioc-*.rst files.
> 
> A quick grep shows that this is very common:
> 
> git grep '``VIDIOC_' Documentation/media/uapi/v4l/vidioc-*
> 
> I honestly think it is silly and even confusing to use a :ref: to the page
> you are already on.
> 
> I keep this as-is since this is consistent with the usage elsewhere in vidioc-qbuuf.rst.
> If we want to change this, then that's something we should do separately from this
> patch.

Ok, let's be consistent with what's already there.

Thanks,
Mauro

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

* Re: [PATCHv17 02/34] uapi/linux/media.h: add request API
  2018-08-14  9:57         ` Hans Verkuil
@ 2018-08-14 12:34           ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-14 12:34 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Em Tue, 14 Aug 2018 11:57:48 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> On 14/08/18 10:46, Mauro Carvalho Chehab wrote:
> > Em Fri, 10 Aug 2018 09:21:59 +0200
> > Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> >   
> >> On 08/09/2018 07:53 PM, Mauro Carvalho Chehab wrote:  
> >>> Em Sat,  4 Aug 2018 14:44:54 +0200
> >>> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> >>>     
> >>>> From: Hans Verkuil <hans.verkuil@cisco.com>
> >>>>
> >>>> Define the public request API.
> >>>>
> >>>> This adds the new MEDIA_IOC_REQUEST_ALLOC ioctl to allocate a request
> >>>> and two ioctls that operate on a request in order to queue the
> >>>> contents of the request to the driver and to re-initialize the
> >>>> request.
> >>>>
> >>>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> >>>> Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> >>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> >>>> ---
> >>>>  include/uapi/linux/media.h | 12 ++++++++++++
> >>>>  1 file changed, 12 insertions(+)
> >>>>
> >>>> diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h
> >>>> index 36f76e777ef9..cf77f00a0f2d 100644
> >>>> --- a/include/uapi/linux/media.h
> >>>> +++ b/include/uapi/linux/media.h
> >>>> @@ -364,11 +364,23 @@ struct media_v2_topology {
> >>>>  
> >>>>  /* ioctls */
> >>>>  
> >>>> +struct __attribute__ ((packed)) media_request_alloc {
> >>>> +	__s32 fd;
> >>>> +};
> >>>> +
> >>>>  #define MEDIA_IOC_DEVICE_INFO	_IOWR('|', 0x00, struct media_device_info)
> >>>>  #define MEDIA_IOC_ENUM_ENTITIES	_IOWR('|', 0x01, struct media_entity_desc)
> >>>>  #define MEDIA_IOC_ENUM_LINKS	_IOWR('|', 0x02, struct media_links_enum)
> >>>>  #define MEDIA_IOC_SETUP_LINK	_IOWR('|', 0x03, struct media_link_desc)
> >>>>  #define MEDIA_IOC_G_TOPOLOGY	_IOWR('|', 0x04, struct media_v2_topology)
> >>>> +#define MEDIA_IOC_REQUEST_ALLOC	_IOWR('|', 0x05, struct media_request_alloc)    
> > 
> > The definition here is wrong... the fd field is not R/W, it is just R, as no
> > fields inside this struct should be filled by userspace.
> > The right declaration for it would be:
> > 
> > 	#define MEDIA_IOC_REQUEST_ALLOC	_IOR('|', 0x05, struct media_request_alloc)
> > 
> > I do have a strong opinion here: ioctls that just return stuff should use _IOR.  
> 
> You are right, this should be _IOR.
> 
> >   
> >>>
> >>> Same comment as in patch 1: keep it simpler: just pass a s32 * as the
> >>> argument for this ioctl.    
> >>
> >> Same comment as in patch 1: I have no strong opinion, but I want the input from others
> >> as well.  
> > 
> > I'm transcribing a comment you wrote on patch 01/34 here, for the sake of
> > keeping everything on a single thread:
> >   
> >> The first version just had a s32 argument, not a struct. The main reason for
> >> going back to a struct was indeed to make it easier to add new fields in the
> >> future. I don't foresee any, but then, you never do.  
> > 
> > First of all, if we declare it as it should be, e. g.: 
> > 
> > #define MEDIA_IOC_REQUEST_ALLOC	_IOR('|', 0x05, int) 
> > 
> > If later find the need for some struct:
> > 
> > 	struct media_request_alloc {
> > 		__s32 fd;
> > 		__s32 foo;
> > 	} __packed;
> > 
> > Assuming that "foo" is a write argument, we'll then have:
> > 
> > 	#define MEDIA_IOC_REQUEST_ALLOC		_IOR('|', 0x05, int) 
> > 	#define MEDIA_IOC_REQUEST_ALLOC_V2	_IOWR('|', 0x05, struct media_request_alloc)  
> > 
> > The size of the ioctl will also be different, and also the direction.
> > So, the magic number will be different.
> > 
> > The Kernel can easily handle it on both ways, and, as 
> > MEDIA_IOC_REQUEST_ALLOC has only an integer output parameter, 
> > there's no need for compat32 or to keep any old struct.
> > The MEDIA_IOC_REQUEST_ALLOC code handler will still be very simple,
> > and backward compatible comes for free.
> > 
> > If, on the other hand, we declare it as:
> > 	#define MEDIA_IOC_REQUEST_ALLOC	_IOR('|', 0x05, struct media_request_alloc_old)
> > 
> > And then we change it to:
> > 	#define MEDIA_IOC_REQUEST_ALLOC	_IORW('|', 0x05, struct media_request_alloc_new)
> > 
> > Keeping backward compatible will be painful, and will require efforts for
> > no gain.  
> 
> In the kernel it doesn't matter much, I agree. But applications that have to
> be able to handle both old and new ioctls will have to switch between either
> an fd or a struct. Whereas if we use a struct from the beginning, then the
> only difference between the old and new ioctls are whether or not additional
> fields beyond the first fd field are set.

Well, if we change the API by adding other fields, applications will need
both behaviors anyway, if they want to support multiple kernel versions
and need the newer version behavior, if available.

> As mentioned before, I don't have very strong feelings about this, but I
> do want input from others on this before making this change.

Feel free to wait for other inputs.

> Frankly, I think it is unlikely that we will need more fields beyond just
> the fd, but I've been wrong about such things before...

In this specific ioctl, I don't expect other fields either. Even on
brain storm with crazy ideas, I can't find any other parameter that
might make any sense here. I'm almost certain that, if we end
by needing some other things for this ioctl to work, the userspace
logic will very likely require a non-trivial logic, up to a point
that implementing it with a different name or have something like
"MEDIA_IOC_PREP_REQUEST_ALLOC" would likely be better than re-using
the same ioctl name and changing its behavior.

Thanks,
Mauro

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

* Re: [PATCHv17 08/34] v4l2-dev: lock req_queue_mutex
  2018-08-14 12:00     ` Hans Verkuil
@ 2018-08-14 12:39       ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 106+ messages in thread
From: Mauro Carvalho Chehab @ 2018-08-14 12:39 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil, Sakari Ailus

Em Tue, 14 Aug 2018 14:00:16 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> On 09/08/18 22:03, Mauro Carvalho Chehab wrote:
> > Em Sat,  4 Aug 2018 14:45:00 +0200
> > Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> >   
> >> From: Hans Verkuil <hans.verkuil@cisco.com>
> >>
> >> We need to serialize streamon/off with queueing new requests.
> >> These ioctls may trigger the cancellation of a streaming
> >> operation, and that should not be mixed with queuing a new
> >> request at the same time.
> >>
> >> Finally close() needs this lock since that too can trigger the
> >> cancellation of a streaming operation.
> >>
> >> We take the req_queue_mutex here before any other locks since
> >> it is a very high-level lock.
> >>
> >> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> >> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> >> ---
> >>  drivers/media/v4l2-core/v4l2-dev.c   | 13 +++++++++++++
> >>  drivers/media/v4l2-core/v4l2-ioctl.c | 22 +++++++++++++++++++++-
> >>  2 files changed, 34 insertions(+), 1 deletion(-)
> >>
> >> diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
> >> index 69e775930fc4..53018e4a4c78 100644
> >> --- a/drivers/media/v4l2-core/v4l2-dev.c
> >> +++ b/drivers/media/v4l2-core/v4l2-dev.c
> >> @@ -444,8 +444,21 @@ static int v4l2_release(struct inode *inode, struct file *filp)
> >>  	struct video_device *vdev = video_devdata(filp);
> >>  	int ret = 0;
> >>  
> >> +	/*
> >> +	 * We need to serialize the release() with queueing new requests.
> >> +	 * The release() may trigger the cancellation of a streaming
> >> +	 * operation, and that should not be mixed with queueing a new
> >> +	 * request at the same time.
> >> +	 */
> >> +	if (v4l2_device_supports_requests(vdev->v4l2_dev))
> >> +		mutex_lock(&vdev->v4l2_dev->mdev->req_queue_mutex);
> >> +
> >>  	if (vdev->fops->release)
> >>  		ret = vdev->fops->release(filp);
> >> +
> >> +	if (v4l2_device_supports_requests(vdev->v4l2_dev))
> >> +		mutex_unlock(&vdev->v4l2_dev->mdev->req_queue_mutex);
> >> +  
> > 
> > This will very likely generate sparse warnings. See my discussions
> > with that regards with Linus.
> > 
> > The only way to avoid it would be to do something like:
> > 
> > 	if (v4l2_device_supports_requests(vdev->v4l2_dev)) {
> > 		mutex_lock(&vdev->v4l2_dev->mdev->req_queue_mutex);
> > 	 	if (vdev->fops->release)
> > 			ret = vdev->fops->release(filp);
> > 		mutex_unlock(&vdev->v4l2_dev->mdev->req_queue_mutex);
> > 	} else {
> > 	 	if (vdev->fops->release)
> > 			ret = vdev->fops->release(filp);
> > 	}  
> 
> I've changed this.
> 
> >   
> >>  	if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
> >>  		dprintk("%s: release\n",
> >>  			video_device_node_name(vdev));
> >> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
> >> index 54afc9c7ee6e..ea475d833dd6 100644
> >> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> >> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> >> @@ -2780,6 +2780,7 @@ static long __video_do_ioctl(struct file *file,
> >>  		unsigned int cmd, void *arg)
> >>  {
> >>  	struct video_device *vfd = video_devdata(file);
> >> +	struct mutex *req_queue_lock = NULL;
> >>  	struct mutex *lock; /* ioctl serialization mutex */
> >>  	const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
> >>  	bool write_only = false;
> >> @@ -2799,10 +2800,27 @@ static long __video_do_ioctl(struct file *file,
> >>  	if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags))
> >>  		vfh = file->private_data;
> >>  
> >> +	/*
> >> +	 * We need to serialize streamon/off with queueing new requests.
> >> +	 * These ioctls may trigger the cancellation of a streaming
> >> +	 * operation, and that should not be mixed with queueing a new
> >> +	 * request at the same time.
> >> +	 */
> >> +	if (v4l2_device_supports_requests(vfd->v4l2_dev) &&
> >> +	    (cmd == VIDIOC_STREAMON || cmd == VIDIOC_STREAMOFF)) {
> >> +		req_queue_lock = &vfd->v4l2_dev->mdev->req_queue_mutex;
> >> +
> >> +		if (mutex_lock_interruptible(req_queue_lock))
> >> +			return -ERESTARTSYS;
> >> +	}
> >> +
> >>  	lock = v4l2_ioctl_get_lock(vfd, vfh, cmd, arg);
> >>  
> >> -	if (lock && mutex_lock_interruptible(lock))
> >> +	if (lock && mutex_lock_interruptible(lock)) {
> >> +		if (req_queue_lock)
> >> +			mutex_unlock(req_queue_lock);
> >>  		return -ERESTARTSYS;
> >> +	}  
> > 
> > Same applies here.  
> 
> I've kept this. I don't get any sparse warnings for this, but if they appear
> then the only way to fix it is likely to split this function into two,
> one that takes the lock(s) and one unlocked variant that does the actual
> job.
> 
> If we need that, then that's a separate patch on top of this patch series.

Works for me.

Thanks,
Mauro

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

* Re: [PATCHv17 03/34] media-request: implement media requests
  2018-08-04 12:44 ` [PATCHv17 03/34] media-request: implement media requests Hans Verkuil
  2018-08-09 18:37   ` Mauro Carvalho Chehab
@ 2018-08-14 13:33   ` Hans Verkuil
  1 sibling, 0 replies; 106+ messages in thread
From: Hans Verkuil @ 2018-08-14 13:33 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil, Sakari Ailus

On 04/08/18 14:44, Hans Verkuil wrote:
> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Add initial media request support:
> 
> 1) Add MEDIA_IOC_REQUEST_ALLOC ioctl support to media-device.c
> 2) Add struct media_request to store request objects.
> 3) Add struct media_request_object to represent a request object.
> 4) Add MEDIA_REQUEST_IOC_QUEUE/REINIT ioctl support.
> 
> Basic lifecycle: the application allocates a request, adds
> objects to it, queues the request, polls until it is completed
> and can then read the final values of the objects at the time
> of completion. When it closes the file descriptor the request
> memory will be freed (actually, when the last user of that request
> releases the request).
> 
> Drivers will bind an object to a request (the 'adds objects to it'
> phase), when MEDIA_REQUEST_IOC_QUEUE is called the request is
> validated (req_validate op), then queued (the req_queue op).
> 
> When done with an object it can either be unbound from the request
> (e.g. when the driver has finished with a vb2 buffer) or marked as
> completed (e.g. for controls associated with a buffer). When all
> objects in the request are completed (or unbound), then the request
> fd will signal an exception (poll).
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> Co-developed-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> Co-developed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Co-developed-by: Alexandre Courbot <acourbot@chromium.org>
> ---
>  drivers/media/Makefile        |   3 +-
>  drivers/media/media-device.c  |  14 ++
>  drivers/media/media-request.c | 423 ++++++++++++++++++++++++++++++++++
>  include/media/media-device.h  |  21 ++
>  include/media/media-request.h | 327 ++++++++++++++++++++++++++
>  5 files changed, 787 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/media/media-request.c
>  create mode 100644 include/media/media-request.h
> 

<snip>

> +static long media_request_ioctl_queue(struct media_request *req)
> +{
> +	struct media_device *mdev = req->mdev;
> +	enum media_request_state state;
> +	unsigned long flags;
> +	int ret;
> +
> +	dev_dbg(mdev->dev, "request: queue %s\n", req->debug_str);
> +
> +	/*
> +	 * Ensure the request that is validated will be the one that gets queued
> +	 * next by serialising the queueing process. This mutex is also used
> +	 * to serialize with canceling a vb2 queue and with setting values such
> +	 * as controls in a request.
> +	 */
> +	mutex_lock(&mdev->req_queue_mutex);
> +
> +	media_request_get(req);

Here we get a reference to the request...

> +
> +	spin_lock_irqsave(&req->lock, flags);
> +	if (req->state == MEDIA_REQUEST_STATE_IDLE)
> +		req->state = MEDIA_REQUEST_STATE_VALIDATING;
> +	state = req->state;
> +	spin_unlock_irqrestore(&req->lock, flags);
> +	if (state != MEDIA_REQUEST_STATE_VALIDATING) {
> +		dev_dbg(mdev->dev,
> +			"request: unable to queue %s, request in state %s\n",
> +			req->debug_str, media_request_state_str(state));

...but we didn't put it again in this error path. Thus this request is never
released.

Fixed by adding a media_request_put(req) here.

Found with v4l2-compliance.

> +		mutex_unlock(&mdev->req_queue_mutex);
> +		return -EBUSY;
> +	}
> +
> +	ret = mdev->ops->req_validate(req);
> +
> +	/*
> +	 * If the req_validate was successful, then we mark the state as QUEUED
> +	 * and call req_queue. The reason we set the state first is that this
> +	 * allows req_queue to unbind or complete the queued objects in case
> +	 * they are immediately 'consumed'. State changes from QUEUED to another
> +	 * state can only happen if either the driver changes the state or if
> +	 * the user cancels the vb2 queue. The driver can only change the state
> +	 * after each object is queued through the req_queue op (and note that
> +	 * that op cannot fail), so setting the state to QUEUED up front is
> +	 * safe.
> +	 *
> +	 * The other reason for changing the state is if the vb2 queue is
> +	 * canceled, and that uses the req_queue_mutex which is still locked
> +	 * while req_queue is called, so that's safe as well.
> +	 */
> +	spin_lock_irqsave(&req->lock, flags);
> +	req->state = ret ? MEDIA_REQUEST_STATE_IDLE
> +			 : MEDIA_REQUEST_STATE_QUEUED;
> +	spin_unlock_irqrestore(&req->lock, flags);
> +
> +	if (!ret)
> +		mdev->ops->req_queue(req);
> +
> +	mutex_unlock(&mdev->req_queue_mutex);
> +
> +	if (ret) {
> +		dev_dbg(mdev->dev, "request: can't queue %s (%d)\n",
> +			req->debug_str, ret);
> +		media_request_put(req);
> +	}
> +
> +	return ret;
> +}

Regards,

	Hans

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

end of thread, other threads:[~2018-08-15 10:38 UTC | newest]

Thread overview: 106+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-08-04 12:44 [PATCHv17 00/34] Request API Hans Verkuil
2018-08-04 12:44 ` [PATCHv17 01/34] Documentation: v4l: document request API Hans Verkuil
2018-08-06 23:44   ` Pavel Machek
2018-08-09 17:43   ` Mauro Carvalho Chehab
2018-08-10  7:20     ` Hans Verkuil
2018-08-14  8:21       ` Mauro Carvalho Chehab
2018-08-14  7:57     ` Hans Verkuil
2018-08-14  8:48       ` Mauro Carvalho Chehab
2018-08-14  9:51         ` Hans Verkuil
2018-08-14 12:18           ` Mauro Carvalho Chehab
2018-08-04 12:44 ` [PATCHv17 02/34] uapi/linux/media.h: add " Hans Verkuil
2018-08-09 17:53   ` Mauro Carvalho Chehab
2018-08-10  7:21     ` Hans Verkuil
2018-08-14  8:46       ` Mauro Carvalho Chehab
2018-08-14  9:57         ` Hans Verkuil
2018-08-14 12:34           ` Mauro Carvalho Chehab
2018-08-04 12:44 ` [PATCHv17 03/34] media-request: implement media requests Hans Verkuil
2018-08-09 18:37   ` Mauro Carvalho Chehab
2018-08-10  7:28     ` Hans Verkuil
2018-08-14 13:33   ` Hans Verkuil
2018-08-04 12:44 ` [PATCHv17 04/34] media: doc: Add media-request.h header to documentation build Hans Verkuil
2018-08-09 18:43   ` Mauro Carvalho Chehab
2018-08-04 12:44 ` [PATCHv17 05/34] media-request: add media_request_get_by_fd Hans Verkuil
2018-08-09 19:55   ` Mauro Carvalho Chehab
2018-08-10  7:32     ` Hans Verkuil
2018-08-14  9:00       ` Mauro Carvalho Chehab
2018-08-04 12:44 ` [PATCHv17 06/34] media-request: add media_request_object_find Hans Verkuil
2018-08-09 19:56   ` Mauro Carvalho Chehab
2018-08-04 12:44 ` [PATCHv17 07/34] v4l2-device.h: add v4l2_device_supports_requests() helper Hans Verkuil
2018-08-09 19:57   ` Mauro Carvalho Chehab
2018-08-04 12:45 ` [PATCHv17 08/34] v4l2-dev: lock req_queue_mutex Hans Verkuil
2018-08-09 20:03   ` Mauro Carvalho Chehab
2018-08-10  7:39     ` Hans Verkuil
2018-08-14  8:09       ` Mauro Carvalho Chehab
2018-08-14 12:00     ` Hans Verkuil
2018-08-14 12:39       ` Mauro Carvalho Chehab
2018-08-04 12:45 ` [PATCHv17 09/34] videodev2.h: add request_fd field to v4l2_ext_controls Hans Verkuil
2018-08-09 20:04   ` Mauro Carvalho Chehab
2018-08-04 12:45 ` [PATCHv17 10/34] v4l2-ctrls: v4l2_ctrl_add_handler: add from_other_dev Hans Verkuil
2018-08-09 20:10   ` Mauro Carvalho Chehab
2018-08-04 12:45 ` [PATCHv17 11/34] v4l2-ctrls: prepare internal structs for request API Hans Verkuil
2018-08-09 20:16   ` Mauro Carvalho Chehab
2018-08-10  7:41     ` Hans Verkuil
2018-08-04 12:45 ` [PATCHv17 12/34] v4l2-ctrls: alloc memory for p_req Hans Verkuil
2018-08-09 20:19   ` Mauro Carvalho Chehab
2018-08-10  7:41     ` Hans Verkuil
2018-08-04 12:45 ` [PATCHv17 13/34] v4l2-ctrls: use ref in helper instead of ctrl Hans Verkuil
2018-08-09 20:22   ` Mauro Carvalho Chehab
2018-08-04 12:45 ` [PATCHv17 14/34] v4l2-ctrls: add core request support Hans Verkuil
2018-08-13 10:55   ` Mauro Carvalho Chehab
2018-08-14  8:34     ` Hans Verkuil
2018-08-14  8:59       ` Mauro Carvalho Chehab
2018-08-04 12:45 ` [PATCHv17 15/34] v4l2-ctrls: support g/s_ext_ctrls for requests Hans Verkuil
2018-08-13 11:05   ` Mauro Carvalho Chehab
2018-08-04 12:45 ` [PATCHv17 16/34] v4l2-ctrls: add v4l2_ctrl_request_hdl_find/put/ctrl_find functions Hans Verkuil
2018-08-13 11:07   ` Mauro Carvalho Chehab
2018-08-14  8:45     ` Hans Verkuil
2018-08-14  8:55       ` Mauro Carvalho Chehab
2018-08-14 10:50         ` Hans Verkuil
2018-08-04 12:45 ` [PATCHv17 17/34] vb2: store userspace data in vb2_v4l2_buffer Hans Verkuil
2018-08-13 11:15   ` Mauro Carvalho Chehab
2018-08-04 12:45 ` [PATCHv17 18/34] davinci_vpfe: remove bogus vb2->state check Hans Verkuil
2018-08-13 11:17   ` Mauro Carvalho Chehab
2018-08-04 12:45 ` [PATCHv17 19/34] vb2: drop VB2_BUF_STATE_PREPARED, use bool prepared/synced instead Hans Verkuil
2018-08-13 11:30   ` Mauro Carvalho Chehab
2018-08-14  8:58     ` Hans Verkuil
2018-08-14  9:06       ` Mauro Carvalho Chehab
2018-08-04 12:45 ` [PATCHv17 20/34] videodev2.h: Add request_fd field to v4l2_buffer Hans Verkuil
2018-08-13 11:41   ` Mauro Carvalho Chehab
2018-08-04 12:45 ` [PATCHv17 21/34] vb2: add init_buffer buffer op Hans Verkuil
2018-08-13 11:56   ` Mauro Carvalho Chehab
2018-08-04 12:45 ` [PATCHv17 22/34] videobuf2-core: embed media_request_object Hans Verkuil
2018-08-13 11:58   ` Mauro Carvalho Chehab
2018-08-04 12:45 ` [PATCHv17 23/34] videobuf2-core: integrate with media requests Hans Verkuil
2018-08-13 12:09   ` Mauro Carvalho Chehab
2018-08-04 12:45 ` [PATCHv17 24/34] videobuf2-v4l2: " Hans Verkuil
2018-08-13 14:30   ` Mauro Carvalho Chehab
2018-08-04 12:45 ` [PATCHv17 25/34] videobuf2-core: add request helper functions Hans Verkuil
2018-08-13 14:50   ` Mauro Carvalho Chehab
2018-08-14  7:22     ` Hans Verkuil
2018-08-04 12:45 ` [PATCHv17 26/34] videobuf2-v4l2: add vb2_request_queue/validate helpers Hans Verkuil
2018-08-13 14:53   ` Mauro Carvalho Chehab
2018-08-14  7:19     ` Hans Verkuil
2018-08-14  7:49       ` Mauro Carvalho Chehab
2018-08-04 12:45 ` [PATCHv17 27/34] videobuf2-core: add uses_requests/qbuf flags Hans Verkuil
2018-08-13 14:55   ` Mauro Carvalho Chehab
2018-08-04 12:45 ` [PATCHv17 28/34] videobuf2-v4l2: refuse qbuf if queue uses requests or vv Hans Verkuil
2018-08-13 14:56   ` Mauro Carvalho Chehab
2018-08-04 12:45 ` [PATCHv17 29/34] v4l2-mem2mem: add vb2_m2m_request_queue Hans Verkuil
2018-08-13 15:02   ` Mauro Carvalho Chehab
2018-08-14  7:26     ` Hans Verkuil
2018-08-04 12:45 ` [PATCHv17 30/34] vim2m: use workqueue Hans Verkuil
2018-08-13 15:05   ` Mauro Carvalho Chehab
2018-08-14  7:28     ` Hans Verkuil
2018-08-14  7:41       ` Mauro Carvalho Chehab
2018-08-04 12:45 ` [PATCHv17 31/34] vim2m: support requests Hans Verkuil
2018-08-06 21:02   ` Ezequiel Garcia
2018-08-13 15:08   ` Mauro Carvalho Chehab
2018-08-04 12:45 ` [PATCHv17 32/34] vivid: add mc Hans Verkuil
2018-08-13 15:09   ` Mauro Carvalho Chehab
2018-08-04 12:45 ` [PATCHv17 33/34] vivid: add request support Hans Verkuil
2018-08-13 15:11   ` Mauro Carvalho Chehab
2018-08-04 12:45 ` [PATCHv17 34/34] RFC: media-requests: add debugfs node Hans Verkuil
2018-08-13 15:15   ` Mauro Carvalho Chehab
2018-08-14  7:33     ` Hans Verkuil
2018-08-14  7:43       ` Mauro Carvalho Chehab

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.