Skip to content

Conversation

@slp
Copy link
Collaborator

@slp slp commented Nov 12, 2025

Imago supports both operations, so let's just enable them, as they may have a significant impact on both performance and disk usage on the host.

@slp
Copy link
Collaborator Author

slp commented Nov 12, 2025

Fixes #360

@slp
Copy link
Collaborator Author

slp commented Nov 12, 2025

This one needs an update of the imago crate.

slp added 5 commits November 27, 2025 15:55
We need the latest version of the image crate to support F_DISCARD
and F_WRITE_ZEROES.

Signed-off-by: Sergio Lopez <[email protected]>
Extend VirtioBlkConfig enough to cover up to write_zeroes_may_unmap, as
we're going to need those fields for implementing support both
F_DISCARD and F_WRITE_ZEROES.

Signed-off-by: Sergio Lopez <[email protected]>
Imago's discard_to_* operations require a mutable reference to Storage.
Let's wrap disk_image in a Mutex so we can obtain such reference when
needed. Since, in practice, we won't have multiple threads accessing the
lock, the overhead should be very small (one or two operations on
atomics).

Signed-off-by: Sergio Lopez <[email protected]>
The F_DISCARD feature enables the guest to request us to free up sectors
on the backing storage. Since imago does most of the heavy lifting, we
just need to announce the feature and call to imago's "discard_to_any"
then needed.

Signed-off-by: Sergio Lopez <[email protected]>
The F_WRITE_ZEROES feature enables the guest to instruct the host to
write zeroes to the disk image without actually having to copy any data
around. Since imago does the heavy lifting for us, we just need to
announce the feature and call to the right method when needed.

Signed-off-by: Sergio Lopez <[email protected]>
@slp slp force-pushed the support-discard-zeroes branch from 95ff720 to 9149ac4 Compare November 27, 2025 14:56
@slp slp marked this pull request as ready for review November 28, 2025 09:36
Copy link
Member

@tylerfanelli tylerfanelli left a comment

Choose a reason for hiding this comment

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

You mention:

Since, in practice, we won't have multiple threads accessing the
lock, the overhead should be very small (one or two operations on
atomics).

Curious as to why we can be sure that multiple threads won't access the lock? Is there a dedicated thread for this?

@mtjhrc
Copy link
Collaborator

mtjhrc commented Dec 1, 2025

Curious as to why we can be sure that multiple threads won't access the lock? Is there a dedicated thread for this?

I think that is accurate, there is a single dedicated worker thread for the device and the main event loop thread in libkrun that will end up activating the device.

But still, I am a not really happy about adding the Mutex, because I think it should be avoidable if you are clear about the actual ownership of the disk object at any given time. Having had a quick look, to do this the object should be moved into the worker thread in BlockWorker::new and returned from the BlockWorker::work(), which would then return it from .join() on the thread, so you get the object back in the Block::reset(), you can then store it and move it into the worker thread on the next activate().

I'm not really worried about the performance here, but the code quality if we were to keep adding mutexes to places where they shouldn't be necessary.

@slp
Copy link
Collaborator Author

slp commented Dec 2, 2025

Curious as to why we can be sure that multiple threads won't access the lock? Is there a dedicated thread for this?

I think that is accurate, there is a single dedicated worker thread for the device and the main event loop thread in libkrun that will end up activating the device.

Correct.

I'm not really worried about the performance here, but the code quality if we were to keep adding mutexes to places where they shouldn't be necessary.

This is pretty much a one-time thing only. And it'll go away once imago 0.2.0 gets released. @XanClic please keep me honest here.

@XanClic
Copy link

XanClic commented Dec 2, 2025

I'm not really worried about the performance here, but the code quality if we were to keep adding mutexes to places where they shouldn't be necessary.

This is pretty much a one-time thing only. And it'll go away once imago 0.2.0 gets released. @XanClic please keep me honest here.

It wasn’t part of the plan originally, but it’s certainly doable for 0.2.

Note though that discarding (or write_zeroes) on an immutable reference will be unsafe. It’s fine for a single thread, but for multiple threads, you have to be careful e.g. about reading from an area while simultaneously discarding it, because that may return data that shouldn’t be there:

  • Thread 1 begins reading from the area, gets the corresponding data offset in the underlying file to read from
  • Thread 2 discards the data, relinquishing the allocation on that offset
  • Thread 2 (or some different thread entirely) writes to some completely different area of the qcow2 disk that’s currently unallocated, requiring an allocation, which reuses the offset that was just discard, writing the data there
  • Thread 1 reads that data that’s supposed to be in a completely different area of the disk

That would be a security risk.

I’ll try to mitigate this problem in imago, but probably not for 0.2.0. Then again, it’s all good as long as I can do it before you use multiple threads.

@slp
Copy link
Collaborator Author

slp commented Dec 2, 2025

I'm not really worried about the performance here, but the code quality if we were to keep adding mutexes to places where they shouldn't be necessary.

This is pretty much a one-time thing only. And it'll go away once imago 0.2.0 gets released. @XanClic please keep me honest here.

It wasn’t part of the plan originally, but it’s certainly doable for 0.2.

Note though that discarding (or write_zeroes) on an immutable reference will be unsafe. It’s fine for a single thread, but for multiple threads, you have to be careful e.g. about reading from an area while simultaneously discarding it, because that may return data that shouldn’t be there:

  • Thread 1 begins reading from the area, gets the corresponding data offset in the underlying file to read from
  • Thread 2 discards the data, relinquishing the allocation on that offset
  • Thread 2 (or some different thread entirely) writes to some completely different area of the qcow2 disk that’s currently unallocated, requiring an allocation, which reuses the offset that was just discard, writing the data there
  • Thread 1 reads that data that’s supposed to be in a completely different area of the disk

That would be a security risk.

I’ll try to mitigate this problem in imago, but probably not for 0.2.0. Then again, it’s all good as long as I can do it before you use multiple threads.

Got it. Thanks a lot, Hanna!

@mtjhrc
Copy link
Collaborator

mtjhrc commented Dec 3, 2025

This is pretty much a one-time thing only. And it'll go away once imago 0.2.0 gets released. @XanClic please keep me honest here.

It wasn’t part of the plan originally, but it’s certainly doable for 0.2.

Ah, ok it's fine for now then, we can reevaluate when imago handles this, don't worry.

@slp
Copy link
Collaborator Author

slp commented Dec 3, 2025

@mtjhrc @tylerfanelli we need an approval here

Copy link
Member

@tylerfanelli tylerfanelli left a comment

Choose a reason for hiding this comment

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

LGTM.

@slp slp merged commit 25b14d1 into containers:main Dec 4, 2025
9 checks passed
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.

4 participants