Merge changes I00deb50c,I82650277 into main
* changes: Clean up userspace-reboot (#2) Clean up userspace-reboot
This commit is contained in:
commit
b29f0dcb5b
9 changed files with 23 additions and 544 deletions
|
|
@ -1433,16 +1433,16 @@ bool WasMetadataEncryptionInterrupted(const FstabEntry& entry) {
|
|||
// When multiple fstab records share the same mount_point, it will try to mount each
|
||||
// one in turn, and ignore any duplicates after a first successful mount.
|
||||
// Returns -1 on error, and FS_MGR_MNTALL_* otherwise.
|
||||
MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
|
||||
int fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
|
||||
int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;
|
||||
int error_count = 0;
|
||||
CheckpointManager checkpoint_manager;
|
||||
AvbUniquePtr avb_handle(nullptr);
|
||||
bool wiped = false;
|
||||
|
||||
bool userdata_mounted = false;
|
||||
|
||||
if (fstab->empty()) {
|
||||
return {FS_MGR_MNTALL_FAIL, userdata_mounted};
|
||||
return FS_MGR_MNTALL_FAIL;
|
||||
}
|
||||
|
||||
bool scratch_can_be_mounted = true;
|
||||
|
|
@ -1521,7 +1521,7 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
|
|||
if (!avb_handle) {
|
||||
LERROR << "Failed to open AvbHandle";
|
||||
set_type_property(encryptable);
|
||||
return {FS_MGR_MNTALL_FAIL, userdata_mounted};
|
||||
return FS_MGR_MNTALL_FAIL;
|
||||
}
|
||||
}
|
||||
if (avb_handle->SetUpAvbHashtree(¤t_entry, true /* wait_for_verity_dev */) ==
|
||||
|
|
@ -1557,7 +1557,7 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
|
|||
|
||||
if (status == FS_MGR_MNTALL_FAIL) {
|
||||
// Fatal error - no point continuing.
|
||||
return {status, userdata_mounted};
|
||||
return status;
|
||||
}
|
||||
|
||||
if (status != FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
|
||||
|
|
@ -1577,7 +1577,7 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
|
|||
nullptr)) {
|
||||
LERROR << "Encryption failed";
|
||||
set_type_property(encryptable);
|
||||
return {FS_MGR_MNTALL_FAIL, userdata_mounted};
|
||||
return FS_MGR_MNTALL_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1696,9 +1696,9 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
|
|||
set_type_property(encryptable);
|
||||
|
||||
if (error_count) {
|
||||
return {FS_MGR_MNTALL_FAIL, userdata_mounted};
|
||||
return FS_MGR_MNTALL_FAIL;
|
||||
} else {
|
||||
return {encryptable, userdata_mounted};
|
||||
return encryptable;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1735,190 +1735,6 @@ int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
static std::chrono::milliseconds GetMillisProperty(const std::string& name,
|
||||
std::chrono::milliseconds default_value) {
|
||||
auto value = GetUintProperty(name, static_cast<uint64_t>(default_value.count()));
|
||||
return std::chrono::milliseconds(std::move(value));
|
||||
}
|
||||
|
||||
static bool fs_mgr_unmount_all_data_mounts(const std::string& data_block_device) {
|
||||
LINFO << __FUNCTION__ << "(): about to umount everything on top of " << data_block_device;
|
||||
Timer t;
|
||||
auto timeout = GetMillisProperty("init.userspace_reboot.userdata_remount.timeoutmillis", 5s);
|
||||
while (true) {
|
||||
bool umount_done = true;
|
||||
Fstab proc_mounts;
|
||||
if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
|
||||
LERROR << __FUNCTION__ << "(): Can't read /proc/mounts";
|
||||
return false;
|
||||
}
|
||||
// Now proceed with other bind mounts on top of /data.
|
||||
for (const auto& entry : proc_mounts) {
|
||||
std::string block_device;
|
||||
if (StartsWith(entry.blk_device, "/dev/block") &&
|
||||
!Realpath(entry.blk_device, &block_device)) {
|
||||
PWARNING << __FUNCTION__ << "(): failed to realpath " << entry.blk_device;
|
||||
block_device = entry.blk_device;
|
||||
}
|
||||
if (data_block_device == block_device) {
|
||||
if (umount2(entry.mount_point.c_str(), 0) != 0) {
|
||||
PERROR << __FUNCTION__ << "(): Failed to umount " << entry.mount_point;
|
||||
umount_done = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (umount_done) {
|
||||
LINFO << __FUNCTION__ << "(): Unmounting /data took " << t;
|
||||
return true;
|
||||
}
|
||||
if (t.duration() > timeout) {
|
||||
LERROR << __FUNCTION__ << "(): Timed out unmounting all mounts on "
|
||||
<< data_block_device;
|
||||
Fstab remaining_mounts;
|
||||
if (!ReadFstabFromFile("/proc/mounts", &remaining_mounts)) {
|
||||
LERROR << __FUNCTION__ << "(): Can't read /proc/mounts";
|
||||
} else {
|
||||
LERROR << __FUNCTION__ << "(): Following mounts remaining";
|
||||
for (const auto& e : remaining_mounts) {
|
||||
LERROR << __FUNCTION__ << "(): mount point: " << e.mount_point
|
||||
<< " block device: " << e.blk_device;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
std::this_thread::sleep_for(50ms);
|
||||
}
|
||||
}
|
||||
|
||||
static bool UnwindDmDeviceStack(const std::string& block_device,
|
||||
std::vector<std::string>* dm_stack) {
|
||||
if (!StartsWith(block_device, "/dev/block/")) {
|
||||
LWARNING << block_device << " is not a block device";
|
||||
return false;
|
||||
}
|
||||
std::string current = block_device;
|
||||
DeviceMapper& dm = DeviceMapper::Instance();
|
||||
while (true) {
|
||||
dm_stack->push_back(current);
|
||||
if (!dm.IsDmBlockDevice(current)) {
|
||||
break;
|
||||
}
|
||||
auto parent = dm.GetParentBlockDeviceByPath(current);
|
||||
if (!parent) {
|
||||
return false;
|
||||
}
|
||||
current = *parent;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
FstabEntry* fs_mgr_get_mounted_entry_for_userdata(Fstab* fstab,
|
||||
const std::string& data_block_device) {
|
||||
std::vector<std::string> dm_stack;
|
||||
if (!UnwindDmDeviceStack(data_block_device, &dm_stack)) {
|
||||
LERROR << "Failed to unwind dm-device stack for " << data_block_device;
|
||||
return nullptr;
|
||||
}
|
||||
for (auto& entry : *fstab) {
|
||||
if (entry.mount_point != "/data") {
|
||||
continue;
|
||||
}
|
||||
std::string block_device;
|
||||
if (entry.fs_mgr_flags.logical) {
|
||||
if (!fs_mgr_update_logical_partition(&entry)) {
|
||||
LERROR << "Failed to update logic partition " << entry.blk_device;
|
||||
continue;
|
||||
}
|
||||
block_device = entry.blk_device;
|
||||
} else if (!Realpath(entry.blk_device, &block_device)) {
|
||||
PWARNING << "Failed to realpath " << entry.blk_device;
|
||||
block_device = entry.blk_device;
|
||||
}
|
||||
if (std::find(dm_stack.begin(), dm_stack.end(), block_device) != dm_stack.end()) {
|
||||
return &entry;
|
||||
}
|
||||
}
|
||||
LERROR << "Didn't find entry that was used to mount /data onto " << data_block_device;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// TODO(b/143970043): return different error codes based on which step failed.
|
||||
int fs_mgr_remount_userdata_into_checkpointing(Fstab* fstab) {
|
||||
Fstab proc_mounts;
|
||||
if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
|
||||
LERROR << "Can't read /proc/mounts";
|
||||
return -1;
|
||||
}
|
||||
auto mounted_entry = GetEntryForMountPoint(&proc_mounts, "/data");
|
||||
if (mounted_entry == nullptr) {
|
||||
LERROR << "/data is not mounted";
|
||||
return -1;
|
||||
}
|
||||
std::string block_device;
|
||||
if (!Realpath(mounted_entry->blk_device, &block_device)) {
|
||||
PERROR << "Failed to realpath " << mounted_entry->blk_device;
|
||||
return -1;
|
||||
}
|
||||
auto fstab_entry = fs_mgr_get_mounted_entry_for_userdata(fstab, block_device);
|
||||
if (fstab_entry == nullptr) {
|
||||
LERROR << "Can't find /data in fstab";
|
||||
return -1;
|
||||
}
|
||||
bool force_umount = GetBoolProperty("sys.init.userdata_remount.force_umount", false);
|
||||
if (force_umount) {
|
||||
LINFO << "Will force an umount of userdata even if it's not required";
|
||||
}
|
||||
if (!force_umount && !SupportsCheckpoint(fstab_entry)) {
|
||||
LINFO << "Userdata doesn't support checkpointing. Nothing to do";
|
||||
return 0;
|
||||
}
|
||||
CheckpointManager checkpoint_manager;
|
||||
if (!force_umount && !checkpoint_manager.NeedsCheckpoint()) {
|
||||
LINFO << "Checkpointing not needed. Don't remount";
|
||||
return 0;
|
||||
}
|
||||
if (!force_umount && fstab_entry->fs_mgr_flags.checkpoint_fs) {
|
||||
// Userdata is f2fs, simply remount it.
|
||||
if (!checkpoint_manager.Update(fstab_entry)) {
|
||||
LERROR << "Failed to remount userdata in checkpointing mode";
|
||||
return -1;
|
||||
}
|
||||
if (mount(block_device.c_str(), fstab_entry->mount_point.c_str(), "none",
|
||||
MS_REMOUNT | fstab_entry->flags, fstab_entry->fs_options.c_str()) != 0) {
|
||||
PERROR << "Failed to remount userdata in checkpointing mode";
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
LINFO << "Unmounting /data before remounting into checkpointing mode";
|
||||
if (!fs_mgr_unmount_all_data_mounts(block_device)) {
|
||||
LERROR << "Failed to umount /data";
|
||||
return -1;
|
||||
}
|
||||
DeviceMapper& dm = DeviceMapper::Instance();
|
||||
while (dm.IsDmBlockDevice(block_device)) {
|
||||
auto next_device = dm.GetParentBlockDeviceByPath(block_device);
|
||||
auto name = dm.GetDmDeviceNameByPath(block_device);
|
||||
if (!name) {
|
||||
LERROR << "Failed to get dm-name for " << block_device;
|
||||
return -1;
|
||||
}
|
||||
LINFO << "Deleting " << block_device << " named " << *name;
|
||||
if (!dm.DeleteDevice(*name, 3s)) {
|
||||
return -1;
|
||||
}
|
||||
if (!next_device) {
|
||||
LERROR << "Failed to find parent device for " << block_device;
|
||||
}
|
||||
block_device = *next_device;
|
||||
}
|
||||
LINFO << "Remounting /data";
|
||||
// TODO(b/143970043): remove this hack after fs_mgr_mount_all is refactored.
|
||||
auto result = fs_mgr_mount_all(fstab, MOUNT_MODE_ONLY_USERDATA);
|
||||
return result.code == FS_MGR_MNTALL_FAIL ? -1 : 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// wrapper to __mount() and expects a fully prepared fstab_rec,
|
||||
// unlike fs_mgr_do_mount which does more things with avb / verity etc.
|
||||
int fs_mgr_do_mount_one(const FstabEntry& entry, const std::string& alt_mount_point) {
|
||||
|
|
|
|||
|
|
@ -58,13 +58,8 @@ enum mount_mode {
|
|||
#define FS_MGR_MNTALL_DEV_NEEDS_RECOVERY 4
|
||||
#define FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE 0
|
||||
#define FS_MGR_MNTALL_FAIL (-1)
|
||||
|
||||
struct MountAllResult {
|
||||
// One of the FS_MGR_MNTALL_* returned code defined above.
|
||||
int code;
|
||||
// Whether userdata was mounted as a result of |fs_mgr_mount_all| call.
|
||||
bool userdata_mounted;
|
||||
};
|
||||
// fs_mgr_mount_all() updates fstab entries that reference device-mapper.
|
||||
int fs_mgr_mount_all(android::fs_mgr::Fstab* fstab, int mount_mode);
|
||||
|
||||
struct HashtreeInfo {
|
||||
// The hash algorithm used to build the merkle tree.
|
||||
|
|
@ -75,13 +70,6 @@ struct HashtreeInfo {
|
|||
bool check_at_most_once;
|
||||
};
|
||||
|
||||
// fs_mgr_mount_all() updates fstab entries that reference device-mapper.
|
||||
// Returns a |MountAllResult|. The first element is one of the FS_MNG_MNTALL_* return codes
|
||||
// defined above, and the second element tells whether this call to fs_mgr_mount_all was responsible
|
||||
// for mounting userdata. Later is required for init to correctly enqueue fs-related events as part
|
||||
// of userdata remount during userspace reboot.
|
||||
MountAllResult fs_mgr_mount_all(android::fs_mgr::Fstab* fstab, int mount_mode);
|
||||
|
||||
#define FS_MGR_DOMNT_FAILED (-1)
|
||||
#define FS_MGR_DOMNT_BUSY (-2)
|
||||
#define FS_MGR_DOMNT_SUCCESS 0
|
||||
|
|
@ -127,11 +115,6 @@ enum FsMgrUmountStatus : int {
|
|||
// it destroys verity devices from device mapper after the device is unmounted.
|
||||
int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab);
|
||||
|
||||
// Finds a entry in |fstab| that was used to mount a /data on |data_block_device|.
|
||||
android::fs_mgr::FstabEntry* fs_mgr_get_mounted_entry_for_userdata(
|
||||
android::fs_mgr::Fstab* fstab, const std::string& data_block_device);
|
||||
int fs_mgr_remount_userdata_into_checkpointing(android::fs_mgr::Fstab* fstab);
|
||||
|
||||
// Finds the dm_bow device on which this block device is stacked, or returns
|
||||
// empty string
|
||||
std::string fs_mgr_find_bow_device(const std::string& block_device);
|
||||
|
|
|
|||
|
|
@ -1062,23 +1062,6 @@ TEST(fs_mgr, DefaultFstabContainsUserdata) {
|
|||
<< "Default fstab doesn't contain /data entry";
|
||||
}
|
||||
|
||||
TEST(fs_mgr, UserdataMountedFromDefaultFstab) {
|
||||
if (getuid() != 0) {
|
||||
GTEST_SKIP() << "Must be run as root.";
|
||||
return;
|
||||
}
|
||||
Fstab fstab;
|
||||
ASSERT_TRUE(ReadDefaultFstab(&fstab)) << "Failed to read default fstab";
|
||||
Fstab proc_mounts;
|
||||
ASSERT_TRUE(ReadFstabFromFile("/proc/mounts", &proc_mounts)) << "Failed to read /proc/mounts";
|
||||
auto mounted_entry = GetEntryForMountPoint(&proc_mounts, "/data");
|
||||
ASSERT_NE(mounted_entry, nullptr) << "/data is not mounted";
|
||||
std::string block_device;
|
||||
ASSERT_TRUE(android::base::Realpath(mounted_entry->blk_device, &block_device));
|
||||
ASSERT_NE(nullptr, fs_mgr_get_mounted_entry_for_userdata(&fstab, block_device))
|
||||
<< "/data wasn't mounted from default fstab";
|
||||
}
|
||||
|
||||
TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Readahead_Size_KB) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
|
|
|
|||
|
|
@ -606,8 +606,6 @@ static Result<void> queue_fs_event(int code) {
|
|||
return Error() << "Invalid code: " << code;
|
||||
}
|
||||
|
||||
static int initial_mount_fstab_return_code = -1;
|
||||
|
||||
/* <= Q: mount_all <fstab> [ <path> ]* [--<options>]*
|
||||
* >= R: mount_all [ <fstab> ] [--<options>]*
|
||||
*
|
||||
|
|
@ -648,19 +646,10 @@ static Result<void> do_mount_all(const BuiltinArguments& args) {
|
|||
import_late(mount_all->rc_paths);
|
||||
}
|
||||
|
||||
if (mount_fstab_result.userdata_mounted) {
|
||||
// This call to fs_mgr_mount_all mounted userdata. Keep the result in
|
||||
// order for userspace reboot to correctly remount userdata.
|
||||
LOG(INFO) << "Userdata mounted using "
|
||||
<< (mount_all->fstab_path.empty() ? "(default fstab)" : mount_all->fstab_path)
|
||||
<< " result : " << mount_fstab_result.code;
|
||||
initial_mount_fstab_return_code = mount_fstab_result.code;
|
||||
}
|
||||
|
||||
if (queue_event) {
|
||||
/* queue_fs_event will queue event based on mount_fstab return code
|
||||
* and return processed return code*/
|
||||
auto queue_fs_result = queue_fs_event(mount_fstab_result.code);
|
||||
auto queue_fs_result = queue_fs_event(mount_fstab_result);
|
||||
if (!queue_fs_result.ok()) {
|
||||
return Error() << "queue_fs_event() failed: " << queue_fs_result.error();
|
||||
}
|
||||
|
|
@ -1148,29 +1137,19 @@ static Result<void> ExecWithFunctionOnFailure(const std::vector<std::string>& ar
|
|||
}
|
||||
|
||||
static Result<void> ExecVdcRebootOnFailure(const std::string& vdc_arg) {
|
||||
bool should_reboot_into_recovery = true;
|
||||
auto reboot_reason = vdc_arg + "_failed";
|
||||
if (android::sysprop::InitProperties::userspace_reboot_in_progress().value_or(false)) {
|
||||
should_reboot_into_recovery = false;
|
||||
reboot_reason = "userspace_failed," + vdc_arg;
|
||||
}
|
||||
|
||||
auto reboot = [reboot_reason, should_reboot_into_recovery](const std::string& message) {
|
||||
auto reboot = [reboot_reason](const std::string& message) {
|
||||
// TODO (b/122850122): support this in gsi
|
||||
if (should_reboot_into_recovery) {
|
||||
if (IsFbeEnabled() && !android::gsi::IsGsiRunning()) {
|
||||
LOG(ERROR) << message << ": Rebooting into recovery, reason: " << reboot_reason;
|
||||
if (auto result = reboot_into_recovery(
|
||||
{"--prompt_and_wipe_data", "--reason="s + reboot_reason});
|
||||
!result.ok()) {
|
||||
LOG(FATAL) << "Could not reboot into recovery: " << result.error();
|
||||
}
|
||||
} else {
|
||||
LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason;
|
||||
if (IsFbeEnabled() && !android::gsi::IsGsiRunning()) {
|
||||
LOG(ERROR) << message << ": Rebooting into recovery, reason: " << reboot_reason;
|
||||
if (auto result = reboot_into_recovery(
|
||||
{"--prompt_and_wipe_data", "--reason="s + reboot_reason});
|
||||
!result.ok()) {
|
||||
LOG(FATAL) << "Could not reboot into recovery: " << result.error();
|
||||
}
|
||||
} else {
|
||||
LOG(ERROR) << message << ": rebooting, reason: " << reboot_reason;
|
||||
trigger_shutdown("reboot," + reboot_reason);
|
||||
LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1178,29 +1157,6 @@ static Result<void> ExecVdcRebootOnFailure(const std::string& vdc_arg) {
|
|||
return ExecWithFunctionOnFailure(args, reboot);
|
||||
}
|
||||
|
||||
static Result<void> do_remount_userdata(const BuiltinArguments& args) {
|
||||
if (initial_mount_fstab_return_code == -1) {
|
||||
return Error() << "Calling remount_userdata too early";
|
||||
}
|
||||
Fstab fstab;
|
||||
if (!ReadDefaultFstab(&fstab)) {
|
||||
// TODO(b/135984674): should we reboot here?
|
||||
return Error() << "Failed to read fstab";
|
||||
}
|
||||
// TODO(b/135984674): check that fstab contains /data.
|
||||
if (auto rc = fs_mgr_remount_userdata_into_checkpointing(&fstab); rc < 0) {
|
||||
std::string proc_mounts_output;
|
||||
android::base::ReadFileToString("/proc/mounts", &proc_mounts_output, true);
|
||||
android::base::WriteStringToFile(proc_mounts_output,
|
||||
"/metadata/userspacereboot/mount_info.txt");
|
||||
trigger_shutdown("reboot,mount_userdata_failed");
|
||||
}
|
||||
if (auto result = queue_fs_event(initial_mount_fstab_return_code); !result.ok()) {
|
||||
return Error() << "queue_fs_event() failed: " << result.error();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
static Result<void> do_installkey(const BuiltinArguments& args) {
|
||||
if (!is_file_crypto()) return {};
|
||||
|
||||
|
|
@ -1361,7 +1317,6 @@ const BuiltinFunctionMap& GetBuiltinFunctionMap() {
|
|||
{"umount_all", {0, 1, {false, do_umount_all}}},
|
||||
{"update_linker_config", {0, 0, {false, do_update_linker_config}}},
|
||||
{"readahead", {1, 2, {true, do_readahead}}},
|
||||
{"remount_userdata", {0, 0, {false, do_remount_userdata}}},
|
||||
{"restart", {1, 2, {false, do_restart}}},
|
||||
{"restorecon", {1, kMax, {true, do_restorecon}}},
|
||||
{"restorecon_recursive", {1, kMax, {true, do_restorecon_recursive}}},
|
||||
|
|
|
|||
|
|
@ -100,7 +100,6 @@ using android::properties::BuildTrie;
|
|||
using android::properties::ParsePropertyInfoFile;
|
||||
using android::properties::PropertyInfoAreaFile;
|
||||
using android::properties::PropertyInfoEntry;
|
||||
using android::sysprop::InitProperties::is_userspace_reboot_supported;
|
||||
|
||||
namespace android {
|
||||
namespace init {
|
||||
|
|
@ -567,8 +566,8 @@ std::optional<uint32_t> HandlePropertySet(const std::string& name, const std::st
|
|||
}
|
||||
LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
|
||||
<< process_log_string;
|
||||
if (value == "reboot,userspace" && !is_userspace_reboot_supported().value_or(false)) {
|
||||
*error = "Userspace reboot is not supported by this device";
|
||||
if (value == "reboot,userspace") {
|
||||
*error = "Userspace reboot is deprecated.";
|
||||
return {PROP_ERROR_INVALID_VALUE};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,12 +80,6 @@ TEST(property_service, userspace_reboot_not_supported) {
|
|||
GTEST_SKIP() << "Skipping test, must be run as root.";
|
||||
return;
|
||||
}
|
||||
const std::string original_value = GetProperty("init.userspace_reboot.is_supported", "");
|
||||
auto guard = android::base::make_scope_guard([&original_value]() {
|
||||
SetProperty("init.userspace_reboot.is_supported", original_value);
|
||||
});
|
||||
|
||||
ASSERT_TRUE(SetProperty("init.userspace_reboot.is_supported", "false"));
|
||||
EXPECT_FALSE(SetProperty("sys.powerctl", "reboot,userspace"));
|
||||
}
|
||||
|
||||
|
|
|
|||
210
init/reboot.cpp
210
init/reboot.cpp
|
|
@ -87,16 +87,6 @@ static bool shutting_down = false;
|
|||
|
||||
static const std::set<std::string> kDebuggingServices{"tombstoned", "logd", "adbd", "console"};
|
||||
|
||||
static std::set<std::string> GetPostDataDebuggingServices() {
|
||||
std::set<std::string> ret;
|
||||
for (const auto& s : ServiceList::GetInstance()) {
|
||||
if (kDebuggingServices.count(s->name()) && s->is_post_data()) {
|
||||
ret.insert(s->name());
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void PersistRebootReason(const char* reason, bool write_to_property) {
|
||||
if (write_to_property) {
|
||||
SetProperty(LAST_REBOOT_REASON_PROPERTY, reason);
|
||||
|
|
@ -810,196 +800,6 @@ static void EnterShutdown() {
|
|||
}
|
||||
}
|
||||
|
||||
static void LeaveShutdown() {
|
||||
LOG(INFO) << "Leaving shutdown mode";
|
||||
shutting_down = false;
|
||||
StartSendingMessages();
|
||||
}
|
||||
|
||||
static std::chrono::milliseconds GetMillisProperty(const std::string& name,
|
||||
std::chrono::milliseconds default_value) {
|
||||
auto value = GetUintProperty(name, static_cast<uint64_t>(default_value.count()));
|
||||
return std::chrono::milliseconds(std::move(value));
|
||||
}
|
||||
|
||||
static Result<void> DoUserspaceReboot() {
|
||||
LOG(INFO) << "Userspace reboot initiated";
|
||||
// An ugly way to pass a more precise reason on why fallback to hard reboot was triggered.
|
||||
std::string sub_reason = "";
|
||||
auto guard = android::base::make_scope_guard([&sub_reason] {
|
||||
// Leave shutdown so that we can handle a full reboot.
|
||||
LeaveShutdown();
|
||||
trigger_shutdown("reboot,userspace_failed,shutdown_aborted," + sub_reason);
|
||||
});
|
||||
// Triggering userspace-reboot-requested will result in a bunch of setprop
|
||||
// actions. We should make sure, that all of them are propagated before
|
||||
// proceeding with userspace reboot. Synchronously setting sys.init.userspace_reboot.in_progress
|
||||
// property is not perfect, but it should do the trick.
|
||||
if (!android::sysprop::InitProperties::userspace_reboot_in_progress(true)) {
|
||||
sub_reason = "setprop";
|
||||
return Error() << "Failed to set sys.init.userspace_reboot.in_progress property";
|
||||
}
|
||||
EnterShutdown();
|
||||
if (!SetProperty("sys.powerctl", "")) {
|
||||
sub_reason = "resetprop";
|
||||
return Error() << "Failed to reset sys.powerctl property";
|
||||
}
|
||||
std::set<std::string> stop_first;
|
||||
// Remember the services that were enabled. We will need to manually enable them again otherwise
|
||||
// triggers like class_start won't restart them.
|
||||
std::set<std::string> were_enabled;
|
||||
for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
|
||||
if (s->is_post_data() && !kDebuggingServices.count(s->name())) {
|
||||
stop_first.insert(s->name());
|
||||
}
|
||||
// TODO(ioffe): we should also filter out temporary services here.
|
||||
if (s->is_post_data() && s->IsEnabled()) {
|
||||
were_enabled.insert(s->name());
|
||||
}
|
||||
}
|
||||
{
|
||||
Timer sync_timer;
|
||||
LOG(INFO) << "sync() before terminating services...";
|
||||
sync();
|
||||
LOG(INFO) << "sync() took " << sync_timer;
|
||||
}
|
||||
auto sigterm_timeout = GetMillisProperty("init.userspace_reboot.sigterm.timeoutmillis", 5s);
|
||||
auto sigkill_timeout = GetMillisProperty("init.userspace_reboot.sigkill.timeoutmillis", 10s);
|
||||
LOG(INFO) << "Timeout to terminate services: " << sigterm_timeout.count() << "ms "
|
||||
<< "Timeout to kill services: " << sigkill_timeout.count() << "ms";
|
||||
std::string services_file_name = "/metadata/userspacereboot/services.txt";
|
||||
const int flags = O_RDWR | O_CREAT | O_SYNC | O_APPEND | O_CLOEXEC;
|
||||
StopServicesAndLogViolations(stop_first, sigterm_timeout, true /* SIGTERM */);
|
||||
if (int r = StopServicesAndLogViolations(stop_first, sigkill_timeout, false /* SIGKILL */);
|
||||
r > 0) {
|
||||
auto fd = unique_fd(TEMP_FAILURE_RETRY(open(services_file_name.c_str(), flags, 0666)));
|
||||
android::base::WriteStringToFd("Post-data services still running: \n", fd);
|
||||
for (const auto& s : ServiceList::GetInstance()) {
|
||||
if (s->IsRunning() && stop_first.count(s->name())) {
|
||||
android::base::WriteStringToFd(s->name() + "\n", fd);
|
||||
}
|
||||
}
|
||||
sub_reason = "sigkill";
|
||||
return Error() << r << " post-data services are still running";
|
||||
}
|
||||
if (auto result = KillZramBackingDevice(); !result.ok()) {
|
||||
sub_reason = "zram";
|
||||
return result;
|
||||
}
|
||||
if (auto result = CallVdc("volume", "reset"); !result.ok()) {
|
||||
sub_reason = "vold_reset";
|
||||
return result;
|
||||
}
|
||||
const auto& debugging_services = GetPostDataDebuggingServices();
|
||||
if (int r = StopServicesAndLogViolations(debugging_services, sigkill_timeout,
|
||||
false /* SIGKILL */);
|
||||
r > 0) {
|
||||
auto fd = unique_fd(TEMP_FAILURE_RETRY(open(services_file_name.c_str(), flags, 0666)));
|
||||
android::base::WriteStringToFd("Debugging services still running: \n", fd);
|
||||
for (const auto& s : ServiceList::GetInstance()) {
|
||||
if (s->IsRunning() && debugging_services.count(s->name())) {
|
||||
android::base::WriteStringToFd(s->name() + "\n", fd);
|
||||
}
|
||||
}
|
||||
sub_reason = "sigkill_debug";
|
||||
return Error() << r << " debugging services are still running";
|
||||
}
|
||||
{
|
||||
Timer sync_timer;
|
||||
LOG(INFO) << "sync() after stopping services...";
|
||||
sync();
|
||||
LOG(INFO) << "sync() took " << sync_timer;
|
||||
}
|
||||
if (auto result = UnmountAllApexes(); !result.ok()) {
|
||||
sub_reason = "apex";
|
||||
return result;
|
||||
}
|
||||
if (!SwitchToMountNamespaceIfNeeded(NS_BOOTSTRAP).ok()) {
|
||||
sub_reason = "ns_switch";
|
||||
return Error() << "Failed to switch to bootstrap namespace";
|
||||
}
|
||||
ActionManager::GetInstance().RemoveActionIf([](const auto& action) -> bool {
|
||||
if (action->IsFromApex()) {
|
||||
std::string trigger_name = action->BuildTriggersString();
|
||||
LOG(INFO) << "Removing action (" << trigger_name << ") from (" << action->filename()
|
||||
<< ":" << action->line() << ")";
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
// Remove services that were defined in an APEX
|
||||
ServiceList::GetInstance().RemoveServiceIf([](const std::unique_ptr<Service>& s) -> bool {
|
||||
if (s->is_from_apex()) {
|
||||
LOG(INFO) << "Removing service '" << s->name() << "' because it's defined in an APEX";
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
// Re-enable services
|
||||
for (const auto& s : ServiceList::GetInstance()) {
|
||||
if (were_enabled.count(s->name())) {
|
||||
LOG(INFO) << "Re-enabling service '" << s->name() << "'";
|
||||
s->Enable();
|
||||
}
|
||||
}
|
||||
ServiceList::GetInstance().ResetState();
|
||||
LeaveShutdown();
|
||||
ActionManager::GetInstance().QueueEventTrigger("userspace-reboot-resume");
|
||||
guard.Disable(); // Go on with userspace reboot.
|
||||
return {};
|
||||
}
|
||||
|
||||
static void UserspaceRebootWatchdogThread() {
|
||||
auto started_timeout = GetMillisProperty("init.userspace_reboot.started.timeoutmillis", 10s);
|
||||
if (!WaitForProperty("sys.init.userspace_reboot.in_progress", "1", started_timeout)) {
|
||||
LOG(ERROR) << "Userspace reboot didn't start in " << started_timeout.count()
|
||||
<< "ms. Switching to full reboot";
|
||||
// Init might be wedged, don't try to write reboot reason into a persistent property and do
|
||||
// a dirty reboot.
|
||||
PersistRebootReason("userspace_failed,watchdog_triggered,failed_to_start", false);
|
||||
RebootSystem(ANDROID_RB_RESTART2, "userspace_failed,watchdog_triggered,failed_to_start");
|
||||
}
|
||||
LOG(INFO) << "Starting userspace reboot watchdog";
|
||||
auto watchdog_timeout = GetMillisProperty("init.userspace_reboot.watchdog.timeoutmillis", 5min);
|
||||
LOG(INFO) << "UserspaceRebootWatchdog timeout: " << watchdog_timeout.count() << "ms";
|
||||
if (!WaitForProperty("sys.boot_completed", "1", watchdog_timeout)) {
|
||||
LOG(ERROR) << "Failed to boot in " << watchdog_timeout.count()
|
||||
<< "ms. Switching to full reboot";
|
||||
// In this case device is in a boot loop. Only way to recover is to do dirty reboot.
|
||||
// Since init might be wedged, don't try to write reboot reason into a persistent property.
|
||||
PersistRebootReason("userspace_failed,watchdog_triggered,failed_to_boot", false);
|
||||
RebootSystem(ANDROID_RB_RESTART2, "userspace_failed,watchdog_triggered,failed_to_boot");
|
||||
}
|
||||
LOG(INFO) << "Device booted, stopping userspace reboot watchdog";
|
||||
}
|
||||
|
||||
static void HandleUserspaceReboot() {
|
||||
if (!android::sysprop::InitProperties::is_userspace_reboot_supported().value_or(false)) {
|
||||
LOG(ERROR) << "Attempted a userspace reboot on a device that doesn't support it";
|
||||
return;
|
||||
}
|
||||
// Spinnig up a separate thread will fail the setns call later in the boot sequence.
|
||||
// Fork a new process to monitor userspace reboot while we are investigating a better solution.
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
PLOG(ERROR) << "Failed to fork process for userspace reboot watchdog. Switching to full "
|
||||
<< "reboot";
|
||||
trigger_shutdown("reboot,userspace_failed,watchdog_fork");
|
||||
return;
|
||||
}
|
||||
if (pid == 0) {
|
||||
// Child
|
||||
UserspaceRebootWatchdogThread();
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
LOG(INFO) << "Clearing queue and starting userspace-reboot-requested trigger";
|
||||
auto& am = ActionManager::GetInstance();
|
||||
am.ClearQueue();
|
||||
am.QueueEventTrigger("userspace-reboot-requested");
|
||||
auto handler = [](const BuiltinArguments&) { return DoUserspaceReboot(); };
|
||||
am.QueueBuiltinAction(handler, "userspace-reboot");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if "command" field is set in bootloader message.
|
||||
*
|
||||
|
|
@ -1030,7 +830,6 @@ void HandlePowerctlMessage(const std::string& command) {
|
|||
std::string reboot_target = "";
|
||||
bool run_fsck = false;
|
||||
bool command_invalid = false;
|
||||
bool userspace_reboot = false;
|
||||
|
||||
if (cmd_params[0] == "shutdown") {
|
||||
cmd = ANDROID_RB_POWEROFF;
|
||||
|
|
@ -1051,8 +850,8 @@ void HandlePowerctlMessage(const std::string& command) {
|
|||
if (cmd_params.size() >= 2) {
|
||||
reboot_target = cmd_params[1];
|
||||
if (reboot_target == "userspace") {
|
||||
LOG(INFO) << "Userspace reboot requested";
|
||||
userspace_reboot = true;
|
||||
LOG(ERROR) << "Userspace reboot is deprecated.";
|
||||
return;
|
||||
}
|
||||
// adb reboot fastboot should boot into bootloader for devices not
|
||||
// supporting logical partitions.
|
||||
|
|
@ -1130,11 +929,6 @@ void HandlePowerctlMessage(const std::string& command) {
|
|||
// messages, etc) from properties during reboot.
|
||||
StopSendingMessages();
|
||||
|
||||
if (userspace_reboot) {
|
||||
HandleUserspaceReboot();
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Clear action queue and start shutdown trigger";
|
||||
ActionManager::GetInstance().ClearQueue();
|
||||
// Queue shutdown trigger first
|
||||
|
|
|
|||
|
|
@ -620,7 +620,6 @@ on post-fs
|
|||
mkdir /metadata/bootstat 0750 system log
|
||||
mkdir /metadata/ota 0750 root system
|
||||
mkdir /metadata/ota/snapshots 0750 root system
|
||||
mkdir /metadata/userspacereboot 0770 root system
|
||||
mkdir /metadata/watchdog 0770 root system
|
||||
|
||||
mkdir /metadata/apex 0700 root system
|
||||
|
|
@ -1331,46 +1330,6 @@ on property:ro.debuggable=1
|
|||
on init && property:ro.debuggable=1
|
||||
start console
|
||||
|
||||
on userspace-reboot-requested
|
||||
# TODO(b/135984674): reset all necessary properties here.
|
||||
setprop sys.boot_completed ""
|
||||
setprop dev.bootcomplete ""
|
||||
setprop sys.init.updatable_crashing ""
|
||||
setprop sys.init.updatable_crashing_process_name ""
|
||||
setprop sys.user.0.ce_available ""
|
||||
setprop sys.shutdown.requested ""
|
||||
setprop service.bootanim.exit ""
|
||||
setprop service.bootanim.progress ""
|
||||
|
||||
on userspace-reboot-fs-remount
|
||||
# Make sure that vold is running.
|
||||
# This is mostly a precaution measure in case vold for some reason wasn't running when
|
||||
# userspace reboot was initiated.
|
||||
start vold
|
||||
exec - system system -- /system/bin/vdc checkpoint resetCheckpoint
|
||||
exec - system system -- /system/bin/vdc checkpoint markBootAttempt
|
||||
# Unmount /data_mirror mounts in the reverse order of corresponding mounts.
|
||||
umount /data_mirror/data_ce/null/0
|
||||
umount /data_mirror/data_ce/null
|
||||
umount /data_mirror/data_de/null
|
||||
umount /data_mirror/storage_area/0
|
||||
umount /data_mirror/storage_area
|
||||
umount /data_mirror/cur_profiles
|
||||
umount /data_mirror/ref_profiles
|
||||
umount /data_mirror
|
||||
remount_userdata
|
||||
start bootanim
|
||||
|
||||
on userspace-reboot-resume
|
||||
trigger userspace-reboot-fs-remount
|
||||
trigger post-fs-data
|
||||
trigger zygote-start
|
||||
trigger early-boot
|
||||
trigger boot
|
||||
|
||||
on property:sys.boot_completed=1 && property:sys.init.userspace_reboot.in_progress=1
|
||||
setprop sys.init.userspace_reboot.in_progress ""
|
||||
|
||||
# Multi-Gen LRU Experiment
|
||||
on property:persist.device_config.mglru_native.lru_gen_config=none
|
||||
write /sys/kernel/mm/lru_gen/enabled 0
|
||||
|
|
|
|||
|
|
@ -139,7 +139,3 @@ on property:sys.usb.typec.power_role=source
|
|||
on property:sys.usb.typec.power_role=sink
|
||||
write /sys/class/dual_role_usb/otg_default/power_role ${sys.usb.typec.power_role}
|
||||
setprop sys.usb.typec.state ${sys.usb.typec.power_role}
|
||||
|
||||
on userspace-reboot-requested
|
||||
setprop sys.usb.config ""
|
||||
setprop sys.usb.state ""
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue