[libcamera-devel] [PATCH v3 22/22] v4l2: v4l2_camera_proxy: Serialize accesses to the proxy
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Wed Jun 24 02:02:43 CEST 2020
Hi Paul,
Thank you for the patch.
On Wed, Jun 24, 2020 at 04:08:36AM +0900, Paul Elder wrote:
> Make the V4L2 compatibility layer thread-safe by serializing accesses to
> the V4L2CameraProxy with a lock. Release the lock when blocking for
> dqbuf.
>
> Signed-off-by: Paul Elder <paul.elder at ideasonboard.com>
>
> ---
> New in v3
> ---
> src/v4l2/v4l2_camera_proxy.cpp | 21 +++++++++++++++++----
> src/v4l2/v4l2_camera_proxy.h | 5 ++++-
> 2 files changed, 21 insertions(+), 5 deletions(-)
>
> diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp
> index ed3bcbc..fb3a1fc 100644
> --- a/src/v4l2/v4l2_camera_proxy.cpp
> +++ b/src/v4l2/v4l2_camera_proxy.cpp
> @@ -43,6 +43,8 @@ V4L2CameraProxy::V4L2CameraProxy(unsigned int index,
>
> int V4L2CameraProxy::open(V4L2CameraFile *file)
> {
> + MutexLocker locker(proxyMutex_);
> +
You could move the lock after the LOG() lines to slightly reduced the
amount of code protected by it.
> LOG(V4L2Compat, Debug) << "Servicing open fd = " << file->efd();
>
> files_.insert(file);
> @@ -75,6 +77,8 @@ int V4L2CameraProxy::open(V4L2CameraFile *file)
>
> void V4L2CameraProxy::close(V4L2CameraFile *file)
> {
> + MutexLocker locker(proxyMutex_);
> +
> LOG(V4L2Compat, Debug) << "Servicing close fd = " << file->efd();
>
> files_.erase(file);
> @@ -90,6 +94,8 @@ void V4L2CameraProxy::close(V4L2CameraFile *file)
> void *V4L2CameraProxy::mmap(void *addr, size_t length, int prot, int flags,
> off64_t offset)
> {
> + MutexLocker locker(proxyMutex_);
> +
> LOG(V4L2Compat, Debug) << "Servicing mmap";
>
> /* \todo Validate prot and flags properly. */
> @@ -124,6 +130,8 @@ void *V4L2CameraProxy::mmap(void *addr, size_t length, int prot, int flags,
>
> int V4L2CameraProxy::munmap(void *addr, size_t length)
> {
> + MutexLocker locker(proxyMutex_);
> +
> LOG(V4L2Compat, Debug) << "Servicing munmap";
>
> auto iter = mmaps_.find(addr);
> @@ -592,7 +600,8 @@ int V4L2CameraProxy::vidioc_qbuf(V4L2CameraFile *file, struct v4l2_buffer *arg)
> return ret;
> }
>
> -int V4L2CameraProxy::vidioc_dqbuf(V4L2CameraFile *file, struct v4l2_buffer *arg)
> +int V4L2CameraProxy::vidioc_dqbuf(V4L2CameraFile *file,
> + struct v4l2_buffer *arg, MutexLocker &locker)
We usually passed parameters that can be modified by pointer, not by
reference.
And you could keep the first two parameters on the first line.
> {
> LOG(V4L2Compat, Debug) << "Servicing vidioc_dqbuf fd = " << file->efd();
>
> @@ -609,9 +618,11 @@ int V4L2CameraProxy::vidioc_dqbuf(V4L2CameraFile *file, struct v4l2_buffer *arg)
> !validateMemoryType(arg->memory))
> return -EINVAL;
>
> - if (!(file->nonBlocking()))
> + if (!(file->nonBlocking())) {
> + locker.unlock();
> vcam_->waitForBufferAvailable();
> - else if (!vcam_->isBufferAvailable())
> + locker.lock();
There's a potential race condition here. waitForBufferAvailable() is
implemented as
void V4L2Camera::waitForBufferAvailable()
{
MutexLocker locker(bufferMutex_);
bufferCV_.wait(locker, [&] {
return bufferAvailableCount_ >= 1 || !isRunning_;
});
if (isRunning_)
bufferAvailableCount_--;
}
and in V4L2Camera::streamOff(), you have
isRunning_ = false;
bufferCV_.notify_all();
without taking the bufferMutex_ lock. streamOff() can run in a parallel
thread to waitForBufferAvailable() as you release the locker before
calling waitForBufferAvailable(). Unless I'm mistaken, the notify_all()
call doesn't count as a release operation (in the C++ memory model
sense, see https://people.cs.pitt.edu/~xianeizhang/notes/cpp11_mem.html
for a local resource :-)). The write to isRunning_ could then be visible
to the thread waiting on bufferCV_ only after bufferCV_.notify_all()
gets visible. The wait() would wake up, not see !isRunning_, go back to
sleep, and stay there forever. I initially thought the global lock would
solve this, but after further analysis, I don't think that's the case.
Fortunately, I think the fix should be a simple as
{
MutexLocker locker(buferMutex_);
isRunning_ = false;
}
bufferCV_notify_all();
in V4L2Camera::streamOff() in the patch that dropped usage of the
Semaphore class.
I'll let you confirm my analysis :-)
For this patch,
Reviewed-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> + } else if (!vcam_->isBufferAvailable())
> return -EAGAIN;
>
> /*
> @@ -706,6 +717,8 @@ const std::set<unsigned long> V4L2CameraProxy::supportedIoctls_ = {
>
> int V4L2CameraProxy::ioctl(V4L2CameraFile *file, unsigned long request, void *arg)
> {
> + MutexLocker locker(proxyMutex_);
> +
> if (!arg && (_IOC_DIR(request) & _IOC_WRITE)) {
> errno = EFAULT;
> return -1;
> @@ -766,7 +779,7 @@ int V4L2CameraProxy::ioctl(V4L2CameraFile *file, unsigned long request, void *ar
> ret = vidioc_qbuf(file, static_cast<struct v4l2_buffer *>(arg));
> break;
> case VIDIOC_DQBUF:
> - ret = vidioc_dqbuf(file, static_cast<struct v4l2_buffer *>(arg));
> + ret = vidioc_dqbuf(file, static_cast<struct v4l2_buffer *>(arg), locker);
> break;
> case VIDIOC_STREAMON:
> ret = vidioc_streamon(file, static_cast<int *>(arg));
> diff --git a/src/v4l2/v4l2_camera_proxy.h b/src/v4l2/v4l2_camera_proxy.h
> index 041f536..b6d2c2c 100644
> --- a/src/v4l2/v4l2_camera_proxy.h
> +++ b/src/v4l2/v4l2_camera_proxy.h
> @@ -61,7 +61,7 @@ private:
> int vidioc_reqbufs(V4L2CameraFile *file, struct v4l2_requestbuffers *arg);
> int vidioc_querybuf(V4L2CameraFile *file, struct v4l2_buffer *arg);
> int vidioc_qbuf(V4L2CameraFile *file, struct v4l2_buffer *arg);
> - int vidioc_dqbuf(V4L2CameraFile *file, struct v4l2_buffer *arg);
> + int vidioc_dqbuf(V4L2CameraFile *file, struct v4l2_buffer *arg, MutexLocker &locker);
> int vidioc_streamon(V4L2CameraFile *file, int *arg);
> int vidioc_streamoff(V4L2CameraFile *file, int *arg);
>
> @@ -105,6 +105,9 @@ private:
> * g/s_priority, enum/g/s_input
> */
> V4L2CameraFile *owner_;
> +
> + /* This mutex is to serialize access to the proxy. */
> + Mutex proxyMutex_;
> };
>
> #endif /* __V4L2_CAMERA_PROXY_H__ */
--
Regards,
Laurent Pinchart
More information about the libcamera-devel
mailing list