mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2025-01-22 04:25:21 +00:00
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:
parent
919226f31e
commit
6ecdd98634
@ -315,7 +315,7 @@ fn max_refcount_clusters(refcount_order: u32, cluster_size: u32, num_clusters: u
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct QcowFile {
|
pub struct QcowFile {
|
||||||
raw_file: QcowRawFile,
|
raw_file: QcowRawFile,
|
||||||
header: QcowHeader,
|
header: QcowHeader,
|
||||||
|
@ -134,3 +134,13 @@ impl QcowRawFile {
|
|||||||
address & self.cluster_mask
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -48,7 +48,7 @@ impl Display for Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Represents the refcount entries for an open qcow file.
|
/// Represents the refcount entries for an open qcow file.
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct RefCount {
|
pub struct RefCount {
|
||||||
ref_table: VecCache<u64>,
|
ref_table: VecCache<u64>,
|
||||||
refcount_table_offset: u64,
|
refcount_table_offset: u64,
|
||||||
|
@ -15,7 +15,7 @@ pub trait Cacheable {
|
|||||||
fn dirty(&self) -> bool;
|
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.
|
/// Represents a vector that implements the `Cacheable` trait so it can be held in a cache.
|
||||||
pub struct VecCache<T: 'static + Copy + Default> {
|
pub struct VecCache<T: 'static + Copy + Default> {
|
||||||
vec: Box<[T]>,
|
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> {
|
pub struct CacheMap<T: Cacheable> {
|
||||||
capacity: usize,
|
capacity: usize,
|
||||||
map: HashMap<usize, T>,
|
map: HashMap<usize, T>,
|
||||||
|
93
vm-virtio/src/block.rs
Normal file → Executable file
93
vm-virtio/src/block.rs
Normal file → Executable file
@ -15,6 +15,7 @@ use std::fs::File;
|
|||||||
use std::io::{self, Read, Seek, SeekFrom, Write};
|
use std::io::{self, Read, Seek, SeekFrom, Write};
|
||||||
use std::os::linux::fs::MetadataExt;
|
use std::os::linux::fs::MetadataExt;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::sync::Arc;
|
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)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
enum RequestType {
|
enum RequestType {
|
||||||
In,
|
In,
|
||||||
@ -119,8 +163,8 @@ fn sector(mem: &GuestMemoryMmap, desc_addr: GuestAddress) -> result::Result<u64,
|
|||||||
mem.read_obj(addr).map_err(Error::GuestMemory)
|
mem.read_obj(addr).map_err(Error::GuestMemory)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_device_id(disk_image: &File) -> result::Result<String, Error> {
|
fn build_device_id(disk_path: &PathBuf) -> result::Result<String, Error> {
|
||||||
let blk_metadata = match disk_image.metadata() {
|
let blk_metadata = match disk_path.metadata() {
|
||||||
Err(_) => return Err(Error::GetFileMetadata),
|
Err(_) => return Err(Error::GetFileMetadata),
|
||||||
Ok(m) => m,
|
Ok(m) => m,
|
||||||
};
|
};
|
||||||
@ -135,9 +179,9 @@ fn build_device_id(disk_image: &File) -> result::Result<String, Error> {
|
|||||||
Ok(device_id)
|
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];
|
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(_) => {
|
Err(_) => {
|
||||||
warn!("Could not generate device id. We'll use a default.");
|
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>,
|
queues: Vec<Queue>,
|
||||||
mem: GuestMemoryMmap,
|
mem: GuestMemoryMmap,
|
||||||
disk_image: File,
|
disk_image: T,
|
||||||
disk_nsectors: u64,
|
disk_nsectors: u64,
|
||||||
interrupt_status: Arc<AtomicUsize>,
|
interrupt_status: Arc<AtomicUsize>,
|
||||||
interrupt_evt: EventFd,
|
interrupt_evt: EventFd,
|
||||||
disk_image_id: Vec<u8>,
|
disk_image_id: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlockEpollHandler {
|
impl<T: DiskFile> BlockEpollHandler<T> {
|
||||||
fn process_queue(&mut self, queue_index: usize) -> bool {
|
fn process_queue(&mut self, queue_index: usize) -> bool {
|
||||||
let queue = &mut self.queues[queue_index];
|
let queue = &mut self.queues[queue_index];
|
||||||
|
|
||||||
@ -340,14 +384,18 @@ impl BlockEpollHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[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_image = disk_image;
|
||||||
self.disk_nsectors = self
|
self.disk_nsectors = self
|
||||||
.disk_image
|
.disk_image
|
||||||
.seek(SeekFrom::End(0))
|
.seek(SeekFrom::End(0))
|
||||||
.map_err(DeviceError::IoError)?
|
.map_err(DeviceError::IoError)?
|
||||||
/ SECTOR_SIZE;
|
/ SECTOR_SIZE;
|
||||||
self.disk_image_id = build_disk_image_id(&self.disk_image);
|
self.disk_image_id = build_disk_image_id(disk_path);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -409,9 +457,10 @@ impl BlockEpollHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Virtio device for exposing block level read/write operations on a host file.
|
/// 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>,
|
kill_evt: Option<EventFd>,
|
||||||
disk_image: Option<File>,
|
disk_image: Option<T>,
|
||||||
|
disk_path: PathBuf,
|
||||||
disk_nsectors: u64,
|
disk_nsectors: u64,
|
||||||
avail_features: u64,
|
avail_features: u64,
|
||||||
acked_features: u64,
|
acked_features: u64,
|
||||||
@ -432,11 +481,15 @@ pub fn build_config_space(disk_size: u64) -> Vec<u8> {
|
|||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Block {
|
impl<T: DiskFile> Block<T> {
|
||||||
/// Create a new virtio block device that operates on the given file.
|
/// Create a new virtio block device that operates on the given file.
|
||||||
///
|
///
|
||||||
/// The given file must be seekable and sizable.
|
/// 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;
|
let disk_size = disk_image.seek(SeekFrom::End(0))? as u64;
|
||||||
if disk_size % SECTOR_SIZE != 0 {
|
if disk_size % SECTOR_SIZE != 0 {
|
||||||
warn!(
|
warn!(
|
||||||
@ -455,6 +508,7 @@ impl Block {
|
|||||||
Ok(Block {
|
Ok(Block {
|
||||||
kill_evt: None,
|
kill_evt: None,
|
||||||
disk_image: Some(disk_image),
|
disk_image: Some(disk_image),
|
||||||
|
disk_path,
|
||||||
disk_nsectors: disk_size / SECTOR_SIZE,
|
disk_nsectors: disk_size / SECTOR_SIZE,
|
||||||
avail_features,
|
avail_features,
|
||||||
acked_features: 0u64,
|
acked_features: 0u64,
|
||||||
@ -465,7 +519,7 @@ impl Block {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Block {
|
impl<T: DiskFile> Drop for Block<T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if let Some(kill_evt) = self.kill_evt.take() {
|
if let Some(kill_evt) = self.kill_evt.take() {
|
||||||
// Ignore the result because there is nothing we can do about it.
|
// 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 {
|
fn device_type(&self) -> u32 {
|
||||||
VirtioDeviceType::TYPE_BLOCK as u32
|
VirtioDeviceType::TYPE_BLOCK as u32
|
||||||
}
|
}
|
||||||
@ -568,13 +622,8 @@ impl VirtioDevice for Block {
|
|||||||
};
|
};
|
||||||
self.kill_evt = Some(self_kill_evt);
|
self.kill_evt = Some(self_kill_evt);
|
||||||
|
|
||||||
if self.disk_image.is_some() {
|
if let Some(disk_image) = self.disk_image.clone() {
|
||||||
let disk_image = self.disk_image.as_ref().unwrap().try_clone().map_err(|e| {
|
let disk_image_id = build_disk_image_id(&self.disk_path);
|
||||||
error!("failed to clone disk image: {}", e);
|
|
||||||
ActivateError::BadActivate
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let disk_image_id = build_disk_image_id(&disk_image);
|
|
||||||
|
|
||||||
// Save the interrupt EventFD as we need to return it on reset
|
// Save the interrupt EventFD as we need to return it on reset
|
||||||
// but clone it to pass into the thread.
|
// but clone it to pass into the thread.
|
||||||
|
@ -28,6 +28,7 @@ use linux_loader::cmdline;
|
|||||||
use linux_loader::loader::KernelLoader;
|
use linux_loader::loader::KernelLoader;
|
||||||
use net_util::{MacAddr, Tap};
|
use net_util::{MacAddr, Tap};
|
||||||
use pci::{PciConfigIo, PciDevice, PciInterruptPin, PciRoot};
|
use pci::{PciConfigIo, PciDevice, PciInterruptPin, PciRoot};
|
||||||
|
use qcow::{self, ImageType, QcowFile};
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::fs::{File, OpenOptions};
|
use std::fs::{File, OpenOptions};
|
||||||
use std::io::{self, stdout};
|
use std::io::{self, stdout};
|
||||||
@ -170,6 +171,12 @@ pub enum Error {
|
|||||||
|
|
||||||
/// Cannot open tap interface
|
/// Cannot open tap interface
|
||||||
OpenTap(net_util::TapError),
|
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>;
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
@ -367,11 +374,25 @@ impl DeviceManager {
|
|||||||
.map_err(Error::Disk)?;
|
.map_err(Error::Disk)?;
|
||||||
|
|
||||||
// Add virtio-blk
|
// Add virtio-blk
|
||||||
let virtio_block_device =
|
let image_type = qcow::detect_image_type(&raw_img).map_err(Error::DetectImageType)?;
|
||||||
vm_virtio::Block::new(raw_img, false).map_err(Error::CreateVirtioBlock)?;
|
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(
|
DeviceManager::add_virtio_pci_device(
|
||||||
Box::new(virtio_block_device),
|
block,
|
||||||
memory.clone(),
|
memory.clone(),
|
||||||
allocator,
|
allocator,
|
||||||
vm_fd,
|
vm_fd,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user