diff --git a/init/Android.bp b/init/Android.bp index 776a3a6c4..d939fccaa 100644 --- a/init/Android.bp +++ b/init/Android.bp @@ -65,6 +65,7 @@ cc_defaults { "libavb", "libc++fs", "libcgrouprc_format", + "liblmkd_utils", "libmodprobe", "libprotobuf-cpp-lite", "libpropertyinfoserializer", @@ -118,6 +119,7 @@ cc_library_static { "init.cpp", "interface_utils.cpp", "keychords.cpp", + "lmkd_service.cpp", "modalias_handler.cpp", "mount_handler.cpp", "mount_namespace.cpp", diff --git a/init/init.cpp b/init/init.cpp index f775d8fa0..6ea2d0092 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -59,6 +59,7 @@ #include "first_stage_mount.h" #include "import_parser.h" #include "keychords.h" +#include "lmkd_service.h" #include "mount_handler.h" #include "mount_namespace.h" #include "property_service.h" @@ -684,9 +685,15 @@ int SecondStageMain(int argc, char** argv) { InitKernelLogging(argv); LOG(INFO) << "init second stage started!"; + // Will handle EPIPE at the time of write by checking the errno + signal(SIGPIPE, SIG_IGN); + // Set init and its forked children's oom_adj. - if (auto result = WriteFile("/proc/1/oom_score_adj", "-1000"); !result) { - LOG(ERROR) << "Unable to write -1000 to /proc/1/oom_score_adj: " << result.error(); + if (auto result = + WriteFile("/proc/1/oom_score_adj", StringPrintf("%d", DEFAULT_OOM_SCORE_ADJUST)); + !result) { + LOG(ERROR) << "Unable to write " << DEFAULT_OOM_SCORE_ADJUST + << " to /proc/1/oom_score_adj: " << result.error(); } // Set up a session keyring that all processes will have access to. It diff --git a/init/lmkd_service.cpp b/init/lmkd_service.cpp new file mode 100644 index 000000000..dd1ab4d61 --- /dev/null +++ b/init/lmkd_service.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "lmkd_service.h" + +#include + +#include +#include + +#include "service_list.h" + +namespace android { +namespace init { + +enum LmkdRegistrationResult { + LMKD_REG_SUCCESS, + LMKD_CONN_FAILED, + LMKD_REG_FAILED, +}; + +static int lmkd_socket = -1; + +static LmkdRegistrationResult RegisterProcess(uid_t uid, pid_t pid, int oom_score_adjust) { + // connect to lmkd if not already connected + if (lmkd_socket < 0) { + lmkd_socket = lmkd_connect(); + if (lmkd_socket < 0) { + return LMKD_CONN_FAILED; + } + } + + // register service with lmkd + struct lmk_procprio params; + params.pid = pid; + params.uid = uid; + params.oomadj = oom_score_adjust; + params.ptype = PROC_TYPE_SERVICE; + if (lmkd_register_proc(lmkd_socket, ¶ms) != 0) { + // data transfer failed, reset the connection + close(lmkd_socket); + lmkd_socket = -1; + return LMKD_REG_FAILED; + } + + return LMKD_REG_SUCCESS; +} + +static bool UnregisterProcess(pid_t pid) { + if (lmkd_socket < 0) { + // no connection or it was lost, no need to unregister + return false; + } + + // unregister service + struct lmk_procremove params; + params.pid = pid; + if (lmkd_unregister_proc(lmkd_socket, ¶ms) != 0) { + // data transfer failed, reset the connection + close(lmkd_socket); + lmkd_socket = -1; + return false; + } + + return true; +} + +static void RegisterServices(pid_t exclude_pid) { + for (const auto& service : ServiceList::GetInstance().services()) { + auto svc = service.get(); + if (svc->oom_score_adjust() != DEFAULT_OOM_SCORE_ADJUST) { + // skip if process is excluded or not yet forked (pid==0) + if (svc->pid() == exclude_pid || svc->pid() == 0) { + continue; + } + if (RegisterProcess(svc->uid(), svc->pid(), svc->oom_score_adjust()) != + LMKD_REG_SUCCESS) { + // a failure here resets the connection, will retry during next registration + break; + } + } + } +} + +void LmkdRegister(const std::string& name, uid_t uid, pid_t pid, int oom_score_adjust) { + bool new_connection = lmkd_socket == -1; + LmkdRegistrationResult result; + + result = RegisterProcess(uid, pid, oom_score_adjust); + if (result == LMKD_REG_FAILED) { + // retry one time if connection to lmkd was lost + result = RegisterProcess(uid, pid, oom_score_adjust); + new_connection = result == LMKD_REG_SUCCESS; + } + switch (result) { + case LMKD_REG_SUCCESS: + // register existing services once new connection is established + if (new_connection) { + RegisterServices(pid); + } + break; + case LMKD_CONN_FAILED: + PLOG(ERROR) << "lmkd connection failed when " << name << " process got started"; + break; + case LMKD_REG_FAILED: + PLOG(ERROR) << "lmkd failed to register " << name << " process"; + break; + } +} + +void LmkdUnregister(const std::string& name, pid_t pid) { + if (!UnregisterProcess(pid)) { + PLOG(ERROR) << "lmkd failed to unregister " << name << " process"; + } +} + +} // namespace init +} // namespace android diff --git a/init/lmkd_service.h b/init/lmkd_service.h new file mode 100644 index 000000000..5b51d528f --- /dev/null +++ b/init/lmkd_service.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include + +namespace android { +namespace init { + +static const int MIN_OOM_SCORE_ADJUST = -1000; +static const int MAX_OOM_SCORE_ADJUST = 1000; +// service with default score is unkillable +static const int DEFAULT_OOM_SCORE_ADJUST = MIN_OOM_SCORE_ADJUST; + +#if defined(__ANDROID__) + +void LmkdRegister(const std::string& name, uid_t uid, pid_t pid, int oom_score_adjust); +void LmkdUnregister(const std::string& name, pid_t pid); + +#else // defined(__ANDROID__) + +static inline void LmkdRegister(const std::string&, uid_t, pid_t, int) {} +static inline void LmkdUnregister(const std::string&, pid_t) {} + +#endif // defined(__ANDROID__) + +} // namespace init +} // namespace android diff --git a/init/service.cpp b/init/service.cpp index c8568a0e9..f8e98a2e9 100644 --- a/init/service.cpp +++ b/init/service.cpp @@ -36,6 +36,7 @@ #include #include +#include "lmkd_service.h" #include "service_list.h" #include "util.h" @@ -151,7 +152,7 @@ Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid, seclabel_(seclabel), onrestart_(false, subcontext_for_restart_commands, "", 0, "onrestart", {}), - oom_score_adjust_(-1000), + oom_score_adjust_(DEFAULT_OOM_SCORE_ADJUST), start_order_(0), args_(args) {} @@ -199,6 +200,10 @@ void Service::KillProcessGroup(int signal) { if (r == 0) process_cgroup_empty_ = true; } + + if (oom_score_adjust_ != DEFAULT_OOM_SCORE_ADJUST) { + LmkdUnregister(name_, pid_); + } } void Service::SetProcessAttributesAndCaps() { @@ -502,7 +507,7 @@ Result Service::Start() { return ErrnoError() << "Failed to fork"; } - if (oom_score_adjust_ != -1000) { + if (oom_score_adjust_ != DEFAULT_OOM_SCORE_ADJUST) { std::string oom_str = std::to_string(oom_score_adjust_); std::string oom_file = StringPrintf("/proc/%d/oom_score_adj", pid); if (!WriteStringToFile(oom_str, oom_file)) { @@ -563,6 +568,10 @@ Result Service::Start() { } } + if (oom_score_adjust_ != DEFAULT_OOM_SCORE_ADJUST) { + LmkdRegister(name_, proc_attr_.uid, pid_, oom_score_adjust_); + } + NotifyStateChange("running"); reboot_on_failure.Disable(); return {}; diff --git a/init/service_parser.cpp b/init/service_parser.cpp index e6a341d77..154d1dd41 100644 --- a/init/service_parser.cpp +++ b/init/service_parser.cpp @@ -29,6 +29,7 @@ #include #include +#include "lmkd_service.h" #include "rlimit_parser.h" #include "service_utils.h" #include "util.h" @@ -261,8 +262,10 @@ Result ServiceParser::ParseNamespace(std::vector&& args) { } Result ServiceParser::ParseOomScoreAdjust(std::vector&& args) { - if (!ParseInt(args[1], &service_->oom_score_adjust_, -1000, 1000)) { - return Error() << "oom_score_adjust value must be in range -1000 - +1000"; + if (!ParseInt(args[1], &service_->oom_score_adjust_, MIN_OOM_SCORE_ADJUST, + MAX_OOM_SCORE_ADJUST)) { + return Error() << "oom_score_adjust value must be in range " << MIN_OOM_SCORE_ADJUST + << " - +" << MAX_OOM_SCORE_ADJUST; } return {}; } diff --git a/init/service_test.cpp b/init/service_test.cpp index c9cc7bdf0..c158b0a5f 100644 --- a/init/service_test.cpp +++ b/init/service_test.cpp @@ -23,6 +23,7 @@ #include +#include "lmkd_service.h" #include "util.h" namespace android { @@ -49,7 +50,7 @@ TEST(service, pod_initialized) { EXPECT_EQ(IoSchedClass_NONE, service_in_old_memory->ioprio_class()); EXPECT_EQ(0, service_in_old_memory->ioprio_pri()); EXPECT_EQ(0, service_in_old_memory->priority()); - EXPECT_EQ(-1000, service_in_old_memory->oom_score_adjust()); + EXPECT_EQ(DEFAULT_OOM_SCORE_ADJUST, service_in_old_memory->oom_score_adjust()); EXPECT_FALSE(service_in_old_memory->process_cgroup_empty()); for (std::size_t i = 0; i < memory_size; ++i) { @@ -68,7 +69,7 @@ TEST(service, pod_initialized) { EXPECT_EQ(IoSchedClass_NONE, service_in_old_memory2->ioprio_class()); EXPECT_EQ(0, service_in_old_memory2->ioprio_pri()); EXPECT_EQ(0, service_in_old_memory2->priority()); - EXPECT_EQ(-1000, service_in_old_memory2->oom_score_adjust()); + EXPECT_EQ(DEFAULT_OOM_SCORE_ADJUST, service_in_old_memory2->oom_score_adjust()); EXPECT_FALSE(service_in_old_memory->process_cgroup_empty()); } diff --git a/rootdir/init.rc b/rootdir/init.rc index a8e0f5f11..75d063f44 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -347,6 +347,8 @@ on init # Start logd before any other services run to ensure we capture all of their logs. start logd + # Start lmkd before any other services run so that it can register them + start lmkd # Start essential services. start servicemanager