cros_sdk: Run fstrim to keep chroot.img sparse
chroot.img is a sparse image, but it gradually becomes allocated as
space is used inside the chroot. The filesystem doesn't garbage collect
the freed space as files are deleted, so chroot.img eventually uses
much more space than the chroot contents need.
Call fstrim in cros_sdk after mounting the chroot to recover the unused
space. fstrim takes some time to run, so only call it if it's likely to
free enough space to be worthwhile (arbitrarily defined as 20GB).
BUG=chromium:776417
TEST=Deleted large files inside chroot and verified that space was
recovered after running cros_sdk.
Change-Id: Id84b2dfac7ef03a5eab96f59726ea5b2b17b227e
Reviewed-on: https://chromium-review.googlesource.com/776003
Commit-Ready: Benjamin Gordon <bmgordon@chromium.org>
Tested-by: Benjamin Gordon <bmgordon@chromium.org>
Reviewed-by: Mike Frysinger <vapier@chromium.org>
diff --git a/scripts/cros_sdk.py b/scripts/cros_sdk.py
index 3d2ae10..b2492a7 100644
--- a/scripts/cros_sdk.py
+++ b/scripts/cros_sdk.py
@@ -71,6 +71,16 @@
IMAGE_NEEDED_TOOLS = ('losetup', 'lvchange', 'lvcreate', 'lvs', 'mke2fs',
'pvscan', 'thin_check', 'vgchange', 'vgcreate', 'vgs')
+# As space is used inside the chroot, the empty space in chroot.img is
+# allocated. Deleting files inside the chroot doesn't automatically return the
+# used space to the OS. Over time, this tends to make the sparse chroot.img
+# less sparse even if the chroot contents don't currently need much space. We
+# can recover most of this unused space with fstrim, but that takes too much
+# time to run it every time. Instead, check the used space against the image
+# size after mounting the chroot and only call fstrim if it looks like we could
+# recover at least this many GiB.
+MAX_UNUSED_IMAGE_GBS = 20
+
def GetArchStageTarballs(version):
"""Returns the URL for a given arch/version"""
@@ -1025,6 +1035,27 @@
chroot_lv):
cros_build_lib.Die('Unable to create snapshot.')
+ img_path = _ImageFileForChroot(options.chroot)
+ if (options.use_image and os.path.exists(options.chroot) and
+ os.path.exists(img_path)):
+ img_stat = os.stat(img_path)
+ img_used_bytes = img_stat.st_blocks * 512
+
+ mount_stat = os.statvfs(options.chroot)
+ mount_used_bytes = mount_stat.f_frsize * (mount_stat.f_blocks -
+ mount_stat.f_bfree)
+
+ extra_gbs = (img_used_bytes - mount_used_bytes) / 2**30
+ if extra_gbs > MAX_UNUSED_IMAGE_GBS:
+ logging.notice('%s is using %s GiB more than needed. Running '
+ 'fstrim.', img_path, extra_gbs)
+ cmd = ['fstrim', options.chroot]
+ try:
+ cros_build_lib.RunCommand(cmd, print_cmd=False)
+ except cros_build_lib.RunCommandError as e:
+ logging.warning('Running fstrim failed. Consider running fstrim on '
+ 'your chroot manually.\nError: %s', e)
+
# Enter a new set of namespaces. Everything after here cannot directly affect
# the hosts's mounts or alter LVM volumes.
namespaces.SimpleUnshare()