devices/virtio/vhost_user_backend/
wl.rs1use 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 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, )));
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 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 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")]
296pub struct Options {
298 #[argh(option, arg_name = "PATH", hidden_help)]
299 socket: Option<String>,
301 #[argh(option, arg_name = "PATH")]
302 socket_path: Option<String>,
305 #[argh(option, arg_name = "FD")]
306 fd: Option<RawDescriptor>,
309
310 #[argh(option, from_str_fn(parse_wayland_sock), arg_name = "PATH[,name=NAME]")]
311 wayland_sock: Vec<(String, PathBuf)>,
314 #[argh(option, arg_name = "PATH")]
315 resource_bridge: Option<String>,
317}
318
319pub 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 ex.run_until(conn.run_backend(backend, &ex))?
358}