use std::ffi::c_void;
use libnvme_sys::{
nvme_zns_append, nvme_zns_append_args, nvme_zns_mgmt_recv, nvme_zns_mgmt_recv_args,
nvme_zns_mgmt_send, nvme_zns_mgmt_send_args,
};
use crate::error::{check_ret, Error, Result};
use crate::namespace::Namespace;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
#[repr(u8)]
pub enum ZoneSendAction {
Close = 0x1,
Finish = 0x2,
Open = 0x3,
Reset = 0x4,
Offline = 0x5,
SetDescriptorExtension = 0x10,
ZrwaFlush = 0x11,
}
impl ZoneSendAction {
fn as_raw(self) -> u32 {
self as u8 as u32
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
#[repr(u8)]
pub enum ZoneRecvAction {
#[default]
Report = 0x0,
ExtendedReport = 0x1,
}
impl ZoneRecvAction {
fn as_raw(self) -> u32 {
self as u8 as u32
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
#[repr(u16)]
pub enum ZoneReportFilter {
#[default]
All = 0x0,
Empty = 0x1,
ImplicitlyOpened = 0x2,
ExplicitlyOpened = 0x3,
Closed = 0x4,
Full = 0x5,
ReadOnly = 0x6,
Offline = 0x7,
}
impl ZoneReportFilter {
fn as_raw(self) -> u16 {
self as u16
}
}
#[must_use = "this builder does nothing until `.execute()` is called"]
pub struct ZnsMgmtSend<'a, 'r> {
ns: &'a Namespace<'r>,
slba: u64,
action: ZoneSendAction,
data: Option<&'a [u8]>,
select_all: bool,
zsaso: u8,
timeout_ms: u32,
}
impl<'a, 'r> ZnsMgmtSend<'a, 'r> {
pub(crate) fn new(ns: &'a Namespace<'r>, slba: u64, action: ZoneSendAction) -> Self {
ZnsMgmtSend {
ns,
slba,
action,
data: None,
select_all: false,
zsaso: 0,
timeout_ms: 0,
}
}
pub fn data(mut self, data: &'a [u8]) -> Self {
self.data = Some(data);
self
}
pub fn select_all(mut self) -> Self {
self.select_all = true;
self
}
pub fn zsaso(mut self, zsaso: u8) -> Self {
self.zsaso = zsaso;
self
}
pub fn timeout_ms(mut self, ms: u32) -> Self {
self.timeout_ms = ms;
self
}
pub fn execute(self) -> Result<u32> {
let fd = ns_fd(self.ns)?;
let mut result: u32 = 0;
let (data_ptr, data_len) = match self.data {
Some(buf) => (buf.as_ptr() as *mut c_void, buf.len() as u32),
None => (std::ptr::null_mut(), 0),
};
let mut args = nvme_zns_mgmt_send_args {
slba: self.slba,
result: &mut result,
data: data_ptr,
args_size: std::mem::size_of::<nvme_zns_mgmt_send_args>() as i32,
fd,
timeout: self.timeout_ms,
nsid: self.ns.nsid(),
zsa: self.action.as_raw(),
data_len,
select_all: self.select_all,
zsaso: self.zsaso,
};
let ret = unsafe { nvme_zns_mgmt_send(&mut args) };
check_ret(ret)?;
Ok(result)
}
}
#[must_use = "this builder does nothing until `.execute()` is called"]
pub struct ZnsMgmtRecv<'a, 'r> {
ns: &'a Namespace<'r>,
slba: u64,
action: ZoneRecvAction,
filter: ZoneReportFilter,
partial: bool,
data: Option<&'a mut [u8]>,
timeout_ms: u32,
}
impl<'a, 'r> ZnsMgmtRecv<'a, 'r> {
pub(crate) fn new(ns: &'a Namespace<'r>, slba: u64) -> Self {
ZnsMgmtRecv {
ns,
slba,
action: ZoneRecvAction::default(),
filter: ZoneReportFilter::default(),
partial: false,
data: None,
timeout_ms: 0,
}
}
pub fn extended_report(mut self) -> Self {
self.action = ZoneRecvAction::ExtendedReport;
self
}
pub fn filter(mut self, filter: ZoneReportFilter) -> Self {
self.filter = filter;
self
}
pub fn partial(mut self) -> Self {
self.partial = true;
self
}
pub fn buffer(mut self, buf: &'a mut [u8]) -> Self {
self.data = Some(buf);
self
}
pub fn timeout_ms(mut self, ms: u32) -> Self {
self.timeout_ms = ms;
self
}
pub fn execute(mut self) -> Result<u32> {
let buf = self.data.as_deref_mut().ok_or(Error::invalid_argument(
"zns_mgmt_recv requires a buffer via .buffer()",
))?;
let fd = ns_fd(self.ns)?;
let mut result: u32 = 0;
let mut args = nvme_zns_mgmt_recv_args {
slba: self.slba,
result: &mut result,
data: buf.as_mut_ptr() as *mut c_void,
args_size: std::mem::size_of::<nvme_zns_mgmt_recv_args>() as i32,
fd,
timeout: self.timeout_ms,
nsid: self.ns.nsid(),
zra: self.action.as_raw(),
data_len: buf.len() as u32,
zrasf: self.filter.as_raw(),
zras_feat: self.partial,
};
let ret = unsafe { nvme_zns_mgmt_recv(&mut args) };
check_ret(ret)?;
Ok(result)
}
}
#[must_use = "this builder does nothing until `.execute()` is called"]
pub struct ZnsAppend<'a, 'r> {
ns: &'a Namespace<'r>,
zslba: u64,
nlb: u32,
data: &'a [u8],
metadata: Option<&'a [u8]>,
ilbrt: u32,
ilbrt_u64: u64,
lbat: u16,
lbatm: u16,
control: u16,
timeout_ms: u32,
}
const ZNS_CTRL_FUA: u16 = 1 << 14;
const ZNS_CTRL_LR: u16 = 1 << 15;
const ZNS_CTRL_PRINFO_PRACT: u16 = 1 << 13;
const ZNS_CTRL_PRINFO_PRCHK_GUARD: u16 = 1 << 12;
const ZNS_CTRL_PRINFO_PRCHK_APP: u16 = 1 << 11;
const ZNS_CTRL_PRINFO_PRCHK_REF: u16 = 1 << 10;
impl<'a, 'r> ZnsAppend<'a, 'r> {
pub(crate) fn new(ns: &'a Namespace<'r>, zslba: u64, nlb: u32, data: &'a [u8]) -> Self {
ZnsAppend {
ns,
zslba,
nlb,
data,
metadata: None,
ilbrt: 0,
ilbrt_u64: 0,
lbat: 0,
lbatm: 0,
control: 0,
timeout_ms: 0,
}
}
pub fn metadata(mut self, md: &'a [u8]) -> Self {
self.metadata = Some(md);
self
}
pub fn fua(mut self) -> Self {
self.control |= ZNS_CTRL_FUA;
self
}
pub fn limited_retry(mut self) -> Self {
self.control |= ZNS_CTRL_LR;
self
}
pub fn protection_action(mut self) -> Self {
self.control |= ZNS_CTRL_PRINFO_PRACT;
self
}
pub fn check_guard(mut self) -> Self {
self.control |= ZNS_CTRL_PRINFO_PRCHK_GUARD;
self
}
pub fn check_apptag(mut self) -> Self {
self.control |= ZNS_CTRL_PRINFO_PRCHK_APP;
self
}
pub fn check_reftag(mut self) -> Self {
self.control |= ZNS_CTRL_PRINFO_PRCHK_REF;
self
}
pub fn ref_tag(mut self, ilbrt: u32) -> Self {
self.ilbrt = ilbrt;
self
}
pub fn ref_tag_u64(mut self, ilbrt: u64) -> Self {
self.ilbrt_u64 = ilbrt;
self
}
pub fn app_tag(mut self, tag: u16, mask: u16) -> Self {
self.lbat = tag;
self.lbatm = mask;
self
}
pub fn timeout_ms(mut self, ms: u32) -> Self {
self.timeout_ms = ms;
self
}
pub fn execute(self) -> Result<u64> {
if self.nlb == 0 || self.nlb > 65_536 {
return Err(Error::invalid_argument(format!(
"nlb must be 1..=65536, got {}",
self.nlb
)));
}
let nlb_enc = (self.nlb - 1) as u16;
let lba_size = self.ns.lba_size();
let want = u64::from(self.nlb) * u64::from(lba_size);
if self.data.len() as u64 != want {
return Err(Error::invalid_argument(format!(
"buffer length {} doesn't match nlb * lba_size = {}",
self.data.len(),
want
)));
}
let fd = ns_fd(self.ns)?;
let mut result: u64 = 0;
let (md_ptr, md_len) = match self.metadata {
Some(md) => (md.as_ptr() as *mut c_void, md.len() as u32),
None => (std::ptr::null_mut(), 0),
};
let mut args = nvme_zns_append_args {
zslba: self.zslba,
result: &mut result,
data: self.data.as_ptr() as *mut c_void,
metadata: md_ptr,
args_size: std::mem::size_of::<nvme_zns_append_args>() as i32,
fd,
timeout: self.timeout_ms,
nsid: self.ns.nsid(),
ilbrt: self.ilbrt,
data_len: self.data.len() as u32,
metadata_len: md_len,
nlb: nlb_enc,
control: self.control,
lbat: self.lbat,
lbatm: self.lbatm,
rsvd1: [0u8; 4],
ilbrt_u64: self.ilbrt_u64,
};
let ret = unsafe { nvme_zns_append(&mut args) };
check_ret(ret)?;
Ok(result)
}
}
fn ns_fd(ns: &Namespace<'_>) -> Result<std::os::raw::c_int> {
let fd = unsafe { libnvme_sys::nvme_ns_get_fd(ns.raw_handle()) };
if fd < 0 {
return Err(Error::Os(std::io::Error::last_os_error()));
}
Ok(fd)
}