Re: [RFC] Request API

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 03/23/2018 05:02 AM, Tomasz Figa wrote:
> On Fri, Mar 23, 2018 at 1:28 AM, Hans Verkuil <hverkuil@xxxxxxxxx> wrote:
>> On 03/22/18 15:47, Tomasz Figa wrote:
>>> Hi Hans,
>>>
>>> On Thu, Mar 22, 2018 at 11:18 PM, Hans Verkuil <hverkuil@xxxxxxxxx> wrote:
>>>>
>>>>    Requests only contain the changes since the previously queued request or
>>>>    since the current hardware state if no other requests are queued.
>>>
>>> Does this mean that a request is tied to certain hardware state from
>>> some point of time? (allocation? queue?) How does it affect the
>>> ability to create multiple requests beforehand, without queuing any?
>>>
>>> My original understanding was that a request would be more like a set
>>> of state change actions (S_CTRL, QBUF), that would apply over any
>>> state that is there at request execution time. As such, requests could
>>> be prepared beforehand (or in parallel), without worrying about
>>> current driver state, and then possibly used in a cyclic fashion.
>>
>> Almost correct, except that it applies over any state that is there at
>> request queue time. Easiest it probably to give an example:
>>
>> Say you have two controls: C1 and C2. They are initialized in the driver
>> to 0.
>>
>> You allocate three requests: R1, R2 and R3.
>>
>> You set control C1 to 1 in R1 and to 3 in R3.
>> You never set control C2 in the request.
>>
>> Now you queue requests R1, R2 and R3.
>>
>> When R1 is applied it sets C1 to 1. When R2 is applied it doesn't change
>> anything. When R3 is applied it sets C1 to 3.
>>
>> Calling VIDIOC_G_EXT_CTRLS for R1 will give C1 = 1 and C2 = 0. R2 returns
>> C1 = 1 and C2 = 0 and R3 returns C1 = 3 and C2 = 0.
>>
>> So each request just has the diff against the previously queued request or
>> (if the control in question has never been set in any of the queued requests)
>> it returns the current HW value.
> 
> I think this matches my original understanding actually, was just
> confused by the wording. Could we perhaps reword your sentence a bit,
> to avoid indicating any dependencies on other requests or hardware
> state at the time of request creation (i.e. allocation and setting the
> state, e.g. S_CTRL, QBUF)? How about the following?
> 
>     Requests only contain changes to the state, which would be applied
> at request
>     queue time on top of the previously queued request or current
> hardware state,
>     if no other requests are queued.

Sounds good.

> 
>>
>> Note that this is not yet implemented in the control framework. I have a good
>> idea how to do this, but I'm waiting for a stable patch series to work on.
>>
>> I believe today in the v4 patch series all the controls are just cloned when
>> you allocate a request, right? That's just a temporary solution that will be
>> replaced later using the approach detailed above.
> 
> I don't think cloning all the controls was the intention, but maybe I
> missed something.

I believe it was an 'intentional temporary measure' :-)

Since the control framework doesn't yet support the mechanism described
above it was the easiest way to get things up and running.

> 
>>
>>>
>>>>
>>>> 3) To associate a v4l2 buffer with a request the 'reserved' field in struct
>>>>    v4l2_buffer is used to store the request fd. Buffers won't be 'prepared'
>>>>    until the request is queued since the request may contain information that
>>>>    is needed to prepare the buffer.
>>>>
>>>>    Queuing a buffer without a request after a buffer with a request is equivalent
>>>>    to queuing a request containing just that buffer and nothing else. I.e. it will
>>>>    just use whatever values the hardware has at the time of processing.
>>>>
>>>> 4) To associate v4l2 controls with a request we take the first of the
>>>>    'reserved[2]' array elements in struct v4l2_ext_controls and use it to store
>>>>    the request fd.
>>>>
>>>>    When querying a control value from a request it will return the newest
>>>>    value in the list of pending requests, or the current hardware value if
>>>>    is not set in any of the pending requests.
>>>
>>> What does it mean to "query a control value from a request"? The first
>>> thing that comes to my mind is checking the value that was earlier set
>>> to that particular request (and maybe -EBUSY if it wasn't set?).
>>
>> Calling VIDIOC_G_EXT_CTRLS with a non-zero request_fd.
>>
>> It's useful to refer to what was discussed in Prague:
>>
>> "A request object is essentially created empty from the point of view of the
>> user. Only values that are changed in the request are part of the request,
>> other values remain unchanged (unless those values change due to a
>> side-effect of setting a request value). When a request completes, make a
>> copy of the volatile controls (since that's the value at that point in
>> time). This is needed for auto-<whatever> functions that need to know such
>> volatile values."
>>
>> Once a request has been queued it is fairly easy: VIDIOC_G_EXT_CTRLS
>> will return the values as will be applied, either by the request or an
>> already pending request, or the hardware.
>>
>> Once the request completed it will return the values at the moment of
>> completion (including copies of the volatile controls at that moment).
>>
>> Where I am uncertain is what to return when the request hasn't been
>> queued yet. If a control is not set in the request, what do I return?
>> It is not unreasonable to return only those controls that are part of
>> the request and an error for other controls.
>>
>> Hmm, I wish I could draw this on a whiteboard...
>>
>>>
>>> Perhaps we need to consider few separate cases here:
>>>
>>>  1) Query control value within a request. Would return the value that
>>> was earlier set to that particular request (and maybe -EBUSY if it
>>> wasn't set?). Perhaps done with v4l2_ext_controls::which set to
>>> V4L2_CTRL_WHICH_REQUEST and v4l2_ext_controls::reserved[2] to FD of
>>> the request in question.
>>
>> I see no reason to use 'WHICH_REQUEST'. If request_fd is non-zero,
>> then it automatically refers to that request.
> 
> Agreed, with one minor detail. Note that zero is a perfectly valid FD.

That's annoying and will actually require WHICH_REQUEST to indicate that
the request_fd should be used. So WHICH_CUR_VAL will ignore the request_fd
field. I'll update this in a new RFC.

For the same reason we'll need a flag for v4l2_buffers to indicate that
request_fd should be used. This will be similar to what the fence patch
series does.

> 
>>
>> So while not queued I would just return the value that was set earlier
>> and an error for values not set. But I am open for discussion.
> 
> Agreed.
> 
>> Once it is queued the framework will know the value of each control
>> since it has the information of the full queue of requests and you can
>> query all controls.
> 
> Functionally, it sounds reasonable. It would allow checking values of
> other controls used for the request. Implementation-wise, it would
> require latching the values of all controls for all requests, which
> could complicate the code a bit.
> 
>>
>>>
>>>  2) Query value of the control at the newest pending request in
>>> request queue. Would return the current hardware value if is not set
>>> in any of the pending requests. Now, this could be as well done with
>>> v4l2_ext_controls::which set to just V4L2_CTRL_WHICH_CUR, because the
>>> meaning would be the same - the value used with operations queued from
>>> now on. On the other hand, for compatibility (?), one could keep
>>> V4L2_CTRL_WHICH_CUR with its current meaning of current hardware state
>>> and define a new enum, say V4L2_CTRL_WHICH_CUR_REQUEST.
>>
>> If request_fd is not set, then VIDIOC_G_EXT_CTRLS will always return the
>> current hardware state.
> 
> I'm not sure about the usability of this. The value read would be
> subject to change as soon as next request is executed. One could
> argue, though, that userspace shouldn't rely on this functionality if
> it uses requests and instead it should always query the last request
> queued.

I agree, it is prone to race conditions. But it is still useful to give
a snapshot of the state. I don't want to change the semantics of
WHICH_CUR_VAL, after all that's been the same since forever. I think
changing that would be very confusing.

> 
>> Otherwise it will return the value of the control
>> as will be applied or was applied (if the request completed) by that request.
> 
> With the way I suggested, userspace could get the values of latest
> queued request, without the need to explicitly track which request it
> queued recently. Such tracking wouldn't be a big burden, though, since
> it would just mean storing the FD of last submitted request somewhere
> in userspace internal state, so maybe it's not a big problem.
> 
>>
>>>
>>>  3) Query current hardware value. I'm not sure if there is any
>>> practical usage, given 2), because even if we can read current value,
>>> it might instantly change, if next queued requests is launched. In my
>>> personal opinion, 2) could just be used as behavior for
>>> V4L2_CTRL_WHICH_CUR, if requests are used.
>>
>> I don't see the problem here. If you want the current HW value you use
>> VIDIOC_G_EXT_CTRLS without a request fd. That's how it works today,
>> and that won't change.
> 
> It will change, because currently, if you call VIDIOC_G_EXT_CTRLS, the
> value that it returns won't change unless:
>  - the same thread proceeds with further execution, e.g.
> VIDIOC_S_EXT_CTRLS or any other action that could potentially make the
> driver change the value itself,
>  - another thread in your application doing the above,
>  - the control is volatile and the hardware value changes based on
> some external circumstances.
> 
> Now, imagine the following case with Request API:
>  1) Control X defaults at 0,
>  2) Queue request A, which sets control X to 1,
>  3) Queue request B, which sets control X to 2,
>  4) G_EXT_CTRLS(X, request_fd = -1)
> 
> Now, 4) may return 0, 1 or 2, depending on how far the execution
> progressed. Moreover, if it returns 0 or 1, the execution might
> progress shortly and the value returned won't match the reality
> anymore.


It's definitely racy when used in combination with requests, and that
fact should be documented. But to get a quick snapshot (e.g. v4l2-ctl -l)
it is still very useful IMHO.

> 
>>
>>>
>>>>
>>>>    Setting controls without specifying a request fd will just act like it does
>>>>    today: the hardware is immediately updated. This can cause race conditions
>>>>    if the same control is also specified in a queued request: it is not defined
>>>>    which will be set first. It is therefor not a good idea to set the same
>>>>    control directly as well as set it as part of a request.
>>>>
>>>> Notes:
>>>>
>>>> - Earlier versions of this API had a TRY command as well to validate the
>>>>   request. I'm not sure that is useful so I dropped it, but it can easily
>>>>   be added if there is a good use-case for it. Traditionally within V4L the
>>>>   TRY ioctl will also update wrong values to something that works, but that
>>>>   is not the intention here as far as I understand it. So the validation
>>>>   step can also be done when the request is queued and, if it fails, it will
>>>>   just return an error.
>>>>
>>>> - If due to performance reasons we will have to allocate/queue/reinit multiple
>>>>   requests with a single ioctl, then we will have to add new ioctls to the
>>>>   media device. At this moment in time it is not clear that this is really
>>>>   needed and it certainly isn't needed for the stateless codec support that
>>>>   we are looking at now.
>>>
>>> An alternative, maybe a bit crazy, idea would be to allow adding
>>> MEDIA_REQUEST_IOC_QUEUE ioctl to the request itself. This would be
>>> similar to the idea of indirect command buffers in the graphics (GPU)
>>> land. It could for example look like this:
>>>
>>> // One time initialization
>>> bulk_fd = ioctl(..., MEDIA_IOC_REQUEST_ALLOC, ...);
>>> for (i = 0; i < N; ++i) {
>>>     fd[i] = ioctl(..., MEDIA_IOC_REQUEST_ALLOC, ...);
>>>     // Add some state
>>>     ioctl(fd[i], MEDIA_IOC_REQUEST_QUEUE, { .request = bulk_fd });
>>> }
>>>
>>> // Do some work
>>>
>>> ioctl(bulk_fd, MEDIA_IOC_REQUEST_QUEUE); // Queues all the requests at once
>>
>> That doesn't reduce the number of ioctl calls :-)
> 
> Depends on what cases we are talking about. If we have cases of
> queuing the same (big) sets of requests multiple time, then only for
> the first time all the ioctls would be needed. Next time, one only
> needs to queue the bulk_fd.
> 
> Personally, I don't have any good use case that would involve queuing
> many requests instantly and would be affected by the number of ioctls,
> though.

Sakari gave the example of 10 cameras running at 60 fps, thus generating
a very large amount of request ioctls.

But this doesn't apply to stateless codecs, so for now this is something
for the future.

Regards,

	Hans

> 
>>
>> If we want to do that, then we needs ioctls like this:
>>
>> (yeah, bogus syntax, but it gets the idea across)
>>
>> __s32 fd_vector[10];
>>
>> ioctl(media_fd, MEDIA_IOC_REQUEST_BULK_ALLOC, { .n_fds = 10, .fds = fd_vector })
>> ioctl(media_fd, MEDIA_IOC_REQUEST_BULK_QUEUE, { .n_fds = 10, .fds = fd_vector })
>> ioctl(media_fd, MEDIA_IOC_REQUEST_BULK_REINIT, { .n_fds = 10, .fds = fd_vector })
> 
> Yeah, that's the base idea I was aware of and it would indeed work. :)
> 
> Best regards,
> Tomasz
> 




[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux