I hit a weird failure with firefox and BigBlueButton
(https://bigbluebutton.org/) where firefox can't use my webcam.
video(1) works, same for other webrtc sites in firefox, eg meet.jit.si.
ktrace shows that a single firefox process tries to open /dev/video0
more than once, and that fails with EBUSY.  The code lies in the
libwebrtc library

  
https://webrtc.googlesource.com/src/+/refs/heads/master/modules/video_capture/linux/

In my tests the multiple open() calls only happen at initialization
time, video streaming only uses one fd.

Back to our kernel, the current restrictive behavior was introduced with

  revision 1.18
  date: 2008/07/23 22:10:21;  author: mglocker;  state: Exp;  lines: +11 -4;
  If /dev/video* is already used by an application, return EBUSY to other
  applications.  Fixes a kernel panic.

  Reported by ian@

Meanwhile, the V4L2 API specifies that a device "can be opened more than
once" from multiple processes, with access to certain methods blocked
when an application starts reading from the device.

  
https://www.kernel.org/doc/html/v5.10/userspace-api/media/v4l/open.html#multiple-opens

So the API says we're being too restrictive, but we don't want to go
back to uncontrolled access either.  I guess the ideal solution would be
to implement multiple process access with fine-grained checks, but...
I don't have time for that!

An easy improvement for my use case would be to allow the same process
to open a device multiple times.  It fixes firefox + BigBlueButton for
me and doesn't make concurrent accesses worse (multiple threads from the
same process can already do concurrent accesses, which is something
we might want to look at).

Here's the diff.  IIUC the use of atomic operations isn't strictly
needed here since open(2) runs with the kernel lock, but the result
is easy to understand IMO.

Thoughts?  ok?


Index: dev/video.c
===================================================================
RCS file: /d/cvs/src/sys/dev/video.c,v
retrieving revision 1.46
diff -u -p -r1.46 video.c
--- dev/video.c 28 Dec 2020 18:28:11 -0000      1.46
+++ dev/video.c 5 Jan 2021 22:34:04 -0000
@@ -28,6 +28,8 @@
 #include <sys/kernel.h>
 #include <sys/malloc.h>
 #include <sys/conf.h>
+#include <sys/atomic.h>
+#include <sys/proc.h>
 #include <sys/videoio.h>
 
 #include <dev/video_if.h>
@@ -46,8 +48,7 @@ struct video_softc {
        struct device           *sc_dev;        /* hardware device struct */
        struct video_hw_if      *hw_if;         /* hardware interface */
        char                     sc_dying;      /* device detached */
-#define VIDEO_OPEN     0x01
-       char                     sc_open;
+       struct process          *sc_owner;      /* owner process */
 
        int                      sc_fsize;
        uint8_t                 *sc_fbuffer;
@@ -101,6 +102,7 @@ videoattach(struct device *parent, struc
        sc->hw_hdl = sa->hdl;
        sc->sc_dev = parent;
        sc->sc_fbufferlen = 0;
+       sc->sc_owner = NULL;
 
        if (sc->hw_if->get_bufsize)
                sc->sc_fbufferlen = (sc->hw_if->get_bufsize)(sc->hw_hdl);
@@ -121,6 +123,7 @@ videoopen(dev_t dev, int flags, int fmt,
 {
        int     unit;
        struct video_softc *sc;
+       struct process *owner;
 
        unit = VIDEOUNIT(dev);
        if (unit >= video_cd.cd_ndevs ||
@@ -128,9 +131,13 @@ videoopen(dev_t dev, int flags, int fmt,
             sc->hw_if == NULL)
                return (ENXIO);
 
-       if (sc->sc_open & VIDEO_OPEN)
-               return (EBUSY);
-       sc->sc_open |= VIDEO_OPEN;
+       owner = atomic_cas_ptr(&sc->sc_owner, NULL, p->p_p);
+       if (owner != NULL) {
+               if (owner == p->p_p)
+                       return (0);
+               else
+                       return (EBUSY);
+       }
 
        sc->sc_vidmode = VIDMODE_NONE;
        sc->sc_frames_ready = 0;
@@ -153,7 +160,7 @@ videoclose(dev_t dev, int flags, int fmt
        if (sc->hw_if->close != NULL)
                r = sc->hw_if->close(sc->hw_hdl);
 
-       sc->sc_open &= ~VIDEO_OPEN;
+       atomic_swap_ptr(&sc->sc_owner, NULL);
 
        return (r);
 }


-- 
jca | PGP : 0x1524E7EE / 5135 92C1 AD36 5293 2BDF  DDCC 0DFA 74AE 1524 E7EE

Reply via email to