Merge "build_image: improvements to right size for dynamic partitions"
This commit is contained in:
commit
1bc81731c8
|
@ -1206,7 +1206,7 @@ endif
|
||||||
|
|
||||||
ifeq ($(INTERNAL_USERIMAGES_USE_EXT),true)
|
ifeq ($(INTERNAL_USERIMAGES_USE_EXT),true)
|
||||||
INTERNAL_USERIMAGES_DEPS := $(SIMG2IMG)
|
INTERNAL_USERIMAGES_DEPS := $(SIMG2IMG)
|
||||||
INTERNAL_USERIMAGES_DEPS += $(MKEXTUSERIMG) $(MAKE_EXT4FS) $(E2FSCK)
|
INTERNAL_USERIMAGES_DEPS += $(MKEXTUSERIMG) $(MAKE_EXT4FS) $(E2FSCK) $(TUNE2FS)
|
||||||
ifeq ($(TARGET_USERIMAGES_USE_F2FS),true)
|
ifeq ($(TARGET_USERIMAGES_USE_F2FS),true)
|
||||||
INTERNAL_USERIMAGES_DEPS += $(MKF2FSUSERIMG) $(MAKE_F2FS)
|
INTERNAL_USERIMAGES_DEPS += $(MKF2FSUSERIMG) $(MAKE_F2FS)
|
||||||
endif
|
endif
|
||||||
|
@ -3099,6 +3099,7 @@ OTATOOLS := $(HOST_OUT_EXECUTABLES)/minigzip \
|
||||||
$(HOST_OUT_EXECUTABLES)/mke2fs \
|
$(HOST_OUT_EXECUTABLES)/mke2fs \
|
||||||
$(HOST_OUT_EXECUTABLES)/mkuserimg_mke2fs \
|
$(HOST_OUT_EXECUTABLES)/mkuserimg_mke2fs \
|
||||||
$(HOST_OUT_EXECUTABLES)/e2fsdroid \
|
$(HOST_OUT_EXECUTABLES)/e2fsdroid \
|
||||||
|
$(HOST_OUT_EXECUTABLES)/tune2fs \
|
||||||
$(HOST_OUT_EXECUTABLES)/mksquashfsimage.sh \
|
$(HOST_OUT_EXECUTABLES)/mksquashfsimage.sh \
|
||||||
$(HOST_OUT_EXECUTABLES)/mksquashfs \
|
$(HOST_OUT_EXECUTABLES)/mksquashfs \
|
||||||
$(HOST_OUT_EXECUTABLES)/mkf2fsuserimg.sh \
|
$(HOST_OUT_EXECUTABLES)/mkf2fsuserimg.sh \
|
||||||
|
|
|
@ -54,23 +54,69 @@ def GetDiskUsage(path):
|
||||||
"""Returns the number of bytes that "path" occupies on host.
|
"""Returns the number of bytes that "path" occupies on host.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
path: The directory or file to calculate size on
|
path: The directory or file to calculate size on.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The number of bytes.
|
The number of bytes based on a 1K block_size.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
BuildImageError: On error.
|
BuildImageError: On error.
|
||||||
"""
|
"""
|
||||||
env_copy = os.environ.copy()
|
cmd = ["du", "-k", "-s", path]
|
||||||
env_copy["POSIXLY_CORRECT"] = "1"
|
|
||||||
cmd = ["du", "-s", path]
|
|
||||||
try:
|
try:
|
||||||
output = common.RunAndCheckOutput(cmd, verbose=False, env=env_copy)
|
output = common.RunAndCheckOutput(cmd, verbose=False)
|
||||||
except common.ExternalError:
|
except common.ExternalError:
|
||||||
raise BuildImageError("Failed to get disk usage:\n{}".format(output))
|
raise BuildImageError("Failed to get disk usage:\n{}".format(output))
|
||||||
# POSIX du returns number of blocks with block size 512
|
return int(output.split()[0]) * 1024
|
||||||
return int(output.split()[0]) * 512
|
|
||||||
|
|
||||||
|
def GetInodeUsage(path):
|
||||||
|
"""Returns the number of inodes that "path" occupies on host.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path: The directory or file to calculate inode number on.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The number of inodes used.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
BuildImageError: On error.
|
||||||
|
"""
|
||||||
|
cmd = ["find", path, "-print"]
|
||||||
|
try:
|
||||||
|
output = common.RunAndCheckOutput(cmd, verbose=False)
|
||||||
|
except common.ExternalError:
|
||||||
|
raise BuildImageError("Failed to get disk inode usage:\n{}".format(output))
|
||||||
|
# increase by > 4% as number of files and directories is not whole picture.
|
||||||
|
return output.count('\n') * 25 // 24
|
||||||
|
|
||||||
|
|
||||||
|
def GetFilesystemCharacteristics(sparse_image_path):
|
||||||
|
"""Returns various filesystem characteristics of "sparse_image_path".
|
||||||
|
|
||||||
|
Args:
|
||||||
|
sparse_image_path: The file to analyze.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The characteristics dictionary.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
BuildImageError: On error.
|
||||||
|
"""
|
||||||
|
unsparse_image_path = UnsparseImage(sparse_image_path, replace=False)
|
||||||
|
|
||||||
|
cmd = ["tune2fs", "-l", unsparse_image_path]
|
||||||
|
try:
|
||||||
|
output = common.RunAndCheckOutput(cmd, verbose=False)
|
||||||
|
except common.ExternalError:
|
||||||
|
raise BuildImageError("Failed to get tune2fs usage:\n{}".format(output))
|
||||||
|
os.remove(unsparse_image_path)
|
||||||
|
fs_dict = { }
|
||||||
|
for line in output.splitlines():
|
||||||
|
fields = line.split(":")
|
||||||
|
if len(fields) == 2:
|
||||||
|
fs_dict[fields[0].strip()] = fields[1].strip()
|
||||||
|
return fs_dict
|
||||||
|
|
||||||
|
|
||||||
def UnsparseImage(sparse_image_path, replace=True):
|
def UnsparseImage(sparse_image_path, replace=True):
|
||||||
|
@ -121,6 +167,10 @@ def SetUpInDirAndFsConfig(origin_in, prop_dict):
|
||||||
if prop_dict["mount_point"] != "system":
|
if prop_dict["mount_point"] != "system":
|
||||||
return origin_in, fs_config
|
return origin_in, fs_config
|
||||||
|
|
||||||
|
if "first_pass" in prop_dict:
|
||||||
|
prop_dict["mount_point"] = "/"
|
||||||
|
return prop_dict["first_pass"]
|
||||||
|
|
||||||
# Construct a staging directory of the root file system.
|
# Construct a staging directory of the root file system.
|
||||||
in_dir = common.MakeTempDir()
|
in_dir = common.MakeTempDir()
|
||||||
root_dir = prop_dict.get("root_dir")
|
root_dir = prop_dict.get("root_dir")
|
||||||
|
@ -144,6 +194,7 @@ def SetUpInDirAndFsConfig(origin_in, prop_dict):
|
||||||
with open(fs_config) as fr:
|
with open(fs_config) as fr:
|
||||||
fw.writelines(fr.readlines())
|
fw.writelines(fr.readlines())
|
||||||
fs_config = merged_fs_config
|
fs_config = merged_fs_config
|
||||||
|
prop_dict["first_pass"] = (in_dir, fs_config)
|
||||||
return in_dir, fs_config
|
return in_dir, fs_config
|
||||||
|
|
||||||
|
|
||||||
|
@ -175,7 +226,7 @@ def CheckHeadroom(ext4fs_output, prop_dict):
|
||||||
m = ext4fs_stats.match(last_line)
|
m = ext4fs_stats.match(last_line)
|
||||||
used_blocks = int(m.groupdict().get('used_blocks'))
|
used_blocks = int(m.groupdict().get('used_blocks'))
|
||||||
total_blocks = int(m.groupdict().get('total_blocks'))
|
total_blocks = int(m.groupdict().get('total_blocks'))
|
||||||
headroom_blocks = int(prop_dict['partition_headroom']) / BLOCK_SIZE
|
headroom_blocks = int(prop_dict['partition_headroom']) // BLOCK_SIZE
|
||||||
adjusted_blocks = total_blocks - headroom_blocks
|
adjusted_blocks = total_blocks - headroom_blocks
|
||||||
if used_blocks > adjusted_blocks:
|
if used_blocks > adjusted_blocks:
|
||||||
mount_point = prop_dict["mount_point"]
|
mount_point = prop_dict["mount_point"]
|
||||||
|
@ -202,6 +253,7 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None):
|
||||||
Raises:
|
Raises:
|
||||||
BuildImageError: On build image failures.
|
BuildImageError: On build image failures.
|
||||||
"""
|
"""
|
||||||
|
original_mount_point = prop_dict["mount_point"]
|
||||||
in_dir, fs_config = SetUpInDirAndFsConfig(in_dir, prop_dict)
|
in_dir, fs_config = SetUpInDirAndFsConfig(in_dir, prop_dict)
|
||||||
|
|
||||||
build_command = []
|
build_command = []
|
||||||
|
@ -233,7 +285,8 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None):
|
||||||
size = GetDiskUsage(in_dir)
|
size = GetDiskUsage(in_dir)
|
||||||
logger.info(
|
logger.info(
|
||||||
"The tree size of %s is %d MB.", in_dir, size // BYTES_IN_MB)
|
"The tree size of %s is %d MB.", in_dir, size // BYTES_IN_MB)
|
||||||
size += int(prop_dict.get("partition_reserved_size", 0))
|
# If not specified, give us 16MB margin for GetDiskUsage error ...
|
||||||
|
size += int(prop_dict.get("partition_reserved_size", BYTES_IN_MB * 16))
|
||||||
# Round this up to a multiple of 4K so that avbtool works
|
# Round this up to a multiple of 4K so that avbtool works
|
||||||
size = common.RoundUpTo4K(size)
|
size = common.RoundUpTo4K(size)
|
||||||
# Adjust partition_size to add more space for AVB footer, to prevent
|
# Adjust partition_size to add more space for AVB footer, to prevent
|
||||||
|
@ -244,6 +297,35 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None):
|
||||||
lambda x: verity_utils.AVBCalcMaxImageSize(
|
lambda x: verity_utils.AVBCalcMaxImageSize(
|
||||||
avbtool, avb_footer_type, x, avb_signing_args))
|
avbtool, avb_footer_type, x, avb_signing_args))
|
||||||
prop_dict["partition_size"] = str(size)
|
prop_dict["partition_size"] = str(size)
|
||||||
|
if fs_type.startswith("ext"):
|
||||||
|
if "extfs_inode_count" not in prop_dict:
|
||||||
|
prop_dict["extfs_inode_count"] = str(GetInodeUsage(in_dir))
|
||||||
|
logger.info(
|
||||||
|
"First Pass based on estimates of %d MB and %s inodes.",
|
||||||
|
size // BYTES_IN_MB, prop_dict["extfs_inode_count"])
|
||||||
|
prop_dict["mount_point"] = original_mount_point
|
||||||
|
BuildImage(in_dir, prop_dict, out_file, target_out)
|
||||||
|
fs_dict = GetFilesystemCharacteristics(out_file)
|
||||||
|
block_size = int(fs_dict.get("Block size", "4096"))
|
||||||
|
free_size = int(fs_dict.get("Free blocks", "0")) * block_size
|
||||||
|
reserved_size = int(prop_dict.get("partition_reserved_size", 0))
|
||||||
|
if free_size <= reserved_size:
|
||||||
|
logger.info(
|
||||||
|
"Not worth reducing image %d <= %d.", free_size, reserved_size)
|
||||||
|
else:
|
||||||
|
size -= free_size
|
||||||
|
size += reserved_size
|
||||||
|
if block_size <= 4096:
|
||||||
|
size = common.RoundUpTo4K(size)
|
||||||
|
else:
|
||||||
|
size = ((size + block_size - 1) // block_size) * block_size
|
||||||
|
extfs_inode_count = prop_dict["extfs_inode_count"]
|
||||||
|
inodes = int(fs_dict.get("Inode count", extfs_inode_count))
|
||||||
|
inodes -= int(fs_dict.get("Free inodes", "0"))
|
||||||
|
prop_dict["extfs_inode_count"] = str(inodes)
|
||||||
|
prop_dict["partition_size"] = str(size)
|
||||||
|
logger.info(
|
||||||
|
"Allocating %d Inodes for %s.", inodes, out_file)
|
||||||
logger.info(
|
logger.info(
|
||||||
"Allocating %d MB for %s.", size // BYTES_IN_MB, out_file)
|
"Allocating %d MB for %s.", size // BYTES_IN_MB, out_file)
|
||||||
|
|
||||||
|
@ -363,7 +445,7 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None):
|
||||||
int(prop_dict.get("partition_reserved_size", 0)),
|
int(prop_dict.get("partition_reserved_size", 0)),
|
||||||
int(prop_dict.get("partition_reserved_size", 0)) // BYTES_IN_MB))
|
int(prop_dict.get("partition_reserved_size", 0)) // BYTES_IN_MB))
|
||||||
print(
|
print(
|
||||||
"The max image size for filsystem files is {} bytes ({} MB), out of a "
|
"The max image size for filesystem files is {} bytes ({} MB), out of a "
|
||||||
"total partition size of {} bytes ({} MB).".format(
|
"total partition size of {} bytes ({} MB).".format(
|
||||||
int(prop_dict["image_size"]),
|
int(prop_dict["image_size"]),
|
||||||
int(prop_dict["image_size"]) // BYTES_IN_MB,
|
int(prop_dict["image_size"]) // BYTES_IN_MB,
|
||||||
|
@ -677,7 +759,7 @@ def main(argv):
|
||||||
|
|
||||||
glob_dict = LoadGlobalDict(glob_dict_file)
|
glob_dict = LoadGlobalDict(glob_dict_file)
|
||||||
if "mount_point" in glob_dict:
|
if "mount_point" in glob_dict:
|
||||||
# The caller knows the mount point and provides a dictionay needed by
|
# The caller knows the mount point and provides a dictionary needed by
|
||||||
# BuildImage().
|
# BuildImage().
|
||||||
image_properties = glob_dict
|
image_properties = glob_dict
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -19,7 +19,7 @@ import os.path
|
||||||
|
|
||||||
import common
|
import common
|
||||||
from build_image import (
|
from build_image import (
|
||||||
BuildImageError, CheckHeadroom, SetUpInDirAndFsConfig)
|
BuildImageError, CheckHeadroom, GetFilesystemCharacteristics, SetUpInDirAndFsConfig)
|
||||||
from test_utils import ReleaseToolsTestCase
|
from test_utils import ReleaseToolsTestCase
|
||||||
|
|
||||||
|
|
||||||
|
@ -176,3 +176,25 @@ class BuildImageTest(ReleaseToolsTestCase):
|
||||||
self.assertIn('fs-config-system\n', fs_config_data)
|
self.assertIn('fs-config-system\n', fs_config_data)
|
||||||
self.assertIn('fs-config-root\n', fs_config_data)
|
self.assertIn('fs-config-root\n', fs_config_data)
|
||||||
self.assertEqual('/', prop_dict['mount_point'])
|
self.assertEqual('/', prop_dict['mount_point'])
|
||||||
|
|
||||||
|
def test_GetFilesystemCharacteristics(self):
|
||||||
|
input_dir = common.MakeTempDir()
|
||||||
|
output_image = common.MakeTempFile(suffix='.img')
|
||||||
|
command = ['mkuserimg_mke2fs', input_dir, output_image, 'ext4',
|
||||||
|
'/system', '409600', '-j', '0']
|
||||||
|
proc = common.Run(command)
|
||||||
|
ext4fs_output, _ = proc.communicate()
|
||||||
|
self.assertEqual(0, proc.returncode)
|
||||||
|
|
||||||
|
output_file = common.MakeTempFile(suffix='.img')
|
||||||
|
cmd = ["img2simg", output_image, output_file]
|
||||||
|
p = common.Run(cmd)
|
||||||
|
p.communicate()
|
||||||
|
self.assertEqual(0, p.returncode)
|
||||||
|
|
||||||
|
fs_dict = GetFilesystemCharacteristics(output_file)
|
||||||
|
self.assertEqual(int(fs_dict['Block size']), 4096)
|
||||||
|
self.assertGreaterEqual(int(fs_dict['Free blocks']), 0) # expect ~88
|
||||||
|
self.assertGreater(int(fs_dict['Inode count']), 0) # expect ~64
|
||||||
|
self.assertGreaterEqual(int(fs_dict['Free inodes']), 0) # expect ~53
|
||||||
|
self.assertGreater(int(fs_dict['Inode count']), int(fs_dict['Free inodes']))
|
||||||
|
|
Loading…
Reference in New Issue