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<BackendReq>,
23
24    // Protocol feature VHOST_USER_PROTOCOL_F_REPLY_ACK has been negotiated.
25    reply_ack_negotiated: bool,
26}
27
28impl FrontendClient {
29    /// Create a new instance from the given connection.
30    pub fn new(ep: Connection<BackendReq>) -> Self {
31        FrontendClient {
32            sock: ep,
33            reply_ack_negotiated: false,
34        }
35    }
36
37    fn send_message<T>(
38        &mut self,
39        request: BackendReq,
40        msg: &T,
41        fds: Option<&[RawDescriptor]>,
42    ) -> HandlerResult<u64>
43    where
44        T: IntoBytes + Immutable,
45    {
46        let len = mem::size_of::<T>();
47        let mut hdr = VhostUserMsgHeader::new(request, 0, len as u32);
48        if self.reply_ack_negotiated {
49            hdr.set_need_reply(true);
50        }
51        self.sock
52            .send_message(&hdr, msg, fds)
53            .map_err(|e| std::io::Error::other(e.to_string()))?;
54
55        self.wait_for_reply(&hdr)
56            .map_err(|e| std::io::Error::other(e.to_string()))
57    }
58
59    fn wait_for_reply(&mut self, hdr: &VhostUserMsgHeader<BackendReq>) -> Result<u64> {
60        let code = hdr.get_code().map_err(|_| Error::InvalidMessage)?;
61        if code != BackendReq::GPU_MAP
62            && code != BackendReq::EXTERNAL_MAP
63            // TODO(fmayle): Remove SHMEM cases once REPLY_ACK negotiation is supported.
64            && code != BackendReq::SHMEM_MAP
65            && code != BackendReq::SHMEM_UNMAP
66            && !self.reply_ack_negotiated
67        {
68            return Ok(0);
69        }
70
71        let (reply, body, rfds) = self.sock.recv_message::<VhostUserU64>()?;
72        if !reply.is_valid() || !reply.is_reply_for(hdr) || !rfds.is_empty() || !body.is_valid() {
73            return Err(Error::InvalidMessage);
74        }
75        if body.value != 0 {
76            return Err(Error::FrontendInternalError);
77        }
78
79        Ok(body.value)
80    }
81
82    /// Set the negotiation state of the `VHOST_USER_PROTOCOL_F_REPLY_ACK` protocol feature.
83    ///
84    /// When the `VHOST_USER_PROTOCOL_F_REPLY_ACK` protocol feature has been negotiated, the
85    /// "REPLY_ACK" flag will be set in the message header for every backend to frontend request
86    /// message.
87    pub fn set_reply_ack_flag(&mut self, enable: bool) {
88        self.reply_ack_negotiated = enable;
89    }
90}
91
92impl Frontend for FrontendClient {
93    /// Handle shared memory region mapping requests.
94    fn shmem_map(&mut self, req: &VhostUserMMap, fd: &dyn AsRawDescriptor) -> HandlerResult<u64> {
95        self.send_message(BackendReq::SHMEM_MAP, req, Some(&[fd.as_raw_descriptor()]))
96    }
97
98    /// Handle shared memory region unmapping requests.
99    fn shmem_unmap(&mut self, req: &VhostUserMMap) -> HandlerResult<u64> {
100        self.send_message(BackendReq::SHMEM_UNMAP, req, None)
101    }
102
103    /// Handle config change requests.
104    fn handle_config_change(&mut self) -> HandlerResult<u64> {
105        self.send_message(BackendReq::CONFIG_CHANGE_MSG, &VhostUserEmptyMessage, None)
106    }
107
108    /// Handle GPU shared memory region mapping requests.
109    fn gpu_map(
110        &mut self,
111        req: &VhostUserGpuMapMsg,
112        descriptor: &dyn AsRawDescriptor,
113    ) -> HandlerResult<u64> {
114        self.send_message(
115            BackendReq::GPU_MAP,
116            req,
117            Some(&[descriptor.as_raw_descriptor()]),
118        )
119    }
120
121    /// Handle external memory region mapping requests.
122    fn external_map(&mut self, req: &VhostUserExternalMapMsg) -> HandlerResult<u64> {
123        self.send_message(BackendReq::EXTERNAL_MAP, req, None)
124    }
125}