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