• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2019 Alibaba Cloud Computing. All rights reserved.
2 // SPDX-License-Identifier: Apache-2.0
3 
4 use std::fs::File;
5 
6 use super::message::*;
7 use super::*;
8 
9 pub const MAX_QUEUE_NUM: usize = 2;
10 pub const MAX_VRING_NUM: usize = 256;
11 pub const MAX_MEM_SLOTS: usize = 32;
12 pub const VIRTIO_FEATURES: u64 = 0x40000003;
13 
14 #[derive(Default)]
15 pub struct DummySlaveReqHandler {
16     pub owned: bool,
17     pub features_acked: bool,
18     pub acked_features: u64,
19     pub acked_protocol_features: u64,
20     pub queue_num: usize,
21     pub vring_num: [u32; MAX_QUEUE_NUM],
22     pub vring_base: [u32; MAX_QUEUE_NUM],
23     pub call_fd: [Option<File>; MAX_QUEUE_NUM],
24     pub kick_fd: [Option<File>; MAX_QUEUE_NUM],
25     pub err_fd: [Option<File>; MAX_QUEUE_NUM],
26     pub vring_started: [bool; MAX_QUEUE_NUM],
27     pub vring_enabled: [bool; MAX_QUEUE_NUM],
28     pub inflight_file: Option<File>,
29 }
30 
31 impl DummySlaveReqHandler {
new() -> Self32     pub fn new() -> Self {
33         DummySlaveReqHandler {
34             queue_num: MAX_QUEUE_NUM,
35             ..Default::default()
36         }
37     }
38 
39     /// Helper to check if VirtioFeature enabled
check_feature(&self, feat: VhostUserVirtioFeatures) -> Result<()>40     fn check_feature(&self, feat: VhostUserVirtioFeatures) -> Result<()> {
41         if self.acked_features & feat.bits() != 0 {
42             Ok(())
43         } else {
44             Err(Error::InactiveFeature(feat))
45         }
46     }
47 
48     /// Helper to check is VhostUserProtocolFeatures enabled
check_proto_feature(&self, feat: VhostUserProtocolFeatures) -> Result<()>49     fn check_proto_feature(&self, feat: VhostUserProtocolFeatures) -> Result<()> {
50         if self.acked_protocol_features & feat.bits() != 0 {
51             Ok(())
52         } else {
53             Err(Error::InactiveOperation(feat))
54         }
55     }
56 }
57 
58 impl VhostUserSlaveReqHandlerMut for DummySlaveReqHandler {
set_owner(&mut self) -> Result<()>59     fn set_owner(&mut self) -> Result<()> {
60         if self.owned {
61             return Err(Error::InvalidOperation("already claimed"));
62         }
63         self.owned = true;
64         Ok(())
65     }
66 
reset_owner(&mut self) -> Result<()>67     fn reset_owner(&mut self) -> Result<()> {
68         self.owned = false;
69         self.features_acked = false;
70         self.acked_features = 0;
71         self.acked_protocol_features = 0;
72         Ok(())
73     }
74 
get_features(&mut self) -> Result<u64>75     fn get_features(&mut self) -> Result<u64> {
76         Ok(VIRTIO_FEATURES)
77     }
78 
set_features(&mut self, features: u64) -> Result<()>79     fn set_features(&mut self, features: u64) -> Result<()> {
80         if !self.owned {
81             return Err(Error::InvalidOperation("not owned"));
82         } else if self.features_acked {
83             return Err(Error::InvalidOperation("features already set"));
84         } else if (features & !VIRTIO_FEATURES) != 0 {
85             return Err(Error::InvalidParam);
86         }
87 
88         self.acked_features = features;
89         self.features_acked = true;
90 
91         // If VHOST_USER_F_PROTOCOL_FEATURES has not been negotiated,
92         // the ring is initialized in an enabled state.
93         // If VHOST_USER_F_PROTOCOL_FEATURES has been negotiated,
94         // the ring is initialized in a disabled state. Client must not
95         // pass data to/from the backend until ring is enabled by
96         // VHOST_USER_SET_VRING_ENABLE with parameter 1, or after it has
97         // been disabled by VHOST_USER_SET_VRING_ENABLE with parameter 0.
98         let vring_enabled =
99             self.acked_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits() == 0;
100         for enabled in &mut self.vring_enabled {
101             *enabled = vring_enabled;
102         }
103 
104         Ok(())
105     }
106 
set_mem_table(&mut self, _ctx: &[VhostUserMemoryRegion], _files: Vec<File>) -> Result<()>107     fn set_mem_table(&mut self, _ctx: &[VhostUserMemoryRegion], _files: Vec<File>) -> Result<()> {
108         Ok(())
109     }
110 
set_vring_num(&mut self, index: u32, num: u32) -> Result<()>111     fn set_vring_num(&mut self, index: u32, num: u32) -> Result<()> {
112         if index as usize >= self.queue_num || num == 0 || num as usize > MAX_VRING_NUM {
113             return Err(Error::InvalidParam);
114         }
115         self.vring_num[index as usize] = num;
116         Ok(())
117     }
118 
set_vring_addr( &mut self, index: u32, _flags: VhostUserVringAddrFlags, _descriptor: u64, _used: u64, _available: u64, _log: u64, ) -> Result<()>119     fn set_vring_addr(
120         &mut self,
121         index: u32,
122         _flags: VhostUserVringAddrFlags,
123         _descriptor: u64,
124         _used: u64,
125         _available: u64,
126         _log: u64,
127     ) -> Result<()> {
128         if index as usize >= self.queue_num {
129             return Err(Error::InvalidParam);
130         }
131         Ok(())
132     }
133 
set_vring_base(&mut self, index: u32, base: u32) -> Result<()>134     fn set_vring_base(&mut self, index: u32, base: u32) -> Result<()> {
135         if index as usize >= self.queue_num || base as usize >= MAX_VRING_NUM {
136             return Err(Error::InvalidParam);
137         }
138         self.vring_base[index as usize] = base;
139         Ok(())
140     }
141 
get_vring_base(&mut self, index: u32) -> Result<VhostUserVringState>142     fn get_vring_base(&mut self, index: u32) -> Result<VhostUserVringState> {
143         if index as usize >= self.queue_num {
144             return Err(Error::InvalidParam);
145         }
146         // Quotation from vhost-user spec:
147         // Client must start ring upon receiving a kick (that is, detecting
148         // that file descriptor is readable) on the descriptor specified by
149         // VHOST_USER_SET_VRING_KICK, and stop ring upon receiving
150         // VHOST_USER_GET_VRING_BASE.
151         self.vring_started[index as usize] = false;
152         Ok(VhostUserVringState::new(
153             index,
154             self.vring_base[index as usize],
155         ))
156     }
157 
set_vring_kick(&mut self, index: u8, fd: Option<File>) -> Result<()>158     fn set_vring_kick(&mut self, index: u8, fd: Option<File>) -> Result<()> {
159         if index as usize >= self.queue_num || index as usize > self.queue_num {
160             return Err(Error::InvalidParam);
161         }
162         self.kick_fd[index as usize] = fd;
163 
164         // Quotation from vhost-user spec:
165         // Client must start ring upon receiving a kick (that is, detecting
166         // that file descriptor is readable) on the descriptor specified by
167         // VHOST_USER_SET_VRING_KICK, and stop ring upon receiving
168         // VHOST_USER_GET_VRING_BASE.
169         //
170         // So we should add fd to event monitor(select, poll, epoll) here.
171         self.vring_started[index as usize] = true;
172         Ok(())
173     }
174 
set_vring_call(&mut self, index: u8, fd: Option<File>) -> Result<()>175     fn set_vring_call(&mut self, index: u8, fd: Option<File>) -> Result<()> {
176         if index as usize >= self.queue_num || index as usize > self.queue_num {
177             return Err(Error::InvalidParam);
178         }
179         self.call_fd[index as usize] = fd;
180         Ok(())
181     }
182 
set_vring_err(&mut self, index: u8, fd: Option<File>) -> Result<()>183     fn set_vring_err(&mut self, index: u8, fd: Option<File>) -> Result<()> {
184         if index as usize >= self.queue_num || index as usize > self.queue_num {
185             return Err(Error::InvalidParam);
186         }
187         self.err_fd[index as usize] = fd;
188         Ok(())
189     }
190 
get_protocol_features(&mut self) -> Result<VhostUserProtocolFeatures>191     fn get_protocol_features(&mut self) -> Result<VhostUserProtocolFeatures> {
192         Ok(VhostUserProtocolFeatures::all())
193     }
194 
set_protocol_features(&mut self, features: u64) -> Result<()>195     fn set_protocol_features(&mut self, features: u64) -> Result<()> {
196         // Note: slave that reported VHOST_USER_F_PROTOCOL_FEATURES must
197         // support this message even before VHOST_USER_SET_FEATURES was
198         // called.
199         // What happens if the master calls set_features() with
200         // VHOST_USER_F_PROTOCOL_FEATURES cleared after calling this
201         // interface?
202         self.acked_protocol_features = features;
203         Ok(())
204     }
205 
get_queue_num(&mut self) -> Result<u64>206     fn get_queue_num(&mut self) -> Result<u64> {
207         Ok(MAX_QUEUE_NUM as u64)
208     }
209 
set_vring_enable(&mut self, index: u32, enable: bool) -> Result<()>210     fn set_vring_enable(&mut self, index: u32, enable: bool) -> Result<()> {
211         // This request should be handled only when VHOST_USER_F_PROTOCOL_FEATURES
212         // has been negotiated.
213         self.check_feature(VhostUserVirtioFeatures::PROTOCOL_FEATURES)?;
214 
215         if index as usize >= self.queue_num || index as usize > self.queue_num {
216             return Err(Error::InvalidParam);
217         }
218 
219         // Slave must not pass data to/from the backend until ring is
220         // enabled by VHOST_USER_SET_VRING_ENABLE with parameter 1,
221         // or after it has been disabled by VHOST_USER_SET_VRING_ENABLE
222         // with parameter 0.
223         self.vring_enabled[index as usize] = enable;
224         Ok(())
225     }
226 
get_config( &mut self, offset: u32, size: u32, _flags: VhostUserConfigFlags, ) -> Result<Vec<u8>>227     fn get_config(
228         &mut self,
229         offset: u32,
230         size: u32,
231         _flags: VhostUserConfigFlags,
232     ) -> Result<Vec<u8>> {
233         self.check_proto_feature(VhostUserProtocolFeatures::CONFIG)?;
234 
235         if !(VHOST_USER_CONFIG_OFFSET..VHOST_USER_CONFIG_SIZE).contains(&offset)
236             || size > VHOST_USER_CONFIG_SIZE - VHOST_USER_CONFIG_OFFSET
237             || size + offset > VHOST_USER_CONFIG_SIZE
238         {
239             return Err(Error::InvalidParam);
240         }
241         assert_eq!(offset, 0x100);
242         assert_eq!(size, 4);
243         Ok(vec![0xa5; size as usize])
244     }
245 
set_config(&mut self, offset: u32, buf: &[u8], _flags: VhostUserConfigFlags) -> Result<()>246     fn set_config(&mut self, offset: u32, buf: &[u8], _flags: VhostUserConfigFlags) -> Result<()> {
247         let size = buf.len() as u32;
248         self.check_proto_feature(VhostUserProtocolFeatures::CONFIG)?;
249 
250         if !(VHOST_USER_CONFIG_OFFSET..VHOST_USER_CONFIG_SIZE).contains(&offset)
251             || size > VHOST_USER_CONFIG_SIZE - VHOST_USER_CONFIG_OFFSET
252             || size + offset > VHOST_USER_CONFIG_SIZE
253         {
254             return Err(Error::InvalidParam);
255         }
256         assert_eq!(offset, 0x100);
257         assert_eq!(buf.len(), 4);
258         assert_eq!(buf, &[0xa5; 4]);
259         Ok(())
260     }
261 
get_inflight_fd( &mut self, inflight: &VhostUserInflight, ) -> Result<(VhostUserInflight, File)>262     fn get_inflight_fd(
263         &mut self,
264         inflight: &VhostUserInflight,
265     ) -> Result<(VhostUserInflight, File)> {
266         let file = tempfile::tempfile().unwrap();
267         self.inflight_file = Some(file.try_clone().unwrap());
268         Ok((
269             VhostUserInflight {
270                 mmap_size: 0x1000,
271                 mmap_offset: 0,
272                 num_queues: inflight.num_queues,
273                 queue_size: inflight.queue_size,
274             },
275             file,
276         ))
277     }
278 
set_inflight_fd(&mut self, _inflight: &VhostUserInflight, _file: File) -> Result<()>279     fn set_inflight_fd(&mut self, _inflight: &VhostUserInflight, _file: File) -> Result<()> {
280         Ok(())
281     }
282 
get_max_mem_slots(&mut self) -> Result<u64>283     fn get_max_mem_slots(&mut self) -> Result<u64> {
284         Ok(MAX_MEM_SLOTS as u64)
285     }
286 
add_mem_region(&mut self, _region: &VhostUserSingleMemoryRegion, _fd: File) -> Result<()>287     fn add_mem_region(&mut self, _region: &VhostUserSingleMemoryRegion, _fd: File) -> Result<()> {
288         Ok(())
289     }
290 
remove_mem_region(&mut self, _region: &VhostUserSingleMemoryRegion) -> Result<()>291     fn remove_mem_region(&mut self, _region: &VhostUserSingleMemoryRegion) -> Result<()> {
292         Ok(())
293     }
294 }
295