Merge "init: flush ext4 file system on shutdown" into main
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
index 127735d..a2a602a 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
@@ -468,7 +468,7 @@
         if (overwritten_blocks.count(block)) {
             overwrite = overwritten_blocks[block];
             LOG(ERROR) << "Invalid Sequence! Block needed for op:\n"
-                       << op << "\noverwritten by previously merged op:\n"
+                       << *op << "\noverwritten by previously merged op:\n"
                        << *overwrite;
         }
         if (misaligned && overwritten_blocks.count(block + 1)) {
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/create_cow.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/create_cow.cpp
index b15e6ab..eadfda8 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/create_cow.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/create_cow.cpp
@@ -376,29 +376,77 @@
     }
     return true;
 }
-
 bool CreateSnapshot::WriteOrderedSnapshots() {
-    std::unordered_map<uint64_t, uint64_t> overwritten_blocks;
-    std::vector<std::pair<uint64_t, uint64_t>> merge_sequence;
-    for (auto it = copy_blocks_.begin(); it != copy_blocks_.end(); it++) {
-        if (overwritten_blocks.count(it->second)) {
-            replace_blocks_.push_back(it->first);
-            continue;
+    // Sort copy_blocks_ by target block index so consecutive
+    // target blocks can be together
+    std::vector<std::pair<uint64_t, uint64_t>> sorted_copy_blocks_(copy_blocks_.begin(),
+                                                                   copy_blocks_.end());
+    std::sort(sorted_copy_blocks_.begin(), sorted_copy_blocks_.end());
+    std::unordered_map<uint64_t, std::vector<uint64_t>> dependency_graph;
+    std::unordered_map<uint64_t, int> in_degree;
+
+    // Initialize in-degree and build the dependency graph
+    for (const auto& [target, source] : sorted_copy_blocks_) {
+        in_degree[target] = 0;
+        if (copy_blocks_.count(source)) {
+            // this source block itself gets modified
+            dependency_graph[source].push_back(target);
+            in_degree[target]++;
         }
-        overwritten_blocks[it->first] = it->second;
-        merge_sequence.emplace_back(std::make_pair(it->first, it->second));
+    }
+
+    std::vector<uint64_t> ordered_copy_ops_;
+    std::deque<uint64_t> queue;
+
+    // Add nodes with in-degree 0 (no dependency) to the queue
+    for (const auto& [target, degree] : in_degree) {
+        if (degree == 0) {
+            queue.push_back(target);
+        }
+    }
+
+    while (!queue.empty()) {
+        uint64_t current_target = queue.front();
+        queue.pop_front();
+        ordered_copy_ops_.push_back(current_target);
+
+        if (dependency_graph.count(current_target)) {
+            for (uint64_t neighbor : dependency_graph[current_target]) {
+                in_degree[neighbor]--;
+                if (in_degree[neighbor] == 0) {
+                    queue.push_back(neighbor);
+                }
+            }
+        }
+    }
+
+    // Detect cycles and change those blocks to replace blocks
+    if (ordered_copy_ops_.size() != copy_blocks_.size()) {
+        LOG(INFO) << "Cycle detected in copy operations! Converting some to replace.";
+        std::unordered_set<uint64_t> safe_targets_(ordered_copy_ops_.begin(),
+                                                   ordered_copy_ops_.end());
+        for (const auto& [target, source] : copy_blocks_) {
+            if (safe_targets_.find(target) == safe_targets_.end()) {
+                replace_blocks_.push_back(target);
+                copy_blocks_.erase(target);
+            }
+        }
+    }
+
+    std::reverse(ordered_copy_ops_.begin(), ordered_copy_ops_.end());
+    // Add the copy blocks
+    copy_ops_ = 0;
+    for (uint64_t target : ordered_copy_ops_) {
+        LOG(DEBUG) << "copy target: " << target << " source: " << copy_blocks_[target];
+        if (!writer_->AddCopy(target, copy_blocks_[target], 1)) {
+            return false;
+        }
+        copy_ops_++;
     }
     // Sort the blocks so that if the blocks are contiguous, it would help
     // compress multiple blocks in one shot based on the compression factor.
     std::sort(replace_blocks_.begin(), replace_blocks_.end());
-
-    copy_ops_ = merge_sequence.size();
-    for (auto it = merge_sequence.begin(); it != merge_sequence.end(); it++) {
-        if (!writer_->AddCopy(it->first, it->second, 1)) {
-            return false;
-        }
-    }
-
+    LOG(DEBUG) << "Total copy ops: " << copy_ops_;
     return true;
 }
 
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index 32c8e37..454ff5e 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -649,6 +649,12 @@
             metadata_on_super = true;
         }
     }
+
+    if (!std::filesystem::exists(path) || std::filesystem::is_empty(path)) {
+        LOG(ERROR) << path << " doesn't exist";
+        return false;
+    }
+
     MapSnapshots cow(path, metadata_on_super);
     if (!cow.ApplyUpdate()) {
         return false;
@@ -921,6 +927,11 @@
     std::string path = std::string(argv[2]);
     std::vector<std::string> patchfiles;
 
+    if (!std::filesystem::exists(path) || std::filesystem::is_empty(path)) {
+        LOG(ERROR) << path << " doesn't exist";
+        return false;
+    }
+
     for (const auto& entry : std::filesystem::directory_iterator(path)) {
         if (android::base::EndsWith(entry.path().generic_string(), ".patch")) {
             patchfiles.push_back(android::base::Basename(entry.path().generic_string()));
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index cc5f658..6bb0ad7 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -211,8 +211,8 @@
 }
 
 #define MODULE_BASE_DIR "/lib/modules"
-bool LoadKernelModules(BootMode boot_mode, bool want_console,
-                       Modprobe::LoadParallelMode want_parallel_mode, int& modules_loaded) {
+bool LoadKernelModules(BootMode boot_mode, bool want_console, bool want_parallel,
+                       int& modules_loaded) {
     struct utsname uts {};
     if (uname(&uts)) {
         LOG(FATAL) << "Failed to get kernel version.";
@@ -279,9 +279,8 @@
     }
 
     Modprobe m({MODULE_BASE_DIR}, GetModuleLoadList(boot_mode, MODULE_BASE_DIR));
-    bool retval = (want_parallel_mode != Modprobe::LoadParallelMode::NONE) ?
-            m.LoadModulesParallel(std::thread::hardware_concurrency(), want_parallel_mode) :
-            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) {
         LOG(INFO) << "Loaded " << modules_loaded << " modules from " << MODULE_BASE_DIR;
@@ -438,21 +437,14 @@
     }
 
     auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline, bootconfig) : 0;
-    auto want_parallel_mode = Modprobe::LoadParallelMode::NONE;
-    if (bootconfig.find("androidboot.load_modules_parallel = \"true\"")
-        != std::string::npos)
-        want_parallel_mode = Modprobe::LoadParallelMode::NORMAL;
-    else if (bootconfig.find("androidboot.load_modules_parallel_mode = \"performance\"")
-        != std::string::npos)
-        want_parallel_mode = Modprobe::LoadParallelMode::PERFORMANCE;
-    else if (bootconfig.find("androidboot.load_modules_parallel = \"conservative\"")
-        != std::string::npos)
-        want_parallel_mode = Modprobe::LoadParallelMode::CONSERVATIVE;
+    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;
     BootMode boot_mode = GetBootMode(cmdline, bootconfig);
-    if (!LoadKernelModules(boot_mode, want_console, want_parallel_mode, module_count)) {
+    if (!LoadKernelModules(boot_mode, want_console,
+                           want_parallel, module_count)) {
         if (want_console != FirstStageConsoleParam::DISABLED) {
             LOG(ERROR) << "Failed to load kernel modules, starting console";
         } else {
diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
index 55931b7..d33e17d 100644
--- a/libmodprobe/include/modprobe/modprobe.h
+++ b/libmodprobe/include/modprobe/modprobe.h
@@ -28,17 +28,10 @@
 
 class Modprobe {
   public:
-    enum LoadParallelMode {
-      NONE = 0,
-      NORMAL,
-      PERFORMANCE,
-      CONSERVATIVE,
-    };
-
     Modprobe(const std::vector<std::string>&, const std::string load_file = "modules.load",
              bool use_blocklist = true);
 
-    bool LoadModulesParallel(int num_threads, int mode);
+    bool LoadModulesParallel(int num_threads);
     bool LoadListedModules(bool strict = true);
     bool LoadWithAliases(const std::string& module_name, bool strict,
                          const std::string& parameters = "");
@@ -52,7 +45,6 @@
     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);
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index c43ad96..bdd114c 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -24,7 +24,6 @@
 #include <sys/wait.h>
 
 #include <algorithm>
-#include <condition_variable>
 #include <map>
 #include <set>
 #include <string>
@@ -507,18 +506,9 @@
 // repeat these steps until all modules are loaded.
 // Discard all blocklist.
 // Softdeps are taken care in InsmodWithDeps().
-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;
+bool Modprobe::LoadModulesParallel(int num_threads) {
+    bool ret = true;
+    std::map<std::string, std::vector<std::string>> mod_with_deps;
 
     // Get dependencies
     for (const auto& module : module_load_) {
@@ -527,61 +517,22 @@
             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;
         }
-
-        mods_with_deps[MakeCanonical(module)] = dependencies;
+        mod_with_deps[MakeCanonical(module)] = dependencies;
     }
 
-    // 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);
+    while (!mod_with_deps.empty()) {
+        std::vector<std::thread> threads;
+        std::vector<std::string> mods_path_to_load;
+        std::mutex vector_lock;
 
-            if (sequential_modules.empty() && parallel_modules.empty()) {
-                sleeping_threads++;
-
-                if (mode == LoadParallelMode::PERFORMANCE)
-                    cv_update_module.notify_one();
-                else if (sleeping_threads == num_threads)
-                    cv_update_module.notify_one();
-
-                cv_load_module.wait(lock, [&](){
-                    return !parallel_modules.empty() ||
-                           !sequential_modules.empty() ||
-                           finish.load(); });
-
-                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));
-            }
-
-            if (!parallel_modules.empty()) {
-                auto mod_to_load = std::move(parallel_modules.back());
-                parallel_modules.pop_back();
-
-                lock.unlock();
-                ret.store(ret.load() && LoadWithAliases(mod_to_load, true));
-            }
-        }
-    };
-
-    std::generate_n(std::back_inserter(threads), num_threads,
-        [&] { return std::thread(thread_function); });
-
-    // 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) {
+        // 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;
@@ -591,55 +542,69 @@
             if (IsBlocklisted(cnd_last)) {
                 LOG(ERROR) << "LMP: Blocklist: Module-dep " << cnd_last
                            << " : failed to load module " << it_mod;
-                ret.store(0);
-                break;
+                return false;
             }
 
-            if (mods_loading.find(cnd_last) == mods_loading.end()) {
-                mods_loading.insert(cnd_last);
+            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());
 
-                if (IsLoadSequential(cnd_last))
-                    sequential_modules.emplace_back(cnd_last);
-                else
-                    parallel_modules.emplace_back(cnd_last);
+                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);
+                }
             }
-
-            if (mode == LoadParallelMode::CONSERVATIVE &&
-                parallel_modules.size() >= num_threads)
-                break;
         }
 
-        cv_load_module.notify_all();
-        cv_update_module.wait(lock, [&](){
-            return parallel_modules.empty() &&
-                   sequential_modules.empty(); });
+        // 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 (!ret.load())
-            break;
+                lk.unlock();
+                ret_load &= LoadWithAliases(mod_to_load, true);
+                lk.lock();
+                if (!ret_load) {
+                    ret &= ret_load;
+                }
+            }
+        };
+
+        std::generate_n(std::back_inserter(threads), num_threads,
+                        [&] { return std::thread(thread_function); });
+
+        // Wait for the threads.
+        for (auto& thread : threads) {
+            thread.join();
+        }
+
+        if (!ret) return ret;
 
         std::lock_guard guard(module_loaded_lock_);
-        // Remove loaded module from mods_with_deps
+        // Remove loaded module form mod_with_deps and soft dependencies of other modules
         for (const auto& module_loaded : module_loaded_)
-            mods_with_deps.erase(module_loaded);
+            mod_with_deps.erase(module_loaded);
 
-        // Remove loaded module from dependency list
+        // 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] : mods_with_deps) {
+            for (auto& [mod, deps] : mod_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.load();
+    return ret;
 }
 
 bool Modprobe::LoadListedModules(bool strict) {
@@ -676,19 +641,6 @@
     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,
diff --git a/trusty/test/driver/trusty_driver_test.py b/trusty/test/driver/trusty_driver_test.py
index bf5e1f0..d86b48b 100644
--- a/trusty/test/driver/trusty_driver_test.py
+++ b/trusty/test/driver/trusty_driver_test.py
@@ -77,8 +77,10 @@
         self.assertTrue(ver.startswith("Project:"))
 
     def testUntaintedLinux(self):
-        tainted = ReadFile("/proc/sys/kernel/tainted")
-        self.assertEqual(tainted, "0")
+        tainted = int(ReadFile("/proc/sys/kernel/tainted"))
+        # Filter out the out-of-tree and unsigned module bits
+        tainted &= ~0x3000
+        self.assertEqual(tainted, 0)
 
     # stdcall test with shared memory buffers.
     # Each test run takes up to 4 arguments: