devices/virtio/gpu/
mod.rs

1// Copyright 2018 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5mod 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
97// First queue is for virtio gpu commands. Second queue is for cursor commands, which we expect
98// there to be fewer of.
99const 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    // Customize serialization to avoid errors when trying to use objects as keys in JSON
150    // dictionaries.
151    #[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
235/// Initializes the virtio_gpu state tracker.
236fn 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
286/// Resources used by the fence handler.
287pub struct FenceHandlerActivationResources<Q>
288where
289    Q: QueueReader + Send + Clone + 'static,
290{
291    pub mem: GuestMemory,
292    pub ctrl_queue: Q,
293}
294
295/// Create a handler that writes into the completed fence queue
296pub 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            // Limits the lifetime of `fence_state`:
308            {
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                // TODO(dverkamp): use `drain_filter()` when it is stabilized
319                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                // Update the last completed fence for this context
335                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    /// Returns the internal connection to the compositor and its associated state.
366    pub fn display(&mut self) -> &Rc<RefCell<GpuDisplay>> {
367        self.virtio_gpu.display()
368    }
369
370    /// Processes the internal `display` events and returns `true` if any display was closed.
371    pub fn process_display(&mut self) -> ProcessDisplayResult {
372        self.virtio_gpu.process_display()
373    }
374
375    /// Processes incoming requests on `resource_bridge`.
376    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    /// Processes the GPU control command and returns the result with a bool indicating if the
391    /// GPU device's config needs to be updated.
392    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                    // Silently accept empty command buffers to allow for
585                    // benchmarking.
586                    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                // As of v4.19, virtio-gpu kms only really uses these formats.  If that changes,
635                // the following may have to change too.
636                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                        // Log the details for triage.
669                        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    /// Processes virtio messages on `queue`.
694    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            // Prepare the response now, even if it is going to wait until
767            // fence is complete.
768            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                // In case the fence is signaled immediately after creation, don't add a return
780                // FenceDescriptor.
781                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            // No fence (or already completed fence), respond now.
795        }
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    // Only allow virglrenderer to fork its own render server when explicitly requested.
935    // Caller can enforce its own restrictions (e.g. not allowed when sandboxed) and set the
936    // allow flag appropriately.
937    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                // We lost the `EventDevice`, so fail hard.
1022                .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        // This loop effectively only runs while the worker is inactive. Once activated via
1044        // a `WorkerRequest::Activate`, the worker will remain in `run_until_sleep_or_exit()`
1045        // until suspended via `kill_evt` or `suspend_evt` being signaled.
1046        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        // TODO(davidriley): The entire main loop processing is somewhat racey and incorrect with
1212        // respect to cursor vs control queue processing.  As both currently and originally
1213        // written, while the control queue is only processed/read from after the the cursor queue
1214        // is finished, the entire queue will be processed at that time.  The end effect of this
1215        // racyiness is that control queue descriptors that are issued after cursors descriptors
1216        // might be handled first instead of the other way around.  In practice, the cursor queue
1217        // isn't used so this isn't a huge issue.
1218
1219        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            // Remove event triggers that have been hung-up to prevent unnecessary worker wake-ups
1232            // (see b/244486346#comment62 for context).
1233            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                        // Set flag that control queue is available to be read, but defer reading
1249                        // until rest of the events are processed.
1250                        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                        // We only need to process_display once-per-wake, regardless of how many
1263                        // WorkerToken::Display events are received.
1264                        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            // Process the entire control queue before the resource bridge in case a resource is
1341            // created or destroyed by the control queue. Processing the resource bridge first may
1342            // lead to a race condition.
1343            // TODO(davidriley): This is still inherently racey if both the control queue request
1344            // and the resource bridge request come in at the same time after the control queue is
1345            // processed above and before the corresponding bridge is processed below.
1346            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/// Indicates a backend that should be tried for the gpu to use for display.
1365///
1366/// Several instances of this enum are used in an ordered list to give the gpu device many backends
1367/// to use as fallbacks in case some do not work.
1368#[derive(Clone)]
1369pub enum DisplayBackend {
1370    #[cfg(any(target_os = "android", target_os = "linux"))]
1371    /// Use the wayland backend with the given socket path if given.
1372    Wayland(Option<PathBuf>),
1373    #[cfg(any(target_os = "android", target_os = "linux"))]
1374    /// Open a connection to the X server at the given display if given.
1375    X(Option<String>),
1376    /// Emulate a display without actually displaying it.
1377    Stub,
1378    #[cfg(windows)]
1379    /// Open a window using WinAPI.
1380    WinApi,
1381    #[cfg(feature = "android_display")]
1382    /// The display buffer is backed by an Android surface. The surface is set via an AIDL service
1383    /// that the backend hosts. Currently, the AIDL service is registered to the service manager
1384    /// using the name given here. The entity holding the surface is expected to locate the service
1385    /// via this name, and pass the surface to it.
1386    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                    /* win_metrics= */ 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    /// Because the Windows GpuDisplay can't expose an epollfd, it has to inform the GPU worker
1447    /// which descriptors to add to its wait context. That's what this Tube is used for (it is
1448    /// provided to each display backend.
1449    gpu_display_wait_descriptor_ctrl_wr: SendTube,
1450    #[cfg(windows)]
1451    /// The GPU worker uses this Tube to receive the descriptors that should be added to its wait
1452    /// context.
1453    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    /// Initializes the internal device state so that it can begin processing virtqueues.
1537    ///
1538    /// Only used by vhost-user GPU.
1539    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                // We lost the `EventDevice`, so fail hard.
1581                .expect("failed to import event device");
1582        }
1583
1584        Some(Frontend::new(virtio_gpu, fence_state))
1585    }
1586
1587    // This is not invoked when running with vhost-user GPU.
1588    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            // Tell the parent thread that the init phase is complete.
1690            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    /// Send a request to exit the process to VMM.
1742    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        // To find the RawDescriptor associated with stdout and stderr on Windows is difficult.
1754        // Resource bridges are used only for Wayland displays. There is also no meaningful way
1755        // casting the underlying DMA buffer wrapped in File to a copyable RawDescriptor.
1756        // TODO(davidriley): Remove once virgl has another path to include
1757        // debugging logs.
1758        #[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 a non-2D component is specified, enable 3D features.  It is possible to run display
1803        // contexts without 3D backend (i.e, gfxstream / virglrender), so check for that too.
1804        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        // Unlike most Virtio devices which start their worker thread in activate(),
1831        // the Gpu's worker thread is started earlier here so that rutabaga and the
1832        // underlying render server have a chance to initialize before the guest OS
1833        // starts. This is needed because the Virtio GPU kernel module has a timeout
1834        // for some calls during initialization and some host GPU drivers have been
1835        // observed to be extremely slow to initialize on fresh GCE instances. The
1836        // entire worker thread is started here (as opposed to just initializing
1837        // rutabaga and the underlying render server) as OpenGL based renderers may
1838        // expect to be initialized on the same thread that later processes commands.
1839        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        // TODO(b/323368701): integrate with fixed_blob_mapping so this can always return true.
1900        !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    // Notes on sleep/wake/snapshot/restore functionality.
1917    //
1918    //   * Only 2d mode is supported so far.
1919    //   * We only snapshot the state relevant to the virtio-gpu 2d mode protocol (i.e. scanouts,
1920    //     resources, fences).
1921    //   * The GpuDisplay is recreated from scratch, we don't want to snapshot the state of a
1922    //     Wayland socket (for example).
1923    //   * No state about pending virtio requests needs to be snapshotted because the 2d backend
1924    //     completes them synchronously.
1925    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                // TODO(khei): activate is just what we want at the moment, but we should probably
2003                // move it into a "start workers" function to make it obvious that it isn't
2004                // strictly used for activate events.
2005                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
2110/// This struct takes the ownership of resource bridges and tracks which ones should be processed.
2111struct 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    // Appends raw descriptors of all resource bridges to the given vector.
2133    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    /// Adds all resource bridges to WaitContext.
2140    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    /// Marks that the resource bridge at the given index should be processed when
2149    /// `process_resource_bridges()` is called.
2150    pub fn set_should_process(&mut self, index: usize) {
2151        self.should_process[index] = true;
2152    }
2153
2154    /// Processes all resource bridges that have been marked as should be processed.  The markings
2155    /// will be cleared before returning. Faulty resource bridges will be removed from WaitContext.
2156    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}