Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
[mc] Documentation of mc_page_snapshot
[simgrid.git] / src / mc / mc_page_snapshot.cpp
1 #include "mc_page_store.h"
2 #include "mc_mmu.h"
3
4 #include <xbt/mmalloc.h>
5
6 #define SOFT_DIRTY_BIT_NUMBER 55
7 #define SOFT_DIRTY (((uint64_t)1) << SOFT_DIRTY_BIT_NUMBER)
8
9 extern "C" {
10
11 // ***** Region management:
12
13 /** @brief Take a per-page snapshot of a region
14  *
15  *  @param data            The start of the region (must be at the beginning of a page)
16  *  @param pag_count       Number of pages of the region
17  *  @param pagemap         Linux kernel pagemap values fot this region (or NULL)
18  *  @param reference_pages Snapshot page numbers of the previous soft_dirty_reset (or NULL)
19  *  @return                Snapshot page numbers of this new snapshot
20  */
21 size_t* mc_take_page_snapshot_region(void* data, size_t page_count, uint64_t* pagemap, size_t* reference_pages)
22 {
23   size_t* pagenos = (size_t*) malloc(page_count * sizeof(size_t));
24
25   for (size_t i=0; i!=page_count; ++i) {
26     bool softclean = pagemap && !(pagemap[i] & SOFT_DIRTY);
27     if (softclean && reference_pages) {
28       // The page is softclean, it is the same page as the reference page:
29       pagenos[i] = reference_pages[i];
30       mc_model_checker->pages->ref_page(reference_pages[i]);
31     } else {
32       // Otherwise, we need to store the page the hard hard
33       // (by reading its content):
34       void* page = (char*) data + (i << xbt_pagebits);
35       pagenos[i] = mc_model_checker->pages->store_page(page);
36     }
37   }
38
39   return pagenos;
40 }
41
42 void mc_free_page_snapshot_region(size_t* pagenos, size_t page_count)
43 {
44   for (size_t i=0; i!=page_count; ++i) {
45     mc_model_checker->pages->unref_page(pagenos[i]);
46   }
47 }
48
49 /** @brief Restore a snapshot of a region
50  *
51  *  If possible, the restoration will be incremental
52  *  (the modified pages will not be touched).
53  *
54  *  @param data            The start of the region (must be at the beginning of a page)
55  *  @param pag_count       Number of pages of the region
56  *  @param pagemap         Linux kernel pagemap values fot this region (or NULL)
57  *  @param reference_pages Snapshot page numbers of the previous soft_dirty_reset (or NULL)
58  */
59 void mc_restore_page_snapshot_region(mc_mem_region_t region, size_t page_count, uint64_t* pagemap, mc_mem_region_t reference_region)
60 {
61   for (size_t i=0; i!=page_count; ++i) {
62
63     bool softclean = pagemap && !(pagemap[i] & SOFT_DIRTY);
64     if (softclean && reference_region && reference_region->page_numbers[i] == region->page_numbers[i]) {
65       // The page is softclean and is the same as the reference one:
66       // the page is already in the target state.
67       continue;
68     }
69
70     // Otherwise, copy the page:
71     void* target_page = mc_page_from_number(region->start_addr, i);
72     const void* source_page = mc_model_checker->pages->get_page(region->page_numbers[i]);
73     memcpy(target_page, source_page, xbt_pagesize);
74   }
75 }
76
77 // ***** Soft dirty tracking
78
79 /** @brief Like pread() but without partial reads */
80 static size_t pread_whole(int fd, void* buf, size_t count, off_t offset) {
81   size_t res = 0;
82
83   char* data = (char*) buf;
84   while(count) {
85     ssize_t n = pread(fd, buf, count, offset);
86     // EOF
87     if (n==0)
88       return res;
89
90     // Error (or EINTR):
91     if (n==-1) {
92       if (errno == EINTR)
93         continue;
94       else
95         return -1;
96     }
97
98     // It might be a partial read:
99     count -= n;
100     data += n;
101     offset += n;
102     res += n;
103   }
104
105   return res;
106 }
107
108 static inline void mc_ensure_fd(int* fd, const char* path, int flags) {
109   if (*fd != -1)
110     return;
111   *fd = open(path, flags);
112   if (*fd == -1) {
113     xbt_die("Could not open file %s", path);
114   }
115 }
116
117 /** @brief Reset the soft-dirty bits
118  *
119  *  This is done after checkpointing and after checkpoint restoration
120  *  (if per page checkpoiting is used) in order to know which pages were
121  *  modified.
122  *
123  *  See https://www.kernel.org/doc/Documentation/vm/soft-dirty.txt
124  * */
125 void mc_softdirty_reset() {
126   mc_ensure_fd(&mc_model_checker->fd_clear_refs, "/proc/self/clear_refs", O_WRONLY|O_CLOEXEC);
127   if( ::write(mc_model_checker->fd_clear_refs, "4\n", 2) != 2) {
128     xbt_die("Could not reset softdirty bits");
129   }
130 }
131
132 /** @brief Read memory page informations
133  *
134  *  For each virtual memory page of the process,
135  *  /proc/self/pagemap provides a 64 bit field of information.
136  *  We are interested in the soft-dirty bit: with this we can track which
137  *  pages were modified between snapshots/restorations and avoid
138  *  copying data which was not modified.
139  *
140  *  See https://www.kernel.org/doc/Documentation/vm/pagemap.txt
141  *
142  *  @param pagemap    Output buffer for pagemap informations
143  *  @param start_addr Address of the first page
144  *  @param page_count Number of pages
145  */
146 static void mc_read_pagemap(uint64_t* pagemap, size_t page_start, size_t page_count)
147 {
148   mc_ensure_fd(&mc_model_checker->fd_pagemap, "/proc/self/pagemap", O_RDONLY|O_CLOEXEC);
149   size_t bytesize = sizeof(uint64_t) * page_count;
150   off_t offset = sizeof(uint64_t) * page_start;
151   if (pread_whole(mc_model_checker->fd_pagemap, pagemap, bytesize, offset) != bytesize) {
152     xbt_die("Could not read pagemap");
153   }
154 }
155
156 // ***** High level API
157
158 mc_mem_region_t mc_region_new_sparse(int type, void *start_addr, size_t size, mc_mem_region_t ref_reg)
159 {
160   mc_mem_region_t new_reg = xbt_new(s_mc_mem_region_t, 1);
161
162   new_reg->start_addr = start_addr;
163   new_reg->data = NULL;
164   new_reg->size = size;
165   new_reg->page_numbers = NULL;
166
167   xbt_assert((((uintptr_t)start_addr) & (xbt_pagesize-1)) == 0,
168     "Not at the beginning of a page");
169   size_t page_count = mc_page_count(size);
170
171   uint64_t* pagemap = NULL;
172   if (_sg_mc_soft_dirty && mc_model_checker->parent_snapshot) {
173       pagemap = (uint64_t*) mmalloc_no_memset((xbt_mheap_t) mc_heap, sizeof(uint64_t) * page_count);
174       mc_read_pagemap(pagemap, mc_page_number(NULL, start_addr), page_count);
175   }
176
177   // Take incremental snapshot:
178   new_reg->page_numbers = mc_take_page_snapshot_region(start_addr, page_count, pagemap,
179     ref_reg==NULL ? NULL : ref_reg->page_numbers);
180
181   if(pagemap) {
182     mfree((xbt_mheap_t) mc_heap, pagemap);
183   }
184   return new_reg;
185 }
186
187 void mc_region_restore_sparse(mc_mem_region_t reg, mc_mem_region_t ref_reg)
188 {
189   xbt_assert((((uintptr_t)reg->start_addr) & (xbt_pagesize-1)) == 0,
190     "Not at the beginning of a page");
191   size_t page_count = mc_page_count(reg->size);
192
193   uint64_t* pagemap = NULL;
194
195   // Read soft-dirty bits if necessary in order to know which pages have changed:
196   if (_sg_mc_soft_dirty && mc_model_checker->parent_snapshot) {
197     pagemap = (uint64_t*) mmalloc_no_memset((xbt_mheap_t) mc_heap, sizeof(uint64_t) * page_count);
198     mc_read_pagemap(pagemap, mc_page_number(NULL, reg->start_addr), page_count);
199   }
200
201   // Incremental per-page snapshot restoration:
202   mc_restore_page_snapshot_region(reg, page_count, pagemap, ref_reg);
203
204   // This is funny, the restoration can restore the state of the current heap,
205   // if this happen free(pagemap) would free from the wrong heap:
206   if(pagemap) {
207     mfree((xbt_mheap_t) mc_heap, pagemap);
208   }
209 }
210
211 }