• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 The Chromium OS Authors. All rights reserved.
2 // SPDX-License-Identifier: Apache-2.0
3 
4 //! Structs for VFIO listener and endpoint.
5 
6 use std::convert::From;
7 use std::fs::File;
8 use std::io::{IoSlice, IoSliceMut};
9 use std::marker::PhantomData;
10 use std::os::unix::io::RawFd;
11 use std::path::Path;
12 
13 use base::{AsRawDescriptor, Event, RawDescriptor};
14 use remain::sorted;
15 use thiserror::Error as ThisError;
16 
17 use super::{Error, Result};
18 use crate::connection::{Endpoint as EndpointTrait, Listener as ListenerTrait, Req};
19 
20 /// Errors for `Device::recv_into_bufs()`.
21 #[sorted]
22 #[derive(Debug, ThisError)]
23 pub enum RecvIntoBufsError {
24     /// Connection is closed.
25     #[error("connection is closed")]
26     Disconnect,
27     /// Fatal error while receiving data.
28     #[error("failed to receive data via VFIO: {0:#}")]
29     Fatal(anyhow::Error),
30 }
31 
32 impl From<RecvIntoBufsError> for Error {
from(e: RecvIntoBufsError) -> Self33     fn from(e: RecvIntoBufsError) -> Self {
34         match e {
35             RecvIntoBufsError::Disconnect => Error::Disconnect,
36             RecvIntoBufsError::Fatal(e) => Error::VfioDeviceError(e),
37         }
38     }
39 }
40 
41 /// VFIO device which can be used as virtio-vhost-user device backend.
42 pub trait Device {
43     /// This event must be read before handle_request() is called.
event(&self) -> &Event44     fn event(&self) -> &Event;
45 
46     /// Starts VFIO device.
start(&mut self) -> std::result::Result<(), anyhow::Error>47     fn start(&mut self) -> std::result::Result<(), anyhow::Error>;
48 
49     /// Sends data in the given slice of slices.
send_bufs( &mut self, iovs: &[IoSlice], fds: Option<&[RawFd]>, ) -> std::result::Result<usize, anyhow::Error>50     fn send_bufs(
51         &mut self,
52         iovs: &[IoSlice],
53         fds: Option<&[RawFd]>,
54     ) -> std::result::Result<usize, anyhow::Error>;
55 
56     /// Receives data into the given slice of slices and returns the size of the received data.
recv_into_bufs( &mut self, iovs: &mut [IoSliceMut], ) -> std::result::Result<usize, RecvIntoBufsError>57     fn recv_into_bufs(
58         &mut self,
59         iovs: &mut [IoSliceMut],
60     ) -> std::result::Result<usize, RecvIntoBufsError>;
61 }
62 
63 /// Listener for accepting incoming connections from virtio-vhost-user device through VFIO.
64 pub struct Listener<D: Device> {
65     // device will be dropped when Listener::accept() is called.
66     device: Option<D>,
67 }
68 
69 impl<D: Device> Listener<D> {
70     /// Creates a VFIO listener.
new(device: D) -> Result<Self>71     pub fn new(device: D) -> Result<Self> {
72         Ok(Self {
73             device: Some(device),
74         })
75     }
76 }
77 
78 impl<D: Device> ListenerTrait for Listener<D> {
79     type Connection = D;
80 
accept(&mut self) -> Result<Option<Self::Connection>>81     fn accept(&mut self) -> Result<Option<Self::Connection>> {
82         let mut device = self
83             .device
84             .take()
85             .expect("Listener isn't initialized correctly");
86         device.start().map_err(Error::VfioDeviceError)?;
87         Ok(Some(device))
88     }
89 
set_nonblocking(&self, _block: bool) -> Result<()>90     fn set_nonblocking(&self, _block: bool) -> Result<()> {
91         unimplemented!("set_nonblocking");
92     }
93 }
94 
95 /// Endpoint for vhost-user connection through VFIO.
96 pub struct Endpoint<R: Req, D: Device> {
97     device: D,
98     _r: PhantomData<R>,
99 }
100 
101 impl<R: Req, D: Device> EndpointTrait<R> for Endpoint<R, D> {
102     type Listener = Listener<D>;
103 
104     /// Create an endpoint from a stream object.
from_connection(device: D) -> Self105     fn from_connection(device: D) -> Self {
106         Self {
107             device,
108             _r: PhantomData,
109         }
110     }
111 
connect<P: AsRef<Path>>(_path: P) -> Result<Self>112     fn connect<P: AsRef<Path>>(_path: P) -> Result<Self> {
113         // TODO: remove this method from Endpoint trait?
114         panic!("VfioEndpoint cannot create a connection from path");
115     }
116 
send_iovec(&mut self, iovs: &[IoSlice], fds: Option<&[RawFd]>) -> Result<usize>117     fn send_iovec(&mut self, iovs: &[IoSlice], fds: Option<&[RawFd]>) -> Result<usize> {
118         self.device
119             .send_bufs(iovs, fds)
120             .map_err(Error::VfioDeviceError)
121     }
122 
recv_into_bufs( &mut self, bufs: &mut [IoSliceMut], _allow_fd: bool, ) -> Result<(usize, Option<Vec<File>>)>123     fn recv_into_bufs(
124         &mut self,
125         bufs: &mut [IoSliceMut],
126         _allow_fd: bool, /* ignore, as VFIO doesn't receive FDs */
127     ) -> Result<(usize, Option<Vec<File>>)> {
128         let size = self
129             .device
130             .recv_into_bufs(bufs)
131             .map_err::<Error, _>(From::<RecvIntoBufsError>::from)?;
132 
133         // VFIO backend doesn't receive any files.
134         Ok((size, None))
135     }
136 }
137 
138 impl<R: Req, D: Device> AsRawDescriptor for Endpoint<R, D> {
as_raw_descriptor(&self) -> RawDescriptor139     fn as_raw_descriptor(&self) -> RawDescriptor {
140         self.device.event().as_raw_descriptor()
141     }
142 }
143