devices/virtio/vhost_user_backend/gpu/sys/
linux.rs

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
5use 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
82/// The frontend state and interrupt of the running device, shared with the GPU control socket
83/// handlers.
84///
85/// The handlers live as long as the process while the frontend only exists while the device is
86/// running, so this is `None` until `start_platform_workers()` and again after the device stops.
87pub type SharedGpuControlState = Rc<RefCell<Option<(Rc<RefCell<gpu::Frontend>>, Interrupt)>>>;
88
89/// Handles a single GPU control request. Kept non-`async` so that the `state` borrow cannot be
90/// held across an await point.
91fn 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        // The device isn't running: not started yet, reset, or suspended.
99        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                        // The handler never references the frontend state across an await point,
140                        // so it doesn't need to be cancelled on reset; it exits when the client
141                        // disconnects.
142                        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        // Start handling the resource bridges.
176        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        // Start handling the display.
190        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        // Publish the state for the GPU control socket handlers, which are spawned once for the
205        // lifetime of the process. Cleared again in `stop_non_queue_workers()`.
206        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/// GPU device
219#[argh(subcommand, name = "gpu")]
220pub struct Options {
221    #[argh(option, arg_name = "PATH", hidden_help)]
222    /// deprecated - please use --socket-path instead
223    socket: Option<String>,
224    #[argh(option, arg_name = "PATH")]
225    /// path to the vhost-user socket to bind to.
226    /// If this flag is set, --fd cannot be specified.
227    socket_path: Option<String>,
228    #[argh(option, arg_name = "FD")]
229    /// file descriptor of a connected vhost-user socket.
230    /// If this flag is set, --socket-path cannot be specified.
231    fd: Option<RawDescriptor>,
232
233    #[argh(option, from_str_fn(parse_wayland_sock), arg_name = "PATH[,name=NAME]")]
234    /// path to one or more Wayland sockets. The unnamed socket is
235    /// used for displaying virtual screens while the named ones are used for IPC
236    wayland_sock: Vec<(String, PathBuf)>,
237    #[argh(option, arg_name = "PATH")]
238    /// path to one or more bridge sockets for communicating with
239    /// other graphics devices (wayland, video, etc)
240    resource_bridge: Vec<String>,
241    #[argh(option, arg_name = "DISPLAY")]
242    /// X11 display name to use
243    x_display: Option<String>,
244    #[argh(option, arg_name = "PATH")]
245    /// path to the control socket to listen on for GPU commands
246    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    /// a JSON object of virtio-gpu parameters
254    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    // Standalone vhost-user GPU device runs out-of-process, so external_blob must be enforced
270    // to allow blobs to be exported to descriptors for sharing with the hypervisor or host display.
271    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    // We don't know the order in which other devices are going to connect to the resource bridges
293    // so start listening for all of them on separate threads. Any devices that connect after the
294    // gpu device starts its queues will not have its resource bridges processed. In practice this
295    // should be fine since the devices that use the resource bridge always try to connect to the
296    // gpu device before handling messages from the VM.
297    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    // TODO(b/232344535): Read side of the tube is ignored currently.
321    // Complete the implementation by polling `exit_evt_rdtube` and
322    // kill the sibling VM.
323    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    // These are only used when there is an input device.
341    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(), // resource_bridges, handled separately by us
351        display_backends,
352        &gpu_parameters,
353        /* rutabaga_server_descriptor */
354        None,
355        event_devices,
356        base_features,
357        &channels,
358        /* gpu_cgroup_path */
359        None,
360    )));
361
362    // The control socket is tied to the lifetime of this process, not of the device: the guest may
363    // reset the device, reboot, reload the driver or suspend, and the socket must keep working.
364    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    // Run until the backend is finished.
400    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    // Process any tasks from the backend's destructor.
407    let _ = ex.run_until(async {});
408
409    res?
410}