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