1mod edid;
6mod parameters;
7mod protocol;
8mod snapshot;
9mod virtio_gpu;
10
11use std::cell::RefCell;
12use std::collections::BTreeMap;
13use std::io::Read;
14use std::path::PathBuf;
15use std::rc::Rc;
16use std::sync::atomic::AtomicBool;
17use std::sync::atomic::Ordering;
18use std::sync::mpsc;
19use std::sync::Arc;
20
21use ::snapshot::AnySnapshot;
22use anyhow::anyhow;
23use anyhow::Context;
24use base::custom_serde::deserialize_map_from_kv_vec;
25use base::custom_serde::serialize_map_as_kv_vec;
26use base::debug;
27use base::error;
28use base::info;
29#[cfg(any(target_os = "android", target_os = "linux"))]
30use base::linux::move_task_to_cgroup;
31use base::warn;
32use base::AsRawDescriptor;
33use base::Event;
34use base::EventToken;
35use base::RawDescriptor;
36use base::ReadNotifier;
37#[cfg(windows)]
38use base::RecvTube;
39use base::Result;
40use base::SafeDescriptor;
41use base::SendTube;
42use base::Tube;
43use base::VmEventType;
44use base::WaitContext;
45use base::WorkerThread;
46use data_model::*;
47pub use gpu_display::EventDevice;
48use gpu_display::*;
49use hypervisor::MemCacheType;
50pub use parameters::AudioDeviceMode;
51pub use parameters::GpuParameters;
52use rutabaga_gfx::*;
53use serde::Deserialize;
54use serde::Serialize;
55use sync::Mutex;
56pub use vm_control::gpu::DisplayMode as GpuDisplayMode;
57pub use vm_control::gpu::DisplayParameters as GpuDisplayParameters;
58use vm_control::gpu::GpuControlCommand;
59use vm_control::gpu::GpuControlResult;
60pub use vm_control::gpu::MouseMode as GpuMouseMode;
61pub use vm_control::gpu::DEFAULT_DISPLAY_HEIGHT;
62pub use vm_control::gpu::DEFAULT_DISPLAY_WIDTH;
63pub use vm_control::gpu::DEFAULT_REFRESH_RATE;
64#[cfg(windows)]
65use vm_control::ModifyWaitContext;
66use vm_memory::GuestAddress;
67use vm_memory::GuestMemory;
68use zerocopy::IntoBytes;
69
70pub use self::protocol::virtio_gpu_config;
71pub use self::protocol::VIRTIO_GPU_F_CONTEXT_INIT;
72pub use self::protocol::VIRTIO_GPU_F_CREATE_GUEST_HANDLE;
73pub use self::protocol::VIRTIO_GPU_F_EDID;
74pub use self::protocol::VIRTIO_GPU_F_RESOURCE_BLOB;
75pub use self::protocol::VIRTIO_GPU_F_RESOURCE_UUID;
76pub use self::protocol::VIRTIO_GPU_F_VIRGL;
77pub use self::protocol::VIRTIO_GPU_MAX_SCANOUTS;
78pub use self::protocol::VIRTIO_GPU_SHM_ID_HOST_VISIBLE;
79use self::protocol::*;
80use self::virtio_gpu::to_rutabaga_descriptor;
81pub use self::virtio_gpu::ProcessDisplayResult;
82use self::virtio_gpu::VirtioGpu;
83use self::virtio_gpu::VirtioGpuSnapshot;
84use super::copy_config;
85use super::resource_bridge::ResourceRequest;
86use super::DescriptorChain;
87use super::DeviceType;
88use super::Interrupt;
89use super::Queue;
90use super::Reader;
91use super::SharedMemoryMapper;
92use super::SharedMemoryPrepareType;
93use super::SharedMemoryRegion;
94use super::VirtioDevice;
95use super::Writer;
96use crate::PciAddress;
97
98const QUEUE_SIZES: &[u16] = &[512, 16];
101
102#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Serialize, Deserialize)]
103pub enum GpuMode {
104 #[default]
105 #[serde(rename = "2d", alias = "2D")]
106 Mode2D,
107 #[serde(rename = "virglrenderer", alias = "3d", alias = "3D")]
108 ModeVirglRenderer,
109 #[serde(rename = "gfxstream")]
110 ModeGfxstream,
111}
112
113#[derive(Clone, Debug, Serialize, Deserialize)]
114#[serde(rename_all = "kebab-case")]
115pub enum GpuWsi {
116 #[serde(alias = "vk")]
117 Vulkan,
118}
119
120#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
121pub struct VirtioScanoutBlobData {
122 pub width: u32,
123 pub height: u32,
124 pub drm_format: u32,
125 pub strides: [u32; 4],
126 pub offsets: [u32; 4],
127}
128
129#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
130enum VirtioGpuRing {
131 Global,
132 ContextSpecific { ctx_id: u32, ring_idx: u8 },
133}
134
135struct FenceDescriptor {
136 ring: VirtioGpuRing,
137 fence_id: u64,
138 desc_chain: DescriptorChain,
139 len: u32,
140}
141
142#[derive(Default)]
143pub struct FenceState {
144 descs: Vec<FenceDescriptor>,
145 completed_fences: BTreeMap<VirtioGpuRing, u64>,
146}
147
148#[derive(Serialize, Deserialize)]
149struct FenceStateSnapshot {
150 #[serde(
153 serialize_with = "serialize_map_as_kv_vec",
154 deserialize_with = "deserialize_map_from_kv_vec"
155 )]
156 completed_fences: BTreeMap<VirtioGpuRing, u64>,
157}
158
159impl FenceState {
160 fn snapshot(&self) -> FenceStateSnapshot {
161 assert!(self.descs.is_empty(), "can't snapshot with pending fences");
162 FenceStateSnapshot {
163 completed_fences: self.completed_fences.clone(),
164 }
165 }
166
167 fn restore(&mut self, snapshot: FenceStateSnapshot) {
168 assert!(self.descs.is_empty(), "can't restore activated device");
169 self.completed_fences = snapshot.completed_fences;
170 }
171}
172
173pub trait QueueReader {
174 fn pop(&self) -> Option<DescriptorChain>;
175 fn add_used(&self, desc_chain: DescriptorChain, len: u32);
176 fn signal_used(&self);
177}
178
179struct LocalQueueReader {
180 queue: RefCell<Queue>,
181}
182
183impl LocalQueueReader {
184 fn new(queue: Queue) -> Self {
185 Self {
186 queue: RefCell::new(queue),
187 }
188 }
189}
190
191impl QueueReader for LocalQueueReader {
192 fn pop(&self) -> Option<DescriptorChain> {
193 self.queue.borrow_mut().pop()
194 }
195
196 fn add_used(&self, desc_chain: DescriptorChain, len: u32) {
197 self.queue
198 .borrow_mut()
199 .add_used_with_bytes_written(desc_chain, len);
200 }
201
202 fn signal_used(&self) {
203 self.queue.borrow_mut().trigger_interrupt();
204 }
205}
206
207#[derive(Clone)]
208struct SharedQueueReader {
209 queue: Arc<Mutex<Queue>>,
210}
211
212impl SharedQueueReader {
213 fn new(queue: Queue) -> Self {
214 Self {
215 queue: Arc::new(Mutex::new(queue)),
216 }
217 }
218}
219
220impl QueueReader for SharedQueueReader {
221 fn pop(&self) -> Option<DescriptorChain> {
222 self.queue.lock().pop()
223 }
224
225 fn add_used(&self, desc_chain: DescriptorChain, len: u32) {
226 self.queue
227 .lock()
228 .add_used_with_bytes_written(desc_chain, len);
229 }
230
231 fn signal_used(&self) {
232 self.queue.lock().trigger_interrupt();
233 }
234}
235
236fn build(
238 display_backends: &[DisplayBackend],
239 display_params: Vec<GpuDisplayParameters>,
240 display_event: Arc<AtomicBool>,
241 rutabaga: Rutabaga,
242 mapper: Arc<Mutex<Option<Box<dyn SharedMemoryMapper>>>>,
243 external_blob: bool,
244 fixed_blob_mapping: bool,
245 #[cfg(windows)] wndproc_thread: &mut Option<WindowProcedureThread>,
246 udmabuf: bool,
247 #[cfg(windows)] gpu_display_wait_descriptor_ctrl_wr: SendTube,
248 snapshot_scratch_directory: Option<PathBuf>,
249) -> Option<VirtioGpu> {
250 let mut display_opt = None;
251 for display_backend in display_backends {
252 match display_backend.build(
253 #[cfg(windows)]
254 wndproc_thread,
255 #[cfg(windows)]
256 gpu_display_wait_descriptor_ctrl_wr
257 .try_clone()
258 .expect("failed to clone wait context ctrl channel"),
259 ) {
260 Ok(c) => {
261 display_opt = Some(c);
262 break;
263 }
264 Err(e) => error!("failed to open display: {}", e),
265 };
266 }
267
268 let display = match display_opt {
269 Some(d) => d,
270 None => {
271 error!("failed to open any displays");
272 return None;
273 }
274 };
275
276 VirtioGpu::new(
277 display,
278 display_params,
279 display_event,
280 rutabaga,
281 mapper,
282 external_blob,
283 fixed_blob_mapping,
284 udmabuf,
285 snapshot_scratch_directory,
286 )
287}
288
289pub struct FenceHandlerActivationResources<Q>
291where
292 Q: QueueReader + Send + Clone + 'static,
293{
294 pub mem: GuestMemory,
295 pub ctrl_queue: Q,
296}
297
298pub fn create_fence_handler<Q>(
300 fence_handler_resources: Arc<Mutex<Option<FenceHandlerActivationResources<Q>>>>,
301 fence_state: Arc<Mutex<FenceState>>,
302) -> RutabagaFenceHandler
303where
304 Q: QueueReader + Send + Clone + 'static,
305{
306 RutabagaFenceHandler::new(move |completed_fence: RutabagaFence| {
307 let mut signal = false;
308
309 if let Some(ref fence_handler_resources) = *fence_handler_resources.lock() {
310 {
312 let ring = match completed_fence.flags & VIRTIO_GPU_FLAG_INFO_RING_IDX {
313 0 => VirtioGpuRing::Global,
314 _ => VirtioGpuRing::ContextSpecific {
315 ctx_id: completed_fence.ctx_id,
316 ring_idx: completed_fence.ring_idx,
317 },
318 };
319
320 let mut fence_state = fence_state.lock();
321 let mut i = 0;
323 while i < fence_state.descs.len() {
324 if fence_state.descs[i].ring == ring
325 && fence_state.descs[i].fence_id <= completed_fence.fence_id
326 {
327 let completed_desc = fence_state.descs.remove(i);
328 fence_handler_resources
329 .ctrl_queue
330 .add_used(completed_desc.desc_chain, completed_desc.len);
331 signal = true;
332 } else {
333 i += 1;
334 }
335 }
336
337 fence_state
339 .completed_fences
340 .insert(ring, completed_fence.fence_id);
341 }
342
343 if signal {
344 fence_handler_resources.ctrl_queue.signal_used();
345 }
346 }
347 })
348}
349
350pub struct ReturnDescriptor {
351 pub desc_chain: DescriptorChain,
352 pub len: u32,
353}
354
355pub struct Frontend {
356 fence_state: Arc<Mutex<FenceState>>,
357 virtio_gpu: VirtioGpu,
358}
359
360impl Frontend {
361 fn new(virtio_gpu: VirtioGpu, fence_state: Arc<Mutex<FenceState>>) -> Frontend {
362 Frontend {
363 fence_state,
364 virtio_gpu,
365 }
366 }
367
368 pub fn display(&mut self) -> &Rc<RefCell<GpuDisplay>> {
370 self.virtio_gpu.display()
371 }
372
373 pub fn process_display(&mut self) -> ProcessDisplayResult {
375 self.virtio_gpu.process_display()
376 }
377
378 pub fn process_resource_bridge(&mut self, resource_bridge: &Tube) -> anyhow::Result<()> {
380 let response = match resource_bridge.recv() {
381 Ok(ResourceRequest::GetBuffer { id }) => self.virtio_gpu.export_resource(id),
382 Ok(ResourceRequest::GetFence { seqno }) => self.virtio_gpu.export_fence(seqno),
383 Err(e) => return Err(e).context("Error receiving resource bridge request"),
384 };
385
386 resource_bridge
387 .send(&response)
388 .context("Error sending resource bridge response")?;
389
390 Ok(())
391 }
392
393 pub fn process_gpu_control_command(&mut self, cmd: GpuControlCommand) -> GpuControlResult {
396 self.virtio_gpu.process_gpu_control_command(cmd)
397 }
398
399 fn process_gpu_command(
400 &mut self,
401 mem: &GuestMemory,
402 cmd: GpuCommand,
403 reader: &mut Reader,
404 ) -> VirtioGpuResult {
405 self.virtio_gpu.force_ctx_0();
406
407 match cmd {
408 GpuCommand::GetDisplayInfo(_) => Ok(GpuResponse::OkDisplayInfo(
409 self.virtio_gpu.display_info().to_vec(),
410 )),
411 GpuCommand::ResourceCreate2d(info) => {
412 let resource_id = info.resource_id.to_native();
413
414 let resource_create_3d = ResourceCreate3D {
415 target: RUTABAGA_PIPE_TEXTURE_2D,
416 format: info.format.to_native(),
417 bind: RUTABAGA_PIPE_BIND_RENDER_TARGET,
418 width: info.width.to_native(),
419 height: info.height.to_native(),
420 depth: 1,
421 array_size: 1,
422 last_level: 0,
423 nr_samples: 0,
424 flags: 0,
425 };
426
427 self.virtio_gpu
428 .resource_create_3d(resource_id, resource_create_3d)
429 }
430 GpuCommand::ResourceUnref(info) => {
431 self.virtio_gpu.unref_resource(info.resource_id.to_native())
432 }
433 GpuCommand::SetScanout(info) => self.virtio_gpu.set_scanout(
434 info.r,
435 info.scanout_id.to_native(),
436 info.resource_id.to_native(),
437 None,
438 ),
439 GpuCommand::ResourceFlush(info) => {
440 self.virtio_gpu.flush_resource(info.resource_id.to_native())
441 }
442 GpuCommand::TransferToHost2d(info) => {
443 let resource_id = info.resource_id.to_native();
444 let transfer = Transfer3D::new_2d(
445 info.r.x.to_native(),
446 info.r.y.to_native(),
447 info.r.width.to_native(),
448 info.r.height.to_native(),
449 info.offset.to_native(),
450 );
451 self.virtio_gpu.transfer_write(0, resource_id, transfer)
452 }
453 GpuCommand::ResourceAttachBacking(info) => {
454 let available_bytes = reader.available_bytes();
455 if available_bytes != 0 {
456 let entry_count = info.nr_entries.to_native() as usize;
457 let mut vecs = Vec::with_capacity(entry_count);
458 for _ in 0..entry_count {
459 match reader.read_obj::<virtio_gpu_mem_entry>() {
460 Ok(entry) => {
461 let addr = GuestAddress(entry.addr.to_native());
462 let len = entry.length.to_native() as usize;
463 vecs.push((addr, len))
464 }
465 Err(_) => return Err(GpuResponse::ErrUnspec),
466 }
467 }
468 self.virtio_gpu
469 .attach_backing(info.resource_id.to_native(), mem, vecs)
470 } else {
471 error!("missing data for command {:?}", cmd);
472 Err(GpuResponse::ErrUnspec)
473 }
474 }
475 GpuCommand::ResourceDetachBacking(info) => {
476 self.virtio_gpu.detach_backing(info.resource_id.to_native())
477 }
478 GpuCommand::UpdateCursor(info) => self.virtio_gpu.update_cursor(
479 info.resource_id.to_native(),
480 info.pos.scanout_id.to_native(),
481 info.pos.x.into(),
482 info.pos.y.into(),
483 ),
484 GpuCommand::MoveCursor(info) => self.virtio_gpu.move_cursor(
485 info.pos.scanout_id.to_native(),
486 info.pos.x.into(),
487 info.pos.y.into(),
488 ),
489 GpuCommand::ResourceAssignUuid(info) => {
490 let resource_id = info.resource_id.to_native();
491 self.virtio_gpu.resource_assign_uuid(resource_id)
492 }
493 GpuCommand::GetCapsetInfo(info) => self
494 .virtio_gpu
495 .get_capset_info(info.capset_index.to_native()),
496 GpuCommand::GetCapset(info) => self
497 .virtio_gpu
498 .get_capset(info.capset_id.to_native(), info.capset_version.to_native()),
499 GpuCommand::CtxCreate(info) => {
500 let context_name: Option<String> = String::from_utf8(info.debug_name.to_vec()).ok();
501 self.virtio_gpu.create_context(
502 info.hdr.ctx_id.to_native(),
503 info.context_init.to_native(),
504 context_name.as_deref(),
505 )
506 }
507 GpuCommand::CtxDestroy(info) => {
508 self.virtio_gpu.destroy_context(info.hdr.ctx_id.to_native())
509 }
510 GpuCommand::CtxAttachResource(info) => self
511 .virtio_gpu
512 .context_attach_resource(info.hdr.ctx_id.to_native(), info.resource_id.to_native()),
513 GpuCommand::CtxDetachResource(info) => self
514 .virtio_gpu
515 .context_detach_resource(info.hdr.ctx_id.to_native(), info.resource_id.to_native()),
516 GpuCommand::ResourceCreate3d(info) => {
517 let resource_id = info.resource_id.to_native();
518 let resource_create_3d = ResourceCreate3D {
519 target: info.target.to_native(),
520 format: info.format.to_native(),
521 bind: info.bind.to_native(),
522 width: info.width.to_native(),
523 height: info.height.to_native(),
524 depth: info.depth.to_native(),
525 array_size: info.array_size.to_native(),
526 last_level: info.last_level.to_native(),
527 nr_samples: info.nr_samples.to_native(),
528 flags: info.flags.to_native(),
529 };
530
531 self.virtio_gpu
532 .resource_create_3d(resource_id, resource_create_3d)
533 }
534 GpuCommand::TransferToHost3d(info) => {
535 let ctx_id = info.hdr.ctx_id.to_native();
536 let resource_id = info.resource_id.to_native();
537
538 let transfer = Transfer3D {
539 x: info.box_.x.to_native(),
540 y: info.box_.y.to_native(),
541 z: info.box_.z.to_native(),
542 w: info.box_.w.to_native(),
543 h: info.box_.h.to_native(),
544 d: info.box_.d.to_native(),
545 level: info.level.to_native(),
546 stride: info.stride.to_native(),
547 layer_stride: info.layer_stride.to_native(),
548 offset: info.offset.to_native(),
549 };
550
551 self.virtio_gpu
552 .transfer_write(ctx_id, resource_id, transfer)
553 }
554 GpuCommand::TransferFromHost3d(info) => {
555 let ctx_id = info.hdr.ctx_id.to_native();
556 let resource_id = info.resource_id.to_native();
557
558 let transfer = Transfer3D {
559 x: info.box_.x.to_native(),
560 y: info.box_.y.to_native(),
561 z: info.box_.z.to_native(),
562 w: info.box_.w.to_native(),
563 h: info.box_.h.to_native(),
564 d: info.box_.d.to_native(),
565 level: info.level.to_native(),
566 stride: info.stride.to_native(),
567 layer_stride: info.layer_stride.to_native(),
568 offset: info.offset.to_native(),
569 };
570
571 self.virtio_gpu
572 .transfer_read(ctx_id, resource_id, transfer, None)
573 }
574 GpuCommand::CmdSubmit3d(info) => {
575 if reader.available_bytes() != 0 {
576 let cmd_size = info.size.to_native() as usize;
577 let mut cmd_buf = vec![0; cmd_size];
578 let ctx_id = info.hdr.ctx_id.to_native();
579
580 if reader.read_exact(&mut cmd_buf[..]).is_ok() {
581 self.virtio_gpu
582 .submit_command(ctx_id, &mut cmd_buf[..], &[])
583 } else {
584 Err(GpuResponse::ErrInvalidParameter)
585 }
586 } else {
587 Ok(GpuResponse::OkNoData)
590 }
591 }
592 GpuCommand::ResourceCreateBlob(info) => {
593 let resource_id = info.resource_id.to_native();
594 let ctx_id = info.hdr.ctx_id.to_native();
595
596 let resource_create_blob = ResourceCreateBlob {
597 blob_mem: info.blob_mem.to_native(),
598 blob_flags: info.blob_flags.to_native(),
599 blob_id: info.blob_id.to_native(),
600 size: info.size.to_native(),
601 };
602
603 let entry_count = info.nr_entries.to_native();
604 if reader.available_bytes() == 0 && entry_count > 0 {
605 return Err(GpuResponse::ErrUnspec);
606 }
607
608 let mut vecs = Vec::with_capacity(entry_count as usize);
609 for _ in 0..entry_count {
610 match reader.read_obj::<virtio_gpu_mem_entry>() {
611 Ok(entry) => {
612 let addr = GuestAddress(entry.addr.to_native());
613 let len = entry.length.to_native() as usize;
614 vecs.push((addr, len))
615 }
616 Err(_) => return Err(GpuResponse::ErrUnspec),
617 }
618 }
619
620 self.virtio_gpu.resource_create_blob(
621 ctx_id,
622 resource_id,
623 resource_create_blob,
624 vecs,
625 mem,
626 )
627 }
628 GpuCommand::SetScanoutBlob(info) => {
629 let scanout_id = info.scanout_id.to_native();
630 let resource_id = info.resource_id.to_native();
631 let virtio_gpu_format = info.format.to_native();
632 let width = info.width.to_native();
633 let height = info.height.to_native();
634 let mut strides: [u32; 4] = [0; 4];
635 let mut offsets: [u32; 4] = [0; 4];
636
637 let drm_format = match virtio_gpu_format {
640 VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM => DrmFormat::new(b'X', b'R', b'2', b'4'),
641 VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM => DrmFormat::new(b'A', b'R', b'2', b'4'),
642 VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM => DrmFormat::new(b'A', b'B', b'2', b'4'),
643 _ => {
644 error!("unrecognized virtio-gpu format {}", virtio_gpu_format);
645 return Err(GpuResponse::ErrUnspec);
646 }
647 };
648
649 for plane_index in 0..PLANE_INFO_MAX_COUNT {
650 offsets[plane_index] = info.offsets[plane_index].to_native();
651 strides[plane_index] = info.strides[plane_index].to_native();
652 }
653
654 let scanout = VirtioScanoutBlobData {
655 width,
656 height,
657 drm_format: drm_format.into(),
658 strides,
659 offsets,
660 };
661
662 self.virtio_gpu
663 .set_scanout(info.r, scanout_id, resource_id, Some(scanout))
664 }
665 GpuCommand::ResourceMapBlob(info) => {
666 let resource_id = info.resource_id.to_native();
667 let offset = info.offset.to_native();
668 self.virtio_gpu
669 .resource_map_blob(resource_id, offset)
670 .inspect_err(|e| {
671 error!(
673 "Failed to map blob, resource id {}, offset {}, error: {:#}",
674 resource_id, offset, e
675 );
676 })
677 .map_err(|e| match e.downcast::<GpuResponse>() {
678 Ok(response) => response,
679 Err(e) => {
680 warn!(
681 "No GPU response specified for {:?}, default to ErrUnspec",
682 e
683 );
684 GpuResponse::ErrUnspec
685 }
686 })
687 }
688 GpuCommand::ResourceUnmapBlob(info) => {
689 let resource_id = info.resource_id.to_native();
690 self.virtio_gpu.resource_unmap_blob(resource_id)
691 }
692 GpuCommand::GetEdid(info) => self.virtio_gpu.get_edid(info.scanout.to_native()),
693 }
694 }
695
696 pub fn process_queue(&mut self, mem: &GuestMemory, queue: &dyn QueueReader) -> bool {
698 let mut signal_used = false;
699 while let Some(desc) = queue.pop() {
700 if let Some(ret_desc) = self.process_descriptor(mem, desc) {
701 queue.add_used(ret_desc.desc_chain, ret_desc.len);
702 signal_used = true;
703 }
704 }
705
706 signal_used
707 }
708
709 fn process_descriptor(
710 &mut self,
711 mem: &GuestMemory,
712 mut desc_chain: DescriptorChain,
713 ) -> Option<ReturnDescriptor> {
714 let reader = &mut desc_chain.reader;
715 let writer = &mut desc_chain.writer;
716 let mut resp = Err(GpuResponse::ErrUnspec);
717 let mut gpu_cmd = None;
718 let mut len = 0;
719 match GpuCommand::decode(reader) {
720 Ok(cmd) => {
721 resp = self.process_gpu_command(mem, cmd, reader);
722 gpu_cmd = Some(cmd);
723 }
724 Err(e) => debug!("descriptor decode error: {}", e),
725 }
726
727 let mut gpu_response = match resp {
728 Ok(gpu_response) => gpu_response,
729 Err(gpu_response) => {
730 if let Some(gpu_cmd) = gpu_cmd {
731 error!(
732 "error processing gpu command {:?}: {:?}",
733 gpu_cmd, gpu_response
734 );
735 }
736 gpu_response
737 }
738 };
739
740 if writer.available_bytes() != 0 {
741 let mut fence_id = 0;
742 let mut ctx_id = 0;
743 let mut flags = 0;
744 let mut ring_idx = 0;
745 if let Some(cmd) = gpu_cmd {
746 let ctrl_hdr = cmd.ctrl_hdr();
747 if ctrl_hdr.flags.to_native() & VIRTIO_GPU_FLAG_FENCE != 0 {
748 flags = ctrl_hdr.flags.to_native();
749 fence_id = ctrl_hdr.fence_id.to_native();
750 ctx_id = ctrl_hdr.ctx_id.to_native();
751 ring_idx = ctrl_hdr.ring_idx;
752
753 let fence = RutabagaFence {
754 flags,
755 fence_id,
756 ctx_id,
757 ring_idx,
758 };
759 gpu_response = match self.virtio_gpu.create_fence(fence) {
760 Ok(_) => gpu_response,
761 Err(fence_resp) => {
762 warn!("create_fence {} -> {:?}", fence_id, fence_resp);
763 fence_resp
764 }
765 };
766 }
767 }
768
769 match gpu_response.encode(flags, fence_id, ctx_id, ring_idx, writer) {
772 Ok(l) => len = l,
773 Err(e) => debug!("ctrl queue response encode error: {}", e),
774 }
775
776 if flags & VIRTIO_GPU_FLAG_FENCE != 0 {
777 let ring = match flags & VIRTIO_GPU_FLAG_INFO_RING_IDX {
778 0 => VirtioGpuRing::Global,
779 _ => VirtioGpuRing::ContextSpecific { ctx_id, ring_idx },
780 };
781
782 let mut fence_state = self.fence_state.lock();
785 if fence_id > *fence_state.completed_fences.get(&ring).unwrap_or(&0) {
786 fence_state.descs.push(FenceDescriptor {
787 ring,
788 fence_id,
789 desc_chain,
790 len,
791 });
792
793 return None;
794 }
795 }
796
797 }
799 Some(ReturnDescriptor { desc_chain, len })
800 }
801
802 pub fn event_poll(&self) {
803 self.virtio_gpu.event_poll();
804 }
805}
806
807#[derive(EventToken, PartialEq, Eq, Clone, Copy, Debug)]
808enum WorkerToken {
809 CtrlQueue,
810 CursorQueue,
811 Display,
812 GpuControl,
813 Sleep,
814 Kill,
815 ResourceBridge {
816 index: usize,
817 },
818 VirtioGpuPoll,
819 #[cfg(windows)]
820 DisplayDescriptorRequest,
821}
822
823struct EventManager<'a> {
824 pub wait_ctx: WaitContext<WorkerToken>,
825 events: Vec<(&'a dyn AsRawDescriptor, WorkerToken)>,
826}
827
828impl<'a> EventManager<'a> {
829 pub fn new() -> Result<EventManager<'a>> {
830 Ok(EventManager {
831 wait_ctx: WaitContext::new()?,
832 events: vec![],
833 })
834 }
835
836 pub fn build_with(
837 triggers: &[(&'a dyn AsRawDescriptor, WorkerToken)],
838 ) -> Result<EventManager<'a>> {
839 let mut manager = EventManager::new()?;
840 manager.wait_ctx.add_many(triggers)?;
841
842 for (descriptor, token) in triggers {
843 manager.events.push((*descriptor, *token));
844 }
845 Ok(manager)
846 }
847
848 pub fn add(&mut self, descriptor: &'a dyn AsRawDescriptor, token: WorkerToken) -> Result<()> {
849 self.wait_ctx.add(descriptor, token)?;
850 self.events.push((descriptor, token));
851 Ok(())
852 }
853
854 pub fn delete(&mut self, token: WorkerToken) {
855 self.events.retain(|event| {
856 if event.1 == token {
857 self.wait_ctx.delete(event.0).ok();
858 return false;
859 }
860 true
861 });
862 }
863}
864
865#[derive(Serialize, Deserialize)]
866struct WorkerSnapshot {
867 fence_state_snapshot: FenceStateSnapshot,
868 virtio_gpu_snapshot: VirtioGpuSnapshot,
869}
870
871struct WorkerActivateRequest {
872 resources: GpuActivationResources,
873}
874
875enum WorkerRequest {
876 Activate(WorkerActivateRequest),
877 Suspend,
878 Snapshot,
879 Restore(WorkerSnapshot),
880}
881
882enum WorkerResponse {
883 Ok,
884 Suspend(GpuDeactivationResources),
885 Snapshot(WorkerSnapshot),
886}
887
888struct GpuActivationResources {
889 mem: GuestMemory,
890 interrupt: Interrupt,
891 ctrl_queue: SharedQueueReader,
892 cursor_queue: LocalQueueReader,
893}
894
895struct GpuDeactivationResources {
896 queues: Option<Vec<Queue>>,
897}
898
899struct Worker {
900 request_receiver: mpsc::Receiver<WorkerRequest>,
901 response_sender: mpsc::Sender<anyhow::Result<WorkerResponse>>,
902 exit_evt_wrtube: SendTube,
903 gpu_control_tube: Tube,
904 resource_bridges: ResourceBridges,
905 suspend_evt: Event,
906 kill_evt: Event,
907 state: Frontend,
908 fence_state: Arc<Mutex<FenceState>>,
909 fence_handler_resources: Arc<Mutex<Option<FenceHandlerActivationResources<SharedQueueReader>>>>,
910 #[cfg(windows)]
911 gpu_display_wait_descriptor_ctrl_rd: RecvTube,
912 activation_resources: Option<GpuActivationResources>,
913}
914
915#[derive(Copy, Clone)]
916enum WorkerStopReason {
917 Sleep,
918 Kill,
919}
920
921enum WorkerState {
922 Inactive,
923 Active,
924 Error,
925}
926
927fn build_rutabaga(
928 gpu_parameters: &GpuParameters,
929 display_params: &[GpuDisplayParameters],
930 rutabaga_component: RutabagaComponentType,
931 rutabaga_paths: Vec<RutabagaPath>,
932 rutabaga_server_descriptor: Option<RutabagaDescriptor>,
933 fence_handler: RutabagaFenceHandler,
934) -> RutabagaResult<Rutabaga> {
935 let (display_width, display_height) = display_params[0].get_virtual_display_size();
936
937 let use_render_server =
941 rutabaga_server_descriptor.is_some() || gpu_parameters.allow_implicit_render_server_exec;
942
943 let rutabaga_wsi = match gpu_parameters.wsi {
944 Some(GpuWsi::Vulkan) => RutabagaWsi::VulkanSwapchain,
945 _ => RutabagaWsi::Surfaceless,
946 };
947
948 RutabagaBuilder::new(gpu_parameters.capset_mask, fence_handler)
949 .set_default_component(rutabaga_component)
950 .set_display_width(display_width)
951 .set_display_height(display_height)
952 .set_rutabaga_paths(Some(rutabaga_paths))
953 .set_use_egl(gpu_parameters.renderer_use_egl)
954 .set_use_gles(gpu_parameters.renderer_use_gles)
955 .set_use_surfaceless(gpu_parameters.renderer_use_surfaceless)
956 .set_use_vulkan(gpu_parameters.use_vulkan.unwrap_or_default())
957 .set_wsi(rutabaga_wsi)
958 .set_use_external_blob(gpu_parameters.external_blob)
959 .set_use_system_blob(gpu_parameters.system_blob)
960 .set_use_render_server(use_render_server)
961 .set_renderer_features(gpu_parameters.renderer_features.clone())
962 .set_server_descriptor(rutabaga_server_descriptor)
963 .build()
964}
965
966impl Worker {
967 fn new(
968 gpu_parameters: GpuParameters,
969 rutabaga_paths: Vec<RutabagaPath>,
970 rutabaga_component: RutabagaComponentType,
971 rutabaga_server_descriptor: Option<RutabagaDescriptor>,
972 display_backends: Vec<DisplayBackend>,
973 display_params: Vec<GpuDisplayParameters>,
974 display_event: Arc<AtomicBool>,
975 mapper: Arc<Mutex<Option<Box<dyn SharedMemoryMapper>>>>,
976 event_devices: Vec<EventDevice>,
977 external_blob: bool,
978 fixed_blob_mapping: bool,
979 udmabuf: bool,
980 request_receiver: mpsc::Receiver<WorkerRequest>,
981 response_sender: mpsc::Sender<anyhow::Result<WorkerResponse>>,
982 exit_evt_wrtube: SendTube,
983 gpu_control_tube: Tube,
984 resource_bridges: ResourceBridges,
985 suspend_evt: Event,
986 kill_evt: Event,
987 #[cfg(windows)] mut wndproc_thread: Option<WindowProcedureThread>,
988 #[cfg(windows)] gpu_display_wait_descriptor_ctrl_rd: RecvTube,
989 #[cfg(windows)] gpu_display_wait_descriptor_ctrl_wr: SendTube,
990 snapshot_scratch_directory: Option<PathBuf>,
991 ) -> anyhow::Result<Worker> {
992 let fence_state = Arc::new(Mutex::new(Default::default()));
993 let fence_handler_resources = Arc::new(Mutex::new(None));
994 let fence_handler =
995 create_fence_handler(fence_handler_resources.clone(), fence_state.clone());
996
997 let rutabaga = build_rutabaga(
998 &gpu_parameters,
999 &display_params,
1000 rutabaga_component,
1001 rutabaga_paths,
1002 rutabaga_server_descriptor,
1003 fence_handler,
1004 )?;
1005
1006 let mut virtio_gpu = build(
1007 &display_backends,
1008 display_params,
1009 display_event,
1010 rutabaga,
1011 mapper,
1012 external_blob,
1013 fixed_blob_mapping,
1014 #[cfg(windows)]
1015 &mut wndproc_thread,
1016 udmabuf,
1017 #[cfg(windows)]
1018 gpu_display_wait_descriptor_ctrl_wr,
1019 snapshot_scratch_directory,
1020 )
1021 .ok_or_else(|| anyhow!("failed to build virtio gpu"))?;
1022
1023 for event_device in event_devices {
1024 virtio_gpu
1025 .import_event_device(event_device)
1026 .context("failed to import event device")?;
1028 }
1029
1030 Ok(Worker {
1031 request_receiver,
1032 response_sender,
1033 exit_evt_wrtube,
1034 gpu_control_tube,
1035 resource_bridges,
1036 suspend_evt,
1037 kill_evt,
1038 state: Frontend::new(virtio_gpu, fence_state.clone()),
1039 fence_state,
1040 fence_handler_resources,
1041 #[cfg(windows)]
1042 gpu_display_wait_descriptor_ctrl_rd,
1043 activation_resources: None,
1044 })
1045 }
1046
1047 fn run(&mut self) {
1048 loop {
1052 let request = match self.request_receiver.recv() {
1053 Ok(r) => r,
1054 Err(_) => {
1055 info!("virtio gpu worker connection ended, exiting.");
1056 return;
1057 }
1058 };
1059
1060 match request {
1061 WorkerRequest::Activate(request) => {
1062 let response = self.on_activate(request).map(|_| WorkerResponse::Ok);
1063 self.response_sender
1064 .send(response)
1065 .expect("failed to send gpu worker response for activate");
1066
1067 let stop_reason = self
1068 .run_until_sleep_or_exit()
1069 .expect("failed to run gpu worker processing");
1070
1071 if let WorkerStopReason::Kill = stop_reason {
1072 break;
1073 }
1074 }
1075 WorkerRequest::Suspend => {
1076 let response = self.on_suspend().map(WorkerResponse::Suspend);
1077 self.response_sender
1078 .send(response)
1079 .expect("failed to send gpu worker response for suspend");
1080 }
1081 WorkerRequest::Snapshot => {
1082 let response = self.on_snapshot().map(WorkerResponse::Snapshot);
1083 self.response_sender
1084 .send(response)
1085 .expect("failed to send gpu worker response for snapshot");
1086 }
1087 WorkerRequest::Restore(snapshot) => {
1088 let response = self.on_restore(snapshot).map(|_| WorkerResponse::Ok);
1089 self.response_sender
1090 .send(response)
1091 .expect("failed to send gpu worker response for restore");
1092 }
1093 }
1094 }
1095 }
1096
1097 fn on_activate(&mut self, request: WorkerActivateRequest) -> anyhow::Result<()> {
1098 self.fence_handler_resources
1099 .lock()
1100 .replace(FenceHandlerActivationResources {
1101 mem: request.resources.mem.clone(),
1102 ctrl_queue: request.resources.ctrl_queue.clone(),
1103 });
1104
1105 self.state
1106 .virtio_gpu
1107 .resume(&request.resources.mem)
1108 .context("gpu worker failed to activate virtio frontend")?;
1109
1110 self.activation_resources = Some(request.resources);
1111
1112 Ok(())
1113 }
1114
1115 fn on_suspend(&mut self) -> anyhow::Result<GpuDeactivationResources> {
1116 self.state
1117 .virtio_gpu
1118 .suspend()
1119 .context("failed to suspend VirtioGpu")?;
1120
1121 self.fence_handler_resources.lock().take();
1122
1123 let queues = if let Some(activation_resources) = self.activation_resources.take() {
1124 Some(vec![
1125 match Arc::try_unwrap(activation_resources.ctrl_queue.queue) {
1126 Ok(x) => x.into_inner(),
1127 Err(_) => panic!("too many refs on ctrl_queue"),
1128 },
1129 activation_resources.cursor_queue.queue.into_inner(),
1130 ])
1131 } else {
1132 None
1133 };
1134
1135 Ok(GpuDeactivationResources { queues })
1136 }
1137
1138 fn on_snapshot(&mut self) -> anyhow::Result<WorkerSnapshot> {
1139 Ok(WorkerSnapshot {
1140 fence_state_snapshot: self.fence_state.lock().snapshot(),
1141 virtio_gpu_snapshot: self
1142 .state
1143 .virtio_gpu
1144 .snapshot()
1145 .context("failed to snapshot VirtioGpu")?,
1146 })
1147 }
1148
1149 fn on_restore(&mut self, snapshot: WorkerSnapshot) -> anyhow::Result<()> {
1150 self.fence_state
1151 .lock()
1152 .restore(snapshot.fence_state_snapshot);
1153
1154 self.state
1155 .virtio_gpu
1156 .restore(snapshot.virtio_gpu_snapshot)
1157 .context("failed to restore VirtioGpu")?;
1158
1159 Ok(())
1160 }
1161
1162 fn run_until_sleep_or_exit(&mut self) -> anyhow::Result<WorkerStopReason> {
1163 let activation_resources = self
1164 .activation_resources
1165 .as_ref()
1166 .context("virtio gpu worker missing activation resources")?;
1167
1168 let display_desc =
1169 SafeDescriptor::try_from(&*self.state.display().borrow() as &dyn AsRawDescriptor)
1170 .context("failed getting event descriptor for display")?;
1171
1172 let ctrl_evt = activation_resources
1173 .ctrl_queue
1174 .queue
1175 .lock()
1176 .event()
1177 .try_clone()
1178 .context("failed to clone queue event")?;
1179 let cursor_evt = activation_resources
1180 .cursor_queue
1181 .queue
1182 .borrow()
1183 .event()
1184 .try_clone()
1185 .context("failed to clone queue event")?;
1186
1187 let mut event_manager = EventManager::build_with(&[
1188 (&ctrl_evt, WorkerToken::CtrlQueue),
1189 (&cursor_evt, WorkerToken::CursorQueue),
1190 (&display_desc, WorkerToken::Display),
1191 (
1192 self.gpu_control_tube.get_read_notifier(),
1193 WorkerToken::GpuControl,
1194 ),
1195 (&self.suspend_evt, WorkerToken::Sleep),
1196 (&self.kill_evt, WorkerToken::Kill),
1197 #[cfg(windows)]
1198 (
1199 self.gpu_display_wait_descriptor_ctrl_rd.get_read_notifier(),
1200 WorkerToken::DisplayDescriptorRequest,
1201 ),
1202 ])
1203 .context("failed creating gpu worker WaitContext")?;
1204
1205 let poll_desc: SafeDescriptor;
1206 if let Some(desc) = self.state.virtio_gpu.poll_descriptor() {
1207 poll_desc = desc;
1208 event_manager
1209 .add(&poll_desc, WorkerToken::VirtioGpuPoll)
1210 .context("failed adding poll event to WaitContext")?;
1211 }
1212
1213 self.resource_bridges
1214 .add_to_wait_context(&mut event_manager.wait_ctx);
1215
1216 loop {
1225 let events = event_manager
1226 .wait_ctx
1227 .wait()
1228 .context("failed polling for gpu worker events")?;
1229
1230 let mut signal_used_cursor = false;
1231 let mut signal_used_ctrl = false;
1232 let mut ctrl_available = false;
1233 let mut display_available = false;
1234 let mut needs_config_interrupt = false;
1235
1236 for event in events.iter().filter(|e| e.is_hungup) {
1239 if event.token == WorkerToken::GpuControl {
1240 return Ok(WorkerStopReason::Kill);
1241 }
1242 error!(
1243 "unhandled virtio-gpu worker event hang-up detected: {:?}",
1244 event.token
1245 );
1246 event_manager.delete(event.token);
1247 }
1248
1249 for event in events.iter().filter(|e| e.is_readable) {
1250 match event.token {
1251 WorkerToken::CtrlQueue => {
1252 let _ = ctrl_evt.wait();
1253 ctrl_available = true;
1256 }
1257 WorkerToken::CursorQueue => {
1258 let _ = cursor_evt.wait();
1259 if self.state.process_queue(
1260 &activation_resources.mem,
1261 &activation_resources.cursor_queue,
1262 ) {
1263 signal_used_cursor = true;
1264 }
1265 }
1266 WorkerToken::Display => {
1267 display_available = true;
1270 }
1271 #[cfg(windows)]
1272 WorkerToken::DisplayDescriptorRequest => {
1273 if let Ok(req) = self
1274 .gpu_display_wait_descriptor_ctrl_rd
1275 .recv::<ModifyWaitContext>()
1276 {
1277 match req {
1278 ModifyWaitContext::Add(desc) => {
1279 if let Err(e) =
1280 event_manager.wait_ctx.add(&desc, WorkerToken::Display)
1281 {
1282 error!(
1283 "failed to add extra descriptor from display \
1284 to GPU worker wait context: {:?}",
1285 e
1286 )
1287 }
1288 }
1289 }
1290 } else {
1291 error!("failed to receive ModifyWaitContext request.")
1292 }
1293 }
1294 WorkerToken::GpuControl => {
1295 let req = self
1296 .gpu_control_tube
1297 .recv()
1298 .context("failed to recv from gpu control socket")?;
1299 let resp = self.state.process_gpu_control_command(req);
1300
1301 if let GpuControlResult::DisplaysUpdated = resp {
1302 needs_config_interrupt = true;
1303 }
1304
1305 self.gpu_control_tube
1306 .send(&resp)
1307 .context("failed to send gpu control socket response")?;
1308 }
1309 WorkerToken::ResourceBridge { index } => {
1310 self.resource_bridges.set_should_process(index);
1311 }
1312 WorkerToken::VirtioGpuPoll => {
1313 self.state.event_poll();
1314 }
1315 WorkerToken::Sleep => {
1316 return Ok(WorkerStopReason::Sleep);
1317 }
1318 WorkerToken::Kill => {
1319 return Ok(WorkerStopReason::Kill);
1320 }
1321 }
1322 }
1323
1324 if display_available {
1325 match self.state.process_display() {
1326 ProcessDisplayResult::CloseRequested => {
1327 let _ = self.exit_evt_wrtube.send::<VmEventType>(&VmEventType::Exit);
1328 }
1329 ProcessDisplayResult::Error(_e) => {
1330 base::error!("Display processing failed, disabling display event handler.");
1331 event_manager.delete(WorkerToken::Display);
1332 }
1333 ProcessDisplayResult::Success => (),
1334 };
1335 }
1336
1337 if ctrl_available
1338 && self
1339 .state
1340 .process_queue(&activation_resources.mem, &activation_resources.ctrl_queue)
1341 {
1342 signal_used_ctrl = true;
1343 }
1344
1345 self.resource_bridges
1352 .process_resource_bridges(&mut self.state, &mut event_manager.wait_ctx);
1353
1354 if signal_used_ctrl {
1355 activation_resources.ctrl_queue.signal_used();
1356 }
1357
1358 if signal_used_cursor {
1359 activation_resources.cursor_queue.signal_used();
1360 }
1361
1362 if needs_config_interrupt {
1363 activation_resources.interrupt.signal_config_changed();
1364 }
1365 }
1366 }
1367}
1368
1369#[derive(Clone)]
1374pub enum DisplayBackend {
1375 #[cfg(any(target_os = "android", target_os = "linux"))]
1376 Wayland(Option<PathBuf>),
1378 #[cfg(any(target_os = "android", target_os = "linux"))]
1379 X(Option<String>),
1381 Stub,
1383 #[cfg(windows)]
1384 WinApi,
1386 #[cfg(feature = "android_display")]
1387 Android(String),
1392}
1393
1394impl DisplayBackend {
1395 fn build(
1396 &self,
1397 #[cfg(windows)] wndproc_thread: &mut Option<WindowProcedureThread>,
1398 #[cfg(windows)] gpu_display_wait_descriptor_ctrl: SendTube,
1399 ) -> std::result::Result<GpuDisplay, GpuDisplayError> {
1400 match self {
1401 #[cfg(any(target_os = "android", target_os = "linux"))]
1402 DisplayBackend::Wayland(path) => GpuDisplay::open_wayland(path.as_ref()),
1403 #[cfg(any(target_os = "android", target_os = "linux"))]
1404 DisplayBackend::X(display) => GpuDisplay::open_x(display.as_deref()),
1405 DisplayBackend::Stub => GpuDisplay::open_stub(),
1406 #[cfg(windows)]
1407 DisplayBackend::WinApi => match wndproc_thread.take() {
1408 Some(wndproc_thread) => GpuDisplay::open_winapi(
1409 wndproc_thread,
1410 None,
1411 gpu_display_wait_descriptor_ctrl,
1412 None,
1413 ),
1414 None => {
1415 error!("wndproc_thread is none");
1416 Err(GpuDisplayError::Allocate)
1417 }
1418 },
1419 #[cfg(feature = "android_display")]
1420 DisplayBackend::Android(service_name) => GpuDisplay::open_android(service_name),
1421 }
1422 }
1423}
1424
1425pub struct Gpu {
1426 exit_evt_wrtube: SendTube,
1427 pub gpu_control_tube: Option<Tube>,
1428 mapper: Arc<Mutex<Option<Box<dyn SharedMemoryMapper>>>>,
1429 resource_bridges: Option<ResourceBridges>,
1430 event_devices: Option<Vec<EventDevice>>,
1431 worker_suspend_evt: Option<Event>,
1432 worker_request_sender: Option<mpsc::Sender<WorkerRequest>>,
1433 worker_response_receiver: Option<mpsc::Receiver<anyhow::Result<WorkerResponse>>>,
1434 worker_state: WorkerState,
1435 worker_thread: Option<WorkerThread<()>>,
1436 display_backends: Vec<DisplayBackend>,
1437 display_params: Vec<GpuDisplayParameters>,
1438 display_event: Arc<AtomicBool>,
1439 gpu_parameters: GpuParameters,
1440 rutabaga_paths: Vec<RutabagaPath>,
1441 pci_address: Option<PciAddress>,
1442 pci_bar_size: u64,
1443 external_blob: bool,
1444 fixed_blob_mapping: bool,
1445 rutabaga_component: RutabagaComponentType,
1446 #[cfg(windows)]
1447 wndproc_thread: Option<WindowProcedureThread>,
1448 base_features: u64,
1449 udmabuf: bool,
1450 rutabaga_server_descriptor: Option<SafeDescriptor>,
1451 #[cfg(windows)]
1452 gpu_display_wait_descriptor_ctrl_wr: SendTube,
1456 #[cfg(windows)]
1457 gpu_display_wait_descriptor_ctrl_rd: Option<RecvTube>,
1460 capset_mask: u64,
1461 #[cfg(any(target_os = "android", target_os = "linux"))]
1462 gpu_cgroup_path: Option<PathBuf>,
1463 snapshot_scratch_directory: Option<PathBuf>,
1464}
1465
1466impl Gpu {
1467 pub fn new(
1468 exit_evt_wrtube: SendTube,
1469 gpu_control_tube: Tube,
1470 resource_bridges: Vec<Tube>,
1471 display_backends: Vec<DisplayBackend>,
1472 gpu_parameters: &GpuParameters,
1473 rutabaga_server_descriptor: Option<SafeDescriptor>,
1474 event_devices: Vec<EventDevice>,
1475 base_features: u64,
1476 paths: &BTreeMap<String, PathBuf>,
1477 #[cfg(windows)] wndproc_thread: WindowProcedureThread,
1478 #[cfg(any(target_os = "android", target_os = "linux"))] gpu_cgroup_path: Option<&PathBuf>,
1479 ) -> Gpu {
1480 let mut display_params = gpu_parameters.display_params.clone();
1481 if display_params.is_empty() {
1482 display_params.push(Default::default());
1483 }
1484
1485 let mut rutabaga_paths: Vec<RutabagaPath> = Vec::new();
1486 for (name, path) in paths {
1487 match &name[..] {
1488 "" => rutabaga_paths.push(RutabagaPath {
1489 path: path.clone(),
1490 path_type: RUTABAGA_PATH_TYPE_WAYLAND,
1491 }),
1492 _ => error!("unknown rutabaga path"),
1493 }
1494 }
1495
1496 let component = match gpu_parameters.mode {
1497 GpuMode::Mode2D => RutabagaComponentType::Rutabaga2D,
1498 GpuMode::ModeVirglRenderer => RutabagaComponentType::VirglRenderer,
1499 GpuMode::ModeGfxstream => RutabagaComponentType::Gfxstream,
1500 };
1501
1502 #[cfg(windows)]
1503 let (gpu_display_wait_descriptor_ctrl_wr, gpu_display_wait_descriptor_ctrl_rd) =
1504 Tube::directional_pair().expect("failed to create wait descriptor control pair.");
1505
1506 Gpu {
1507 exit_evt_wrtube,
1508 gpu_control_tube: Some(gpu_control_tube),
1509 mapper: Arc::new(Mutex::new(None)),
1510 resource_bridges: Some(ResourceBridges::new(resource_bridges)),
1511 event_devices: Some(event_devices),
1512 worker_request_sender: None,
1513 worker_response_receiver: None,
1514 worker_suspend_evt: None,
1515 worker_state: WorkerState::Inactive,
1516 worker_thread: None,
1517 display_backends,
1518 display_params,
1519 display_event: Arc::new(AtomicBool::new(false)),
1520 gpu_parameters: gpu_parameters.clone(),
1521 rutabaga_paths,
1522 pci_address: gpu_parameters.pci_address,
1523 pci_bar_size: gpu_parameters.pci_bar_size,
1524 external_blob: gpu_parameters.external_blob,
1525 fixed_blob_mapping: gpu_parameters.fixed_blob_mapping,
1526 rutabaga_component: component,
1527 #[cfg(windows)]
1528 wndproc_thread: Some(wndproc_thread),
1529 base_features,
1530 udmabuf: gpu_parameters.udmabuf,
1531 rutabaga_server_descriptor,
1532 #[cfg(windows)]
1533 gpu_display_wait_descriptor_ctrl_wr,
1534 #[cfg(windows)]
1535 gpu_display_wait_descriptor_ctrl_rd: Some(gpu_display_wait_descriptor_ctrl_rd),
1536 capset_mask: gpu_parameters.capset_mask,
1537 #[cfg(any(target_os = "android", target_os = "linux"))]
1538 gpu_cgroup_path: gpu_cgroup_path.cloned(),
1539 snapshot_scratch_directory: gpu_parameters.snapshot_scratch_path.clone(),
1540 }
1541 }
1542
1543 pub fn initialize_frontend(
1547 &mut self,
1548 fence_state: Arc<Mutex<FenceState>>,
1549 fence_handler: RutabagaFenceHandler,
1550 mapper: Arc<Mutex<Option<Box<dyn SharedMemoryMapper>>>>,
1551 ) -> Option<Frontend> {
1552 let rutabaga_server_descriptor = self.rutabaga_server_descriptor.as_ref().map(|d| {
1553 to_rutabaga_descriptor(d.try_clone().expect("failed to clone server descriptor"))
1554 });
1555
1556 let rutabaga = build_rutabaga(
1557 &self.gpu_parameters,
1558 &self.display_params,
1559 self.rutabaga_component,
1560 self.rutabaga_paths.clone(),
1561 rutabaga_server_descriptor,
1562 fence_handler,
1563 )
1564 .map_err(|e| error!("failed to build rutabaga {}", e))
1565 .ok()?;
1566
1567 let mut virtio_gpu = build(
1568 &self.display_backends,
1569 self.display_params.clone(),
1570 self.display_event.clone(),
1571 rutabaga,
1572 mapper,
1573 self.external_blob,
1574 self.fixed_blob_mapping,
1575 #[cfg(windows)]
1576 &mut self.wndproc_thread,
1577 self.udmabuf,
1578 #[cfg(windows)]
1579 self.gpu_display_wait_descriptor_ctrl_wr
1580 .try_clone()
1581 .expect("failed to clone wait context control channel"),
1582 self.snapshot_scratch_directory.clone(),
1583 )?;
1584
1585 for event_device in self.event_devices.take().expect("missing event_devices") {
1586 virtio_gpu
1587 .import_event_device(event_device)
1588 .expect("failed to import event device");
1590 }
1591
1592 Some(Frontend::new(virtio_gpu, fence_state))
1593 }
1594
1595 fn start_worker_thread(&mut self) {
1597 let suspend_evt = Event::new().unwrap();
1598 let suspend_evt_copy = suspend_evt
1599 .try_clone()
1600 .context("error cloning suspend event")
1601 .unwrap();
1602
1603 let exit_evt_wrtube = self
1604 .exit_evt_wrtube
1605 .try_clone()
1606 .context("error cloning exit tube")
1607 .unwrap();
1608
1609 let gpu_control_tube = self
1610 .gpu_control_tube
1611 .take()
1612 .context("gpu_control_tube is none")
1613 .unwrap();
1614
1615 let resource_bridges = self
1616 .resource_bridges
1617 .take()
1618 .context("resource_bridges is none")
1619 .unwrap();
1620
1621 let display_backends = self.display_backends.clone();
1622 let display_params = self.display_params.clone();
1623 let display_event = self.display_event.clone();
1624 let event_devices = self.event_devices.take().expect("missing event_devices");
1625 let external_blob = self.external_blob;
1626 let fixed_blob_mapping = self.fixed_blob_mapping;
1627 let udmabuf = self.udmabuf;
1628 let snapshot_scratch_directory = self.snapshot_scratch_directory.clone();
1629
1630 #[cfg(windows)]
1631 let mut wndproc_thread = self.wndproc_thread.take();
1632
1633 #[cfg(windows)]
1634 let gpu_display_wait_descriptor_ctrl_wr = self
1635 .gpu_display_wait_descriptor_ctrl_wr
1636 .try_clone()
1637 .expect("failed to clone wait context ctrl channel");
1638
1639 #[cfg(windows)]
1640 let gpu_display_wait_descriptor_ctrl_rd = self
1641 .gpu_display_wait_descriptor_ctrl_rd
1642 .take()
1643 .expect("failed to take gpu_display_wait_descriptor_ctrl_rd");
1644
1645 #[cfg(any(target_os = "android", target_os = "linux"))]
1646 let gpu_cgroup_path = self.gpu_cgroup_path.clone();
1647
1648 let mapper = Arc::clone(&self.mapper);
1649
1650 let gpu_parameters = self.gpu_parameters.clone();
1651 let rutabaga_paths = self.rutabaga_paths.clone();
1652 let rutabaga_component = self.rutabaga_component;
1653 let rutabaga_server_descriptor = self.rutabaga_server_descriptor.as_ref().map(|d| {
1654 to_rutabaga_descriptor(d.try_clone().expect("failed to clone server descriptor"))
1655 });
1656
1657 let (init_finished_tx, init_finished_rx) = mpsc::channel();
1658
1659 let (worker_request_sender, worker_request_receiver) = mpsc::channel();
1660 let (worker_response_sender, worker_response_receiver) = mpsc::channel();
1661
1662 let worker_thread = WorkerThread::start("v_gpu", move |kill_evt| {
1663 #[cfg(any(target_os = "android", target_os = "linux"))]
1664 if let Some(cgroup_path) = gpu_cgroup_path {
1665 move_task_to_cgroup(cgroup_path, base::gettid())
1666 .expect("Failed to move v_gpu into requested cgroup");
1667 }
1668
1669 let mut worker = Worker::new(
1670 gpu_parameters,
1671 rutabaga_paths,
1672 rutabaga_component,
1673 rutabaga_server_descriptor,
1674 display_backends,
1675 display_params,
1676 display_event,
1677 mapper,
1678 event_devices,
1679 external_blob,
1680 fixed_blob_mapping,
1681 udmabuf,
1682 worker_request_receiver,
1683 worker_response_sender,
1684 exit_evt_wrtube,
1685 gpu_control_tube,
1686 resource_bridges,
1687 suspend_evt_copy,
1688 kill_evt,
1689 #[cfg(windows)]
1690 wndproc_thread,
1691 #[cfg(windows)]
1692 gpu_display_wait_descriptor_ctrl_rd,
1693 #[cfg(windows)]
1694 gpu_display_wait_descriptor_ctrl_wr,
1695 snapshot_scratch_directory,
1696 )
1697 .expect("Failed to create virtio gpu worker thread");
1698
1699 let _ = init_finished_tx.send(());
1701
1702 worker.run()
1703 });
1704
1705 self.worker_request_sender = Some(worker_request_sender);
1706 self.worker_response_receiver = Some(worker_response_receiver);
1707 self.worker_suspend_evt = Some(suspend_evt);
1708 self.worker_state = WorkerState::Inactive;
1709 self.worker_thread = Some(worker_thread);
1710
1711 match init_finished_rx.recv() {
1712 Ok(()) => {}
1713 Err(mpsc::RecvError) => panic!("virtio-gpu worker thread init failed"),
1714 }
1715 }
1716
1717 fn stop_worker_thread(&mut self) {
1718 self.worker_request_sender.take();
1719 self.worker_response_receiver.take();
1720 self.worker_suspend_evt.take();
1721 if let Some(worker_thread) = self.worker_thread.take() {
1722 worker_thread.stop();
1723 }
1724 }
1725
1726 fn get_config(&self) -> virtio_gpu_config {
1727 let mut events_read = 0;
1728
1729 if self.display_event.load(Ordering::Relaxed) {
1730 events_read |= VIRTIO_GPU_EVENT_DISPLAY;
1731 }
1732
1733 let num_capsets = match self.capset_mask {
1734 0 => match self.rutabaga_component {
1735 RutabagaComponentType::Rutabaga2D => 0,
1736 RutabagaComponentType::VirglRenderer => 3,
1737 RutabagaComponentType::Gfxstream => 1,
1738 _ => unimplemented!(),
1739 },
1740 _ => self.capset_mask.count_ones(),
1741 };
1742
1743 virtio_gpu_config {
1744 events_read: Le32::from(events_read),
1745 events_clear: Le32::from(0),
1746 num_scanouts: Le32::from(VIRTIO_GPU_MAX_SCANOUTS as u32),
1747 num_capsets: Le32::from(num_capsets),
1748 }
1749 }
1750
1751 pub fn send_exit_evt(&self) -> anyhow::Result<()> {
1753 self.exit_evt_wrtube
1754 .send::<VmEventType>(&VmEventType::Exit)
1755 .context("failed to send exit event")
1756 }
1757}
1758
1759impl VirtioDevice for Gpu {
1760 fn keep_rds(&self) -> Vec<RawDescriptor> {
1761 let mut keep_rds = Vec::new();
1762
1763 #[cfg(any(target_os = "android", target_os = "linux"))]
1769 if cfg!(debug_assertions) {
1770 keep_rds.push(libc::STDOUT_FILENO);
1771 keep_rds.push(libc::STDERR_FILENO);
1772 }
1773
1774 if let Some(ref mapper) = *self.mapper.lock() {
1775 if let Some(descriptor) = mapper.as_raw_descriptor() {
1776 keep_rds.push(descriptor);
1777 }
1778 }
1779
1780 if let Some(ref rutabaga_server_descriptor) = self.rutabaga_server_descriptor {
1781 keep_rds.push(rutabaga_server_descriptor.as_raw_descriptor());
1782 }
1783
1784 keep_rds.push(self.exit_evt_wrtube.as_raw_descriptor());
1785
1786 if let Some(gpu_control_tube) = &self.gpu_control_tube {
1787 keep_rds.push(gpu_control_tube.as_raw_descriptor());
1788 }
1789
1790 if let Some(resource_bridges) = &self.resource_bridges {
1791 resource_bridges.append_raw_descriptors(&mut keep_rds);
1792 }
1793
1794 for event_device in self.event_devices.iter().flatten() {
1795 keep_rds.push(event_device.as_raw_descriptor());
1796 }
1797
1798 keep_rds
1799 }
1800
1801 fn device_type(&self) -> DeviceType {
1802 DeviceType::Gpu
1803 }
1804
1805 fn queue_max_sizes(&self) -> &[u16] {
1806 QUEUE_SIZES
1807 }
1808
1809 fn features(&self) -> u64 {
1810 let mut virtio_gpu_features = 1 << VIRTIO_GPU_F_EDID | 1 << VIRTIO_GPU_F_RESOURCE_BLOB;
1811
1812 if self.rutabaga_component != RutabagaComponentType::Rutabaga2D || self.capset_mask != 0 {
1815 virtio_gpu_features |= 1 << VIRTIO_GPU_F_VIRGL
1816 | 1 << VIRTIO_GPU_F_RESOURCE_UUID
1817 | 1 << VIRTIO_GPU_F_CONTEXT_INIT;
1818
1819 if self.udmabuf {
1820 virtio_gpu_features |= 1 << VIRTIO_GPU_F_CREATE_GUEST_HANDLE;
1821 }
1822 }
1823
1824 self.base_features | virtio_gpu_features
1825 }
1826
1827 fn ack_features(&mut self, value: u64) {
1828 let _ = value;
1829 }
1830
1831 fn read_config(&self, offset: u64, data: &mut [u8]) {
1832 copy_config(data, 0, self.get_config().as_bytes(), offset);
1833 }
1834
1835 fn write_config(&mut self, offset: u64, data: &[u8]) {
1836 let mut cfg = self.get_config();
1837 copy_config(cfg.as_mut_bytes(), offset, data, 0);
1838 if (cfg.events_clear.to_native() & VIRTIO_GPU_EVENT_DISPLAY) != 0 {
1839 self.display_event.store(false, Ordering::Relaxed);
1840 }
1841 }
1842
1843 fn on_device_sandboxed(&mut self) {
1844 self.start_worker_thread();
1854 }
1855
1856 fn activate(
1857 &mut self,
1858 mem: GuestMemory,
1859 interrupt: Interrupt,
1860 mut queues: BTreeMap<usize, Queue>,
1861 ) -> anyhow::Result<()> {
1862 if queues.len() != QUEUE_SIZES.len() {
1863 return Err(anyhow!(
1864 "expected {} queues, got {}",
1865 QUEUE_SIZES.len(),
1866 queues.len()
1867 ));
1868 }
1869
1870 let ctrl_queue = SharedQueueReader::new(queues.remove(&0).unwrap());
1871 let cursor_queue = LocalQueueReader::new(queues.remove(&1).unwrap());
1872
1873 self.worker_request_sender
1874 .as_ref()
1875 .context("worker thread missing on activate?")?
1876 .send(WorkerRequest::Activate(WorkerActivateRequest {
1877 resources: GpuActivationResources {
1878 mem,
1879 interrupt,
1880 ctrl_queue,
1881 cursor_queue,
1882 },
1883 }))
1884 .map_err(|e| anyhow!("failed to send virtio gpu worker activate request: {:?}", e))?;
1885
1886 self.worker_response_receiver
1887 .as_ref()
1888 .context("worker thread missing on activate?")?
1889 .recv()
1890 .inspect(|_| self.worker_state = WorkerState::Active)
1891 .inspect_err(|_| self.worker_state = WorkerState::Error)
1892 .context("failed to receive response for virtio gpu worker resume request")??;
1893
1894 Ok(())
1895 }
1896
1897 fn pci_address(&self) -> Option<PciAddress> {
1898 self.pci_address
1899 }
1900
1901 fn get_shared_memory_region(&self) -> Option<SharedMemoryRegion> {
1902 Some(SharedMemoryRegion {
1903 id: VIRTIO_GPU_SHM_ID_HOST_VISIBLE,
1904 length: self.pci_bar_size,
1905 })
1906 }
1907
1908 fn set_shared_memory_mapper(&mut self, mapper: Box<dyn SharedMemoryMapper>) {
1909 self.mapper.lock().replace(mapper);
1910 }
1911
1912 fn expose_shmem_descriptors_with_viommu(&self) -> bool {
1913 !self.fixed_blob_mapping
1915 }
1916
1917 fn get_shared_memory_prepare_type(&mut self) -> SharedMemoryPrepareType {
1918 if self.fixed_blob_mapping {
1919 let cache_type = if cfg!(feature = "noncoherent-dma") {
1920 MemCacheType::CacheNonCoherent
1921 } else {
1922 MemCacheType::CacheCoherent
1923 };
1924 SharedMemoryPrepareType::SingleMappingOnFirst(cache_type)
1925 } else {
1926 SharedMemoryPrepareType::DynamicPerMapping
1927 }
1928 }
1929
1930 fn virtio_sleep(&mut self) -> anyhow::Result<Option<BTreeMap<usize, Queue>>> {
1940 match self.worker_state {
1941 WorkerState::Error => {
1942 return Err(anyhow!(
1943 "failed to sleep virtio gpu worker which is in error state"
1944 ));
1945 }
1946 WorkerState::Inactive => {
1947 return Ok(None);
1948 }
1949 _ => (),
1950 };
1951
1952 if let (
1953 Some(worker_request_sender),
1954 Some(worker_response_receiver),
1955 Some(worker_suspend_evt),
1956 ) = (
1957 &self.worker_request_sender,
1958 &self.worker_response_receiver,
1959 &self.worker_suspend_evt,
1960 ) {
1961 worker_request_sender
1962 .send(WorkerRequest::Suspend)
1963 .map_err(|e| {
1964 anyhow!(
1965 "failed to send suspend request to virtio gpu worker: {:?}",
1966 e
1967 )
1968 })?;
1969
1970 worker_suspend_evt
1971 .signal()
1972 .context("failed to signal virtio gpu worker suspend event")?;
1973
1974 let response = worker_response_receiver
1975 .recv()
1976 .inspect(|_| self.worker_state = WorkerState::Inactive)
1977 .inspect_err(|_| self.worker_state = WorkerState::Error)
1978 .context("failed to receive response for virtio gpu worker suspend request")??;
1979
1980 worker_suspend_evt
1981 .reset()
1982 .context("failed to reset virtio gpu worker suspend event")?;
1983
1984 match response {
1985 WorkerResponse::Suspend(deactivation_resources) => Ok(deactivation_resources
1986 .queues
1987 .map(|q| q.into_iter().enumerate().collect())),
1988 _ => {
1989 panic!("unexpected response from virtio gpu worker sleep request");
1990 }
1991 }
1992 } else {
1993 Err(anyhow!("virtio gpu worker not available for sleep"))
1994 }
1995 }
1996
1997 fn virtio_wake(
1998 &mut self,
1999 queues_state: Option<(GuestMemory, Interrupt, BTreeMap<usize, Queue>)>,
2000 ) -> anyhow::Result<()> {
2001 match self.worker_state {
2002 WorkerState::Error => {
2003 return Err(anyhow!(
2004 "failed to wake virtio gpu worker which is in error state"
2005 ));
2006 }
2007 WorkerState::Active => {
2008 return Ok(());
2009 }
2010 _ => (),
2011 };
2012
2013 match queues_state {
2014 None => Ok(()),
2015 Some((mem, interrupt, queues)) => {
2016 self.activate(mem, interrupt, queues)?;
2020 Ok(())
2021 }
2022 }
2023 }
2024
2025 fn virtio_snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
2026 match self.worker_state {
2027 WorkerState::Error => {
2028 return Err(anyhow!(
2029 "failed to snapshot virtio gpu worker which is in error state"
2030 ));
2031 }
2032 WorkerState::Active => {
2033 return Err(anyhow!(
2034 "failed to snapshot virtio gpu worker which is in active state"
2035 ));
2036 }
2037 _ => (),
2038 };
2039
2040 if let (Some(worker_request_sender), Some(worker_response_receiver)) =
2041 (&self.worker_request_sender, &self.worker_response_receiver)
2042 {
2043 worker_request_sender
2044 .send(WorkerRequest::Snapshot)
2045 .map_err(|e| {
2046 anyhow!(
2047 "failed to send snapshot request to virtio gpu worker: {:?}",
2048 e
2049 )
2050 })?;
2051
2052 match worker_response_receiver
2053 .recv()
2054 .inspect_err(|_| self.worker_state = WorkerState::Error)
2055 .context("failed to receive response for virtio gpu worker suspend request")??
2056 {
2057 WorkerResponse::Snapshot(snapshot) => Ok(AnySnapshot::to_any(snapshot)?),
2058 _ => {
2059 panic!("unexpected response from virtio gpu worker sleep request");
2060 }
2061 }
2062 } else {
2063 Err(anyhow!("virtio gpu worker not available for snapshot"))
2064 }
2065 }
2066
2067 fn virtio_restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
2068 match self.worker_state {
2069 WorkerState::Error => {
2070 return Err(anyhow!(
2071 "failed to restore virtio gpu worker which is in error state"
2072 ));
2073 }
2074 WorkerState::Active => {
2075 return Err(anyhow!(
2076 "failed to restore virtio gpu worker which is in active state"
2077 ));
2078 }
2079 _ => (),
2080 };
2081
2082 let snapshot: WorkerSnapshot = AnySnapshot::from_any(data)?;
2083
2084 if let (Some(worker_request_sender), Some(worker_response_receiver)) =
2085 (&self.worker_request_sender, &self.worker_response_receiver)
2086 {
2087 worker_request_sender
2088 .send(WorkerRequest::Restore(snapshot))
2089 .map_err(|e| {
2090 anyhow!(
2091 "failed to send suspend request to virtio gpu worker: {:?}",
2092 e
2093 )
2094 })?;
2095
2096 let response = worker_response_receiver
2097 .recv()
2098 .inspect_err(|_| self.worker_state = WorkerState::Error)
2099 .context("failed to receive response for virtio gpu worker suspend request")??;
2100
2101 match response {
2102 WorkerResponse::Ok => Ok(()),
2103 _ => {
2104 panic!("unexpected response from virtio gpu worker sleep request");
2105 }
2106 }
2107 } else {
2108 Err(anyhow!("virtio gpu worker not available for restore"))
2109 }
2110 }
2111
2112 fn reset(&mut self) -> anyhow::Result<()> {
2113 self.stop_worker_thread();
2114 Ok(())
2115 }
2116}
2117
2118impl Drop for Gpu {
2119 fn drop(&mut self) {
2120 let _ = self.reset();
2121 }
2122}
2123
2124struct ResourceBridges {
2126 resource_bridges: Vec<Tube>,
2127 should_process: Vec<bool>,
2128}
2129
2130impl ResourceBridges {
2131 pub fn new(resource_bridges: Vec<Tube>) -> Self {
2132 #[cfg(windows)]
2133 assert!(
2134 resource_bridges.is_empty(),
2135 "resource bridges are not supported on Windows"
2136 );
2137
2138 let mut resource_bridges = Self {
2139 resource_bridges,
2140 should_process: Default::default(),
2141 };
2142 resource_bridges.reset_should_process();
2143 resource_bridges
2144 }
2145
2146 pub fn append_raw_descriptors(&self, rds: &mut Vec<RawDescriptor>) {
2148 for bridge in &self.resource_bridges {
2149 rds.push(bridge.as_raw_descriptor());
2150 }
2151 }
2152
2153 pub fn add_to_wait_context(&self, wait_ctx: &mut WaitContext<WorkerToken>) {
2155 for (index, bridge) in self.resource_bridges.iter().enumerate() {
2156 if let Err(e) = wait_ctx.add(bridge, WorkerToken::ResourceBridge { index }) {
2157 error!("failed to add resource bridge to WaitContext: {}", e);
2158 }
2159 }
2160 }
2161
2162 pub fn set_should_process(&mut self, index: usize) {
2165 self.should_process[index] = true;
2166 }
2167
2168 pub fn process_resource_bridges(
2171 &mut self,
2172 state: &mut Frontend,
2173 wait_ctx: &mut WaitContext<WorkerToken>,
2174 ) {
2175 for (bridge, &should_process) in self.resource_bridges.iter().zip(&self.should_process) {
2176 if should_process {
2177 if let Err(e) = state.process_resource_bridge(bridge) {
2178 error!("Failed to process resource bridge: {:#}", e);
2179 error!("Removing that resource bridge from the wait context.");
2180 wait_ctx.delete(bridge).unwrap_or_else(|e| {
2181 error!("Failed to remove faulty resource bridge: {:#}", e)
2182 });
2183 }
2184 }
2185 }
2186 self.reset_should_process();
2187 }
2188
2189 fn reset_should_process(&mut self) {
2190 self.should_process.clear();
2191 self.should_process
2192 .resize(self.resource_bridges.len(), false);
2193 }
2194}