Merge "fs_mgr_fstab: Refactor & cleanup DSU mounting logic"
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 2c878f0..af0c89e 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -463,6 +463,11 @@
     {"watchdog,gsa,hard", 215},
     {"watchdog,gsa,soft", 216},
     {"watchdog,pmucal", 217},
+    {"reboot,early,bl", 218},
+    {"watchdog,apc,gsa,crashed", 219},
+    {"watchdog,apc,bl31,crashed", 220},
+    {"watchdog,apc,pbl,crashed", 221},
+    {"reboot,memory_protect,hyp", 222},
 };
 
 // Converts a string value representing the reason the system booted to an
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 5c7d847..ad0231d 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -293,13 +293,6 @@
         "libdebuggerd/test/utility_test.cpp",
     ],
 
-    product_variables: {
-        malloc_not_svelte: {
-            srcs: ["libdebuggerd/test/scudo_test.cpp"],
-            header_libs: ["scudo_headers"],
-        },
-    },
-
     target: {
         android: {
             srcs: [
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
index 68bfd5b..a506859 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
@@ -34,10 +34,9 @@
 
 class ScudoCrashData {
  public:
-  ScudoCrashData() = default;
+  ScudoCrashData() = delete;
   ~ScudoCrashData() = default;
-
-  bool SetErrorInfo(unwindstack::Memory* process_memory, const ProcessInfo& process_info);
+  ScudoCrashData(unwindstack::Memory* process_memory, const ProcessInfo& process_info);
 
   bool CrashIsMine() const;
 
diff --git a/debuggerd/libdebuggerd/scudo.cpp b/debuggerd/libdebuggerd/scudo.cpp
index 9483e59..5d861f8 100644
--- a/debuggerd/libdebuggerd/scudo.cpp
+++ b/debuggerd/libdebuggerd/scudo.cpp
@@ -14,11 +14,6 @@
  * limitations under the License.
  */
 
-#include <stdint.h>
-#include <unistd.h>
-
-#include <vector>
-
 #include "libdebuggerd/scudo.h"
 #include "libdebuggerd/tombstone.h"
 
@@ -30,92 +25,57 @@
 
 #include "tombstone.pb.h"
 
-bool ScudoCrashData::SetErrorInfo(unwindstack::Memory* process_memory,
-                                  const ProcessInfo& process_info) {
+std::unique_ptr<char[]> AllocAndReadFully(unwindstack::Memory* process_memory, uint64_t addr,
+                                          size_t size) {
+  auto buf = std::make_unique<char[]>(size);
+  if (!process_memory->ReadFully(addr, buf.get(), size)) {
+    return std::unique_ptr<char[]>();
+  }
+  return buf;
+}
+
+ScudoCrashData::ScudoCrashData(unwindstack::Memory* process_memory,
+                               const ProcessInfo& process_info) {
   if (!process_info.has_fault_address) {
-    return false;
+    return;
   }
 
-  std::vector<char> stack_depot(__scudo_get_stack_depot_size());
-  if (!process_memory->ReadFully(process_info.scudo_stack_depot, stack_depot.data(),
-                                 stack_depot.size())) {
-    return false;
+  auto stack_depot = AllocAndReadFully(process_memory, process_info.scudo_stack_depot,
+                                       __scudo_get_stack_depot_size());
+  auto region_info = AllocAndReadFully(process_memory, process_info.scudo_region_info,
+                                       __scudo_get_region_info_size());
+  auto ring_buffer = AllocAndReadFully(process_memory, process_info.scudo_ring_buffer,
+                                       __scudo_get_ring_buffer_size());
+  if (!stack_depot || !region_info || !ring_buffer) {
+    return;
   }
-  std::vector<char> region_info(__scudo_get_region_info_size());
-  if (!process_memory->ReadFully(process_info.scudo_region_info, region_info.data(),
-                                 region_info.size())) {
-    return false;
-  }
-  std::vector<char> ring_buffer(__scudo_get_ring_buffer_size());
-  if (!process_memory->ReadFully(process_info.scudo_ring_buffer, ring_buffer.data(),
-                                 ring_buffer.size())) {
-    return false;
-  }
-
-  uintptr_t page_size = getpagesize();
 
   untagged_fault_addr_ = process_info.untagged_fault_address;
-  uintptr_t fault_page = untagged_fault_addr_ & ~(page_size - 1);
+  uintptr_t fault_page = untagged_fault_addr_ & ~(PAGE_SIZE - 1);
 
-  // Attempt to get 16 pages before the fault page and 16 pages after.
-  constexpr size_t kExtraPages = 16;
-  std::vector<char> memory(page_size * (kExtraPages * 2 + 1));
-
-  // Read faulting page first.
-  size_t memory_index = kExtraPages;
-  if (!process_memory->ReadFully(fault_page, &memory[memory_index * page_size], page_size)) {
-    return false;
+  uintptr_t memory_begin = fault_page - PAGE_SIZE * 16;
+  if (memory_begin > fault_page) {
+    return;
   }
 
-  // Attempt to read the pages after the fault page, stop as soon as we
-  // fail to read.
-  uintptr_t read_addr = fault_page;
-  if (!__builtin_add_overflow(fault_page, page_size, &read_addr)) {
-    memory_index++;
-    for (size_t i = 0; i < kExtraPages; i++, memory_index++) {
-      if (!process_memory->ReadFully(read_addr, &memory[memory_index * page_size], page_size)) {
-        break;
-      }
-      if (__builtin_add_overflow(read_addr, page_size, &read_addr)) {
-        break;
-      }
-    }
-  }
-  uintptr_t memory_end = read_addr;
-
-  // Attempt to read the pages before the fault page, stop as soon as we
-  // fail to read.
-  memory_index = kExtraPages;
-  if (fault_page > 0) {
-    read_addr = fault_page - page_size;
-    for (size_t i = 0; i < kExtraPages; i++, memory_index--) {
-      if (!process_memory->ReadFully(read_addr, &memory[(memory_index - 1) * page_size],
-                                     page_size)) {
-        break;
-      }
-      if (read_addr == 0) {
-        memory_index--;
-        break;
-      }
-      read_addr -= page_size;
-    }
-  }
-  size_t start_memory_index = memory_index;
-  uintptr_t memory_begin = fault_page - (kExtraPages - memory_index) * page_size;
-
-  std::vector<long> memory_tags((memory_end - memory_begin) / kTagGranuleSize);
-  read_addr = memory_begin;
-  for (size_t i = 0; i < memory_tags.size(); i++) {
-    memory_tags[i] = process_memory->ReadTag(read_addr);
-    read_addr += kTagGranuleSize;
+  uintptr_t memory_end = fault_page + PAGE_SIZE * 16;
+  if (memory_end < fault_page) {
+    return;
   }
 
-  __scudo_get_error_info(
-      &error_info_, process_info.maybe_tagged_fault_address, stack_depot.data(), region_info.data(),
-      ring_buffer.data(), &memory[start_memory_index * page_size],
-      reinterpret_cast<const char*>(memory_tags.data()), memory_begin, memory_end - memory_begin);
+  auto memory = std::make_unique<char[]>(memory_end - memory_begin);
+  for (auto i = memory_begin; i != memory_end; i += PAGE_SIZE) {
+    process_memory->ReadFully(i, memory.get() + i - memory_begin, PAGE_SIZE);
+  }
 
-  return true;
+  auto memory_tags = std::make_unique<char[]>((memory_end - memory_begin) / kTagGranuleSize);
+  for (auto i = memory_begin; i != memory_end; i += kTagGranuleSize) {
+    memory_tags[(i - memory_begin) / kTagGranuleSize] = process_memory->ReadTag(i);
+  }
+
+  __scudo_get_error_info(&error_info_, process_info.maybe_tagged_fault_address, stack_depot.get(),
+                         region_info.get(), ring_buffer.get(), memory.get(), memory_tags.get(),
+                         memory_begin, memory_end - memory_begin);
 }
 
 bool ScudoCrashData::CrashIsMine() const {
diff --git a/debuggerd/libdebuggerd/test/scudo_test.cpp b/debuggerd/libdebuggerd/test/scudo_test.cpp
deleted file mode 100644
index d8fc6a7..0000000
--- a/debuggerd/libdebuggerd/test/scudo_test.cpp
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (C) 2022 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 <stdlib.h>
-#include <unistd.h>
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include "libdebuggerd/scudo.h"
-#include "libdebuggerd/types.h"
-#include "unwindstack/Memory.h"
-
-#include "log_fake.h"
-
-#include <inttypes.h>
-
-// This needs to match the kExtraPages from ScudoCrashData::SetErrorInfo.
-constexpr uint64_t kMaxPages = 16;
-
-class MemoryAlwaysZero : public unwindstack::Memory {
- public:
-  MemoryAlwaysZero() = default;
-  virtual ~MemoryAlwaysZero() = default;
-
-  size_t Read(uint64_t addr, void* buffer, size_t size) override {
-    if (test_unreadable_addrs_.count(addr) != 0) {
-      return 0;
-    }
-    test_read_addrs_.insert(addr);
-    memset(buffer, 0, size);
-    return size;
-  }
-
-  void TestAddUnreadableAddress(uint64_t addr) { test_unreadable_addrs_.insert(addr); }
-
-  void TestClearAddresses() {
-    test_read_addrs_.clear();
-    test_unreadable_addrs_.clear();
-  }
-
-  std::set<uint64_t>& test_read_addrs() { return test_read_addrs_; }
-
- private:
-  std::set<uint64_t> test_unreadable_addrs_;
-
-  std::set<uint64_t> test_read_addrs_;
-};
-
-TEST(ScudoTest, no_fault_address) {
-  MemoryAlwaysZero process_memory;
-  ProcessInfo info;
-  info.has_fault_address = false;
-  info.untagged_fault_address = 0x5000;
-  info.scudo_stack_depot = 0x1000;
-  info.scudo_region_info = 0x2000;
-  info.scudo_ring_buffer = 0x3000;
-
-  ScudoCrashData crash;
-  ASSERT_FALSE(crash.SetErrorInfo(&process_memory, info));
-
-  info.has_fault_address = true;
-  ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
-}
-
-TEST(ScudoTest, scudo_data_read_check) {
-  MemoryAlwaysZero process_memory;
-  ProcessInfo info;
-  info.has_fault_address = true;
-  info.untagged_fault_address = 0x5000;
-  info.scudo_stack_depot = 0x1000;
-  info.scudo_region_info = 0x2000;
-  info.scudo_ring_buffer = 0x3000;
-
-  ScudoCrashData crash;
-  ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
-
-  // Stack Depot unreadable
-  process_memory.TestClearAddresses();
-  process_memory.TestAddUnreadableAddress(0x1000);
-  ASSERT_FALSE(crash.SetErrorInfo(&process_memory, info));
-
-  // The Region Info doesn't exist for 32 bit.
-#if defined(__LP64__)
-  // Region Info unreadable
-  process_memory.TestClearAddresses();
-  process_memory.TestAddUnreadableAddress(0x2000);
-  ASSERT_FALSE(crash.SetErrorInfo(&process_memory, info));
-#endif
-
-  // Ring Buffer unreadable
-  process_memory.TestClearAddresses();
-  process_memory.TestAddUnreadableAddress(0x3000);
-  ASSERT_FALSE(crash.SetErrorInfo(&process_memory, info));
-
-  // Verify that with all scudo data readable, the error info works.
-  process_memory.TestClearAddresses();
-  ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
-}
-
-TEST(ScudoTest, fault_page_unreadable) {
-  MemoryAlwaysZero process_memory;
-  ProcessInfo info;
-  info.has_fault_address = true;
-  info.untagged_fault_address = 0x5124;
-  info.scudo_stack_depot = 0x1000;
-  info.scudo_region_info = 0x2000;
-  info.scudo_ring_buffer = 0x3000;
-
-  ScudoCrashData crash;
-  ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
-
-  uint64_t fault_page = info.untagged_fault_address & ~(getpagesize() - 1);
-  process_memory.TestAddUnreadableAddress(fault_page);
-  ASSERT_FALSE(crash.SetErrorInfo(&process_memory, info));
-}
-
-TEST(ScudoTest, pages_before_fault_unreadable) {
-  MemoryAlwaysZero process_memory;
-  ProcessInfo info;
-  info.has_fault_address = true;
-  info.untagged_fault_address = 0x15124;
-  info.scudo_stack_depot = 0x1000;
-  info.scudo_region_info = 0x2000;
-  info.scudo_ring_buffer = 0x3000;
-
-  ScudoCrashData crash;
-  ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
-
-  uint64_t page_size = getpagesize();
-  uint64_t fault_page = info.untagged_fault_address & ~(page_size - 1);
-
-  std::vector<uint64_t> expected_reads = {0x1000, 0x2000, 0x3000};
-  for (size_t i = 0; i <= kMaxPages; i++) {
-    expected_reads.emplace_back(fault_page + i * page_size);
-  }
-
-  // Loop through and make pages before the fault page unreadable.
-  for (size_t i = 1; i <= kMaxPages + 1; i++) {
-    process_memory.TestClearAddresses();
-    uint64_t unreadable_addr = fault_page - i * page_size;
-    SCOPED_TRACE(testing::Message()
-                 << "Failed at unreadable address 0x" << std::hex << unreadable_addr);
-    process_memory.TestAddUnreadableAddress(unreadable_addr);
-    ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
-    ASSERT_THAT(process_memory.test_read_addrs(),
-                testing::UnorderedElementsAreArray(expected_reads));
-    // Need to add the previous unreadable_addr to the list of expected addresses.
-    expected_reads.emplace_back(unreadable_addr);
-  }
-}
-
-TEST(ScudoTest, pages_after_fault_unreadable) {
-  MemoryAlwaysZero process_memory;
-  ProcessInfo info;
-  info.has_fault_address = true;
-  info.untagged_fault_address = 0x15124;
-  info.scudo_stack_depot = 0x1000;
-  info.scudo_region_info = 0x2000;
-  info.scudo_ring_buffer = 0x3000;
-
-  ScudoCrashData crash;
-  ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
-
-  uint64_t page_size = getpagesize();
-  uint64_t fault_page = info.untagged_fault_address & ~(page_size - 1);
-
-  std::vector<uint64_t> expected_reads = {0x1000, 0x2000, 0x3000};
-  for (size_t i = 0; i <= kMaxPages; i++) {
-    expected_reads.emplace_back(fault_page - i * page_size);
-  }
-
-  // Loop through and make pages after the fault page unreadable.
-  for (size_t i = 1; i <= kMaxPages + 1; i++) {
-    process_memory.TestClearAddresses();
-    uint64_t unreadable_addr = fault_page + i * page_size;
-    SCOPED_TRACE(testing::Message()
-                 << "Failed at unreadable address 0x" << std::hex << unreadable_addr);
-    process_memory.TestAddUnreadableAddress(unreadable_addr);
-    ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
-    ASSERT_THAT(process_memory.test_read_addrs(),
-                testing::UnorderedElementsAreArray(expected_reads));
-    // Need to add the previous unreadable_addr to the list of expected addresses.
-    expected_reads.emplace_back(unreadable_addr);
-  }
-}
-
-// Make sure that if the fault address is low, you won't underflow.
-TEST(ScudoTest, fault_address_low) {
-  MemoryAlwaysZero process_memory;
-  ProcessInfo info;
-  info.has_fault_address = true;
-  info.scudo_stack_depot = 0x21000;
-  info.scudo_region_info = 0x22000;
-  info.scudo_ring_buffer = 0x23000;
-
-  ScudoCrashData crash;
-  ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
-
-  uint64_t page_size = getpagesize();
-  for (size_t i = 0; i < kMaxPages + 1; i++) {
-    process_memory.TestClearAddresses();
-    info.untagged_fault_address = 0x124 + i * getpagesize();
-    SCOPED_TRACE(testing::Message()
-                 << "Failed with fault address 0x" << std::hex << info.untagged_fault_address);
-    ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
-    std::vector<uint64_t> expected_reads = {0x21000, 0x22000, 0x23000};
-    uint64_t fault_page = info.untagged_fault_address & ~(page_size - 1);
-    expected_reads.emplace_back(fault_page);
-    for (size_t j = 1; j <= kMaxPages; j++) {
-      expected_reads.emplace_back(fault_page + j * page_size);
-    }
-    while (fault_page != 0) {
-      fault_page -= page_size;
-      expected_reads.emplace_back(fault_page);
-    }
-    ASSERT_THAT(process_memory.test_read_addrs(),
-                testing::UnorderedElementsAreArray(expected_reads));
-  }
-}
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index 6e1ce8f..159ebc8 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -193,9 +193,8 @@
 static void dump_probable_cause(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
                                 const ProcessInfo& process_info, const ThreadInfo& main_thread) {
 #if defined(USE_SCUDO)
-  ScudoCrashData scudo_crash_data;
-  if (scudo_crash_data.SetErrorInfo(unwinder->GetProcessMemory().get(), process_info) &&
-      scudo_crash_data.CrashIsMine()) {
+  ScudoCrashData scudo_crash_data(unwinder->GetProcessMemory().get(), process_info);
+  if (scudo_crash_data.CrashIsMine()) {
     scudo_crash_data.AddCauseProtos(tombstone, unwinder);
     return;
   }
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index c8684a2..9670706 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -2151,8 +2151,17 @@
         if (!suffix.empty() && !android::base::EndsWith(name, suffix)) {
             continue;
         }
-        snapshots->emplace_back(std::move(name));
+
+        // Insert system and product partition at the beginning so that
+        // during snapshot-merge, these partitions are merged first.
+        if (name == "system_a" || name == "system_b" || name == "product_a" ||
+            name == "product_b") {
+            snapshots->insert(snapshots->begin(), std::move(name));
+        } else {
+            snapshots->emplace_back(std::move(name));
+        }
     }
+
     return true;
 }
 
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
index afc653f..8939b78 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -147,17 +147,18 @@
     NotifyRAForMergeReady();
 }
 
-void SnapshotHandler::CheckMergeCompletionStatus() {
+bool SnapshotHandler::CheckMergeCompletionStatus() {
     if (!merge_initiated_) {
         SNAP_LOG(INFO) << "Merge was not initiated. Total-data-ops: "
                        << reader_->get_num_total_data_ops();
-        return;
+        return false;
     }
 
     struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
 
     SNAP_LOG(INFO) << "Merge-status: Total-Merged-ops: " << ch->num_merge_ops
                    << " Total-data-ops: " << reader_->get_num_total_data_ops();
+    return true;
 }
 
 bool SnapshotHandler::ReadMetadata() {
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
index 90fba75..42237ef 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
@@ -18,6 +18,9 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <unistd.h>
 
 #include <condition_variable>
 #include <cstring>
@@ -56,6 +59,8 @@
 
 static constexpr int kNumWorkerThreads = 4;
 
+static constexpr int kNiceValueForMergeThreads = -5;
+
 #define SNAP_LOG(level) LOG(level) << misc_name_ << ": "
 #define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": "
 
@@ -306,7 +311,7 @@
     const bool& IsAttached() const { return attached_; }
     void AttachControlDevice() { attached_ = true; }
 
-    void CheckMergeCompletionStatus();
+    bool CheckMergeCompletionStatus();
     bool CommitMerge(int num_merge_ops);
 
     void CloseFds() { cow_fd_ = {}; }
@@ -337,6 +342,8 @@
 
     // State transitions for merge
     void InitiateMerge();
+    void MonitorMerge();
+    void WakeupMonitorMergeThread();
     void WaitForMergeComplete();
     bool WaitForMergeBegin();
     void NotifyRAForMergeReady();
@@ -365,6 +372,7 @@
     void SetSocketPresent(bool socket) { is_socket_present_ = socket; }
     void SetIouringEnabled(bool io_uring_enabled) { is_io_uring_enabled_ = io_uring_enabled; }
     bool MergeInitiated() { return merge_initiated_; }
+    bool MergeMonitored() { return merge_monitored_; }
     double GetMergePercentage() { return merge_completion_percentage_; }
 
     // Merge Block State Transitions
@@ -431,6 +439,7 @@
     double merge_completion_percentage_;
 
     bool merge_initiated_ = false;
+    bool merge_monitored_ = false;
     bool attached_ = false;
     bool is_socket_present_;
     bool is_io_uring_enabled_ = false;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
index c26a2cd..17f3c4f 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
@@ -543,6 +543,10 @@
         return true;
     }
 
+    if (setpriority(PRIO_PROCESS, gettid(), kNiceValueForMergeThreads)) {
+        SNAP_PLOG(ERROR) << "Failed to set priority for TID: " << gettid();
+    }
+
     SNAP_LOG(INFO) << "Merge starting..";
 
     if (!Init()) {
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
index fa2866f..b9e4255 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
@@ -727,6 +727,10 @@
 
     InitializeIouring();
 
+    if (setpriority(PRIO_PROCESS, gettid(), kNiceValueForMergeThreads)) {
+        SNAP_PLOG(ERROR) << "Failed to set priority for TID: " << gettid();
+    }
+
     while (!RAIterDone()) {
         if (!ReadAheadIOStart()) {
             break;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
index b7f7f54..1bf33c8 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
@@ -60,6 +60,14 @@
     return DaemonOps::INVALID;
 }
 
+UserSnapshotServer::UserSnapshotServer() {
+    monitor_merge_event_fd_.reset(eventfd(0, EFD_CLOEXEC));
+    if (monitor_merge_event_fd_ == -1) {
+        PLOG(FATAL) << "monitor_merge_event_fd_: failed to create eventfd";
+    }
+    terminating_ = false;
+}
+
 UserSnapshotServer::~UserSnapshotServer() {
     // Close any client sockets that were added via AcceptClient().
     for (size_t i = 1; i < watched_fds_.size(); i++) {
@@ -250,7 +258,7 @@
                     return Sendmsg(fd, "fail");
                 }
 
-                if (!StartMerge(*iter)) {
+                if (!StartMerge(&lock, *iter)) {
                     return Sendmsg(fd, "fail");
                 }
 
@@ -307,7 +315,7 @@
     }
 
     handler->snapuserd()->CloseFds();
-    handler->snapuserd()->CheckMergeCompletionStatus();
+    bool merge_completed = handler->snapuserd()->CheckMergeCompletionStatus();
     handler->snapuserd()->UnmapBufferRegion();
 
     auto misc_name = handler->misc_name();
@@ -315,7 +323,11 @@
 
     {
         std::lock_guard<std::mutex> lock(lock_);
-        num_partitions_merge_complete_ += 1;
+        if (merge_completed) {
+            num_partitions_merge_complete_ += 1;
+            active_merge_threads_ -= 1;
+            WakeupMonitorMergeThread();
+        }
         handler->SetThreadTerminated();
         auto iter = FindHandler(&lock, handler->misc_name());
         if (iter == dm_users_.end()) {
@@ -427,6 +439,9 @@
 
         if (th.joinable()) th.join();
     }
+
+    stop_monitor_merge_thread_ = true;
+    WakeupMonitorMergeThread();
 }
 
 void UserSnapshotServer::AddWatchedFd(android::base::borrowed_fd fd, int events) {
@@ -511,13 +526,24 @@
     return true;
 }
 
-bool UserSnapshotServer::StartMerge(const std::shared_ptr<UserSnapshotDmUserHandler>& handler) {
+bool UserSnapshotServer::StartMerge(std::lock_guard<std::mutex>* proof_of_lock,
+                                    const std::shared_ptr<UserSnapshotDmUserHandler>& handler) {
+    CHECK(proof_of_lock);
+
     if (!handler->snapuserd()->IsAttached()) {
         LOG(ERROR) << "Handler not attached to dm-user - Merge thread cannot be started";
         return false;
     }
 
-    handler->snapuserd()->InitiateMerge();
+    handler->snapuserd()->MonitorMerge();
+
+    if (!is_merge_monitor_started_.has_value()) {
+        std::thread(&UserSnapshotServer::MonitorMerge, this).detach();
+        is_merge_monitor_started_ = true;
+    }
+
+    merge_handlers_.push(handler);
+    WakeupMonitorMergeThread();
     return true;
 }
 
@@ -599,6 +625,42 @@
     return true;
 }
 
+void UserSnapshotServer::WakeupMonitorMergeThread() {
+    uint64_t notify = 1;
+    ssize_t rc = TEMP_FAILURE_RETRY(write(monitor_merge_event_fd_.get(), &notify, sizeof(notify)));
+    if (rc < 0) {
+        PLOG(FATAL) << "failed to notify monitor merge thread";
+    }
+}
+
+void UserSnapshotServer::MonitorMerge() {
+    while (!stop_monitor_merge_thread_) {
+        uint64_t testVal;
+        ssize_t ret =
+                TEMP_FAILURE_RETRY(read(monitor_merge_event_fd_.get(), &testVal, sizeof(testVal)));
+        if (ret == -1) {
+            PLOG(FATAL) << "Failed to read from eventfd";
+        } else if (ret == 0) {
+            LOG(FATAL) << "Hit EOF on eventfd";
+        }
+
+        LOG(INFO) << "MonitorMerge: active-merge-threads: " << active_merge_threads_;
+        {
+            std::lock_guard<std::mutex> lock(lock_);
+            while (active_merge_threads_ < kMaxMergeThreads && merge_handlers_.size() > 0) {
+                auto handler = merge_handlers_.front();
+                merge_handlers_.pop();
+                LOG(INFO) << "Starting merge for partition: "
+                          << handler->snapuserd()->GetMiscName();
+                handler->snapuserd()->InitiateMerge();
+                active_merge_threads_ += 1;
+            }
+        }
+    }
+
+    LOG(INFO) << "Exiting MonitorMerge: size: " << merge_handlers_.size();
+}
+
 bool UserSnapshotServer::WaitForSocket() {
     auto scope_guard = android::base::make_scope_guard([this]() -> void { JoinAllThreads(); });
 
@@ -655,6 +717,7 @@
     if (!StartWithSocket(true)) {
         return false;
     }
+
     return Run();
 }
 
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
index 00734a9..c2af61f 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
@@ -15,6 +15,7 @@
 #pragma once
 
 #include <poll.h>
+#include <sys/eventfd.h>
 
 #include <cstdio>
 #include <cstring>
@@ -22,6 +23,8 @@
 #include <future>
 #include <iostream>
 #include <mutex>
+#include <optional>
+#include <queue>
 #include <sstream>
 #include <string>
 #include <thread>
@@ -34,6 +37,7 @@
 namespace snapshot {
 
 static constexpr uint32_t kMaxPacketSize = 512;
+static constexpr uint8_t kMaxMergeThreads = 2;
 
 enum class DaemonOps {
     INIT,
@@ -85,13 +89,19 @@
     std::vector<struct pollfd> watched_fds_;
     bool is_socket_present_ = false;
     int num_partitions_merge_complete_ = 0;
+    int active_merge_threads_ = 0;
+    bool stop_monitor_merge_thread_ = false;
     bool is_server_running_ = false;
     bool io_uring_enabled_ = false;
+    std::optional<bool> is_merge_monitor_started_;
+
+    android::base::unique_fd monitor_merge_event_fd_;
 
     std::mutex lock_;
 
     using HandlerList = std::vector<std::shared_ptr<UserSnapshotDmUserHandler>>;
     HandlerList dm_users_;
+    std::queue<std::shared_ptr<UserSnapshotDmUserHandler>> merge_handlers_;
 
     void AddWatchedFd(android::base::borrowed_fd fd, int events);
     void AcceptClient();
@@ -109,6 +119,8 @@
     bool IsTerminating() { return terminating_; }
 
     void RunThread(std::shared_ptr<UserSnapshotDmUserHandler> handler);
+    void MonitorMerge();
+
     void JoinAllThreads();
     bool StartWithSocket(bool start_listening);
 
@@ -122,7 +134,7 @@
     bool UpdateVerification(std::lock_guard<std::mutex>* proof_of_lock);
 
   public:
-    UserSnapshotServer() { terminating_ = false; }
+    UserSnapshotServer();
     ~UserSnapshotServer();
 
     bool Start(const std::string& socketname);
@@ -136,9 +148,11 @@
                                                           const std::string& backing_device,
                                                           const std::string& base_path_merge);
     bool StartHandler(const std::shared_ptr<UserSnapshotDmUserHandler>& handler);
-    bool StartMerge(const std::shared_ptr<UserSnapshotDmUserHandler>& handler);
+    bool StartMerge(std::lock_guard<std::mutex>* proof_of_lock,
+                    const std::shared_ptr<UserSnapshotDmUserHandler>& handler);
     std::string GetMergeStatus(const std::shared_ptr<UserSnapshotDmUserHandler>& handler);
 
+    void WakeupMonitorMergeThread();
     void SetTerminating() { terminating_ = true; }
     void ReceivedSocketSignal() { received_socket_signal_ = true; }
     void SetServerRunning() { is_server_running_ = true; }
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
index d4e1d7c..28c9f68 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
@@ -165,6 +165,13 @@
 using namespace android::dm;
 using android::base::unique_fd;
 
+void SnapshotHandler::MonitorMerge() {
+    {
+        std::lock_guard<std::mutex> lock(lock_);
+        merge_monitored_ = true;
+    }
+}
+
 // This is invoked once primarily by update-engine to initiate
 // the merge
 void SnapshotHandler::InitiateMerge() {
@@ -361,10 +368,16 @@
 
 std::string SnapshotHandler::GetMergeStatus() {
     bool merge_not_initiated = false;
+    bool merge_monitored = false;
     bool merge_failed = false;
 
     {
         std::lock_guard<std::mutex> lock(lock_);
+
+        if (MergeMonitored()) {
+            merge_monitored = true;
+        }
+
         if (!MergeInitiated()) {
             merge_not_initiated = true;
         }
@@ -387,6 +400,12 @@
             return "snapshot-merge-complete";
         }
 
+        // Merge monitor thread is tracking the merge but the merge thread
+        // is not started yet.
+        if (merge_monitored) {
+            return "snapshot-merge";
+        }
+
         // Return the state as "snapshot". If the device was rebooted during
         // merge, we will return the status as "snapshot". This is ok, as
         // libsnapshot will explicitly resume the merge. This is slightly