vmm/devices/virtio/block/
device.rs

1// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::sync::Arc;
5
6use event_manager::{EventOps, Events, MutEventSubscriber};
7use log::info;
8use vmm_sys_util::eventfd::EventFd;
9
10use super::BlockError;
11use super::persist::{BlockConstructorArgs, BlockState};
12use super::vhost_user::device::{VhostUserBlock, VhostUserBlockConfig};
13use super::virtio::device::{VirtioBlock, VirtioBlockConfig};
14use crate::devices::virtio::ActivateError;
15use crate::devices::virtio::device::VirtioDevice;
16use crate::devices::virtio::generated::virtio_ids::VIRTIO_ID_BLOCK;
17use crate::devices::virtio::queue::{InvalidAvailIdx, Queue};
18use crate::devices::virtio::transport::VirtioInterrupt;
19use crate::impl_device_type;
20use crate::rate_limiter::BucketUpdate;
21use crate::snapshot::Persist;
22use crate::vmm_config::drive::BlockDeviceConfig;
23use crate::vstate::memory::GuestMemoryMmap;
24
25// Clippy thinks that values of the enum are too different in size.
26#[allow(clippy::large_enum_variant)]
27#[derive(Debug)]
28pub enum Block {
29    Virtio(VirtioBlock),
30    VhostUser(VhostUserBlock),
31}
32
33impl Block {
34    pub fn new(config: BlockDeviceConfig) -> Result<Block, BlockError> {
35        if let Ok(config) = VirtioBlockConfig::try_from(&config) {
36            Ok(Self::Virtio(
37                VirtioBlock::new(config).map_err(BlockError::VirtioBackend)?,
38            ))
39        } else if let Ok(config) = VhostUserBlockConfig::try_from(&config) {
40            Ok(Self::VhostUser(
41                VhostUserBlock::new(config).map_err(BlockError::VhostUserBackend)?,
42            ))
43        } else {
44            Err(BlockError::InvalidBlockConfig)
45        }
46    }
47
48    pub fn config(&self) -> BlockDeviceConfig {
49        match self {
50            Self::Virtio(b) => b.config().into(),
51            Self::VhostUser(b) => b.config().into(),
52        }
53    }
54
55    pub fn update_disk_image(&mut self, disk_image_path: String) -> Result<(), BlockError> {
56        match self {
57            Self::Virtio(b) => b
58                .update_disk_image(disk_image_path)
59                .map_err(BlockError::VirtioBackend),
60            Self::VhostUser(_) => Err(BlockError::InvalidBlockBackend),
61        }
62    }
63
64    pub fn update_rate_limiter(
65        &mut self,
66        bytes: BucketUpdate,
67        ops: BucketUpdate,
68    ) -> Result<(), BlockError> {
69        match self {
70            Self::Virtio(b) => {
71                b.update_rate_limiter(bytes, ops);
72                Ok(())
73            }
74            Self::VhostUser(_) => Err(BlockError::InvalidBlockBackend),
75        }
76    }
77
78    pub fn update_config(&mut self) -> Result<(), BlockError> {
79        match self {
80            Self::Virtio(_) => Err(BlockError::InvalidBlockBackend),
81            Self::VhostUser(b) => b.config_update().map_err(BlockError::VhostUserBackend),
82        }
83    }
84
85    pub fn prepare_save(&mut self) {
86        match self {
87            Self::Virtio(b) => b.prepare_save(),
88            Self::VhostUser(b) => b.prepare_save(),
89        }
90    }
91
92    pub fn process_virtio_queues(&mut self) -> Result<(), InvalidAvailIdx> {
93        match self {
94            Self::Virtio(b) => b.process_virtio_queues(),
95            Self::VhostUser(_) => Ok(()),
96        }
97    }
98
99    pub fn id(&self) -> &str {
100        match self {
101            Self::Virtio(b) => &b.id,
102            Self::VhostUser(b) => &b.id,
103        }
104    }
105
106    pub fn root_device(&self) -> bool {
107        match self {
108            Self::Virtio(b) => b.root_device,
109            Self::VhostUser(b) => b.root_device,
110        }
111    }
112
113    pub fn read_only(&self) -> bool {
114        match self {
115            Self::Virtio(b) => b.read_only,
116            Self::VhostUser(b) => b.read_only,
117        }
118    }
119
120    pub fn partuuid(&self) -> &Option<String> {
121        match self {
122            Self::Virtio(b) => &b.partuuid,
123            Self::VhostUser(b) => &b.partuuid,
124        }
125    }
126
127    pub fn is_vhost_user(&self) -> bool {
128        match self {
129            Self::Virtio(_) => false,
130            Self::VhostUser(_) => true,
131        }
132    }
133}
134
135impl VirtioDevice for Block {
136    impl_device_type!(VIRTIO_ID_BLOCK);
137
138    fn avail_features(&self) -> u64 {
139        match self {
140            Self::Virtio(b) => b.avail_features,
141            Self::VhostUser(b) => b.avail_features,
142        }
143    }
144
145    fn acked_features(&self) -> u64 {
146        match self {
147            Self::Virtio(b) => b.acked_features,
148            Self::VhostUser(b) => b.acked_features,
149        }
150    }
151
152    fn set_acked_features(&mut self, acked_features: u64) {
153        match self {
154            Self::Virtio(b) => b.acked_features = acked_features,
155            Self::VhostUser(b) => b.acked_features = acked_features,
156        }
157    }
158
159    fn queues(&self) -> &[Queue] {
160        match self {
161            Self::Virtio(b) => &b.queues,
162            Self::VhostUser(b) => &b.queues,
163        }
164    }
165
166    fn queues_mut(&mut self) -> &mut [Queue] {
167        match self {
168            Self::Virtio(b) => &mut b.queues,
169            Self::VhostUser(b) => &mut b.queues,
170        }
171    }
172
173    fn queue_events(&self) -> &[EventFd] {
174        match self {
175            Self::Virtio(b) => &b.queue_evts,
176            Self::VhostUser(b) => &b.queue_evts,
177        }
178    }
179
180    fn interrupt_trigger(&self) -> &dyn VirtioInterrupt {
181        match self {
182            Self::Virtio(b) => b.interrupt_trigger(),
183            Self::VhostUser(b) => b.interrupt_trigger(),
184        }
185    }
186
187    fn read_config(&self, offset: u64, data: &mut [u8]) {
188        match self {
189            Self::Virtio(b) => b.read_config(offset, data),
190            Self::VhostUser(b) => b.read_config(offset, data),
191        }
192    }
193
194    fn write_config(&mut self, offset: u64, data: &[u8]) {
195        match self {
196            Self::Virtio(b) => b.write_config(offset, data),
197            Self::VhostUser(b) => b.write_config(offset, data),
198        }
199    }
200
201    fn activate(
202        &mut self,
203        mem: GuestMemoryMmap,
204        interrupt: Arc<dyn VirtioInterrupt>,
205    ) -> Result<(), ActivateError> {
206        match self {
207            Self::Virtio(b) => b.activate(mem, interrupt),
208            Self::VhostUser(b) => b.activate(mem, interrupt),
209        }
210    }
211
212    fn is_activated(&self) -> bool {
213        match self {
214            Self::Virtio(b) => b.device_state.is_activated(),
215            Self::VhostUser(b) => b.device_state.is_activated(),
216        }
217    }
218
219    fn kick(&mut self) {
220        // If device is activated, kick the block queue(s) to make up for any
221        // pending or in-flight epoll events we may have not captured in
222        // snapshot. No need to kick Ratelimiters
223        // because they are restored 'unblocked' so
224        // any inflight `timer_fd` events can be safely discarded.
225        if self.is_activated() {
226            info!("kick block {}.", self.id());
227            self.process_virtio_queues();
228        }
229    }
230}
231
232impl MutEventSubscriber for Block {
233    fn process(&mut self, event: Events, ops: &mut EventOps) {
234        match self {
235            Self::Virtio(b) => b.process(event, ops),
236            Self::VhostUser(b) => b.process(event, ops),
237        }
238    }
239
240    fn init(&mut self, ops: &mut EventOps) {
241        match self {
242            Self::Virtio(b) => b.init(ops),
243            Self::VhostUser(b) => b.init(ops),
244        }
245    }
246}
247
248impl Persist<'_> for Block {
249    type State = BlockState;
250    type ConstructorArgs = BlockConstructorArgs;
251    type Error = BlockError;
252
253    fn save(&self) -> Self::State {
254        match self {
255            Self::Virtio(b) => BlockState::Virtio(b.save()),
256            Self::VhostUser(b) => BlockState::VhostUser(b.save()),
257        }
258    }
259
260    fn restore(
261        constructor_args: Self::ConstructorArgs,
262        state: &Self::State,
263    ) -> Result<Self, Self::Error> {
264        match state {
265            BlockState::Virtio(s) => Ok(Self::Virtio(
266                VirtioBlock::restore(constructor_args, s).map_err(BlockError::VirtioBackend)?,
267            )),
268            BlockState::VhostUser(s) => Ok(Self::VhostUser(
269                VhostUserBlock::restore(constructor_args, s)
270                    .map_err(BlockError::VhostUserBackend)?,
271            )),
272        }
273    }
274}