Skip to content

Commit

Permalink
Merge pull request #3 from ublk-org/next
Browse files Browse the repository at this point in the history
convert page_aligned_vec!() into Qcow2Iobuf
  • Loading branch information
ming1 authored Jan 17, 2024
2 parents 0b4ef52 + 779b5f4 commit 5aee7e2
Show file tree
Hide file tree
Showing 12 changed files with 214 additions and 112 deletions.
31 changes: 30 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ env:
CARGO_TERM_COLOR: always

jobs:
build:
build_linux:
name: Rust project - latest
runs-on: ubuntu-latest
strategy:
Expand All @@ -23,3 +23,32 @@ jobs:
- run: cargo build --verbose
- run: cargo test -- --nocapture
- run: cargo test -r

build_win:
name: Rust project - latest
runs-on: windows-latest
strategy:
matrix:
toolchain:
- stable
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Chocolatey
run: |
$qemuZipPath = Join-Path $env:USERPROFILE 'qemu.zip'
Invoke-WebRequest -Uri 'https://cloudbase.it/downloads/qemu-img-win-x64-2_3_0.zip' -OutFile $qemuZipPath
Expand-Archive -Path $qemuZipPath -DestinationPath $env:USERPROFILE
- run: |
$env:PATH = "$env:USERPROFILE;$env:PATH"
qemu-img --help
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
- run: cargo build --verbose
- name: Rust test(debug)
run: |
$env:PATH = "$env:USERPROFILE;$env:PATH"
cargo test -- --nocapture
- name: Rust test(release)
run: |
$env:PATH = "$env:USERPROFILE;$env:PATH"
cargo test -r
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ host cluster leak, format qcow2 image, ....
.await
.unwrap();

let mut buf = qcow2_rs::page_aligned_vec!(u8, 4096);
let mut buf = qcow2_rs::helpers::Qcow2IoBuf::<u8>::new(4096);

// read 4096 bytes to `buf` from virt offset 0 of `test.qcow2`
let _ = dev.read_at(&mut buf, 0).await.unwrap();
Expand Down
19 changes: 10 additions & 9 deletions src/dev.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::cache::AsyncLruCache;
use crate::cache::AsyncLruCacheEntry;
use crate::error::Qcow2Result;
use crate::helpers::qcow2_type_of;
use crate::helpers::{qcow2_type_of, Qcow2IoBuf};
use crate::meta::{
L1Entry, L1Table, L2Entry, L2Table, Mapping, MappingSource, Qcow2Header, RefBlock, RefTable,
RefTableEntry, SplitGuestOffset, Table, TableEntry,
Expand Down Expand Up @@ -500,10 +500,10 @@ impl<T: Qcow2IoOps> Qcow2Dev<T> {
let res = self.file.fallocate(offset, len, flags).await;
match res {
Err(_) => {
let mut zero_data = crate::page_aligned_vec!(u8, len);
zero_buf!(zero_data);
let mut zero_data = Qcow2IoBuf::<u8>::new(len);

log::trace!("discard fallback off {:x} len {}", offset, len);
zero_data.zero_buf();
self.call_write(offset, &zero_data).await
}
Ok(_) => Ok(()),
Expand Down Expand Up @@ -1537,7 +1537,7 @@ impl<T: Qcow2IoOps> Qcow2Dev<T> {
let pad = (compressed_offset - aligned_off) as usize;
let aligned_len = (pad + compressed_length + bs - 1) & (bs_mask as usize);

let mut _compressed_data = crate::page_aligned_vec!(u8, aligned_len);
let mut _compressed_data = Qcow2IoBuf::<u8>::new(aligned_len);
let res = self.call_read(aligned_off, &mut _compressed_data).await?;
if res != aligned_len {
return Err("do_read_compressed: short read compressed data".into());
Expand Down Expand Up @@ -1880,7 +1880,7 @@ impl<T: Qcow2IoOps> Qcow2Dev<T> {
host_off: u64,
compressed_mapping: &Mapping,
) -> Qcow2Result<()> {
let mut cbuf = crate::page_aligned_vec!(u8, self.info.cluster_size());
let mut cbuf = Qcow2IoBuf::<u8>::new(self.info.cluster_size());

// copy & write
self.do_read_compressed(compressed_mapping.clone(), 0, &mut cbuf)
Expand All @@ -1899,7 +1899,7 @@ impl<T: Qcow2IoOps> Qcow2Dev<T> {
) -> Qcow2Result<()> {
match self.backing_file.as_ref() {
Some(backing) => {
let mut cbuf = crate::page_aligned_vec!(u8, self.info.cluster_size());
let mut cbuf = Qcow2IoBuf::<u8>::new(self.info.cluster_size());

// copy & write
backing
Expand Down Expand Up @@ -2781,9 +2781,10 @@ impl<T: Qcow2IoOps> Qcow2Dev<T> {
#[cfg(test)]
mod tests {
use crate::dev::*;
use crate::helpers::Qcow2IoBuf;
use crate::qcow2_default_params;
use crate::tokio_io::Qcow2IoTokio;
use crate::utils::qcow2_setup_dev_tokio;
use crate::{page_aligned_vec, qcow2_default_params};
use std::path::PathBuf;
use tokio::runtime::Runtime;
use utilities::*;
Expand Down Expand Up @@ -2909,7 +2910,7 @@ mod tests {
let size = 64_u64 << 20;
let img_file = make_temp_qcow2_img(size, 16, 4);
let io = Qcow2IoTokio::new(&img_file.path().to_path_buf(), true, false).await;
let mut buf = page_aligned_vec!(u8, 4096);
let mut buf = Qcow2IoBuf::<u8>::new(4096);
let _ = io.read_to(0, &mut buf).await;
let header = Qcow2Header::from_buf(&buf).unwrap();

Expand All @@ -2929,7 +2930,7 @@ mod tests {
let params = qcow2_default_params!(true, false);
let dev = qcow2_setup_dev_tokio(&path, &params).await.unwrap();

let mut buf = page_aligned_vec!(u8, 1 << cluster_bits);
let mut buf = Qcow2IoBuf::<u8>::new(1 << cluster_bits);
dev.read_at(&mut buf, 0).await.unwrap();
});
}
Expand Down
103 changes: 89 additions & 14 deletions src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#![allow(dead_code)]
use crate::error::Qcow2Result;
use core::future::Future;
use std::ops::{Deref, DerefMut};
use std::pin::Pin;

#[macro_export]
Expand Down Expand Up @@ -88,22 +89,96 @@ impl_int_alignment_for_primitive!(usize);
pub type BoxedFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;
pub type Qcow2FutureResult<'a, T> = BoxedFuture<'a, Qcow2Result<T>>;

#[macro_export]
macro_rules! page_aligned_vec {
($type:ty, $size:expr) => {{
#[repr(C, align(4096))]
#[derive(Clone)]
struct PageAlignedBuf([u8; 512]);

let sz = ($size + 511) & !511;
let nr = sz / 512;
let buf = Vec::<PageAlignedBuf>::with_capacity(nr);
/// Slice like buffer, which address is aligned with 4096.
///
pub struct Qcow2IoBuf<T> {
ptr: *mut T,
size: usize,
}

// Users of Qcow2IoBuf has to deal with Send & Sync
unsafe impl<T> Send for Qcow2IoBuf<T> {}
unsafe impl<T> Sync for Qcow2IoBuf<T> {}

impl<'a, T> Qcow2IoBuf<T> {
pub fn new(size: usize) -> Self {
let layout = std::alloc::Layout::from_size_align(size, 4096).unwrap();
let ptr = unsafe { std::alloc::alloc(layout) } as *mut T;

assert!(size != 0);

Qcow2IoBuf { ptr, size }
}

/// how many elements in this buffer
pub fn len(&self) -> usize {
let elem_size = core::mem::size_of::<T>();
self.size / elem_size
}

/// Return raw address of this buffer
pub fn as_ptr(&self) -> *const T {
self.ptr
}

/// Return mutable raw address of this buffer
pub fn as_mut_ptr(&self) -> *mut T {
self.ptr
}

/// slice with u8 element, only for RefBlock
pub(crate) fn as_u8_slice(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.ptr as *const u8, self.size) }
}

/// mutable slice with u8 element, only for RefBlock
pub(crate) fn as_u8_slice_mut(&mut self) -> &mut [u8] {
unsafe { std::slice::from_raw_parts_mut(self.ptr as *mut u8, self.size) }
}

/// fill zero for every bits of this buffer
pub fn zero_buf(&mut self) {
unsafe {
let mut a: Vec<$type> = std::mem::transmute(buf);
a.set_len(sz / core::mem::size_of::<$type>());
a
std::ptr::write_bytes(self.as_mut_ptr(), 0, self.len());
}
}};
}
}

impl<T> std::fmt::Debug for Qcow2IoBuf<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"ptr {:?} size {} element type {}",
self.ptr,
self.size,
qcow2_type_of(unsafe { &*self.ptr })
)
}
}

/// Slice reference of this buffer
impl<T> Deref for Qcow2IoBuf<T> {
type Target = [T];
fn deref(&self) -> &[T] {
let elem_size = core::mem::size_of::<T>();
unsafe { std::slice::from_raw_parts(self.ptr, self.size / elem_size) }
}
}

/// Mutable slice reference of this buffer
impl<T> DerefMut for Qcow2IoBuf<T> {
fn deref_mut(&mut self) -> &mut [T] {
let elem_size = core::mem::size_of::<T>();
unsafe { std::slice::from_raw_parts_mut(self.ptr, self.size / elem_size) }
}
}

/// Free buffer with same alloc layout
impl<T> Drop for Qcow2IoBuf<T> {
fn drop(&mut self) {
let layout = std::alloc::Layout::from_size_align(self.size, 4096).unwrap();
unsafe { std::alloc::dealloc(self.ptr as *mut u8, layout) };
}
}

/// It is user's responsibility to not free buffer of the slice
Expand Down
6 changes: 3 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ use clap::{Args, Parser, Subcommand};
use clap_num::maybe_hex;
use qcow2_rs::dev::{Qcow2DevParams, Qcow2Info};
use qcow2_rs::error::Qcow2Result;
use qcow2_rs::helpers::Qcow2IoBuf;
use qcow2_rs::meta::{
L1Table, L2Table, Qcow2FeatureType, Qcow2Header, RefBlock, RefTable, Table, TableEntry,
};
use qcow2_rs::page_aligned_vec;
use qcow2_rs::utils::qcow2_setup_dev_tokio;
use std::io::{Read, Seek, SeekFrom, Write};
use std::path::PathBuf;
Expand Down Expand Up @@ -620,7 +620,7 @@ fn read_qcow2(args: ReadArgs) -> Qcow2Result<()> {
eprintln!("unaligned offset {:x} or len {:x}", args.start, len);
}

let mut buf = page_aligned_vec!(u8, len);
let mut buf = Qcow2IoBuf::<u8>::new(len);

dev.read_at(&mut buf, args.start)
.await
Expand Down Expand Up @@ -648,7 +648,7 @@ fn write_qcow2(args: WriteArgs) -> Qcow2Result<()> {
eprintln!("unaligned offset {:x} or len {:x}", args.start, len);
}

let mut buf = page_aligned_vec!(u8, len);
let mut buf = Qcow2IoBuf::<u8>::new(len);

let pattern = (0..=15).cycle().take(buf.len());
for (elem, pattern_element) in buf.iter_mut().zip(pattern) {
Expand Down
Loading

0 comments on commit 5aee7e2

Please sign in to comment.