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::SHMEM_MAP
62            && code != BackendReq::SHMEM_UNMAP
63            && code != BackendReq::GPU_MAP
64            && code != BackendReq::EXTERNAL_MAP
65            && !self.reply_ack_negotiated
66        {
67            return Ok(0);
68        }
69
70        let (reply, body, rfds) = self.sock.recv_message::<VhostUserU64>()?;
71        if !reply.is_valid() || !reply.is_reply_for(hdr) || !rfds.is_empty() || !body.is_valid() {
72            return Err(Error::InvalidMessage);
73        }
74        if body.value != 0 {
75            return Err(Error::FrontendInternalError);
76        }
77
78        Ok(body.value)
79    }
80
81    /// Set the negotiation state of the `VHOST_USER_PROTOCOL_F_REPLY_ACK` protocol feature.
82    ///
83    /// When the `VHOST_USER_PROTOCOL_F_REPLY_ACK` protocol feature has been negotiated, the
84    /// "REPLY_ACK" flag will be set in the message header for every backend to frontend request
85    /// message.
86    pub fn set_reply_ack_flag(&mut self, enable: bool) {
87        self.reply_ack_negotiated = enable;
88    }
89}
90
91impl Frontend for FrontendClient {
92    /// Handle shared memory region mapping requests.
93    fn shmem_map(
94        &mut self,
95        req: &VhostUserShmemMapMsg,
96        fd: &dyn AsRawDescriptor,
97    ) -> HandlerResult<u64> {
98        self.send_message(BackendReq::SHMEM_MAP, req, Some(&[fd.as_raw_descriptor()]))
99    }
100
101    /// Handle shared memory region unmapping requests.
102    fn shmem_unmap(&mut self, req: &VhostUserShmemUnmapMsg) -> HandlerResult<u64> {
103        self.send_message(BackendReq::SHMEM_UNMAP, req, None)
104    }
105
106    /// Handle config change requests.
107    fn handle_config_change(&mut self) -> HandlerResult<u64> {
108        self.send_message(BackendReq::CONFIG_CHANGE_MSG, &VhostUserEmptyMessage, None)
109    }
110
111    /// Handle GPU shared memory region mapping requests.
112    fn gpu_map(
113        &mut self,
114        req: &VhostUserGpuMapMsg,
115        descriptor: &dyn AsRawDescriptor,
116    ) -> HandlerResult<u64> {
117        self.send_message(
118            BackendReq::GPU_MAP,
119            req,
120            Some(&[descriptor.as_raw_descriptor()]),
121        )
122    }
123
124    /// Handle external memory region mapping requests.
125    fn external_map(&mut self, req: &VhostUserExternalMapMsg) -> HandlerResult<u64> {
126        self.send_message(BackendReq::EXTERNAL_MAP, req, None)
127    }
128}