devices/virtio/vhost_user_backend/
vsock.rs1use std::convert::TryInto;
6use std::fs::File;
7use std::fs::OpenOptions;
8use std::mem::size_of;
9use std::num::Wrapping;
10use std::os::unix::fs::OpenOptionsExt;
11use std::path::Path;
12use std::str;
13
14use anyhow::Context;
15use argh::FromArgs;
16use base::AsRawDescriptor;
17use base::Event;
18use base::RawDescriptor;
19use base::SafeDescriptor;
20use cros_async::Executor;
21use data_model::Le64;
22use vhost::Vhost;
23use vhost::Vsock;
24use vm_memory::GuestMemory;
25use vmm_vhost::connection::Connection;
26use vmm_vhost::message::VhostUserConfigFlags;
27use vmm_vhost::message::VhostUserInflight;
28use vmm_vhost::message::VhostUserMemoryRegion;
29use vmm_vhost::message::VhostUserMigrationPhase;
30use vmm_vhost::message::VhostUserProtocolFeatures;
31use vmm_vhost::message::VhostUserSingleMemoryRegion;
32use vmm_vhost::message::VhostUserTransferDirection;
33use vmm_vhost::message::VhostUserVringAddrFlags;
34use vmm_vhost::message::VhostUserVringState;
35use vmm_vhost::Error;
36use vmm_vhost::Result;
37use vmm_vhost::SharedMemoryRegion;
38use vmm_vhost::VHOST_USER_F_PROTOCOL_FEATURES;
39use zerocopy::IntoBytes;
40
41use super::BackendConnection;
42use crate::virtio::device_constants::vsock::NUM_QUEUES;
43use crate::virtio::vhost_user_backend::handler::vmm_va_to_gpa;
44use crate::virtio::vhost_user_backend::handler::MappingInfo;
45use crate::virtio::vhost_user_backend::handler::VhostUserRegularOps;
46use crate::virtio::vhost_user_backend::VhostUserDeviceBuilder;
47use crate::virtio::Queue;
48use crate::virtio::QueueConfig;
49
50const EVENT_QUEUE: usize = NUM_QUEUES - 1;
51
52struct VsockBackend {
53 queues: [QueueConfig; NUM_QUEUES],
54 vmm_maps: Option<Vec<MappingInfo>>,
55 mem: Option<GuestMemory>,
56
57 handle: Vsock,
58 cid: u64,
59 protocol_features: VhostUserProtocolFeatures,
60}
61
62pub struct VhostUserVsockDevice {
66 cid: u64,
67 handle: Vsock,
68}
69
70impl VhostUserVsockDevice {
71 pub fn new<P: AsRef<Path>>(cid: u64, vhost_device: P) -> anyhow::Result<Self> {
72 let handle = Vsock::new(
73 OpenOptions::new()
74 .read(true)
75 .write(true)
76 .custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK)
77 .open(vhost_device.as_ref())
78 .with_context(|| {
79 format!(
80 "failed to open vhost-vsock device {}",
81 vhost_device.as_ref().display()
82 )
83 })?,
84 );
85
86 Ok(Self { cid, handle })
87 }
88}
89
90impl AsRawDescriptor for VhostUserVsockDevice {
91 fn as_raw_descriptor(&self) -> base::RawDescriptor {
92 self.handle.as_raw_descriptor()
93 }
94}
95
96impl VhostUserDeviceBuilder for VhostUserVsockDevice {
97 fn build(self: Box<Self>, _ex: &Executor) -> anyhow::Result<Box<dyn vmm_vhost::Backend>> {
98 let backend = VsockBackend {
99 queues: [
100 QueueConfig::new(Queue::MAX_SIZE, 0),
101 QueueConfig::new(Queue::MAX_SIZE, 0),
102 QueueConfig::new(Queue::MAX_SIZE, 0),
103 ],
104 vmm_maps: None,
105 mem: None,
106 handle: self.handle,
107 cid: self.cid,
108 protocol_features: VhostUserProtocolFeatures::MQ | VhostUserProtocolFeatures::CONFIG,
109 };
110
111 Ok(Box::new(backend))
112 }
113}
114
115fn convert_vhost_error(err: vhost::Error) -> Error {
116 use vhost::Error::*;
117 match err {
118 IoctlError(e) => Error::ReqHandlerError(e),
119 _ => Error::BackendInternalError,
120 }
121}
122
123impl vmm_vhost::Backend for VsockBackend {
124 fn set_owner(&mut self) -> Result<()> {
125 self.handle.set_owner().map_err(convert_vhost_error)
126 }
127
128 fn reset_owner(&mut self) -> Result<()> {
129 self.handle.reset_owner().map_err(convert_vhost_error)
130 }
131
132 fn get_features(&mut self) -> Result<u64> {
133 let features = self.handle.get_features().map_err(convert_vhost_error)?
135 | 1 << VHOST_USER_F_PROTOCOL_FEATURES;
136 Ok(features)
137 }
138
139 fn set_features(&mut self, features: u64) -> Result<()> {
140 let features = features & !(1 << VHOST_USER_F_PROTOCOL_FEATURES);
143 self.handle
144 .set_features(features)
145 .map_err(convert_vhost_error)
146 }
147
148 fn get_protocol_features(&mut self) -> Result<VhostUserProtocolFeatures> {
149 Ok(self.protocol_features)
150 }
151
152 fn set_protocol_features(&mut self, features: u64) -> Result<()> {
153 let unrequested_features = features & !self.protocol_features.bits();
154 if unrequested_features != 0 {
155 Err(Error::InvalidParam("unsupported protocol feature"))
156 } else {
157 Ok(())
158 }
159 }
160
161 fn set_mem_table(
162 &mut self,
163 contexts: &[VhostUserMemoryRegion],
164 files: Vec<File>,
165 ) -> Result<()> {
166 let (guest_mem, vmm_maps) = VhostUserRegularOps::set_mem_table(contexts, files)?;
167
168 self.handle
169 .set_mem_table(&guest_mem)
170 .map_err(convert_vhost_error)?;
171
172 self.mem = Some(guest_mem);
173 self.vmm_maps = Some(vmm_maps);
174
175 Ok(())
176 }
177
178 fn get_queue_num(&mut self) -> Result<u64> {
179 Ok(NUM_QUEUES as u64)
180 }
181
182 fn set_vring_num(&mut self, index: u32, num: u32) -> Result<()> {
183 if index >= NUM_QUEUES as u32 || num == 0 || num > Queue::MAX_SIZE.into() {
184 return Err(Error::InvalidParam(
185 "set_vring_num: vring index or size out of range",
186 ));
187 }
188
189 let index = index as usize;
191 let num = num as u16;
192 self.queues[index].set_size(num);
193
194 if index == EVENT_QUEUE {
196 return Ok(());
197 }
198
199 self.handle
200 .set_vring_num(index, num)
201 .map_err(convert_vhost_error)
202 }
203
204 fn set_vring_addr(
205 &mut self,
206 index: u32,
207 flags: VhostUserVringAddrFlags,
208 descriptor: u64,
209 used: u64,
210 available: u64,
211 log: u64,
212 ) -> Result<()> {
213 if index >= NUM_QUEUES as u32 {
214 return Err(Error::InvalidParam("set_vring_addr: index out of range"));
215 }
216
217 let index = index as usize;
218
219 let mem = self
220 .mem
221 .as_ref()
222 .ok_or(Error::InvalidParam("set_vring_addr: could not get mem"))?;
223 let maps = self.vmm_maps.as_ref().ok_or(Error::InvalidParam(
224 "set_vring_addr: could not get vmm_maps",
225 ))?;
226
227 let queue = &mut self.queues[index];
228 queue.set_desc_table(vmm_va_to_gpa(maps, descriptor)?);
229 queue.set_avail_ring(vmm_va_to_gpa(maps, available)?);
230 queue.set_used_ring(vmm_va_to_gpa(maps, used)?);
231 let log_addr = if flags.contains(VhostUserVringAddrFlags::VHOST_VRING_F_LOG) {
232 vmm_va_to_gpa(maps, log).map(Some)?
233 } else {
234 None
235 };
236
237 if index == EVENT_QUEUE {
238 return Ok(());
239 }
240
241 self.handle
242 .set_vring_addr(
243 mem,
244 queue.size(),
245 index,
246 flags.bits(),
247 queue.desc_table(),
248 queue.used_ring(),
249 queue.avail_ring(),
250 log_addr,
251 )
252 .map_err(convert_vhost_error)
253 }
254
255 fn set_vring_base(&mut self, index: u32, base: u32) -> Result<()> {
256 if index >= NUM_QUEUES as u32 {
257 return Err(Error::InvalidParam("set_vring_base: index out of range"));
258 }
259
260 let index = index as usize;
261 let base = base as u16;
262
263 let queue = &mut self.queues[index];
264 queue.set_next_avail(Wrapping(base));
265 queue.set_next_used(Wrapping(base));
266
267 if index == EVENT_QUEUE {
268 return Ok(());
269 }
270
271 self.handle
272 .set_vring_base(index, base)
273 .map_err(convert_vhost_error)
274 }
275
276 fn get_vring_base(&mut self, index: u32) -> Result<VhostUserVringState> {
277 if index >= NUM_QUEUES as u32 {
278 return Err(Error::InvalidParam("get_vring_base: index out of range"));
279 }
280
281 let index = index as usize;
282 let next_avail = if index == EVENT_QUEUE {
283 self.queues[index].next_avail().0
284 } else {
285 self.handle
286 .get_vring_base(index)
287 .map_err(convert_vhost_error)?
288 };
289
290 Ok(VhostUserVringState::new(index as u32, next_avail.into()))
291 }
292
293 fn set_vring_kick(&mut self, index: u8, fd: Option<File>) -> Result<()> {
294 if index >= NUM_QUEUES as u8 {
295 return Err(Error::InvalidParam("set_vring_kick: index out of range"));
296 }
297
298 let file = fd.ok_or(Error::InvalidParam("set_vring_kick: missing fd"))?;
299 let event = Event::from(SafeDescriptor::from(file));
300 let index = usize::from(index);
301 if index != EVENT_QUEUE {
302 self.handle
303 .set_vring_kick(index, &event)
304 .map_err(convert_vhost_error)?;
305 }
306
307 Ok(())
308 }
309
310 fn set_vring_call(&mut self, index: u8, fd: Option<File>) -> Result<()> {
311 if index >= NUM_QUEUES as u8 {
312 return Err(Error::InvalidParam("set_vring_call: index out of range"));
313 }
314
315 let file = fd.ok_or(Error::InvalidParam("set_vring_call: missing fd"))?;
316 let event = Event::from(SafeDescriptor::from(file));
317 let index = usize::from(index);
318 if index != EVENT_QUEUE {
319 self.handle
320 .set_vring_call(index, &event)
321 .map_err(convert_vhost_error)?;
322 }
323
324 Ok(())
325 }
326
327 fn set_vring_err(&mut self, index: u8, fd: Option<File>) -> Result<()> {
328 if index >= NUM_QUEUES as u8 {
329 return Err(Error::InvalidParam("set_vring_err: index out of range"));
330 }
331
332 let index = usize::from(index);
333 let file = fd.ok_or(Error::InvalidParam("set_vring_err: missing fd"))?;
334
335 let event = Event::from(SafeDescriptor::from(file));
336
337 if index == EVENT_QUEUE {
338 return Ok(());
339 }
340
341 self.handle
342 .set_vring_err(index, &event)
343 .map_err(convert_vhost_error)
344 }
345
346 fn set_vring_enable(&mut self, index: u32, enable: bool) -> Result<()> {
347 if index >= NUM_QUEUES as u32 {
348 return Err(Error::InvalidParam("vring index out of range"));
349 }
350
351 self.queues[index as usize].set_ready(enable);
352
353 if index == (EVENT_QUEUE) as u32 {
354 return Ok(());
355 }
356
357 if self.queues[..EVENT_QUEUE].iter().all(|q| q.ready()) {
358 self.handle.set_cid(self.cid).map_err(convert_vhost_error)?;
360 self.handle.start().map_err(convert_vhost_error)
361 } else if !enable {
362 self.handle.stop().map_err(convert_vhost_error)
364 } else {
365 Ok(())
366 }
367 }
368
369 fn get_config(
370 &mut self,
371 offset: u32,
372 size: u32,
373 _flags: VhostUserConfigFlags,
374 ) -> Result<Vec<u8>> {
375 let start: usize = offset
376 .try_into()
377 .map_err(|_| Error::InvalidParam("offset does not fit in usize"))?;
378 let end: usize = offset
379 .checked_add(size)
380 .and_then(|e| e.try_into().ok())
381 .ok_or(Error::InvalidParam("offset + size does not fit in usize"))?;
382
383 if start >= size_of::<Le64>() || end > size_of::<Le64>() {
384 return Err(Error::InvalidParam(
385 "get_config: offset and/or size out of range",
386 ));
387 }
388
389 Ok(Le64::from(self.cid).as_bytes()[start..end].to_vec())
390 }
391
392 fn set_config(
393 &mut self,
394 _offset: u32,
395 _buf: &[u8],
396 _flags: VhostUserConfigFlags,
397 ) -> Result<()> {
398 Err(Error::InvalidOperation)
399 }
400
401 fn set_backend_req_fd(&mut self, _vu_req: Connection) {
402 unreachable!("unexpected set_backend_req_fd");
404 }
405
406 fn get_inflight_fd(
407 &mut self,
408 _inflight: &VhostUserInflight,
409 ) -> Result<(VhostUserInflight, File)> {
410 Err(Error::InvalidOperation)
411 }
412
413 fn set_inflight_fd(&mut self, _inflight: &VhostUserInflight, _file: File) -> Result<()> {
414 Err(Error::InvalidOperation)
415 }
416
417 fn get_max_mem_slots(&mut self) -> Result<u64> {
418 Err(Error::InvalidOperation)
419 }
420
421 fn add_mem_region(&mut self, _region: &VhostUserSingleMemoryRegion, _fd: File) -> Result<()> {
422 Err(Error::InvalidOperation)
423 }
424
425 fn remove_mem_region(&mut self, _region: &VhostUserSingleMemoryRegion) -> Result<()> {
426 Err(Error::InvalidOperation)
427 }
428
429 fn set_device_state_fd(
430 &mut self,
431 _transfer_direction: VhostUserTransferDirection,
432 _migration_phase: VhostUserMigrationPhase,
433 _fd: File,
434 ) -> Result<Option<File>> {
435 Err(Error::InvalidOperation)
436 }
437
438 fn check_device_state(&mut self) -> Result<()> {
439 Err(Error::InvalidOperation)
440 }
441
442 fn get_shmem_config(&mut self) -> Result<Vec<SharedMemoryRegion>> {
443 Ok(Vec::new())
444 }
445}
446
447#[derive(FromArgs)]
448#[argh(subcommand, name = "vsock")]
449pub struct Options {
451 #[argh(option, arg_name = "PATH", hidden_help)]
452 socket: Option<String>,
454 #[argh(option, arg_name = "PATH")]
455 socket_path: Option<String>,
458 #[argh(option, arg_name = "FD")]
459 fd: Option<RawDescriptor>,
462
463 #[argh(option, arg_name = "INT")]
464 cid: u64,
466 #[argh(
467 option,
468 default = "String::from(\"/dev/vhost-vsock\")",
469 arg_name = "PATH"
470 )]
471 vhost_socket: String,
473}
474
475pub fn run_vsock_device(opts: Options) -> anyhow::Result<()> {
477 let ex = Executor::new().context("failed to create executor")?;
478
479 let conn =
480 BackendConnection::from_opts(opts.socket.as_deref(), opts.socket_path.as_deref(), opts.fd)?;
481
482 let vsock_device = Box::new(VhostUserVsockDevice::new(opts.cid, opts.vhost_socket)?);
483
484 conn.run_device(ex, vsock_device)
485}