mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-12-22 21:55:20 +00:00
block: qcow: 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.
This commit is based on crosvm implementation:
5ad3bc3459
Signed-off-by: Yu Li <liyu.yukiteru@bytedance.com>
This commit is contained in:
parent
e98ea329bf
commit
5ffc6d6107
@ -1106,7 +1106,7 @@ impl QcowFile {
|
|||||||
let l2_table = if l2_addr_disk == 0 {
|
let l2_table = if l2_addr_disk == 0 {
|
||||||
// Allocate a new cluster to store the L2 table and update the L1 table to point
|
// Allocate a new cluster to store the L2 table and update the L1 table to point
|
||||||
// to the new table.
|
// 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.
|
// The cluster refcount starts at one meaning it is used but doesn't need COW.
|
||||||
set_refcounts.push((new_addr, 1));
|
set_refcounts.push((new_addr, 1));
|
||||||
self.l1_table[l1_index] = new_addr;
|
self.l1_table[l1_index] = new_addr;
|
||||||
@ -1127,8 +1127,18 @@ impl QcowFile {
|
|||||||
|
|
||||||
let cluster_addr = match self.l2_cache.get(l1_index).unwrap()[l2_index] {
|
let cluster_addr = match self.l2_cache.get(l1_index).unwrap()[l2_index] {
|
||||||
0 => {
|
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];
|
||||||
|
backing.seek(SeekFrom::Start(cluster_begin))?;
|
||||||
|
backing.read_exact(&mut cluster_data)?;
|
||||||
|
Some(cluster_data)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
// Need to allocate a data cluster
|
// 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)?;
|
self.update_cluster_addr(l1_index, l2_index, cluster_addr, &mut set_refcounts)?;
|
||||||
cluster_addr
|
cluster_addr
|
||||||
}
|
}
|
||||||
@ -1165,7 +1175,7 @@ impl QcowFile {
|
|||||||
// Allocate a new cluster to store the L2 table and update the L1 table to point
|
// 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
|
// to the new table. The cluster will be written when the cache is flushed, no
|
||||||
// need to copy the data now.
|
// 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
|
// The cluster refcount starts at one indicating it is used but doesn't need
|
||||||
// COW.
|
// COW.
|
||||||
set_refcounts.push((new_addr, 1));
|
set_refcounts.push((new_addr, 1));
|
||||||
@ -1177,15 +1187,22 @@ impl QcowFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Allocate a new cluster and return its offset within the raw file.
|
// 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.
|
// First use a pre allocated cluster if one is available.
|
||||||
if let Some(free_cluster) = self.avail_clusters.pop() {
|
if let Some(free_cluster) = self.avail_clusters.pop() {
|
||||||
|
if let Some(initial_data) = initial_data {
|
||||||
|
self.raw_file.write_cluster(free_cluster, initial_data)?;
|
||||||
|
} else {
|
||||||
self.raw_file.zero_cluster(free_cluster)?;
|
self.raw_file.zero_cluster(free_cluster)?;
|
||||||
|
}
|
||||||
return Ok(free_cluster);
|
return Ok(free_cluster);
|
||||||
}
|
}
|
||||||
|
|
||||||
let max_valid_cluster_offset = self.refcounts.max_valid_cluster_offset();
|
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(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)
|
Ok(new_cluster)
|
||||||
} else {
|
} else {
|
||||||
error!("No free clusters in get_new_cluster()");
|
error!("No free clusters in get_new_cluster()");
|
||||||
@ -1195,8 +1212,8 @@ impl QcowFile {
|
|||||||
|
|
||||||
// Allocate and initialize a new data cluster. Returns the offset of the
|
// Allocate and initialize a new data cluster. Returns the offset of the
|
||||||
// cluster in to the file on success.
|
// cluster in to the file on success.
|
||||||
fn append_data_cluster(&mut self) -> std::io::Result<u64> {
|
fn append_data_cluster(&mut self, initial_data: Option<Vec<u8>>) -> std::io::Result<u64> {
|
||||||
let new_addr: u64 = self.get_new_cluster()?;
|
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.
|
// 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)?;
|
let mut newly_unref = self.set_cluster_refcount(new_addr, 1)?;
|
||||||
self.unref_clusters.append(&mut newly_unref);
|
self.unref_clusters.append(&mut newly_unref);
|
||||||
@ -1430,7 +1447,7 @@ impl QcowFile {
|
|||||||
}
|
}
|
||||||
Err(refcount::Error::NeedNewCluster) => {
|
Err(refcount::Error::NeedNewCluster) => {
|
||||||
// Allocate the cluster and call set_cluster_refcount again.
|
// 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);
|
added_clusters.push(addr);
|
||||||
new_cluster = Some((
|
new_cluster = Some((
|
||||||
addr,
|
addr,
|
||||||
@ -2119,6 +2136,26 @@ mod tests {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn write_read_start_backing_overlap() {
|
||||||
|
let disk_file = basic_file(&valid_header_v3());
|
||||||
|
let mut backing = QcowFile::from(disk_file).unwrap();
|
||||||
|
backing
|
||||||
|
.write_all(b"test first bytes")
|
||||||
|
.expect("Failed to write test string.");
|
||||||
|
let wrapping_disk_file = basic_file(&valid_header_v3());
|
||||||
|
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_all(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_exact(&mut buf).expect("Failed to read.");
|
||||||
|
assert_eq!(&buf, b"TEST first");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn offset_write_read() {
|
fn offset_write_read() {
|
||||||
with_basic_file(&valid_header_v3(), |disk_file: RawFile| {
|
with_basic_file(&valid_header_v3(), |disk_file: RawFile| {
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
use super::RawFile;
|
use super::RawFile;
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use std::io::{self, BufWriter, Seek, SeekFrom};
|
use std::io::{self, BufWriter, Seek, SeekFrom, Write};
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use vmm_sys_util::write_zeroes::WriteZeroes;
|
use vmm_sys_util::write_zeroes::WriteZeroes;
|
||||||
|
|
||||||
@ -137,6 +137,13 @@ impl QcowRawFile {
|
|||||||
self.file.write_zeroes(cluster_size)?;
|
self.file.write_zeroes(cluster_size)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Writes
|
||||||
|
pub fn write_cluster(&mut self, address: u64, data: Vec<u8>) -> io::Result<()> {
|
||||||
|
let cluster_size = self.cluster_size as usize;
|
||||||
|
self.file.seek(SeekFrom::Start(address))?;
|
||||||
|
self.file.write_all(&data[0..cluster_size])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for QcowRawFile {
|
impl Clone for QcowRawFile {
|
||||||
|
Loading…
Reference in New Issue
Block a user