• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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