1 // Copyright (C) 2019-2021 Alibaba Cloud. All rights reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 4 use std::fs::File; 5 use std::mem; 6 7 use base::AsRawDescriptor; 8 9 use crate::message::*; 10 use crate::BackendReq; 11 use crate::Connection; 12 use crate::Error; 13 use crate::HandlerResult; 14 use crate::Result; 15 16 /// Trait for vhost-user frontends to respond to requests from the backend. 17 /// 18 /// Each method corresponds to a vhost-user protocol method. See the specification for details. 19 pub trait Frontend { 20 /// Handle device configuration change notifications. handle_config_change(&mut self) -> HandlerResult<u64>21 fn handle_config_change(&mut self) -> HandlerResult<u64> { 22 Err(std::io::Error::from_raw_os_error(libc::ENOSYS)) 23 } 24 25 /// Handle shared memory region mapping requests. shmem_map( &mut self, _req: &VhostUserShmemMapMsg, _fd: &dyn AsRawDescriptor, ) -> HandlerResult<u64>26 fn shmem_map( 27 &mut self, 28 _req: &VhostUserShmemMapMsg, 29 _fd: &dyn AsRawDescriptor, 30 ) -> HandlerResult<u64> { 31 Err(std::io::Error::from_raw_os_error(libc::ENOSYS)) 32 } 33 34 /// Handle shared memory region unmapping requests. shmem_unmap(&mut self, _req: &VhostUserShmemUnmapMsg) -> HandlerResult<u64>35 fn shmem_unmap(&mut self, _req: &VhostUserShmemUnmapMsg) -> HandlerResult<u64> { 36 Err(std::io::Error::from_raw_os_error(libc::ENOSYS)) 37 } 38 39 // fn handle_iotlb_msg(&mut self, iotlb: VhostUserIotlb); 40 // fn handle_vring_host_notifier(&mut self, area: VhostUserVringArea, fd: RawDescriptor); 41 42 /// Handle GPU shared memory region mapping requests. gpu_map( &mut self, _req: &VhostUserGpuMapMsg, _descriptor: &dyn AsRawDescriptor, ) -> HandlerResult<u64>43 fn gpu_map( 44 &mut self, 45 _req: &VhostUserGpuMapMsg, 46 _descriptor: &dyn AsRawDescriptor, 47 ) -> HandlerResult<u64> { 48 Err(std::io::Error::from_raw_os_error(libc::ENOSYS)) 49 } 50 51 /// Handle external memory region mapping requests. external_map(&mut self, _req: &VhostUserExternalMapMsg) -> HandlerResult<u64>52 fn external_map(&mut self, _req: &VhostUserExternalMapMsg) -> HandlerResult<u64> { 53 Err(std::io::Error::from_raw_os_error(libc::ENOSYS)) 54 } 55 } 56 57 /// Handles requests from a vhost-user backend connection by dispatching them to [[Frontend]] 58 /// methods. 59 pub struct FrontendServer<S: Frontend> { 60 // underlying Unix domain socket for communication 61 pub(crate) sub_sock: Connection<BackendReq>, 62 // Protocol feature VHOST_USER_PROTOCOL_F_REPLY_ACK has been negotiated. 63 reply_ack_negotiated: bool, 64 65 frontend: S, 66 } 67 68 impl<S: Frontend> FrontendServer<S> { 69 /// Create a server to handle requests from `connection`. new(frontend: S, connection: Connection<BackendReq>) -> Result<Self>70 pub(crate) fn new(frontend: S, connection: Connection<BackendReq>) -> Result<Self> { 71 Ok(FrontendServer { 72 sub_sock: connection, 73 reply_ack_negotiated: false, 74 frontend, 75 }) 76 } 77 78 /// Set the negotiation state of the `VHOST_USER_PROTOCOL_F_REPLY_ACK` protocol feature. 79 /// 80 /// When the `VHOST_USER_PROTOCOL_F_REPLY_ACK` protocol feature has been negotiated, 81 /// the "REPLY_ACK" flag will be set in the message header for every request message. set_reply_ack_flag(&mut self, enable: bool)82 pub fn set_reply_ack_flag(&mut self, enable: bool) { 83 self.reply_ack_negotiated = enable; 84 } 85 86 /// Get the underlying frontend frontend_mut(&mut self) -> &mut S87 pub fn frontend_mut(&mut self) -> &mut S { 88 &mut self.frontend 89 } 90 91 /// Process the next received request. 92 /// 93 /// The caller needs to: 94 /// - serialize calls to this function 95 /// - decide what to do when errer happens 96 /// - optional recover from failure handle_request(&mut self) -> Result<u64>97 pub fn handle_request(&mut self) -> Result<u64> { 98 // The underlying communication channel is a Unix domain socket in 99 // stream mode, and recvmsg() is a little tricky here. To successfully 100 // receive attached file descriptors, we need to receive messages and 101 // corresponding attached file descriptors in this way: 102 // . recv messsage header and optional attached file 103 // . validate message header 104 // . recv optional message body and payload according size field in 105 // message header 106 // . validate message body and optional payload 107 let (hdr, files) = self.sub_sock.recv_header()?; 108 self.check_attached_files(&hdr, &files)?; 109 let buf = self.sub_sock.recv_body_bytes(&hdr)?; 110 let size = buf.len(); 111 112 let res = match hdr.get_code() { 113 Ok(BackendReq::CONFIG_CHANGE_MSG) => { 114 self.check_msg_size(&hdr, size, 0)?; 115 self.frontend 116 .handle_config_change() 117 .map_err(Error::ReqHandlerError) 118 } 119 Ok(BackendReq::SHMEM_MAP) => { 120 let msg = self.extract_msg_body::<VhostUserShmemMapMsg>(&hdr, size, &buf)?; 121 // check_attached_files() has validated files 122 self.frontend 123 .shmem_map(&msg, &files[0]) 124 .map_err(Error::ReqHandlerError) 125 } 126 Ok(BackendReq::SHMEM_UNMAP) => { 127 let msg = self.extract_msg_body::<VhostUserShmemUnmapMsg>(&hdr, size, &buf)?; 128 self.frontend 129 .shmem_unmap(&msg) 130 .map_err(Error::ReqHandlerError) 131 } 132 Ok(BackendReq::GPU_MAP) => { 133 let msg = self.extract_msg_body::<VhostUserGpuMapMsg>(&hdr, size, &buf)?; 134 // check_attached_files() has validated files 135 self.frontend 136 .gpu_map(&msg, &files[0]) 137 .map_err(Error::ReqHandlerError) 138 } 139 Ok(BackendReq::EXTERNAL_MAP) => { 140 let msg = self.extract_msg_body::<VhostUserExternalMapMsg>(&hdr, size, &buf)?; 141 self.frontend 142 .external_map(&msg) 143 .map_err(Error::ReqHandlerError) 144 } 145 _ => Err(Error::InvalidMessage), 146 }; 147 148 self.send_reply(&hdr, &res)?; 149 150 res 151 } 152 check_msg_size( &self, hdr: &VhostUserMsgHeader<BackendReq>, size: usize, expected: usize, ) -> Result<()>153 fn check_msg_size( 154 &self, 155 hdr: &VhostUserMsgHeader<BackendReq>, 156 size: usize, 157 expected: usize, 158 ) -> Result<()> { 159 if hdr.get_size() as usize != expected 160 || hdr.is_reply() 161 || hdr.get_version() != 0x1 162 || size != expected 163 { 164 return Err(Error::InvalidMessage); 165 } 166 Ok(()) 167 } 168 check_attached_files( &self, hdr: &VhostUserMsgHeader<BackendReq>, files: &[File], ) -> Result<()>169 fn check_attached_files( 170 &self, 171 hdr: &VhostUserMsgHeader<BackendReq>, 172 files: &[File], 173 ) -> Result<()> { 174 let expected_num_files = match hdr.get_code().map_err(|_| Error::InvalidMessage)? { 175 // Expect a single file is passed. 176 BackendReq::SHMEM_MAP | BackendReq::GPU_MAP => 1, 177 _ => 0, 178 }; 179 180 if files.len() == expected_num_files { 181 Ok(()) 182 } else { 183 Err(Error::InvalidMessage) 184 } 185 } 186 extract_msg_body<T: Sized + VhostUserMsgValidator>( &self, hdr: &VhostUserMsgHeader<BackendReq>, size: usize, buf: &[u8], ) -> Result<T>187 fn extract_msg_body<T: Sized + VhostUserMsgValidator>( 188 &self, 189 hdr: &VhostUserMsgHeader<BackendReq>, 190 size: usize, 191 buf: &[u8], 192 ) -> Result<T> { 193 self.check_msg_size(hdr, size, mem::size_of::<T>())?; 194 // SAFETY: above check ensures that buf is `T` sized. 195 let msg = unsafe { std::ptr::read_unaligned(buf.as_ptr() as *const T) }; 196 if !msg.is_valid() { 197 return Err(Error::InvalidMessage); 198 } 199 Ok(msg) 200 } 201 new_reply_header<T: Sized>( &self, req: &VhostUserMsgHeader<BackendReq>, ) -> Result<VhostUserMsgHeader<BackendReq>>202 fn new_reply_header<T: Sized>( 203 &self, 204 req: &VhostUserMsgHeader<BackendReq>, 205 ) -> Result<VhostUserMsgHeader<BackendReq>> { 206 Ok(VhostUserMsgHeader::new( 207 req.get_code().map_err(|_| Error::InvalidMessage)?, 208 VhostUserHeaderFlag::REPLY.bits(), 209 mem::size_of::<T>() as u32, 210 )) 211 } 212 send_reply( &mut self, req: &VhostUserMsgHeader<BackendReq>, res: &Result<u64>, ) -> Result<()>213 fn send_reply( 214 &mut self, 215 req: &VhostUserMsgHeader<BackendReq>, 216 res: &Result<u64>, 217 ) -> Result<()> { 218 let code = req.get_code().map_err(|_| Error::InvalidMessage)?; 219 if code == BackendReq::SHMEM_MAP 220 || code == BackendReq::SHMEM_UNMAP 221 || code == BackendReq::GPU_MAP 222 || code == BackendReq::EXTERNAL_MAP 223 || (self.reply_ack_negotiated && req.is_need_reply()) 224 { 225 let hdr = self.new_reply_header::<VhostUserU64>(req)?; 226 let def_err = libc::EINVAL; 227 let val = match res { 228 Ok(n) => *n, 229 Err(e) => match e { 230 Error::ReqHandlerError(ioerr) => match ioerr.raw_os_error() { 231 Some(rawerr) => -rawerr as u64, 232 None => -def_err as u64, 233 }, 234 _ => -def_err as u64, 235 }, 236 }; 237 let msg = VhostUserU64::new(val); 238 self.sub_sock.send_message(&hdr, &msg, None)?; 239 } 240 Ok(()) 241 } 242 } 243