runtime-rs: every CH raw disk is preallocated to its full size since #13292

Author: alexander-turnerCreated Sep 16, 2026Updated Sep 16, 2026

Description of problem

Since #13292 (a886f99a0), TryFrom<BlockConfigModern> for DiskConfig in src/runtime-rs/crates/hypervisor/src/ch/inner_device.rs sets sparse: blkcfg.discard_unmap. discard_unmap defaults to false and is only set by the block-plain emptyDir volume, so every other block device reaches Cloud Hypervisor as sparse=false. Cloud Hypervisor v51.0 (cloud-hypervisor/cloud-hypervisor@49a30cbbaf) preallocates a raw disk file with fallocate() when sparse=false, and Kata pins v51.1.

On a host with the runtime-rs shim and the Cloud Hypervisor config, as root:

bash
# A sparse 4 GiB raw image. Only the ext4 metadata is allocated, about 33M.
truncate -s 4G /var/tmp/vol.img && mkfs.ext4 -q /var/tmp/vol.img
du -h /var/tmp/vol.img

# Register it as a direct volume behind the path /var/tmp/vol.
mkdir -p /var/tmp/vol
kata-ctl direct-volume add /var/tmp/vol '{"volume-type":"directvol","device":"/var/tmp/vol.img","fstype":"ext4","metadata":{},"options":[]}'

# Start a container that bind-mounts that path. The runtime attaches the image as a
# hot-plugged disk instead of sharing the directory.
ctr image pull docker.io/library/busybox:latest
ctr run --rm --runtime io.containerd.kata.v2 \
  --mount type=bind,src=/var/tmp/vol,dst=/mnt,options=rbind \
  docker.io/library/busybox:latest vol-test sh -c 'df -h /mnt'

# The allocation after the VMM opened the disk.
du -h /var/tmp/vol.img
filefrag -v /var/tmp/vol.img | head -5

Expected result

The second du reports about the same as the first, and the host disk loses only what the guest writes.

Actual result

The second du reports the full 4.0G, and filefrag -v shows one large unwritten extent, which only fallocate() produces. strace -f -e trace=fallocate -p $(pidof containerd) during the start shows the call:

fallocate(129, 0, 0, 4294967296) = 0

Measured today on a 4 GiB and an 8 GiB image attached to one pod: du went from 33M each to 4.1G and 8.1G. A 40 GiB host filled after three pods.

Further information

  • Kata 4.1.0 static tarball, runtime-rs shim, [hypervisor.clh], shared_fs = "none", block_device_driver = "virtio-blk-pci", rootless = true
  • Cloud Hypervisor v51.1 (the bundled binary), containerd 2.2.2, Ubuntu 26.04 arm64 guest under Lima on an Apple Silicon host
  • The shim here is the 4.1.0 source rebuilt with one unrelated patch to MemoryConfig.shared; the sparse line is the same as in the tarball's shim.
  • The Go runtime does not preallocate: NewDiskConfig() in the generated Cloud Hypervisor client sets Sparse = true and clh.go never overrides it. That is also Cloud Hypervisor's own default and the default in ch-config. Cloud Hypervisor has one flag for both "advertise DISCARD" and "do not preallocate", so the only way to stop discard_unmap=false disks from preallocating is to stop mapping it onto sparse (#13865).

Source: kata-containers/kata-containers