Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'master' of scm.gforge.inria.fr:/gitroot/simgrid/simgrid
[simgrid.git] / src / mc / PageStore.hpp
1 /* Copyright (c) 2015. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 /* This program is free software; you can redistribute it and/or modify it
5  * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 #ifndef SIMGRID_MC_PAGESTORE_HPP
8 #define SIMGRID_MC_PAGESTORE_HPP
9
10 #include <cstdint>
11 #include <vector>
12
13 #include <unordered_map>
14 #include <unordered_set>
15
16 #include <xbt/base.h>
17
18 #include "src/mc/mc_mmu.h"
19 #include "src/mc/mc_forward.hpp"
20
21 namespace simgrid {
22 namespace mc {
23
24 /** @brief Storage for snapshot memory pages
25  *
26  * The first (lower) layer of the per-page snapshot mechanism is a page
27  * store: it's responsibility is to store immutable shareable
28  * reference-counted memory pages independently of the snapshoting
29  * logic. Snapshot management and representation, soft-dirty tracking is
30  * handled to an higher layer. READMORE
31  *
32  * Data structure:
33  *
34  *  * A pointer (`memory_`) to a (currently anonymous) `mmap()`ed memory
35  *    region holding the memory pages (the address of the first page).
36  *
37  *    We want to keep this memory region aligned on the memory pages (so
38  *    that we might be able to create non-linear memory mappings on those
39  *    pages in the future) and be able to expand it without coyping the
40  *    data (there will be a lot of pages here): we will be able to
41  *    efficiently expand the memory mapping using `mremap()`, moving it
42  *    to another virtual address if necessary.
43  *
44  *    Because we will move this memory mapping on the virtual address
45  *    space, only the index of the page will be stored in the snapshots
46  *    and the page will always be looked up by going through `memory`:
47  *
48  *         void* page = (char*) page_store->memory + page_index << pagebits;
49  *
50  *  * The number of pages mapped in virtual memory (`capacity_`). Once all
51  *    those pages are used, we need to expand the page store with
52  *    `mremap()`.
53  *
54  *  * A reference count for each memory page `page_counts_`. Each time a
55  *    snapshot references a page, the counter is incremented. If a
56  *    snapshot is freed, the reference count is decremented. When the
57  *    reference count, of a page reaches 0 it is added to a list of available
58  *    pages (`free_pages_`).
59  *
60  *  * A list of free pages `free_pages_` which can be reused. This avoids having
61  *    to scan the reference count list to find a free page.
62  *
63  *  * When we are expanding the memory map we do not want to add thousand of page
64  *    to the `free_pages_` list and remove them just afterwards. The `top_index_`
65  *    field is an index after which all pages are free and are not in the `free_pages_`
66  *    list.
67  *
68  *  * When we are adding a page, we need to check if a page with the same
69  *    content is already in the page store in order to reuse it. For this
70  *    reason, we maintain an index (`hash_index_`) mapping the hash of a
71  *    page to the list of page indices with this hash.
72  *    We use a fast (non cryptographic) hash so there may be conflicts:
73  *    we must be able to store multiple indices for the same hash.
74  *
75  */
76 class PageStore {
77 public: // Types
78   typedef std::uint64_t hash_type;
79 private: // Types
80   // We are using a cheap hash to index a page.
81   // We should expect collision and we need to associate multiple page indices
82   // to the same hash.
83   typedef std::unordered_set<std::size_t> page_set_type;
84   typedef std::unordered_map<hash_type, page_set_type> pages_map_type;
85
86 private: // Fields:
87   /** First page */
88   void* memory_;
89   /** Number of available pages in virtual memory */
90   std::size_t capacity_;
91   /** Top of the used pages (index of the next available page) */
92   std::size_t top_index_;
93   /** Page reference count */
94   std::vector<std::uint64_t> page_counts_;
95   /** Index of available pages before the top */
96   std::vector<std::size_t> free_pages_;
97   /** Index from page hash to page index */
98   pages_map_type hash_index_;
99
100 private: // Methods
101   void resize(std::size_t size);
102   std::size_t alloc_page();
103   void remove_page(std::size_t pageno);
104
105 public: // Constructors
106   PageStore(PageStore const&) = delete;
107   PageStore& operator=(PageStore const&) = delete;
108   explicit PageStore(std::size_t size);
109   ~PageStore();
110
111 public: // Methods
112
113   /** @brief Decrement the reference count for a given page
114    *
115    * Decrement the reference count of this page. Used when a snapshot is
116    * destroyed.
117    *
118    * If the reference count reaches zero, the page is recycled:
119    * it is added to the `free_pages_` list and removed from the `hash_index_`.
120    *
121    * */
122   void unref_page(std::size_t pageno);
123
124   /** @brief Increment the refcount for a given page
125    *
126    * This method used to increase a reference count of a page if we know
127    * that the content of a page is the same as a page already in the page
128    * store.
129    *
130    * This will be the case if a page if soft clean: we know that is has not
131    * changed since the previous cnapshot/restoration and we can avoid
132    * hashing the page, comparing byte-per-byte to candidates.
133    * */
134   void ref_page(size_t pageno);
135
136   /** @brief Store a page in the page store */
137   std::size_t store_page(void* page);
138
139   /** @brief Get a page from its page number
140    *
141    *  @param Number of the memory page in the store
142    *  @return Start of the page
143    */
144   const void* get_page(std::size_t pageno) const;
145
146 public: // Debug/test methods
147
148   /** @brief Get the number of references for a page */
149   std::size_t get_ref(std::size_t pageno);
150
151   /** @brief Get the number of used pages */
152   std::size_t size();
153
154   /** @brief Get the capacity of the page store
155    *
156    *  The capacity is expanded by a system call (mremap).
157    * */
158   std::size_t capacity();
159
160 };
161
162 inline __attribute__((always_inline))
163 void PageStore::unref_page(std::size_t pageno) {
164   if ((--this->page_counts_[pageno]) == 0)
165     this->remove_page(pageno);
166 }
167
168 inline __attribute__((always_inline))
169 void PageStore::ref_page(size_t pageno)
170 {
171   ++this->page_counts_[pageno];
172 }
173
174 inline __attribute__((always_inline))
175 const void* PageStore::get_page(std::size_t pageno) const
176 {
177   return mc_page_from_number(this->memory_, pageno);
178 }
179
180 inline __attribute__((always_inline))
181 std::size_t PageStore::get_ref(std::size_t pageno)
182 {
183   return this->page_counts_[pageno];
184 }
185
186 inline __attribute__((always_inline))
187 std::size_t PageStore::size() {
188   return this->top_index_ - this->free_pages_.size();
189 }
190
191 inline __attribute__((always_inline))
192 std::size_t PageStore::capacity()
193 {
194   return this->capacity_;
195 }
196
197 }
198 }
199
200 #endif