Branch data Line data Source code
# 1 : : // Copyright (c) 2016-2020 The Bitcoin Core developers
# 2 : : // Distributed under the MIT software license, see the accompanying
# 3 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
# 4 : :
# 5 : : #ifndef BITCOIN_SUPPORT_LOCKEDPOOL_H
# 6 : : #define BITCOIN_SUPPORT_LOCKEDPOOL_H
# 7 : :
# 8 : : #include <stdint.h>
# 9 : : #include <list>
# 10 : : #include <map>
# 11 : : #include <mutex>
# 12 : : #include <memory>
# 13 : : #include <unordered_map>
# 14 : :
# 15 : : /**
# 16 : : * OS-dependent allocation and deallocation of locked/pinned memory pages.
# 17 : : * Abstract base class.
# 18 : : */
# 19 : : class LockedPageAllocator
# 20 : : {
# 21 : : public:
# 22 : 67 : virtual ~LockedPageAllocator() {}
# 23 : : /** Allocate and lock memory pages.
# 24 : : * If len is not a multiple of the system page size, it is rounded up.
# 25 : : * Returns nullptr in case of allocation failure.
# 26 : : *
# 27 : : * If locking the memory pages could not be accomplished it will still
# 28 : : * return the memory, however the lockingSuccess flag will be false.
# 29 : : * lockingSuccess is undefined if the allocation fails.
# 30 : : */
# 31 : : virtual void* AllocateLocked(size_t len, bool *lockingSuccess) = 0;
# 32 : :
# 33 : : /** Unlock and free memory pages.
# 34 : : * Clear the memory before unlocking.
# 35 : : */
# 36 : : virtual void FreeLocked(void* addr, size_t len) = 0;
# 37 : :
# 38 : : /** Get the total limit on the amount of memory that may be locked by this
# 39 : : * process, in bytes. Return size_t max if there is no limit or the limit
# 40 : : * is unknown. Return 0 if no memory can be locked at all.
# 41 : : */
# 42 : : virtual size_t GetLimit() = 0;
# 43 : : };
# 44 : :
# 45 : : /* An arena manages a contiguous region of memory by dividing it into
# 46 : : * chunks.
# 47 : : */
# 48 : : class Arena
# 49 : : {
# 50 : : public:
# 51 : : Arena(void *base, size_t size, size_t alignment);
# 52 : : virtual ~Arena();
# 53 : :
# 54 : : Arena(const Arena& other) = delete; // non construction-copyable
# 55 : : Arena& operator=(const Arena&) = delete; // non copyable
# 56 : :
# 57 : : /** Memory statistics. */
# 58 : : struct Stats
# 59 : : {
# 60 : : size_t used;
# 61 : : size_t free;
# 62 : : size_t total;
# 63 : : size_t chunks_used;
# 64 : : size_t chunks_free;
# 65 : : };
# 66 : :
# 67 : : /** Allocate size bytes from this arena.
# 68 : : * Returns pointer on success, or 0 if memory is full or
# 69 : : * the application tried to allocate 0 bytes.
# 70 : : */
# 71 : : void* alloc(size_t size);
# 72 : :
# 73 : : /** Free a previously allocated chunk of memory.
# 74 : : * Freeing the zero pointer has no effect.
# 75 : : * Raises std::runtime_error in case of error.
# 76 : : */
# 77 : : void free(void *ptr);
# 78 : :
# 79 : : /** Get arena usage statistics */
# 80 : : Stats stats() const;
# 81 : :
# 82 : : #ifdef ARENA_DEBUG
# 83 : : void walk() const;
# 84 : : #endif
# 85 : :
# 86 : : /** Return whether a pointer points inside this arena.
# 87 : : * This returns base <= ptr < (base+size) so only use it for (inclusive)
# 88 : : * chunk starting addresses.
# 89 : : */
# 90 [ + + ][ + + ]: 1752686 : bool addressInArena(void *ptr) const { return ptr >= base && ptr < end; }
# 91 : : private:
# 92 : : typedef std::multimap<size_t, char*> SizeToChunkSortedMap;
# 93 : : /** Map to enable O(log(n)) best-fit allocation, as it's sorted by size */
# 94 : : SizeToChunkSortedMap size_to_free_chunk;
# 95 : :
# 96 : : typedef std::unordered_map<char*, SizeToChunkSortedMap::const_iterator> ChunkToSizeMap;
# 97 : : /** Map from begin of free chunk to its node in size_to_free_chunk */
# 98 : : ChunkToSizeMap chunks_free;
# 99 : : /** Map from end of free chunk to its node in size_to_free_chunk */
# 100 : : ChunkToSizeMap chunks_free_end;
# 101 : :
# 102 : : /** Map from begin of used chunk to its size */
# 103 : : std::unordered_map<char*, size_t> chunks_used;
# 104 : :
# 105 : : /** Base address of arena */
# 106 : : char* base;
# 107 : : /** End address of arena */
# 108 : : char* end;
# 109 : : /** Minimum chunk alignment */
# 110 : : size_t alignment;
# 111 : : };
# 112 : :
# 113 : : /** Pool for locked memory chunks.
# 114 : : *
# 115 : : * To avoid sensitive key data from being swapped to disk, the memory in this pool
# 116 : : * is locked/pinned.
# 117 : : *
# 118 : : * An arena manages a contiguous region of memory. The pool starts out with one arena
# 119 : : * but can grow to multiple arenas if the need arises.
# 120 : : *
# 121 : : * Unlike a normal C heap, the administrative structures are separate from the managed
# 122 : : * memory. This has been done as the sizes and bases of objects are not in themselves sensitive
# 123 : : * information, as to conserve precious locked memory. In some operating systems
# 124 : : * the amount of memory that can be locked is small.
# 125 : : */
# 126 : : class LockedPool
# 127 : : {
# 128 : : public:
# 129 : : /** Size of one arena of locked memory. This is a compromise.
# 130 : : * Do not set this too low, as managing many arenas will increase
# 131 : : * allocation and deallocation overhead. Setting it too high allocates
# 132 : : * more locked memory from the OS than strictly necessary.
# 133 : : */
# 134 : : static const size_t ARENA_SIZE = 256*1024;
# 135 : : /** Chunk alignment. Another compromise. Setting this too high will waste
# 136 : : * memory, setting it too low will facilitate fragmentation.
# 137 : : */
# 138 : : static const size_t ARENA_ALIGN = 16;
# 139 : :
# 140 : : /** Callback when allocation succeeds but locking fails.
# 141 : : */
# 142 : : typedef bool (*LockingFailed_Callback)();
# 143 : :
# 144 : : /** Memory statistics. */
# 145 : : struct Stats
# 146 : : {
# 147 : : size_t used;
# 148 : : size_t free;
# 149 : : size_t total;
# 150 : : size_t locked;
# 151 : : size_t chunks_used;
# 152 : : size_t chunks_free;
# 153 : : };
# 154 : :
# 155 : : /** Create a new LockedPool. This takes ownership of the MemoryPageLocker,
# 156 : : * you can only instantiate this with LockedPool(std::move(...)).
# 157 : : *
# 158 : : * The second argument is an optional callback when locking a newly allocated arena failed.
# 159 : : * If this callback is provided and returns false, the allocation fails (hard fail), if
# 160 : : * it returns true the allocation proceeds, but it could warn.
# 161 : : */
# 162 : : explicit LockedPool(std::unique_ptr<LockedPageAllocator> allocator, LockingFailed_Callback lf_cb_in = nullptr);
# 163 : : ~LockedPool();
# 164 : :
# 165 : : LockedPool(const LockedPool& other) = delete; // non construction-copyable
# 166 : : LockedPool& operator=(const LockedPool&) = delete; // non copyable
# 167 : :
# 168 : : /** Allocate size bytes from this arena.
# 169 : : * Returns pointer on success, or 0 if memory is full or
# 170 : : * the application tried to allocate 0 bytes.
# 171 : : */
# 172 : : void* alloc(size_t size);
# 173 : :
# 174 : : /** Free a previously allocated chunk of memory.
# 175 : : * Freeing the zero pointer has no effect.
# 176 : : * Raises std::runtime_error in case of error.
# 177 : : */
# 178 : : void free(void *ptr);
# 179 : :
# 180 : : /** Get pool usage statistics */
# 181 : : Stats stats() const;
# 182 : : private:
# 183 : : std::unique_ptr<LockedPageAllocator> allocator;
# 184 : :
# 185 : : /** Create an arena from locked pages */
# 186 : : class LockedPageArena: public Arena
# 187 : : {
# 188 : : public:
# 189 : : LockedPageArena(LockedPageAllocator *alloc_in, void *base_in, size_t size, size_t align);
# 190 : : ~LockedPageArena();
# 191 : : private:
# 192 : : void *base;
# 193 : : size_t size;
# 194 : : LockedPageAllocator *allocator;
# 195 : : };
# 196 : :
# 197 : : bool new_arena(size_t size, size_t align);
# 198 : :
# 199 : : std::list<LockedPageArena> arenas;
# 200 : : LockingFailed_Callback lf_cb;
# 201 : : size_t cumulative_bytes_locked;
# 202 : : /** Mutex protects access to this pool's data structures, including arenas.
# 203 : : */
# 204 : : mutable std::mutex mutex;
# 205 : : };
# 206 : :
# 207 : : /**
# 208 : : * Singleton class to keep track of locked (ie, non-swappable) memory, for use in
# 209 : : * std::allocator templates.
# 210 : : *
# 211 : : * Some implementations of the STL allocate memory in some constructors (i.e., see
# 212 : : * MSVC's vector<T> implementation where it allocates 1 byte of memory in the allocator.)
# 213 : : * Due to the unpredictable order of static initializers, we have to make sure the
# 214 : : * LockedPoolManager instance exists before any other STL-based objects that use
# 215 : : * secure_allocator are created. So instead of having LockedPoolManager also be
# 216 : : * static-initialized, it is created on demand.
# 217 : : */
# 218 : : class LockedPoolManager : public LockedPool
# 219 : : {
# 220 : : public:
# 221 : : /** Return the current instance, or create it once */
# 222 : : static LockedPoolManager& Instance()
# 223 : 3436121 : {
# 224 : 3436121 : static std::once_flag init_flag;
# 225 : 3436121 : std::call_once(init_flag, LockedPoolManager::CreateInstance);
# 226 : 3436121 : return *LockedPoolManager::_instance;
# 227 : 3436121 : }
# 228 : :
# 229 : : private:
# 230 : : explicit LockedPoolManager(std::unique_ptr<LockedPageAllocator> allocator);
# 231 : :
# 232 : : /** Create a new LockedPoolManager specialized to the OS */
# 233 : : static void CreateInstance();
# 234 : : /** Called when locking fails, warn the user here */
# 235 : : static bool LockingFailed();
# 236 : :
# 237 : : static LockedPoolManager* _instance;
# 238 : : };
# 239 : :
# 240 : : #endif // BITCOIN_SUPPORT_LOCKEDPOOL_H
|