Skip to content

Commit

Permalink
zoned: support file-backed zoned
Browse files Browse the repository at this point in the history
'--path' passes this zoned device's work directory.

If 'superblock' file is found from this directory, parse zones parameter
and use zone files in this directory for populate the zoned device.

Otherwise, create 'superblock' file and store current zoned parameter to
this file, meantime create zone files.

It is one re-write of kernel module zloop[1] over ublk:

- store each zone into one file in the '--path' directory, so each
  seq-zone file size implies zone's write pointer

[1] https://lore.kernel.org/linux-block/[email protected]/

Signed-off-by: Ming Lei <[email protected]>
  • Loading branch information
ming1 committed Jan 11, 2025
1 parent a7945a9 commit 2345490
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 37 deletions.
158 changes: 137 additions & 21 deletions src/zoned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ use libublk::{ctrl::UblkCtrl, io::UblkDev, io::UblkIOCtx, io::UblkQueue};
use io_uring::{opcode, types};
use libc::{c_void, memset, pread, pwrite};
use log::trace;
use std::fs::File;
use std::fs::{File, OpenOptions};
use std::io::{BufRead, BufReader, Write};
use std::os::unix::io::AsRawFd;
use std::path::PathBuf;
use std::rc::Rc;
Expand Down Expand Up @@ -63,9 +64,71 @@ struct ZonedTgt {
}

impl ZonedTgt {
fn install_zone_files(&mut self, new_dev: bool) -> anyhow::Result<()> {
for i in 0..self.nr_zones as usize {
let path = self.back_file_path(i.try_into().unwrap())?;
let f = OpenOptions::new()
.create(new_dev)
.read(true)
.write(true)
.open(path)?;
unsafe {
libc::fcntl(f.as_raw_fd(), libc::F_SETFL, libc::O_DIRECT);
}
self.zfiles.push(f);
}
Ok(())
}

fn update_zones(&mut self, new_dev: bool) -> anyhow::Result<()> {
for i in 0..self.nr_zones as usize {
if i < self.zone_nr_conv.try_into().unwrap() {
let sz = self.back_file_size(i.try_into().unwrap())?;
if sz != self.zone_size {
if !new_dev {
return Err(anyhow::anyhow!("wrong conv zone size {}", sz));
} else {
self.back_file_truncate(i.try_into().unwrap(), self.zone_size)?;
}
}
} else {
let size = if new_dev {
self.back_file_truncate(i.try_into().unwrap(), 0)?;
0
} else {
self.back_file_size(i.try_into().unwrap())?
};
let size = (size >> 9) as u32;
let mut z = self.zones[i].write().unwrap();

if size == 0 {
z.cond = BLK_ZONE_COND_EMPTY;
z.wp = z.start;
} else if size == z.capacity {
z.cond = BLK_ZONE_COND_FULL;
z.wp = z.start + self.zone_size;
} else {
z.cond = BLK_ZONE_COND_CLOSED;
z.wp = z.start + (size as u64);

let mut data = self.data.write().unwrap();
data.nr_zones_closed += 1;
}
}
}

Ok(())
}

#[allow(clippy::uninit_vec)]
fn new(size: u64, zone_size: u64, conv_zones: u32) -> ZonedTgt {
let ram_backed = true;
fn new(
size: u64,
zone_size: u64,
conv_zones: u32,
base: Option<PathBuf>,
new_dev: bool,
) -> anyhow::Result<ZonedTgt> {
let ram_backed = base.is_none();
let _buf = if ram_backed {
Some(IoBuf::<u8>::new(size as usize))
} else {
Expand Down Expand Up @@ -99,7 +162,7 @@ impl ZonedTgt {
})
.collect();

ZonedTgt {
let mut dev = ZonedTgt {
_size: size,
start: buf_addr,
zone_size,
Expand All @@ -113,9 +176,16 @@ impl ZonedTgt {
zone_nr_conv: conv_zones,
zones,
_buf,
base: None,
base,
zfiles: Vec::new(),
};

if !ram_backed {
dev.install_zone_files(new_dev)?;
dev.update_zones(new_dev)?;
}

Ok(dev)
}

#[inline(always)]
Expand Down Expand Up @@ -179,7 +249,6 @@ impl ZonedTgt {
(data.nr_zones_exp_open, data.nr_zones_imp_open)
}

#[allow(dead_code)]
fn back_file_path(&self, zno: u32) -> anyhow::Result<PathBuf> {
match self.base {
Some(ref p) => {
Expand All @@ -196,7 +265,6 @@ impl ZonedTgt {
}
}

#[allow(dead_code)]
fn back_file_size(&self, zno: u32) -> anyhow::Result<u64> {
let fd = self.__get_zone_fd(zno as usize);
let mut file_stat: libc::stat = unsafe { std::mem::zeroed() };
Expand Down Expand Up @@ -810,8 +878,11 @@ pub(crate) struct ZonedAddArgs {
#[command(flatten)]
pub gen_arg: super::args::GenAddArgs,

///backing file of Zoned; So far, only None is allowed, and
///support ramdisk only
///work directory of file-backed Zoned, if `superblock` file exists,
///parse parameters from this file and setup zoned; otherwise, create
///it and store current parameters to this file
///
/// Default: ram backed zoned
#[clap(long, default_value = None)]
path: Option<PathBuf>,

Expand All @@ -828,27 +899,72 @@ pub(crate) struct ZonedAddArgs {
conv_zones: u32,
}

fn parse_zone_params(zo: &ZonedAddArgs) -> anyhow::Result<(u64, u64, u32, Option<PathBuf>, bool)> {
if zo.path.is_none() {
Ok((
((zo.size as u64) << 20),
((zo.zone_size as u64) << 20),
zo.conv_zones,
None,
true,
))
} else {
let path = zo.path.clone().ok_or(PathBuf::new()).expect("path failure");
let path = zo.gen_arg.build_abs_path(path);

if !path.exists() {
return Err(anyhow::anyhow!("base dir doesn't exist"));
}
let zf = path.join("superblock");
if zf.exists() {
//populate file-backed zoned from this superblock file
let file = File::open(zf)?;
let reader = BufReader::new(file);

for line in reader.lines() {
let line = line?; // Read each line
let num: Vec<u32> = line
.split_whitespace() // Split the line by whitespace
.map(|s| s.parse().expect("Failed to parse number")) // Parse each part as i32
.collect();

return Ok((
((num[0] as u64) << 20),
((num[1] as u64) << 20),
num[2],
Some(path),
false,
));
}
Err(anyhow::anyhow!("Not read superblock data"))
} else {
//create superblock file with passed parameter
let mut file = File::create(zf)?;

writeln!(file, "{} {} {}", zo.size, zo.zone_size, zo.conv_zones)?;
return Ok((
((zo.size as u64) << 20),
((zo.zone_size as u64) << 20),
zo.conv_zones,
Some(path),
true,
));
}
}
}

pub(crate) fn ublk_add_zoned(
ctrl: UblkCtrl,
opt: Option<ZonedAddArgs>,
comm_arc: &Arc<crate::DevIdComm>,
) -> anyhow::Result<i32> {
//It doesn't make sense to support recovery for zoned_ramdisk
let (size, zone_size, conv_zones) = match opt {
let (size, zone_size, conv_zones, dir, is_new) = match opt {
Some(ref o) => {
if o.gen_arg.user_recovery {
return Err(anyhow::anyhow!("zoned(ramdisk) can't support recovery\n"));
}

if o.path.is_some() {
return Err(anyhow::anyhow!("only support ramdisk now\n"));
} else {
(
((o.size as u64) << 20),
((o.zone_size as u64) << 20),
o.conv_zones,
)
}
parse_zone_params(o)?
}
None => return Err(anyhow::anyhow!("invalid parameter")),
};
Expand Down Expand Up @@ -880,7 +996,7 @@ pub(crate) fn ublk_add_zoned(
Ok(())
};

let zoned_tgt = Arc::new(ZonedTgt::new(size, zone_size, conv_zones));
let zoned_tgt = Arc::new(ZonedTgt::new(size, zone_size, conv_zones, dir, is_new)?);
let depth = ctrl.dev_info().queue_depth;

match ctrl.get_driver_features() {
Expand Down
49 changes: 33 additions & 16 deletions tests/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,24 +249,33 @@ mod integration {
}
}

fn __test_ublk_add_del_zoned<F>(bs: u32, tf: F)
fn __test_ublk_add_del_zoned<F>(bs: u32, tf: F, dir: Option<&String>)
where
F: Fn(i32, u32, usize),
{
match UblkCtrl::get_features() {
Some(f) => {
if (f & sys::UBLK_F_ZONED as u64) != 0 {
let id = run_rublk_add_dev(
[
"add",
"zoned",
"--zone-size",
"4",
"--logical-block-size",
&bs.to_string(),
]
.to_vec(),
);
let bs_str = format!("{}", bs).to_string();
let mut cmdline = [
"add",
"zoned",
"--zone-size",
"4",
"--logical-block-size",
&bs_str,
]
.to_vec();

match dir {
Some(d) => {
cmdline.push("--path");
cmdline.push(d);
}
_ => {}
};

let id = run_rublk_add_dev(cmdline);
tf(id, bs, 4 << 20);
run_rublk_del_dev(id);
}
Expand All @@ -288,8 +297,8 @@ mod integration {
read_ublk_disk(&ctrl);
check_block_size(&ctrl, bs);
};
__test_ublk_add_del_zoned(512, tf);
__test_ublk_add_del_zoned(4096, tf);
__test_ublk_add_del_zoned(512, tf, None);
__test_ublk_add_del_zoned(4096, tf, None);
}
}
None => {}
Expand Down Expand Up @@ -428,7 +437,8 @@ mod integration {
if !has_mkfs_btrfs() {
return;
}
__test_ublk_add_del_zoned(4096, |id, _bs, _file_size| {

let tf = |id: i32, _bs: u32, _file_size: usize| {
let ctrl = UblkCtrl::new_simple(id).unwrap();
let bdev = ctrl.get_bdev_path();
let tmp_dir = tempfile::TempDir::new().unwrap();
Expand Down Expand Up @@ -457,6 +467,13 @@ mod integration {
.status()
.expect("Failed to execute umount");
assert!(res.success());
});
};

__test_ublk_add_del_zoned(4096, tf, None);

let path_dir = tempfile::TempDir::new().unwrap();
let path_str = path_dir.path().to_string_lossy().to_string();

__test_ublk_add_del_zoned(4096, tf, Some(&path_str));
}
}

0 comments on commit 2345490

Please sign in to comment.