Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/virtio/vhost_user_backend/gpu/sys/windows.rs
5394 views
1
// Copyright 2022 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
use std::cell::RefCell;
6
use std::collections::BTreeMap;
7
use std::path::PathBuf;
8
use std::rc::Rc;
9
use std::sync::Arc;
10
11
use anyhow::bail;
12
use anyhow::Context;
13
use argh::FromArgs;
14
use base::error;
15
use base::info;
16
use base::Event;
17
use base::FromRawDescriptor;
18
use base::RawDescriptor;
19
use base::SafeDescriptor;
20
use base::SendTube;
21
use base::StreamChannel;
22
use base::Tube;
23
use cros_async::AsyncTube;
24
use cros_async::AsyncWrapper;
25
use cros_async::EventAsync;
26
use cros_async::Executor;
27
use gpu_display::EventDevice;
28
use gpu_display::WindowProcedureThread;
29
use gpu_display::WindowProcedureThreadBuilder;
30
use hypervisor::ProtectionType;
31
use proc_init::common_child_setup;
32
use proc_init::CommonChildStartupArgs;
33
use serde::Deserialize;
34
use serde::Serialize;
35
use sync::Mutex;
36
use tube_transporter::TubeToken;
37
use vm_control::gpu::GpuControlCommand;
38
use vm_control::gpu::GpuControlResult;
39
40
use crate::virtio;
41
use crate::virtio::gpu;
42
use crate::virtio::gpu::ProcessDisplayResult;
43
use crate::virtio::vhost_user_backend::gpu::GpuBackend;
44
use crate::virtio::vhost_user_backend::handler::sys::windows::read_from_tube_transporter;
45
use crate::virtio::vhost_user_backend::handler::sys::windows::run_handler;
46
use crate::virtio::vhost_user_backend::handler::DeviceRequestHandler;
47
use crate::virtio::Gpu;
48
use crate::virtio::GpuDisplayParameters;
49
use crate::virtio::GpuParameters;
50
use crate::virtio::Interrupt;
51
52
pub mod generic;
53
pub use generic as product;
54
55
async fn run_display(
56
display: EventAsync,
57
state: Rc<RefCell<gpu::Frontend>>,
58
gpu: Rc<RefCell<gpu::Gpu>>,
59
) {
60
loop {
61
if let Err(e) = display.next_val().await {
62
error!(
63
"Failed to wait for display context to become readable: {}",
64
e
65
);
66
break;
67
}
68
69
match state.borrow_mut().process_display() {
70
ProcessDisplayResult::Error(e) => {
71
error!("Failed to process display events: {}", e);
72
break;
73
}
74
ProcessDisplayResult::CloseRequested => {
75
let res = gpu.borrow().send_exit_evt();
76
if res.is_err() {
77
error!("Failed to send exit event: {:?}", res);
78
}
79
break;
80
}
81
ProcessDisplayResult::Success => {}
82
}
83
}
84
}
85
86
async fn run_gpu_control_command_handler(
87
mut gpu_control_tube: AsyncTube,
88
state: Rc<RefCell<gpu::Frontend>>,
89
interrupt: Interrupt,
90
) {
91
'wait: loop {
92
let req = match gpu_control_tube.next::<GpuControlCommand>().await {
93
Ok(req) => req,
94
Err(e) => {
95
error!("GPU control socket failed to recv: {:?}", e);
96
break 'wait;
97
}
98
};
99
100
let resp = state.borrow_mut().process_gpu_control_command(req);
101
102
if let GpuControlResult::DisplaysUpdated = resp {
103
info!("Signaling display config change");
104
interrupt.signal_config_changed();
105
}
106
107
if let Err(e) = gpu_control_tube.send(resp).await {
108
error!("Display control socket failed to send: {}", e);
109
break 'wait;
110
}
111
}
112
}
113
114
impl GpuBackend {
115
pub fn start_platform_workers(&mut self, interrupt: Interrupt) -> anyhow::Result<()> {
116
let state = self
117
.state
118
.as_ref()
119
.context("frontend state wasn't set")?
120
.clone();
121
122
// Start handling the display.
123
// SAFETY:
124
// Safe because the raw descriptor is valid, and an event.
125
let display = unsafe {
126
EventAsync::clone_raw_without_reset(&*state.borrow_mut().display().borrow(), &self.ex)
127
}
128
.context("failed to clone inner WaitContext for gpu display")?;
129
130
let task = self
131
.ex
132
.spawn_local(run_display(display, state.clone(), self.gpu.clone()));
133
self.platform_worker_tx
134
.unbounded_send(task)
135
.context("sending the run_display task for the initial display")?;
136
137
let task = self.ex.spawn_local(run_gpu_control_command_handler(
138
AsyncTube::new(
139
&self.ex,
140
self.gpu
141
.borrow_mut()
142
.gpu_control_tube
143
.take()
144
.expect("gpu control tube must exist"),
145
)
146
.expect("gpu control tube creation"),
147
state,
148
interrupt,
149
));
150
self.platform_worker_tx
151
.unbounded_send(task)
152
.context("sending the run_gpu_control_command_handler task")?;
153
154
Ok(())
155
}
156
}
157
158
#[derive(FromArgs)]
159
/// GPU device
160
#[argh(subcommand, name = "gpu", description = "")]
161
pub struct Options {
162
#[argh(
163
option,
164
description = "pipe handle end for Tube Transporter",
165
arg_name = "HANDLE"
166
)]
167
bootstrap: usize,
168
}
169
170
/// Main process end for input event devices.
171
#[derive(Deserialize, Serialize)]
172
pub struct InputEventVmmConfig {
173
// Pipes to receive input events on.
174
pub multi_touch_pipes: Vec<StreamChannel>,
175
pub mouse_pipes: Vec<StreamChannel>,
176
pub keyboard_pipes: Vec<StreamChannel>,
177
}
178
179
/// Backend process end for input event devices.
180
#[derive(Deserialize, Serialize)]
181
pub struct InputEventBackendConfig {
182
// Event devices to send input events to.
183
pub event_devices: Vec<EventDevice>,
184
}
185
186
/// Configuration for running input event devices, split by a part sent to the main VMM and a part
187
/// sent to the window thread (either main process or a vhost-user process).
188
#[derive(Deserialize, Serialize)]
189
pub struct InputEventSplitConfig {
190
// Config sent to the backend.
191
pub backend_config: Option<InputEventBackendConfig>,
192
// Config sent to the main process.
193
pub vmm_config: InputEventVmmConfig,
194
}
195
196
/// Main process end for a GPU device.
197
#[derive(Deserialize, Serialize)]
198
pub struct GpuVmmConfig {
199
// Tube for setting up the vhost-user connection. May not exist if not using vhost-user.
200
pub main_vhost_user_tube: Option<Tube>,
201
// A tube to forward GPU control commands in the main process.
202
pub gpu_control_host_tube: Option<Tube>,
203
pub product_config: product::GpuVmmConfig,
204
}
205
206
/// Config arguments passed through the bootstrap Tube from the broker to the Gpu backend
207
/// process.
208
#[derive(Deserialize, Serialize)]
209
pub struct GpuBackendConfig {
210
// Tube for setting up the vhost-user connection. May not exist if not using vhost-user.
211
pub device_vhost_user_tube: Option<Tube>,
212
// An event for an incoming exit request.
213
pub exit_event: Event,
214
// A tube to send an exit request.
215
pub exit_evt_wrtube: SendTube,
216
// A tube to handle GPU control commands in the GPU device.
217
pub gpu_control_device_tube: Tube,
218
// GPU parameters.
219
pub params: GpuParameters,
220
// Product related configurations.
221
pub product_config: product::GpuBackendConfig,
222
}
223
224
#[derive(Deserialize, Serialize)]
225
pub struct WindowProcedureThreadVmmConfig {
226
pub product_config: product::WindowProcedureThreadVmmConfig,
227
}
228
229
#[derive(Deserialize, Serialize)]
230
pub struct WindowProcedureThreadSplitConfig {
231
// This is the config sent to the backend process.
232
pub wndproc_thread_builder: Option<WindowProcedureThreadBuilder>,
233
// Config sent to the main process.
234
pub vmm_config: WindowProcedureThreadVmmConfig,
235
}
236
237
pub fn run_gpu_device(opts: Options) -> anyhow::Result<()> {
238
cros_tracing::init();
239
240
let raw_transport_tube = opts.bootstrap as RawDescriptor;
241
242
let mut tubes = read_from_tube_transporter(raw_transport_tube)?;
243
244
let bootstrap_tube = tubes.get_tube(TubeToken::Bootstrap)?;
245
246
let startup_args: CommonChildStartupArgs = bootstrap_tube.recv::<CommonChildStartupArgs>()?;
247
let _child_cleanup = common_child_setup(startup_args)?;
248
249
let (mut config, input_event_backend_config, wndproc_thread_builder): (
250
GpuBackendConfig,
251
InputEventBackendConfig,
252
WindowProcedureThreadBuilder,
253
) = bootstrap_tube
254
.recv()
255
.context("failed to parse GPU backend config from bootstrap tube")?;
256
257
// TODO(b/213170185): Uncomment once sandbox is upstreamed.
258
// if sandbox::is_sandbox_target() {
259
// sandbox::TargetServices::get()
260
// .expect("failed to get target services")
261
// .unwrap()
262
// .lower_token();
263
// }
264
265
let wndproc_thread = wndproc_thread_builder
266
.start_thread()
267
.context("Failed to create window procedure thread for vhost GPU")?;
268
269
run_gpu_device_worker(
270
config,
271
input_event_backend_config.event_devices,
272
wndproc_thread,
273
)
274
}
275
276
/// Run the GPU device worker.
277
pub fn run_gpu_device_worker(
278
mut config: GpuBackendConfig,
279
event_devices: Vec<EventDevice>,
280
wndproc_thread: WindowProcedureThread,
281
) -> anyhow::Result<()> {
282
let vhost_user_tube = config
283
.device_vhost_user_tube
284
.expect("vhost-user gpu tube must be set");
285
286
if config.params.display_params.is_empty() {
287
config
288
.params
289
.display_params
290
.push(GpuDisplayParameters::default());
291
}
292
293
let display_backends = vec![virtio::DisplayBackend::WinApi];
294
295
let mut gpu_params = config.params.clone();
296
297
// Fallback for when external_blob is not available on the machine. Currently always off.
298
gpu_params.system_blob = false;
299
300
let base_features = virtio::base_features(ProtectionType::Unprotected);
301
302
let gpu = Rc::new(RefCell::new(Gpu::new(
303
config.exit_evt_wrtube,
304
config.gpu_control_device_tube,
305
/* resource_bridges= */ Vec::new(),
306
display_backends,
307
&gpu_params,
308
/* render_server_descriptor */ None,
309
event_devices,
310
base_features,
311
/* channels= */ &Default::default(),
312
wndproc_thread,
313
)));
314
315
let ex = Executor::new().context("failed to create executor")?;
316
317
let (platform_worker_tx, platform_worker_rx) = futures::channel::mpsc::unbounded();
318
let backend = GpuBackend {
319
ex: ex.clone(),
320
gpu,
321
resource_bridges: Default::default(),
322
state: None,
323
fence_state: Default::default(),
324
queue_workers: Default::default(),
325
platform_worker_tx,
326
platform_worker_rx,
327
shmem_mapper: Arc::new(Mutex::new(None)),
328
};
329
330
let handler = DeviceRequestHandler::new(backend);
331
332
info!("vhost-user gpu device ready, starting run loop...");
333
334
// Run until the backend is finished.
335
ex.run_until(run_handler(
336
Box::new(handler),
337
vhost_user_tube,
338
config.exit_event,
339
&ex,
340
))
341
.context("run_until error")?
342
.context("run_handler error")?;
343
344
// Process any tasks from the backend's destructor.
345
Ok(ex.run_until(async {})?)
346
}
347
348