diff options
author | A. Cody Schuffelen <schuffelen@google.com> | 2019-12-26 13:05:10 -0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-02-25 20:28:07 +0000 |
commit | 5ad3bc345904b252efd6dd2ef4853f5ee06ae3c5 (patch) | |
tree | 16b281f8be0f7daa06cd8b98937e8395ab589f2a /disk | |
parent | 0275efb3a00b2cbe15ec92a314bf163a3ca1433e (diff) | |
download | crosvm-5ad3bc345904b252efd6dd2ef4853f5ee06ae3c5.tar crosvm-5ad3bc345904b252efd6dd2ef4853f5ee06ae3c5.tar.gz crosvm-5ad3bc345904b252efd6dd2ef4853f5ee06ae3c5.tar.bz2 crosvm-5ad3bc345904b252efd6dd2ef4853f5ee06ae3c5.tar.lz crosvm-5ad3bc345904b252efd6dd2ef4853f5ee06ae3c5.tar.xz crosvm-5ad3bc345904b252efd6dd2ef4853f5ee06ae3c5.tar.zst crosvm-5ad3bc345904b252efd6dd2ef4853f5ee06ae3c5.zip |
Initialize qcow clusters off the backing file when present
This preserves any data that the backing file had on a cluster when doing a write to a subset of that cluster. These writes cause a performance penalty on creating new clusters if a backing file is present. TEST=unit tests BUG=b:140069322 Change-Id: I724990225617c05e5f2dea39e39ce84c940328fc Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1982832 Tested-by: kokoro <noreply+kokoro@google.com> Commit-Queue: Cody Schuffelen <schuffelen@google.com> Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Diffstat (limited to 'disk')
-rw-r--r-- | disk/src/qcow/mod.rs | 54 | ||||
-rw-r--r-- | disk/src/qcow/qcow_raw_file.rs | 12 |
2 files changed, 57 insertions, 9 deletions
diff --git a/disk/src/qcow/mod.rs b/disk/src/qcow/mod.rs index dfc8acd..c5e119d 100644 --- a/disk/src/qcow/mod.rs +++ b/disk/src/qcow/mod.rs @@ -1065,7 +1065,7 @@ impl QcowFile { let l2_table = if l2_addr_disk == 0 { // Allocate a new cluster to store the L2 table and update the L1 table to point // to the new table. - let new_addr: u64 = self.get_new_cluster()?; + let new_addr: u64 = self.get_new_cluster(None)?; // The cluster refcount starts at one meaning it is used but doesn't need COW. set_refcounts.push((new_addr, 1)); self.l1_table[l1_index] = new_addr; @@ -1086,8 +1086,19 @@ impl QcowFile { let cluster_addr = match self.l2_cache.get(&l1_index).unwrap()[l2_index] { 0 => { + let initial_data = if let Some(backing) = self.backing_file.as_mut() { + let cluster_size = self.raw_file.cluster_size(); + let cluster_begin = address - (address % cluster_size); + let mut cluster_data = vec![0u8; cluster_size as usize]; + let raw_slice = cluster_data.as_mut_slice(); + let volatile_slice = raw_slice.get_slice(0, cluster_size).unwrap(); + backing.read_exact_at_volatile(volatile_slice, cluster_begin)?; + Some(cluster_data) + } else { + None + }; // Need to allocate a data cluster - let cluster_addr = self.append_data_cluster()?; + let cluster_addr = self.append_data_cluster(initial_data)?; self.update_cluster_addr(l1_index, l2_index, cluster_addr, &mut set_refcounts)?; cluster_addr } @@ -1124,7 +1135,7 @@ impl QcowFile { // Allocate a new cluster to store the L2 table and update the L1 table to point // to the new table. The cluster will be written when the cache is flushed, no // need to copy the data now. - let new_addr: u64 = self.get_new_cluster()?; + let new_addr: u64 = self.get_new_cluster(None)?; // The cluster refcount starts at one indicating it is used but doesn't need // COW. set_refcounts.push((new_addr, 1)); @@ -1136,15 +1147,22 @@ impl QcowFile { } // Allocate a new cluster and return its offset within the raw file. - fn get_new_cluster(&mut self) -> std::io::Result<u64> { + fn get_new_cluster(&mut self, initial_data: Option<Vec<u8>>) -> std::io::Result<u64> { // First use a pre allocated cluster if one is available. if let Some(free_cluster) = self.avail_clusters.pop() { - self.raw_file.zero_cluster(free_cluster)?; + if let Some(initial_data) = initial_data { + self.raw_file.write_cluster(free_cluster, initial_data)?; + } else { + self.raw_file.zero_cluster(free_cluster)?; + } return Ok(free_cluster); } let max_valid_cluster_offset = self.refcounts.max_valid_cluster_offset(); if let Some(new_cluster) = self.raw_file.add_cluster_end(max_valid_cluster_offset)? { + if let Some(initial_data) = initial_data { + self.raw_file.write_cluster(new_cluster, initial_data)?; + } Ok(new_cluster) } else { error!("No free clusters in get_new_cluster()"); @@ -1154,8 +1172,8 @@ impl QcowFile { // Allocate and initialize a new data cluster. Returns the offset of the // cluster in to the file on success. - fn append_data_cluster(&mut self) -> std::io::Result<u64> { - let new_addr: u64 = self.get_new_cluster()?; + fn append_data_cluster(&mut self, initial_data: Option<Vec<u8>>) -> std::io::Result<u64> { + let new_addr: u64 = self.get_new_cluster(initial_data)?; // The cluster refcount starts at one indicating it is used but doesn't need COW. let mut newly_unref = self.set_cluster_refcount(new_addr, 1)?; self.unref_clusters.append(&mut newly_unref); @@ -1386,7 +1404,7 @@ impl QcowFile { } Err(refcount::Error::NeedNewCluster) => { // Allocate the cluster and call set_cluster_refcount again. - let addr = self.get_new_cluster()?; + let addr = self.get_new_cluster(None)?; added_clusters.push(addr); new_cluster = Some(( addr, @@ -1996,6 +2014,26 @@ mod tests { } #[test] + fn write_read_start_backing_overlap() { + let disk_file = basic_file(&valid_header()); + let mut backing = QcowFile::from(disk_file).unwrap(); + backing + .write(b"test first bytes") + .expect("Failed to write test string."); + let wrapping_disk_file = basic_file(&valid_header()); + let mut wrapping = QcowFile::from(wrapping_disk_file).unwrap(); + wrapping.set_backing_file(Some(Box::new(backing))); + wrapping.seek(SeekFrom::Start(0)).expect("Failed to seek."); + wrapping + .write(b"TEST") + .expect("Failed to write second test string."); + let mut buf = [0u8; 10]; + wrapping.seek(SeekFrom::Start(0)).expect("Failed to seek."); + wrapping.read(&mut buf).expect("Failed to read."); + assert_eq!(&buf, b"TEST first"); + } + + #[test] fn offset_write_read() { with_basic_file(&valid_header(), |disk_file: File| { let mut q = QcowFile::from(disk_file).unwrap(); diff --git a/disk/src/qcow/qcow_raw_file.rs b/disk/src/qcow/qcow_raw_file.rs index ede28d8..09d2176 100644 --- a/disk/src/qcow/qcow_raw_file.rs +++ b/disk/src/qcow/qcow_raw_file.rs @@ -6,7 +6,8 @@ use std::fs::File; use std::io::{self, BufWriter, Read, Seek, SeekFrom, Write}; use std::mem::size_of; -use sys_util::WriteZeroes; +use data_model::VolatileMemory; +use sys_util::{FileReadWriteAtVolatile, WriteZeroes}; /// A qcow file. Allows reading/writing clusters and appending clusters. #[derive(Debug)] @@ -145,4 +146,13 @@ impl QcowRawFile { self.file.write_zeroes_all(cluster_size)?; Ok(()) } + + /// Writes + pub fn write_cluster(&mut self, address: u64, mut initial_data: Vec<u8>) -> io::Result<()> { + let raw_slice = initial_data.as_mut_slice(); + let volatile_slice = raw_slice + .get_slice(0, self.cluster_size) + .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{:?}", e)))?; + self.file.write_all_at_volatile(volatile_slice, address) + } } |