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