Re: [RFC PATCH 2/9] [media] v4l2-core: add core jobs API support
From: Alexandre Courbot
Date: Mon Oct 23 2017 - 04:36:06 EST
Hi Hans,
On Mon, Oct 16, 2017 at 7:01 PM, Hans Verkuil <hverkuil@xxxxxxxxx> wrote:
>> +static long v4l2_jobqueue_device_do_ioctl(struct file *filp, unsigned int cmd,
>> + void *arg)
>> +{
>> + switch (cmd) {
>> + case VIDIOC_JOBQUEUE_INIT:
>> + return v4l2_jobqueue_ioctl_init(filp, arg);
>> +
>> + case VIDIOC_JOBQUEUE_QJOB:
>> + return v4l2_jobqueue_device_ioctl_qjob(filp, arg);
>> +
>> + case VIDIOC_JOBQUEUE_DQJOB:
>> + return v4l2_jobqueue_device_ioctl_dqjob(filp, arg);
>> +
>> + case VIDIOC_JOBQUEUE_EXPORT_JOB:
>> + return v4l2_jobqueue_device_ioctl_export_job(filp, arg);
>> +
>> + case VIDIOC_JOBQUEUE_IMPORT_JOB:
>> + return v4l2_jobqueue_device_ioctl_import_job(filp, arg);
>
> There really is no need for these stub functions, just inline them for each
> case.
Indeed!
>> diff --git a/drivers/media/v4l2-core/v4l2-jobqueue.c b/drivers/media/v4l2-core/v4l2-jobqueue.c
>> new file mode 100644
>> index 000000000000..36d2dd48b086
>> --- /dev/null
>> +++ b/drivers/media/v4l2-core/v4l2-jobqueue.c
>> @@ -0,0 +1,764 @@
>> +/*
>> + V4L2 job queue implementation
>> +
>> + Copyright (C) 2017 The Chromium project
>> +
>> + This program is free software; you can redistribute it and/or modify
>> + it under the terms of the GNU General Public License as published by
>> + the Free Software Foundation; either version 2 of the License, or
>> + (at your option) any later version.
>> +
>> + This program is distributed in the hope that it will be useful,
>> + but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + GNU General Public License for more details.
>> +
>> + */
>> +
>> +#include <linux/compat.h>
>> +#include <linux/export.h>
>> +#include <linux/string.h>
>> +#include <linux/file.h>
>> +#include <linux/list.h>
>> +#include <linux/kref.h>
>> +#include <linux/anon_inodes.h>
>> +#include <linux/slab.h>
>> +#include <linux/mutex.h>
>> +#include <linux/workqueue.h>
>> +#include <media/v4l2-dev.h>
>> +#include <media/v4l2-fh.h>
>> +#include <media/v4l2-ctrls.h>
>> +#include <media/v4l2-jobqueue.h>
>> +#include <media/v4l2-job-state.h>
>> +#include <uapi/linux/v4l2-jobs.h>
>> +
>> +/* Limited by the size of atomic_t to track devices that completed a job */
>> +#define V4L2_JOBQUEUE_MAX_DEVICES sizeof(atomic_t)
>> +
>> +/*
>> + * State of all managed devices for a given job
>> + */
>> +struct v4l2_job {
>> + struct kref refcount;
>> + struct v4l2_jobqueue *jq;
>> + /* node in v4l2_jobqueue's queued_jobs or completed_jobs */
>> + struct list_head node;
>> + /* global list of existing jobs for this queue */
>> + struct list_head jobs_list;
>> + /* mask of devices that completed this job */
>> + atomic_t completed;
>> + /* fd exported to user-space */
>> + int fd;
>> + enum v4l2_job_status status;
>> +
>> + /* per-device states */
>> + struct v4l2_job_state *state[0];
>> +};
>> +
>> +/*
>> + * A job queue manages the job flow for a given set of devices, applies their
>> + * state, and activates them in lockstep.
>> + *
>> + * A job goes through the following stages through its life:
>> + *
>> + * * current_job: the job has been created and is waiting to be queued. S_CTRL
>> + * will apply to it. Once queued, it is pushed into
>> + * * queued_jobs: a queue of jobs to be processed in sequential order. The head
>> + * of this list becomes the
>> + * * active_job: the job currently being processed by the hardware. Once
>> + * completed, the next job in queued_job becomes active, and the previous
>> + * active job goes into
>> + * * completed_jobs: a list of completed jobs waiting to be dequeued by
>> + * user-space. As user-space called the DQJOB ioctl, the head becomes the
>> + * * dequeued_job: the job on which G_CTRL will be performed on. A job stays
>> + * in this state until another one is dequeued, at which point it is deleted.
>> + */
>> +struct v4l2_jobqueue {
>> + /* List of all jobs created for this queue, regardless of state */
>> + struct list_head jobs_list;
>> + /*
>> + * Job that user-space is currently preparing, to be added to
>> + * queued_jobs upon QJOB ioctl.
>> + */
>> + struct v4l2_job *current_job;
>> +
>> + /* List of jobs that are ready to be processed */
>> + struct list_head queued_jobs;
>> +
>> + /* Job that is currently processed by the devices */
>> + struct v4l2_job *active_job;
>
> Shouldn't this be a list as well? I interpret 'active job' as being a
> job that is passed to the various drivers that need to process it.
>
> Just as with video buffers where the hardware may need a minimum of
> buffers before it can start the DMA (min_buffers_needed), so the same
> is true for jobs. E.g. a driver may have to look ahead by a few frames
> to see what changes are requested. Some changes (esp. sensor related)
> can take a few frames before they take effect and the hardware has to
> be programmed ahead of time.
Regarding the number of queued frames, frames end up being queued into
the VB2 queue even if they are not passed to the driver, so I guess
the driver can still peek at forward frames through that mechanism.
Same can be done for the list of jobs themselves with the state change
listener.
> It would make more sense if this was a list of active jobs. It would
> likely also solve the TODOs you have in the code w.r.t. min_buffers_needed:
> after STREAMON you'd just queue the jobs to the active list, and also
> queue any associated buffers. Once the minimum number of buffers has been
> reached the DMA is started.
Mmm, but wouldn't that kind of defeat the idea of jobs as a single
unit of work if they needed to be grouped before being processed? I
need to think a bit more about that. Maybe we could pass the buffers
to the drivers prior to their job being scheduled, but I like the idea
of having VB2 managing the buffer's flow as it is easy to screw the
flow otherwise.
>> diff --git a/include/uapi/linux/v4l2-jobs.h b/include/uapi/linux/v4l2-jobs.h
>> new file mode 100644
>> index 000000000000..2cba4d20e62f
>> --- /dev/null
>> +++ b/include/uapi/linux/v4l2-jobs.h
>> @@ -0,0 +1,40 @@
>> +/*
>> + * V4L2 jobs API
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#ifndef __LINUX_V4L2_JOBS_H
>> +#define __LINUX_V4L2_JOBS_H
>> +
>> +#ifndef __KERNEL__
>> +#include <stdint.h>
>> +#endif
>> +#include <linux/ioctl.h>
>> +#include <linux/types.h>
>> +
>> +struct v4l2_jobqueue_init {
>> + __u32 nb_devs;
>> + __s32 *fd;
>> +};
>> +
>> +struct v4l2_jobqueue_job {
>> + __s32 fd;
>> +};
>> +
>> +#define VIDIOC_JOBQUEUE_IOCTL_START 0x80
>
> Why this offset?
IIRC this is to avoid collision with other V4L2 ioctls that start at
offset 0. I have not thought this thoroughly however, just wanted to
have something that works for experimenting.
Thanks,
Alex.