diff --git a/build.rs b/build.rs index e8394c0..a7736bb 100644 --- a/build.rs +++ b/build.rs @@ -338,6 +338,7 @@ fn main() { .rustified_enum("spdk_nvme_path_status_code") .allowlist_type("spdk_ftl_mode") .rustified_enum("spdk_ftl_mode") + .blocklist_type("spdk_bdev_io_error_stat") .allowlist_var("^NVMF.*") .allowlist_var("^SPDK.*") .allowlist_var("^spdk.*") diff --git a/nix/pkgs/libspdk/default.nix b/nix/pkgs/libspdk/default.nix index 57ebdf1..77a61f4 100644 --- a/nix/pkgs/libspdk/default.nix +++ b/nix/pkgs/libspdk/default.nix @@ -92,8 +92,8 @@ let # Derivation attributes # spdk = rec { - rev = "f61f929aac373ed8af3a27a4bafa4cee046e1922"; - sha256 = "sha256-k+jezN1lGteu9AN7qozVyaTmtuYC7dgNRPDpSSYt2l8="; + rev = "b78cde6f5cdd2f0f50810743d2865d7bee825ce4"; + sha256 = "sha256-GVWrKeYsKpro1XMYblky7ik6qw07aa/4CZqb9JU5cQU="; pname = "libspdk${nameSuffix}"; version = "25.05-${lib.substring 0 7 rev}"; name = "${pname}-${version}"; diff --git a/src/bdev_async.rs b/src/bdev_async.rs index 8a23552..4068473 100644 --- a/src/bdev_async.rs +++ b/src/bdev_async.rs @@ -1,5 +1,5 @@ -///! Asynchronous methods of `Bdev<>` wrapper. -use std::os::raw::c_void; +//! Asynchronous methods of `Bdev<>` wrapper. +use std::{ops::Deref, os::raw::c_void}; use futures::channel::{oneshot, oneshot::Canceled}; @@ -7,14 +7,62 @@ use crate::{ error::{SpdkError::BdevUnregisterFailed, SpdkResult}, ffihelper::{cb_arg, done_errno_cb, errno_error, errno_result_from_i32, ErrnoResult}, libspdk::{ - bdev_reset_device_stat, spdk_bdev, spdk_bdev_get_device_stat, spdk_bdev_io_stat, - spdk_bdev_unregister, SPDK_BDEV_RESET_STAT_ALL, SPDK_BDEV_RESET_STAT_NONE, + bdev_reset_device_stat, spdk_bdev, spdk_bdev_get_device_stat, spdk_bdev_io_error_stat, + spdk_bdev_io_stat, spdk_bdev_unregister, }, Bdev, BdevOps, }; -/// TODO -pub type BdevStats = spdk_bdev_io_stat; +/// Wrapper for [`spdk_bdev_io_error_stat`]. +pub type BdevErrorStats = Box; + +/// Wrapper for [`spdk_bdev_io_stat`]. +/// # Safety +/// Don't attempt to copy and rebox the error stats point without ensuring it's erased first. +/// This can be done via [`Self::take_error_stats`]. +pub struct BdevStats(spdk_bdev_io_stat); +impl Deref for BdevStats { + type Target = spdk_bdev_io_stat; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl Drop for BdevStats { + fn drop(&mut self) { + if !self.0.io_error.is_null() { + unsafe { + drop(Box::from_raw(self.0.io_error)); + } + } + } +} +impl BdevStats { + /// Take the errors stats [`spdk_bdev_io_error_stat`] from the total stats. + pub fn take_error_stats(&mut self) -> Option { + if self.0.io_error.is_null() { + return None; + } + let errors = self.io_error; + self.0.io_error = std::ptr::null_mut(); + Some(unsafe { Box::from_raw(errors) }) + } +} + +/// Bdev Stat reset mode. +pub enum BdevStatsResetMode { + All, + MaxMin, + Errors, +} +impl From for crate::libspdk::spdk_bdev_reset_stat_mode { + fn from(value: BdevStatsResetMode) -> Self { + match value { + BdevStatsResetMode::All => crate::libspdk::SPDK_BDEV_RESET_STAT_ALL, + BdevStatsResetMode::MaxMin => crate::libspdk::SPDK_BDEV_RESET_STAT_MAXMIN, + BdevStatsResetMode::Errors => crate::libspdk::SPDK_BDEV_RESET_STAT_ERROR, + } + } +} /// TODO pub struct BdevAsyncCallContext { @@ -76,8 +124,15 @@ where } /// Get bdev IOStats or errno value in case of an error. - pub async fn stats_async(&self) -> ErrnoResult { + pub async fn stats_async(&self, errors: bool) -> ErrnoResult { let mut stat: spdk_bdev_io_stat = unsafe { std::mem::zeroed() }; + if errors { + let io_err = Box::new(spdk_bdev_io_error_stat::default()); + unsafe { + stat.io_error = Box::into_raw(io_err); + } + } + let (s, r) = oneshot::channel::(); // This will iterate over I/O channels and call async callback when @@ -86,26 +141,26 @@ where spdk_bdev_get_device_stat( self.as_inner_ptr(), &mut stat as *mut _, - SPDK_BDEV_RESET_STAT_NONE, + crate::libspdk::SPDK_BDEV_RESET_STAT_NONE, Some(inner_stats_callback), cb_arg(s), ); } let errno = r.await.expect("Cancellation is not supported"); - errno_result_from_i32(stat, errno) + errno_result_from_i32(BdevStats(stat), errno) } /// This function resets all stat counters for a given Bdev. /// Returns Errno in case of an error. - pub async fn stats_reset_async(&self) -> ErrnoResult<()> { + pub async fn stats_reset_async(&self, reset_mode: BdevStatsResetMode) -> ErrnoResult<()> { let (s, r) = oneshot::channel::(); // This will iterate over I/O channels to reset IOStats and call async // callback when done. unsafe { bdev_reset_device_stat( self.as_inner_ptr(), - SPDK_BDEV_RESET_STAT_ALL, + reset_mode.into(), Some(inner_stats_reset_callback), cb_arg(s), ); diff --git a/src/lib.rs b/src/lib.rs index ed359f7..e76421c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,7 +52,7 @@ mod uuid; pub use crate::{ bdev::Bdev, - bdev_async::{BdevAsyncCallContext, BdevStats}, + bdev_async::{BdevAsyncCallContext, BdevErrorStats, BdevStats, BdevStatsResetMode}, bdev_builder::BdevBuilder, bdev_desc::{BdevDesc, BdevDescError, BdevEvent, LbaRange, LbaRangeLock}, bdev_io::BdevIo, diff --git a/src/libspdk/mod.rs b/src/libspdk/mod.rs index 9ce52a2..94c6733 100644 --- a/src/libspdk/mod.rs +++ b/src/libspdk/mod.rs @@ -20,6 +20,14 @@ pub use spdk_nvme_media_error_status_code::*; pub use spdk_nvme_path_status_code::*; pub use spdk_nvme_status_code_type::*; +/// SPDK Bdev IO Error stats. +#[repr(C)] +#[derive(Debug, Default, Clone, Copy)] +pub struct spdk_bdev_io_error_stat { + /// A count of error status, indexed by the spdk status codes. + pub error_status: [u32; -SPDK_MIN_BDEV_IO_STATUS as usize], +} + /// Initializes a size field of a struct with struct's size_of. /// Other fields must be initialized explicitly. ///