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 <chao.p.peng@linux.intel.com>
This commit is contained in:
Chao Peng 2019-05-10 07:27:56 +00:00 committed by Rob Bradford
parent 919226f31e
commit 6ecdd98634
6 changed files with 109 additions and 29 deletions

View File

@ -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,

View File

@ -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,
}
}
}

View File

@ -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<u64>,
refcount_table_offset: u64,

View File

@ -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<T: 'static + Copy + Default> {
vec: Box<[T]>,
@ -83,7 +83,7 @@ impl<T: 'static + Copy + Default> IndexMut<usize> for VecCache<T> {
}
}
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct CacheMap<T: Cacheable> {
capacity: usize,
map: HashMap<usize, T>,

93
vm-virtio/src/block.rs Normal file → Executable file
View File

@ -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<D: Read + Seek + Write + Clone> 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<usize> {
self.file.read(buf)
}
}
impl Seek for RawFile {
fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
self.file.seek(pos)
}
}
impl Write for RawFile {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
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<u64,
mem.read_obj(addr).map_err(Error::GuestMemory)
}
fn build_device_id(disk_image: &File) -> result::Result<String, Error> {
let blk_metadata = match disk_image.metadata() {
fn build_device_id(disk_path: &PathBuf) -> result::Result<String, Error> {
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<String, Error> {
Ok(device_id)
}
fn build_disk_image_id(disk_image: &File) -> Vec<u8> {
fn build_disk_image_id(disk_path: &PathBuf) -> Vec<u8> {
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<T: DiskFile> {
queues: Vec<Queue>,
mem: GuestMemoryMmap,
disk_image: File,
disk_image: T,
disk_nsectors: u64,
interrupt_status: Arc<AtomicUsize>,
interrupt_evt: EventFd,
disk_image_id: Vec<u8>,
}
impl BlockEpollHandler {
impl<T: DiskFile> BlockEpollHandler<T> {
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<T: DiskFile> {
kill_evt: Option<EventFd>,
disk_image: Option<File>,
disk_image: Option<T>,
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<u8> {
config
}
impl Block {
impl<T: DiskFile> Block<T> {
/// 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<Block> {
pub fn new(
mut disk_image: T,
disk_path: PathBuf,
is_disk_read_only: bool,
) -> io::Result<Block<T>> {
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<T: DiskFile> Drop for Block<T> {
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<T: 'static + DiskFile + Send> VirtioDevice for Block<T> {
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.

View File

@ -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<T> = result::Result<T, Error>;
@ -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<vm_virtio::VirtioDevice>
}
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<vm_virtio::VirtioDevice>
}
};
DeviceManager::add_virtio_pci_device(
Box::new(virtio_block_device),
block,
memory.clone(),
allocator,
vm_fd,