pagemap: Read compressed page images

Existing pagemap readers derive each pages-image offset from a fixed
PAGE_SIZE payload per present page. Packed compressed blocks make that
calculation invalid and must be reconstructed before reaching restore
destinations.

Track block and intra-region cursors while advancing, skipping, and
reading pagemap entries. Validate block counts, decoded sizes, totals,
and aligned-payload offsets before I/O. Read per-page and region images
locally, preserve ordinary parent entries, and support sequential
per-page image streams.

Carry block metadata with queued iovecs, coalesce only compatible
regions, read each packed batch once, and rebuild its destinations block
by block. Allow exactly IOV_MAX destinations and roll back iovec changes
if appending metadata fails. Decompress complete regions directly into
a contiguous destination and use scratch space for partial or split
regions. Copy the metadata into restorer memory for the later PIE decode
path.

Keep compressed images on buffered I/O, avoid hole punching packed
payloads, handle short reads, and keep compressed local reads out of PIE
until that path can decode them.

Assisted-by: Codex:GPT-5
Signed-off-by: Radostin Stoyanov <rstoyanov@fedoraproject.org>
This commit is contained in:
Radostin Stoyanov 2026-03-15 17:44:20 +00:00
parent b7e6e6e19e
commit 1ab35f2e5f
3 changed files with 1262 additions and 18 deletions

View file

@ -98,6 +98,23 @@ struct page_read {
/* Current offset in pages file */
off_t pi_off;
/* Alignment bytes a sequential image-streamer reader must discard. */
size_t stream_padding;
/*
* Index into pe->compressed_size[] for the current pagemap
* entry. Tracks which compressed block (page in per-page mode,
* region in region mode) we are on when reading or skipping
* compressed pages. Reset to 0 on advance().
*/
size_t compressed_size_index;
/*
* In region mode: pages already consumed (read or skipped) from
* the current block. Always 0 in per-page mode. Reset to 0 on
* advance() and whenever the reader crosses a block boundary.
*/
unsigned int region_block_offset;
/* Record consequent neighbour iov-ecs to punch together */
struct iovec bunch;
@ -201,4 +218,12 @@ static inline bool pagemap_payload_aligned(PagemapEntry *pe)
return !!(pe->flags & PE_PAYLOAD_ALIGNED);
}
/* Keep the mask as wide as off_t so offsets above 4 GiB remain intact. */
static inline off_t pagemap_page_align_offset(off_t offset)
{
off_t mask = ~((off_t)PAGE_SIZE - 1);
return (offset + (off_t)PAGE_SIZE - 1) & mask;
}
#endif /* __CR_PAGE_READ_H__ */

View file

@ -138,6 +138,20 @@ typedef long (*thread_restore_fcall_t)(struct thread_restore_args *args);
struct restore_vma_io {
int nr_iovs;
loff_t off;
uint32_t *compressed_size;
uint64_t total_compressed_size;
int n_compressed_size;
/*
* Region compression metadata. region_pages == 0 means per-page
* compression and block_pages is unused. When region_pages > 0,
* compressed_size[] holds n_compressed_size (== n_blocks) entries
* and block_pages is a parallel uint16_t-per-block array giving
* each block's page count (the last block of any pagemap entry
* spanning this iov may be shorter than region_pages).
*/
uint32_t region_pages;
int n_pages;
uint16_t *block_pages;
struct iovec iovs[0];
};

File diff suppressed because it is too large Load diff