From 00c05f4761ae106f94269b5b6255dbd6ca734f9a Mon Sep 17 00:00:00 2001 From: Saravanan D Date: Wed, 18 Feb 2026 14:23:03 -0800 Subject: [PATCH] block: Use logical block size for alignment O_DIRECT requires buffer addresses to be aligned to the backend device's logical block size. The existing bounce buffer logic in execute_async() hardcodes SECTOR_SIZE (512) for the alignment check and bounce buffer allocation. This is insufficient for devices with a 4096-byte logical block size, where misaligned buffers cause -EINVAL from the host kernel. Add an alignment() method to the AsyncIo trait that returns the backend's logical block size, defaulting to SECTOR_SIZE. The three raw I/O backends (io_uring, AIO, synchronous) probe the device topology via DiskTopology::probe() at creation time and return the actual logical block size. All image format backends would simply use the default value of 512 bytes since their underlying are not block devices. execute_async() now queries disk_image.alignment() instead of using the hardcoded SECTOR_SIZE Fixes: #7720 Signed-off-by: Saravanan D --- block/src/async_io.rs | 5 ++++- block/src/lib.rs | 14 ++++++++------ block/src/raw_async.rs | 18 +++++++++++++----- block/src/raw_async_aio.rs | 24 ++++++++++++++++++------ block/src/raw_sync.rs | 14 ++++++++++++-- 5 files changed, 55 insertions(+), 20 deletions(-) diff --git a/block/src/async_io.rs b/block/src/async_io.rs index a1e8fa3e4..fe3349e6a 100644 --- a/block/src/async_io.rs +++ b/block/src/async_io.rs @@ -8,7 +8,7 @@ use std::os::fd::{AsRawFd, OwnedFd, RawFd}; use thiserror::Error; use vmm_sys_util::eventfd::EventFd; -use crate::{BatchRequest, DiskTopology}; +use crate::{BatchRequest, DiskTopology, SECTOR_SIZE}; #[derive(Error, Debug)] pub enum DiskFileError { @@ -145,4 +145,7 @@ pub trait AsyncIo: Send { fn submit_batch_requests(&mut self, _batch_request: &[BatchRequest]) -> AsyncIoResult<()> { Ok(()) } + fn alignment(&self) -> u64 { + SECTOR_SIZE + } } diff --git a/block/src/lib.rs b/block/src/lib.rs index 3d45473a9..3ab8de9e4 100644 --- a/block/src/lib.rs +++ b/block/src/lib.rs @@ -436,6 +436,7 @@ impl Request { let sector = self.sector; let request_type = self.request_type; let offset = (sector << SECTOR_SHIFT) as libc::off_t; + let alignment = disk_image.alignment(); let mut iovecs: SmallVec<[libc::iovec; DEFAULT_DESCRIPTOR_VEC_SIZE]> = SmallVec::with_capacity(self.data_descriptors.len()); @@ -466,14 +467,15 @@ impl Request { assert!(origin_ptr.len() >= data_len); let origin_ptr = origin_ptr.ptr_guard(); - // Verify the buffer alignment. - // In case it's not properly aligned, an intermediate buffer is - // created with the correct alignment, and a copy from/to the - // origin buffer is performed, depending on the type of operation. - let iov_base = if (origin_ptr.as_ptr() as u64).is_multiple_of(SECTOR_SIZE) { + // O_DIRECT requires buffer addresses to be aligned to the + // backend device's logical block size. In case it's not properly + // aligned, an intermediate buffer is created with the correct + // alignment, and a copy from/to the origin buffer is performed, + // depending on the type of operation. + let iov_base = if (origin_ptr.as_ptr() as u64).is_multiple_of(alignment) { origin_ptr.as_ptr() as *mut libc::c_void } else { - let layout = Layout::from_size_align(data_len, SECTOR_SIZE as usize).unwrap(); + let layout = Layout::from_size_align(data_len, alignment as usize).unwrap(); // SAFETY: layout has non-zero size let aligned_ptr = unsafe { alloc_zeroed(layout) }; if aligned_ptr.is_null() { diff --git a/block/src/raw_async.rs b/block/src/raw_async.rs index 539aaa909..3a890d716 100644 --- a/block/src/raw_async.rs +++ b/block/src/raw_async.rs @@ -13,7 +13,7 @@ use vmm_sys_util::eventfd::EventFd; use crate::async_io::{ AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult, }; -use crate::{BatchRequest, DiskTopology, RequestType, probe_sparse_support}; +use crate::{BatchRequest, DiskTopology, RequestType, SECTOR_SIZE, probe_sparse_support}; pub struct RawFileDisk { file: File, @@ -40,10 +40,12 @@ impl DiskFile for RawFileDisk { } fn new_async_io(&self, ring_depth: u32) -> DiskFileResult> { - Ok(Box::new( - RawFileAsync::new(self.file.as_raw_fd(), ring_depth) - .map_err(DiskFileError::NewAsyncIo)?, - ) as Box) + let mut raw = RawFileAsync::new(self.file.as_raw_fd(), ring_depth) + .map_err(DiskFileError::NewAsyncIo)?; + raw.alignment = DiskTopology::probe(&self.file) + .map(|t| t.logical_block_size) + .unwrap_or(SECTOR_SIZE); + Ok(Box::new(raw) as Box) } fn topology(&mut self) -> DiskTopology { @@ -72,6 +74,7 @@ pub struct RawFileAsync { fd: RawFd, io_uring: IoUring, eventfd: EventFd, + alignment: u64, } impl RawFileAsync { @@ -87,6 +90,7 @@ impl RawFileAsync { fd, io_uring, eventfd, + alignment: SECTOR_SIZE, }) } } @@ -96,6 +100,10 @@ impl AsyncIo for RawFileAsync { &self.eventfd } + fn alignment(&self) -> u64 { + self.alignment + } + fn read_vectored( &mut self, offset: libc::off_t, diff --git a/block/src/raw_async_aio.rs b/block/src/raw_async_aio.rs index 6447a727d..7266a3633 100644 --- a/block/src/raw_async_aio.rs +++ b/block/src/raw_async_aio.rs @@ -16,7 +16,7 @@ use vmm_sys_util::eventfd::EventFd; use crate::async_io::{ AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult, }; -use crate::{DiskTopology, probe_sparse_support}; +use crate::{DiskTopology, SECTOR_SIZE, probe_sparse_support}; pub struct RawFileDiskAio { file: File, @@ -43,10 +43,12 @@ impl DiskFile for RawFileDiskAio { } fn new_async_io(&self, ring_depth: u32) -> DiskFileResult> { - Ok(Box::new( - RawFileAsyncAio::new(self.file.as_raw_fd(), ring_depth) - .map_err(DiskFileError::NewAsyncIo)?, - ) as Box) + let mut raw = RawFileAsyncAio::new(self.file.as_raw_fd(), ring_depth) + .map_err(DiskFileError::NewAsyncIo)?; + raw.alignment = DiskTopology::probe(&self.file) + .map(|t| t.logical_block_size) + .unwrap_or(SECTOR_SIZE); + Ok(Box::new(raw) as Box) } fn topology(&mut self) -> DiskTopology { @@ -71,6 +73,7 @@ pub struct RawFileAsyncAio { fd: RawFd, ctx: aio::IoContext, eventfd: EventFd, + alignment: u64, } impl RawFileAsyncAio { @@ -78,7 +81,12 @@ impl RawFileAsyncAio { let eventfd = EventFd::new(libc::EFD_NONBLOCK)?; let ctx = aio::IoContext::new(queue_depth)?; - Ok(RawFileAsyncAio { fd, ctx, eventfd }) + Ok(RawFileAsyncAio { + fd, + ctx, + eventfd, + alignment: SECTOR_SIZE, + }) } } @@ -87,6 +95,10 @@ impl AsyncIo for RawFileAsyncAio { &self.eventfd } + fn alignment(&self) -> u64 { + self.alignment + } + fn read_vectored( &mut self, offset: libc::off_t, diff --git a/block/src/raw_sync.rs b/block/src/raw_sync.rs index e1a5433b8..9c96863b6 100644 --- a/block/src/raw_sync.rs +++ b/block/src/raw_sync.rs @@ -13,7 +13,7 @@ use vmm_sys_util::eventfd::EventFd; use crate::async_io::{ AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult, }; -use crate::{DiskTopology, probe_sparse_support}; +use crate::{DiskTopology, SECTOR_SIZE, probe_sparse_support}; pub struct RawFileDiskSync { file: File, @@ -40,7 +40,11 @@ impl DiskFile for RawFileDiskSync { } fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult> { - Ok(Box::new(RawFileSync::new(self.file.as_raw_fd())) as Box) + let mut raw = RawFileSync::new(self.file.as_raw_fd()); + raw.alignment = DiskTopology::probe(&self.file) + .map(|t| t.logical_block_size) + .unwrap_or(SECTOR_SIZE); + Ok(Box::new(raw) as Box) } fn topology(&mut self) -> DiskTopology { @@ -65,6 +69,7 @@ pub struct RawFileSync { fd: RawFd, eventfd: EventFd, completion_list: VecDeque<(u64, i32)>, + alignment: u64, } impl RawFileSync { @@ -73,6 +78,7 @@ impl RawFileSync { fd, eventfd: EventFd::new(libc::EFD_NONBLOCK).expect("Failed creating EventFd for RawFile"), completion_list: VecDeque::new(), + alignment: SECTOR_SIZE, } } } @@ -82,6 +88,10 @@ impl AsyncIo for RawFileSync { &self.eventfd } + fn alignment(&self) -> u64 { + self.alignment + } + fn read_vectored( &mut self, offset: libc::off_t,