Logo AND Algorithmique Numérique Distribuée

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