Add a new --skip-pages flag for lightweight dumps that only generate pagemap.img without the actual pages.img. All pages are marked as PE_LAZY so that external restore mechanisms can provide page data. This is useful for RMFork integration where kernel-side pmem handles the actual page data. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --- criu/mem.c | 16 +++++++++++++++- criu/page-xfer.c | 44 ++++++++++++++++++++++++++++++++++++-------- criu/shmem.c | 19 +++++++++++++------ 3 files changed, 64 insertions(+), 15 deletions(-) diff --git a/criu/mem.c b/criu/mem.c index 803cb545b..b06a0d850 100644 --- a/criu/mem.c +++ b/criu/mem.c @@ -543,7 +543,15 @@ static int __parasite_dump_pages_seized(struct pstree_item *item, struct parasit if (ret < 0) goto out_pp; - xfer.transfer_lazy = !mdc->lazy; + /* + * With --skip-pages, we don't write actual page data. + * Set transfer_lazy to false so pages are only recorded + * in pagemap without being written to pages.img. + */ + if (opts.skip_pages) + xfer.transfer_lazy = false; + else + xfer.transfer_lazy = !mdc->lazy; } else { ret = check_parent_page_xfer(CR_FD_PAGEMAP, vpid(item)); if (ret < 0) @@ -582,9 +590,15 @@ static int __parasite_dump_pages_seized(struct pstree_item *item, struct parasit * will happen after task unfreezing in cr_pre_dump_finish(). This is * actual optimization which reduces time for which process was frozen * during pre-dump. + * + * With --skip-pages, we don't need to actually drain page data + * since pages.img won't be generated. This is a significant + * optimization for lightweight snapshots. */ if (mdc->pre_dump && opts.pre_dump_mode == PRE_DUMP_READ) ret = 0; + else if (opts.skip_pages) + ret = 0; /* Skip draining pages for lightweight dump */ else ret = drain_pages(pp, ctl, args); diff --git a/criu/page-xfer.c b/criu/page-xfer.c index 0314963e6..f59983782 100644 --- a/criu/page-xfer.c +++ b/criu/page-xfer.c @@ -257,6 +257,10 @@ static int write_pages_loc(struct page_xfer *xfer, int p, unsigned long len) ssize_t ret; ssize_t curr = 0; + /* With --skip-pages, pi is NULL, so just return success */ + if (!xfer->pi) + return 0; + while (1) { ret = splice(p, NULL, img_raw_fd(xfer->pi), NULL, len - curr, SPLICE_F_MOVE); if (ret == -1) { @@ -358,7 +362,8 @@ static void close_page_xfer(struct page_xfer *xfer) xfree(xfer->parent); xfer->parent = NULL; } - close_image(xfer->pi); + if (xfer->pi) + close_image(xfer->pi); close_image(xfer->pmi); } @@ -370,9 +375,22 @@ static int open_page_local_xfer(struct page_xfer *xfer, int fd_type, unsigned lo if (!xfer->pmi) return -1; - xfer->pi = open_pages_image(O_DUMP, xfer->pmi, &pages_id); - if (!xfer->pi) - goto err_pmi; + /* + * With --skip-pages, we only create pagemap.img without pages.img. + * Pages will be marked as PE_LAZY for external restore mechanism. + */ + if (opts.skip_pages) { + /* Still write PagemapHead with a dummy pages_id for compatibility */ + PagemapHead h = PAGEMAP_HEAD__INIT; + h.pages_id = 0; /* No actual pages.img will exist */ + if (pb_write_one(xfer->pmi, &h, PB_PAGEMAP_HEAD) < 0) + goto err_pmi; + xfer->pi = NULL; + } else { + xfer->pi = open_pages_image(O_DUMP, xfer->pmi, &pages_id); + if (!xfer->pi) + goto err_pmi; + } /* * Open page-read for parent images (if it exists). It will @@ -380,9 +398,10 @@ static int open_page_local_xfer(struct page_xfer *xfer, int fd_type, unsigned lo * 1) when writing a page, those from parent will be dedup-ed * 2) when writing a hole, the respective place would be checked * to exist in parent (either pagemap or hole) + * Note: skip-pages mode doesn't support incremental dump, so skip this. */ xfer->parent = NULL; - if (fd_type == CR_FD_PAGEMAP || fd_type == CR_FD_SHMEM_PAGEMAP) { + if (!opts.skip_pages && (fd_type == CR_FD_PAGEMAP || fd_type == CR_FD_SHMEM_PAGEMAP)) { int ret; int pfd; int pr_flags = (fd_type == CR_FD_PAGEMAP) ? PR_TASK : PR_SHMEM; @@ -420,7 +439,8 @@ out: return 0; err_pi: - close_image(xfer->pi); + if (xfer->pi) + close_image(xfer->pi); err_pmi: close_image(xfer->pmi); return -1; @@ -490,8 +510,16 @@ static inline u32 ppb_xfer_flags(struct page_xfer *xfer, struct page_pipe_buf *p * as present as well. */ return (xfer->transfer_lazy ? PE_PRESENT : 0) | PE_LAZY; - else - return PE_PRESENT; + + /* + * With --skip-pages, all pages are marked as PE_LAZY without PE_PRESENT. + * This signals that page data is not in pages.img and should be + * provided by external mechanism during restore. + */ + if (opts.skip_pages) + return PE_LAZY; + + return PE_PRESENT; } /* diff --git a/criu/shmem.c b/criu/shmem.c index 9e3178352..d984d2c17 100644 --- a/criu/shmem.c +++ b/criu/shmem.c @@ -693,12 +693,19 @@ static int dump_pages(struct page_pipe *pp, struct page_xfer *xfer) { struct page_pipe_buf *ppb; - list_for_each_entry(ppb, &pp->bufs, l) - if (vmsplice(ppb->p[1], ppb->iov, ppb->nr_segs, SPLICE_F_GIFT | SPLICE_F_NONBLOCK) != - ppb->pages_in * PAGE_SIZE) { - pr_perror("Can't get shmem into page-pipe"); - return -1; - } + /* + * With --skip-pages, we don't need to actually splice page data + * since pages.img won't be generated. This is an optimization + * for lightweight snapshots. + */ + if (!opts.skip_pages) { + list_for_each_entry(ppb, &pp->bufs, l) + if (vmsplice(ppb->p[1], ppb->iov, ppb->nr_segs, SPLICE_F_GIFT | SPLICE_F_NONBLOCK) != + ppb->pages_in * PAGE_SIZE) { + pr_perror("Can't get shmem into page-pipe"); + return -1; + } + } return page_xfer_dump_pages(xfer, pp); } -- 2.53.0