From 82a431eb2fadf7a99b1649e028aba8a5d85931db Mon Sep 17 00:00:00 2001 From: Nikita Ioffe Date: Thu, 7 Nov 2019 15:37:38 +0000 Subject: [PATCH] Add a very basic userspace reboot watchdog Watchdog is just a forked process that is going to fall back to the full reboot in case device wasn't able to boot in given amount of time. Currently this amount is hard-coded to 1 minute, but in the future it will be controlled by a read-only property. Also added sync calls before and after tearing down services. Test: adb reboot userspace Bug: 135984674 Change-Id: Ie6053c9446a6761deae6dc104036bb35b09ef0e2 --- init/reboot.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/init/reboot.cpp b/init/reboot.cpp index 7040f26a4..7167672d0 100644 --- a/init/reboot.cpp +++ b/init/reboot.cpp @@ -760,6 +760,12 @@ static Result DoUserspaceReboot() { were_enabled.push_back(s); } } + { + Timer sync_timer; + LOG(INFO) << "sync() before terminating services..."; + sync(); + LOG(INFO) << "sync() took " << sync_timer; + } // TODO(b/135984674): do we need shutdown animation for userspace reboot? // TODO(b/135984674): control userspace timeout via read-only property? StopServicesAndLogViolations(stop_first, 10s, true /* SIGTERM */); @@ -775,6 +781,12 @@ static Result DoUserspaceReboot() { // TODO(b/135984674): store information about offending services for debugging. 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) { return result; } @@ -792,7 +804,38 @@ static Result DoUserspaceReboot() { return {}; } +static void UserspaceRebootWatchdogThread() { + if (!WaitForProperty("sys.init.userspace_reboot_in_progress", "1", 20s)) { + // TODO(b/135984674): should we reboot instead? + LOG(WARNING) << "Userspace reboot didn't start in 20 seconds. Stopping watchdog"; + return; + } + LOG(INFO) << "Starting userspace reboot watchdog"; + // TODO(b/135984674): this should be configured via a read-only sysprop. + std::chrono::milliseconds timeout = 60s; + if (!WaitForProperty("sys.boot_completed", "1", timeout)) { + LOG(ERROR) << "Failed to boot in " << 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. + RebootSystem(ANDROID_RB_RESTART2, "userspace-reboot-watchdog-triggered"); + } + LOG(INFO) << "Device booted, stopping userspace reboot watchdog"; +} + static void HandleUserspaceReboot() { + // 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-reboot-failed-to-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();