xref: /cloud-hypervisor/virtio-devices/src/vhost_user/blk.rs (revision f67b3f79ea19c9a66e04074cbbf5d292f6529e43)
1 // Copyright 2019 Intel Corporation. All Rights Reserved.
2 // SPDX-License-Identifier: Apache-2.0
3 
4 use super::super::{ActivateResult, Queue, VirtioCommon, VirtioDevice, VirtioDeviceType};
5 use super::vu_common_ctrl::{VhostUserConfig, VhostUserHandle};
6 use super::{Error, Result, DEFAULT_VIRTIO_FEATURES};
7 use crate::seccomp_filters::Thread;
8 use crate::thread_helper::spawn_virtio_thread;
9 use crate::vhost_user::VhostUserCommon;
10 use crate::VirtioInterrupt;
11 use crate::{GuestMemoryMmap, GuestRegionMmap};
12 use block_util::VirtioBlockConfig;
13 use seccompiler::SeccompAction;
14 use std::mem;
15 use std::result;
16 use std::sync::{Arc, Barrier, Mutex};
17 use std::thread;
18 use std::vec::Vec;
19 use versionize::{VersionMap, Versionize, VersionizeResult};
20 use versionize_derive::Versionize;
21 use vhost::vhost_user::message::{
22     VhostUserConfigFlags, VhostUserProtocolFeatures, VhostUserVirtioFeatures,
23     VHOST_USER_CONFIG_OFFSET,
24 };
25 use vhost::vhost_user::{MasterReqHandler, VhostUserMaster, VhostUserMasterReqHandler};
26 use virtio_bindings::bindings::virtio_blk::{
27     VIRTIO_BLK_F_BLK_SIZE, VIRTIO_BLK_F_CONFIG_WCE, VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH,
28     VIRTIO_BLK_F_GEOMETRY, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX,
29     VIRTIO_BLK_F_SIZE_MAX, VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_WRITE_ZEROES,
30 };
31 use vm_memory::{ByteValued, GuestMemoryAtomic};
32 use vm_migration::{
33     protocol::MemoryRangeTable, Migratable, MigratableError, Pausable, Snapshot, Snapshottable,
34     Transportable, VersionMapped,
35 };
36 use vmm_sys_util::eventfd::EventFd;
37 
38 const DEFAULT_QUEUE_NUMBER: usize = 1;
39 
40 #[derive(Versionize)]
41 pub struct State {
42     pub avail_features: u64,
43     pub acked_features: u64,
44     pub config: VirtioBlockConfig,
45     pub acked_protocol_features: u64,
46     pub vu_num_queues: usize,
47 }
48 
49 impl VersionMapped for State {}
50 
51 struct SlaveReqHandler {}
52 impl VhostUserMasterReqHandler for SlaveReqHandler {}
53 
54 pub struct Blk {
55     common: VirtioCommon,
56     vu_common: VhostUserCommon,
57     id: String,
58     config: VirtioBlockConfig,
59     guest_memory: Option<GuestMemoryAtomic<GuestMemoryMmap>>,
60     epoll_thread: Option<thread::JoinHandle<()>>,
61     seccomp_action: SeccompAction,
62     exit_evt: EventFd,
63 }
64 
65 impl Blk {
66     /// Create a new vhost-user-blk device
67     pub fn new(
68         id: String,
69         vu_cfg: VhostUserConfig,
70         restoring: bool,
71         seccomp_action: SeccompAction,
72         exit_evt: EventFd,
73     ) -> Result<Blk> {
74         let num_queues = vu_cfg.num_queues;
75 
76         if restoring {
77             // We need 'queue_sizes' to report a number of queues that will be
78             // enough to handle all the potential queues. VirtioPciDevice::new()
79             // will create the actual queues based on this information.
80             return Ok(Blk {
81                 common: VirtioCommon {
82                     device_type: VirtioDeviceType::Block as u32,
83                     queue_sizes: vec![vu_cfg.queue_size; num_queues],
84                     paused_sync: Some(Arc::new(Barrier::new(2))),
85                     min_queues: DEFAULT_QUEUE_NUMBER as u16,
86                     ..Default::default()
87                 },
88                 vu_common: VhostUserCommon {
89                     socket_path: vu_cfg.socket,
90                     vu_num_queues: num_queues,
91                     ..Default::default()
92                 },
93                 id,
94                 config: VirtioBlockConfig::default(),
95                 guest_memory: None,
96                 epoll_thread: None,
97                 seccomp_action,
98                 exit_evt,
99             });
100         }
101 
102         let mut vu =
103             VhostUserHandle::connect_vhost_user(false, &vu_cfg.socket, num_queues as u64, false)?;
104 
105         // Filling device and vring features VMM supports.
106         let mut avail_features = 1 << VIRTIO_BLK_F_SIZE_MAX
107             | 1 << VIRTIO_BLK_F_SEG_MAX
108             | 1 << VIRTIO_BLK_F_GEOMETRY
109             | 1 << VIRTIO_BLK_F_RO
110             | 1 << VIRTIO_BLK_F_BLK_SIZE
111             | 1 << VIRTIO_BLK_F_FLUSH
112             | 1 << VIRTIO_BLK_F_TOPOLOGY
113             | 1 << VIRTIO_BLK_F_CONFIG_WCE
114             | 1 << VIRTIO_BLK_F_DISCARD
115             | 1 << VIRTIO_BLK_F_WRITE_ZEROES
116             | DEFAULT_VIRTIO_FEATURES;
117 
118         if num_queues > 1 {
119             avail_features |= 1 << VIRTIO_BLK_F_MQ;
120         }
121 
122         let avail_protocol_features = VhostUserProtocolFeatures::CONFIG
123             | VhostUserProtocolFeatures::MQ
124             | VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS
125             | VhostUserProtocolFeatures::REPLY_ACK
126             | VhostUserProtocolFeatures::INFLIGHT_SHMFD
127             | VhostUserProtocolFeatures::LOG_SHMFD;
128 
129         let (acked_features, acked_protocol_features) =
130             vu.negotiate_features_vhost_user(avail_features, avail_protocol_features)?;
131 
132         let backend_num_queues =
133             if acked_protocol_features & VhostUserProtocolFeatures::MQ.bits() != 0 {
134                 vu.socket_handle()
135                     .get_queue_num()
136                     .map_err(Error::VhostUserGetQueueMaxNum)? as usize
137             } else {
138                 DEFAULT_QUEUE_NUMBER
139             };
140 
141         if num_queues > backend_num_queues {
142             error!("vhost-user-blk requested too many queues ({}) since the backend only supports {}\n",
143                 num_queues, backend_num_queues);
144             return Err(Error::BadQueueNum);
145         }
146 
147         let config_len = mem::size_of::<VirtioBlockConfig>();
148         let config_space: Vec<u8> = vec![0u8; config_len as usize];
149         let (_, config_space) = vu
150             .socket_handle()
151             .get_config(
152                 VHOST_USER_CONFIG_OFFSET,
153                 config_len as u32,
154                 VhostUserConfigFlags::WRITABLE,
155                 config_space.as_slice(),
156             )
157             .map_err(Error::VhostUserGetConfig)?;
158         let mut config = VirtioBlockConfig::default();
159         if let Some(backend_config) = VirtioBlockConfig::from_slice(config_space.as_slice()) {
160             config = *backend_config;
161             config.num_queues = num_queues as u16;
162         }
163 
164         Ok(Blk {
165             common: VirtioCommon {
166                 device_type: VirtioDeviceType::Block as u32,
167                 queue_sizes: vec![vu_cfg.queue_size; num_queues],
168                 avail_features: acked_features,
169                 acked_features: 0,
170                 paused_sync: Some(Arc::new(Barrier::new(2))),
171                 min_queues: DEFAULT_QUEUE_NUMBER as u16,
172                 ..Default::default()
173             },
174             vu_common: VhostUserCommon {
175                 vu: Some(Arc::new(Mutex::new(vu))),
176                 acked_protocol_features,
177                 socket_path: vu_cfg.socket,
178                 vu_num_queues: num_queues,
179                 ..Default::default()
180             },
181             id,
182             config,
183             guest_memory: None,
184             epoll_thread: None,
185             seccomp_action,
186             exit_evt,
187         })
188     }
189 
190     fn state(&self) -> State {
191         State {
192             avail_features: self.common.avail_features,
193             acked_features: self.common.acked_features,
194             config: self.config,
195             acked_protocol_features: self.vu_common.acked_protocol_features,
196             vu_num_queues: self.vu_common.vu_num_queues,
197         }
198     }
199 
200     fn set_state(&mut self, state: &State) {
201         self.common.avail_features = state.avail_features;
202         self.common.acked_features = state.acked_features;
203         self.config = state.config;
204         self.vu_common.acked_protocol_features = state.acked_protocol_features;
205         self.vu_common.vu_num_queues = state.vu_num_queues;
206 
207         if let Err(e) = self
208             .vu_common
209             .restore_backend_connection(self.common.acked_features)
210         {
211             error!(
212                 "Failed restoring connection with vhost-user backend: {:?}",
213                 e
214             );
215         }
216     }
217 }
218 
219 impl Drop for Blk {
220     fn drop(&mut self) {
221         if let Some(kill_evt) = self.common.kill_evt.take() {
222             if let Err(e) = kill_evt.write(1) {
223                 error!("failed to kill vhost-user-blk: {:?}", e);
224             }
225         }
226     }
227 }
228 
229 impl VirtioDevice for Blk {
230     fn device_type(&self) -> u32 {
231         self.common.device_type
232     }
233 
234     fn queue_max_sizes(&self) -> &[u16] {
235         &self.common.queue_sizes
236     }
237 
238     fn features(&self) -> u64 {
239         self.common.avail_features
240     }
241 
242     fn ack_features(&mut self, value: u64) {
243         self.common.ack_features(value)
244     }
245 
246     fn read_config(&self, offset: u64, data: &mut [u8]) {
247         self.read_config_from_slice(self.config.as_slice(), offset, data);
248     }
249 
250     fn write_config(&mut self, offset: u64, data: &[u8]) {
251         // The "writeback" field is the only mutable field
252         let writeback_offset =
253             (&self.config.writeback as *const _ as u64) - (&self.config as *const _ as u64);
254         if offset != writeback_offset || data.len() != std::mem::size_of_val(&self.config.writeback)
255         {
256             error!(
257                 "Attempt to write to read-only field: offset {:x} length {}",
258                 offset,
259                 data.len()
260             );
261             return;
262         }
263 
264         self.config.writeback = data[0];
265         if let Some(vu) = &self.vu_common.vu {
266             if let Err(e) = vu
267                 .lock()
268                 .unwrap()
269                 .socket_handle()
270                 .set_config(offset as u32, VhostUserConfigFlags::WRITABLE, data)
271                 .map_err(Error::VhostUserSetConfig)
272             {
273                 error!("Failed setting vhost-user-blk configuration: {:?}", e);
274             }
275         }
276     }
277 
278     fn activate(
279         &mut self,
280         mem: GuestMemoryAtomic<GuestMemoryMmap>,
281         interrupt_cb: Arc<dyn VirtioInterrupt>,
282         queues: Vec<Queue>,
283         queue_evts: Vec<EventFd>,
284     ) -> ActivateResult {
285         self.common.activate(&queues, &queue_evts, &interrupt_cb)?;
286         self.guest_memory = Some(mem.clone());
287 
288         let slave_req_handler: Option<MasterReqHandler<SlaveReqHandler>> = None;
289 
290         // The backend acknowledged features must contain the protocol feature
291         // bit in case it was initially set but lost through the features
292         // negotiation with the guest.
293         let backend_acked_features = self.common.acked_features
294             | (self.common.avail_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits());
295 
296         // Run a dedicated thread for handling potential reconnections with
297         // the backend.
298         let (kill_evt, pause_evt) = self.common.dup_eventfds();
299 
300         let mut handler = self.vu_common.activate(
301             mem,
302             queues,
303             queue_evts,
304             interrupt_cb,
305             backend_acked_features,
306             slave_req_handler,
307             kill_evt,
308             pause_evt,
309         )?;
310 
311         let paused = self.common.paused.clone();
312         let paused_sync = self.common.paused_sync.clone();
313 
314         let mut epoll_threads = Vec::new();
315 
316         spawn_virtio_thread(
317             &self.id,
318             &self.seccomp_action,
319             Thread::VirtioVhostBlock,
320             &mut epoll_threads,
321             &self.exit_evt,
322             move || {
323                 if let Err(e) = handler.run(paused, paused_sync.unwrap()) {
324                     error!("Error running worker: {:?}", e);
325                 }
326             },
327         )?;
328         self.epoll_thread = Some(epoll_threads.remove(0));
329 
330         Ok(())
331     }
332 
333     fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
334         // We first must resume the virtio thread if it was paused.
335         if self.common.pause_evt.take().is_some() {
336             self.common.resume().ok()?;
337         }
338 
339         if let Some(vu) = &self.vu_common.vu {
340             if let Err(e) = vu
341                 .lock()
342                 .unwrap()
343                 .reset_vhost_user(self.common.queue_sizes.len())
344             {
345                 error!("Failed to reset vhost-user daemon: {:?}", e);
346                 return None;
347             }
348         }
349 
350         if let Some(kill_evt) = self.common.kill_evt.take() {
351             // Ignore the result because there is nothing we can do about it.
352             let _ = kill_evt.write(1);
353         }
354 
355         event!("virtio-device", "reset", "id", &self.id);
356 
357         // Return the interrupt
358         Some(self.common.interrupt_cb.take().unwrap())
359     }
360 
361     fn shutdown(&mut self) {
362         self.vu_common.shutdown()
363     }
364 
365     fn add_memory_region(
366         &mut self,
367         region: &Arc<GuestRegionMmap>,
368     ) -> std::result::Result<(), crate::Error> {
369         self.vu_common.add_memory_region(&self.guest_memory, region)
370     }
371 }
372 
373 impl Pausable for Blk {
374     fn pause(&mut self) -> result::Result<(), MigratableError> {
375         self.vu_common.pause()?;
376         self.common.pause()
377     }
378 
379     fn resume(&mut self) -> result::Result<(), MigratableError> {
380         self.common.resume()?;
381 
382         if let Some(epoll_thread) = &self.epoll_thread {
383             epoll_thread.thread().unpark();
384         }
385 
386         self.vu_common.resume()
387     }
388 }
389 
390 impl Snapshottable for Blk {
391     fn id(&self) -> String {
392         self.id.clone()
393     }
394 
395     fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
396         self.vu_common.snapshot(&self.id(), &self.state())
397     }
398 
399     fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
400         self.set_state(&snapshot.to_versioned_state(&self.id)?);
401         Ok(())
402     }
403 }
404 impl Transportable for Blk {}
405 
406 impl Migratable for Blk {
407     fn start_dirty_log(&mut self) -> std::result::Result<(), MigratableError> {
408         self.vu_common.start_dirty_log(&self.guest_memory)
409     }
410 
411     fn stop_dirty_log(&mut self) -> std::result::Result<(), MigratableError> {
412         self.vu_common.stop_dirty_log()
413     }
414 
415     fn dirty_log(&mut self) -> std::result::Result<MemoryRangeTable, MigratableError> {
416         self.vu_common.dirty_log(&self.guest_memory)
417     }
418 
419     fn complete_migration(&mut self) -> std::result::Result<(), MigratableError> {
420         self.vu_common
421             .complete_migration(self.common.kill_evt.take())
422     }
423 }
424