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
5 use std::cell::RefCell;
6 use std::collections::BTreeMap;
7 use std::path::PathBuf;
8 use std::rc::Rc;
9 use std::thread;
10 use std::time::Duration;
11 use std::time::Instant;
12
13 use anyhow::anyhow;
14 use anyhow::bail;
15 use anyhow::Context;
16 use argh::FromArgs;
17 use base::clone_descriptor;
18 use base::error;
19 use base::warn;
20 use base::Event;
21 use base::FromRawDescriptor;
22 use base::SafeDescriptor;
23 use base::Tube;
24 use base::UnixSeqpacket;
25 use cros_async::AsyncWrapper;
26 use cros_async::EventAsync;
27 use cros_async::Executor;
28 use cros_async::IoSource;
29 use futures::future::AbortHandle;
30 use futures::future::Abortable;
31 use hypervisor::ProtectionType;
32 #[cfg(feature = "minigbm")]
33 use rutabaga_gfx::RutabagaGralloc;
34 use vm_memory::GuestMemory;
35 use vmm_vhost::message::VhostUserProtocolFeatures;
36 use vmm_vhost::message::VhostUserVirtioFeatures;
37
38 use crate::virtio::base_features;
39 use crate::virtio::device_constants::wl::QUEUE_SIZES;
40 use crate::virtio::device_constants::wl::VIRTIO_WL_F_SEND_FENCES;
41 use crate::virtio::device_constants::wl::VIRTIO_WL_F_TRANS_FLAGS;
42 use crate::virtio::device_constants::wl::VIRTIO_WL_F_USE_SHMEM;
43 use crate::virtio::vhost::user::device::handler::sys::Doorbell;
44 use crate::virtio::vhost::user::device::handler::VhostBackendReqConnection;
45 use crate::virtio::vhost::user::device::handler::VhostBackendReqConnectionState;
46 use crate::virtio::vhost::user::device::handler::VhostUserBackend;
47 use crate::virtio::vhost::user::device::listener::sys::VhostUserListener;
48 use crate::virtio::vhost::user::device::listener::VhostUserListenerTrait;
49 use crate::virtio::wl;
50 use crate::virtio::Queue;
51 use crate::virtio::SharedMemoryRegion;
52
53 const MAX_QUEUE_NUM: usize = QUEUE_SIZES.len();
54
run_out_queue( mut queue: Queue, mem: GuestMemory, doorbell: Doorbell, kick_evt: EventAsync, wlstate: Rc<RefCell<wl::WlState>>, )55 async fn run_out_queue(
56 mut queue: Queue,
57 mem: GuestMemory,
58 doorbell: Doorbell,
59 kick_evt: EventAsync,
60 wlstate: Rc<RefCell<wl::WlState>>,
61 ) {
62 loop {
63 if let Err(e) = kick_evt.next_val().await {
64 error!("Failed to read kick event for out queue: {}", e);
65 break;
66 }
67
68 wl::process_out_queue(&doorbell, &mut queue, &mem, &mut wlstate.borrow_mut());
69 }
70 }
71
run_in_queue( mut queue: Queue, mem: GuestMemory, doorbell: Doorbell, kick_evt: EventAsync, wlstate: Rc<RefCell<wl::WlState>>, wlstate_ctx: IoSource<AsyncWrapper<SafeDescriptor>>, )72 async fn run_in_queue(
73 mut queue: Queue,
74 mem: GuestMemory,
75 doorbell: Doorbell,
76 kick_evt: EventAsync,
77 wlstate: Rc<RefCell<wl::WlState>>,
78 wlstate_ctx: IoSource<AsyncWrapper<SafeDescriptor>>,
79 ) {
80 loop {
81 if let Err(e) = wlstate_ctx.wait_readable().await {
82 error!(
83 "Failed to wait for inner WaitContext to become readable: {}",
84 e
85 );
86 break;
87 }
88
89 if wl::process_in_queue(&doorbell, &mut queue, &mem, &mut wlstate.borrow_mut())
90 == Err(wl::DescriptorsExhausted)
91 {
92 if let Err(e) = kick_evt.next_val().await {
93 error!("Failed to read kick event for in queue: {}", e);
94 break;
95 }
96 }
97 }
98 }
99
100 struct WlBackend {
101 ex: Executor,
102 wayland_paths: Option<BTreeMap<String, PathBuf>>,
103 resource_bridge: Option<Tube>,
104 use_transition_flags: bool,
105 use_send_vfd_v2: bool,
106 use_shmem: bool,
107 features: u64,
108 acked_features: u64,
109 wlstate: Option<Rc<RefCell<wl::WlState>>>,
110 workers: [Option<AbortHandle>; MAX_QUEUE_NUM],
111 backend_req_conn: VhostBackendReqConnectionState,
112 }
113
114 impl WlBackend {
new( ex: &Executor, wayland_paths: BTreeMap<String, PathBuf>, resource_bridge: Option<Tube>, ) -> WlBackend115 fn new(
116 ex: &Executor,
117 wayland_paths: BTreeMap<String, PathBuf>,
118 resource_bridge: Option<Tube>,
119 ) -> WlBackend {
120 let features = base_features(ProtectionType::Unprotected)
121 | 1 << VIRTIO_WL_F_TRANS_FLAGS
122 | 1 << VIRTIO_WL_F_SEND_FENCES
123 | 1 << VIRTIO_WL_F_USE_SHMEM
124 | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits();
125 WlBackend {
126 ex: ex.clone(),
127 wayland_paths: Some(wayland_paths),
128 resource_bridge,
129 use_transition_flags: false,
130 use_send_vfd_v2: false,
131 use_shmem: false,
132 features,
133 acked_features: 0,
134 wlstate: None,
135 workers: Default::default(),
136 backend_req_conn: VhostBackendReqConnectionState::NoConnection,
137 }
138 }
139 }
140
141 impl VhostUserBackend for WlBackend {
max_queue_num(&self) -> usize142 fn max_queue_num(&self) -> usize {
143 MAX_QUEUE_NUM
144 }
145
features(&self) -> u64146 fn features(&self) -> u64 {
147 self.features
148 }
149
ack_features(&mut self, value: u64) -> anyhow::Result<()>150 fn ack_features(&mut self, value: u64) -> anyhow::Result<()> {
151 let unrequested_features = value & !self.features();
152 if unrequested_features != 0 {
153 bail!("invalid features are given: {:#x}", unrequested_features);
154 }
155
156 self.acked_features |= value;
157
158 if value & (1 << VIRTIO_WL_F_TRANS_FLAGS) != 0 {
159 self.use_transition_flags = true;
160 }
161 if value & (1 << VIRTIO_WL_F_SEND_FENCES) != 0 {
162 self.use_send_vfd_v2 = true;
163 }
164 if value & (1 << VIRTIO_WL_F_USE_SHMEM) != 0 {
165 self.use_shmem = true;
166 }
167
168 Ok(())
169 }
170
acked_features(&self) -> u64171 fn acked_features(&self) -> u64 {
172 self.acked_features
173 }
174
protocol_features(&self) -> VhostUserProtocolFeatures175 fn protocol_features(&self) -> VhostUserProtocolFeatures {
176 VhostUserProtocolFeatures::SLAVE_REQ | VhostUserProtocolFeatures::SHARED_MEMORY_REGIONS
177 }
178
ack_protocol_features(&mut self, features: u64) -> anyhow::Result<()>179 fn ack_protocol_features(&mut self, features: u64) -> anyhow::Result<()> {
180 if features & self.protocol_features().bits() != self.protocol_features().bits() {
181 Err(anyhow!(
182 "Acked features {:#x} missing required protocol features",
183 features
184 ))
185 } else if features & !self.protocol_features().bits() != 0 {
186 Err(anyhow!(
187 "Acked features {:#x} contains unexpected features",
188 features
189 ))
190 } else {
191 Ok(())
192 }
193 }
194
acked_protocol_features(&self) -> u64195 fn acked_protocol_features(&self) -> u64 {
196 VhostUserProtocolFeatures::empty().bits()
197 }
198
read_config(&self, _offset: u64, _dst: &mut [u8])199 fn read_config(&self, _offset: u64, _dst: &mut [u8]) {}
200
start_queue( &mut self, idx: usize, queue: Queue, mem: GuestMemory, doorbell: Doorbell, kick_evt: Event, ) -> anyhow::Result<()>201 fn start_queue(
202 &mut self,
203 idx: usize,
204 queue: Queue,
205 mem: GuestMemory,
206 doorbell: Doorbell,
207 kick_evt: Event,
208 ) -> anyhow::Result<()> {
209 if let Some(handle) = self.workers.get_mut(idx).and_then(Option::take) {
210 warn!("Starting new queue handler without stopping old handler");
211 handle.abort();
212 }
213
214 let kick_evt = EventAsync::new(kick_evt, &self.ex)
215 .context("failed to create EventAsync for kick_evt")?;
216
217 if !self.use_shmem {
218 bail!("Incompatible driver: vhost-user-wl requires shmem support");
219 }
220
221 // We use this de-structuring let binding to separate borrows so that the compiler doesn't
222 // think we're borrowing all of `self` in the closure below.
223 let WlBackend {
224 ref mut wayland_paths,
225 ref mut resource_bridge,
226 ref use_transition_flags,
227 ref use_send_vfd_v2,
228 ..
229 } = self;
230
231 #[cfg(feature = "minigbm")]
232 let gralloc = RutabagaGralloc::new().context("Failed to initailize gralloc")?;
233 let wlstate = match &self.wlstate {
234 None => {
235 let mapper = {
236 match &mut self.backend_req_conn {
237 VhostBackendReqConnectionState::Connected(request) => {
238 request.take_shmem_mapper()?
239 }
240 VhostBackendReqConnectionState::NoConnection => {
241 bail!("No backend request connection found")
242 }
243 }
244 };
245
246 let wlstate = Rc::new(RefCell::new(wl::WlState::new(
247 wayland_paths.take().expect("WlState already initialized"),
248 mapper,
249 *use_transition_flags,
250 *use_send_vfd_v2,
251 resource_bridge.take(),
252 #[cfg(feature = "minigbm")]
253 gralloc,
254 None, /* address_offset */
255 )));
256 self.wlstate = Some(wlstate.clone());
257 wlstate
258 }
259 Some(state) => state.clone(),
260 };
261 let (handle, registration) = AbortHandle::new_pair();
262 match idx {
263 0 => {
264 let wlstate_ctx = clone_descriptor(wlstate.borrow().wait_ctx())
265 .map(|fd| {
266 // Safe because we just created this fd.
267 AsyncWrapper::new(unsafe { SafeDescriptor::from_raw_descriptor(fd) })
268 })
269 .context("failed to clone inner WaitContext for WlState")
270 .and_then(|ctx| {
271 self.ex
272 .async_from(ctx)
273 .context("failed to create async WaitContext")
274 })?;
275
276 self.ex
277 .spawn_local(Abortable::new(
278 run_in_queue(queue, mem, doorbell, kick_evt, wlstate, wlstate_ctx),
279 registration,
280 ))
281 .detach();
282 }
283 1 => {
284 self.ex
285 .spawn_local(Abortable::new(
286 run_out_queue(queue, mem, doorbell, kick_evt, wlstate),
287 registration,
288 ))
289 .detach();
290 }
291 _ => bail!("attempted to start unknown queue: {}", idx),
292 }
293 self.workers[idx] = Some(handle);
294 Ok(())
295 }
296
stop_queue(&mut self, idx: usize)297 fn stop_queue(&mut self, idx: usize) {
298 if let Some(handle) = self.workers.get_mut(idx).and_then(Option::take) {
299 handle.abort();
300 }
301 }
reset(&mut self)302 fn reset(&mut self) {
303 for handle in self.workers.iter_mut().filter_map(Option::take) {
304 handle.abort();
305 }
306 }
307
get_shared_memory_region(&self) -> Option<SharedMemoryRegion>308 fn get_shared_memory_region(&self) -> Option<SharedMemoryRegion> {
309 Some(SharedMemoryRegion {
310 id: wl::WL_SHMEM_ID,
311 length: wl::WL_SHMEM_SIZE,
312 })
313 }
314
set_backend_req_connection(&mut self, conn: VhostBackendReqConnection)315 fn set_backend_req_connection(&mut self, conn: VhostBackendReqConnection) {
316 if let VhostBackendReqConnectionState::Connected(_) = &self.backend_req_conn {
317 warn!("connection already established. Overwriting");
318 }
319
320 self.backend_req_conn = VhostBackendReqConnectionState::Connected(conn);
321 }
322 }
323
parse_wayland_sock(value: &str) -> Result<(String, PathBuf), String>324 pub fn parse_wayland_sock(value: &str) -> Result<(String, PathBuf), String> {
325 let mut components = value.split(',');
326 let path = PathBuf::from(match components.next() {
327 None => return Err("missing socket path".to_string()),
328 Some(c) => c,
329 });
330 let mut name = "";
331 for c in components {
332 let mut kv = c.splitn(2, '=');
333 let (kind, value) = match (kv.next(), kv.next()) {
334 (Some(kind), Some(value)) => (kind, value),
335 _ => return Err(format!("option must be of the form `kind=value`: {}", c)),
336 };
337 match kind {
338 "name" => name = value,
339 _ => return Err(format!("unrecognized option: {}", kind)),
340 }
341 }
342
343 Ok((name.to_string(), path))
344 }
345
346 #[derive(FromArgs)]
347 #[argh(subcommand, name = "wl")]
348 /// Wayland device
349 pub struct Options {
350 #[argh(option, arg_name = "PATH")]
351 /// path to bind a listening vhost-user socket
352 socket: Option<String>,
353 #[argh(option, arg_name = "STRING")]
354 /// VFIO-PCI device name (e.g. '0000:00:07.0')
355 vfio: Option<String>,
356 #[argh(option, from_str_fn(parse_wayland_sock), arg_name = "PATH[,name=NAME]")]
357 /// path to one or more Wayland sockets. The unnamed socket is used for
358 /// displaying virtual screens while the named ones are used for IPC
359 wayland_sock: Vec<(String, PathBuf)>,
360 #[argh(option, arg_name = "PATH")]
361 /// path to the GPU resource bridge
362 resource_bridge: Option<String>,
363 }
364
365 /// Starts a vhost-user wayland device.
366 /// Returns an error if the given `args` is invalid or the device fails to run.
run_wl_device(opts: Options) -> anyhow::Result<()>367 pub fn run_wl_device(opts: Options) -> anyhow::Result<()> {
368 let Options {
369 wayland_sock,
370 socket,
371 vfio,
372 resource_bridge,
373 } = opts;
374
375 let wayland_paths: BTreeMap<_, _> = wayland_sock.into_iter().collect();
376
377 let resource_bridge = resource_bridge
378 .map(|p| -> anyhow::Result<Tube> {
379 let deadline = Instant::now() + Duration::from_secs(5);
380 loop {
381 match UnixSeqpacket::connect(&p) {
382 Ok(s) => return Ok(Tube::new_from_unix_seqpacket(s)),
383 Err(e) => {
384 if Instant::now() < deadline {
385 thread::sleep(Duration::from_millis(50));
386 } else {
387 return Err(anyhow::Error::new(e));
388 }
389 }
390 }
391 }
392 })
393 .transpose()
394 .context("failed to connect to resource bridge socket")?;
395
396 let ex = Executor::new().context("failed to create executor")?;
397
398 let listener =
399 VhostUserListener::new_from_socket_or_vfio(&socket, &vfio, QUEUE_SIZES.len(), None)?;
400
401 let backend = Box::new(WlBackend::new(&ex, wayland_paths, resource_bridge));
402 // run_until() returns an Result<Result<..>> which the ? operator lets us flatten.
403 ex.run_until(listener.run_backend(backend, &ex))?
404 }
405