Skip to content

feat(virtio-block): Add support for VIRTIO_BLK_T_DISCARD request type #5168

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

LDagnachew
Copy link

(Reupload) Opening this as a Draft PR, as I am still working on fully integrating unmap/fstrim/discard. Right now I'm currently working on the functionality.

Changes

Expose discard feature bit

  • Added VIRTIO_BLK_F_DISCARD to the device’s advertised avail_features so guests can negotiate discard support

Extend RequestType enum

  • Introduced a new Discard variant and mapped VIRTIO_BLK_T_DISCARDRequestType::Discard in the request conversion

Update request parsing (Request::parse) in request.rs

  • Allowed Discard (alongside Flush) to skip the data descriptor
  • Enforced sector‑aligned length and bounds checks for Discard requests

Implement handle_discard backend

  • Created handle_discard(offset, len) to call fallocate() on the host file descriptor

Reason

Attempting to resolve Issue #2708

Next Steps

  • Checking for VIRTIO_BLK_F_DISCARD support (need suggestions for this, not sure of an efficient way to check for Discard Support within a given VirtIO block).
  • Writing Unit & Integration Tests.

License Acceptance

By submitting this pull request, I confirm that my contribution is made under
the terms of the Apache 2.0 license. For more information on following Developer
Certificate of Origin and signing off your commits, please check
CONTRIBUTING.md.

PR Checklist

  • I have read and understand CONTRIBUTING.md.
  • I have run tools/devtool checkstyle to verify that the PR passes the
    automated style checks.
  • I have described what is done in these changes, why they are needed, and
    how they are solving the problem in a clear and encompassing way.
  • I have updated any relevant documentation (both in code and in the docs)
    in the PR.
  • I have mentioned all user-facing changes in CHANGELOG.md.
  • If a specific issue led to this PR, this PR closes the issue.
  • When making API changes, I have followed the
    Runbook for Firecracker API changes.
  • I have tested all new and changed functionalities in unit tests and/or
    integration tests.
  • I have linked an issue to every new TODO.

  • This functionality cannot be added in rust-vmm.

- Implemented parsing and validation for `VIRTIO_BLK_T_DISCARD` requests.
- Added `SyncIoError::Discard` to represent errors specific to discard operations.
- Updated `Request::process` to handle discard requests using `handle_discard` in `FileEngine`.
- Integrated discard metrics (`discard_count` and `invalid_reqs_count`) for successful and invalid discard operations.

Signed-off-by: Leul Dagnachew <leulmdagnachew@gmail.com>
Signed-off-by: LDagnachew <leulmdagnachew@gmail.com>
@LDagnachew
Copy link
Author

LDagnachew commented Apr 24, 2025

As far as checking whether the system even supports fstrim/unmap/discard, a trivial way would be to simply check whether fallocate() returns an error, however not entirely sure if there's a better way to do this. @ShadowCurse

@LDagnachew LDagnachew marked this pull request as ready for review April 24, 2025 21:41
Copy link
Contributor

@ShadowCurse ShadowCurse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding the check if fallocate is supported: for now let's assume it is supported.
Doing a test fallocate call might not a bad idea. Alternatively, we can always make this an optional feature, so users will need to configure it manually (line read_only) if they know their fs supports it.

Also found a patch series for qemu with this feature: https://lore.kernel.org/all/20190208134950.187665-5-sgarzare@redhat.com/ might be useful for a reference.

Comment on lines +38 to +44
bitflags::bitflags! {
pub struct FallocateFlags: c_int {
const FALLOC_FL_KEEP_SIZE = 0x01;
const FALLOC_FL_PUNCH_HOLE = 0x02;
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

libc crate already has these constants

Comment on lines +186 to +197
let fd = self.file().as_raw_fd();
let result = Self::fallocate(
fd,
FallocateFlags::FALLOC_FL_PUNCH_HOLE | FallocateFlags::FALLOC_FL_KEEP_SIZE,
offset as i64,
len as i64,
);
if let Err(e) = result {
eprintln!("Discard failed: {}", e);
return Err(std::io::Error::last_os_error());
}
Ok(())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we have 2 backends for block device, each need to implement it's own version of discard. For sync version this impl is basically what it needs to be, for io_uring backend there is IORING_OP_FALLOCATE op.

Comment on lines +246 to +250
pub struct DiscardSegment {
sector: u64,
num_sectors: u32,
flags: u32,
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

flags is not just a 32 bit integer, it is a struct with 2 u32 inside. Also it needs to have a C struct layout attribute.

#[derive(Debug, PartialEq, Eq)]
pub struct Request {
pub r#type: RequestType,
pub data_len: u32,
pub status_addr: GuestAddress,
sector: u64,
data_addr: GuestAddress,
discard_segments: Option<Vec<DiscardSegment>>, // New field, holds ranges
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this comment?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh lol that was just a mental note to myself, forgot to take it out.

Comment on lines +338 to +340
// Get data descriptor
let data_desc = avail_desc.next_descriptor()
.ok_or(VirtioBlockError::DescriptorChainTooShort)?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a place to parse the request. It needs to be done above. There is already a place where data_desc is assigned.

if num_segments == 0 {
return Err(VirtioBlockError::InvalidDiscardRequest);
}
let mut segments = Vec::with_capacity(num_segments as usize);
Copy link
Contributor

@ShadowCurse ShadowCurse Apr 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can avoid having to deal with multiple segments if we just limit their number to 1 in the config space of the device. You will need to expand the ConfigSpace type to include max_discard_sectors and everything before it. Also in all other code please avoid dynamic allocations, it can cause issues in the future.

Comment on lines +465 to +474
match res {
Ok(()) => Ok(block_io::FileEngineOk::Executed(block_io::RequestOk {
req: pending,
count: 0,
})),
Err(e) => Err(block_io::RequestError {
req: pending,
error: BlockIoError::Sync(SyncIoError::Discard(e)),
}),
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice hallucinations. The file_engine.handle_discard should return same result type as file_engine.read/file_engine.write and so on. And then it will be handled by the code bellow.

Copy link
Author

@LDagnachew LDagnachew Apr 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha, that checks out, I'll make that change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants