Re: [PATCH v1 1/6] tools/migration: introduce PAGE_DATA_LZ4 stream record type
Frediano Ziglio <[email protected]> Tue, 4 Aug 2026 10:27:44 +0100
| Newsgroups | gmane.comp.emulators.xen.devel |
|---|---|
| Message-ID | <CAHt6W4dHih5z1LpRpdRiU_CC0dCQTF=324=KM6QJbUHS1xQ1GQ@mail.gmail.com> |
On Tue, 4 Aug 2026 at 10:15, Teddy Astie <[email protected]> wrote: > > Le 03/08/2026 =C3=A0 19:23, Marcus Granado a =C3=A9crit : > > On Wed, 29 Jul 2026 at 10:14, Frediano Ziglio <[email protected]> wrot= e: > >> About compressing all together and considering also the issue of > >> memory changing while sending I would vote to copy the memory in a > >> temporary buffer to avoid this. One advantage is that it simplified > >> the format. The current LZ4 implementation seems to cope with data > >> changes but nothing guarantees it in the future, the buffer you are > >> passing is not supposed to change while you compress it. > > > > > > Agreed. Frediano and I discussed this further, including simplification= s on > > the compression record for v2 so that the page data compression: > > * compresses the whole batch together > > * and uses a staged copy for it as you and Teddy suggested (so that we = do > > not need to worry about mutating buffers affecting LZ4 or other algorit= hms). > > > > Once there's exactly one compressed blob per record, there's no need fo= r > > extra fields to describe the compressed data, as the uncompressed batch > > size is known (and bounded by MAX_BATCH_SIZE * page_size), and its > > compressed size can be inferred from the size of the emitted compressed > > record without a need for an explicit field for clen or a field with th= e > > number of raw compressed blobs. This means the separate PAGE_DATA_LZ4 > > record type is not needed, and the proposal for v2 compression record c= ould > > simplify to reusing PAGE_DATA and spending one octet of its reserved wo= rd > > as follows: > > > > > > 0 1 2 3 4 5 6 7 octet > > +-----------------------+-----+-------------------+ > > | count (C) | comp| (reserved) | > > +-----------------------+-----+-------------------+ > > | pfn[0] | > > +-------------------------------------------------+ > > ... > > +-------------------------------------------------+ > > | pfn[C-1] | > > +-------------------------------------------------+ > > | page_data[0..N-1] if comp =3D=3D 0 | > > | or page_cdata if comp !=3D 0 | > > +-------------------------------------------------+ > > > > with two new entries in the field table: > > > > comp Compression algorithm applied to the page contents. 0 > > means none, and the record is exactly as it is today. 1 > > means LZ4 block format. Other values are reserved for > > other future formats like ZSTD etc. A comp !=3D 0 can only > > be emitted if 0 < len(page_cdata) < N * page_size. An > > unknown comp must cause the receiver to fail with a > > "Compression algorithm <value> not handled" error. > > > > page_cdata Present instead of page_data when comp is non-zero. A > > single compressed object holding the concatenation of the > > N page_data entries. The receiver must verify that > > 0 < count <=3D MAX_BATCH_SIZE. > > > > > > Teddy, I hope this format covers your points: it's no longer LZ4 specif= ic, > > the inline clen inconsistency in libxenguest record disappears, it's on= e > > block over an immutable copy of the batch instead of one per page, the > > decompressed size is known up front, and the allocation derived from co= unt > > is bounded on the receiver. > > > > Looks good to me. > > > > > This simplification is also forward-compatible in two different ways: y= our > > 64KB chunking for cache locality doesn't depend on the format, as the > > staging copy can be done in chunks while still making a single compress > > call over the whole batch. And if we find benefits in splitting the pay= load > > into several compressed units, that can be implemented as a new comp va= lue > > using a self-delimiting format like zstd or lz4f frames, which report t= he > > consumed bytes without a need to specify clen or extra framing fields a= t the > > record level. > > > > > > On Wed, 22 Jul 2026 at 20:42, Frediano Ziglio <[email protected]> wrot= e: > >> Don't we need to bump the version number while we add a new mandatory = record? > > > > I believe we may avoid having to do a version bump if we adopt the prop= erty > > that 0 < len(page_cdata) < N * page_size when comp !=3D 0, as in this c= ase > > the compressed data sent to an old receiver would fail in handle_page_d= ata() > > with "PAGE_DATA record wrong size". Bumping the version would make > > uncompressed migrations fail if they are sent to old receivers that als= o > > understand uncompressed migration, so avoiding if possible would be goo= d. > > The spec says migration tools "shall always save images using version V= ", > > so it doesn't seem like we could bump the version only when compression= is on. > > > > > >> Is there no kind of dialog about the supported version? > > > > There is no in-stream negotiation, and this proposal does not add one. > > Compression is opt-in at the sender via xl migrate --compress, so an op= erator > > who enables it against an old receiver gets a clean failure rather than > > corruption. > > > >> Why not extending the generalization compressing all payload of > >> uncompressed packets (type+body), > > > > A composable wrapper would be a clean generic mechanism, but I think th= ere > > are two reasons not to go that way in v2: PAGE_DATA is effectively all = of the > > stream bytes, so compressing the other record types would not show up i= n a > > measurement. And the no-extra-fields property above depends on PAGE_DAT= A > > specifically: a generic wrapper has no pfn array from where we can deri= ve > > the uncompressed size, so it would need explicit algorithm, compressed > > size and uncompressed size fields on every record, which re-adds the fi= elds > > that this proposal tries to avoid. > > > > > > Please let me know if the ideas above capture what you had in mind in t= erms of > > suggestions to improve v1. > > > > In particular, I wonder what the maintainers think of the use of one of= the > > octets of the PAGE_DATA reserved field for the purpose of indicating th= e data > > is compressed, or is it preferable to use a new PAGE_DATA_COMPRESSED (0= x13) > > type record as a mandatory record so that an old receiver fails more cl= eanly > > when it doesn't understand compression "Mandatory record <name> not han= dled" > > (caused by the specification of mandatory records) instead of with a ge= neric > > error "PAGE_DATA record wrong size" (caused by the current safety > > implementation that rejects unexpected record sizes)? > > > > The specification says > > > Padding and reserved fields are set to zero on save and must be > ignored during restore. > > Which is not really going to help if we add a new field that matters on > how the content is organized. > Yes, there's no check for "_res1" being 0, however there's this check: if ( rec->length !=3D (sizeof(*pages) + (sizeof(uint64_t) * pages->count) + (PAGE_SIZE * pages_of_data)) ) { ERROR("PAGE_DATA record wrong size: length %u, expected " "%zu + %zu + %lu", rec->length, sizeof(*pages), (sizeof(uint64_t) * pages->count), (PAGE_SIZE * pages_of_data= )); goto err; } so, as long as we require that the compressed data is less than the uncompressed one (we should) it's fine. > To avoid confusion, it may be desirable to add a new type > (PAGE_DATA_COMPRESSED), but I'm not fully sold on it. > Not strong but if I could vote I would just use part of "_res1" as proposed= . > > > > Marcus > > > > > > Teddy Frediano