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