devices/virtio/vhost_user_backend/gpu/sys/
linux.rs1use std::cell::RefCell;
6use std::collections::BTreeMap;
7use std::path::PathBuf;
8use std::rc::Rc;
9use std::sync::Arc;
10
11use anyhow::Context;
12use argh::FromArgs;
13use base::clone_descriptor;
14use base::error;
15use base::RawDescriptor;
16use base::SafeDescriptor;
17use base::Tube;
18use base::UnixSeqpacketListener;
19use base::UnlinkUnixSeqpacketListener;
20use cros_async::AsyncTube;
21use cros_async::AsyncWrapper;
22use cros_async::Executor;
23use cros_async::IoSource;
24use hypervisor::ProtectionType;
25use sync::Mutex;
26use vm_control::gpu::GpuControlResult;
27use vm_control::VmRequest;
28use vm_control::VmResponse;
29
30use crate::sys::linux::parse_wayland_sock;
31use crate::virtio;
32use crate::virtio::gpu;
33use crate::virtio::gpu::ProcessDisplayResult;
34use crate::virtio::vhost_user_backend::gpu::GpuBackend;
35use crate::virtio::vhost_user_backend::BackendConnection;
36use crate::virtio::Gpu;
37use crate::virtio::GpuDisplayParameters;
38use crate::virtio::GpuParameters;
39use crate::virtio::Interrupt;
40
41async fn run_display(
42 display: IoSource<AsyncWrapper<SafeDescriptor>>,
43 state: Rc<RefCell<gpu::Frontend>>,
44) {
45 loop {
46 if let Err(e) = display.wait_readable().await {
47 error!(
48 "Failed to wait for display context to become readable: {}",
49 e
50 );
51 break;
52 }
53
54 match state.borrow_mut().process_display() {
55 ProcessDisplayResult::Error(e) => {
56 error!("Failed to process display events: {}", e);
57 break;
58 }
59 ProcessDisplayResult::CloseRequested => break,
60 ProcessDisplayResult::Success => {}
61 }
62 }
63}
64
65async fn run_resource_bridge(tube: IoSource<Tube>, state: Rc<RefCell<gpu::Frontend>>) {
66 loop {
67 if let Err(e) = tube.wait_readable().await {
68 error!(
69 "Failed to wait for resource bridge tube to become readable: {}",
70 e
71 );
72 break;
73 }
74
75 if let Err(e) = state.borrow_mut().process_resource_bridge(tube.as_source()) {
76 error!("Failed to process resource bridge: {:#}", e);
77 break;
78 }
79 }
80}
81
82pub type SharedGpuControlState = Rc<RefCell<Option<(Rc<RefCell<gpu::Frontend>>, Interrupt)>>>;
88
89fn process_gpu_control_request(state: &SharedGpuControlState, req: VmRequest) -> VmResponse {
92 let VmRequest::GpuCommand(cmd) = req else {
93 return VmResponse::Err(base::Error::new(libc::EINVAL));
94 };
95
96 let state = state.borrow();
97 let Some((frontend, interrupt)) = state.as_ref() else {
98 return VmResponse::Err(base::Error::new(libc::ENODEV));
100 };
101
102 let res = frontend.borrow_mut().process_gpu_control_command(cmd);
103 if let GpuControlResult::DisplaysUpdated = &res {
104 interrupt.signal_config_changed();
105 }
106 VmResponse::GpuResponse(res)
107}
108
109async fn run_gpu_control_command_handler(tube: AsyncTube, state: SharedGpuControlState) {
110 loop {
111 let req = match tube.next::<VmRequest>().await {
112 Ok(req) => req,
113 Err(_) => break,
114 };
115
116 let resp = process_gpu_control_request(&state, req);
117
118 if let Err(e) = tube.send(resp).await {
119 error!("GPU control socket failed to send response: {:#}", e);
120 break;
121 }
122 }
123}
124
125async fn run_gpu_control_listener(
126 ex: Executor,
127 listener: IoSource<AsyncWrapper<UnlinkUnixSeqpacketListener>>,
128 state: SharedGpuControlState,
129) {
130 loop {
131 if let Err(e) = listener.wait_readable().await {
132 error!("Failed to wait for control socket: {:#}", e);
133 break;
134 }
135 match listener.as_source().accept() {
136 Ok(stream) => match Tube::try_from(stream) {
137 Ok(tube) => match AsyncTube::new(&ex, tube) {
138 Ok(async_tube) => {
139 ex.spawn_local(run_gpu_control_command_handler(async_tube, state.clone()))
143 .detach();
144 }
145 Err(e) => {
146 error!("Failed to create AsyncTube for control socket: {:#}", e);
147 }
148 },
149 Err(e) => {
150 error!("Failed to create Tube for gpu control: {:#}", e);
151 }
152 },
153 Err(e)
154 if e.kind() == std::io::ErrorKind::WouldBlock
155 || e.kind() == std::io::ErrorKind::Interrupted =>
156 {
157 continue;
158 }
159 Err(e) => {
160 error!("Failed to accept gpu control connection: {:#}", e);
161 break;
162 }
163 }
164 }
165}
166
167impl GpuBackend {
168 pub fn start_platform_workers(&mut self, interrupt: Interrupt) -> anyhow::Result<()> {
169 let state = self
170 .state
171 .as_ref()
172 .context("frontend state wasn't set")?
173 .clone();
174
175 for bridge in self.resource_bridges.lock().drain(..) {
177 let tube = self
178 .ex
179 .async_from(bridge)
180 .context("failed to create async tube")?;
181 let task = self
182 .ex
183 .spawn_local(run_resource_bridge(tube, state.clone()));
184 self.platform_worker_tx
185 .unbounded_send(task)
186 .context("sending the run_resource_bridge task")?;
187 }
188
189 let display = clone_descriptor(&*state.borrow_mut().display().borrow())
191 .map(AsyncWrapper::new)
192 .context("failed to clone inner WaitContext for gpu display")
193 .and_then(|ctx| {
194 self.ex
195 .async_from(ctx)
196 .context("failed to create async WaitContext")
197 })?;
198
199 let task = self.ex.spawn_local(run_display(display, state.clone()));
200 self.platform_worker_tx
201 .unbounded_send(task)
202 .context("sending the run_display task")?;
203
204 self.gpu_control_state
207 .borrow_mut()
208 .replace((state, interrupt));
209
210 Ok(())
211 }
212}
213fn gpu_parameters_from_str(input: &str) -> Result<GpuParameters, String> {
214 serde_json::from_str(input).map_err(|e| e.to_string())
215}
216
217#[derive(FromArgs)]
218#[argh(subcommand, name = "gpu")]
220pub struct Options {
221 #[argh(option, arg_name = "PATH", hidden_help)]
222 socket: Option<String>,
224 #[argh(option, arg_name = "PATH")]
225 socket_path: Option<String>,
228 #[argh(option, arg_name = "FD")]
229 fd: Option<RawDescriptor>,
232
233 #[argh(option, from_str_fn(parse_wayland_sock), arg_name = "PATH[,name=NAME]")]
234 wayland_sock: Vec<(String, PathBuf)>,
237 #[argh(option, arg_name = "PATH")]
238 resource_bridge: Vec<String>,
241 #[argh(option, arg_name = "DISPLAY")]
242 x_display: Option<String>,
244 #[argh(option, arg_name = "PATH")]
245 control_socket_path: Option<PathBuf>,
247 #[argh(
248 option,
249 from_str_fn(gpu_parameters_from_str),
250 default = "Default::default()",
251 arg_name = "JSON"
252 )]
253 params: GpuParameters,
255}
256
257pub fn run_gpu_device(opts: Options) -> anyhow::Result<()> {
258 let Options {
259 x_display,
260 control_socket_path,
261 params: mut gpu_parameters,
262 resource_bridge,
263 socket,
264 socket_path,
265 fd,
266 wayland_sock,
267 } = opts;
268
269 gpu_parameters.external_blob = true;
272
273 let channels: BTreeMap<_, _> = wayland_sock.into_iter().collect();
274
275 let resource_bridge_listeners = resource_bridge
276 .into_iter()
277 .map(|p| {
278 UnixSeqpacketListener::bind(&p)
279 .map(UnlinkUnixSeqpacketListener)
280 .with_context(|| format!("failed to bind socket at path {p}"))
281 })
282 .collect::<anyhow::Result<Vec<_>>>()?;
283
284 if gpu_parameters.display_params.is_empty() {
285 gpu_parameters
286 .display_params
287 .push(GpuDisplayParameters::default());
288 }
289
290 let ex = Executor::new().context("failed to create executor")?;
291
292 let resource_bridges = Arc::new(Mutex::new(Vec::with_capacity(
298 resource_bridge_listeners.len(),
299 )));
300 for listener in resource_bridge_listeners {
301 let resource_bridges = Arc::clone(&resource_bridges);
302 ex.spawn_blocking(move || match listener.accept() {
303 Ok(stream) => resource_bridges
304 .lock()
305 .push(Tube::try_from(stream).unwrap()),
306 Err(e) => {
307 let path = listener
308 .path()
309 .unwrap_or_else(|_| PathBuf::from("{unknown}"));
310 error!(
311 "Failed to accept resource bridge connection for socket {}: {}",
312 path.display(),
313 e
314 );
315 }
316 })
317 .detach();
318 }
319
320 let (exit_evt_wrtube, _) =
324 Tube::directional_pair().context("failed to create vm event tube")?;
325
326 let (gpu_control_tube, _) = Tube::pair().context("failed to create gpu control tube")?;
327
328 let mut display_backends = vec![
329 virtio::DisplayBackend::X(x_display),
330 virtio::DisplayBackend::Stub,
331 ];
332 #[cfg(feature = "android_display")]
333 if let Some(service_name) = &gpu_parameters.android_display_service {
334 display_backends.insert(0, virtio::DisplayBackend::Android(service_name.to_string()));
335 }
336 if let Some(p) = channels.get("") {
337 display_backends.insert(0, virtio::DisplayBackend::Wayland(Some(p.to_owned())));
338 }
339
340 let event_devices = Vec::new();
342
343 let base_features = virtio::base_features(ProtectionType::Unprotected);
344
345 let conn = BackendConnection::from_opts(socket.as_deref(), socket_path.as_deref(), fd)?;
346
347 let gpu = Rc::new(RefCell::new(Gpu::new(
348 exit_evt_wrtube,
349 gpu_control_tube,
350 Vec::new(), display_backends,
352 &gpu_parameters,
353 None,
355 event_devices,
356 base_features,
357 &channels,
358 None,
360 )));
361
362 let gpu_control_state: SharedGpuControlState = Rc::new(RefCell::new(None));
365
366 let control_listener_task = if let Some(path) = &control_socket_path {
367 let listener = UnixSeqpacketListener::bind(path)
368 .map(UnlinkUnixSeqpacketListener)
369 .with_context(|| format!("failed to bind control socket at path {}", path.display()))?;
370 listener
371 .set_nonblocking(true)
372 .context("failed to set nonblocking for control socket")?;
373 let async_listener = ex
374 .async_from(AsyncWrapper::new(listener))
375 .context("failed to create async control socket listener")?;
376 Some(ex.spawn_local(run_gpu_control_listener(
377 ex.clone(),
378 async_listener,
379 gpu_control_state.clone(),
380 )))
381 } else {
382 None
383 };
384
385 let (platform_worker_tx, platform_worker_rx) = futures::channel::mpsc::unbounded();
386 let backend = GpuBackend {
387 ex: ex.clone(),
388 gpu,
389 resource_bridges,
390 state: None,
391 fence_state: Default::default(),
392 queue_workers: Default::default(),
393 platform_worker_rx,
394 platform_worker_tx,
395 shmem_mapper: Arc::new(Mutex::new(None)),
396 gpu_control_state,
397 };
398
399 let res = ex.run_until(conn.run_backend(backend, &ex));
401
402 if let Some(task) = control_listener_task {
403 let _ = ex.run_until(task.cancel());
404 }
405
406 let _ = ex.run_until(async {});
408
409 res?
410}