vmm/devices/virtio/block/vhost_user/
device.rs

1// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4// Portions Copyright 2019 Intel Corporation. All Rights Reserved.
5// SPDX-License-Identifier: Apache-2.0
6
7use 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
36/// Block device config space size in bytes.
37const BLOCK_CONFIG_SPACE_SIZE: u32 = 60;
38
39const AVAILABLE_FEATURES: u64 = (1 << VIRTIO_F_VERSION_1)
40    | (1 << VIRTIO_RING_F_EVENT_IDX)
41    // vhost-user specific bit. Not defined in standard virtio spec.
42    // Specifies ability of frontend to negotiate protocol features.
43    | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits()
44    // We always try to negotiate readonly with the backend.
45    // If the backend is configured as readonly, we will accept it.
46    | (1 << VIRTIO_BLK_F_RO);
47
48/// Use this structure to set up the Block Device before booting the kernel.
49#[derive(Debug, PartialEq, Eq)]
50pub struct VhostUserBlockConfig {
51    /// Unique identifier of the drive.
52    pub drive_id: String,
53    /// Part-UUID. Represents the unique id of the boot partition of this device. It is
54    /// optional and it will be used only if the `is_root_device` field is true.
55    pub partuuid: Option<String>,
56    /// If set to true, it makes the current device the root block device.
57    /// Setting this flag to true will mount the block device in the
58    /// guest under /dev/vda unless the partuuid is present.
59    pub is_root_device: bool,
60    /// If set to true, the drive will ignore flush requests coming from
61    /// the guest driver.
62    pub cache_type: CacheType,
63
64    /// Socket path of the vhost-user process
65    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
112/// vhost-user block device.
113pub struct VhostUserBlockImpl<T: VhostUserHandleBackend> {
114    // Virtio fields.
115    pub avail_features: u64,
116    pub acked_features: u64,
117    pub config_space: Vec<u8>,
118    pub activate_evt: EventFd,
119
120    // Transport related fields.
121    pub queues: Vec<Queue>,
122    pub queue_evts: [EventFd; u64_to_usize(NUM_QUEUES)],
123    pub device_state: DeviceState,
124
125    // Implementation specific fields.
126    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    // Vhost user protocol handle
133    pub vu_handle: VhostUserHandleImpl<T>,
134    pub vu_acked_protocol_features: u64,
135    pub metrics: Arc<VhostUserDeviceMetrics>,
136}
137
138// Need custom implementation because otherwise `Debug` is required for `vhost::Master`
139impl<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        // Get config from backend if CONFIG is acked or use empty buffer.
183        let config_space =
184            if acked_protocol_features & VhostUserProtocolFeatures::CONFIG.bits() != 0 {
185                // This buffer is used for config size check in vhost crate.
186                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        // We negotiated features with backend. Now these acked_features
210        // are available for guest driver to choose from.
211        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    /// Prepare device for being snapshotted.
243    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        // This buffer is used for config size check in vhost crate.
267        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        // We do not advertise VIRTIO_BLK_F_CONFIG_WCE
337        // that would allow configuring the "writeback" field.
338        // Other block config fields are immutable.
339    }
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        // Setting features again, because now we negotiated them
353        // with guest driver as well.
354        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        // If backend has no features, nothing should be negotiated and
505        // no flags should be set.
506        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        // If backend has all features, features offered by block device
605        // should be negotiated and header flags should be set.
606        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        // Test some `VirtioDevice` methods
643        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        // Valid read
653        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        // Invalid offset
658        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        // Writing to the config does nothing
663        vhost_block.write_config(0x69, &[0]);
664        assert_eq!(vhost_block.config_space, vec![0x69, 0x69, 0x69]);
665
666        // Testing [`config_update`]
667        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        // Block creation
784        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        // Memory creation
795        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, &regions);
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        // During actiavion of the device features, memory and queues should be set and activated.
805        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}