Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a41f3812 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Load kernel modules in parallel"

parents 99c892e5 c60300a2
Loading
Loading
Loading
Loading
+6 −3
Original line number Diff line number Diff line
@@ -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.";
@@ -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;
@@ -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 {
+6 −0
Original line number Diff line number Diff line
@@ -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 = "");
@@ -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;
};
+93 −0
Original line number Diff line number Diff line
@@ -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>
@@ -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_) {
+5 −0
Original line number Diff line number Diff line
@@ -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;
        }
@@ -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;
@@ -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;
}
+5 −4
Original line number Diff line number Diff line
@@ -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();