devices/virtio/vhost_user_backend/
wl.rs

1// Copyright 2021 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::thread;
10use std::time::Duration;
11use std::time::Instant;
12
13use anyhow::bail;
14use anyhow::Context;
15use argh::FromArgs;
16use base::clone_descriptor;
17use base::error;
18use base::warn;
19use base::RawDescriptor;
20use base::SafeDescriptor;
21use base::Tube;
22use base::UnixSeqpacket;
23use cros_async::AsyncWrapper;
24use cros_async::EventAsync;
25use cros_async::Executor;
26use cros_async::IoSource;
27use hypervisor::ProtectionType;
28#[cfg(feature = "gbm")]
29use rutabaga_gfx::RutabagaGralloc;
30#[cfg(feature = "gbm")]
31use rutabaga_gfx::RutabagaGrallocBackendFlags;
32use snapshot::AnySnapshot;
33use vm_memory::GuestMemory;
34use vmm_vhost::message::VhostUserProtocolFeatures;
35use vmm_vhost::VHOST_USER_F_PROTOCOL_FEATURES;
36
37use crate::sys::linux::parse_wayland_sock;
38use crate::virtio::base_features;
39use crate::virtio::device_constants::wl::NUM_QUEUES;
40use crate::virtio::device_constants::wl::VIRTIO_WL_F_SEND_FENCES;
41use crate::virtio::device_constants::wl::VIRTIO_WL_F_TRANS_FLAGS;
42use crate::virtio::device_constants::wl::VIRTIO_WL_F_USE_SHMEM;
43use crate::virtio::vhost_user_backend::handler::Error as DeviceError;
44use crate::virtio::vhost_user_backend::handler::VhostBackendReqConnection;
45use crate::virtio::vhost_user_backend::handler::VhostUserDevice;
46use crate::virtio::vhost_user_backend::handler::WorkerState;
47use crate::virtio::vhost_user_backend::BackendConnection;
48use crate::virtio::wl;
49use crate::virtio::Queue;
50use crate::virtio::SharedMemoryRegion;
51
52async fn run_out_queue(
53    queue: Rc<RefCell<Queue>>,
54    kick_evt: EventAsync,
55    wlstate: Rc<RefCell<wl::WlState>>,
56) {
57    loop {
58        if let Err(e) = kick_evt.next_val().await {
59            error!("Failed to read kick event for out queue: {}", e);
60            break;
61        }
62
63        wl::process_out_queue(&mut queue.borrow_mut(), &mut wlstate.borrow_mut());
64    }
65}
66
67async fn run_in_queue(
68    queue: Rc<RefCell<Queue>>,
69    kick_evt: EventAsync,
70    wlstate: Rc<RefCell<wl::WlState>>,
71    wlstate_ctx: IoSource<AsyncWrapper<SafeDescriptor>>,
72) {
73    loop {
74        if let Err(e) = wlstate_ctx.wait_readable().await {
75            error!(
76                "Failed to wait for inner WaitContext to become readable: {}",
77                e
78            );
79            break;
80        }
81
82        if wl::process_in_queue(&mut queue.borrow_mut(), &mut wlstate.borrow_mut())
83            == Err(wl::DescriptorsExhausted)
84        {
85            if let Err(e) = kick_evt.next_val().await {
86                error!("Failed to read kick event for in queue: {}", e);
87                break;
88            }
89        }
90    }
91}
92
93struct WlBackend {
94    ex: Executor,
95    wayland_paths: Option<BTreeMap<String, PathBuf>>,
96    resource_bridge: Option<Tube>,
97    use_transition_flags: bool,
98    use_send_vfd_v2: bool,
99    use_shmem: bool,
100    features: u64,
101    acked_features: u64,
102    wlstate: Option<Rc<RefCell<wl::WlState>>>,
103    workers: [Option<WorkerState<Rc<RefCell<Queue>>, ()>>; NUM_QUEUES],
104    backend_req_conn: Option<VhostBackendReqConnection>,
105}
106
107impl WlBackend {
108    fn new(
109        ex: &Executor,
110        wayland_paths: BTreeMap<String, PathBuf>,
111        resource_bridge: Option<Tube>,
112    ) -> WlBackend {
113        let features = base_features(ProtectionType::Unprotected)
114            | 1 << VIRTIO_WL_F_TRANS_FLAGS
115            | 1 << VIRTIO_WL_F_SEND_FENCES
116            | 1 << VIRTIO_WL_F_USE_SHMEM
117            | 1 << VHOST_USER_F_PROTOCOL_FEATURES;
118        WlBackend {
119            ex: ex.clone(),
120            wayland_paths: Some(wayland_paths),
121            resource_bridge,
122            use_transition_flags: false,
123            use_send_vfd_v2: false,
124            use_shmem: false,
125            features,
126            acked_features: 0,
127            wlstate: None,
128            workers: Default::default(),
129            backend_req_conn: None,
130        }
131    }
132}
133
134impl VhostUserDevice for WlBackend {
135    fn max_queue_num(&self) -> usize {
136        NUM_QUEUES
137    }
138
139    fn features(&self) -> u64 {
140        self.features
141    }
142
143    fn ack_features(&mut self, value: u64) -> anyhow::Result<()> {
144        self.acked_features |= value;
145
146        if value & (1 << VIRTIO_WL_F_TRANS_FLAGS) != 0 {
147            self.use_transition_flags = true;
148        }
149        if value & (1 << VIRTIO_WL_F_SEND_FENCES) != 0 {
150            self.use_send_vfd_v2 = true;
151        }
152        if value & (1 << VIRTIO_WL_F_USE_SHMEM) != 0 {
153            self.use_shmem = true;
154        }
155
156        Ok(())
157    }
158
159    fn protocol_features(&self) -> VhostUserProtocolFeatures {
160        VhostUserProtocolFeatures::BACKEND_REQ | VhostUserProtocolFeatures::SHMEM
161    }
162
163    fn read_config(&self, _offset: u64, _dst: &mut [u8]) {}
164
165    fn start_queue(&mut self, idx: usize, queue: Queue, _mem: GuestMemory) -> anyhow::Result<()> {
166        if self.workers[idx].is_some() {
167            warn!("Starting new queue handler without stopping old handler");
168            self.stop_queue(idx)?;
169        }
170
171        let kick_evt = queue
172            .event()
173            .try_clone()
174            .context("failed to clone queue event")?;
175        let kick_evt = EventAsync::new(kick_evt, &self.ex)
176            .context("failed to create EventAsync for kick_evt")?;
177
178        if !self.use_shmem {
179            bail!("Incompatible driver: vhost-user-wl requires shmem support");
180        }
181
182        // We use this de-structuring let binding to separate borrows so that the compiler doesn't
183        // think we're borrowing all of `self` in the closure below.
184        let WlBackend {
185            ref mut wayland_paths,
186            ref mut resource_bridge,
187            ref use_transition_flags,
188            ref use_send_vfd_v2,
189            ..
190        } = self;
191
192        #[cfg(feature = "gbm")]
193        let gralloc = RutabagaGralloc::new(RutabagaGrallocBackendFlags::new())
194            .context("Failed to initailize gralloc")?;
195        let wlstate = match &self.wlstate {
196            None => {
197                let mapper = self
198                    .backend_req_conn
199                    .as_ref()
200                    .context("No backend request connection found")?
201                    .shmem_mapper()
202                    .context("Shared memory mapper not available")?;
203
204                let wlstate = Rc::new(RefCell::new(wl::WlState::new(
205                    wayland_paths.take().expect("WlState already initialized"),
206                    mapper,
207                    *use_transition_flags,
208                    *use_send_vfd_v2,
209                    resource_bridge.take(),
210                    #[cfg(feature = "gbm")]
211                    gralloc,
212                    None, /* address_offset */
213                )));
214                self.wlstate = Some(wlstate.clone());
215                wlstate
216            }
217            Some(state) => state.clone(),
218        };
219        let queue = Rc::new(RefCell::new(queue));
220        let queue_task = match idx {
221            0 => {
222                let wlstate_ctx = clone_descriptor(wlstate.borrow().wait_ctx())
223                    .map(AsyncWrapper::new)
224                    .context("failed to clone inner WaitContext for WlState")
225                    .and_then(|ctx| {
226                        self.ex
227                            .async_from(ctx)
228                            .context("failed to create async WaitContext")
229                    })?;
230
231                self.ex
232                    .spawn_local(run_in_queue(queue.clone(), kick_evt, wlstate, wlstate_ctx))
233            }
234            1 => self
235                .ex
236                .spawn_local(run_out_queue(queue.clone(), kick_evt, wlstate)),
237            _ => bail!("attempted to start unknown queue: {}", idx),
238        };
239        self.workers[idx] = Some(WorkerState { queue_task, queue });
240        Ok(())
241    }
242
243    fn stop_queue(&mut self, idx: usize) -> anyhow::Result<Queue> {
244        if let Some(worker) = self.workers.get_mut(idx).and_then(Option::take) {
245            // Wait for queue_task to be aborted.
246            let _ = self.ex.run_until(worker.queue_task.cancel());
247
248            let queue = match Rc::try_unwrap(worker.queue) {
249                Ok(queue_cell) => queue_cell.into_inner(),
250                Err(_) => panic!("failed to recover queue from worker"),
251            };
252
253            Ok(queue)
254        } else {
255            Err(anyhow::Error::new(DeviceError::WorkerNotFound))
256        }
257    }
258
259    fn reset(&mut self) {
260        for worker in self.workers.iter_mut().filter_map(Option::take) {
261            let _ = self.ex.run_until(worker.queue_task.cancel());
262        }
263    }
264
265    fn get_shared_memory_region(&self) -> Option<SharedMemoryRegion> {
266        Some(SharedMemoryRegion {
267            id: wl::WL_SHMEM_ID,
268            length: wl::WL_SHMEM_SIZE,
269        })
270    }
271
272    fn set_backend_req_connection(&mut self, conn: VhostBackendReqConnection) {
273        if self.backend_req_conn.is_some() {
274            warn!("connection already established. Overwriting");
275        }
276
277        self.backend_req_conn = Some(conn);
278    }
279
280    fn enter_suspended_state(&mut self) -> anyhow::Result<()> {
281        // No non-queue workers.
282        Ok(())
283    }
284
285    fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
286        bail!("snapshot not implemented for vhost-user wl");
287    }
288
289    fn restore(&mut self, _data: AnySnapshot) -> anyhow::Result<()> {
290        bail!("snapshot not implemented for vhost-user wl");
291    }
292}
293
294#[derive(FromArgs)]
295#[argh(subcommand, name = "wl")]
296/// Wayland device
297pub struct Options {
298    #[argh(option, arg_name = "PATH", hidden_help)]
299    /// deprecated - please use --socket-path instead
300    socket: Option<String>,
301    #[argh(option, arg_name = "PATH")]
302    /// path to the vhost-user socket to bind to.
303    /// If this flag is set, --fd cannot be specified.
304    socket_path: Option<String>,
305    #[argh(option, arg_name = "FD")]
306    /// file descriptor of a connected vhost-user socket.
307    /// If this flag is set, --socket-path cannot be specified.
308    fd: Option<RawDescriptor>,
309
310    #[argh(option, from_str_fn(parse_wayland_sock), arg_name = "PATH[,name=NAME]")]
311    /// path to one or more Wayland sockets. The unnamed socket is used for
312    /// displaying virtual screens while the named ones are used for IPC
313    wayland_sock: Vec<(String, PathBuf)>,
314    #[argh(option, arg_name = "PATH")]
315    /// path to the GPU resource bridge
316    resource_bridge: Option<String>,
317}
318
319/// Starts a vhost-user wayland device.
320/// Returns an error if the given `args` is invalid or the device fails to run.
321pub fn run_wl_device(opts: Options) -> anyhow::Result<()> {
322    let Options {
323        wayland_sock,
324        socket,
325        socket_path,
326        fd,
327        resource_bridge,
328    } = opts;
329
330    let wayland_paths: BTreeMap<_, _> = wayland_sock.into_iter().collect();
331
332    let resource_bridge = resource_bridge
333        .map(|p| -> anyhow::Result<Tube> {
334            let deadline = Instant::now() + Duration::from_secs(5);
335            loop {
336                match UnixSeqpacket::connect(&p) {
337                    Ok(s) => return Ok(Tube::try_from(s).unwrap()),
338                    Err(e) => {
339                        if Instant::now() < deadline {
340                            thread::sleep(Duration::from_millis(50));
341                        } else {
342                            return Err(anyhow::Error::new(e));
343                        }
344                    }
345                }
346            }
347        })
348        .transpose()
349        .context("failed to connect to resource bridge socket")?;
350
351    let ex = Executor::new().context("failed to create executor")?;
352
353    let conn = BackendConnection::from_opts(socket.as_deref(), socket_path.as_deref(), fd)?;
354
355    let backend = WlBackend::new(&ex, wayland_paths, resource_bridge);
356    // run_until() returns an Result<Result<..>> which the ? operator lets us flatten.
357    ex.run_until(conn.run_backend(backend, &ex))?
358}