Skip to content

Commit d4a2ab0

Browse files
committed
feat: add a check for a single root device
Both virtio-block and virtio-pmem can act as root devices for a VM. Add a check to prevent specifing more than 1 root device for a VM. Signed-off-by: Egor Lazarchuk <[email protected]>
1 parent 5757124 commit d4a2ab0

File tree

4 files changed

+112
-30
lines changed

4 files changed

+112
-30
lines changed

src/vmm/src/builder.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -762,7 +762,9 @@ pub(crate) mod tests {
762762
socket: None,
763763
};
764764

765-
block_dev_configs.insert(block_device_config).unwrap();
765+
block_dev_configs
766+
.insert(block_device_config, false)
767+
.unwrap();
766768
}
767769

768770
attach_block_devices(

src/vmm/src/resources.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,8 @@ impl VmResources {
375375
&mut self,
376376
block_device_config: BlockDeviceConfig,
377377
) -> Result<(), DriveError> {
378-
self.block.insert(block_device_config)
378+
let has_pmem_root = self.pmem.has_root_device();
379+
self.block.insert(block_device_config, has_pmem_root)
379380
}
380381

381382
/// Builds a network device to be attached when the VM starts.
@@ -402,7 +403,8 @@ impl VmResources {
402403

403404
/// Builds a pmem device to be attached when the VM starts.
404405
pub fn build_pmem_device(&mut self, body: PmemConfig) -> Result<(), PmemConfigError> {
405-
self.pmem.build(body)
406+
let has_block_root = self.block.has_root_device();
407+
self.pmem.build(body, has_block_root)
406408
}
407409

408410
/// Setter for mmds config.
@@ -614,7 +616,7 @@ mod tests {
614616
fn default_blocks() -> BlockBuilder {
615617
let mut blocks = BlockBuilder::new();
616618
let (cfg, _file) = default_block_cfg();
617-
blocks.insert(cfg).unwrap();
619+
blocks.insert(cfg, false).unwrap();
618620
blocks
619621
}
620622

src/vmm/src/vmm_config/drive.rs

Lines changed: 70 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ use crate::devices::virtio::block::{BlockError, CacheType};
1616
/// Errors associated with the operations allowed on a drive.
1717
#[derive(Debug, thiserror::Error, displaydoc::Display)]
1818
pub enum DriveError {
19+
/// Attempt to add block as a root device while the root device defined as a pmem device
20+
AddingSecondRootDevice,
1921
/// Unable to create the virtio block device: {0}
2022
CreateBlockDevice(BlockError),
2123
/// Cannot create RateLimiter: {0}
@@ -99,7 +101,7 @@ impl BlockBuilder {
99101
}
100102

101103
/// Specifies whether there is a root block device already present in the list.
102-
fn has_root_device(&self) -> bool {
104+
pub fn has_root_device(&self) -> bool {
103105
// If there is a root device, it would be at the top of the list.
104106
if let Some(block) = self.devices.front() {
105107
block.lock().expect("Poisoned lock").root_device()
@@ -127,11 +129,19 @@ impl BlockBuilder {
127129
/// Inserts a `Block` in the block devices list using the specified configuration.
128130
/// If a block with the same id already exists, it will overwrite it.
129131
/// Inserting a secondary root block device will fail.
130-
pub fn insert(&mut self, config: BlockDeviceConfig) -> Result<(), DriveError> {
132+
pub fn insert(
133+
&mut self,
134+
config: BlockDeviceConfig,
135+
has_pmem_root: bool,
136+
) -> Result<(), DriveError> {
131137
let position = self.get_index_of_drive_id(&config.drive_id);
132138
let has_root_device = self.has_root_device();
133139
let configured_as_root = config.is_root_device;
134140

141+
if configured_as_root && has_pmem_root {
142+
return Err(DriveError::AddingSecondRootDevice);
143+
}
144+
135145
// Don't allow adding a second root block device.
136146
// If the new device cfg is root and not an update to the existing root, fail fast.
137147
if configured_as_root && has_root_device && position != Some(0) {
@@ -234,7 +244,9 @@ mod tests {
234244
};
235245

236246
let mut block_devs = BlockBuilder::new();
237-
block_devs.insert(dummy_block_device.clone()).unwrap();
247+
block_devs
248+
.insert(dummy_block_device.clone(), false)
249+
.unwrap();
238250

239251
assert!(!block_devs.has_root_device());
240252
assert_eq!(block_devs.devices.len(), 1);
@@ -266,7 +278,9 @@ mod tests {
266278
};
267279

268280
let mut block_devs = BlockBuilder::new();
269-
block_devs.insert(dummy_block_device.clone()).unwrap();
281+
block_devs
282+
.insert(dummy_block_device.clone(), false)
283+
.unwrap();
270284

271285
assert!(block_devs.has_root_device());
272286
assert_eq!(block_devs.devices.len(), 1);
@@ -276,6 +290,36 @@ mod tests {
276290
assert_eq!(block.read_only(), dummy_block_device.is_read_only.unwrap());
277291
}
278292

293+
#[test]
294+
fn test_add_one_root_block_device_with_pmem_already_as_root() {
295+
let dummy_file = TempFile::new().unwrap();
296+
let dummy_path = dummy_file.as_path().to_str().unwrap().to_string();
297+
298+
let dummy_block_device = BlockDeviceConfig {
299+
drive_id: String::from("1"),
300+
partuuid: None,
301+
is_root_device: true,
302+
cache_type: CacheType::Unsafe,
303+
304+
is_read_only: Some(true),
305+
path_on_host: Some(dummy_path),
306+
rate_limiter: None,
307+
file_engine_type: None,
308+
309+
socket: None,
310+
};
311+
312+
let mut block_devs = BlockBuilder::new();
313+
assert!(matches!(
314+
block_devs
315+
.insert(dummy_block_device.clone(), true)
316+
.unwrap_err(),
317+
DriveError::AddingSecondRootDevice,
318+
));
319+
assert!(!block_devs.has_root_device());
320+
assert_eq!(block_devs.devices.len(), 0);
321+
}
322+
279323
#[test]
280324
fn test_add_two_root_block_devs() {
281325
let dummy_file_1 = TempFile::new().unwrap();
@@ -311,9 +355,9 @@ mod tests {
311355
};
312356

313357
let mut block_devs = BlockBuilder::new();
314-
block_devs.insert(root_block_device_1).unwrap();
358+
block_devs.insert(root_block_device_1, false).unwrap();
315359
assert_eq!(
316-
block_devs.insert(root_block_device_2).unwrap_err(),
360+
block_devs.insert(root_block_device_2, false).unwrap_err(),
317361
DriveError::RootBlockDeviceAlreadyAdded
318362
);
319363
}
@@ -370,9 +414,9 @@ mod tests {
370414
};
371415

372416
let mut block_devs = BlockBuilder::new();
373-
block_devs.insert(dummy_block_dev_2.clone()).unwrap();
374-
block_devs.insert(dummy_block_dev_3.clone()).unwrap();
375-
block_devs.insert(root_block_device.clone()).unwrap();
417+
block_devs.insert(dummy_block_dev_2.clone(), false).unwrap();
418+
block_devs.insert(dummy_block_dev_3.clone(), false).unwrap();
419+
block_devs.insert(root_block_device.clone(), false).unwrap();
376420

377421
assert_eq!(block_devs.devices.len(), 3);
378422

@@ -443,9 +487,9 @@ mod tests {
443487
};
444488

445489
let mut block_devs = BlockBuilder::new();
446-
block_devs.insert(dummy_block_dev_2.clone()).unwrap();
447-
block_devs.insert(dummy_block_dev_3.clone()).unwrap();
448-
block_devs.insert(root_block_device.clone()).unwrap();
490+
block_devs.insert(dummy_block_dev_2.clone(), false).unwrap();
491+
block_devs.insert(dummy_block_dev_3.clone(), false).unwrap();
492+
block_devs.insert(root_block_device.clone(), false).unwrap();
449493

450494
assert_eq!(block_devs.devices.len(), 3);
451495

@@ -503,8 +547,10 @@ mod tests {
503547
let mut block_devs = BlockBuilder::new();
504548

505549
// Add 2 block devices.
506-
block_devs.insert(root_block_device).unwrap();
507-
block_devs.insert(dummy_block_device_2.clone()).unwrap();
550+
block_devs.insert(root_block_device, false).unwrap();
551+
block_devs
552+
.insert(dummy_block_device_2.clone(), false)
553+
.unwrap();
508554

509555
// Get index zero.
510556
assert_eq!(
@@ -528,7 +574,9 @@ mod tests {
528574
);
529575
// Update OK.
530576
dummy_block_device_2.is_read_only = Some(true);
531-
block_devs.insert(dummy_block_device_2.clone()).unwrap();
577+
block_devs
578+
.insert(dummy_block_device_2.clone(), false)
579+
.unwrap();
532580

533581
let index = block_devs
534582
.get_index_of_drive_id(&dummy_block_device_2.drive_id)
@@ -540,7 +588,7 @@ mod tests {
540588
let dummy_path_3 = String::from("test_update_3");
541589
dummy_block_device_2.path_on_host = Some(dummy_path_3);
542590
assert!(matches!(
543-
block_devs.insert(dummy_block_device_2.clone()),
591+
block_devs.insert(dummy_block_device_2.clone(), false),
544592
Err(DriveError::CreateBlockDevice(BlockError::VirtioBackend(
545593
VirtioBlockError::BackingFile(_, _)
546594
)))
@@ -550,7 +598,7 @@ mod tests {
550598
dummy_block_device_2.path_on_host = Some(dummy_path_2.clone());
551599
dummy_block_device_2.is_root_device = true;
552600
assert_eq!(
553-
block_devs.insert(dummy_block_device_2),
601+
block_devs.insert(dummy_block_device_2, false),
554602
Err(DriveError::RootBlockDeviceAlreadyAdded)
555603
);
556604

@@ -584,9 +632,9 @@ mod tests {
584632
socket: None,
585633
};
586634

587-
block_devs.insert(root_block_device_old).unwrap();
635+
block_devs.insert(root_block_device_old, false).unwrap();
588636
let root_block_id = root_block_device_new.drive_id.clone();
589-
block_devs.insert(root_block_device_new).unwrap();
637+
block_devs.insert(root_block_device_new, false).unwrap();
590638
assert!(block_devs.has_root_device());
591639
// Verify it's been moved to the first position.
592640
assert_eq!(block_devs.devices[0].lock().unwrap().id(), root_block_id);
@@ -611,7 +659,9 @@ mod tests {
611659
};
612660

613661
let mut block_devs = BlockBuilder::new();
614-
block_devs.insert(dummy_block_device.clone()).unwrap();
662+
block_devs
663+
.insert(dummy_block_device.clone(), false)
664+
.unwrap();
615665

616666
let configs = block_devs.configs();
617667
assert_eq!(configs.len(), 1);

src/vmm/src/vmm_config/pmem.rs

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use crate::devices::virtio::pmem::device::{Pmem, PmemError};
1010
/// Errors associated wit the operations allowed on a pmem device
1111
#[derive(Debug, thiserror::Error, displaydoc::Display)]
1212
pub enum PmemConfigError {
13+
/// Attempt to add pmem as a root device while the root device defined as a block device
14+
AddingSecondRootDevice,
1315
/// A root pmem device already exist
1416
RootPmemDeviceAlreadyExist,
1517
/// Unable to create the virtio-pmem device: {0}
@@ -53,7 +55,14 @@ impl PmemBuilder {
5355
}
5456

5557
/// Build a device from the config
56-
pub fn build(&mut self, config: PmemConfig) -> Result<(), PmemConfigError> {
58+
pub fn build(
59+
&mut self,
60+
config: PmemConfig,
61+
has_block_root: bool,
62+
) -> Result<(), PmemConfigError> {
63+
if config.root_device && has_block_root {
64+
return Err(PmemConfigError::AddingSecondRootDevice);
65+
}
5766
let position = self
5867
.devices
5968
.iter()
@@ -112,19 +121,19 @@ mod tests {
112121
root_device: true,
113122
read_only: false,
114123
};
115-
builder.build(config.clone()).unwrap();
124+
builder.build(config.clone(), false).unwrap();
116125
assert_eq!(builder.devices.len(), 1);
117126
assert!(builder.has_root_device());
118127

119128
// First device got replaced with new one
120129
config.root_device = false;
121-
builder.build(config).unwrap();
130+
builder.build(config, false).unwrap();
122131
assert_eq!(builder.devices.len(), 1);
123132
assert!(!builder.has_root_device());
124133
}
125134

126135
#[test]
127-
fn test_pmem_builder_build_second_root() {
136+
fn test_pmem_builder_build_seconde_root() {
128137
let mut builder = PmemBuilder::default();
129138

130139
let dummy_file = TempFile::new().unwrap();
@@ -136,12 +145,31 @@ mod tests {
136145
root_device: true,
137146
read_only: false,
138147
};
139-
builder.build(config.clone()).unwrap();
148+
builder.build(config.clone(), false).unwrap();
140149

141150
config.id = "2".into();
142151
assert!(matches!(
143-
builder.build(config.clone()).unwrap_err(),
152+
builder.build(config.clone(), false).unwrap_err(),
144153
PmemConfigError::RootPmemDeviceAlreadyExist,
145154
));
146155
}
156+
157+
#[test]
158+
fn test_pmem_builder_build_root_with_block_already_a_root() {
159+
let mut builder = PmemBuilder::default();
160+
161+
let dummy_file = TempFile::new().unwrap();
162+
dummy_file.as_file().set_len(Pmem::ALIGNMENT).unwrap();
163+
let dummy_path = dummy_file.as_path().to_str().unwrap().to_string();
164+
let config = PmemConfig {
165+
id: "1".into(),
166+
path_on_host: dummy_path,
167+
root_device: true,
168+
read_only: false,
169+
};
170+
assert!(matches!(
171+
builder.build(config, true).unwrap_err(),
172+
PmemConfigError::AddingSecondRootDevice,
173+
));
174+
}
147175
}

0 commit comments

Comments
 (0)