• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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