Skip to content

Commit a7b6086

Browse files
dianpopaacatangiu
authored andcommitted
drive: add support for specifying...
location of root partition through the PARTUUID kernel parameter. Signed-off-by: Diana Popa <[email protected]>
1 parent 9c26e78 commit a7b6086

File tree

4 files changed

+74
-24
lines changed

4 files changed

+74
-24
lines changed

api_server/src/http_service.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,7 @@ mod tests {
10891089
path_on_host: String::from("/foo/bar"),
10901090
state: DeviceState::Attached,
10911091
is_root_device: true,
1092+
partuuid: None,
10921093
permissions: DrivePermissions::ro,
10931094
rate_limiter: None,
10941095
};

api_server/src/request/sync/drive.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ pub struct DriveDescription {
2626
pub is_root_device: bool,
2727
pub permissions: DrivePermissions,
2828
#[serde(skip_serializing_if = "Option::is_none")]
29+
pub partuuid: Option<String>,
30+
#[serde(skip_serializing_if = "Option::is_none")]
2931
pub rate_limiter: Option<RateLimiterDescription>,
3032
}
3133

@@ -129,6 +131,7 @@ mod tests {
129131
state: DeviceState::Attached,
130132
is_root_device: true,
131133
permissions: DrivePermissions::ro,
134+
partuuid: None,
132135
rate_limiter: None,
133136
}.is_read_only()
134137
);
@@ -204,6 +207,7 @@ mod tests {
204207
state: DeviceState::Attached,
205208
is_root_device: true,
206209
permissions: DrivePermissions::ro,
210+
partuuid: None,
207211
rate_limiter: None,
208212
};
209213

vmm/src/device_config/drive.rs

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,22 @@ pub struct BlockDeviceConfig {
1313
pub drive_id: String,
1414
pub path_on_host: PathBuf,
1515
pub is_root_device: bool,
16+
pub partuuid: Option<String>,
1617
pub is_read_only: bool,
1718
pub rate_limiter: Option<RateLimiterDescription>,
1819
}
1920

21+
impl BlockDeviceConfig {
22+
pub fn get_partuuid(&self) -> Option<&String> {
23+
self.partuuid.as_ref()
24+
}
25+
}
26+
2027
// Wrapper for the collection that holds all the Block Devices Configs
2128
pub struct BlockDeviceConfigs {
2229
pub config_list: LinkedList<BlockDeviceConfig>,
2330
has_root_block: bool,
31+
has_partuuid_root: bool,
2432
read_only_root: bool,
2533
}
2634

@@ -31,6 +39,7 @@ impl From<DriveDescription> for BlockDeviceConfig {
3139
drive_id: item.drive_id,
3240
path_on_host: PathBuf::from(item.path_on_host),
3341
is_root_device: item.is_root_device,
42+
partuuid: item.partuuid,
3443
is_read_only,
3544
rate_limiter: item.rate_limiter,
3645
}
@@ -42,6 +51,7 @@ impl BlockDeviceConfigs {
4251
BlockDeviceConfigs {
4352
config_list: LinkedList::<BlockDeviceConfig>::new(),
4453
has_root_block: false,
54+
has_partuuid_root: false,
4555
read_only_root: false,
4656
}
4757
}
@@ -54,6 +64,10 @@ impl BlockDeviceConfigs {
5464
self.read_only_root
5565
}
5666

67+
pub fn has_partuuid_root(&self) -> bool {
68+
self.has_partuuid_root
69+
}
70+
5771
pub fn contains_drive_path(&self, drive_path: PathBuf) -> bool {
5872
for drive_config in self.config_list.iter() {
5973
if drive_config.path_on_host == drive_path {
@@ -92,7 +106,10 @@ impl BlockDeviceConfigs {
92106
} else {
93107
self.has_root_block = true;
94108
self.read_only_root = block_device_config.is_read_only;
95-
// Root Device should be the first in the list
109+
self.has_partuuid_root = block_device_config.partuuid.is_some();
110+
// Root Device should be the first in the list whether or not PARTUUID is specified
111+
// in order to avoid bugs in case of switching from partuuid boot scenarios to
112+
// /dev/vda boot type.
96113
self.config_list.push_front(block_device_config);
97114
}
98115
} else {
@@ -118,7 +135,7 @@ impl BlockDeviceConfigs {
118135
/// This function updates a Block Device Config prior to the guest boot. The update fails if it
119136
/// would result in two root block devices.
120137
pub fn update(&mut self, block_device_config: &BlockDeviceConfig) -> Result<()> {
121-
// Check if the path exists
138+
// Check if the path exists.
122139
if !block_device_config.path_on_host.exists() {
123140
return Err(DriveError::InvalidBlockDevicePath);
124141
}
@@ -127,23 +144,27 @@ impl BlockDeviceConfigs {
127144
for cfg in self.config_list.iter_mut() {
128145
if cfg.drive_id == block_device_config.drive_id {
129146
if cfg.is_root_device {
130-
// Check if the root block device is being updated
147+
// Check if the root block device is being updated.
131148
self.has_root_block = block_device_config.is_root_device;
132149
self.read_only_root =
133150
block_device_config.is_root_device && block_device_config.is_read_only;
151+
self.has_partuuid_root = block_device_config.partuuid.is_some();
134152
} else if block_device_config.is_root_device {
135-
// Check if a second root block device is being added
153+
// Check if a second root block device is being added.
136154
if root_id.is_some() {
137155
return Err(DriveError::RootBlockDeviceAlreadyAdded);
138156
} else {
139-
// One of the non-root blocks is becoming root
157+
// One of the non-root blocks is becoming root.
140158
self.has_root_block = true;
141159
self.read_only_root = block_device_config.is_read_only;
160+
self.has_partuuid_root = block_device_config.partuuid.is_some();
142161
}
143162
}
144163
cfg.is_root_device = block_device_config.is_root_device;
145164
cfg.path_on_host = block_device_config.path_on_host.clone();
146165
cfg.is_read_only = block_device_config.is_read_only;
166+
cfg.rate_limiter = block_device_config.rate_limiter.clone();
167+
cfg.partuuid = block_device_config.partuuid.clone();
147168

148169
return Ok(());
149170
}
@@ -177,6 +198,7 @@ mod tests {
177198
let dummy_block_device = BlockDeviceConfig {
178199
path_on_host: dummy_path.clone(),
179200
is_root_device: false,
201+
partuuid: None,
180202
is_read_only: false,
181203
drive_id: dummy_id.clone(),
182204
rate_limiter: None,
@@ -206,6 +228,7 @@ mod tests {
206228
let dummy_block_device = BlockDeviceConfig {
207229
path_on_host: dummy_path,
208230
is_root_device: true,
231+
partuuid: None,
209232
is_read_only: true,
210233
drive_id: String::from("1"),
211234
rate_limiter: None,
@@ -232,6 +255,7 @@ mod tests {
232255
let root_block_device_1 = BlockDeviceConfig {
233256
path_on_host: dummy_path_1,
234257
is_root_device: true,
258+
partuuid: None,
235259
is_read_only: false,
236260
drive_id: String::from("1"),
237261
rate_limiter: None,
@@ -242,6 +266,7 @@ mod tests {
242266
let root_block_device_2 = BlockDeviceConfig {
243267
path_on_host: dummy_path_2,
244268
is_root_device: true,
269+
partuuid: None,
245270
is_read_only: false,
246271
drive_id: String::from("2"),
247272
rate_limiter: None,
@@ -263,6 +288,7 @@ mod tests {
263288
let root_block_device = BlockDeviceConfig {
264289
path_on_host: dummy_path_1,
265290
is_root_device: true,
291+
partuuid: None,
266292
is_read_only: false,
267293
drive_id: String::from("1"),
268294
rate_limiter: None,
@@ -273,6 +299,7 @@ mod tests {
273299
let dummy_block_device_2 = BlockDeviceConfig {
274300
path_on_host: dummy_path_2,
275301
is_root_device: false,
302+
partuuid: None,
276303
is_read_only: false,
277304
drive_id: String::from("2"),
278305
rate_limiter: None,
@@ -283,6 +310,7 @@ mod tests {
283310
let dummy_block_device_3 = BlockDeviceConfig {
284311
path_on_host: dummy_path_3,
285312
is_root_device: false,
313+
partuuid: None,
286314
is_read_only: false,
287315
drive_id: String::from("3"),
288316
rate_limiter: None,
@@ -319,6 +347,7 @@ mod tests {
319347
let root_block_device = BlockDeviceConfig {
320348
path_on_host: dummy_path_1,
321349
is_root_device: true,
350+
partuuid: None,
322351
is_read_only: false,
323352
drive_id: String::from("1"),
324353
rate_limiter: None,
@@ -329,6 +358,7 @@ mod tests {
329358
let dummy_block_device_2 = BlockDeviceConfig {
330359
path_on_host: dummy_path_2,
331360
is_root_device: false,
361+
partuuid: None,
332362
is_read_only: false,
333363
drive_id: String::from("2"),
334364
rate_limiter: None,
@@ -339,6 +369,7 @@ mod tests {
339369
let dummy_block_device_3 = BlockDeviceConfig {
340370
path_on_host: dummy_path_3,
341371
is_root_device: false,
372+
partuuid: None,
342373
is_read_only: false,
343374
drive_id: String::from("3"),
344375
rate_limiter: None,
@@ -361,7 +392,8 @@ mod tests {
361392
assert_eq!(block_devices_configs.config_list.len(), 3);
362393

363394
let mut block_dev_iter = block_devices_configs.config_list.iter();
364-
// The root device should be first in the list no matter of the order in which the devices were added
395+
// The root device should be first in the list no matter of the order in
396+
// which the devices were added.
365397
assert_eq!(block_dev_iter.next().unwrap(), &root_block_device);
366398
assert_eq!(block_dev_iter.next().unwrap(), &dummy_block_device_2);
367399
assert_eq!(block_dev_iter.next().unwrap(), &dummy_block_device_3);
@@ -371,6 +403,7 @@ mod tests {
371403
fn test_from_drive_description() {
372404
let dd = DriveDescription {
373405
is_root_device: true,
406+
partuuid: None,
374407
path_on_host: String::from("/foo/bar"),
375408
drive_id: String::from("foo"),
376409
state: DeviceState::Attached,
@@ -392,6 +425,7 @@ mod tests {
392425
let root_block_device = BlockDeviceConfig {
393426
path_on_host: dummy_path_1,
394427
is_root_device: true,
428+
partuuid: None,
395429
is_read_only: false,
396430
drive_id: String::from("1"),
397431
rate_limiter: None,
@@ -402,6 +436,7 @@ mod tests {
402436
let mut dummy_block_device_2 = BlockDeviceConfig {
403437
path_on_host: dummy_path_2.clone(),
404438
is_root_device: false,
439+
partuuid: None,
405440
is_read_only: false,
406441
drive_id: String::from("2"),
407442
rate_limiter: None,

vmm/src/lib.rs

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ impl Vmm {
445445
}
446446

447447
/// Only call this function as part of the API.
448-
/// If the drive_id does not exit, a new Block Device Config is added to the list.
448+
/// If the drive_id does not exist, a new Block Device Config is added to the list.
449449
/// Else, if the VM is running, the block device will be updated.
450450
/// Updating before the VM has started is not allowed.
451451
pub fn put_block_device(
@@ -527,23 +527,22 @@ impl Vmm {
527527
Ok(())
528528
}
529529

530-
/// Attach all block devices from the BlockDevicesConfig
531-
/// If there is no root block device, no other devices are attached.The root device should be
532-
/// the first to be attached as a way to make sure it ends up on /dev/vda
533-
/// This function is to be called only from boot_source
530+
/// Attach all block devices from the BlockDevicesConfig.
531+
/// If there is no root block device, no other devices are attached.
534532
fn attach_block_devices(&mut self, device_manager: &mut MMIODeviceManager) -> Result<()> {
535533
// If there's no root device, do not attach any other devices
536534
let block_dev = &self.block_device_configs;
537-
let kernel_config = match self.kernel_config.as_mut() {
538-
Some(x) => x,
539-
None => return Err(Error::MissingKernelConfig),
540-
};
535+
// We rely on check_health function for making sure kernel_config is not None.
536+
let kernel_config = self.kernel_config.as_mut().unwrap();
541537

542538
if block_dev.has_root_block_device() {
543-
// this is a simple solution to add a block as a root device; should be improved
544-
kernel_config.cmdline.insert_str(" root=/dev/vda")?;
545-
if block_dev.has_read_only_root() {
546-
kernel_config.cmdline.insert_str(" ro")?;
539+
// If no PARTUUID was specified for the root device, try with the /dev/vda.
540+
if !block_dev.has_partuuid_root() {
541+
kernel_config.cmdline.insert_str(" root=/dev/vda")?;
542+
543+
if block_dev.has_read_only_root() {
544+
kernel_config.cmdline.insert_str(" ro")?;
545+
}
547546
}
548547

549548
let epoll_context = &mut self.epoll_context;
@@ -554,6 +553,17 @@ impl Vmm {
554553
.write(!drive_config.is_read_only)
555554
.open(&drive_config.path_on_host)
556555
.map_err(Error::RootDiskImage)?;
556+
557+
if drive_config.is_root_device && drive_config.partuuid.is_some() {
558+
kernel_config.cmdline.insert_str(format!(
559+
" root=PARTUUID={}",
560+
drive_config.get_partuuid().unwrap()
561+
))?;
562+
if drive_config.is_read_only {
563+
kernel_config.cmdline.insert_str(" ro")?;
564+
}
565+
}
566+
557567
let epoll_config = epoll_context.allocate_virtio_block_tokens();
558568

559569
let rate_limiter = rate_limiter_description_into_implementation(
@@ -630,10 +640,8 @@ impl Vmm {
630640
}
631641

632642
fn attach_net_devices(&mut self, device_manager: &mut MMIODeviceManager) -> Result<()> {
633-
let kernel_config = match self.kernel_config.as_mut() {
634-
Some(x) => x,
635-
None => return Err(Error::MissingKernelConfig),
636-
};
643+
// We rely on check_health function for making sure kernel_config is not None.
644+
let kernel_config = self.kernel_config.as_mut().unwrap();
637645

638646
for cfg in self.network_interface_configs.iter_mut() {
639647
let epoll_config = self.epoll_context.allocate_virtio_net_tokens();
@@ -1079,7 +1087,7 @@ impl Vmm {
10791087
Ok(_) => AsyncOutcome::Ok(0),
10801088
Err(e) => {
10811089
let _ = self.stop();
1082-
AsyncOutcome::Error(format!("cannot boot kernel: {:?}", e))
1090+
AsyncOutcome::Error(format!("Cannot start microvm: {:?}", e))
10831091
}
10841092
},
10851093
};
@@ -1364,6 +1372,7 @@ mod tests {
13641372
drive_id: String::from("root"),
13651373
path_on_host: f.path().to_path_buf(),
13661374
is_root_device: true,
1375+
partuuid: None,
13671376
is_read_only: false,
13681377
rate_limiter: None,
13691378
};
@@ -1382,6 +1391,7 @@ mod tests {
13821391
drive_id: String::from("root"),
13831392
path_on_host: f.path().to_path_buf(),
13841393
is_root_device: true,
1394+
partuuid: None,
13851395
is_read_only: true,
13861396
rate_limiter: None,
13871397
};

0 commit comments

Comments
 (0)