[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v1 1/6] tools/migration: introduce PAGE_DATA_LZ4 stream record type


  • To: Teddy Astie <teddy.astie@xxxxxxxxxx>, "xen-devel@xxxxxxxxxxxxxxxxxxxx" <xen-devel@xxxxxxxxxxxxxxxxxxxx>
  • From: Marcus Granado <marcus.granado@xxxxxxxxxx>
  • Date: Thu, 23 Jul 2026 13:14:01 +0000
  • Accept-language: en-GB, en-US
  • Arc-authentication-results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=citrix.com; dmarc=pass action=none header.from=citrix.com; dkim=pass header.d=citrix.com; arc=none
  • Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=nolBbTm22vHTne5a275fXU1vUqEx0wtdFj8Wk3AQHVE=; b=N4Xmz7OvXpRC6QL4BPmALJce5qCkhy4gioIzHAyBF2+qE+NT+PWj3/XQr3Ijfh7/DeYvZmJIrdO0bDx/8Dtxqy8YXk4jESx5gnKTR1XJcIbaMdAPNjeEwZ+63NQCGJHsWnPxshkyam7WjqWM1UZ7WZ3jOdxtAkxdcV21ncDVvuErFbcyEcHYtqV0kLP2FrNiOPJoRMvEhvdSTQFdR12sjzPEaEPabk6EJ/IpOdN+FnCPyXAQfyshNeK0mo/SHv9I39hz1qSANws8nZOn3JrwPfkKBFLy9iAZJhH6fkjcAEIi71DwKAL1fnd+l7v4x2YGlHEkiq7DuMx3GyFmcoAvhA==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=xUR+UlFmFrf7XWsZ5RH5btzrukM2mGUrxMPZzE768tncS3t4GJT+kK8iZNho9fKAgMDQsziwv4X//CmLo1SShdmxcd0ETE4o+4mtxSkaHn7uMcpZsFcFRUhe6sqsQIJ4vaeVMZqQ++CMIM7UXKx9FZx/CJWQrx/EXsleaU2evIO7/LhqSUAMQvw5AMTWe/AaXL3oCyfOu45Dy6nqRVRFJa9z0DhScr8LOLLl2zjdcC9nR7tbCTQwJ4uQrJMx3rxFTto3/Xfvt++0dwXRNfe/BvqD4qrRsWksgTuH+J0c7LLGLYbsPQjpzvcireaPy6kMcstL20wW3w3mknmusn8gpA==
  • Authentication-results: eu.smtp.expurgate.cloud; dkim=pass header.s=selector1 header.d=citrix.com header.i="@citrix.com" header.h="From:Date:Subject:Message-ID:Content-Type:MIME-Version:x-ms-exchange-senderadcheck"
  • Authentication-results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=citrix.com;
  • Cc: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>, Roger Pau Monne <roger.pau@xxxxxxxxxx>, Frediano Ziglio <freddy77@xxxxxxxxx>, Anthony PERARD <anthony.perard@xxxxxxxxxx>, Marek Marczykowski-Górecki <marmarek@xxxxxxxxxxxxxxxxxxxxxx>
  • Delivery-date: Thu, 23 Jul 2026 13:14:13 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>
  • Msip_labels:
  • Thread-index: AQHdGF9mhIhyvqGSRUyPVZ4Z3HHB7rZ3oucAgANsT7g=
  • Thread-topic: [PATCH v1 1/6] tools/migration: introduce PAGE_DATA_LZ4 stream record type

On 21/07/2026 at 09:20, Teddy Astie wrote:
> Le 20/07/2026 à 17:51, Marcus Granado a écrit :
>> +PAGE_DATA_LZ4
>> +-------------
>> +
>> +A PAGE_DATA_LZ4 record carries exactly the same information as a
>> +PAGE_DATA record, but with the page contents LZ4-compressed.  The saver
>> +may emit it in place of a PAGE_DATA record when LZ4 compression has been
>> +requested.
>
> Would it be preferable to make this structure more generic, i.e
> PAGE_DATA_COMPRESSED, as I'm not sure it's wise to restrict to only LZ4
> (someone may want to add support for e.g zstd or another algorithm
> without having to change the format) ?

It should be possible to create a more generic structure like
PAGE_DATA_COMPRESSED instead of PAGE_DATA_LZ4, which accepts different
compression algorithms in extra new fields. I suspect the challenge here is
how generic to make it: I wonder if it wouldn't be equivalent and simpler to
instead add new record types (PAGE_DATA_ZSTD etc) in the future when
necessary, as a new algorithm needs new code on both ends anyway and reuses
the same per-page framing, so a generic record would not actually avoid a
code change. Perhaps we could adopt a PAGE_DATA_COMPRESSED_* family prefix
for the wire format doc, and rename the algorithm type PAGE_DATA_LZ4 ->
PAGE_DATA_COMPRESSED_LZ4, so that eg. in the future
PAGE_DATA_COMPRESSED_ZSTD etc reuses the same family document and is only a
new type id. This would be an interesting question for the maintainers of
libxenguest.

>> +            Each sub-block is a `uint16` little-endian length `clen`
>> +            followed by either an LZ4 block or a raw page.  When
>> +            `clen > 0`, page_data_lz4 is `clen` octets of a raw LZ4
>> +            block [...] When `clen == 0`, page_data_lz4 is
>> +            page_size octets of a raw, uncompressed page.
>
> The inline clen at the beginning of page_data_lz4 feels a bit odd to me,
> or at least, a bit inconsistent with how pfns are arranged. I think
> something like i.e
>
> 1. count and (reserved)
> 2. pfn[0] ... pfn[C-1]
> 3. clen[0] ... clen[N-1]
> 4. page_data_lz4[0] ... page_data_lz4[N-1]
>
> would be better.

Using an array clen[0..N-1] before the page_data_lz4[0..N-1] is neat and as
you said follows the existing pfn[0..C-1] pattern, and I think it would be
good to adopt it in v2.

> Though, aside that, I'm not sure having separate compressed block for
> each page is a good idea. Compression is more efficient when processing
> larger blocks, LZ4 documents a 64 KB deduplication window (of the past
> bytes) [1].

LZ4 expects the source buffer to be immutable for the duration of the
compression call. However, during live migration the guest is still running
and updating its pages concurrently with the compression. This mutable
source buffer can cause compressed back-references to be inconsistent in the
>4KB deduplication window (LZ4's match window reaches 64 KB, ie. up to 16
pages), resulting in corrupt decompressed pages on the destination.
Corrupted dirty pages would be resent and healed in the next iteration, but
corrupted clean (non-dirty) pages wouldn't. Indeed, I initially experienced
with compressing the whole 4MB batch and found the guests would crash on the
destination due to corrupted page contents after decompression. Compressing
individual 4KB pages avoids this back-reference inconsistency issue across
pages. Any inconsistency caused by a concurrent write inside an
individually-compressed 4KB page is not a problem, as it's marked dirty
anyway and will be resend in the next iteration, so the problem is only
present when compressing together >1 page.

I also tried staging: compress from a read-only copy of the whole 4 MB batch
to avoid the concurrent guest writes. That is safe, but it adds a second,
slower full memory pass per batch, which I suspect is better to avoid to
minimise memory bottlenecks in this high-throughput memory transfer
operation. Measured on identical buffers, per-page in general costs <1% of
the batch on ordinary compressible pages and is no worse on incompressible
pages (it stores them raw rather than expanding them); per-batch wins when a
page is duplicated within LZ4's 64 KB window, which per-page cannot dedup,
and in the measured scenarios the aggregate effect on a real migration is
not visible within the run-to-run variations when comparing per-batch
compression vs per-page compression, so in v1 I adopted the per-4K-page
compression.

> In my opinion, it may be preferable to have one large block with all the
> page datas (covering up to MAX_BATCH_SIZE=1024 pages) rather than having
> to uncompress individual blocks. Which in the end would remove the need
> to have individual clen[n].
>
> What do you think ?

Regarding using one large block (B=1), this would resurface the issue of the
cross-page back references problem, as the guest keeps updating the pages
while we compress them. Per-page independent blocks (B=N) make it safe to
compress directly from the live mapping. Right now we have B=N (the
zero-copy non-staged compression in v1), and we could extend it to allow B=1
(the cross-page compression with staging) in the future, something like:

1. count and (reserved)
2. pfn[0] ... pfn[C-1]
3. B
   if B==N: zero-copy, non-staged per-page compression units
   if B==1: staged per-batch compression unit
4. clen[0] ... clen[B-1]
5. cdata[0] ... cdata[B-1]

If we wanted to allow a generic 1<=B<=N, for B arbitrary compression units
for arbitrary blocks in each unit, then we'd probably need another array
plen[0..B-1] to map a compression unit to expected decompressed
cdata[0..B-1] size for the decompressor.




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.