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 12, 2025
1 parent bd5c5f7 commit 1621dd0
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 32 deletions.
132 changes: 116 additions & 16 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 @@ -112,6 +113,19 @@ impl TgtCfg {
path,
)
}
fn from_file(p: &PathBuf) -> anyhow::Result<Self> {
let first_line = BufReader::new(File::open(p)?).lines().next();
if let Some(Ok(l)) = first_line {
let n: Vec<u32> = l
.split_whitespace()
.map(|s| s.parse().expect("fail"))
.collect();
let base = p.parent().expect("no parent?").to_path_buf();
let c = TgtCfg::new(n[0], n[1], n[2], n[3], n[4], Some(base));
return Ok(c);
}
anyhow::bail!("faile to create TgtCfg from file")
}
}

#[derive(Debug)]
Expand All @@ -126,9 +140,66 @@ 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.cfg.zone_nr_conv.try_into().unwrap() {
let sz = self.back_file_size(i.try_into().unwrap())?;
if sz != self.cfg.zone_size {
if !new_dev {
return Err(anyhow::anyhow!("wrong conv zone size {}", sz));
} else {
self.back_file_truncate(i.try_into().unwrap(), self.cfg.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 z = &self.zones[i];
let mut zs = z.get_stat_mut();

if size == 0 {
zs.cond = BLK_ZONE_COND_EMPTY;
zs.wp = z.start;
} else if size == z.capacity {
zs.cond = BLK_ZONE_COND_FULL;
zs.wp = z.start + self.cfg.zone_size;
} else {
zs.cond = BLK_ZONE_COND_CLOSED;
zs.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(cfg: TgtCfg) -> ZonedTgt {
let ram_backed = true;
fn new(cfg: TgtCfg, new_dev: bool) -> anyhow::Result<ZonedTgt> {
let ram_backed = cfg.base.is_none();
let _buf = if ram_backed {
Some(IoBuf::<u8>::new(cfg.size as usize))
} else {
Expand Down Expand Up @@ -162,7 +233,7 @@ impl ZonedTgt {
})
.collect();

ZonedTgt {
let mut dev = ZonedTgt {
start: buf_addr,
nr_zones: nr_zones as u32,
data: RwLock::new(TgtData {
Expand All @@ -172,7 +243,14 @@ impl ZonedTgt {
_buf,
zfiles: Vec::new(),
cfg,
};

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

Ok(dev)
}

#[inline(always)]
Expand Down Expand Up @@ -233,7 +311,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.cfg.base {
Some(ref p) => {
Expand All @@ -250,7 +327,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 @@ -871,8 +947,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 @@ -897,23 +976,44 @@ pub(crate) struct ZonedAddArgs {
max_active_zones: u32,
}

fn parse_zone_params(zo: &ZonedAddArgs) -> anyhow::Result<(TgtCfg, bool)> {
if zo.path.is_none() {
Ok((TgtCfg::from_argument(zo), 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() {
Ok((TgtCfg::from_file(&zf)?, false))
} else {
//create superblock file with passed parameter
let mut file = File::create(zf)?;
writeln!(
file,
"{} {} {} {} {}",
zo.size, zo.zone_size, zo.conv_zones, zo.max_open_zones, zo.max_active_zones
)?;
Ok((TgtCfg::from_argument(zo), 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 cfg = match opt {
let (cfg, 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 {
TgtCfg::from_argument(o)
}
parse_zone_params(o)?
}
None => return Err(anyhow::anyhow!("invalid parameter")),
};
Expand Down Expand Up @@ -946,7 +1046,7 @@ pub(crate) fn ublk_add_zoned(
Ok(())
};

let zoned_tgt = Arc::new(ZonedTgt::new(cfg));
let zoned_tgt = Arc::new(ZonedTgt::new(cfg, 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 1621dd0

Please sign in to comment.