diff --git a/src/zoned.rs b/src/zoned.rs index ee118e5..66c1ff0 100644 --- a/src/zoned.rs +++ b/src/zoned.rs @@ -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; @@ -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, + new_dev: bool, + ) -> anyhow::Result { + let ram_backed = base.is_none(); let _buf = if ram_backed { Some(IoBuf::::new(size as usize)) } else { @@ -99,7 +162,7 @@ impl ZonedTgt { }) .collect(); - ZonedTgt { + let mut dev = ZonedTgt { _size: size, start: buf_addr, zone_size, @@ -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)] @@ -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 { match self.base { Some(ref p) => { @@ -196,7 +265,6 @@ impl ZonedTgt { } } - #[allow(dead_code)] fn back_file_size(&self, zno: u32) -> anyhow::Result { let fd = self.__get_zone_fd(zno as usize); let mut file_stat: libc::stat = unsafe { std::mem::zeroed() }; @@ -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, @@ -828,27 +899,72 @@ pub(crate) struct ZonedAddArgs { conv_zones: u32, } +fn parse_zone_params(zo: &ZonedAddArgs) -> anyhow::Result<(u64, u64, u32, Option, 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 = 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, comm_arc: &Arc, ) -> anyhow::Result { //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")), }; @@ -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() { diff --git a/tests/basic.rs b/tests/basic.rs index 0974310..8829628 100644 --- a/tests/basic.rs +++ b/tests/basic.rs @@ -249,24 +249,33 @@ mod integration { } } - fn __test_ublk_add_del_zoned(bs: u32, tf: F) + fn __test_ublk_add_del_zoned(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); } @@ -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 => {} @@ -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(); @@ -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)); } }