Loading init/first_stage_init.cpp +6 −3 Original line number Diff line number Diff line Loading @@ -157,7 +157,7 @@ std::string GetModuleLoadList(bool recovery, const std::string& dir_path) { } #define MODULE_BASE_DIR "/lib/modules" bool LoadKernelModules(bool recovery, bool want_console, int& modules_loaded) { bool LoadKernelModules(bool recovery, bool want_console, bool want_parallel, int& modules_loaded) { struct utsname uts; if (uname(&uts)) { LOG(FATAL) << "Failed to get kernel version."; Loading Loading @@ -206,7 +206,8 @@ bool LoadKernelModules(bool recovery, bool want_console, int& modules_loaded) { } Modprobe m({MODULE_BASE_DIR}, GetModuleLoadList(recovery, MODULE_BASE_DIR)); bool retval = m.LoadListedModules(!want_console); bool retval = (want_parallel) ? m.LoadModulesParallel(std::thread::hardware_concurrency()) : m.LoadListedModules(!want_console); modules_loaded = m.GetModuleCount(); if (modules_loaded > 0) { return retval; Loading Loading @@ -319,11 +320,13 @@ int FirstStageMain(int argc, char** argv) { } auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline, bootconfig) : 0; auto want_parallel = bootconfig.find("androidboot.load_modules_parallel = \"true\"") != std::string::npos; boot_clock::time_point module_start_time = boot_clock::now(); int module_count = 0; if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console, module_count)) { want_parallel, module_count)) { if (want_console != FirstStageConsoleParam::DISABLED) { LOG(ERROR) << "Failed to load kernel modules, starting console"; } else { Loading libmodprobe/include/modprobe/modprobe.h +6 −0 Original line number Diff line number Diff line Loading @@ -16,17 +16,21 @@ #pragma once #include <mutex> #include <set> #include <string> #include <unordered_map> #include <unordered_set> #include <vector> #include <android-base/thread_annotations.h> class Modprobe { public: Modprobe(const std::vector<std::string>&, const std::string load_file = "modules.load", bool use_blocklist = true); bool LoadModulesParallel(int num_threads); bool LoadListedModules(bool strict = true); bool LoadWithAliases(const std::string& module_name, bool strict, const std::string& parameters = ""); Loading Loading @@ -66,7 +70,9 @@ class Modprobe { std::vector<std::string> module_load_; std::unordered_map<std::string, std::string> module_options_; std::set<std::string> module_blocklist_; std::mutex module_loaded_lock_; std::unordered_set<std::string> module_loaded_; std::unordered_set<std::string> module_loaded_paths_; int module_count_ = 0; bool blocklist_enabled = false; }; libmodprobe/libmodprobe.cpp +93 −0 Original line number Diff line number Diff line Loading @@ -21,8 +21,10 @@ #include <sys/syscall.h> #include <algorithm> #include <map> #include <set> #include <string> #include <thread> #include <vector> #include <android-base/chrono_utils.h> Loading Loading @@ -437,6 +439,97 @@ bool Modprobe::IsBlocklisted(const std::string& module_name) { return module_blocklist_.count(canonical_name) > 0; } // Another option to load kernel modules. load in independent modules in parallel // and then load modules which only have soft dependency, third update dependency list of other // remaining modules, repeat these steps until all modules are loaded. bool Modprobe::LoadModulesParallel(int num_threads) { bool ret = true; std::map<std::string, std::set<std::string>> mod_with_deps; std::map<std::string, std::set<std::string>> mod_with_softdeps; // Get dependencies for (const auto& module : module_load_) { auto dependencies = GetDependencies(MakeCanonical(module)); for (auto dep = dependencies.rbegin(); dep != dependencies.rend(); dep++) { mod_with_deps[module].emplace(*dep); } } // Get soft dependencies for (const auto& [it_mod, it_softdep] : module_pre_softdep_) { mod_with_softdeps[MakeCanonical(it_mod)].emplace(it_softdep); } // Get soft post dependencies for (const auto& [it_mod, it_softdep] : module_post_softdep_) { mod_with_softdeps[MakeCanonical(it_mod)].emplace(it_softdep); } while (!mod_with_deps.empty()) { std::vector<std::thread> threads; std::vector<std::string> mods_path_to_load; std::vector<std::string> mods_with_softdep_to_load; std::mutex vector_lock; // Find independent modules and modules only having soft dependencies for (const auto& [it_mod, it_dep] : mod_with_deps) { if (it_dep.size() == 1 && mod_with_softdeps[it_mod].empty()) { mods_path_to_load.emplace_back(*(it_dep.begin())); } else if (it_dep.size() == 1) { mods_with_softdep_to_load.emplace_back(it_mod); } } // Load independent modules in parallel auto thread_function = [&] { std::unique_lock lk(vector_lock); while (!mods_path_to_load.empty()) { auto mod_path_to_load = std::move(mods_path_to_load.back()); mods_path_to_load.pop_back(); lk.unlock(); ret &= Insmod(mod_path_to_load, ""); lk.lock(); } }; std::generate_n(std::back_inserter(threads), num_threads, [&] { return std::thread(thread_function); }); // Wait for the threads. for (auto& thread : threads) { thread.join(); } // Since we cannot assure if these soft dependencies tree are overlap, // we loaded these modules one by one. for (auto dep = mods_with_softdep_to_load.rbegin(); dep != mods_with_softdep_to_load.rend(); dep++) { ret &= LoadWithAliases(*dep, true); } std::lock_guard guard(module_loaded_lock_); // Remove loaded module form mod_with_deps and soft dependencies of other modules for (const auto& module_loaded : module_loaded_) { mod_with_deps.erase(module_loaded); for (auto& [mod, softdeps] : mod_with_softdeps) { softdeps.erase(module_loaded); } } // Remove loaded module form dependencies of other modules which are not loaded yet for (const auto& module_loaded_path : module_loaded_paths_) { for (auto& [mod, deps] : mod_with_deps) { deps.erase(module_loaded_path); } } } return ret; } bool Modprobe::LoadListedModules(bool strict) { auto ret = true; for (const auto& module : module_load_) { Loading libmodprobe/libmodprobe_ext.cpp +5 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,8 @@ bool Modprobe::Insmod(const std::string& path_name, const std::string& parameter if (ret != 0) { if (errno == EEXIST) { // Module already loaded std::lock_guard guard(module_loaded_lock_); module_loaded_paths_.emplace(path_name); module_loaded_.emplace(canonical_name); return true; } Loading @@ -62,6 +64,8 @@ bool Modprobe::Insmod(const std::string& path_name, const std::string& parameter } LOG(INFO) << "Loaded kernel module " << path_name; std::lock_guard guard(module_loaded_lock_); module_loaded_paths_.emplace(path_name); module_loaded_.emplace(canonical_name); module_count_++; return true; Loading @@ -74,6 +78,7 @@ bool Modprobe::Rmmod(const std::string& module_name) { PLOG(ERROR) << "Failed to remove module '" << module_name << "'"; return false; } std::lock_guard guard(module_loaded_lock_); module_loaded_.erase(canonical_name); return true; } Loading libmodprobe/libmodprobe_test.cpp +5 −4 Original line number Diff line number Diff line Loading @@ -188,10 +188,11 @@ TEST(libmodprobe, Test) { EXPECT_TRUE(modules_loaded == expected_after_remove); m = Modprobe({dir.path}); EXPECT_FALSE(m.LoadWithAliases("test4", true)); while (modules_loaded.size() > 0) EXPECT_TRUE(m.Remove(modules_loaded.front())); EXPECT_TRUE(m.LoadListedModules()); Modprobe m2({dir.path}); EXPECT_FALSE(m2.LoadWithAliases("test4", true)); while (modules_loaded.size() > 0) EXPECT_TRUE(m2.Remove(modules_loaded.front())); EXPECT_TRUE(m2.LoadListedModules()); GTEST_LOG_(INFO) << "Expected modules loaded after enabling blocklist (in order):"; for (auto i = expected_modules_blocklist_enabled.begin(); Loading Loading
init/first_stage_init.cpp +6 −3 Original line number Diff line number Diff line Loading @@ -157,7 +157,7 @@ std::string GetModuleLoadList(bool recovery, const std::string& dir_path) { } #define MODULE_BASE_DIR "/lib/modules" bool LoadKernelModules(bool recovery, bool want_console, int& modules_loaded) { bool LoadKernelModules(bool recovery, bool want_console, bool want_parallel, int& modules_loaded) { struct utsname uts; if (uname(&uts)) { LOG(FATAL) << "Failed to get kernel version."; Loading Loading @@ -206,7 +206,8 @@ bool LoadKernelModules(bool recovery, bool want_console, int& modules_loaded) { } Modprobe m({MODULE_BASE_DIR}, GetModuleLoadList(recovery, MODULE_BASE_DIR)); bool retval = m.LoadListedModules(!want_console); bool retval = (want_parallel) ? m.LoadModulesParallel(std::thread::hardware_concurrency()) : m.LoadListedModules(!want_console); modules_loaded = m.GetModuleCount(); if (modules_loaded > 0) { return retval; Loading Loading @@ -319,11 +320,13 @@ int FirstStageMain(int argc, char** argv) { } auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline, bootconfig) : 0; auto want_parallel = bootconfig.find("androidboot.load_modules_parallel = \"true\"") != std::string::npos; boot_clock::time_point module_start_time = boot_clock::now(); int module_count = 0; if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console, module_count)) { want_parallel, module_count)) { if (want_console != FirstStageConsoleParam::DISABLED) { LOG(ERROR) << "Failed to load kernel modules, starting console"; } else { Loading
libmodprobe/include/modprobe/modprobe.h +6 −0 Original line number Diff line number Diff line Loading @@ -16,17 +16,21 @@ #pragma once #include <mutex> #include <set> #include <string> #include <unordered_map> #include <unordered_set> #include <vector> #include <android-base/thread_annotations.h> class Modprobe { public: Modprobe(const std::vector<std::string>&, const std::string load_file = "modules.load", bool use_blocklist = true); bool LoadModulesParallel(int num_threads); bool LoadListedModules(bool strict = true); bool LoadWithAliases(const std::string& module_name, bool strict, const std::string& parameters = ""); Loading Loading @@ -66,7 +70,9 @@ class Modprobe { std::vector<std::string> module_load_; std::unordered_map<std::string, std::string> module_options_; std::set<std::string> module_blocklist_; std::mutex module_loaded_lock_; std::unordered_set<std::string> module_loaded_; std::unordered_set<std::string> module_loaded_paths_; int module_count_ = 0; bool blocklist_enabled = false; };
libmodprobe/libmodprobe.cpp +93 −0 Original line number Diff line number Diff line Loading @@ -21,8 +21,10 @@ #include <sys/syscall.h> #include <algorithm> #include <map> #include <set> #include <string> #include <thread> #include <vector> #include <android-base/chrono_utils.h> Loading Loading @@ -437,6 +439,97 @@ bool Modprobe::IsBlocklisted(const std::string& module_name) { return module_blocklist_.count(canonical_name) > 0; } // Another option to load kernel modules. load in independent modules in parallel // and then load modules which only have soft dependency, third update dependency list of other // remaining modules, repeat these steps until all modules are loaded. bool Modprobe::LoadModulesParallel(int num_threads) { bool ret = true; std::map<std::string, std::set<std::string>> mod_with_deps; std::map<std::string, std::set<std::string>> mod_with_softdeps; // Get dependencies for (const auto& module : module_load_) { auto dependencies = GetDependencies(MakeCanonical(module)); for (auto dep = dependencies.rbegin(); dep != dependencies.rend(); dep++) { mod_with_deps[module].emplace(*dep); } } // Get soft dependencies for (const auto& [it_mod, it_softdep] : module_pre_softdep_) { mod_with_softdeps[MakeCanonical(it_mod)].emplace(it_softdep); } // Get soft post dependencies for (const auto& [it_mod, it_softdep] : module_post_softdep_) { mod_with_softdeps[MakeCanonical(it_mod)].emplace(it_softdep); } while (!mod_with_deps.empty()) { std::vector<std::thread> threads; std::vector<std::string> mods_path_to_load; std::vector<std::string> mods_with_softdep_to_load; std::mutex vector_lock; // Find independent modules and modules only having soft dependencies for (const auto& [it_mod, it_dep] : mod_with_deps) { if (it_dep.size() == 1 && mod_with_softdeps[it_mod].empty()) { mods_path_to_load.emplace_back(*(it_dep.begin())); } else if (it_dep.size() == 1) { mods_with_softdep_to_load.emplace_back(it_mod); } } // Load independent modules in parallel auto thread_function = [&] { std::unique_lock lk(vector_lock); while (!mods_path_to_load.empty()) { auto mod_path_to_load = std::move(mods_path_to_load.back()); mods_path_to_load.pop_back(); lk.unlock(); ret &= Insmod(mod_path_to_load, ""); lk.lock(); } }; std::generate_n(std::back_inserter(threads), num_threads, [&] { return std::thread(thread_function); }); // Wait for the threads. for (auto& thread : threads) { thread.join(); } // Since we cannot assure if these soft dependencies tree are overlap, // we loaded these modules one by one. for (auto dep = mods_with_softdep_to_load.rbegin(); dep != mods_with_softdep_to_load.rend(); dep++) { ret &= LoadWithAliases(*dep, true); } std::lock_guard guard(module_loaded_lock_); // Remove loaded module form mod_with_deps and soft dependencies of other modules for (const auto& module_loaded : module_loaded_) { mod_with_deps.erase(module_loaded); for (auto& [mod, softdeps] : mod_with_softdeps) { softdeps.erase(module_loaded); } } // Remove loaded module form dependencies of other modules which are not loaded yet for (const auto& module_loaded_path : module_loaded_paths_) { for (auto& [mod, deps] : mod_with_deps) { deps.erase(module_loaded_path); } } } return ret; } bool Modprobe::LoadListedModules(bool strict) { auto ret = true; for (const auto& module : module_load_) { Loading
libmodprobe/libmodprobe_ext.cpp +5 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,8 @@ bool Modprobe::Insmod(const std::string& path_name, const std::string& parameter if (ret != 0) { if (errno == EEXIST) { // Module already loaded std::lock_guard guard(module_loaded_lock_); module_loaded_paths_.emplace(path_name); module_loaded_.emplace(canonical_name); return true; } Loading @@ -62,6 +64,8 @@ bool Modprobe::Insmod(const std::string& path_name, const std::string& parameter } LOG(INFO) << "Loaded kernel module " << path_name; std::lock_guard guard(module_loaded_lock_); module_loaded_paths_.emplace(path_name); module_loaded_.emplace(canonical_name); module_count_++; return true; Loading @@ -74,6 +78,7 @@ bool Modprobe::Rmmod(const std::string& module_name) { PLOG(ERROR) << "Failed to remove module '" << module_name << "'"; return false; } std::lock_guard guard(module_loaded_lock_); module_loaded_.erase(canonical_name); return true; } Loading
libmodprobe/libmodprobe_test.cpp +5 −4 Original line number Diff line number Diff line Loading @@ -188,10 +188,11 @@ TEST(libmodprobe, Test) { EXPECT_TRUE(modules_loaded == expected_after_remove); m = Modprobe({dir.path}); EXPECT_FALSE(m.LoadWithAliases("test4", true)); while (modules_loaded.size() > 0) EXPECT_TRUE(m.Remove(modules_loaded.front())); EXPECT_TRUE(m.LoadListedModules()); Modprobe m2({dir.path}); EXPECT_FALSE(m2.LoadWithAliases("test4", true)); while (modules_loaded.size() > 0) EXPECT_TRUE(m2.Remove(modules_loaded.front())); EXPECT_TRUE(m2.LoadListedModules()); GTEST_LOG_(INFO) << "Expected modules loaded after enabling blocklist (in order):"; for (auto i = expected_modules_blocklist_enabled.begin(); Loading