Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
[mc] Add a (compile time) option for content addressage page store
[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(mc_page_offset(page)==0, "Not at the beginning of a page");
144   xbt_assert(top_index_ <= this->capacity_, "top_index is not consistent");
145
146   // First, we check if a page with the same content is already in the page
147   // store:
148   //  1. compute the hash of the page;
149   //  2. find pages with the same hash using `hash_index_`;
150   //  3. find a page with the same content.
151   hash_type hash = mc_hash_page(page);
152 #ifdef MC_PAGE_STORE_MD4
153   s_mc_pages_store::pages_map_type::const_iterator i =
154     this->hash_index_.find(hash);
155   if (i!=this->hash_index_.cend()) {
156     // If a page with the same content is already in the page store it is
157     // reused and its reference count is incremented.
158     size_t pageno = i->second;
159     page_counts_[pageno]++;
160     return pageno;
161   }
162 #else
163
164   // Try to find a duplicate in set of pages with the same hash:
165   page_set_type& page_set = this->hash_index_[hash];
166   BOOST_FOREACH (size_t pageno, page_set) {
167     const void* snapshot_page = this->get_page(pageno);
168     if (memcmp(page, snapshot_page, xbt_pagesize) == 0) {
169
170       // If a page with the same content is already in the page store it is
171       // reused and its reference count is incremented.
172       page_counts_[pageno]++;
173       return pageno;
174
175     }
176   }
177 #endif
178
179   // Otherwise, a new page is allocated in the page store and the content
180   // of the page is `memcpy()`-ed to this new page.
181   size_t pageno = alloc_page();
182   xbt_assert(this->page_counts_[pageno]==0, "Allocated page is already used");
183   void* snapshot_page = (void*) this->get_page(pageno);
184   memcpy(snapshot_page, page, xbt_pagesize);
185 #ifdef MC_PAGE_STORE_MD4
186   this->hash_index_[hash] = pageno;
187 #else
188   page_set.insert(pageno);
189 #endif
190   page_counts_[pageno]++;
191   return pageno;
192 }
193
194 // ***** Main C API
195
196 extern "C" {
197
198 mc_pages_store_t mc_pages_store_new()
199 {
200   return new s_mc_pages_store_t(500);
201 }
202
203 void mc_pages_store_delete(mc_pages_store_t store)
204 {
205   delete store;
206 }
207
208 }
209
210 #ifdef SIMGRID_TEST
211
212 #include <string.h>
213 #include <stdlib.h>
214 #include <stdint.h>
215 #include <unistd.h>
216 #include <sys/mman.h>
217
218 #include <memory>
219
220 #include "mc/mc_page_store.h"
221
222 static int value = 0;
223
224 static void new_content(void* data, size_t size)
225 {
226   memset(data, ++value, size);
227 }
228
229 static void* getpage()
230 {
231   return mmap(NULL, getpagesize(), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
232 }
233
234 extern "C" {
235
236 XBT_TEST_SUITE("mc_page_store", "Page store");
237
238 XBT_TEST_UNIT("base", test_mc_page_store, "Test adding/removing pages in the store")
239 {
240   xbt_test_add("Init");
241   size_t pagesize = (size_t) getpagesize();
242   std::auto_ptr<s_mc_pages_store_t> store = std::auto_ptr<s_mc_pages_store_t>(new s_mc_pages_store(500));
243   void* data = getpage();
244   xbt_test_assert(store->size()==0, "Bad size");
245
246   xbt_test_add("Store the page once");
247   new_content(data, pagesize);
248   size_t pageno1 = store->store_page(data);
249   xbt_test_assert(store->get_ref(pageno1)==1, "Bad refcount");
250   const void* copy = store->get_page(pageno1);
251   xbt_test_assert(memcmp(data, copy, pagesize)==0, "Page data should be the same");
252   xbt_test_assert(store->size()==1, "Bad size");
253
254   xbt_test_add("Store the same page again");
255   size_t pageno2 = store->store_page(data);
256   xbt_test_assert(pageno1==pageno2, "Page should be the same");
257   xbt_test_assert(store->get_ref(pageno1)==2, "Bad refcount");
258   xbt_test_assert(store->size()==1, "Bad size");
259
260   xbt_test_add("Store a new page");
261   new_content(data, pagesize);
262   size_t pageno3 = store->store_page(data);
263   xbt_test_assert(pageno1 != pageno3, "New page should be different");
264   xbt_test_assert(store->size()==2, "Bad size");
265
266   xbt_test_add("Unref pages");
267   store->unref_page(pageno1);
268   xbt_assert(store->get_ref(pageno1)==1, "Bad refcount");
269   xbt_assert(store->size()==2, "Bad size");
270   store->unref_page(pageno2);
271   xbt_test_assert(store->size()==1, "Bad size");
272
273   xbt_test_add("Reallocate page");
274   new_content(data, pagesize);
275   size_t pageno4 = store->store_page(data);
276   xbt_test_assert(pageno1 == pageno4, "Page was not reused");
277   xbt_test_assert(store->get_ref(pageno4)==1, "Bad refcount");
278   xbt_test_assert(store->size()==2, "Bad size");
279 }
280
281 }
282
283 #endif /* SIMGRID_TEST */