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