linker: Purge block allocator memory when possible

If all allocated memory from a block allocator is freed, it is a good
opportunity to purge all the pages allocated to reduce lingering dirty
pages.

Memory saving varies with the platform and what processes are running.
Measuring right after boot, this saves ~1.8MB on cuttelfish and ~1.3MB
on a 32-bit ARM device.

Bug: 112073665
Test: Boot and check memory usage with 'showmap'.
Change-Id: I53769e0ec9699f0b3645cdf281a2c0bbffb98676
diff --git a/linker/linker_block_allocator.cpp b/linker/linker_block_allocator.cpp
index dca944e..34df9a5 100644
--- a/linker/linker_block_allocator.cpp
+++ b/linker/linker_block_allocator.cpp
@@ -52,7 +52,8 @@
   : block_size_(
       round_up(block_size < sizeof(FreeBlockInfo) ? sizeof(FreeBlockInfo) : block_size, 16)),
     page_list_(nullptr),
-    free_block_list_(nullptr)
+    free_block_list_(nullptr),
+    allocated_(0)
 {}
 
 void* LinkerBlockAllocator::alloc() {
@@ -73,6 +74,8 @@
 
   memset(block_info, 0, block_size_);
 
+  ++allocated_;
+
   return block_info;
 }
 
@@ -101,6 +104,11 @@
   block_info->num_free_blocks = 1;
 
   free_block_list_ = block_info;
+
+  --allocated_;
+  if (allocated_ == 0) {
+    free_all_pages();
+  }
 }
 
 void LinkerBlockAllocator::protect_all(int prot) {
@@ -151,3 +159,18 @@
 
   abort();
 }
+
+void LinkerBlockAllocator::free_all_pages() {
+  if (allocated_) {
+    abort();
+  }
+
+  LinkerBlockAllocatorPage* page = page_list_;
+  while (page) {
+    LinkerBlockAllocatorPage* next = page->next;
+    munmap(page, PAGE_SIZE);
+    page = next;
+  }
+  page_list_ = nullptr;
+  free_block_list_ = nullptr;
+}
diff --git a/linker/linker_block_allocator.h b/linker/linker_block_allocator.h
index bd44fc8..85e6bd9 100644
--- a/linker/linker_block_allocator.h
+++ b/linker/linker_block_allocator.h
@@ -53,10 +53,12 @@
  private:
   void create_new_page();
   LinkerBlockAllocatorPage* find_page(void* block);
+  void free_all_pages();
 
   size_t block_size_;
   LinkerBlockAllocatorPage* page_list_;
   void* free_block_list_;
+  size_t allocated_;
 
   DISALLOW_COPY_AND_ASSIGN(LinkerBlockAllocator);
 };
@@ -73,7 +75,8 @@
  *    with size 513 this allocator will use 516 (520 for lp64) bytes of data
  *    where generalized implementation is going to use 1024 sized blocks.
  *
- * 2. This allocator does not munmap allocated memory, where LinkerMemoryAllocator does.
+ * 2. Unless all allocated memory is freed, this allocator does not munmap
+ *    allocated memory, where LinkerMemoryAllocator does.
  *
  * 3. This allocator provides mprotect services to the user, where LinkerMemoryAllocator
  *    always treats it's memory as READ|WRITE.