Activate system APEXes early

Summary: Boot sequence around apexd is changed to make it possible for
pre-apexd processes to use libraries from APEXes. They no longer need to
wait for the apexd to finish activating APEXes, which again can be
done only after /data/ is mounted. This improves overall boot
performance.

Detail: This change fixes the problem that processes that are started
before apexd (so called pre-apexd processes) can't access libraries
that are provided only by the APEXes but are not found in the system
partition (e.g. libdexfile_external.so, etc.). Main idea is to activate
system APEXes (/system/apex/*.apex) before /data is mounted and then
activate the updated APEXes (/data/apex/*.apex) after the /data mount.

Detailed boot sequence is as follows.

1) init prepares the bootstrap and default mount namespaces. A tmpfs is
mounted on /apex and the propagation type of the mountpoint is set to
private.

2) before any other process is started, apexd is started in bootstrap
mode. When executed in the mode, apexd only activates APEXes under
/system/apex. Note that APEXes activated in this phase are mounted in
the bootstrap mount namespace only.

3) other pre-apexd processes are started. They are in the bootstrap
mount namespace and thus are provided with the libraries from the system
APEXes.

4) /data is mounted. init switches into the default mount namespace and
starts apexd as a daemon as usual.

5) apexd scans both /data/apex and /system/apex, and activate latest
APEXes from the directories. Note that APEXes activated in this phase
are mounted in the default namespaces only and thus are not visible to
the pre-apexd processes.

Bug: 125549215
Test: m; device boots
Change-Id: I21c60d0ebe188fa4f24d6e6861f85ca204843069
This commit is contained in:
Jiyong Park 2019-02-22 22:15:25 +09:00
parent 4ba548d845
commit dcbaf9f41b
4 changed files with 49 additions and 27 deletions

View File

@ -1119,13 +1119,21 @@ static Result<Success> do_parse_apex_configs(const BuiltinArguments& args) {
}
static Result<Success> do_setup_runtime_bionic(const BuiltinArguments& args) {
if (SwitchToDefaultMountNamespace()) {
if (SetupRuntimeBionic()) {
return Success();
} else {
return Error() << "Failed to setup runtime bionic";
}
}
static Result<Success> do_enter_default_mount_ns(const BuiltinArguments& args) {
if (SwitchToDefaultMountNamespace()) {
return Success();
} else {
return Error() << "Failed to enter into default mount namespace";
}
}
// Builtin-function-map start
const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
@ -1177,6 +1185,7 @@ const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
{"start", {1, 1, {false, do_start}}},
{"stop", {1, 1, {false, do_stop}}},
{"swapon_all", {1, 1, {false, do_swapon_all}}},
{"enter_default_mount_ns", {0, 0, {false, do_enter_default_mount_ns}}},
{"symlink", {2, 2, {true, do_symlink}}},
{"sysclktz", {1, 1, {false, do_sysclktz}}},
{"trigger", {1, 1, {false, do_trigger}}},

View File

@ -172,6 +172,11 @@ bool SetupMountNamespaces() {
kBionicLibsMountPointDir64))
return false;
// /apex is also a private mountpoint to give different sets of APEXes for
// the bootstrap and default mount namespaces. The processes running with
// the bootstrap namespace get APEXes from the read-only partition.
if (!(MakePrivate("/apex"))) return false;
bootstrap_ns_fd.reset(OpenMountNamespace());
bootstrap_ns_id = GetMountNamespaceId();
@ -227,6 +232,17 @@ bool SwitchToDefaultMountNamespace() {
}
}
LOG(INFO) << "Switched to default mount namespace";
return true;
}
// TODO(jiyong): remove this when /system/lib/libc.so becomes
// a symlink to /apex/com.android.runtime/lib/bionic/libc.so
bool SetupRuntimeBionic() {
if (IsRecoveryMode()) {
// We don't have multiple namespaces in recovery mode
return true;
}
// Bind-mount bionic from the runtime APEX since it is now available. Note
// that in case of IsBionicUpdatable() == false, these mounts are over the
// existing existing bind mounts for the bootstrap bionic, which effectively
@ -238,7 +254,7 @@ bool SwitchToDefaultMountNamespace() {
kBionicLibsMountPointDir64))
return false;
LOG(INFO) << "Switched to default mount namespace";
LOG(INFO) << "Runtime bionic is set up";
return true;
}

View File

@ -20,6 +20,7 @@ namespace android {
namespace init {
bool SetupMountNamespaces();
bool SetupRuntimeBionic();
bool SwitchToDefaultMountNamespace();
bool SwitchToBootstrapMountNamespaceIfNeeded();

View File

@ -13,12 +13,6 @@ import /init.${ro.zygote}.rc
# Cgroups are mounted right before early-init using list from /etc/cgroups.json
on early-init
# Mount shared so changes propagate into child namespaces
# Do this before other processes are started from init. Otherwise,
# processes launched while the propagation type of / is 'private'
# won't get mount events from others.
mount rootfs rootfs / shared rec
# Set init and its forked children's oom_adj.
write /proc/1/oom_score_adj -1000
@ -43,6 +37,11 @@ on early-init
start ueventd
# Run apexd-bootstrap so that APEXes that provide critical libraries
# become available. Note that this is executed as exec_start to ensure that
# the libraries are available to the processes started after this statement.
exec_start apexd-bootstrap
on init
sysclktz 0
@ -281,9 +280,6 @@ on init
# Start logd before any other services run to ensure we capture all of their logs.
start logd
# Start apexd as soon as we can
start apexd
# Start essential services.
start servicemanager
start hwservicemanager
@ -419,8 +415,16 @@ on post-fs-data
mkdir /data/bootchart 0755 shell shell
bootchart start
# /data/apex is now available. Let apexd to scan and activate APEXes.
setprop apexd.data.status ready
# Make sure that apexd is started in the default namespace
enter_default_mount_ns
# /data/apex is now available. Start apexd to scan and activate APEXes.
mkdir /data/apex 0750 root system
mkdir /data/apex/active 0750 root system
mkdir /data/apex/backup 0700 root system
mkdir /data/apex/sessions 0700 root system
mkdir /data/pkg_staging 0750 system system
start apexd
# Avoid predictable entropy pool. Carry over entropy from previous boot.
copy /data/system/entropy.dat /dev/urandom
@ -537,12 +541,6 @@ on post-fs-data
mkdir /data/anr 0775 system system
mkdir /data/apex 0750 root system
mkdir /data/apex/active 0750 root system
mkdir /data/apex/backup 0700 root system
mkdir /data/apex/sessions 0700 root system
mkdir /data/pkg_staging 0750 system system
# NFC: create data/nfc for nv storage
mkdir /data/nfc 0770 nfc nfc
mkdir /data/nfc/param 0770 nfc nfc
@ -575,6 +573,12 @@ on post-fs-data
mkdir /data/cache/backup_stage 0700 system system
mkdir /data/cache/backup 0700 system system
# Wait for apexd to finish activating APEXes before starting more processes.
wait_for_prop apexd.status ready
# TODO(jiyong): remove setup_runtime_bionic
setup_runtime_bionic
parse_apex_configs
init_user0
# Set SELinux security contexts on upgrade or policy update.
@ -583,14 +587,6 @@ on post-fs-data
# load fsverity keys
exec -- /system/bin/mini-keyctl -c /product/etc/security/cacerts_fsverity,/vendor/etc/security/cacerts_fsverity -k .fs-verity
# Wait for apexd to finish activating APEXes before starting more processes.
# This certainly reduces the parallelism but is required to make as many processes
# as possible to use the bionic libs from the runtime APEX. This takes less than 50ms
# so the impact on the booting time is not significant.
wait_for_prop apexd.status ready
setup_runtime_bionic
parse_apex_configs
# Check any timezone data in /data is newer than the copy in the runtime module, delete if not.
exec - system system -- /system/bin/tzdatacheck /apex/com.android.runtime/etc/tz /data/misc/zoneinfo