Sparse guest disk image files are a dream. I can have many guests on a
small amount of storage because they are only using what they need. Of
course, if each guest were to suddenly use all of the space in their
filesystems then the host filesystem containing the guest disk images
would fill up as well. However, since filesystems grow over time rather
than overnight, with proper monitoring you can foresee this event and
add more storage as needed.
Sparse guest disk images aren’t all bells and whistles though. Over time files are created/deleted within the filesystems on the disk images and the images themselves are no longer as compact as they were in the past. There is good news though; we can recover the space from all of those deleted files!
A Little History
With the rise of SSDs has come along a new low level command known as TRIM that allows the filesystem to notify the underlying block device of blocks that are no longer in use by the filesystem. This allows for improved performance in SSDs because delete operations can be handled in advance of write operations, thus speeding up writes.
Fortunately for us this TRIM notification also has plenty of application with thinly provisioned block devices. If the filesystem can notify a thin LV or a sparse disk image of blocks that are no longer being used then the blocks can be released back to the pool of available space.
“So I should be able to recover space from my guest disk images, right?” The answer is “yes”! It is relatively new, but virtio-scsi devices (QEMU) support TRIM operations. This is available in QEMU 1.5.0 by adding
discard=unmap to the
-drive option. You can also bypass the
QEMU command line by using Libvirt 1.0.6 and adding the
option to disk XML.
Creating/Configuring Guest For Discard
To take advantage of discard/TRIM operations I needed a guest that utilizes virtio-scsi. I created a guest with a virtio-scsi backed device by using the following
[root@host ~]# virt-install --name Fedora19 \ --disk path=/guests/Fedora19.img,size=30,bus=scsi \ --controller scsi,model=virtio-scsi \ --network=bridge:virbr0,model=virtio \ --accelerate --ram 2048 -c /images/F19.iso
The XML that was generated clearly shows that scsi controller 0 is of model virtio-scsi and thus all scsi devices on that controller will be virtio-scsi devices.
<controller type='scsi' index='0' model='virtio-scsi'> <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/> </controller>
The next step was to actually notify QEMU that we want to relay discard operations from the guest to the host. This is supported in QEMU 1.5.0 (since commit a9384aff5315e7568b6ebc171f4a482e01f06526). Fortunately libvirt also added support for this in version 1.0.6 (since commit a7c4202cdd12208dcd107fde3b79b2420d863370).
For libvirt, to make all discard/TRIM operations be passed from the guest back to the host I had to add the
discard='unmap' to the disk
XML description. After adding the option the XML looked like the
<disk type='file' device='disk'> <driver name='qemu' type='raw' discard='unmap'/> <source file='/guests/Fedora19.img'/> <target dev='sda' bus='scsi'/> <address type='drive' controller='0' bus='0' target='0' unit='0'/> </disk>
Trimming The Fat
After a power cycle of the guest I am now able to test it out. First I checked the disk image size and then copied a 1.2G file into the guest. Afterwards I confirmed the sparse disk image had increased size in the host.
[root@host ~]# du -sh /guests/Fedora19.img 1.1G /guests/Fedora19.img [root@host ~]# [root@host ~]# du -sh /tmp/code.tar.gz 1.2G /tmp/code.tar.gz [root@host ~]# [root@host ~]# scp /tmp/code.tar.gz firstname.lastname@example.org:/root/ email@example.com's password: code.tar.gz 100% 1134MB 81.0MB/s 00:14 : [root@host ~]# [root@host ~]# du -sh /guests/Fedora19.img 2.1G /guests/Fedora19.img
Within the guest I then deleted the file and executed the
command in order to notify the block devices that the blocks for that
file (and any other file that had been deleted) are no longer being used
by the filesystem.
[root@guest ~]# rm /root/code.tar.gz rm: remove regular file ‘/root/code.tar.gz’? y [root@guest ~]# [root@guest ~]# fstrim -v / /: 1.3 GiB (1372569600 bytes) trimmed
As can be seen from the output of the
fstrim command approximately
1.3G were trimmed. A final check of the guest disk image confirms that
the space was recovered in the host filesystem.
[root@host ~]# du -sh /guests/Fedora19.img 1.1G /guests/Fedora19.img
If anyone is interested I have posted my full guest libvirt XML here .
Until Next Time,
NOTE: An easy way to tell if trim operations are supported in the guest is to cat out the /sys/block/sda/queue/discard_* files. On my system that supports trim operations it looks like:
[root@guest ~]# cat /sys/block/sda/queue/discard_* 4096 4294966784 0
TRIM/SSD Reference Material: