From 45b115aeb0c20f4ad9df01a98233dea15466a022 Mon Sep 17 00:00:00 2001 From: Anatol Belski Date: Thu, 29 Jan 2026 15:48:36 +0100 Subject: [PATCH] block: raw: Implement punch_hole and write_zeroes Implement punch_hole() and write_zeroes() for raw file backends using io_uring and fallocate. punch_hole() uses FALLOC_FL_PUNCH_HOLE to deallocate storage. write_zeroes() uses FALLOC_FL_ZERO_RANGE to write zeros efficiently. Both use FALLOC_FL_KEEP_SIZE to maintain file size. Signed-off-by: Anatol Belski --- block/src/raw_async.rs | 58 ++++++++++++++++++++++++++++++++++++------ block/src/raw_sync.rs | 52 +++++++++++++++++++++++++++++++------ 2 files changed, 94 insertions(+), 16 deletions(-) diff --git a/block/src/raw_async.rs b/block/src/raw_async.rs index 7d070ceb4..539aaa909 100644 --- a/block/src/raw_async.rs +++ b/block/src/raw_async.rs @@ -258,15 +258,57 @@ impl AsyncIo for RawFileAsync { Ok(()) } - fn punch_hole(&mut self, _offset: u64, _length: u64, _user_data: u64) -> AsyncIoResult<()> { - Err(AsyncIoError::PunchHole(std::io::Error::other( - "punch_hole not supported for raw async backend", - ))) + fn punch_hole(&mut self, offset: u64, length: u64, user_data: u64) -> AsyncIoResult<()> { + let (submitter, mut sq, _) = self.io_uring.split(); + + const FALLOC_FL_PUNCH_HOLE: i32 = 0x02; + const FALLOC_FL_KEEP_SIZE: i32 = 0x01; + let mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE; + + // SAFETY: The file descriptor is known to be valid. + unsafe { + sq.push( + &opcode::Fallocate::new(types::Fd(self.fd), length) + .offset(offset) + .mode(mode) + .build() + .user_data(user_data), + ) + .map_err(|e| { + AsyncIoError::PunchHole(Error::other(format!("Submission queue is full: {e:?}"))) + })?; + }; + + sq.sync(); + submitter.submit().map_err(AsyncIoError::PunchHole)?; + + Ok(()) } - fn write_zeroes(&mut self, _offset: u64, _length: u64, _user_data: u64) -> AsyncIoResult<()> { - Err(AsyncIoError::WriteZeroes(std::io::Error::other( - "write_zeroes not supported for raw async backend", - ))) + fn write_zeroes(&mut self, offset: u64, length: u64, user_data: u64) -> AsyncIoResult<()> { + let (submitter, mut sq, _) = self.io_uring.split(); + + const FALLOC_FL_ZERO_RANGE: i32 = 0x10; + const FALLOC_FL_KEEP_SIZE: i32 = 0x01; + let mode = FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE; + + // SAFETY: The file descriptor is known to be valid. + unsafe { + sq.push( + &opcode::Fallocate::new(types::Fd(self.fd), length) + .offset(offset) + .mode(mode) + .build() + .user_data(user_data), + ) + .map_err(|e| { + AsyncIoError::WriteZeroes(Error::other(format!("Submission queue is full: {e:?}"))) + })?; + }; + + sq.sync(); + submitter.submit().map_err(AsyncIoError::WriteZeroes)?; + + Ok(()) } } diff --git a/block/src/raw_sync.rs b/block/src/raw_sync.rs index b540eb0ec..796d1e86b 100644 --- a/block/src/raw_sync.rs +++ b/block/src/raw_sync.rs @@ -151,15 +151,51 @@ impl AsyncIo for RawFileSync { self.completion_list.pop_front() } - fn punch_hole(&mut self, _offset: u64, _length: u64, _user_data: u64) -> AsyncIoResult<()> { - Err(AsyncIoError::PunchHole(std::io::Error::other( - "punch_hole not supported for raw sync backend", - ))) + fn punch_hole(&mut self, offset: u64, length: u64, user_data: u64) -> AsyncIoResult<()> { + const FALLOC_FL_PUNCH_HOLE: i32 = 0x02; + const FALLOC_FL_KEEP_SIZE: i32 = 0x01; + let mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE; + + // SAFETY: FFI call with valid arguments + let result = unsafe { + libc::fallocate( + self.fd as libc::c_int, + mode, + offset as libc::off_t, + length as libc::off_t, + ) + }; + if result < 0 { + return Err(AsyncIoError::PunchHole(std::io::Error::last_os_error())); + } + + self.completion_list.push_back((user_data, result)); + self.eventfd.write(1).unwrap(); + + Ok(()) } - fn write_zeroes(&mut self, _offset: u64, _length: u64, _user_data: u64) -> AsyncIoResult<()> { - Err(AsyncIoError::WriteZeroes(std::io::Error::other( - "write_zeroes not supported for raw sync backend", - ))) + fn write_zeroes(&mut self, offset: u64, length: u64, user_data: u64) -> AsyncIoResult<()> { + const FALLOC_FL_ZERO_RANGE: i32 = 0x10; + const FALLOC_FL_KEEP_SIZE: i32 = 0x01; + let mode = FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE; + + // SAFETY: FFI call with valid arguments + let result = unsafe { + libc::fallocate( + self.fd as libc::c_int, + mode, + offset as libc::off_t, + length as libc::off_t, + ) + }; + if result < 0 { + return Err(AsyncIoError::WriteZeroes(std::io::Error::last_os_error())); + } + + self.completion_list.push_back((user_data, result)); + self.eventfd.write(1).unwrap(); + + Ok(()) } }