All of lore.kernel.org
 help / color / mirror / Atom feed
From: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
To: Hans Verkuil <hverkuil@xs4all.nl>
Cc: linux-media@vger.kernel.org,
	Hans Verkuil <hans.verkuil@cisco.com>,
	Sakari Ailus <sakari.ailus@linux.intel.com>
Subject: Re: [PATCHv18 03/35] media-request: implement media requests
Date: Tue, 14 Aug 2018 16:20:34 -0300	[thread overview]
Message-ID: <20180814162034.26a07871@coco.lan> (raw)
In-Reply-To: <20180814142047.93856-4-hverkuil@xs4all.nl>

Em Tue, 14 Aug 2018 16:20:15 +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>

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

> ---
>  drivers/media/Makefile        |   3 +-
>  drivers/media/media-device.c  |  24 +-
>  drivers/media/media-request.c | 464 ++++++++++++++++++++++++++++++++++
>  include/media/media-device.h  |  29 +++
>  include/media/media-request.h | 334 ++++++++++++++++++++++++
>  5 files changed, 849 insertions(+), 5 deletions(-)
>  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..d78e991bce46 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,10 +378,19 @@ static long media_device_get_topology(struct media_device *mdev, void *arg)
>  	return ret;
>  }
>  
> +static long media_device_request_alloc(struct media_device *mdev,
> +				       int *alloc_fd)
> +{
> +	if (!mdev->ops || !mdev->ops->req_validate || !mdev->ops->req_queue)
> +		return -ENOTTY;
> +
> +	return media_request_alloc(mdev, alloc_fd);
> +}
> +
>  static long copy_arg_from_user(void *karg, void __user *uarg, unsigned int cmd)
>  {
> -	/* All media IOCTLs are _IOWR() */
> -	if (copy_from_user(karg, uarg, _IOC_SIZE(cmd)))
> +	if ((_IOC_DIR(cmd) & _IOC_WRITE) &&
> +	    copy_from_user(karg, uarg, _IOC_SIZE(cmd)))
>  		return -EFAULT;
>  
>  	return 0;
> @@ -388,8 +398,8 @@ static long copy_arg_from_user(void *karg, void __user *uarg, unsigned int cmd)
>  
>  static long copy_arg_to_user(void __user *uarg, void *karg, unsigned int cmd)
>  {
> -	/* All media IOCTLs are _IOWR() */
> -	if (copy_to_user(uarg, karg, _IOC_SIZE(cmd)))
> +	if ((_IOC_DIR(cmd) & _IOC_READ) &&
> +	    copy_to_user(uarg, karg, _IOC_SIZE(cmd)))
>  		return -EFAULT;
>  
>  	return 0;

IMHO, the two above hunks should be there since version 0 :-)

I'm grateful that you remind to change it.

> @@ -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,9 +708,13 @@ 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);
>  
> +	atomic_set(&mdev->request_id, 0);
> +
>  	dev_dbg(mdev->dev, "Media device initialized\n");
>  }
>  EXPORT_SYMBOL_GPL(media_device_init);
> @@ -710,6 +725,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..8d3c7360c8f3
> --- /dev/null
> +++ b/drivers/media/media-request.c
> @@ -0,0 +1,464 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * 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 __poll_t media_request_poll(struct file *filp,
> +				   struct poll_table_struct *wait)
> +{
> +	struct media_request *req = filp->private_data;
> +	unsigned long flags;
> +	__poll_t ret = 0;
> +
> +	if (!(poll_requested_events(wait) & EPOLLPRI))
> +		return 0;
> +
> +	spin_lock_irqsave(&req->lock, flags);
> +	if (req->state == MEDIA_REQUEST_STATE_COMPLETE) {
> +		ret = EPOLLPRI;
> +		goto unlock;
> +	}
> +	if (req->state != MEDIA_REQUEST_STATE_QUEUED) {
> +		ret = EPOLLERR;
> +		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));
> +		media_request_put(req);
> +		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,
> +};
> +
> +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, int *alloc_fd)
> +{
> +	struct media_request *req;
> +	struct file *filp;
> +	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;
> +
> +	snprintf(req->debug_str, sizeof(req->debug_str), "%u:%d",
> +		 atomic_inc_return(&mdev->request_id), 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, bool is_buffer,
> +			      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;
> +
> +	if (is_buffer)
> +		list_add_tail(&obj->list, &req->objects);
> +	else
> +		list_add(&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..c8ddbfe8b74c 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,32 @@ 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.
> + *	       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.
>   */
>  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 +111,9 @@ 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.
> + * @request_id: Used to generate unique request IDs
>   *
>   * 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 +184,9 @@ struct media_device {
>  	void (*disable_source)(struct media_entity *entity);
>  
>  	const struct media_device_ops *ops;
> +
> +	struct mutex req_queue_mutex;
> +	atomic_t request_id;
>  };
>  
>  /* 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..9664ebac5dc4
> --- /dev/null
> +++ b/include/media/media-request.h
> @@ -0,0 +1,334 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * 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_fd: Store the request's file descriptor in this int
> + *
> + * Allocated the media request and put the fd in @alloc_fd.
> + */
> +int media_request_alloc(struct media_device *mdev,
> +			int *alloc_fd);
> +
> +#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
> + * @is_buffer: Set to true if the object a buffer 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.
> + *
> + * Buffer objects will be added to the end of the request's object
> + * list, non-buffer objects will be added to the front of the list.
> + * This ensures that all buffer objects are at the end of the list
> + * and that all non-buffer objects that they depend on are processed
> + * first.
> + */
> +int media_request_object_bind(struct media_request *req,
> +			      const struct media_request_object_ops *ops,
> +			      void *priv, bool is_buffer,
> +			      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, bool is_buffer,
> +			       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

  reply	other threads:[~2018-08-14 22:09 UTC|newest]

Thread overview: 71+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-08-14 14:20 [PATCHv18 00/35] Request API Hans Verkuil
2018-08-14 14:20 ` [PATCHv18 01/35] Documentation: v4l: document request API Hans Verkuil
2018-08-14 19:16   ` Mauro Carvalho Chehab
2018-08-15 16:14   ` Laurent Pinchart
2018-08-16  9:58     ` Hans Verkuil
2018-08-16 10:16       ` Hans Verkuil
2018-08-16 10:28       ` Mauro Carvalho Chehab
2018-11-12 19:06   ` Thomas Gleixner
2018-11-18 13:52     ` Mauro Carvalho Chehab
2018-11-23  9:51       ` Mauro Carvalho Chehab
2018-11-23 10:38         ` Thomas Gleixner
2018-11-23 12:29           ` Mauro Carvalho Chehab
2018-11-23 12:44             ` Thomas Gleixner
2018-11-27 18:54               ` Mauro Carvalho Chehab
2018-11-26  3:27             ` Tomasz Figa
2018-08-14 14:20 ` [PATCHv18 02/35] uapi/linux/media.h: add " Hans Verkuil
2018-08-14 19:17   ` Mauro Carvalho Chehab
2018-08-14 14:20 ` [PATCHv18 03/35] media-request: implement media requests Hans Verkuil
2018-08-14 19:20   ` Mauro Carvalho Chehab [this message]
2018-08-14 14:20 ` [PATCHv18 04/35] media: doc: Add media-request.h header to documentation build Hans Verkuil
2018-08-14 14:20 ` [PATCHv18 05/35] media-request: add media_request_get_by_fd Hans Verkuil
2018-08-14 14:20 ` [PATCHv18 06/35] media-request: add media_request_object_find Hans Verkuil
2018-08-14 14:20 ` [PATCHv18 07/35] v4l2-device.h: add v4l2_device_supports_requests() helper Hans Verkuil
2018-08-24 10:21   ` Sakari Ailus
2018-08-14 14:20 ` [PATCHv18 08/35] v4l2-dev: lock req_queue_mutex Hans Verkuil
2018-08-14 19:22   ` Mauro Carvalho Chehab
2018-08-14 14:20 ` [PATCHv18 09/35] videodev2.h: add request_fd field to v4l2_ext_controls Hans Verkuil
2018-08-14 14:20 ` [PATCHv18 10/35] v4l2-ctrls: v4l2_ctrl_add_handler: add from_other_dev Hans Verkuil
2018-08-14 14:20 ` [PATCHv18 11/35] v4l2-ctrls: prepare internal structs for request API Hans Verkuil
2018-08-14 14:20 ` [PATCHv18 12/35] v4l2-ctrls: alloc memory for p_req Hans Verkuil
2018-08-14 14:20 ` [PATCHv18 13/35] v4l2-ctrls: use ref in helper instead of ctrl Hans Verkuil
2018-08-14 14:20 ` [PATCHv18 14/35] v4l2-ctrls: add core request support Hans Verkuil
2018-08-14 19:27   ` Mauro Carvalho Chehab
2018-08-14 14:20 ` [PATCHv18 15/35] v4l2-ctrls: support g/s_ext_ctrls for requests Hans Verkuil
2018-08-14 19:33   ` Mauro Carvalho Chehab
2018-08-14 14:20 ` [PATCHv18 16/35] v4l2-ctrls: add v4l2_ctrl_request_hdl_find/put/ctrl_find functions Hans Verkuil
2018-08-14 19:34   ` Mauro Carvalho Chehab
2018-08-14 14:20 ` [PATCHv18 17/35] videobuf2-v4l2: move __fill_v4l2_buffer() function Hans Verkuil
2018-08-14 19:36   ` Mauro Carvalho Chehab
2018-08-14 14:20 ` [PATCHv18 18/35] videobuf2-v4l2: replace if by switch in __fill_vb2_buffer() Hans Verkuil
2018-08-15 11:51   ` Mauro Carvalho Chehab
2018-08-14 14:20 ` [PATCHv18 19/35] vb2: store userspace data in vb2_v4l2_buffer Hans Verkuil
2018-08-14 19:47   ` Mauro Carvalho Chehab
2018-08-15 11:54     ` Hans Verkuil
2018-08-15 12:28       ` Mauro Carvalho Chehab
2018-08-15 12:33         ` Hans Verkuil
2018-08-14 14:20 ` [PATCHv18 20/35] davinci_vpfe: remove bogus vb2->state check Hans Verkuil
2018-08-14 14:20 ` [PATCHv18 21/35] vb2: drop VB2_BUF_STATE_PREPARED, use bool prepared/synced instead Hans Verkuil
2018-08-14 19:50   ` Mauro Carvalho Chehab
2018-08-14 14:20 ` [PATCHv18 22/35] videodev2.h: Add request_fd field to v4l2_buffer Hans Verkuil
2018-08-25 12:58   ` Sakari Ailus
2018-08-14 14:20 ` [PATCHv18 23/35] vb2: add init_buffer buffer op Hans Verkuil
2018-08-25 12:58   ` Sakari Ailus
2018-08-14 14:20 ` [PATCHv18 24/35] videobuf2-core: embed media_request_object Hans Verkuil
2018-08-14 19:53   ` Mauro Carvalho Chehab
2018-08-25 13:01   ` Sakari Ailus
2018-08-14 14:20 ` [PATCHv18 25/35] videobuf2-core: integrate with media requests Hans Verkuil
2018-08-14 14:20 ` [PATCHv18 26/35] videobuf2-v4l2: " Hans Verkuil
2018-08-14 14:20 ` [PATCHv18 27/35] videobuf2-core: add request helper functions Hans Verkuil
2018-08-14 14:20 ` [PATCHv18 28/35] videobuf2-v4l2: add vb2_request_queue/validate helpers Hans Verkuil
2018-08-14 19:54   ` Mauro Carvalho Chehab
2018-08-14 14:20 ` [PATCHv18 29/35] videobuf2-core: add uses_requests/qbuf flags Hans Verkuil
2018-08-14 14:20 ` [PATCHv18 30/35] videobuf2-v4l2: refuse qbuf if queue uses requests or vv Hans Verkuil
2018-08-14 14:20 ` [PATCHv18 31/35] v4l2-mem2mem: add vb2_m2m_request_queue Hans Verkuil
2018-08-14 19:56   ` Mauro Carvalho Chehab
2018-08-14 14:20 ` [PATCHv18 32/35] vim2m: use workqueue Hans Verkuil
2018-08-14 14:20 ` [PATCHv18 33/35] vim2m: support requests Hans Verkuil
2018-08-14 14:20 ` [PATCHv18 34/35] vivid: add mc Hans Verkuil
2018-08-14 14:20 ` [PATCHv18 35/35] vivid: add request support Hans Verkuil
2018-08-14 20:01 ` [PATCHv18 00/35] Request API Mauro Carvalho Chehab
2018-08-14 20:04   ` Mauro Carvalho Chehab

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180814162034.26a07871@coco.lan \
    --to=mchehab+samsung@kernel.org \
    --cc=hans.verkuil@cisco.com \
    --cc=hverkuil@xs4all.nl \
    --cc=linux-media@vger.kernel.org \
    --cc=sakari.ailus@linux.intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.