• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef BASE_MEMORY_MADV_FREE_DISCARDABLE_MEMORY_POSIX_H_
6 #define BASE_MEMORY_MADV_FREE_DISCARDABLE_MEMORY_POSIX_H_
7 
8 #include <stddef.h>
9 #include <stdint.h>
10 
11 #include <atomic>
12 #include <vector>
13 
14 #include "base/base_export.h"
15 #include "base/functional/callback.h"
16 #include "base/memory/discardable_memory.h"
17 #include "base/memory/raw_ptr.h"
18 #include "base/sequence_checker.h"
19 #include "base/threading/thread_collision_warner.h"
20 #include "build/build_config.h"
21 
22 namespace base {
23 // Discardable memory backed by the MADV_FREE advice value, available since
24 // Linux 4.5.
25 //
26 // When unlocked, this implementation of discardable memory will
27 // apply the MADV_FREE advice value to all pages within the allocated range,
28 // causing pages to be discarded instead of swapped upon memory pressure.
29 // When pages are discarded, they become zero-fill-on-demand pages.
30 // Attempting to unlock an already-unlocked instance is undefined behaviour.
31 //
32 // When locked, all pages will be checked for eviction. If any page has
33 // been discarded, the entire allocated range is unmapped and the lock fails.
34 // After a failed lock, the instance remains unlocked but any further attempts
35 // to lock will fail. Additionally, the discardable memory instance is
36 // invalidated and access to memory obtained via data() is undefined behaviour.
37 // Attempting to lock an already-locked instance is undefined behaviour. If no
38 // page in the allocated range has been discarded, then lock succeeds and the
39 // allocated range of memory is available for use without any page fault,
40 // additional allocations, or memory zeroing.
41 //
42 // If DCHECK_IS_ON(), additional checks are added to ensure that the discardable
43 // memory instance is being used correctly. These checks are not present by
44 // default, as some incur a significant performance penalty or do not warrant
45 // crashing the process. These checks are:
46 // -    Do not allow lock while already locked or unlock while already unlocked
47 // -    Do not allow memory access via data() if instance is deallocated after
48 //      Lock() (although invalid memory can still be accessed through existing
49 //      pointers)
50 // -    After Unlock(), disallow read or write of memory pointed to by data()
51 //      with PROT_NONE until next Lock()
52 //
53 // Caveats:
54 // [1]: The smallest allocation unit is the size of a page, so it is
55 //      unsuitable for small allocations.
56 //
57 // [2]: The size of a discardable memory instance must be greater than 0 bytes.
58 //
59 class BASE_EXPORT MadvFreeDiscardableMemoryPosix : public DiscardableMemory {
60  public:
61   MadvFreeDiscardableMemoryPosix(size_t size_in_pages,
62                                  std::atomic<size_t>* allocator_byte_count);
63 
64   MadvFreeDiscardableMemoryPosix(const MadvFreeDiscardableMemoryPosix&) =
65       delete;
66   MadvFreeDiscardableMemoryPosix& operator=(
67       const MadvFreeDiscardableMemoryPosix&) = delete;
68 
69   ~MadvFreeDiscardableMemoryPosix() override;
70 
71   bool Lock() override;
72   void Unlock() override;
73   void* data() const override;
74 
75   bool IsLockedForTesting() const;
76   void DiscardForTesting() override;
77 
78   trace_event::MemoryAllocatorDump* CreateMemoryAllocatorDump(
79       const char* name,
80       trace_event::ProcessMemoryDump* pmd) const override;
81 
82  protected:
GetPageCount()83   size_t GetPageCount() const { return allocated_pages_; }
84 
85   bool IsValid() const;
86 
87   void SetKeepMemoryForTesting(bool keep_memory);
88 
89   // Force page discard by applying MADV_DONTNEED hint on a page.
90   // Has the same effect as if the page was naturally discarded during
91   // memory pressure due to MADV_FREE (i.e. zero-fill-on-demand pages for
92   // anonymous private mappings).
93   // Note that MADV_DONTNEED takes effect immediately for non-shared mappings.
94   void DiscardPage(size_t page_index);
95 
96  private:
97   bool LockPage(size_t page_index);
98   void UnlockPage(size_t page_index);
99 
100   bool Deallocate();
101 
102   // Gets whether this instance has been discarded (but not yet unmapped).
103   bool IsDiscarded() const;
104 
105   // Get whether all pages in this discardable memory instance are resident.
106   bool IsResident() const;
107 
108   const size_t size_in_bytes_;
109   const size_t allocated_pages_;
110 
111   // Pointer to allocator memory usage metric for updating upon allocation and
112   // destruction.
113   raw_ptr<std::atomic<size_t>> allocator_byte_count_;
114 
115   // Data comes from mmap() and we manage its poisioning.
116   RAW_PTR_EXCLUSION void* data_;
117   bool is_locked_ = true;
118 
119   // If true, MADV_FREE will not be set on Unlock().
120   bool keep_memory_for_testing_ = false;
121 
122   // Stores the first word of a page for use during locking.
123   std::vector<std::atomic<intptr_t>> page_first_word_;
124 
125   DFAKE_MUTEX(thread_collision_warner_);
126 };
127 
128 enum class MadvFreeSupport { kUnsupported, kSupported };
129 BASE_EXPORT MadvFreeSupport GetMadvFreeSupport();
130 
131 }  // namespace base
132 
133 #endif  // BASE_MEMORY_MADV_FREE_DISCARDABLE_MEMORY_POSIX_H_
134