Path: blob/main/devices/src/virtio/vhost_user_backend/gpu/sys/windows.rs
5394 views
// Copyright 2022 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34use std::cell::RefCell;5use std::collections::BTreeMap;6use std::path::PathBuf;7use std::rc::Rc;8use std::sync::Arc;910use anyhow::bail;11use anyhow::Context;12use argh::FromArgs;13use base::error;14use base::info;15use base::Event;16use base::FromRawDescriptor;17use base::RawDescriptor;18use base::SafeDescriptor;19use base::SendTube;20use base::StreamChannel;21use base::Tube;22use cros_async::AsyncTube;23use cros_async::AsyncWrapper;24use cros_async::EventAsync;25use cros_async::Executor;26use gpu_display::EventDevice;27use gpu_display::WindowProcedureThread;28use gpu_display::WindowProcedureThreadBuilder;29use hypervisor::ProtectionType;30use proc_init::common_child_setup;31use proc_init::CommonChildStartupArgs;32use serde::Deserialize;33use serde::Serialize;34use sync::Mutex;35use tube_transporter::TubeToken;36use vm_control::gpu::GpuControlCommand;37use vm_control::gpu::GpuControlResult;3839use crate::virtio;40use crate::virtio::gpu;41use crate::virtio::gpu::ProcessDisplayResult;42use crate::virtio::vhost_user_backend::gpu::GpuBackend;43use crate::virtio::vhost_user_backend::handler::sys::windows::read_from_tube_transporter;44use crate::virtio::vhost_user_backend::handler::sys::windows::run_handler;45use crate::virtio::vhost_user_backend::handler::DeviceRequestHandler;46use crate::virtio::Gpu;47use crate::virtio::GpuDisplayParameters;48use crate::virtio::GpuParameters;49use crate::virtio::Interrupt;5051pub mod generic;52pub use generic as product;5354async fn run_display(55display: EventAsync,56state: Rc<RefCell<gpu::Frontend>>,57gpu: Rc<RefCell<gpu::Gpu>>,58) {59loop {60if let Err(e) = display.next_val().await {61error!(62"Failed to wait for display context to become readable: {}",63e64);65break;66}6768match state.borrow_mut().process_display() {69ProcessDisplayResult::Error(e) => {70error!("Failed to process display events: {}", e);71break;72}73ProcessDisplayResult::CloseRequested => {74let res = gpu.borrow().send_exit_evt();75if res.is_err() {76error!("Failed to send exit event: {:?}", res);77}78break;79}80ProcessDisplayResult::Success => {}81}82}83}8485async fn run_gpu_control_command_handler(86mut gpu_control_tube: AsyncTube,87state: Rc<RefCell<gpu::Frontend>>,88interrupt: Interrupt,89) {90'wait: loop {91let req = match gpu_control_tube.next::<GpuControlCommand>().await {92Ok(req) => req,93Err(e) => {94error!("GPU control socket failed to recv: {:?}", e);95break 'wait;96}97};9899let resp = state.borrow_mut().process_gpu_control_command(req);100101if let GpuControlResult::DisplaysUpdated = resp {102info!("Signaling display config change");103interrupt.signal_config_changed();104}105106if let Err(e) = gpu_control_tube.send(resp).await {107error!("Display control socket failed to send: {}", e);108break 'wait;109}110}111}112113impl GpuBackend {114pub fn start_platform_workers(&mut self, interrupt: Interrupt) -> anyhow::Result<()> {115let state = self116.state117.as_ref()118.context("frontend state wasn't set")?119.clone();120121// Start handling the display.122// SAFETY:123// Safe because the raw descriptor is valid, and an event.124let display = unsafe {125EventAsync::clone_raw_without_reset(&*state.borrow_mut().display().borrow(), &self.ex)126}127.context("failed to clone inner WaitContext for gpu display")?;128129let task = self130.ex131.spawn_local(run_display(display, state.clone(), self.gpu.clone()));132self.platform_worker_tx133.unbounded_send(task)134.context("sending the run_display task for the initial display")?;135136let task = self.ex.spawn_local(run_gpu_control_command_handler(137AsyncTube::new(138&self.ex,139self.gpu140.borrow_mut()141.gpu_control_tube142.take()143.expect("gpu control tube must exist"),144)145.expect("gpu control tube creation"),146state,147interrupt,148));149self.platform_worker_tx150.unbounded_send(task)151.context("sending the run_gpu_control_command_handler task")?;152153Ok(())154}155}156157#[derive(FromArgs)]158/// GPU device159#[argh(subcommand, name = "gpu", description = "")]160pub struct Options {161#[argh(162option,163description = "pipe handle end for Tube Transporter",164arg_name = "HANDLE"165)]166bootstrap: usize,167}168169/// Main process end for input event devices.170#[derive(Deserialize, Serialize)]171pub struct InputEventVmmConfig {172// Pipes to receive input events on.173pub multi_touch_pipes: Vec<StreamChannel>,174pub mouse_pipes: Vec<StreamChannel>,175pub keyboard_pipes: Vec<StreamChannel>,176}177178/// Backend process end for input event devices.179#[derive(Deserialize, Serialize)]180pub struct InputEventBackendConfig {181// Event devices to send input events to.182pub event_devices: Vec<EventDevice>,183}184185/// Configuration for running input event devices, split by a part sent to the main VMM and a part186/// sent to the window thread (either main process or a vhost-user process).187#[derive(Deserialize, Serialize)]188pub struct InputEventSplitConfig {189// Config sent to the backend.190pub backend_config: Option<InputEventBackendConfig>,191// Config sent to the main process.192pub vmm_config: InputEventVmmConfig,193}194195/// Main process end for a GPU device.196#[derive(Deserialize, Serialize)]197pub struct GpuVmmConfig {198// Tube for setting up the vhost-user connection. May not exist if not using vhost-user.199pub main_vhost_user_tube: Option<Tube>,200// A tube to forward GPU control commands in the main process.201pub gpu_control_host_tube: Option<Tube>,202pub product_config: product::GpuVmmConfig,203}204205/// Config arguments passed through the bootstrap Tube from the broker to the Gpu backend206/// process.207#[derive(Deserialize, Serialize)]208pub struct GpuBackendConfig {209// Tube for setting up the vhost-user connection. May not exist if not using vhost-user.210pub device_vhost_user_tube: Option<Tube>,211// An event for an incoming exit request.212pub exit_event: Event,213// A tube to send an exit request.214pub exit_evt_wrtube: SendTube,215// A tube to handle GPU control commands in the GPU device.216pub gpu_control_device_tube: Tube,217// GPU parameters.218pub params: GpuParameters,219// Product related configurations.220pub product_config: product::GpuBackendConfig,221}222223#[derive(Deserialize, Serialize)]224pub struct WindowProcedureThreadVmmConfig {225pub product_config: product::WindowProcedureThreadVmmConfig,226}227228#[derive(Deserialize, Serialize)]229pub struct WindowProcedureThreadSplitConfig {230// This is the config sent to the backend process.231pub wndproc_thread_builder: Option<WindowProcedureThreadBuilder>,232// Config sent to the main process.233pub vmm_config: WindowProcedureThreadVmmConfig,234}235236pub fn run_gpu_device(opts: Options) -> anyhow::Result<()> {237cros_tracing::init();238239let raw_transport_tube = opts.bootstrap as RawDescriptor;240241let mut tubes = read_from_tube_transporter(raw_transport_tube)?;242243let bootstrap_tube = tubes.get_tube(TubeToken::Bootstrap)?;244245let startup_args: CommonChildStartupArgs = bootstrap_tube.recv::<CommonChildStartupArgs>()?;246let _child_cleanup = common_child_setup(startup_args)?;247248let (mut config, input_event_backend_config, wndproc_thread_builder): (249GpuBackendConfig,250InputEventBackendConfig,251WindowProcedureThreadBuilder,252) = bootstrap_tube253.recv()254.context("failed to parse GPU backend config from bootstrap tube")?;255256// TODO(b/213170185): Uncomment once sandbox is upstreamed.257// if sandbox::is_sandbox_target() {258// sandbox::TargetServices::get()259// .expect("failed to get target services")260// .unwrap()261// .lower_token();262// }263264let wndproc_thread = wndproc_thread_builder265.start_thread()266.context("Failed to create window procedure thread for vhost GPU")?;267268run_gpu_device_worker(269config,270input_event_backend_config.event_devices,271wndproc_thread,272)273}274275/// Run the GPU device worker.276pub fn run_gpu_device_worker(277mut config: GpuBackendConfig,278event_devices: Vec<EventDevice>,279wndproc_thread: WindowProcedureThread,280) -> anyhow::Result<()> {281let vhost_user_tube = config282.device_vhost_user_tube283.expect("vhost-user gpu tube must be set");284285if config.params.display_params.is_empty() {286config287.params288.display_params289.push(GpuDisplayParameters::default());290}291292let display_backends = vec![virtio::DisplayBackend::WinApi];293294let mut gpu_params = config.params.clone();295296// Fallback for when external_blob is not available on the machine. Currently always off.297gpu_params.system_blob = false;298299let base_features = virtio::base_features(ProtectionType::Unprotected);300301let gpu = Rc::new(RefCell::new(Gpu::new(302config.exit_evt_wrtube,303config.gpu_control_device_tube,304/* resource_bridges= */ Vec::new(),305display_backends,306&gpu_params,307/* render_server_descriptor */ None,308event_devices,309base_features,310/* channels= */ &Default::default(),311wndproc_thread,312)));313314let ex = Executor::new().context("failed to create executor")?;315316let (platform_worker_tx, platform_worker_rx) = futures::channel::mpsc::unbounded();317let backend = GpuBackend {318ex: ex.clone(),319gpu,320resource_bridges: Default::default(),321state: None,322fence_state: Default::default(),323queue_workers: Default::default(),324platform_worker_tx,325platform_worker_rx,326shmem_mapper: Arc::new(Mutex::new(None)),327};328329let handler = DeviceRequestHandler::new(backend);330331info!("vhost-user gpu device ready, starting run loop...");332333// Run until the backend is finished.334ex.run_until(run_handler(335Box::new(handler),336vhost_user_tube,337config.exit_event,338&ex,339))340.context("run_until error")?341.context("run_handler error")?;342343// Process any tasks from the backend's destructor.344Ok(ex.run_until(async {})?)345}346347348