Path: blob/main/devices/src/virtio/vhost_user_backend/wl.rs
5394 views
// Copyright 2021 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::thread;9use std::time::Duration;10use std::time::Instant;1112use anyhow::bail;13use anyhow::Context;14use argh::FromArgs;15use base::clone_descriptor;16use base::error;17use base::warn;18use base::RawDescriptor;19use base::SafeDescriptor;20use base::Tube;21use base::UnixSeqpacket;22use cros_async::AsyncWrapper;23use cros_async::EventAsync;24use cros_async::Executor;25use cros_async::IoSource;26use hypervisor::ProtectionType;27#[cfg(feature = "gbm")]28use rutabaga_gfx::RutabagaGralloc;29#[cfg(feature = "gbm")]30use rutabaga_gfx::RutabagaGrallocBackendFlags;31use snapshot::AnySnapshot;32use vm_memory::GuestMemory;33use vmm_vhost::message::VhostUserProtocolFeatures;34use vmm_vhost::VHOST_USER_F_PROTOCOL_FEATURES;3536use crate::virtio::base_features;37use crate::virtio::device_constants::wl::NUM_QUEUES;38use crate::virtio::device_constants::wl::VIRTIO_WL_F_SEND_FENCES;39use crate::virtio::device_constants::wl::VIRTIO_WL_F_TRANS_FLAGS;40use crate::virtio::device_constants::wl::VIRTIO_WL_F_USE_SHMEM;41use crate::virtio::vhost_user_backend::handler::Error as DeviceError;42use crate::virtio::vhost_user_backend::handler::VhostBackendReqConnection;43use crate::virtio::vhost_user_backend::handler::VhostUserDevice;44use crate::virtio::vhost_user_backend::handler::WorkerState;45use crate::virtio::vhost_user_backend::BackendConnection;46use crate::virtio::wl;47use crate::virtio::Queue;48use crate::virtio::SharedMemoryRegion;4950async fn run_out_queue(51queue: Rc<RefCell<Queue>>,52kick_evt: EventAsync,53wlstate: Rc<RefCell<wl::WlState>>,54) {55loop {56if let Err(e) = kick_evt.next_val().await {57error!("Failed to read kick event for out queue: {}", e);58break;59}6061wl::process_out_queue(&mut queue.borrow_mut(), &mut wlstate.borrow_mut());62}63}6465async fn run_in_queue(66queue: Rc<RefCell<Queue>>,67kick_evt: EventAsync,68wlstate: Rc<RefCell<wl::WlState>>,69wlstate_ctx: IoSource<AsyncWrapper<SafeDescriptor>>,70) {71loop {72if let Err(e) = wlstate_ctx.wait_readable().await {73error!(74"Failed to wait for inner WaitContext to become readable: {}",75e76);77break;78}7980if wl::process_in_queue(&mut queue.borrow_mut(), &mut wlstate.borrow_mut())81== Err(wl::DescriptorsExhausted)82{83if let Err(e) = kick_evt.next_val().await {84error!("Failed to read kick event for in queue: {}", e);85break;86}87}88}89}9091struct WlBackend {92ex: Executor,93wayland_paths: Option<BTreeMap<String, PathBuf>>,94resource_bridge: Option<Tube>,95use_transition_flags: bool,96use_send_vfd_v2: bool,97use_shmem: bool,98features: u64,99acked_features: u64,100wlstate: Option<Rc<RefCell<wl::WlState>>>,101workers: [Option<WorkerState<Rc<RefCell<Queue>>, ()>>; NUM_QUEUES],102backend_req_conn: Option<VhostBackendReqConnection>,103}104105impl WlBackend {106fn new(107ex: &Executor,108wayland_paths: BTreeMap<String, PathBuf>,109resource_bridge: Option<Tube>,110) -> WlBackend {111let features = base_features(ProtectionType::Unprotected)112| 1 << VIRTIO_WL_F_TRANS_FLAGS113| 1 << VIRTIO_WL_F_SEND_FENCES114| 1 << VIRTIO_WL_F_USE_SHMEM115| 1 << VHOST_USER_F_PROTOCOL_FEATURES;116WlBackend {117ex: ex.clone(),118wayland_paths: Some(wayland_paths),119resource_bridge,120use_transition_flags: false,121use_send_vfd_v2: false,122use_shmem: false,123features,124acked_features: 0,125wlstate: None,126workers: Default::default(),127backend_req_conn: None,128}129}130}131132impl VhostUserDevice for WlBackend {133fn max_queue_num(&self) -> usize {134NUM_QUEUES135}136137fn features(&self) -> u64 {138self.features139}140141fn ack_features(&mut self, value: u64) -> anyhow::Result<()> {142self.acked_features |= value;143144if value & (1 << VIRTIO_WL_F_TRANS_FLAGS) != 0 {145self.use_transition_flags = true;146}147if value & (1 << VIRTIO_WL_F_SEND_FENCES) != 0 {148self.use_send_vfd_v2 = true;149}150if value & (1 << VIRTIO_WL_F_USE_SHMEM) != 0 {151self.use_shmem = true;152}153154Ok(())155}156157fn protocol_features(&self) -> VhostUserProtocolFeatures {158VhostUserProtocolFeatures::BACKEND_REQ | VhostUserProtocolFeatures::SHMEM_MAP159}160161fn read_config(&self, _offset: u64, _dst: &mut [u8]) {}162163fn start_queue(&mut self, idx: usize, queue: Queue, _mem: GuestMemory) -> anyhow::Result<()> {164if self.workers[idx].is_some() {165warn!("Starting new queue handler without stopping old handler");166self.stop_queue(idx)?;167}168169let kick_evt = queue170.event()171.try_clone()172.context("failed to clone queue event")?;173let kick_evt = EventAsync::new(kick_evt, &self.ex)174.context("failed to create EventAsync for kick_evt")?;175176if !self.use_shmem {177bail!("Incompatible driver: vhost-user-wl requires shmem support");178}179180// We use this de-structuring let binding to separate borrows so that the compiler doesn't181// think we're borrowing all of `self` in the closure below.182let WlBackend {183ref mut wayland_paths,184ref mut resource_bridge,185ref use_transition_flags,186ref use_send_vfd_v2,187..188} = self;189190#[cfg(feature = "gbm")]191let gralloc = RutabagaGralloc::new(RutabagaGrallocBackendFlags::new())192.context("Failed to initailize gralloc")?;193let wlstate = match &self.wlstate {194None => {195let mapper = self196.backend_req_conn197.as_ref()198.context("No backend request connection found")?199.shmem_mapper()200.context("Shared memory mapper not available")?;201202let wlstate = Rc::new(RefCell::new(wl::WlState::new(203wayland_paths.take().expect("WlState already initialized"),204mapper,205*use_transition_flags,206*use_send_vfd_v2,207resource_bridge.take(),208#[cfg(feature = "gbm")]209gralloc,210None, /* address_offset */211)));212self.wlstate = Some(wlstate.clone());213wlstate214}215Some(state) => state.clone(),216};217let queue = Rc::new(RefCell::new(queue));218let queue_task = match idx {2190 => {220let wlstate_ctx = clone_descriptor(wlstate.borrow().wait_ctx())221.map(AsyncWrapper::new)222.context("failed to clone inner WaitContext for WlState")223.and_then(|ctx| {224self.ex225.async_from(ctx)226.context("failed to create async WaitContext")227})?;228229self.ex230.spawn_local(run_in_queue(queue.clone(), kick_evt, wlstate, wlstate_ctx))231}2321 => self233.ex234.spawn_local(run_out_queue(queue.clone(), kick_evt, wlstate)),235_ => bail!("attempted to start unknown queue: {}", idx),236};237self.workers[idx] = Some(WorkerState { queue_task, queue });238Ok(())239}240241fn stop_queue(&mut self, idx: usize) -> anyhow::Result<Queue> {242if let Some(worker) = self.workers.get_mut(idx).and_then(Option::take) {243// Wait for queue_task to be aborted.244let _ = self.ex.run_until(worker.queue_task.cancel());245246let queue = match Rc::try_unwrap(worker.queue) {247Ok(queue_cell) => queue_cell.into_inner(),248Err(_) => panic!("failed to recover queue from worker"),249};250251Ok(queue)252} else {253Err(anyhow::Error::new(DeviceError::WorkerNotFound))254}255}256257fn reset(&mut self) {258for worker in self.workers.iter_mut().filter_map(Option::take) {259let _ = self.ex.run_until(worker.queue_task.cancel());260}261}262263fn get_shared_memory_region(&self) -> Option<SharedMemoryRegion> {264Some(SharedMemoryRegion {265id: wl::WL_SHMEM_ID,266length: wl::WL_SHMEM_SIZE,267})268}269270fn set_backend_req_connection(&mut self, conn: VhostBackendReqConnection) {271if self.backend_req_conn.is_some() {272warn!("connection already established. Overwriting");273}274275self.backend_req_conn = Some(conn);276}277278fn enter_suspended_state(&mut self) -> anyhow::Result<()> {279// No non-queue workers.280Ok(())281}282283fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {284bail!("snapshot not implemented for vhost-user wl");285}286287fn restore(&mut self, _data: AnySnapshot) -> anyhow::Result<()> {288bail!("snapshot not implemented for vhost-user wl");289}290}291292pub fn parse_wayland_sock(value: &str) -> Result<(String, PathBuf), String> {293let mut components = value.split(',');294let path = PathBuf::from(match components.next() {295None => return Err("missing socket path".to_string()),296Some(c) => c,297});298let mut name = "";299for c in components {300let mut kv = c.splitn(2, '=');301let (kind, value) = match (kv.next(), kv.next()) {302(Some(kind), Some(value)) => (kind, value),303_ => return Err(format!("option must be of the form `kind=value`: {c}")),304};305match kind {306"name" => name = value,307_ => return Err(format!("unrecognized option: {kind}")),308}309}310311Ok((name.to_string(), path))312}313314#[derive(FromArgs)]315#[argh(subcommand, name = "wl")]316/// Wayland device317pub struct Options {318#[argh(option, arg_name = "PATH", hidden_help)]319/// deprecated - please use --socket-path instead320socket: Option<String>,321#[argh(option, arg_name = "PATH")]322/// path to the vhost-user socket to bind to.323/// If this flag is set, --fd cannot be specified.324socket_path: Option<String>,325#[argh(option, arg_name = "FD")]326/// file descriptor of a connected vhost-user socket.327/// If this flag is set, --socket-path cannot be specified.328fd: Option<RawDescriptor>,329330#[argh(option, from_str_fn(parse_wayland_sock), arg_name = "PATH[,name=NAME]")]331/// path to one or more Wayland sockets. The unnamed socket is used for332/// displaying virtual screens while the named ones are used for IPC333wayland_sock: Vec<(String, PathBuf)>,334#[argh(option, arg_name = "PATH")]335/// path to the GPU resource bridge336resource_bridge: Option<String>,337}338339/// Starts a vhost-user wayland device.340/// Returns an error if the given `args` is invalid or the device fails to run.341pub fn run_wl_device(opts: Options) -> anyhow::Result<()> {342let Options {343wayland_sock,344socket,345socket_path,346fd,347resource_bridge,348} = opts;349350let wayland_paths: BTreeMap<_, _> = wayland_sock.into_iter().collect();351352let resource_bridge = resource_bridge353.map(|p| -> anyhow::Result<Tube> {354let deadline = Instant::now() + Duration::from_secs(5);355loop {356match UnixSeqpacket::connect(&p) {357Ok(s) => return Ok(Tube::try_from(s).unwrap()),358Err(e) => {359if Instant::now() < deadline {360thread::sleep(Duration::from_millis(50));361} else {362return Err(anyhow::Error::new(e));363}364}365}366}367})368.transpose()369.context("failed to connect to resource bridge socket")?;370371let ex = Executor::new().context("failed to create executor")?;372373let conn = BackendConnection::from_opts(socket.as_deref(), socket_path.as_deref(), fd)?;374375let backend = WlBackend::new(&ex, wayland_paths, resource_bridge);376// run_until() returns an Result<Result<..>> which the ? operator lets us flatten.377ex.run_until(conn.run_backend(backend, &ex))?378}379380381