From 6ecdd9863435aa4ad9514c6eeaf0aa939a686b88 Mon Sep 17 00:00:00 2001 From: Chao Peng Date: Fri, 10 May 2019 07:27:56 +0000 Subject: [PATCH] virtio: Enable qcow support for virtio-block With this enabled, one can pass a QCOW format disk image with '--disk' switch. Signed-off-by: Chao Peng --- qcow/src/qcow.rs | 2 +- qcow/src/qcow_raw_file.rs | 10 +++++ qcow/src/refcount.rs | 2 +- qcow/src/vec_cache.rs | 4 +- vm-virtio/src/block.rs | 93 ++++++++++++++++++++++++++++++--------- vmm/src/vm.rs | 27 ++++++++++-- 6 files changed, 109 insertions(+), 29 deletions(-) mode change 100644 => 100755 vm-virtio/src/block.rs diff --git a/qcow/src/qcow.rs b/qcow/src/qcow.rs index 084b87070..c6947816f 100755 --- a/qcow/src/qcow.rs +++ b/qcow/src/qcow.rs @@ -315,7 +315,7 @@ fn max_refcount_clusters(refcount_order: u32, cluster_size: u32, num_clusters: u /// # Ok(()) /// # } /// ``` -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct QcowFile { raw_file: QcowRawFile, header: QcowHeader, diff --git a/qcow/src/qcow_raw_file.rs b/qcow/src/qcow_raw_file.rs index eaef14bf1..dab57c5ff 100644 --- a/qcow/src/qcow_raw_file.rs +++ b/qcow/src/qcow_raw_file.rs @@ -134,3 +134,13 @@ impl QcowRawFile { address & self.cluster_mask } } + +impl Clone for QcowRawFile { + fn clone(&self) -> Self { + QcowRawFile { + file: self.file.try_clone().expect("QcowRawFile cloning failed"), + cluster_size: self.cluster_size, + cluster_mask: self.cluster_mask, + } + } +} diff --git a/qcow/src/refcount.rs b/qcow/src/refcount.rs index 1c82f72af..43937b7ae 100644 --- a/qcow/src/refcount.rs +++ b/qcow/src/refcount.rs @@ -48,7 +48,7 @@ impl Display for Error { } /// Represents the refcount entries for an open qcow file. -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct RefCount { ref_table: VecCache, refcount_table_offset: u64, diff --git a/qcow/src/vec_cache.rs b/qcow/src/vec_cache.rs index 668f2c71a..debf7657e 100644 --- a/qcow/src/vec_cache.rs +++ b/qcow/src/vec_cache.rs @@ -15,7 +15,7 @@ pub trait Cacheable { fn dirty(&self) -> bool; } -#[derive(Debug)] +#[derive(Clone, Debug)] /// Represents a vector that implements the `Cacheable` trait so it can be held in a cache. pub struct VecCache { vec: Box<[T]>, @@ -83,7 +83,7 @@ impl IndexMut for VecCache { } } -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct CacheMap { capacity: usize, map: HashMap, diff --git a/vm-virtio/src/block.rs b/vm-virtio/src/block.rs old mode 100644 new mode 100755 index 9089610de..681750166 --- a/vm-virtio/src/block.rs +++ b/vm-virtio/src/block.rs @@ -15,6 +15,7 @@ use std::fs::File; use std::io::{self, Read, Seek, SeekFrom, Write}; use std::os::linux::fs::MetadataExt; use std::os::unix::io::AsRawFd; +use std::path::PathBuf; use std::result; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; @@ -86,6 +87,49 @@ impl ExecuteError { } } +pub trait DiskFile: Read + Seek + Write + Clone {} +impl DiskFile for D {} + +pub struct RawFile { + file: File, +} + +impl RawFile { + pub fn new(file: File) -> Self { + RawFile { file } + } +} + +impl Read for RawFile { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.file.read(buf) + } +} + +impl Seek for RawFile { + fn seek(&mut self, pos: SeekFrom) -> std::io::Result { + self.file.seek(pos) + } +} + +impl Write for RawFile { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.file.write(buf) + } + + fn flush(&mut self) -> std::io::Result<()> { + self.file.flush() + } +} + +impl Clone for RawFile { + fn clone(&self) -> Self { + RawFile { + file: self.file.try_clone().expect("RawFile cloning failed"), + } + } +} + #[derive(Clone, Copy, Debug, PartialEq)] enum RequestType { In, @@ -119,8 +163,8 @@ fn sector(mem: &GuestMemoryMmap, desc_addr: GuestAddress) -> result::Result result::Result { - let blk_metadata = match disk_image.metadata() { +fn build_device_id(disk_path: &PathBuf) -> result::Result { + let blk_metadata = match disk_path.metadata() { Err(_) => return Err(Error::GetFileMetadata), Ok(m) => m, }; @@ -135,9 +179,9 @@ fn build_device_id(disk_image: &File) -> result::Result { Ok(device_id) } -fn build_disk_image_id(disk_image: &File) -> Vec { +fn build_disk_image_id(disk_path: &PathBuf) -> Vec { let mut default_disk_image_id = vec![0; VIRTIO_BLK_ID_BYTES as usize]; - match build_device_id(disk_image) { + match build_device_id(disk_path) { Err(_) => { warn!("Could not generate device id. We'll use a default."); } @@ -275,17 +319,17 @@ impl Request { } } -struct BlockEpollHandler { +struct BlockEpollHandler { queues: Vec, mem: GuestMemoryMmap, - disk_image: File, + disk_image: T, disk_nsectors: u64, interrupt_status: Arc, interrupt_evt: EventFd, disk_image_id: Vec, } -impl BlockEpollHandler { +impl BlockEpollHandler { fn process_queue(&mut self, queue_index: usize) -> bool { let queue = &mut self.queues[queue_index]; @@ -340,14 +384,18 @@ impl BlockEpollHandler { } #[allow(dead_code)] - fn update_disk_image(&mut self, disk_image: File) -> result::Result<(), DeviceError> { + fn update_disk_image( + &mut self, + disk_image: T, + disk_path: &PathBuf, + ) -> result::Result<(), DeviceError> { self.disk_image = disk_image; self.disk_nsectors = self .disk_image .seek(SeekFrom::End(0)) .map_err(DeviceError::IoError)? / SECTOR_SIZE; - self.disk_image_id = build_disk_image_id(&self.disk_image); + self.disk_image_id = build_disk_image_id(disk_path); Ok(()) } @@ -409,9 +457,10 @@ impl BlockEpollHandler { } /// Virtio device for exposing block level read/write operations on a host file. -pub struct Block { +pub struct Block { kill_evt: Option, - disk_image: Option, + disk_image: Option, + disk_path: PathBuf, disk_nsectors: u64, avail_features: u64, acked_features: u64, @@ -432,11 +481,15 @@ pub fn build_config_space(disk_size: u64) -> Vec { config } -impl Block { +impl Block { /// Create a new virtio block device that operates on the given file. /// /// The given file must be seekable and sizable. - pub fn new(mut disk_image: File, is_disk_read_only: bool) -> io::Result { + pub fn new( + mut disk_image: T, + disk_path: PathBuf, + is_disk_read_only: bool, + ) -> io::Result> { let disk_size = disk_image.seek(SeekFrom::End(0))? as u64; if disk_size % SECTOR_SIZE != 0 { warn!( @@ -455,6 +508,7 @@ impl Block { Ok(Block { kill_evt: None, disk_image: Some(disk_image), + disk_path, disk_nsectors: disk_size / SECTOR_SIZE, avail_features, acked_features: 0u64, @@ -465,7 +519,7 @@ impl Block { } } -impl Drop for Block { +impl Drop for Block { fn drop(&mut self) { if let Some(kill_evt) = self.kill_evt.take() { // Ignore the result because there is nothing we can do about it. @@ -474,7 +528,7 @@ impl Drop for Block { } } -impl VirtioDevice for Block { +impl VirtioDevice for Block { fn device_type(&self) -> u32 { VirtioDeviceType::TYPE_BLOCK as u32 } @@ -568,13 +622,8 @@ impl VirtioDevice for Block { }; self.kill_evt = Some(self_kill_evt); - if self.disk_image.is_some() { - let disk_image = self.disk_image.as_ref().unwrap().try_clone().map_err(|e| { - error!("failed to clone disk image: {}", e); - ActivateError::BadActivate - })?; - - let disk_image_id = build_disk_image_id(&disk_image); + if let Some(disk_image) = self.disk_image.clone() { + let disk_image_id = build_disk_image_id(&self.disk_path); // Save the interrupt EventFD as we need to return it on reset // but clone it to pass into the thread. diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index 3c369d7c3..5c79f508b 100755 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -28,6 +28,7 @@ use linux_loader::cmdline; use linux_loader::loader::KernelLoader; use net_util::{MacAddr, Tap}; use pci::{PciConfigIo, PciDevice, PciInterruptPin, PciRoot}; +use qcow::{self, ImageType, QcowFile}; use std::ffi::CString; use std::fs::{File, OpenOptions}; use std::io::{self, stdout}; @@ -170,6 +171,12 @@ pub enum Error { /// Cannot open tap interface OpenTap(net_util::TapError), + + /// Failed parsing disk image format + DetectImageType(qcow::Error), + + /// Cannot open qcow disk path + QcowDeviceCreate(qcow::Error), } pub type Result = result::Result; @@ -367,11 +374,25 @@ impl DeviceManager { .map_err(Error::Disk)?; // Add virtio-blk - let virtio_block_device = - vm_virtio::Block::new(raw_img, false).map_err(Error::CreateVirtioBlock)?; + let image_type = qcow::detect_image_type(&raw_img).map_err(Error::DetectImageType)?; + let disk_path = vm_cfg.disk_path.to_path_buf(); + let block = match image_type { + ImageType::Raw => { + let raw_img = vm_virtio::RawFile::new(raw_img); + let dev = vm_virtio::Block::new(raw_img, disk_path, false) + .map_err(Error::CreateVirtioBlock)?; + Box::new(dev) as Box + } + ImageType::Qcow2 => { + let qcow_img = QcowFile::from(raw_img).map_err(Error::QcowDeviceCreate)?; + let dev = vm_virtio::Block::new(qcow_img, disk_path, false) + .map_err(Error::CreateVirtioBlock)?; + Box::new(dev) as Box + } + }; DeviceManager::add_virtio_pci_device( - Box::new(virtio_block_device), + block, memory.clone(), allocator, vm_fd,