Skip to content

Commit b23ad22

Browse files
committed
Don't dump pages which only contain zero bytes
Introduces a new command line option '--skip-zero-bytes' which detects pages which only contain zero bytes and prohibits that they get dumped in the processes image file. It is a potentially expensive operation because it checks for every single process page if it contains only zeros, but it can significantly decrease the image size and improve the startup-time if many such pages exist. It effectively replaces such pages which the kernel's zero-page on restore. Signed-off-by: Volker Simonis <[email protected]>
1 parent 50190ae commit b23ad22

File tree

18 files changed

+347
-7
lines changed

18 files changed

+347
-7
lines changed

Documentation/criu.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,14 @@ mount -t cgroup -o devices,freezer none devices,freezer
369369
Deduplicate "old" data in pages images of previous *dump*. This option
370370
implies incremental *dump* mode (see the *pre-dump* command).
371371

372+
*--skip-zero-pages*::
373+
Don't dump pages containing only zero bytes. This is a
374+
potentially expensive operation because it checks for
375+
every single process page if it contains only zeros, but
376+
it can significantly decrease the image size and improve the
377+
startup-time if many such pages exist. It effectively
378+
replaces such pages which the kernel's zero-page on restore.
379+
372380
*-l*, *--file-locks*::
373381
Dump file locks. It is necessary to make sure that all file lock users
374382
are taken into dump, so it is only safe to use this for enclosed containers

criu/config.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,7 @@ int parse_options(int argc, char **argv, bool *usage_error, bool *has_exec_cmd,
650650
{ "ms", no_argument, 0, 1054 },
651651
BOOL_OPT("track-mem", &opts.track_mem),
652652
BOOL_OPT("auto-dedup", &opts.auto_dedup),
653+
BOOL_OPT("skip-zero-pages", &opts.skip_zero_pages),
653654
{ "libdir", required_argument, 0, 'L' },
654655
{ "cpu-cap", optional_argument, 0, 1057 },
655656
BOOL_OPT("force-irmap", &opts.force_irmap),

criu/cr-service.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,9 @@ static int setup_opts_from_req(int sk, CriuOpts *req)
541541
if (req->has_auto_dedup)
542542
opts.auto_dedup = req->auto_dedup;
543543

544+
if (req->has_skip_zero_pages)
545+
opts.skip_zero_pages = req->skip_zero_pages;
546+
544547
if (req->has_force_irmap)
545548
opts.force_irmap = req->force_irmap;
546549

criu/crtools.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,7 @@ int main(int argc, char *argv[], char *envp[])
541541
" pages images of previous dump\n"
542542
" when used on restore, as soon as page is restored, it\n"
543543
" will be punched from the image\n"
544+
" --skip-zero-pages don't dump pages containing only zero bytes.\n"
544545
" --pre-dump-mode splice - parasite based pre-dumping (default)\n"
545546
" read - process_vm_readv syscall based pre-dumping\n"
546547
"\n"

criu/include/cr_options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ struct cr_options {
157157
int track_mem;
158158
char *img_parent;
159159
int auto_dedup;
160+
int skip_zero_pages;
160161
unsigned int cpu_cap;
161162
int force_irmap;
162163
char **exec_cmd;

criu/include/stats.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ enum {
3333
CNT_SHPAGES_SKIPPED_PARENT,
3434
CNT_SHPAGES_WRITTEN,
3535

36+
CNT_SKIPPED_ZERO_PAGES,
37+
3638
DUMP_CNT_NR_STATS,
3739
};
3840

criu/mem.c

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
#include <sys/mman.h>
44
#include <errno.h>
55
#include <fcntl.h>
6+
#include <string.h>
67
#include <sys/syscall.h>
78
#include <sys/prctl.h>
9+
#include <sys/uio.h>
810

911
#include "types.h"
1012
#include "cr_options.h"
@@ -31,6 +33,7 @@
3133
#include "prctl.h"
3234
#include "compel/infect-util.h"
3335
#include "pidfd-store.h"
36+
#include "xmalloc.h"
3437

3538
#include "protobuf.h"
3639
#include "images/pagemap.pb-c.h"
@@ -191,11 +194,33 @@ static int generate_iovs(struct pstree_item *item, struct vma_area *vma, struct
191194
bool has_parent)
192195
{
193196
unsigned long nr_scanned;
194-
unsigned long pages[3] = {};
197+
/* Counters for PAGES_SKIPPED_PARENT, PAGES_LAZY, PAGES_WRITTEN and SKIPPED_ZERO_PAGES */
198+
unsigned long pages[4] = {};
195199
unsigned long vaddr;
196200
bool dump_all_pages;
197201
int ret = 0;
198202

203+
static char *zero_page = NULL;
204+
static char *remote_page = NULL;
205+
int zero = 0;
206+
struct iovec local[2];
207+
struct iovec remote[1];
208+
int nread = 0;
209+
if (opts.skip_zero_pages && zero_page == NULL) {
210+
zero_page = xmalloc(PAGE_SIZE);
211+
remote_page = xmalloc(PAGE_SIZE);
212+
if (zero_page == NULL || remote_page == NULL) {
213+
pr_warn("Can't allocate memory - disabling --skip-zero-pages\n");
214+
opts.skip_zero_pages = 0;
215+
} else {
216+
memzero(zero_page, PAGE_SIZE);
217+
local[0].iov_base = remote_page;
218+
local[0].iov_len = PAGE_SIZE;
219+
remote[0].iov_base = (void *)0x0;
220+
remote[0].iov_len = PAGE_SIZE;
221+
}
222+
}
223+
199224
dump_all_pages = should_dump_entire_vma(vma->e);
200225

201226
nr_scanned = 0;
@@ -207,9 +232,25 @@ static int generate_iovs(struct pstree_item *item, struct vma_area *vma, struct
207232

208233
/* If dump_all_pages is true, should_dump_page is called to get pme. */
209234
next = should_dump_page(pmc, vma->e, vaddr, &softdirty);
210-
if (!dump_all_pages && next != vaddr) {
211-
vaddr = next - PAGE_SIZE;
212-
continue;
235+
if (!dump_all_pages) {
236+
if (next != vaddr) {
237+
vaddr = next - PAGE_SIZE;
238+
continue;
239+
} else if (opts.skip_zero_pages) {
240+
remote[0].iov_base = (void *)vaddr;
241+
nread = process_vm_readv(item->pid->real, local, 1, remote, 1, 0);
242+
if (nread == PAGE_SIZE) {
243+
zero = memcmp(zero_page, remote_page, PAGE_SIZE);
244+
/*
245+
* If the page contains just zeros we can treat it like the zero page and skip it.
246+
* At restore it will be replaced by a reference to the zero page and COWed if accessed.
247+
*/
248+
if (zero == 0) {
249+
pages[3]++;
250+
continue;
251+
}
252+
}
253+
}
213254
}
214255

215256
if (vma_entry_can_be_lazy(vma->e) && !is_stack(item, vaddr))
@@ -247,8 +288,10 @@ static int generate_iovs(struct pstree_item *item, struct vma_area *vma, struct
247288
cnt_add(CNT_PAGES_SKIPPED_PARENT, pages[0]);
248289
cnt_add(CNT_PAGES_LAZY, pages[1]);
249290
cnt_add(CNT_PAGES_WRITTEN, pages[2]);
291+
cnt_add(CNT_SKIPPED_ZERO_PAGES, pages[3]);
250292

251-
pr_info("Pagemap generated: %lu pages (%lu lazy) %lu holes\n", pages[2] + pages[1], pages[1], pages[0]);
293+
pr_info("Pagemap generated: %lu pages (%lu lazy) %lu holes %lu skipped zero\n",
294+
pages[2] + pages[1], pages[1], pages[0], pages[3]);
252295
return ret;
253296
}
254297

criu/stats.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ static void display_stats(int what, StatsEntry *stats)
134134
stats->dump->pages_skipped_parent, stats->dump->pages_skipped_parent);
135135
pr_msg("Memory pages written: %" PRIu64 " (0x%" PRIx64 ")\n", stats->dump->pages_written,
136136
stats->dump->pages_written);
137+
if (stats->dump->has_skipped_zero_pages)
138+
pr_msg("Memory pages skipped because zero: %" PRIu64 " (0x%" PRIx64 ")\n",
139+
stats->dump->skipped_zero_pages, stats->dump->skipped_zero_pages);
137140
pr_msg("Lazy memory pages: %" PRIu64 " (0x%" PRIx64 ")\n", stats->dump->pages_lazy,
138141
stats->dump->pages_lazy);
139142
} else if (what == RESTORE_STATS) {
@@ -178,6 +181,10 @@ void write_stats(int what)
178181
ds_entry.has_page_pipes = true;
179182
ds_entry.page_pipe_bufs = dstats->counts[CNT_PAGE_PIPE_BUFS];
180183
ds_entry.has_page_pipe_bufs = true;
184+
if (opts.skip_zero_pages) {
185+
ds_entry.has_skipped_zero_pages = true;
186+
ds_entry.skipped_zero_pages = dstats->counts[CNT_SKIPPED_ZERO_PAGES];
187+
}
181188

182189
ds_entry.shpages_scanned = dstats->counts[CNT_SHPAGES_SCANNED];
183190
ds_entry.has_shpages_scanned = true;

images/rpc.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ message criu_opts {
145145
optional bool leave_stopped = 69;
146146
optional bool display_stats = 70;
147147
optional bool log_to_stderr = 71;
148+
optional bool skip_zero_pages = 72;
148149
/* optional bool check_mounts = 128; */
149150
}
150151

images/stats.proto

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ message dump_stats_entry {
2222
optional uint64 shpages_scanned = 12;
2323
optional uint64 shpages_skipped_parent = 13;
2424
optional uint64 shpages_written = 14;
25+
26+
optional uint64 skipped_zero_pages = 15;
2527
}
2628

2729
message restore_stats_entry {

0 commit comments

Comments
 (0)