Loading init/first_stage_init.cpp +10 −8 Original line number Diff line number Diff line Loading @@ -211,8 +211,8 @@ std::string GetModuleLoadList(BootMode boot_mode, const std::string& dir_path) { } #define MODULE_BASE_DIR "/lib/modules" bool LoadKernelModules(BootMode boot_mode, bool want_console, bool want_parallel, int& modules_loaded) { bool LoadKernelModules(BootMode boot_mode, bool want_console, Modprobe::LoadParallelMode want_parallel_mode, int& modules_loaded) { struct utsname uts {}; if (uname(&uts)) { LOG(FATAL) << "Failed to get kernel version."; Loading Loading @@ -279,8 +279,9 @@ bool LoadKernelModules(BootMode boot_mode, bool want_console, bool want_parallel } Modprobe m({MODULE_BASE_DIR}, GetModuleLoadList(boot_mode, MODULE_BASE_DIR)); bool retval = (want_parallel) ? m.LoadModulesParallel(std::thread::hardware_concurrency()) : m.LoadListedModules(!want_console); bool retval = (want_parallel_mode != Modprobe::LoadParallelMode::NONE) ? m.LoadModulesParallel(std::thread::hardware_concurrency(), want_parallel_mode) : m.LoadListedModules(!want_console); modules_loaded = m.GetModuleCount(); if (modules_loaded > 0) { LOG(INFO) << "Loaded " << modules_loaded << " modules from " << MODULE_BASE_DIR; Loading Loading @@ -437,14 +438,15 @@ 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; auto want_parallel_mode = Modprobe::LoadParallelMode::NONE; if (bootconfig.find("androidboot.load_modules_parallel = \"true\"") != std::string::npos) want_parallel_mode = Modprobe::LoadParallelMode::NORMAL; boot_clock::time_point module_start_time = boot_clock::now(); int module_count = 0; BootMode boot_mode = GetBootMode(cmdline, bootconfig); if (!LoadKernelModules(boot_mode, want_console, want_parallel, module_count)) { if (!LoadKernelModules(boot_mode, want_console, want_parallel_mode, module_count)) { if (want_console != FirstStageConsoleParam::DISABLED) { LOG(ERROR) << "Failed to load kernel modules, starting console"; } else { Loading libmodprobe/include/modprobe/modprobe.h +7 −1 Original line number Diff line number Diff line Loading @@ -28,10 +28,15 @@ class Modprobe { public: enum LoadParallelMode { NONE = 0, NORMAL, }; Modprobe(const std::vector<std::string>&, const std::string load_file = "modules.load", bool use_blocklist = true); bool LoadModulesParallel(int num_threads); bool LoadModulesParallel(int num_threads, int mode); bool LoadListedModules(bool strict = true); bool LoadWithAliases(const std::string& module_name, bool strict, const std::string& parameters = ""); Loading @@ -45,6 +50,7 @@ class Modprobe { bool IsBlocklisted(const std::string& module_name); private: bool IsLoadSequential(const std::string& module); std::string MakeCanonical(const std::string& module_path); bool InsmodWithDeps(const std::string& module_name, const std::string& parameters); bool Insmod(const std::string& path_name, const std::string& parameters); Loading libmodprobe/libmodprobe.cpp +97 −55 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ #include <sys/wait.h> #include <algorithm> #include <condition_variable> #include <map> #include <set> #include <string> Loading Loading @@ -506,9 +507,18 @@ bool Modprobe::IsBlocklisted(const std::string& module_name) { // repeat these steps until all modules are loaded. // Discard all blocklist. // Softdeps are taken care in InsmodWithDeps(). bool Modprobe::LoadModulesParallel(int num_threads) { bool ret = true; std::map<std::string, std::vector<std::string>> mod_with_deps; bool Modprobe::LoadModulesParallel(int num_threads, int mode) { std::map<std::string, std::vector<std::string>> mods_with_deps; std::unordered_set<std::string> mods_loading; std::vector<std::string> parallel_modules, sequential_modules; std::vector<std::thread> threads; std::atomic<bool> ret(true); std::atomic<bool> finish(false); std::mutex mods_to_load_lock; std::condition_variable cv_update_module, cv_load_module; int sleeping_threads = 0; LOG(INFO) << "LoadParallelMode:" << mode; // Get dependencies for (const auto& module : module_load_) { Loading @@ -517,63 +527,48 @@ bool Modprobe::LoadModulesParallel(int num_threads) { LOG(VERBOSE) << "LMP: Blocklist: Module " << module << " skipping..."; continue; } auto dependencies = GetDependencies(MakeCanonical(module)); if (dependencies.empty()) { LOG(ERROR) << "LMP: Hard-dep: Module " << module << " not in .dep file"; return false; } mod_with_deps[MakeCanonical(module)] = dependencies; mods_with_deps[MakeCanonical(module)] = dependencies; } while (!mod_with_deps.empty()) { std::vector<std::thread> threads; std::vector<std::string> mods_path_to_load; std::mutex vector_lock; // Consumers load modules in parallel or sequentially auto thread_function = [&] { while (!mods_with_deps.empty() && ret.load()) { std::unique_lock<std::mutex> lock(mods_to_load_lock); // Find independent modules for (const auto& [it_mod, it_dep] : mod_with_deps) { auto itd_last = it_dep.rbegin(); if (itd_last == it_dep.rend()) continue; if (sequential_modules.empty() && parallel_modules.empty()) { sleeping_threads++; auto cnd_last = MakeCanonical(*itd_last); // Hard-dependencies cannot be blocklisted if (IsBlocklisted(cnd_last)) { LOG(ERROR) << "LMP: Blocklist: Module-dep " << cnd_last << " : failed to load module " << it_mod; return false; } if (mode == LoadParallelMode::NORMAL && sleeping_threads == num_threads) cv_update_module.notify_one(); std::string str = "load_sequential=1"; auto it = module_options_[cnd_last].find(str); if (it != std::string::npos) { module_options_[cnd_last].erase(it, it + str.size()); cv_load_module.wait(lock, [&](){ return !parallel_modules.empty() || !sequential_modules.empty() || finish.load(); }); if (!LoadWithAliases(cnd_last, true)) { return false; } } else { if (std::find(mods_path_to_load.begin(), mods_path_to_load.end(), cnd_last) == mods_path_to_load.end()) { mods_path_to_load.emplace_back(cnd_last); } sleeping_threads--; } while (!sequential_modules.empty()) { auto mod_to_load = std::move(sequential_modules.back()); sequential_modules.pop_back(); ret.store(ret.load() && LoadWithAliases(mod_to_load, true)); } // Load independent modules in parallel auto thread_function = [&] { std::unique_lock lk(vector_lock); while (!mods_path_to_load.empty()) { auto ret_load = true; auto mod_to_load = std::move(mods_path_to_load.back()); mods_path_to_load.pop_back(); if (!parallel_modules.empty()) { auto mod_to_load = std::move(parallel_modules.back()); parallel_modules.pop_back(); lk.unlock(); ret_load &= LoadWithAliases(mod_to_load, true); lk.lock(); if (!ret_load) { ret &= ret_load; lock.unlock(); ret.store(ret.load() && LoadWithAliases(mod_to_load, true)); } } }; Loading @@ -581,30 +576,64 @@ bool Modprobe::LoadModulesParallel(int num_threads) { std::generate_n(std::back_inserter(threads), num_threads, [&] { return std::thread(thread_function); }); // Wait for the threads. for (auto& thread : threads) { thread.join(); // Producer check there's any independent module while (!mods_with_deps.empty()) { std::unique_lock<std::mutex> lock(mods_to_load_lock); for (const auto& [it_mod, it_dep] : mods_with_deps) { auto itd_last = it_dep.rbegin(); if (itd_last == it_dep.rend()) continue; auto cnd_last = MakeCanonical(*itd_last); // Hard-dependencies cannot be blocklisted if (IsBlocklisted(cnd_last)) { LOG(ERROR) << "LMP: Blocklist: Module-dep " << cnd_last << " : failed to load module " << it_mod; ret.store(0); break; } if (!ret) return ret; if (mods_loading.find(cnd_last) == mods_loading.end()) { mods_loading.insert(cnd_last); if (IsLoadSequential(cnd_last)) sequential_modules.emplace_back(cnd_last); else parallel_modules.emplace_back(cnd_last); } } cv_load_module.notify_all(); cv_update_module.wait(lock, [&](){ return parallel_modules.empty() && sequential_modules.empty(); }); if (!ret.load()) break; std::lock_guard guard(module_loaded_lock_); // Remove loaded module form mod_with_deps and soft dependencies of other modules // Remove loaded module from mods_with_deps for (const auto& module_loaded : module_loaded_) mod_with_deps.erase(module_loaded); mods_with_deps.erase(module_loaded); // Remove loaded module form dependencies of other modules which are not loaded yet // Remove loaded module from dependency list for (const auto& module_loaded_path : module_loaded_paths_) { for (auto& [mod, deps] : mod_with_deps) { for (auto& [mod, deps] : mods_with_deps) { auto it = std::find(deps.begin(), deps.end(), module_loaded_path); if (it != deps.end()) { if (it != deps.end()) deps.erase(it); } } } finish.store(true); cv_load_module.notify_all(); for (auto& thread : threads) { thread.join(); } return ret; return ret.load(); } bool Modprobe::LoadListedModules(bool strict) { Loading Loading @@ -641,6 +670,19 @@ std::vector<std::string> Modprobe::ListModules(const std::string& pattern) { return rv; } bool Modprobe::IsLoadSequential(const std::string& module) { std::string str = "load_sequential=1"; auto it = module_options_[module].find(str); if (it != std::string::npos) { module_options_[module].erase(it, it + str.size()); return true; } return false; } bool Modprobe::GetAllDependencies(const std::string& module, std::vector<std::string>* pre_dependencies, std::vector<std::string>* dependencies, Loading Loading
init/first_stage_init.cpp +10 −8 Original line number Diff line number Diff line Loading @@ -211,8 +211,8 @@ std::string GetModuleLoadList(BootMode boot_mode, const std::string& dir_path) { } #define MODULE_BASE_DIR "/lib/modules" bool LoadKernelModules(BootMode boot_mode, bool want_console, bool want_parallel, int& modules_loaded) { bool LoadKernelModules(BootMode boot_mode, bool want_console, Modprobe::LoadParallelMode want_parallel_mode, int& modules_loaded) { struct utsname uts {}; if (uname(&uts)) { LOG(FATAL) << "Failed to get kernel version."; Loading Loading @@ -279,8 +279,9 @@ bool LoadKernelModules(BootMode boot_mode, bool want_console, bool want_parallel } Modprobe m({MODULE_BASE_DIR}, GetModuleLoadList(boot_mode, MODULE_BASE_DIR)); bool retval = (want_parallel) ? m.LoadModulesParallel(std::thread::hardware_concurrency()) : m.LoadListedModules(!want_console); bool retval = (want_parallel_mode != Modprobe::LoadParallelMode::NONE) ? m.LoadModulesParallel(std::thread::hardware_concurrency(), want_parallel_mode) : m.LoadListedModules(!want_console); modules_loaded = m.GetModuleCount(); if (modules_loaded > 0) { LOG(INFO) << "Loaded " << modules_loaded << " modules from " << MODULE_BASE_DIR; Loading Loading @@ -437,14 +438,15 @@ 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; auto want_parallel_mode = Modprobe::LoadParallelMode::NONE; if (bootconfig.find("androidboot.load_modules_parallel = \"true\"") != std::string::npos) want_parallel_mode = Modprobe::LoadParallelMode::NORMAL; boot_clock::time_point module_start_time = boot_clock::now(); int module_count = 0; BootMode boot_mode = GetBootMode(cmdline, bootconfig); if (!LoadKernelModules(boot_mode, want_console, want_parallel, module_count)) { if (!LoadKernelModules(boot_mode, want_console, want_parallel_mode, module_count)) { if (want_console != FirstStageConsoleParam::DISABLED) { LOG(ERROR) << "Failed to load kernel modules, starting console"; } else { Loading
libmodprobe/include/modprobe/modprobe.h +7 −1 Original line number Diff line number Diff line Loading @@ -28,10 +28,15 @@ class Modprobe { public: enum LoadParallelMode { NONE = 0, NORMAL, }; Modprobe(const std::vector<std::string>&, const std::string load_file = "modules.load", bool use_blocklist = true); bool LoadModulesParallel(int num_threads); bool LoadModulesParallel(int num_threads, int mode); bool LoadListedModules(bool strict = true); bool LoadWithAliases(const std::string& module_name, bool strict, const std::string& parameters = ""); Loading @@ -45,6 +50,7 @@ class Modprobe { bool IsBlocklisted(const std::string& module_name); private: bool IsLoadSequential(const std::string& module); std::string MakeCanonical(const std::string& module_path); bool InsmodWithDeps(const std::string& module_name, const std::string& parameters); bool Insmod(const std::string& path_name, const std::string& parameters); Loading
libmodprobe/libmodprobe.cpp +97 −55 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ #include <sys/wait.h> #include <algorithm> #include <condition_variable> #include <map> #include <set> #include <string> Loading Loading @@ -506,9 +507,18 @@ bool Modprobe::IsBlocklisted(const std::string& module_name) { // repeat these steps until all modules are loaded. // Discard all blocklist. // Softdeps are taken care in InsmodWithDeps(). bool Modprobe::LoadModulesParallel(int num_threads) { bool ret = true; std::map<std::string, std::vector<std::string>> mod_with_deps; bool Modprobe::LoadModulesParallel(int num_threads, int mode) { std::map<std::string, std::vector<std::string>> mods_with_deps; std::unordered_set<std::string> mods_loading; std::vector<std::string> parallel_modules, sequential_modules; std::vector<std::thread> threads; std::atomic<bool> ret(true); std::atomic<bool> finish(false); std::mutex mods_to_load_lock; std::condition_variable cv_update_module, cv_load_module; int sleeping_threads = 0; LOG(INFO) << "LoadParallelMode:" << mode; // Get dependencies for (const auto& module : module_load_) { Loading @@ -517,63 +527,48 @@ bool Modprobe::LoadModulesParallel(int num_threads) { LOG(VERBOSE) << "LMP: Blocklist: Module " << module << " skipping..."; continue; } auto dependencies = GetDependencies(MakeCanonical(module)); if (dependencies.empty()) { LOG(ERROR) << "LMP: Hard-dep: Module " << module << " not in .dep file"; return false; } mod_with_deps[MakeCanonical(module)] = dependencies; mods_with_deps[MakeCanonical(module)] = dependencies; } while (!mod_with_deps.empty()) { std::vector<std::thread> threads; std::vector<std::string> mods_path_to_load; std::mutex vector_lock; // Consumers load modules in parallel or sequentially auto thread_function = [&] { while (!mods_with_deps.empty() && ret.load()) { std::unique_lock<std::mutex> lock(mods_to_load_lock); // Find independent modules for (const auto& [it_mod, it_dep] : mod_with_deps) { auto itd_last = it_dep.rbegin(); if (itd_last == it_dep.rend()) continue; if (sequential_modules.empty() && parallel_modules.empty()) { sleeping_threads++; auto cnd_last = MakeCanonical(*itd_last); // Hard-dependencies cannot be blocklisted if (IsBlocklisted(cnd_last)) { LOG(ERROR) << "LMP: Blocklist: Module-dep " << cnd_last << " : failed to load module " << it_mod; return false; } if (mode == LoadParallelMode::NORMAL && sleeping_threads == num_threads) cv_update_module.notify_one(); std::string str = "load_sequential=1"; auto it = module_options_[cnd_last].find(str); if (it != std::string::npos) { module_options_[cnd_last].erase(it, it + str.size()); cv_load_module.wait(lock, [&](){ return !parallel_modules.empty() || !sequential_modules.empty() || finish.load(); }); if (!LoadWithAliases(cnd_last, true)) { return false; } } else { if (std::find(mods_path_to_load.begin(), mods_path_to_load.end(), cnd_last) == mods_path_to_load.end()) { mods_path_to_load.emplace_back(cnd_last); } sleeping_threads--; } while (!sequential_modules.empty()) { auto mod_to_load = std::move(sequential_modules.back()); sequential_modules.pop_back(); ret.store(ret.load() && LoadWithAliases(mod_to_load, true)); } // Load independent modules in parallel auto thread_function = [&] { std::unique_lock lk(vector_lock); while (!mods_path_to_load.empty()) { auto ret_load = true; auto mod_to_load = std::move(mods_path_to_load.back()); mods_path_to_load.pop_back(); if (!parallel_modules.empty()) { auto mod_to_load = std::move(parallel_modules.back()); parallel_modules.pop_back(); lk.unlock(); ret_load &= LoadWithAliases(mod_to_load, true); lk.lock(); if (!ret_load) { ret &= ret_load; lock.unlock(); ret.store(ret.load() && LoadWithAliases(mod_to_load, true)); } } }; Loading @@ -581,30 +576,64 @@ bool Modprobe::LoadModulesParallel(int num_threads) { std::generate_n(std::back_inserter(threads), num_threads, [&] { return std::thread(thread_function); }); // Wait for the threads. for (auto& thread : threads) { thread.join(); // Producer check there's any independent module while (!mods_with_deps.empty()) { std::unique_lock<std::mutex> lock(mods_to_load_lock); for (const auto& [it_mod, it_dep] : mods_with_deps) { auto itd_last = it_dep.rbegin(); if (itd_last == it_dep.rend()) continue; auto cnd_last = MakeCanonical(*itd_last); // Hard-dependencies cannot be blocklisted if (IsBlocklisted(cnd_last)) { LOG(ERROR) << "LMP: Blocklist: Module-dep " << cnd_last << " : failed to load module " << it_mod; ret.store(0); break; } if (!ret) return ret; if (mods_loading.find(cnd_last) == mods_loading.end()) { mods_loading.insert(cnd_last); if (IsLoadSequential(cnd_last)) sequential_modules.emplace_back(cnd_last); else parallel_modules.emplace_back(cnd_last); } } cv_load_module.notify_all(); cv_update_module.wait(lock, [&](){ return parallel_modules.empty() && sequential_modules.empty(); }); if (!ret.load()) break; std::lock_guard guard(module_loaded_lock_); // Remove loaded module form mod_with_deps and soft dependencies of other modules // Remove loaded module from mods_with_deps for (const auto& module_loaded : module_loaded_) mod_with_deps.erase(module_loaded); mods_with_deps.erase(module_loaded); // Remove loaded module form dependencies of other modules which are not loaded yet // Remove loaded module from dependency list for (const auto& module_loaded_path : module_loaded_paths_) { for (auto& [mod, deps] : mod_with_deps) { for (auto& [mod, deps] : mods_with_deps) { auto it = std::find(deps.begin(), deps.end(), module_loaded_path); if (it != deps.end()) { if (it != deps.end()) deps.erase(it); } } } finish.store(true); cv_load_module.notify_all(); for (auto& thread : threads) { thread.join(); } return ret; return ret.load(); } bool Modprobe::LoadListedModules(bool strict) { Loading Loading @@ -641,6 +670,19 @@ std::vector<std::string> Modprobe::ListModules(const std::string& pattern) { return rv; } bool Modprobe::IsLoadSequential(const std::string& module) { std::string str = "load_sequential=1"; auto it = module_options_[module].find(str); if (it != std::string::npos) { module_options_[module].erase(it, it + str.size()); return true; } return false; } bool Modprobe::GetAllDependencies(const std::string& module, std::vector<std::string>* pre_dependencies, std::vector<std::string>* dependencies, Loading