#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DirectIoFlags {
pub skip_read_cache: bool,
pub skip_write_cache: bool,
pub unbuffered: bool,
}
impl DirectIoFlags {
pub fn enabled() -> Self {
Self {
skip_read_cache: true,
skip_write_cache: true,
unbuffered: true,
}
}
pub fn read_only() -> Self {
Self {
skip_read_cache: true,
skip_write_cache: false,
unbuffered: true,
}
}
pub fn write_only() -> Self {
Self {
skip_read_cache: false,
skip_write_cache: true,
unbuffered: true,
}
}
pub fn disabled() -> Self {
Self {
skip_read_cache: false,
skip_write_cache: false,
unbuffered: false,
}
}
pub fn is_enabled(&self) -> bool {
self.skip_read_cache || self.skip_write_cache || self.unbuffered
}
}
pub struct DirectIoEngine;
impl DirectIoEngine {
pub fn should_use_direct_io(offset: u64, size: u64, last_offset: Option<u64>) -> bool {
const DIRECT_IO_THRESHOLD: u64 = 1024 * 1024; const SMALL_IO_THRESHOLD: u64 = 64 * 1024; const SEQUENTIAL_WINDOW: u64 = 128 * 1024;
if size < SMALL_IO_THRESHOLD {
return false;
}
if size >= DIRECT_IO_THRESHOLD {
if let Some(last) = last_offset {
let is_sequential = offset.abs_diff(last) <= SEQUENTIAL_WINDOW;
return is_sequential;
}
return true;
}
false
}
pub fn validate_alignment(offset: u64, size: u64, block_size: u64) -> Result<(), &'static str> {
if offset % block_size != 0 {
return Err("Offset not block-aligned");
}
if size % block_size != 0 {
return Err("Size not multiple of block size");
}
Ok(())
}
pub fn align_size(requested_size: u64, block_size: u64) -> u64 {
requested_size.div_ceil(block_size) * block_size
}
pub fn recommended_block_size(device_type: DeviceType) -> u64 {
match device_type {
DeviceType::Nvme => 4096, DeviceType::Ssd => 4096, DeviceType::Hdd => 4096, DeviceType::Ram => 4096, }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DeviceType {
Nvme,
Ssd,
Hdd,
Ram,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_direct_io_flags() {
let enabled = DirectIoFlags::enabled();
assert!(enabled.skip_read_cache);
assert!(enabled.skip_write_cache);
assert!(enabled.unbuffered);
assert!(enabled.is_enabled());
let disabled = DirectIoFlags::disabled();
assert!(!disabled.skip_read_cache);
assert!(!disabled.skip_write_cache);
assert!(!disabled.unbuffered);
assert!(!disabled.is_enabled());
}
#[test]
fn test_should_use_direct_io_small() {
assert!(!DirectIoEngine::should_use_direct_io(0, 4096, None));
assert!(!DirectIoEngine::should_use_direct_io(0, 32 * 1024, None));
}
#[test]
fn test_should_use_direct_io_large_sequential() {
let size = 2 * 1024 * 1024; let offset1 = 0;
let offset2 = 64 * 1024;
assert!(DirectIoEngine::should_use_direct_io(offset1, size, None));
assert!(DirectIoEngine::should_use_direct_io(
offset2,
size,
Some(offset1)
));
}
#[test]
fn test_should_use_direct_io_large_random() {
let offset1 = 0;
let offset2 = 100 * 1024 * 1024; let size = 2 * 1024 * 1024;
assert!(DirectIoEngine::should_use_direct_io(offset1, size, None));
assert!(!DirectIoEngine::should_use_direct_io(
offset2,
size,
Some(offset1)
));
}
#[test]
fn test_alignment_validation() {
let block_size = 4096;
assert!(DirectIoEngine::validate_alignment(0, 4096, block_size).is_ok());
assert!(DirectIoEngine::validate_alignment(4096, 8192, block_size).is_ok());
assert!(DirectIoEngine::validate_alignment(100, 4096, block_size).is_err());
assert!(DirectIoEngine::validate_alignment(0, 4000, block_size).is_err());
}
#[test]
fn test_align_size() {
let block_size = 4096;
assert_eq!(DirectIoEngine::align_size(4096, block_size), 4096);
assert_eq!(DirectIoEngine::align_size(4097, block_size), 8192);
assert_eq!(DirectIoEngine::align_size(8000, block_size), 8192);
assert_eq!(DirectIoEngine::align_size(1, block_size), 4096);
}
#[test]
fn test_recommended_block_size() {
assert_eq!(
DirectIoEngine::recommended_block_size(DeviceType::Nvme),
4096
);
assert_eq!(
DirectIoEngine::recommended_block_size(DeviceType::Ssd),
4096
);
assert_eq!(
DirectIoEngine::recommended_block_size(DeviceType::Hdd),
4096
);
assert_eq!(
DirectIoEngine::recommended_block_size(DeviceType::Ram),
4096
);
}
}