Support --pmem-ext2 option

BUG=b:329359333
TEST=run crosvm with `--pmem-ext2 /path/to/crosvm/ext2/src/`

Change-Id: Ifd655bd533f637d6c475494c74dbfd544edd0b70
Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/5439074
Commit-Queue: Keiichi Watanabe <keiichiw@chromium.org>
Reviewed-by: Junichi Uekawa <uekawa@chromium.org>
This commit is contained in:
Keiichi Watanabe 2024-04-05 19:52:35 +09:00 committed by crosvm LUCI
parent 28847f9303
commit c9f4ad0da9
7 changed files with 110 additions and 4 deletions

1
Cargo.lock generated
View file

@ -805,6 +805,7 @@ dependencies = [
"disk",
"document-features",
"enumn",
"ext2",
"futures",
"gdbstub",
"gdbstub_arch",

View file

@ -451,6 +451,7 @@ devices = { path = "devices" }
disk = { path = "disk" }
document-features = { version = "0.2", optional = true }
enumn = "0.1.0"
ext2 = { path = "ext2" }
gdbstub = { version = "0.7.0", optional = true }
gdbstub_arch = { version = "0.3.0", optional = true }
rutabaga_gfx = { path = "rutabaga_gfx"}

View file

@ -371,7 +371,7 @@ struct PmemSnapshot {
impl Pmem {
pub fn new(
base_features: u64,
disk_image: File,
disk_image: Option<File>,
mapping_address: GuestAddress,
mapping_arena_slot: MemSlot,
mapping_size: u64,
@ -391,7 +391,7 @@ impl Pmem {
Ok(Pmem {
worker_thread: None,
features: avail_features,
disk_image: Some(disk_image),
disk_image,
mapping_address,
mapping_arena_slot,
mapping_size,

View file

@ -94,6 +94,7 @@ use crate::crosvm::config::HypervisorKind;
use crate::crosvm::config::InputDeviceOption;
use crate::crosvm::config::IrqChipKind;
use crate::crosvm::config::MemOptions;
use crate::crosvm::config::PmemExt2Option;
use crate::crosvm::config::TouchDeviceOption;
use crate::crosvm::config::VhostUserFrontendOption;
use crate::crosvm::config::VhostUserFsOption;
@ -1837,6 +1838,12 @@ pub struct RunCommand {
/// path to a disk image
pmem_device: Vec<DiskOption>,
#[argh(option, arg_name = "PATH")]
#[serde(default)]
#[merge(strategy = append)]
/// (EXPERIMENTAL): construct an ext2 file system on a pmem device from the given directory
pub pmem_ext2: Vec<PmemExt2Option>,
#[cfg(feature = "process-invariants")]
#[argh(option, arg_name = "PATH")]
#[serde(skip)] // TODO(b/255223604)
@ -2891,6 +2898,8 @@ impl TryFrom<RunCommand> for super::config::Config {
}
}
cfg.pmem_ext2 = cmd.pmem_ext2;
#[cfg(feature = "pvclock")]
{
cfg.pvclock = cmd.pvclock.unwrap_or_default();

View file

@ -386,6 +386,12 @@ pub struct FileBackedMappingParameters {
pub align: bool,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, serde_keyvalue::FromKeyValues)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
pub struct PmemExt2Option {
pub path: PathBuf,
}
fn parse_hex_or_decimal(maybe_hex_string: &str) -> Result<u64, String> {
// Parse string starting with 0x as hex and others as numbers.
if let Some(hex_string) = maybe_hex_string.strip_prefix("0x") {
@ -795,6 +801,7 @@ pub struct Config {
#[cfg(feature = "plugin")]
pub plugin_mounts: Vec<crate::crosvm::plugin::BindMount>,
pub plugin_root: Option<PathBuf>,
pub pmem_ext2: Vec<PmemExt2Option>,
pub pmems: Vec<PmemOption>,
#[cfg(feature = "process-invariants")]
pub process_invariants_data_handle: Option<u64>,
@ -1016,6 +1023,7 @@ impl Default for Config {
#[cfg(feature = "plugin")]
plugin_mounts: Vec::new(),
plugin_root: None,
pmem_ext2: Vec::new(),
pmems: Vec::new(),
#[cfg(feature = "process-invariants")]
process_invariants_data_handle: None,
@ -2408,4 +2416,19 @@ mod tests {
.unwrap_err()
.contains("swap-interval parameter can only be set for writable pmem device"));
}
#[test]
fn parse_pmem_ext2() {
let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
&[],
&["--pmem-ext2", "/path/to/dir", "/dev/null"],
)
.unwrap()
.try_into()
.unwrap();
let opt = config.pmem_ext2.first().unwrap();
assert_eq!(opt.path, PathBuf::from("/path/to/dir"));
}
}

View file

@ -403,6 +403,19 @@ fn create_virtio_devices(
)?);
}
for (index, pmem_ext2) in cfg.pmem_ext2.iter().enumerate() {
let pmem_device_tube = pmem_device_tubes.remove(0);
devs.push(create_pmem_ext2_device(
cfg.protection_type,
&cfg.jail_config,
vm,
resources,
pmem_ext2,
index,
pmem_device_tube,
)?);
}
if cfg.rng {
devs.push(create_rng_device(cfg.protection_type, &cfg.jail_config)?);
}
@ -1803,7 +1816,7 @@ where
}
let mut pmem_device_tubes = Vec::new();
let pmem_count = cfg.pmems.len();
let pmem_count = cfg.pmems.len() + cfg.pmem_ext2.len();
for _ in 0..pmem_count {
let (pmem_host_tube, pmem_device_tube) = Tube::pair().context("failed to create tube")?;
pmem_device_tubes.push(pmem_device_tube);

View file

@ -88,6 +88,7 @@ use sync::Mutex;
use vm_control::api::VmMemoryClient;
use vm_memory::GuestAddress;
use crate::crosvm::config::PmemExt2Option;
use crate::crosvm::config::PmemOption;
use crate::crosvm::config::VhostUserFrontendOption;
use crate::crosvm::config::VhostUserFsOption;
@ -1169,7 +1170,7 @@ pub fn create_pmem_device(
let dev = virtio::Pmem::new(
virtio::base_features(protection_type),
fd,
Some(fd),
GuestAddress(mapping_address),
slot,
arena_size,
@ -1185,6 +1186,64 @@ pub fn create_pmem_device(
})
}
pub fn create_pmem_ext2_device(
protection_type: ProtectionType,
jail_config: &Option<JailConfig>,
vm: &mut impl Vm,
resources: &mut SystemAllocator,
opts: &PmemExt2Option,
index: usize,
pmem_device_tube: Tube,
) -> DeviceResult {
let cfg = ext2::Config {
inodes_per_group: 4096,
blocks_per_group: 1024,
};
let arena = ext2::create_ext2_region(&cfg, Some(opts.path.as_path()))?;
let arena_size = arena.size() as u64;
let mapping_address = resources
.allocate_mmio(
arena_size,
Alloc::PmemDevice(index),
format!("pmem_ext2_image_{}", index),
AllocOptions::new()
.top_down(true)
.prefetchable(true)
// 2MB alignment for DAX
// cf. https://docs.pmem.io/persistent-memory/getting-started-guide/creating-development-environments/linux-environments/advanced-topics/i-o-alignment-considerations#verifying-io-alignment
.align(2 * 1024 * 1024),
)
.context("failed to allocate memory for pmem device")?;
let slot = vm
.add_memory_region(
GuestAddress(mapping_address),
Box::new(arena),
/* read_only= */ true,
/* log_dirty_pages= */ false,
MemCacheType::CacheCoherent,
)
.context("failed to add pmem device memory")?;
let dev = virtio::Pmem::new(
virtio::base_features(protection_type),
None,
GuestAddress(mapping_address),
slot,
arena_size,
pmem_device_tube,
/* swap_interval= */ None,
/* mapping_writable= */ false,
)
.context("failed to create pmem device")?;
Ok(VirtioDeviceStub {
dev: Box::new(dev) as Box<dyn VirtioDevice>,
jail: simple_jail(jail_config, "pmem_device")?,
})
}
pub fn create_anonymous_file<P: AsRef<Path>>(path: P, size: u64) -> Result<File> {
let file_name = path
.as_ref()