1use std::ops::Deref;
8use std::sync::Arc;
9
10use log::error;
11use utils::time::{ClockType, get_time_us};
12use vhost::vhost_user::Frontend;
13use vhost::vhost_user::message::*;
14use vmm_sys_util::eventfd::EventFd;
15
16use super::{NUM_QUEUES, QUEUE_SIZE, VhostUserBlockError};
17use crate::devices::virtio::ActivateError;
18use crate::devices::virtio::block::CacheType;
19use crate::devices::virtio::device::{ActiveState, DeviceState, VirtioDevice};
20use crate::devices::virtio::generated::virtio_blk::{VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_RO};
21use crate::devices::virtio::generated::virtio_config::VIRTIO_F_VERSION_1;
22use crate::devices::virtio::generated::virtio_ids::VIRTIO_ID_BLOCK;
23use crate::devices::virtio::generated::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
24use crate::devices::virtio::queue::Queue;
25use crate::devices::virtio::transport::{VirtioInterrupt, VirtioInterruptType};
26use crate::devices::virtio::vhost_user::{VhostUserHandleBackend, VhostUserHandleImpl};
27use crate::devices::virtio::vhost_user_metrics::{
28 VhostUserDeviceMetrics, VhostUserMetricsPerDevice,
29};
30use crate::impl_device_type;
31use crate::logger::{IncMetric, StoreMetric, log_dev_preview_warning};
32use crate::utils::u64_to_usize;
33use crate::vmm_config::drive::BlockDeviceConfig;
34use crate::vstate::memory::GuestMemoryMmap;
35
36const BLOCK_CONFIG_SPACE_SIZE: u32 = 60;
38
39const AVAILABLE_FEATURES: u64 = (1 << VIRTIO_F_VERSION_1)
40 | (1 << VIRTIO_RING_F_EVENT_IDX)
41 | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits()
44 | (1 << VIRTIO_BLK_F_RO);
47
48#[derive(Debug, PartialEq, Eq)]
50pub struct VhostUserBlockConfig {
51 pub drive_id: String,
53 pub partuuid: Option<String>,
56 pub is_root_device: bool,
60 pub cache_type: CacheType,
63
64 pub socket: String,
66}
67
68impl TryFrom<&BlockDeviceConfig> for VhostUserBlockConfig {
69 type Error = VhostUserBlockError;
70
71 fn try_from(value: &BlockDeviceConfig) -> Result<Self, Self::Error> {
72 if value.socket.is_some()
73 && value.is_read_only.is_none()
74 && value.path_on_host.is_none()
75 && value.rate_limiter.is_none()
76 && value.file_engine_type.is_none()
77 {
78 Ok(Self {
79 drive_id: value.drive_id.clone(),
80 partuuid: value.partuuid.clone(),
81 is_root_device: value.is_root_device,
82 cache_type: value.cache_type,
83
84 socket: value.socket.as_ref().unwrap().clone(),
85 })
86 } else {
87 Err(VhostUserBlockError::Config)
88 }
89 }
90}
91
92impl From<VhostUserBlockConfig> for BlockDeviceConfig {
93 fn from(value: VhostUserBlockConfig) -> Self {
94 Self {
95 drive_id: value.drive_id,
96 partuuid: value.partuuid,
97 is_root_device: value.is_root_device,
98 cache_type: value.cache_type,
99
100 is_read_only: None,
101 path_on_host: None,
102 rate_limiter: None,
103 file_engine_type: None,
104
105 socket: Some(value.socket),
106 }
107 }
108}
109
110pub type VhostUserBlock = VhostUserBlockImpl<Frontend>;
111
112pub struct VhostUserBlockImpl<T: VhostUserHandleBackend> {
114 pub avail_features: u64,
116 pub acked_features: u64,
117 pub config_space: Vec<u8>,
118 pub activate_evt: EventFd,
119
120 pub queues: Vec<Queue>,
122 pub queue_evts: [EventFd; u64_to_usize(NUM_QUEUES)],
123 pub device_state: DeviceState,
124
125 pub id: String,
127 pub partuuid: Option<String>,
128 pub cache_type: CacheType,
129 pub root_device: bool,
130 pub read_only: bool,
131
132 pub vu_handle: VhostUserHandleImpl<T>,
134 pub vu_acked_protocol_features: u64,
135 pub metrics: Arc<VhostUserDeviceMetrics>,
136}
137
138impl<T: VhostUserHandleBackend> std::fmt::Debug for VhostUserBlockImpl<T> {
140 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141 f.debug_struct("VhostUserBlockImpl")
142 .field("avail_features", &self.avail_features)
143 .field("acked_features", &self.acked_features)
144 .field("config_space", &self.config_space)
145 .field("activate_evt", &self.activate_evt)
146 .field("queues", &self.queues)
147 .field("queue_evts", &self.queue_evts)
148 .field("device_state", &self.device_state)
149 .field("id", &self.id)
150 .field("partuuid", &self.partuuid)
151 .field("cache_type", &self.cache_type)
152 .field("root_device", &self.root_device)
153 .field("read_only", &self.read_only)
154 .field("vu_handle", &self.vu_handle)
155 .field(
156 "vu_acked_protocol_features",
157 &self.vu_acked_protocol_features,
158 )
159 .field("metrics", &self.metrics)
160 .finish()
161 }
162}
163
164impl<T: VhostUserHandleBackend> VhostUserBlockImpl<T> {
165 pub fn new(config: VhostUserBlockConfig) -> Result<Self, VhostUserBlockError> {
166 log_dev_preview_warning("vhost-user-blk device", Option::None);
167 let start_time = get_time_us(ClockType::Monotonic);
168 let mut requested_features = AVAILABLE_FEATURES;
169
170 if config.cache_type == CacheType::Writeback {
171 requested_features |= 1 << VIRTIO_BLK_F_FLUSH;
172 }
173
174 let requested_protocol_features = VhostUserProtocolFeatures::CONFIG;
175
176 let mut vu_handle = VhostUserHandleImpl::<T>::new(&config.socket, NUM_QUEUES)
177 .map_err(VhostUserBlockError::VhostUser)?;
178 let (acked_features, acked_protocol_features) = vu_handle
179 .negotiate_features(requested_features, requested_protocol_features)
180 .map_err(VhostUserBlockError::VhostUser)?;
181
182 let config_space =
184 if acked_protocol_features & VhostUserProtocolFeatures::CONFIG.bits() != 0 {
185 let buffer = [0u8; BLOCK_CONFIG_SPACE_SIZE as usize];
187 let (_, new_config_space) = vu_handle
188 .vu
189 .get_config(
190 0,
191 BLOCK_CONFIG_SPACE_SIZE,
192 VhostUserConfigFlags::WRITABLE,
193 &buffer,
194 )
195 .map_err(VhostUserBlockError::Vhost)?;
196 new_config_space
197 } else {
198 vec![]
199 };
200
201 let activate_evt =
202 EventFd::new(libc::EFD_NONBLOCK).map_err(VhostUserBlockError::EventFd)?;
203
204 let queues = vec![Queue::new(QUEUE_SIZE)];
205 let queue_evts = [EventFd::new(libc::EFD_NONBLOCK).map_err(VhostUserBlockError::EventFd)?;
206 u64_to_usize(NUM_QUEUES)];
207 let device_state = DeviceState::Inactive;
208
209 let avail_features = acked_features;
212 let acked_features = acked_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits();
213 let read_only = acked_features & (1 << VIRTIO_BLK_F_RO) != 0;
214 let vhost_user_block_metrics_name = format!("block_{}", config.drive_id);
215
216 let metrics = VhostUserMetricsPerDevice::alloc(vhost_user_block_metrics_name);
217 let delta_us = get_time_us(ClockType::Monotonic) - start_time;
218 metrics.init_time_us.store(delta_us);
219
220 Ok(Self {
221 avail_features,
222 acked_features,
223 config_space,
224 activate_evt,
225
226 queues,
227 queue_evts,
228 device_state,
229
230 id: config.drive_id,
231 partuuid: config.partuuid,
232 cache_type: config.cache_type,
233 read_only,
234 root_device: config.is_root_device,
235
236 vu_handle,
237 vu_acked_protocol_features: acked_protocol_features,
238 metrics,
239 })
240 }
241
242 pub fn prepare_save(&mut self) {
244 unimplemented!("VhostUserBlock does not support snapshotting yet");
245 }
246
247 pub fn config(&self) -> VhostUserBlockConfig {
248 VhostUserBlockConfig {
249 drive_id: self.id.clone(),
250 partuuid: self.partuuid.clone(),
251 is_root_device: self.root_device,
252 cache_type: self.cache_type,
253 socket: self.vu_handle.socket_path.clone(),
254 }
255 }
256
257 pub fn config_update(&mut self) -> Result<(), VhostUserBlockError> {
258 let start_time = get_time_us(ClockType::Monotonic);
259 let interrupt = self
260 .device_state
261 .active_state()
262 .expect("Device is not initialized")
263 .interrupt
264 .clone();
265
266 let buffer = [0u8; BLOCK_CONFIG_SPACE_SIZE as usize];
268 let (_, new_config_space) = self
269 .vu_handle
270 .vu
271 .get_config(
272 0,
273 BLOCK_CONFIG_SPACE_SIZE,
274 VhostUserConfigFlags::WRITABLE,
275 &buffer,
276 )
277 .map_err(VhostUserBlockError::Vhost)?;
278 self.config_space = new_config_space;
279 interrupt
280 .trigger(VirtioInterruptType::Config)
281 .map_err(VhostUserBlockError::Interrupt)?;
282
283 let delta_us = get_time_us(ClockType::Monotonic) - start_time;
284 self.metrics.config_change_time_us.store(delta_us);
285
286 Ok(())
287 }
288}
289
290impl<T: VhostUserHandleBackend + Send + 'static> VirtioDevice for VhostUserBlockImpl<T> {
291 impl_device_type!(VIRTIO_ID_BLOCK);
292
293 fn avail_features(&self) -> u64 {
294 self.avail_features
295 }
296
297 fn acked_features(&self) -> u64 {
298 self.acked_features
299 }
300
301 fn set_acked_features(&mut self, acked_features: u64) {
302 self.acked_features = acked_features;
303 }
304
305 fn queues(&self) -> &[Queue] {
306 &self.queues
307 }
308
309 fn queues_mut(&mut self) -> &mut [Queue] {
310 &mut self.queues
311 }
312
313 fn queue_events(&self) -> &[EventFd] {
314 &self.queue_evts
315 }
316
317 fn interrupt_trigger(&self) -> &dyn VirtioInterrupt {
318 self.device_state
319 .active_state()
320 .expect("Device is not initialized")
321 .interrupt
322 .deref()
323 }
324
325 fn read_config(&self, offset: u64, data: &mut [u8]) {
326 if let Some(config_space_bytes) = self.config_space.as_slice().get(u64_to_usize(offset)..) {
327 let len = config_space_bytes.len().min(data.len());
328 data[..len].copy_from_slice(&config_space_bytes[..len]);
329 } else {
330 error!("Failed to read config space");
331 self.metrics.cfg_fails.inc();
332 }
333 }
334
335 fn write_config(&mut self, _offset: u64, _data: &[u8]) {
336 }
340
341 fn activate(
342 &mut self,
343 mem: GuestMemoryMmap,
344 interrupt: Arc<dyn VirtioInterrupt>,
345 ) -> Result<(), ActivateError> {
346 for q in self.queues.iter_mut() {
347 q.initialize(&mem)
348 .map_err(ActivateError::QueueMemoryError)?;
349 }
350
351 let start_time = get_time_us(ClockType::Monotonic);
352 self.vu_handle
355 .set_features(self.acked_features)
356 .and_then(|()| {
357 self.vu_handle.setup_backend(
358 &mem,
359 &[(0, &self.queues[0], &self.queue_evts[0])],
360 interrupt.clone(),
361 )
362 })
363 .map_err(|err| {
364 self.metrics.activate_fails.inc();
365 ActivateError::VhostUser(err)
366 })?;
367 self.device_state = DeviceState::Activated(ActiveState { mem, interrupt });
368 let delta_us = get_time_us(ClockType::Monotonic) - start_time;
369 self.metrics.activate_time_us.store(delta_us);
370 Ok(())
371 }
372
373 fn is_activated(&self) -> bool {
374 self.device_state.is_activated()
375 }
376}
377
378#[cfg(test)]
379mod tests {
380 #![allow(clippy::undocumented_unsafe_blocks)]
381
382 use std::os::unix::net::UnixStream;
383 use std::sync::atomic::Ordering;
384
385 use vhost::{VhostUserMemoryRegionInfo, VringConfigData};
386 use vmm_sys_util::tempfile::TempFile;
387
388 use super::*;
389 use crate::devices::virtio::block::virtio::device::FileEngineType;
390 use crate::devices::virtio::test_utils::{VirtQueue, default_interrupt, default_mem};
391 use crate::devices::virtio::transport::mmio::VIRTIO_MMIO_INT_CONFIG;
392 use crate::devices::virtio::vhost_user::tests::create_mem;
393 use crate::test_utils::create_tmp_socket;
394 use crate::vstate::memory::GuestAddress;
395
396 #[test]
397 fn test_from_config() {
398 let block_config = BlockDeviceConfig {
399 drive_id: "".to_string(),
400 partuuid: None,
401 is_root_device: false,
402 cache_type: CacheType::Unsafe,
403
404 is_read_only: None,
405 path_on_host: None,
406 rate_limiter: None,
407 file_engine_type: None,
408
409 socket: Some("sock".to_string()),
410 };
411 VhostUserBlockConfig::try_from(&block_config).unwrap();
412
413 let block_config = BlockDeviceConfig {
414 drive_id: "".to_string(),
415 partuuid: None,
416 is_root_device: false,
417 cache_type: CacheType::Unsafe,
418
419 is_read_only: Some(true),
420 path_on_host: Some("path".to_string()),
421 rate_limiter: None,
422 file_engine_type: Some(FileEngineType::Sync),
423
424 socket: None,
425 };
426 VhostUserBlockConfig::try_from(&block_config).unwrap_err();
427
428 let block_config = BlockDeviceConfig {
429 drive_id: "".to_string(),
430 partuuid: None,
431 is_root_device: false,
432 cache_type: CacheType::Unsafe,
433
434 is_read_only: Some(true),
435 path_on_host: Some("path".to_string()),
436 rate_limiter: None,
437 file_engine_type: Some(FileEngineType::Sync),
438
439 socket: Some("sock".to_string()),
440 };
441 VhostUserBlockConfig::try_from(&block_config).unwrap_err();
442 }
443
444 #[test]
445 fn test_new_no_features() {
446 struct MockMaster {
447 sock: UnixStream,
448 max_queue_num: u64,
449 is_owner: std::cell::UnsafeCell<bool>,
450 features: u64,
451 protocol_features: VhostUserProtocolFeatures,
452 hdr_flags: std::cell::UnsafeCell<VhostUserHeaderFlag>,
453 }
454
455 impl VhostUserHandleBackend for MockMaster {
456 fn from_stream(sock: UnixStream, max_queue_num: u64) -> Self {
457 Self {
458 sock,
459 max_queue_num,
460 is_owner: std::cell::UnsafeCell::new(false),
461 features: 0,
462 protocol_features: VhostUserProtocolFeatures::empty(),
463 hdr_flags: std::cell::UnsafeCell::new(VhostUserHeaderFlag::empty()),
464 }
465 }
466
467 fn set_owner(&self) -> Result<(), vhost::Error> {
468 unsafe { *self.is_owner.get() = true };
469 Ok(())
470 }
471
472 fn set_hdr_flags(&self, flags: VhostUserHeaderFlag) {
473 unsafe { *self.hdr_flags.get() = flags };
474 }
475
476 fn get_features(&self) -> Result<u64, vhost::Error> {
477 Ok(self.features)
478 }
479
480 fn get_protocol_features(&mut self) -> Result<VhostUserProtocolFeatures, vhost::Error> {
481 Ok(self.protocol_features)
482 }
483
484 fn set_protocol_features(
485 &mut self,
486 features: VhostUserProtocolFeatures,
487 ) -> Result<(), vhost::Error> {
488 self.protocol_features = features;
489 Ok(())
490 }
491 }
492
493 let (_tmp_dir, tmp_socket_path) = create_tmp_socket();
494
495 let vhost_block_config = VhostUserBlockConfig {
496 drive_id: "test_drive".to_string(),
497 partuuid: None,
498 is_root_device: false,
499 cache_type: CacheType::Unsafe,
500 socket: tmp_socket_path.clone(),
501 };
502 let vhost_block = VhostUserBlockImpl::<MockMaster>::new(vhost_block_config).unwrap();
503
504 assert_eq!(
507 vhost_block
508 .vu_handle
509 .vu
510 .sock
511 .peer_addr()
512 .unwrap()
513 .as_pathname()
514 .unwrap()
515 .to_str()
516 .unwrap(),
517 &tmp_socket_path,
518 );
519 assert_eq!(vhost_block.vu_handle.vu.max_queue_num, NUM_QUEUES);
520 assert!(unsafe { *vhost_block.vu_handle.vu.is_owner.get() });
521 assert_eq!(vhost_block.avail_features, 0);
522 assert_eq!(vhost_block.acked_features, 0);
523 assert_eq!(vhost_block.vu_acked_protocol_features, 0);
524 assert_eq!(
525 unsafe { &*vhost_block.vu_handle.vu.hdr_flags.get() }.bits(),
526 VhostUserHeaderFlag::empty().bits()
527 );
528 assert!(!vhost_block.root_device);
529 assert!(!vhost_block.read_only);
530 assert_eq!(vhost_block.config_space, Vec::<u8>::new());
531 }
532
533 #[test]
534 fn test_new_all_features() {
535 struct MockMaster {
536 sock: UnixStream,
537 max_queue_num: u64,
538 is_owner: std::cell::UnsafeCell<bool>,
539 features: u64,
540 protocol_features: VhostUserProtocolFeatures,
541 hdr_flags: std::cell::UnsafeCell<VhostUserHeaderFlag>,
542 }
543
544 impl VhostUserHandleBackend for MockMaster {
545 fn from_stream(sock: UnixStream, max_queue_num: u64) -> Self {
546 Self {
547 sock,
548 max_queue_num,
549 is_owner: std::cell::UnsafeCell::new(false),
550 features: AVAILABLE_FEATURES | (1 << VIRTIO_BLK_F_FLUSH),
551
552 protocol_features: VhostUserProtocolFeatures::all(),
553 hdr_flags: std::cell::UnsafeCell::new(VhostUserHeaderFlag::empty()),
554 }
555 }
556
557 fn set_owner(&self) -> Result<(), vhost::Error> {
558 unsafe { *self.is_owner.get() = true };
559 Ok(())
560 }
561
562 fn set_hdr_flags(&self, flags: VhostUserHeaderFlag) {
563 unsafe { *self.hdr_flags.get() = flags };
564 }
565
566 fn get_features(&self) -> Result<u64, vhost::Error> {
567 Ok(self.features)
568 }
569
570 fn get_protocol_features(&mut self) -> Result<VhostUserProtocolFeatures, vhost::Error> {
571 Ok(self.protocol_features)
572 }
573
574 fn set_protocol_features(
575 &mut self,
576 features: VhostUserProtocolFeatures,
577 ) -> Result<(), vhost::Error> {
578 self.protocol_features = features;
579 Ok(())
580 }
581
582 fn get_config(
583 &mut self,
584 _offset: u32,
585 _size: u32,
586 _flags: VhostUserConfigFlags,
587 _buf: &[u8],
588 ) -> Result<(VhostUserConfig, VhostUserConfigPayload), vhost::Error> {
589 Ok((VhostUserConfig::default(), vec![0x69, 0x69, 0x69]))
590 }
591 }
592
593 let (_tmp_dir, tmp_socket_path) = create_tmp_socket();
594
595 let vhost_block_config = VhostUserBlockConfig {
596 drive_id: "test_drive".to_string(),
597 partuuid: None,
598 is_root_device: false,
599 cache_type: CacheType::Writeback,
600 socket: tmp_socket_path.clone(),
601 };
602 let mut vhost_block = VhostUserBlockImpl::<MockMaster>::new(vhost_block_config).unwrap();
603
604 assert_eq!(
607 vhost_block
608 .vu_handle
609 .vu
610 .sock
611 .peer_addr()
612 .unwrap()
613 .as_pathname()
614 .unwrap()
615 .to_str()
616 .unwrap(),
617 &tmp_socket_path,
618 );
619 assert_eq!(vhost_block.vu_handle.vu.max_queue_num, NUM_QUEUES);
620 assert!(unsafe { *vhost_block.vu_handle.vu.is_owner.get() });
621
622 assert_eq!(
623 vhost_block.avail_features,
624 AVAILABLE_FEATURES | (1 << VIRTIO_BLK_F_FLUSH)
625 );
626 assert_eq!(
627 vhost_block.acked_features,
628 VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits()
629 );
630 assert_eq!(
631 vhost_block.vu_acked_protocol_features,
632 VhostUserProtocolFeatures::CONFIG.bits()
633 );
634 assert_eq!(
635 unsafe { &*vhost_block.vu_handle.vu.hdr_flags.get() }.bits(),
636 VhostUserHeaderFlag::empty().bits()
637 );
638 assert!(!vhost_block.root_device);
639 assert!(!vhost_block.read_only);
640 assert_eq!(vhost_block.config_space, vec![0x69, 0x69, 0x69]);
641
642 assert_eq!(
644 vhost_block.avail_features(),
645 AVAILABLE_FEATURES | (1 << VIRTIO_BLK_F_FLUSH)
646 );
647 assert_eq!(
648 vhost_block.acked_features(),
649 VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits()
650 );
651
652 let mut read_config = vec![0, 0, 0];
654 vhost_block.read_config(0, &mut read_config);
655 assert_eq!(read_config, vec![0x69, 0x69, 0x69]);
656
657 let mut read_config = vec![0, 0, 0];
659 vhost_block.read_config(0x69, &mut read_config);
660 assert_eq!(read_config, vec![0, 0, 0]);
661
662 vhost_block.write_config(0x69, &[0]);
664 assert_eq!(vhost_block.config_space, vec![0x69, 0x69, 0x69]);
665
666 vhost_block.device_state = DeviceState::Activated(ActiveState {
668 mem: default_mem(),
669 interrupt: default_interrupt(),
670 });
671 vhost_block.config_space = vec![];
672 vhost_block.config_update().unwrap();
673 assert_eq!(vhost_block.config_space, vec![0x69, 0x69, 0x69]);
674 assert_eq!(
675 vhost_block.interrupt_status().load(Ordering::SeqCst),
676 VIRTIO_MMIO_INT_CONFIG
677 );
678 }
679
680 #[test]
681 fn test_activate() {
682 struct MockMaster {
683 features_are_set: std::cell::UnsafeCell<bool>,
684 memory_is_set: std::cell::UnsafeCell<bool>,
685 vring_enabled: std::cell::UnsafeCell<bool>,
686 }
687
688 impl VhostUserHandleBackend for MockMaster {
689 fn from_stream(_sock: UnixStream, _max_queue_num: u64) -> Self {
690 Self {
691 features_are_set: std::cell::UnsafeCell::new(false),
692 memory_is_set: std::cell::UnsafeCell::new(false),
693 vring_enabled: std::cell::UnsafeCell::new(false),
694 }
695 }
696
697 fn set_owner(&self) -> Result<(), vhost::Error> {
698 Ok(())
699 }
700
701 fn set_hdr_flags(&self, _flags: VhostUserHeaderFlag) {}
702
703 fn get_features(&self) -> Result<u64, vhost::Error> {
704 Ok(0)
705 }
706
707 fn get_protocol_features(&mut self) -> Result<VhostUserProtocolFeatures, vhost::Error> {
708 Ok(VhostUserProtocolFeatures::empty())
709 }
710
711 fn set_protocol_features(
712 &mut self,
713 _features: VhostUserProtocolFeatures,
714 ) -> Result<(), vhost::Error> {
715 Ok(())
716 }
717
718 fn get_config(
719 &mut self,
720 _offset: u32,
721 _size: u32,
722 _flags: VhostUserConfigFlags,
723 _buf: &[u8],
724 ) -> Result<(VhostUserConfig, VhostUserConfigPayload), vhost::Error> {
725 Ok((VhostUserConfig::default(), vec![]))
726 }
727
728 fn set_features(&self, _features: u64) -> Result<(), vhost::Error> {
729 unsafe { (*self.features_are_set.get()) = true };
730 Ok(())
731 }
732
733 fn set_mem_table(
734 &self,
735 _regions: &[VhostUserMemoryRegionInfo],
736 ) -> Result<(), vhost::Error> {
737 unsafe { (*self.memory_is_set.get()) = true };
738 Ok(())
739 }
740
741 fn set_vring_num(&self, _queue_index: usize, _num: u16) -> Result<(), vhost::Error> {
742 Ok(())
743 }
744
745 fn set_vring_addr(
746 &self,
747 _queue_index: usize,
748 _config_data: &VringConfigData,
749 ) -> Result<(), vhost::Error> {
750 Ok(())
751 }
752
753 fn set_vring_base(&self, _queue_index: usize, _base: u16) -> Result<(), vhost::Error> {
754 Ok(())
755 }
756
757 fn set_vring_call(
758 &self,
759 _queue_index: usize,
760 _fd: &EventFd,
761 ) -> Result<(), vhost::Error> {
762 Ok(())
763 }
764
765 fn set_vring_kick(
766 &self,
767 _queue_index: usize,
768 _fd: &EventFd,
769 ) -> Result<(), vhost::Error> {
770 Ok(())
771 }
772
773 fn set_vring_enable(
774 &mut self,
775 _queue_index: usize,
776 _enable: bool,
777 ) -> Result<(), vhost::Error> {
778 unsafe { (*self.vring_enabled.get()) = true };
779 Ok(())
780 }
781 }
782
783 let (_tmp_dir, tmp_socket_path) = create_tmp_socket();
785 let vhost_block_config = VhostUserBlockConfig {
786 drive_id: "test_drive".to_string(),
787 partuuid: None,
788 is_root_device: false,
789 cache_type: CacheType::Writeback,
790 socket: tmp_socket_path,
791 };
792 let mut vhost_block = VhostUserBlockImpl::<MockMaster>::new(vhost_block_config).unwrap();
793
794 let region_size = 0x10000;
796 let file = TempFile::new().unwrap().into_file();
797 file.set_len(region_size as u64).unwrap();
798 let regions = vec![(GuestAddress(0x0), region_size)];
799 let guest_memory = create_mem(file, ®ions);
800 let q = VirtQueue::new(GuestAddress(0), &guest_memory, 16);
801 vhost_block.queues[0] = q.create_queue();
802 let interrupt = default_interrupt();
803
804 vhost_block.activate(guest_memory, interrupt).unwrap();
806 assert!(unsafe { *vhost_block.vu_handle.vu.features_are_set.get() });
807 assert!(unsafe { *vhost_block.vu_handle.vu.memory_is_set.get() });
808 assert!(unsafe { *vhost_block.vu_handle.vu.vring_enabled.get() });
809 assert!(vhost_block.is_activated());
810 }
811}