vmm_vhost/
frontend_client.rs

1// Copyright (C) 2020 Alibaba Cloud. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::mem;
5use std::string::ToString;
6
7use base::AsRawDescriptor;
8use base::RawDescriptor;
9use zerocopy::Immutable;
10use zerocopy::IntoBytes;
11
12use crate::message::*;
13use crate::BackendReq;
14use crate::Connection;
15use crate::Error;
16use crate::Frontend;
17use crate::HandlerResult;
18use crate::Result;
19
20/// Client for a vhost-user frontend. Allows a backend to send requests to the frontend.
21pub struct FrontendClient {
22    sock: Connection,
23
24    // Protocol feature VHOST_USER_PROTOCOL_F_REPLY_ACK has been negotiated.
25    //
26    // When true, we automatically set the "need_reply" bit for a subset of message types where we
27    // think it is useful to receive an ACK.
28    reply_ack_negotiated: bool,
29}
30
31impl FrontendClient {
32    /// Create a new instance from the given connection.
33    pub fn new(ep: Connection, reply_ack_negotiated: bool) -> Self {
34        FrontendClient {
35            sock: ep,
36            reply_ack_negotiated,
37        }
38    }
39
40    fn send_message<T>(
41        &mut self,
42        request: BackendReq,
43        msg: &T,
44        fds: Option<&[RawDescriptor]>,
45        want_reply: bool,
46    ) -> HandlerResult<()>
47    where
48        T: IntoBytes + Immutable,
49    {
50        let need_reply = want_reply && self.reply_ack_negotiated;
51
52        let len = mem::size_of::<T>();
53        let hdr = VhostUserMsgHeader::new_request_header(request, len as u32, need_reply);
54        self.sock
55            .send_message(&hdr, msg, fds)
56            .map_err(|e| std::io::Error::other(e.to_string()))?;
57
58        let non_standard_forced_reply =
59            [BackendReq::GPU_MAP, BackendReq::EXTERNAL_MAP].contains(&request);
60        if need_reply || non_standard_forced_reply {
61            self.wait_for_reply(&hdr)
62                .map_err(|e| std::io::Error::other(e.to_string()))?;
63        }
64        Ok(())
65    }
66
67    fn wait_for_reply(&mut self, hdr: &VhostUserMsgHeader) -> Result<()> {
68        let (reply, body, rfds) = self.sock.recv_message::<VhostUserU64>()?;
69        if !reply.is_valid() || !reply.is_reply_for(hdr) || !rfds.is_empty() || !body.is_valid() {
70            return Err(Error::InvalidMessage);
71        }
72        if body.value != 0 {
73            return Err(Error::FrontendInternalError);
74        }
75
76        Ok(())
77    }
78}
79
80impl Frontend for FrontendClient {
81    /// Handle shared memory region mapping requests.
82    fn shmem_map(&mut self, req: &VhostUserMMap, fd: &dyn AsRawDescriptor) -> HandlerResult<()> {
83        if !self.reply_ack_negotiated {
84            base::warn!("SHMEM_MAP without REPLY_ACK is prone to race conditions");
85        }
86        self.send_message(
87            BackendReq::SHMEM_MAP,
88            req,
89            Some(&[fd.as_raw_descriptor()]),
90            /* want_reply= */ true,
91        )
92    }
93
94    /// Handle shared memory region unmapping requests.
95    fn shmem_unmap(&mut self, req: &VhostUserMMap) -> HandlerResult<()> {
96        self.send_message(
97            BackendReq::SHMEM_UNMAP,
98            req,
99            None,
100            /* want_reply= */ true,
101        )
102    }
103
104    /// Handle config change requests.
105    fn handle_config_change(&mut self) -> HandlerResult<()> {
106        self.send_message(
107            BackendReq::CONFIG_CHANGE_MSG,
108            &VhostUserEmptyMessage,
109            None,
110            /* want_reply= */ false,
111        )
112    }
113
114    /// Handle GPU shared memory region mapping requests.
115    fn gpu_map(
116        &mut self,
117        req: &VhostUserGpuMapMsg,
118        descriptor: &dyn AsRawDescriptor,
119    ) -> HandlerResult<()> {
120        self.send_message(
121            BackendReq::GPU_MAP,
122            req,
123            Some(&[descriptor.as_raw_descriptor()]),
124            /* want_reply= */ false,
125        )
126    }
127
128    /// Handle external memory region mapping requests.
129    fn external_map(&mut self, req: &VhostUserExternalMapMsg) -> HandlerResult<()> {
130        self.send_message(
131            BackendReq::EXTERNAL_MAP,
132            req,
133            None,
134            /* want_reply= */ false,
135        )
136    }
137}