[libcamera-devel,v3,5/5] libcamera: v4l2_videodevice: Empty the V4L2 buffer cache on streamOff()
diff mbox series

Message ID 20220321102702.1753800-6-naush@raspberrypi.com
State Superseded
Headers show
Series
  • Raspberry Pi: Efficient start/stop/start sequences
Related show

Commit Message

Naushir Patuck March 21, 2022, 10:27 a.m. UTC
When streamOff() is called, ensure the cache entires for the remaining queued
buffers are freed since this will not happen via the dequeueBuffer() mechanism.

Additionally, add a V4L2BufferCache::isEmpty() function and assert that the
cache is empty at the end of the streamOff() call.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Tested-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
---
 include/libcamera/internal/v4l2_videodevice.h |  1 +
 src/libcamera/v4l2_videodevice.cpp            | 16 ++++++++++++++++
 2 files changed, 17 insertions(+)

Comments

Laurent Pinchart March 21, 2022, 1:08 p.m. UTC | #1
Hi Naush,

Thank you for the patch.

On Mon, Mar 21, 2022 at 10:27:02AM +0000, Naushir Patuck via libcamera-devel wrote:
> When streamOff() is called, ensure the cache entires for the remaining queued

s/entires/entries/

> buffers are freed since this will not happen via the dequeueBuffer() mechanism.
> 
> Additionally, add a V4L2BufferCache::isEmpty() function and assert that the
> cache is empty at the end of the streamOff() call.
> 
> Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> Tested-by: David Plowman <david.plowman@raspberrypi.com>
> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> ---
>  include/libcamera/internal/v4l2_videodevice.h |  1 +
>  src/libcamera/v4l2_videodevice.cpp            | 16 ++++++++++++++++
>  2 files changed, 17 insertions(+)
> 
> diff --git a/include/libcamera/internal/v4l2_videodevice.h b/include/libcamera/internal/v4l2_videodevice.h
> index 2d2ccc477c91..37747c0b2f27 100644
> --- a/include/libcamera/internal/v4l2_videodevice.h
> +++ b/include/libcamera/internal/v4l2_videodevice.h
> @@ -126,6 +126,7 @@ public:
>  
>  	int get(const FrameBuffer &buffer);
>  	void put(unsigned int index);
> +	bool isEmpty() const;

I'd move this before get() and put(), as we usually declare the const
query functions first. Same in the .cpp file. This can be handled when
applying.

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

>  private:
>  	class Entry
> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
> index 5f36ee20710d..9da82697e7f0 100644
> --- a/src/libcamera/v4l2_videodevice.cpp
> +++ b/src/libcamera/v4l2_videodevice.cpp
> @@ -262,6 +262,19 @@ void V4L2BufferCache::put(unsigned int index)
>  	cache_[index].free_ = true;
>  }
>  
> +/**
> + * \brief Check if all the entries in the cache are unused
> + */
> +bool V4L2BufferCache::isEmpty() const
> +{
> +	for (auto const &entry : cache_) {
> +		if (!entry.free_)
> +			return false;
> +	}
> +
> +	return true;
> +}
> +
>  V4L2BufferCache::Entry::Entry()
>  	: free_(true), lastUsed_(0)
>  {
> @@ -1832,10 +1845,13 @@ int V4L2VideoDevice::streamOff()
>  	for (auto it : queuedBuffers_) {
>  		FrameBuffer *buffer = it.second;
>  
> +		cache_->put(it.first);
>  		buffer->metadata_.status = FrameMetadata::FrameCancelled;
>  		bufferReady.emit(buffer);
>  	}
>  
> +	ASSERT(cache_->isEmpty());
> +
>  	queuedBuffers_.clear();
>  	fdBufferNotifier_->setEnabled(false);
>  	streaming_ = false;
Kieran Bingham March 21, 2022, 1:34 p.m. UTC | #2
Quoting Naushir Patuck (2022-03-21 10:27:02)
> When streamOff() is called, ensure the cache entires for the remaining queued
> buffers are freed since this will not happen via the dequeueBuffer() mechanism.
> 
> Additionally, add a V4L2BufferCache::isEmpty() function and assert that the
> cache is empty at the end of the streamOff() call.
> 
> Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> Tested-by: David Plowman <david.plowman@raspberrypi.com>
> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>

Ohh I love an assert addition. It catches things. I wonder if these are
good things or bad things to catch:

Processed 31 frames
Buffer received
Buffer received
Buffer received
Buffer received
Buffer received
Buffer received
Buffer received
Buffer received
stderr:
[339:43:53.887319781] [3693294]  WARN CameraSensorProperties camera_sensor_properties.cpp:148 No static properties available for 'Sensor A'
[339:43:53.887388240] [3693294]  WARN CameraSensorProperties camera_sensor_properties.cpp:150 Please consider updating the camera sensor properties database
[339:43:53.887416152] [3693294]  WARN CameraSensor camera_sensor.cpp:411 'Sensor A': Failed to retrieve the camera location
[339:43:54.491916393] [3693294] FATAL default v4l2_videodevice.cpp:1854 /dev/video6[7:cap]: assertion "cache_->isEmpty()" failed in streamOff()
Backtrace:
libcamera::V4L2VideoDevice::streamOff()+0x14c2 (../../src/libcamera/src/libcamera/v4l2_videodevice.cpp:1854)
CaptureAsyncTest::run()+0x2dbb (../../src/libcamera/test/v4l2_videodevice/capture_async.cpp:82)
Test::execute()+0x472 (../../src/libcamera/test/libtest/test.cpp:33)
main+0x2d2 (../../src/libcamera/test/v4l2_videodevice/capture_async.cpp:93)
__libc_start_call_main+0x80 (../sysdeps/nptl/libc_start_call_main.h:58)
__libc_start_main@@GLIBC_2.34+0x7d (../csu/libc-start.c:128)
_start+0x25 (/home/kbingham/iob/libcamera/ci/integrator/builds/unit-tests/test/v4l2_videodevice/capture_async [0x000055a86d271fb5])
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――

38/67 libcamera:v4l2_videodevice / buffer_sharing                OK              6.31s
39/67 libcamera:v4l2_videodevice / v4l2_m2mdevice                FAIL            1.58s   killed by signal 6 SIGABRT
>>> MALLOC_PERTURB_=171 /home/kbingham/iob/libcamera/ci/integrator/builds/unit-tests/test/v4l2_videodevice/v4l2_m2mdevice
――――――――――――――――――――――――――――――――――――― ✀  ―――――――――――――――――――――――――――――――――――――
stdout:



Received capture buffer
Received capture buffer
Received capture buffer
Received capture buffer
stderr:
Output 31 frames
Captured 31 frames
[339:44:02.406962689] [3693463] FATAL default v4l2_videodevice.cpp:1854 /dev/video9[9:cap]: assertion "cache_->isEmpty()" failed in streamOff()
Backtrace:
libcamera::V4L2VideoDevice::streamOff()+0x14c2 (../../src/libcamera/src/libcamera/v4l2_videodevice.cpp:1854)
V4L2M2MDeviceTest::run()+0x523d (../../src/libcamera/test/v4l2_videodevice/v4l2_m2mdevice.cpp:173)
Test::execute()+0x472 (../../src/libcamera/test/libtest/test.cpp:33)
main+0x2bb (../../src/libcamera/test/v4l2_videodevice/v4l2_m2mdevice.cpp:205)
__libc_start_call_main+0x80 (../sysdeps/nptl/libc_start_call_main.h:58)
__libc_start_main@@GLIBC_2.34+0x7d (../csu/libc-start.c:128)
_start+0x25 (/home/kbingham/iob/libcamera/ci/integrator/builds/unit-tests/test/v4l2_videodevice/v4l2_m2mdevice [0x000055b106567015])
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――

--
Kieran



> ---
>  include/libcamera/internal/v4l2_videodevice.h |  1 +
>  src/libcamera/v4l2_videodevice.cpp            | 16 ++++++++++++++++
>  2 files changed, 17 insertions(+)
> 
> diff --git a/include/libcamera/internal/v4l2_videodevice.h b/include/libcamera/internal/v4l2_videodevice.h
> index 2d2ccc477c91..37747c0b2f27 100644
> --- a/include/libcamera/internal/v4l2_videodevice.h
> +++ b/include/libcamera/internal/v4l2_videodevice.h
> @@ -126,6 +126,7 @@ public:
>  
>         int get(const FrameBuffer &buffer);
>         void put(unsigned int index);
> +       bool isEmpty() const;
>  
>  private:
>         class Entry
> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
> index 5f36ee20710d..9da82697e7f0 100644
> --- a/src/libcamera/v4l2_videodevice.cpp
> +++ b/src/libcamera/v4l2_videodevice.cpp
> @@ -262,6 +262,19 @@ void V4L2BufferCache::put(unsigned int index)
>         cache_[index].free_ = true;
>  }
>  
> +/**
> + * \brief Check if all the entries in the cache are unused
> + */
> +bool V4L2BufferCache::isEmpty() const
> +{
> +       for (auto const &entry : cache_) {
> +               if (!entry.free_)
> +                       return false;
> +       }
> +
> +       return true;
> +}
> +
>  V4L2BufferCache::Entry::Entry()
>         : free_(true), lastUsed_(0)
>  {
> @@ -1832,10 +1845,13 @@ int V4L2VideoDevice::streamOff()
>         for (auto it : queuedBuffers_) {
>                 FrameBuffer *buffer = it.second;
>  
> +               cache_->put(it.first);
>                 buffer->metadata_.status = FrameMetadata::FrameCancelled;
>                 bufferReady.emit(buffer);
>         }
>  
> +       ASSERT(cache_->isEmpty());
> +
>         queuedBuffers_.clear();
>         fdBufferNotifier_->setEnabled(false);
>         streaming_ = false;
> -- 
> 2.25.1
>
Naushir Patuck March 21, 2022, 1:37 p.m. UTC | #3
On Mon, 21 Mar 2022 at 13:34, Kieran Bingham <
kieran.bingham@ideasonboard.com> wrote:

> Quoting Naushir Patuck (2022-03-21 10:27:02)
> > When streamOff() is called, ensure the cache entires for the remaining
> queued
> > buffers are freed since this will not happen via the dequeueBuffer()
> mechanism.
> >
> > Additionally, add a V4L2BufferCache::isEmpty() function and assert that
> the
> > cache is empty at the end of the streamOff() call.
> >
> > Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> > Tested-by: David Plowman <david.plowman@raspberrypi.com>
> > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
>
> Ohh I love an assert addition. It catches things. I wonder if these are
> good things or bad things to catch:
>

Hmmm... How can this be possible?
Presumably on streamOff(), all buffers must have been dequeued out of the
device
driver, and the cache has to be empty.  Is that an invalid assumption?

Naush


>
> Processed 31 frames
> Buffer received
> Buffer received
> Buffer received
> Buffer received
> Buffer received
> Buffer received
> Buffer received
> Buffer received
> stderr:
> [339:43:53.887319781] [3693294]  WARN CameraSensorProperties
> camera_sensor_properties.cpp:148 No static properties available for 'Sensor
> A'
> [339:43:53.887388240] [3693294]  WARN CameraSensorProperties
> camera_sensor_properties.cpp:150 Please consider updating the camera sensor
> properties database
> [339:43:53.887416152] [3693294]  WARN CameraSensor camera_sensor.cpp:411
> 'Sensor A': Failed to retrieve the camera location
> [339:43:54.491916393] [3693294] FATAL default v4l2_videodevice.cpp:1854
> /dev/video6[7:cap]: assertion "cache_->isEmpty()" failed in streamOff()
> Backtrace:
> libcamera::V4L2VideoDevice::streamOff()+0x14c2
> (../../src/libcamera/src/libcamera/v4l2_videodevice.cpp:1854)
> CaptureAsyncTest::run()+0x2dbb
> (../../src/libcamera/test/v4l2_videodevice/capture_async.cpp:82)
> Test::execute()+0x472 (../../src/libcamera/test/libtest/test.cpp:33)
> main+0x2d2 (../../src/libcamera/test/v4l2_videodevice/capture_async.cpp:93)
> __libc_start_call_main+0x80 (../sysdeps/nptl/libc_start_call_main.h:58)
> __libc_start_main@@GLIBC_2.34+0x7d (../csu/libc-start.c:128)
> _start+0x25
> (/home/kbingham/iob/libcamera/ci/integrator/builds/unit-tests/test/v4l2_videodevice/capture_async
> [0x000055a86d271fb5])
>
> ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
>
> 38/67 libcamera:v4l2_videodevice / buffer_sharing                OK
>       6.31s
> 39/67 libcamera:v4l2_videodevice / v4l2_m2mdevice                FAIL
>       1.58s   killed by signal 6 SIGABRT
> >>> MALLOC_PERTURB_=171
> /home/kbingham/iob/libcamera/ci/integrator/builds/unit-tests/test/v4l2_videodevice/v4l2_m2mdevice
> ――――――――――――――――――――――――――――――――――――― ✀
> ―――――――――――――――――――――――――――――――――――――
> stdout:
>
>
>
> Received capture buffer
> Received capture buffer
> Received capture buffer
> Received capture buffer
> stderr:
> Output 31 frames
> Captured 31 frames
> [339:44:02.406962689] [3693463] FATAL default v4l2_videodevice.cpp:1854
> /dev/video9[9:cap]: assertion "cache_->isEmpty()" failed in streamOff()
> Backtrace:
> libcamera::V4L2VideoDevice::streamOff()+0x14c2
> (../../src/libcamera/src/libcamera/v4l2_videodevice.cpp:1854)
> V4L2M2MDeviceTest::run()+0x523d
> (../../src/libcamera/test/v4l2_videodevice/v4l2_m2mdevice.cpp:173)
> Test::execute()+0x472 (../../src/libcamera/test/libtest/test.cpp:33)
> main+0x2bb
> (../../src/libcamera/test/v4l2_videodevice/v4l2_m2mdevice.cpp:205)
> __libc_start_call_main+0x80 (../sysdeps/nptl/libc_start_call_main.h:58)
> __libc_start_main@@GLIBC_2.34+0x7d (../csu/libc-start.c:128)
> _start+0x25
> (/home/kbingham/iob/libcamera/ci/integrator/builds/unit-tests/test/v4l2_videodevice/v4l2_m2mdevice
> [0x000055b106567015])
>
> ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
>
> --
> Kieran
>
>
>
> > ---
> >  include/libcamera/internal/v4l2_videodevice.h |  1 +
> >  src/libcamera/v4l2_videodevice.cpp            | 16 ++++++++++++++++
> >  2 files changed, 17 insertions(+)
> >
> > diff --git a/include/libcamera/internal/v4l2_videodevice.h
> b/include/libcamera/internal/v4l2_videodevice.h
> > index 2d2ccc477c91..37747c0b2f27 100644
> > --- a/include/libcamera/internal/v4l2_videodevice.h
> > +++ b/include/libcamera/internal/v4l2_videodevice.h
> > @@ -126,6 +126,7 @@ public:
> >
> >         int get(const FrameBuffer &buffer);
> >         void put(unsigned int index);
> > +       bool isEmpty() const;
> >
> >  private:
> >         class Entry
> > diff --git a/src/libcamera/v4l2_videodevice.cpp
> b/src/libcamera/v4l2_videodevice.cpp
> > index 5f36ee20710d..9da82697e7f0 100644
> > --- a/src/libcamera/v4l2_videodevice.cpp
> > +++ b/src/libcamera/v4l2_videodevice.cpp
> > @@ -262,6 +262,19 @@ void V4L2BufferCache::put(unsigned int index)
> >         cache_[index].free_ = true;
> >  }
> >
> > +/**
> > + * \brief Check if all the entries in the cache are unused
> > + */
> > +bool V4L2BufferCache::isEmpty() const
> > +{
> > +       for (auto const &entry : cache_) {
> > +               if (!entry.free_)
> > +                       return false;
> > +       }
> > +
> > +       return true;
> > +}
> > +
> >  V4L2BufferCache::Entry::Entry()
> >         : free_(true), lastUsed_(0)
> >  {
> > @@ -1832,10 +1845,13 @@ int V4L2VideoDevice::streamOff()
> >         for (auto it : queuedBuffers_) {
> >                 FrameBuffer *buffer = it.second;
> >
> > +               cache_->put(it.first);
> >                 buffer->metadata_.status = FrameMetadata::FrameCancelled;
> >                 bufferReady.emit(buffer);
> >         }
> >
> > +       ASSERT(cache_->isEmpty());
> > +
> >         queuedBuffers_.clear();
> >         fdBufferNotifier_->setEnabled(false);
> >         streaming_ = false;
> > --
> > 2.25.1
> >
>
Laurent Pinchart March 21, 2022, 1:52 p.m. UTC | #4
On Mon, Mar 21, 2022 at 01:37:11PM +0000, Naushir Patuck via libcamera-devel wrote:
> On Mon, 21 Mar 2022 at 13:34, Kieran Bingham wrote:
> > Quoting Naushir Patuck (2022-03-21 10:27:02)
> > > When streamOff() is called, ensure the cache entires for the remaining queued
> > > buffers are freed since this will not happen via the dequeueBuffer() mechanism.
> > >
> > > Additionally, add a V4L2BufferCache::isEmpty() function and assert that the
> > > cache is empty at the end of the streamOff() call.
> > >
> > > Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> > > Tested-by: David Plowman <david.plowman@raspberrypi.com>
> > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> >
> > Ohh I love an assert addition. It catches things. I wonder if these are
> > good things or bad things to catch:
> 
> Hmmm... How can this be possible?
> Presumably on streamOff(), all buffers must have been dequeued out of the device
> driver, and the cache has to be empty.  Is that an invalid assumption?

It doesn't sound invalid, but there's clearly a problem :-) You can
reproduce this by running unit tests with "ninja test" (the assertion
fails when running tests on an x86 machine with vimc, vim2m and vidid).

And while at that, I forgot to note during review that the buffer cache
unit test (test/v4l2_videodevice/buffer_cache.cpp) should be extended to
test the new isEmpty() function.

> > Processed 31 frames
> > Buffer received
> > Buffer received
> > Buffer received
> > Buffer received
> > Buffer received
> > Buffer received
> > Buffer received
> > Buffer received
> > stderr:
> > [339:43:53.887319781] [3693294]  WARN CameraSensorProperties camera_sensor_properties.cpp:148 No static properties available for 'Sensor A'
> > [339:43:53.887388240] [3693294]  WARN CameraSensorProperties camera_sensor_properties.cpp:150 Please consider updating the camera sensor properties database
> > [339:43:53.887416152] [3693294]  WARN CameraSensor camera_sensor.cpp:411 'Sensor A': Failed to retrieve the camera location
> > [339:43:54.491916393] [3693294] FATAL default v4l2_videodevice.cpp:1854 /dev/video6[7:cap]: assertion "cache_->isEmpty()" failed in streamOff()
> > Backtrace:
> > libcamera::V4L2VideoDevice::streamOff()+0x14c2 (../../src/libcamera/src/libcamera/v4l2_videodevice.cpp:1854)
> > CaptureAsyncTest::run()+0x2dbb (../../src/libcamera/test/v4l2_videodevice/capture_async.cpp:82)
> > Test::execute()+0x472 (../../src/libcamera/test/libtest/test.cpp:33)
> > main+0x2d2 (../../src/libcamera/test/v4l2_videodevice/capture_async.cpp:93)
> > __libc_start_call_main+0x80 (../sysdeps/nptl/libc_start_call_main.h:58)
> > __libc_start_main@@GLIBC_2.34+0x7d (../csu/libc-start.c:128)
> > _start+0x25 (/home/kbingham/iob/libcamera/ci/integrator/builds/unit-tests/test/v4l2_videodevice/capture_async
> > [0x000055a86d271fb5])
> >
> > ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
> >
> > 38/67 libcamera:v4l2_videodevice / buffer_sharing                OK
> >       6.31s
> > 39/67 libcamera:v4l2_videodevice / v4l2_m2mdevice                FAIL
> >       1.58s   killed by signal 6 SIGABRT
> > >>> MALLOC_PERTURB_=171
> > /home/kbingham/iob/libcamera/ci/integrator/builds/unit-tests/test/v4l2_videodevice/v4l2_m2mdevice
> > ――――――――――――――――――――――――――――――――――――― ✀
> > ―――――――――――――――――――――――――――――――――――――
> > stdout:
> >
> >
> >
> > Received capture buffer
> > Received capture buffer
> > Received capture buffer
> > Received capture buffer
> > stderr:
> > Output 31 frames
> > Captured 31 frames
> > [339:44:02.406962689] [3693463] FATAL default v4l2_videodevice.cpp:1854 /dev/video9[9:cap]: assertion "cache_->isEmpty()" failed in streamOff()
> > Backtrace:
> > libcamera::V4L2VideoDevice::streamOff()+0x14c2 (../../src/libcamera/src/libcamera/v4l2_videodevice.cpp:1854)
> > V4L2M2MDeviceTest::run()+0x523d (../../src/libcamera/test/v4l2_videodevice/v4l2_m2mdevice.cpp:173)
> > Test::execute()+0x472 (../../src/libcamera/test/libtest/test.cpp:33)
> > main+0x2bb (../../src/libcamera/test/v4l2_videodevice/v4l2_m2mdevice.cpp:205)
> > __libc_start_call_main+0x80 (../sysdeps/nptl/libc_start_call_main.h:58)
> > __libc_start_main@@GLIBC_2.34+0x7d (../csu/libc-start.c:128)
> > _start+0x25 (/home/kbingham/iob/libcamera/ci/integrator/builds/unit-tests/test/v4l2_videodevice/v4l2_m2mdevice
> > [0x000055b106567015])
> >
> > ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
> >
> > > ---
> > >  include/libcamera/internal/v4l2_videodevice.h |  1 +
> > >  src/libcamera/v4l2_videodevice.cpp            | 16 ++++++++++++++++
> > >  2 files changed, 17 insertions(+)
> > >
> > > diff --git a/include/libcamera/internal/v4l2_videodevice.h
> > b/include/libcamera/internal/v4l2_videodevice.h
> > > index 2d2ccc477c91..37747c0b2f27 100644
> > > --- a/include/libcamera/internal/v4l2_videodevice.h
> > > +++ b/include/libcamera/internal/v4l2_videodevice.h
> > > @@ -126,6 +126,7 @@ public:
> > >
> > >         int get(const FrameBuffer &buffer);
> > >         void put(unsigned int index);
> > > +       bool isEmpty() const;
> > >
> > >  private:
> > >         class Entry
> > > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
> > > index 5f36ee20710d..9da82697e7f0 100644
> > > --- a/src/libcamera/v4l2_videodevice.cpp
> > > +++ b/src/libcamera/v4l2_videodevice.cpp
> > > @@ -262,6 +262,19 @@ void V4L2BufferCache::put(unsigned int index)
> > >         cache_[index].free_ = true;
> > >  }
> > >
> > > +/**
> > > + * \brief Check if all the entries in the cache are unused
> > > + */
> > > +bool V4L2BufferCache::isEmpty() const
> > > +{
> > > +       for (auto const &entry : cache_) {
> > > +               if (!entry.free_)
> > > +                       return false;
> > > +       }
> > > +
> > > +       return true;
> > > +}
> > > +
> > >  V4L2BufferCache::Entry::Entry()
> > >         : free_(true), lastUsed_(0)
> > >  {
> > > @@ -1832,10 +1845,13 @@ int V4L2VideoDevice::streamOff()
> > >         for (auto it : queuedBuffers_) {
> > >                 FrameBuffer *buffer = it.second;
> > >
> > > +               cache_->put(it.first);
> > >                 buffer->metadata_.status = FrameMetadata::FrameCancelled;
> > >                 bufferReady.emit(buffer);
> > >         }
> > >
> > > +       ASSERT(cache_->isEmpty());
> > > +
> > >         queuedBuffers_.clear();
> > >         fdBufferNotifier_->setEnabled(false);
> > >         streaming_ = false;
Kieran Bingham March 21, 2022, 2:01 p.m. UTC | #5
Quoting Naushir Patuck (2022-03-21 13:37:11)
> On Mon, 21 Mar 2022 at 13:34, Kieran Bingham <
> kieran.bingham@ideasonboard.com> wrote:
> 
> > Quoting Naushir Patuck (2022-03-21 10:27:02)
> > > When streamOff() is called, ensure the cache entires for the remaining
> > queued
> > > buffers are freed since this will not happen via the dequeueBuffer()
> > mechanism.
> > >
> > > Additionally, add a V4L2BufferCache::isEmpty() function and assert that
> > the
> > > cache is empty at the end of the streamOff() call.
> > >
> > > Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> > > Tested-by: David Plowman <david.plowman@raspberrypi.com>
> > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> >
> > Ohh I love an assert addition. It catches things. I wonder if these are
> > good things or bad things to catch:
> >
> 
> Hmmm... How can this be possible?

I haven't looked close enough yet, but on the capture_async case, I
think the (application/test) code is requeing buffers unconditionally
when they come back - without caring if they are cancelled - so it's
never considering that we are shutting down.

We might want to make sure that when we call streamOff we set some flag
in V4L2VideoDevice that then prevents queueing buffers... as long as
that's expected...

It seems the V4L2 M2M test case is doing the same - unconditionally
requeueing buffers as long as it receives them.

While an application shouldn't do this - it 'can' so I think it
highlights that we should be more defensive in the library and make sure
those calls to queueBuffer fail with an error.

As soon as we call streamOff we should enter a Stopping state and fail
to accept any new calls to queueBuffer I believe.

--
Kieran

> Presumably on streamOff(), all buffers must have been dequeued out of the
> device
> driver, and the cache has to be empty.  Is that an invalid assumption?
> 
> Naush
> 
> 
> >
> > Processed 31 frames
> > Buffer received
> > Buffer received
> > Buffer received
> > Buffer received
> > Buffer received
> > Buffer received
> > Buffer received
> > Buffer received
> > stderr:
> > [339:43:53.887319781] [3693294]  WARN CameraSensorProperties
> > camera_sensor_properties.cpp:148 No static properties available for 'Sensor
> > A'
> > [339:43:53.887388240] [3693294]  WARN CameraSensorProperties
> > camera_sensor_properties.cpp:150 Please consider updating the camera sensor
> > properties database
> > [339:43:53.887416152] [3693294]  WARN CameraSensor camera_sensor.cpp:411
> > 'Sensor A': Failed to retrieve the camera location
> > [339:43:54.491916393] [3693294] FATAL default v4l2_videodevice.cpp:1854
> > /dev/video6[7:cap]: assertion "cache_->isEmpty()" failed in streamOff()
> > Backtrace:
> > libcamera::V4L2VideoDevice::streamOff()+0x14c2
> > (../../src/libcamera/src/libcamera/v4l2_videodevice.cpp:1854)
> > CaptureAsyncTest::run()+0x2dbb
> > (../../src/libcamera/test/v4l2_videodevice/capture_async.cpp:82)
> > Test::execute()+0x472 (../../src/libcamera/test/libtest/test.cpp:33)
> > main+0x2d2 (../../src/libcamera/test/v4l2_videodevice/capture_async.cpp:93)
> > __libc_start_call_main+0x80 (../sysdeps/nptl/libc_start_call_main.h:58)
> > __libc_start_main@@GLIBC_2.34+0x7d (../csu/libc-start.c:128)
> > _start+0x25
> > (/home/kbingham/iob/libcamera/ci/integrator/builds/unit-tests/test/v4l2_videodevice/capture_async
> > [0x000055a86d271fb5])
> >
> > ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
> >
> > 38/67 libcamera:v4l2_videodevice / buffer_sharing                OK
> >       6.31s
> > 39/67 libcamera:v4l2_videodevice / v4l2_m2mdevice                FAIL
> >       1.58s   killed by signal 6 SIGABRT
> > >>> MALLOC_PERTURB_=171
> > /home/kbingham/iob/libcamera/ci/integrator/builds/unit-tests/test/v4l2_videodevice/v4l2_m2mdevice
> > ――――――――――――――――――――――――――――――――――――― ✀
> > ―――――――――――――――――――――――――――――――――――――
> > stdout:
> >
> >
> >
> > Received capture buffer
> > Received capture buffer
> > Received capture buffer
> > Received capture buffer
> > stderr:
> > Output 31 frames
> > Captured 31 frames
> > [339:44:02.406962689] [3693463] FATAL default v4l2_videodevice.cpp:1854
> > /dev/video9[9:cap]: assertion "cache_->isEmpty()" failed in streamOff()
> > Backtrace:
> > libcamera::V4L2VideoDevice::streamOff()+0x14c2
> > (../../src/libcamera/src/libcamera/v4l2_videodevice.cpp:1854)
> > V4L2M2MDeviceTest::run()+0x523d
> > (../../src/libcamera/test/v4l2_videodevice/v4l2_m2mdevice.cpp:173)
> > Test::execute()+0x472 (../../src/libcamera/test/libtest/test.cpp:33)
> > main+0x2bb
> > (../../src/libcamera/test/v4l2_videodevice/v4l2_m2mdevice.cpp:205)
> > __libc_start_call_main+0x80 (../sysdeps/nptl/libc_start_call_main.h:58)
> > __libc_start_main@@GLIBC_2.34+0x7d (../csu/libc-start.c:128)
> > _start+0x25
> > (/home/kbingham/iob/libcamera/ci/integrator/builds/unit-tests/test/v4l2_videodevice/v4l2_m2mdevice
> > [0x000055b106567015])
> >
> > ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
> >
> > --
> > Kieran
> >
> >
> >
> > > ---
> > >  include/libcamera/internal/v4l2_videodevice.h |  1 +
> > >  src/libcamera/v4l2_videodevice.cpp            | 16 ++++++++++++++++
> > >  2 files changed, 17 insertions(+)
> > >
> > > diff --git a/include/libcamera/internal/v4l2_videodevice.h
> > b/include/libcamera/internal/v4l2_videodevice.h
> > > index 2d2ccc477c91..37747c0b2f27 100644
> > > --- a/include/libcamera/internal/v4l2_videodevice.h
> > > +++ b/include/libcamera/internal/v4l2_videodevice.h
> > > @@ -126,6 +126,7 @@ public:
> > >
> > >         int get(const FrameBuffer &buffer);
> > >         void put(unsigned int index);
> > > +       bool isEmpty() const;
> > >
> > >  private:
> > >         class Entry
> > > diff --git a/src/libcamera/v4l2_videodevice.cpp
> > b/src/libcamera/v4l2_videodevice.cpp
> > > index 5f36ee20710d..9da82697e7f0 100644
> > > --- a/src/libcamera/v4l2_videodevice.cpp
> > > +++ b/src/libcamera/v4l2_videodevice.cpp
> > > @@ -262,6 +262,19 @@ void V4L2BufferCache::put(unsigned int index)
> > >         cache_[index].free_ = true;
> > >  }
> > >
> > > +/**
> > > + * \brief Check if all the entries in the cache are unused
> > > + */
> > > +bool V4L2BufferCache::isEmpty() const
> > > +{
> > > +       for (auto const &entry : cache_) {
> > > +               if (!entry.free_)
> > > +                       return false;
> > > +       }
> > > +
> > > +       return true;
> > > +}
> > > +
> > >  V4L2BufferCache::Entry::Entry()
> > >         : free_(true), lastUsed_(0)
> > >  {
> > > @@ -1832,10 +1845,13 @@ int V4L2VideoDevice::streamOff()
> > >         for (auto it : queuedBuffers_) {
> > >                 FrameBuffer *buffer = it.second;
> > >
> > > +               cache_->put(it.first);
> > >                 buffer->metadata_.status = FrameMetadata::FrameCancelled;
> > >                 bufferReady.emit(buffer);
> > >         }
> > >
> > > +       ASSERT(cache_->isEmpty());
> > > +
> > >         queuedBuffers_.clear();
> > >         fdBufferNotifier_->setEnabled(false);
> > >         streaming_ = false;
> > > --
> > > 2.25.1
> > >
> >
Naushir Patuck March 21, 2022, 4:05 p.m. UTC | #6
On Mon, 21 Mar 2022 at 14:01, Kieran Bingham <
kieran.bingham@ideasonboard.com> wrote:

> Quoting Naushir Patuck (2022-03-21 13:37:11)
> > On Mon, 21 Mar 2022 at 13:34, Kieran Bingham <
> > kieran.bingham@ideasonboard.com> wrote:
> >
> > > Quoting Naushir Patuck (2022-03-21 10:27:02)
> > > > When streamOff() is called, ensure the cache entires for the
> remaining
> > > queued
> > > > buffers are freed since this will not happen via the dequeueBuffer()
> > > mechanism.
> > > >
> > > > Additionally, add a V4L2BufferCache::isEmpty() function and assert
> that
> > > the
> > > > cache is empty at the end of the streamOff() call.
> > > >
> > > > Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> > > > Tested-by: David Plowman <david.plowman@raspberrypi.com>
> > > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > >
> > > Ohh I love an assert addition. It catches things. I wonder if these are
> > > good things or bad things to catch:
> > >
> >
> > Hmmm... How can this be possible?
>
> I haven't looked close enough yet, but on the capture_async case, I
> think the (application/test) code is requeing buffers unconditionally
> when they come back - without caring if they are cancelled - so it's
> never considering that we are shutting down.
>
> We might want to make sure that when we call streamOff we set some flag
> in V4L2VideoDevice that then prevents queueing buffers... as long as
> that's expected...
>
> It seems the V4L2 M2M test case is doing the same - unconditionally
> requeueing buffers as long as it receives them.
>

> While an application shouldn't do this - it 'can' so I think it
> highlights that we should be more defensive in the library and make sure
> those calls to queueBuffer fail with an error.
>
> As soon as we call streamOff we should enter a Stopping state and fail
> to accept any new calls to queueBuffer I believe.
>

Presumably in V4L2 you are allowed to queue buffers if the device is in a
"stream off" state?  If so, I can't catch this in
V4L2VideoDevice::queueBuffer()
with a simple test of if (!streaming_) ...

Hmmm, perhaps the ASSERT() test is not actually valid then?


>
> --
> Kieran
>
> > Presumably on streamOff(), all buffers must have been dequeued out of the
> > device
> > driver, and the cache has to be empty.  Is that an invalid assumption?
> >
> > Naush
> >
> >
> > >
> > > Processed 31 frames
> > > Buffer received
> > > Buffer received
> > > Buffer received
> > > Buffer received
> > > Buffer received
> > > Buffer received
> > > Buffer received
> > > Buffer received
> > > stderr:
> > > [339:43:53.887319781] [3693294]  WARN CameraSensorProperties
> > > camera_sensor_properties.cpp:148 No static properties available for
> 'Sensor
> > > A'
> > > [339:43:53.887388240] [3693294]  WARN CameraSensorProperties
> > > camera_sensor_properties.cpp:150 Please consider updating the camera
> sensor
> > > properties database
> > > [339:43:53.887416152] [3693294]  WARN CameraSensor
> camera_sensor.cpp:411
> > > 'Sensor A': Failed to retrieve the camera location
> > > [339:43:54.491916393] [3693294] FATAL default v4l2_videodevice.cpp:1854
> > > /dev/video6[7:cap]: assertion "cache_->isEmpty()" failed in streamOff()
> > > Backtrace:
> > > libcamera::V4L2VideoDevice::streamOff()+0x14c2
> > > (../../src/libcamera/src/libcamera/v4l2_videodevice.cpp:1854)
> > > CaptureAsyncTest::run()+0x2dbb
> > > (../../src/libcamera/test/v4l2_videodevice/capture_async.cpp:82)
> > > Test::execute()+0x472 (../../src/libcamera/test/libtest/test.cpp:33)
> > > main+0x2d2
> (../../src/libcamera/test/v4l2_videodevice/capture_async.cpp:93)
> > > __libc_start_call_main+0x80 (../sysdeps/nptl/libc_start_call_main.h:58)
> > > __libc_start_main@@GLIBC_2.34+0x7d (../csu/libc-start.c:128)
> > > _start+0x25
> > >
> (/home/kbingham/iob/libcamera/ci/integrator/builds/unit-tests/test/v4l2_videodevice/capture_async
> > > [0x000055a86d271fb5])
> > >
> > >
> ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
> > >
> > > 38/67 libcamera:v4l2_videodevice / buffer_sharing                OK
> > >       6.31s
> > > 39/67 libcamera:v4l2_videodevice / v4l2_m2mdevice                FAIL
> > >       1.58s   killed by signal 6 SIGABRT
> > > >>> MALLOC_PERTURB_=171
> > >
> /home/kbingham/iob/libcamera/ci/integrator/builds/unit-tests/test/v4l2_videodevice/v4l2_m2mdevice
> > > ――――――――――――――――――――――――――――――――――――― ✀
> > > ―――――――――――――――――――――――――――――――――――――
> > > stdout:
> > >
> > >
> > >
> > > Received capture buffer
> > > Received capture buffer
> > > Received capture buffer
> > > Received capture buffer
> > > stderr:
> > > Output 31 frames
> > > Captured 31 frames
> > > [339:44:02.406962689] [3693463] FATAL default v4l2_videodevice.cpp:1854
> > > /dev/video9[9:cap]: assertion "cache_->isEmpty()" failed in streamOff()
> > > Backtrace:
> > > libcamera::V4L2VideoDevice::streamOff()+0x14c2
> > > (../../src/libcamera/src/libcamera/v4l2_videodevice.cpp:1854)
> > > V4L2M2MDeviceTest::run()+0x523d
> > > (../../src/libcamera/test/v4l2_videodevice/v4l2_m2mdevice.cpp:173)
> > > Test::execute()+0x472 (../../src/libcamera/test/libtest/test.cpp:33)
> > > main+0x2bb
> > > (../../src/libcamera/test/v4l2_videodevice/v4l2_m2mdevice.cpp:205)
> > > __libc_start_call_main+0x80 (../sysdeps/nptl/libc_start_call_main.h:58)
> > > __libc_start_main@@GLIBC_2.34+0x7d (../csu/libc-start.c:128)
> > > _start+0x25
> > >
> (/home/kbingham/iob/libcamera/ci/integrator/builds/unit-tests/test/v4l2_videodevice/v4l2_m2mdevice
> > > [0x000055b106567015])
> > >
> > >
> ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
> > >
> > > --
> > > Kieran
> > >
> > >
> > >
> > > > ---
> > > >  include/libcamera/internal/v4l2_videodevice.h |  1 +
> > > >  src/libcamera/v4l2_videodevice.cpp            | 16 ++++++++++++++++
> > > >  2 files changed, 17 insertions(+)
> > > >
> > > > diff --git a/include/libcamera/internal/v4l2_videodevice.h
> > > b/include/libcamera/internal/v4l2_videodevice.h
> > > > index 2d2ccc477c91..37747c0b2f27 100644
> > > > --- a/include/libcamera/internal/v4l2_videodevice.h
> > > > +++ b/include/libcamera/internal/v4l2_videodevice.h
> > > > @@ -126,6 +126,7 @@ public:
> > > >
> > > >         int get(const FrameBuffer &buffer);
> > > >         void put(unsigned int index);
> > > > +       bool isEmpty() const;
> > > >
> > > >  private:
> > > >         class Entry
> > > > diff --git a/src/libcamera/v4l2_videodevice.cpp
> > > b/src/libcamera/v4l2_videodevice.cpp
> > > > index 5f36ee20710d..9da82697e7f0 100644
> > > > --- a/src/libcamera/v4l2_videodevice.cpp
> > > > +++ b/src/libcamera/v4l2_videodevice.cpp
> > > > @@ -262,6 +262,19 @@ void V4L2BufferCache::put(unsigned int index)
> > > >         cache_[index].free_ = true;
> > > >  }
> > > >
> > > > +/**
> > > > + * \brief Check if all the entries in the cache are unused
> > > > + */
> > > > +bool V4L2BufferCache::isEmpty() const
> > > > +{
> > > > +       for (auto const &entry : cache_) {
> > > > +               if (!entry.free_)
> > > > +                       return false;
> > > > +       }
> > > > +
> > > > +       return true;
> > > > +}
> > > > +
> > > >  V4L2BufferCache::Entry::Entry()
> > > >         : free_(true), lastUsed_(0)
> > > >  {
> > > > @@ -1832,10 +1845,13 @@ int V4L2VideoDevice::streamOff()
> > > >         for (auto it : queuedBuffers_) {
> > > >                 FrameBuffer *buffer = it.second;
> > > >
> > > > +               cache_->put(it.first);
> > > >                 buffer->metadata_.status =
> FrameMetadata::FrameCancelled;
> > > >                 bufferReady.emit(buffer);
> > > >         }
> > > >
> > > > +       ASSERT(cache_->isEmpty());
> > > > +
> > > >         queuedBuffers_.clear();
> > > >         fdBufferNotifier_->setEnabled(false);
> > > >         streaming_ = false;
> > > > --
> > > > 2.25.1
> > > >
> > >
>

Patch
diff mbox series

diff --git a/include/libcamera/internal/v4l2_videodevice.h b/include/libcamera/internal/v4l2_videodevice.h
index 2d2ccc477c91..37747c0b2f27 100644
--- a/include/libcamera/internal/v4l2_videodevice.h
+++ b/include/libcamera/internal/v4l2_videodevice.h
@@ -126,6 +126,7 @@  public:
 
 	int get(const FrameBuffer &buffer);
 	void put(unsigned int index);
+	bool isEmpty() const;
 
 private:
 	class Entry
diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
index 5f36ee20710d..9da82697e7f0 100644
--- a/src/libcamera/v4l2_videodevice.cpp
+++ b/src/libcamera/v4l2_videodevice.cpp
@@ -262,6 +262,19 @@  void V4L2BufferCache::put(unsigned int index)
 	cache_[index].free_ = true;
 }
 
+/**
+ * \brief Check if all the entries in the cache are unused
+ */
+bool V4L2BufferCache::isEmpty() const
+{
+	for (auto const &entry : cache_) {
+		if (!entry.free_)
+			return false;
+	}
+
+	return true;
+}
+
 V4L2BufferCache::Entry::Entry()
 	: free_(true), lastUsed_(0)
 {
@@ -1832,10 +1845,13 @@  int V4L2VideoDevice::streamOff()
 	for (auto it : queuedBuffers_) {
 		FrameBuffer *buffer = it.second;
 
+		cache_->put(it.first);
 		buffer->metadata_.status = FrameMetadata::FrameCancelled;
 		bufferReady.emit(buffer);
 	}
 
+	ASSERT(cache_->isEmpty());
+
 	queuedBuffers_.clear();
 	fdBufferNotifier_->setEnabled(false);
 	streaming_ = false;