Logo AND Algorithmique Numérique Distribuée

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