1 // Copyright (C) 2020 Alibaba Cloud. All rights reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 4 use std::mem; 5 use std::string::ToString; 6 7 use base::AsRawDescriptor; 8 use base::RawDescriptor; 9 use zerocopy::AsBytes; 10 11 use crate::message::*; 12 use crate::BackendReq; 13 use crate::Connection; 14 use crate::Error; 15 use crate::Frontend; 16 use crate::HandlerResult; 17 use crate::Result; 18 use crate::SystemStream; 19 20 /// Client for a vhost-user frontend. Allows a backend to send requests to the frontend. 21 pub struct FrontendClient { 22 sock: Connection<BackendReq>, 23 24 // Protocol feature VHOST_USER_PROTOCOL_F_REPLY_ACK has been negotiated. 25 reply_ack_negotiated: bool, 26 27 // whether the connection has encountered any failure 28 error: Option<i32>, 29 } 30 31 impl FrontendClient { 32 /// Create a new instance from the given connection. new(ep: Connection<BackendReq>) -> Self33 pub fn new(ep: Connection<BackendReq>) -> Self { 34 FrontendClient { 35 sock: ep, 36 reply_ack_negotiated: false, 37 error: None, 38 } 39 } 40 41 /// Create a new instance from a `SystemStream` object. from_stream(connection: SystemStream) -> Self42 pub fn from_stream(connection: SystemStream) -> Self { 43 Self::new(Connection::from(connection)) 44 } 45 send_message<T>( &mut self, request: BackendReq, msg: &T, fds: Option<&[RawDescriptor]>, ) -> HandlerResult<u64> where T: AsBytes,46 fn send_message<T>( 47 &mut self, 48 request: BackendReq, 49 msg: &T, 50 fds: Option<&[RawDescriptor]>, 51 ) -> HandlerResult<u64> 52 where 53 T: AsBytes, 54 { 55 let len = mem::size_of::<T>(); 56 let mut hdr = VhostUserMsgHeader::new(request, 0, len as u32); 57 if self.reply_ack_negotiated { 58 hdr.set_need_reply(true); 59 } 60 self.sock 61 .send_message(&hdr, msg, fds) 62 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; 63 64 self.wait_for_reply(&hdr) 65 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string())) 66 } 67 wait_for_reply(&mut self, hdr: &VhostUserMsgHeader<BackendReq>) -> Result<u64>68 fn wait_for_reply(&mut self, hdr: &VhostUserMsgHeader<BackendReq>) -> Result<u64> { 69 let code = hdr.get_code().map_err(|_| Error::InvalidMessage)?; 70 if code != BackendReq::SHMEM_MAP 71 && code != BackendReq::SHMEM_UNMAP 72 && code != BackendReq::GPU_MAP 73 && code != BackendReq::EXTERNAL_MAP 74 && !self.reply_ack_negotiated 75 { 76 return Ok(0); 77 } 78 79 let (reply, body, rfds) = self.sock.recv_message::<VhostUserU64>()?; 80 if !reply.is_reply_for(hdr) || !rfds.is_empty() || !body.is_valid() { 81 return Err(Error::InvalidMessage); 82 } 83 if body.value != 0 { 84 return Err(Error::FrontendInternalError); 85 } 86 87 Ok(body.value) 88 } 89 90 /// Set the negotiation state of the `VHOST_USER_PROTOCOL_F_REPLY_ACK` protocol feature. 91 /// 92 /// When the `VHOST_USER_PROTOCOL_F_REPLY_ACK` protocol feature has been negotiated, the 93 /// "REPLY_ACK" flag will be set in the message header for every backend to frontend request 94 /// message. set_reply_ack_flag(&mut self, enable: bool)95 pub fn set_reply_ack_flag(&mut self, enable: bool) { 96 self.reply_ack_negotiated = enable; 97 } 98 99 /// Mark connection as failed with specified error code. set_failed(&mut self, error: i32)100 pub fn set_failed(&mut self, error: i32) { 101 self.error = Some(error); 102 } 103 } 104 105 impl Frontend for FrontendClient { 106 /// Handle shared memory region mapping requests. shmem_map( &mut self, req: &VhostUserShmemMapMsg, fd: &dyn AsRawDescriptor, ) -> HandlerResult<u64>107 fn shmem_map( 108 &mut self, 109 req: &VhostUserShmemMapMsg, 110 fd: &dyn AsRawDescriptor, 111 ) -> HandlerResult<u64> { 112 self.send_message(BackendReq::SHMEM_MAP, req, Some(&[fd.as_raw_descriptor()])) 113 } 114 115 /// Handle shared memory region unmapping requests. shmem_unmap(&mut self, req: &VhostUserShmemUnmapMsg) -> HandlerResult<u64>116 fn shmem_unmap(&mut self, req: &VhostUserShmemUnmapMsg) -> HandlerResult<u64> { 117 self.send_message(BackendReq::SHMEM_UNMAP, req, None) 118 } 119 120 /// Handle config change requests. handle_config_change(&mut self) -> HandlerResult<u64>121 fn handle_config_change(&mut self) -> HandlerResult<u64> { 122 self.send_message(BackendReq::CONFIG_CHANGE_MSG, &VhostUserEmptyMessage, None) 123 } 124 125 /// Handle GPU shared memory region mapping requests. gpu_map( &mut self, req: &VhostUserGpuMapMsg, descriptor: &dyn AsRawDescriptor, ) -> HandlerResult<u64>126 fn gpu_map( 127 &mut self, 128 req: &VhostUserGpuMapMsg, 129 descriptor: &dyn AsRawDescriptor, 130 ) -> HandlerResult<u64> { 131 self.send_message( 132 BackendReq::GPU_MAP, 133 req, 134 Some(&[descriptor.as_raw_descriptor()]), 135 ) 136 } 137 138 /// Handle external memory region mapping requests. external_map(&mut self, req: &VhostUserExternalMapMsg) -> HandlerResult<u64>139 fn external_map(&mut self, req: &VhostUserExternalMapMsg) -> HandlerResult<u64> { 140 self.send_message(BackendReq::EXTERNAL_MAP, req, None) 141 } 142 } 143 144 #[cfg(test)] 145 mod tests { 146 147 use super::*; 148 use crate::SystemStream; 149 150 #[test] test_backend_req_set_failed()151 fn test_backend_req_set_failed() { 152 let (p1, _p2) = SystemStream::pair().unwrap(); 153 let mut frontend_client = FrontendClient::from_stream(p1); 154 155 assert!(frontend_client.error.is_none()); 156 frontend_client.set_failed(libc::EAGAIN); 157 assert_eq!(frontend_client.error, Some(libc::EAGAIN)); 158 } 159 } 160