From 7bc99e41a5752fbba6548981a0d7247cb43a7a1a Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Mon, 29 Sep 2025 13:57:13 +0000 Subject: [PATCH 1/2] rutabaga: consider virgl initialized if successful It's only possible to call VirglRenderer::init() once to prevent attempting to initialize virglrenderer twice. But this also prevents us for retrying the initialization if the first time fails. Let's only set the INIT_ONCE atomic when the call to virgl_renderer_init has been successful. Signed-off-by: Sergio Lopez --- src/rutabaga_gfx/src/virgl_renderer.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/rutabaga_gfx/src/virgl_renderer.rs b/src/rutabaga_gfx/src/virgl_renderer.rs index bbb312887..e219d38ae 100644 --- a/src/rutabaga_gfx/src/virgl_renderer.rs +++ b/src/rutabaga_gfx/src/virgl_renderer.rs @@ -301,17 +301,6 @@ impl VirglRenderer { } } - // virglrenderer is a global state backed library that uses thread bound OpenGL contexts. - // Initialize it only once and use the non-send/non-sync Renderer struct to keep things tied - // to whichever thread called this function first. - static INIT_ONCE: AtomicBool = AtomicBool::new(false); - if INIT_ONCE - .compare_exchange(false, true, Ordering::Acquire, Ordering::Acquire) - .is_err() - { - return Err(RutabagaError::AlreadyInUse); - } - unsafe { virgl_set_debug_callback(Some(debug_callback)) }; // Cookie is intentionally never freed because virglrenderer never gets uninitialized. @@ -335,6 +324,19 @@ impl VirglRenderer { ) }; + if ret == 0 { + // virglrenderer is a global state backed library that uses thread bound OpenGL contexts. + // Initialize it only once and use the non-send/non-sync Renderer struct to keep things tied + // to whichever thread called this function first. + static INIT_ONCE: AtomicBool = AtomicBool::new(false); + if INIT_ONCE + .compare_exchange(false, true, Ordering::Acquire, Ordering::Acquire) + .is_err() + { + return Err(RutabagaError::AlreadyInUse); + } + } + ret_to_res(ret)?; Ok(Box::new(VirglRenderer {})) } From 9e4109a9da3d5af8f0131496ba67d135095c91e8 Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Mon, 29 Sep 2025 14:07:40 +0000 Subject: [PATCH 2/2] devices/virtio_gpu: retry rutabaga initialization So far, if the initialization of rutabaga fails, the GPU thread panics with an "expect()". Instead, let's retry the initialization with safe flags (NO_VIRGL). Fixes: #418 Suggested-by: Val Packett Signed-off-by: Sergio Lopez --- src/devices/src/virtio/gpu/virtio_gpu.rs | 67 ++++++++++++++++++++---- 1 file changed, 57 insertions(+), 10 deletions(-) diff --git a/src/devices/src/virtio/gpu/virtio_gpu.rs b/src/devices/src/virtio/gpu/virtio_gpu.rs index c383b6ed7..8d64a326f 100644 --- a/src/devices/src/virtio/gpu/virtio_gpu.rs +++ b/src/devices/src/virtio/gpu/virtio_gpu.rs @@ -210,17 +210,14 @@ impl VirtioGpu { }) } - #[allow(clippy::too_many_arguments)] - pub fn new( + pub fn create_rutabaga( mem: GuestMemoryMmap, queue_ctl: Arc>, interrupt: InterruptTransport, + fence_state: Arc>, virgl_flags: u32, - #[cfg(target_os = "macos")] map_sender: Sender, export_table: Option, - displays: Box<[DisplayInfo]>, - display_backend: DisplayBackend, - ) -> Self { + ) -> Option { let xdg_runtime_dir = match env::var("XDG_RUNTIME_DIR") { Ok(dir) => dir, Err(_) => "/run/user/1000".to_string(), @@ -274,12 +271,62 @@ impl VirtioGpu { builder }; - let fence_state = Arc::new(Mutex::new(Default::default())); let fence = Self::create_fence_handler(mem, queue_ctl.clone(), fence_state.clone(), interrupt); - let rutabaga = builder - .build(fence, None) - .expect("Rutabaga initialization failed!"); + builder.clone().build(fence.clone(), None).ok() + } + + pub fn create_fallback_rutabaga( + mem: GuestMemoryMmap, + queue_ctl: Arc>, + interrupt: InterruptTransport, + fence_state: Arc>, + ) -> Option { + const VIRGLRENDERER_NO_VIRGL: u32 = 1 << 7; + let builder = RutabagaBuilder::new( + rutabaga_gfx::RutabagaComponentType::VirglRenderer, + VIRGLRENDERER_NO_VIRGL, + 0, + ); + + let fence = + Self::create_fence_handler(mem, queue_ctl.clone(), fence_state.clone(), interrupt); + builder.clone().build(fence.clone(), None).ok() + } + + #[allow(clippy::too_many_arguments)] + pub fn new( + mem: GuestMemoryMmap, + queue_ctl: Arc>, + interrupt: InterruptTransport, + virgl_flags: u32, + #[cfg(target_os = "macos")] map_sender: Sender, + export_table: Option, + displays: Box<[DisplayInfo]>, + display_backend: DisplayBackend, + ) -> Self { + let fence_state = Arc::new(Mutex::new(Default::default())); + + let rutabaga = match Self::create_rutabaga( + mem.clone(), + queue_ctl.clone(), + interrupt.clone(), + fence_state.clone(), + virgl_flags, + export_table.clone(), + ) { + Some(rutabaga) => rutabaga, + None => { + error!("Failed to create virtio_gpu backend with the requested parameters"); + Self::create_fallback_rutabaga( + mem.clone(), + queue_ctl.clone(), + interrupt.clone(), + fence_state.clone(), + ) + .expect("Fallback rutabaga initialization failed") + } + }; let display_backend = display_backend .create_instance()