Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Enable C++11
[simgrid.git] / src / mc / mc_page_store.cpp
1 /* Copyright (c) 2014. 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 #include <unistd.h>
8 #include <string.h> // memcpy, memcp
9
10 #include <sys/mman.h>
11
12 #include <boost/foreach.hpp>
13
14 #include <xbt.h>
15
16 #include "mc_page_store.h"
17
18 #ifdef MC_PAGE_STORE_MD4
19 #include <nettle/md4.h>
20 #endif
21
22 #include "mc_mmu.h"
23
24 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_page_snapshot, mc,
25                                 "Logging specific to mc_page_snapshot");
26
27 // ***** Utility:
28
29 /** @brief Compte a hash for the given memory page
30  *
31  *  The page is used before inserting the page in the page store
32  *  in order to find duplicate of this page in the page store.
33  *
34  *  @param data Memory page
35  *  @return hash off the page
36  */
37 static inline  __attribute__ ((always_inline))
38 s_mc_pages_store::hash_type mc_hash_page(const void* data)
39 {
40 #ifdef MC_PAGE_STORE_MD4
41    boost::array<uint64_t,2> result;
42    md4_ctx context;
43    md4_init(&context);
44    md4_update(&context, xbt_pagesize, (const uint8_t*) data);
45    md4_digest(&context, MD4_DIGEST_SIZE, (uint8_t*) &(result[0]));
46    return result;
47 #else
48   const uint64_t* values = (const uint64_t*) data;
49   size_t n = xbt_pagesize / sizeof(uint64_t);
50
51   // This djb2:
52   uint64_t hash = 5381;
53   for (size_t i=0; i!=n; ++i) {
54     hash = ((hash << 5) + hash) + values[i];
55   }
56   return hash;
57 #endif
58 }
59
60 // ***** snapshot_page_manager
61
62 s_mc_pages_store::s_mc_pages_store(size_t size) :
63   memory_(NULL), capacity_(0), top_index_(0)
64 {
65   // Using mmap in order to be able to expand the region
66   // by relocating it somewhere else in the virtual memory
67   // space:
68   void * memory = ::mmap(NULL, size << xbt_pagebits, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_POPULATE, -1, 0);
69   if (memory==MAP_FAILED) {
70     xbt_die("Could not mmap initial snapshot pages.");
71   }
72
73   this->top_index_ = 0;
74   this->capacity_ = size;
75   this->memory_ = memory;
76   this->page_counts_.resize(size);
77 }
78
79 s_mc_pages_store::~s_mc_pages_store()
80 {
81   ::munmap(this->memory_, this->capacity_ << xbt_pagebits);
82 }
83
84 void s_mc_pages_store::resize(size_t size)
85 {
86   size_t old_bytesize = this->capacity_ << xbt_pagebits;
87   size_t new_bytesize = size << xbt_pagebits;
88
89   // Expand the memory region by moving it into another
90   // virtual memory address if necessary:
91   void* new_memory = mremap(this->memory_, old_bytesize, new_bytesize, MREMAP_MAYMOVE);
92   if (new_memory == MAP_FAILED) {
93     xbt_die("Could not mremap snapshot pages.");
94   }
95
96   this->capacity_ = size;
97   this->memory_ = new_memory;
98   this->page_counts_.resize(size, 0);
99 }
100
101 /** Allocate a free page
102  *
103  *  @return index of the free page
104  */
105 size_t s_mc_pages_store::alloc_page()
106 {
107   if (this->free_pages_.empty()) {
108
109     // Expand the region:
110     if (this->top_index_ == this->capacity_) {
111       // All the pages are allocated, we need add more pages:
112       this->resize(2 * this->capacity_);
113     }
114
115     // Use a page from the top:
116     return this->top_index_++;
117
118   } else {
119
120     // Use a page from free_pages_ (inside of the region):
121     size_t res = this->free_pages_[this->free_pages_.size() - 1];
122     this->free_pages_.pop_back();
123     return res;
124
125   }
126 }
127
128 void s_mc_pages_store::remove_page(size_t pageno)
129 {
130   this->free_pages_.push_back(pageno);
131   const void* page = this->get_page(pageno);
132   hash_type hash = mc_hash_page(page);
133 #ifdef MC_PAGE_STORE_MD4
134   this->hash_index_.erase(hash);
135 #else
136   this->hash_index_[hash].erase(pageno);
137 #endif
138 }
139
140 /** Store a page in memory */
141 size_t s_mc_pages_store::store_page(void* page)
142 {
143   xbt_assert(top_index_ <= this->capacity_, "top_index is not consistent");
144
145   // First, we check if a page with the same content is already in the page
146   // store:
147   //  1. compute the hash of the page;
148   //  2. find pages with the same hash using `hash_index_`;
149   //  3. find a page with the same content.
150   hash_type hash = mc_hash_page(page);
151 #ifdef MC_PAGE_STORE_MD4
152   s_mc_pages_store::pages_map_type::const_iterator i =
153     this->hash_index_.find(hash);
154   if (i!=this->hash_index_.cend()) {
155     // If a page with the same content is already in the page store it is
156     // reused and its reference count is incremented.
157     size_t pageno = i->second;
158     page_counts_[pageno]++;
159     return pageno;
160   }
161 #else
162
163   // Try to find a duplicate in set of pages with the same hash:
164   page_set_type& page_set = this->hash_index_[hash];
165   BOOST_FOREACH (size_t pageno, page_set) {
166     const void* snapshot_page = this->get_page(pageno);
167     if (memcmp(page, snapshot_page, xbt_pagesize) == 0) {
168
169       // If a page with the same content is already in the page store it is
170       // reused and its reference count is incremented.
171       page_counts_[pageno]++;
172       return pageno;
173
174     }
175   }
176 #endif
177
178   // Otherwise, a new page is allocated in the page store and the content
179   // of the page is `memcpy()`-ed to this new page.
180   size_t pageno = alloc_page();
181   xbt_assert(this->page_counts_[pageno]==0, "Allocated page is already used");
182   void* snapshot_page = (void*) this->get_page(pageno);
183   memcpy(snapshot_page, page, xbt_pagesize);
184 #ifdef MC_PAGE_STORE_MD4
185   this->hash_index_[hash] = pageno;
186 #else
187   page_set.insert(pageno);
188 #endif
189   page_counts_[pageno]++;
190   return pageno;
191 }
192
193 // ***** Main C API
194
195 extern "C" {
196
197 mc_pages_store_t mc_pages_store_new()
198 {
199   return new s_mc_pages_store_t(500);
200 }
201
202 void mc_pages_store_delete(mc_pages_store_t store)
203 {
204   delete store;
205 }
206
207 }
208
209 #ifdef SIMGRID_TEST
210
211 #include <string.h>
212 #include <stdlib.h>
213 #include <stdint.h>
214 #include <unistd.h>
215 #include <sys/mman.h>
216
217 #include <memory>
218
219 #include "mc/mc_page_store.h"
220
221 static int value = 0;
222
223 static void new_content(void* data, size_t size)
224 {
225   memset(data, ++value, size);
226 }
227
228 static void* getpage()
229 {
230   return mmap(NULL, getpagesize(), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
231 }
232
233 extern "C" {
234
235 XBT_TEST_SUITE("mc_page_store", "Page store");
236
237 XBT_TEST_UNIT("base", test_mc_page_store, "Test adding/removing pages in the store")
238 {
239   xbt_test_add("Init");
240   size_t pagesize = (size_t) getpagesize();
241   std::unique_ptr<s_mc_pages_store_t> store = std::unique_ptr<s_mc_pages_store_t>(new s_mc_pages_store(500));
242   void* data = getpage();
243   xbt_test_assert(store->size()==0, "Bad size");
244
245   xbt_test_add("Store the page once");
246   new_content(data, pagesize);
247   size_t pageno1 = store->store_page(data);
248   xbt_test_assert(store->get_ref(pageno1)==1, "Bad refcount");
249   const void* copy = store->get_page(pageno1);
250   xbt_test_assert(memcmp(data, copy, pagesize)==0, "Page data should be the same");
251   xbt_test_assert(store->size()==1, "Bad size");
252
253   xbt_test_add("Store the same page again");
254   size_t pageno2 = store->store_page(data);
255   xbt_test_assert(pageno1==pageno2, "Page should be the same");
256   xbt_test_assert(store->get_ref(pageno1)==2, "Bad refcount");
257   xbt_test_assert(store->size()==1, "Bad size");
258
259   xbt_test_add("Store a new page");
260   new_content(data, pagesize);
261   size_t pageno3 = store->store_page(data);
262   xbt_test_assert(pageno1 != pageno3, "New page should be different");
263   xbt_test_assert(store->size()==2, "Bad size");
264
265   xbt_test_add("Unref pages");
266   store->unref_page(pageno1);
267   xbt_assert(store->get_ref(pageno1)==1, "Bad refcount");
268   xbt_assert(store->size()==2, "Bad size");
269   store->unref_page(pageno2);
270   xbt_test_assert(store->size()==1, "Bad size");
271
272   xbt_test_add("Reallocate page");
273   new_content(data, pagesize);
274   size_t pageno4 = store->store_page(data);
275   xbt_test_assert(pageno1 == pageno4, "Page was not reused");
276   xbt_test_assert(store->get_ref(pageno4)==1, "Bad refcount");
277   xbt_test_assert(store->size()==2, "Bad size");
278 }
279
280 }
281
282 #endif /* SIMGRID_TEST */