Compare commits

..

40 Commits

Author SHA1 Message Date
Fabiano Rosas
9f1a8f4e85 tests/qtest/migration: Use the new migration_test_add
Replace the tests registration with the new function that prints tests
names.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 13:30:16 -03:00
Fabiano Rosas
47b2c3d4f6 tests/qtest/migration: Add a wrapper to print test names
Our usage of gtest results in us losing the very basic functionality
of "knowing which test failed". The issue is that gtest only prints
test names ("paths" in gtest parlance) once the test has finished, but
we use asserts in the tests and crash gtest itself before it can print
anything. We also use a final abort when the result of g_test_run is
not 0.

Depending on how the test failed/broke we can see the function that
trigged the abort, which may be representative of the test, but it
could also just be some generic function.

We have been relying on the primitive method of looking at the name of
the previous successful test and then looking at the code to figure
out which test should have come next.

Add a wrapper to the test registration that does the job of printing
the test name before running.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 13:30:16 -03:00
Fabiano Rosas
086d8dc142 tests/qtest: Add a test for fixed-ram with passing of fds
Add a multifd test for fixed-ram with passing of fds into QEMU. This
is how libvirt will consume the feature.

There are a couple of details to the fdset mechanism:

- multifd needs two distinct file descriptors (not duplicated with
  dup()) on the outgoing side so it can enable O_DIRECT only on the
  channels that write with alignment. The dup() system call creates
  file descriptors that share status flags, of which O_DIRECT is one.

  the incoming side doesn't set O_DIRECT, so it can dup() fds and
  therefore can receive only one in the fdset.

- the open() access mode flags used for the fds passed into QEMU need
  to match the flags QEMU uses to open the file. Currently O_WRONLY
  for src and O_RDONLY for dst.

O_DIRECT is not supported on all systems/filesystems, so run the fdset
test without O_DIRECT if that's the case. The migration code should
still work in that scenario.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 13:30:16 -03:00
Fabiano Rosas
4b6eeb2335 migration: Add support for fdset with multifd + file
Allow multifd to use an fdset when migrating to a file. This is useful
for the scenario where the management layer wants to have control over
the migration file.

By receiving the file descriptors directly, QEMU can delegate some
high level operating system operations to the management layer (such
as mandatory access control).

The management layer might also want to add its own headers before the
migration stream.

Enable the "file:/dev/fdset/#" syntax for the multifd migration with
fixed-ram. The fdset should contain two fds on the source side of
migration and 1 fd on the destination side. The two fds should not be
duplicates between themselves.

Multifd enables O_DIRECT on the source side using one of the fds and
keeps the other without the flag. None of the fds should have the
O_DIRECT flag already set.

The fdset mechanism also requires that the open() access mode flags be
the same as what QEMU uses internally: WRONLY for the source fds and
RDONLY for the destination fds.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 13:30:16 -03:00
Fabiano Rosas
2cb56cb34e monitor: fdset: Match against O_DIRECT
We're about to enable the use of O_DIRECT in the migration code and
due to the alignment restrictions imposed by filesystems we need to
make sure the flag is only used when doing aligned IO.

The migration will do parallel IO to different regions of a file, so
we need to use more than one file descriptor. Those cannot be obtained
by duplicating (dup()) since duplicated file descriptors share the
file status flags, including O_DIRECT. If one migration channel does
unaligned IO while another sets O_DIRECT to do aligned IO, the
filesystem would fail the unaligned operation.

The add-fd QMP command along with the fdset code are specifically
designed to allow the user to pass a set of file descriptors with
different access flags into QEMU to be later fetched by code that
needs to alternate between those flags when doing IO.

Extend the fdset matching function to behave the same with the
O_DIRECT flag.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 13:30:16 -03:00
Fabiano Rosas
5deb383d1a monitor: Extract fdset fd flags comparison into a function
We're about to add one more condition to the flags comparison that
requires an ifdef. Move the code into a separate function now to make
it cleaner after the next patch.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 13:30:16 -03:00
Fabiano Rosas
4478aef543 monitor: Honor QMP request for fd removal immediately
We're currently only removing an fd from the fdset if the VM is
running. This causes a QMP call to "remove-fd" to not actually remove
the fd if the VM happens to be stopped.

While the fd would eventually be removed when monitor_fdset_cleanup()
is called again, the user request should be honored and the fd
actually removed. Calling remove-fd + query-fdset shows a recently
removed fd still present.

The runstate_is_running() check was introduced by commit ebe52b592d
("monitor: Prevent removing fd from set during init"), which by the
shortlog indicates that they were trying to avoid removing an
yet-unduplicated fd too early.

I don't see why an fd explicitly removed with qmp_remove_fd() should
be under runstate_is_running(). I'm assuming this was a mistake when
adding the parenthesis around the expression.

Move the runstate_is_running() check to apply only to the
QLIST_EMPTY(dup_fds) side of the expression and ignore it when
mon_fdset_fd->removed has been explicitly set.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 13:30:15 -03:00
Fabiano Rosas
a8adda79e7 tests/qtest: Add a test for migration with direct-io and multifd
The test is only allowed to run in systems that know and in
filesystems which support O_DIRECT.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 13:30:15 -03:00
Fabiano Rosas
cf07faa04a migration: Add direct-io parameter
Add the direct-io migration parameter that tells the migration code to
use O_DIRECT when opening the migration stream file whenever possible.

This is currently only used for the secondary channels of fixed-ram
migration, which can guarantee that writes are page aligned.

However the parameter could be made to affect other types of
file-based migrations in the future.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 13:30:15 -03:00
Fabiano Rosas
b0839f1600 tests/qtest: Add a multifd + fixed-ram migration test
Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 13:30:15 -03:00
Fabiano Rosas
db401f5302 migration/multifd: Support incoming fixed-ram stream format
For the incoming fixed-ram migration we need to read the ramblock
headers, get the pages bitmap and send the host address of each
non-zero page to the multifd channel thread for writing.

To read from the migration file we need a preadv function that can
read into the iovs in segments of contiguous pages because (as in the
writing case) the file offset applies to the entire iovec.

Usage on HMP is:

(qemu) migrate_set_capability multifd on
(qemu) migrate_set_capability fixed-ram on
(qemu) migrate_set_parameter max-bandwidth 0
(qemu) migrate_set_parameter multifd-channels 8
(qemu) migrate_incoming file:migfile
(qemu) info status
(qemu) c

Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 13:30:15 -03:00
Fabiano Rosas
ed95cd0446 migration/multifd: Support outgoing fixed-ram stream format
The new fixed-ram stream format uses a file transport and puts ram
pages in the migration file at their respective offsets and can be
done in parallel by using the pwritev system call which takes iovecs
and an offset.

Add support to enabling the new format along with multifd to make use
of the threading and page handling already in place.

This requires multifd to stop sending headers and leaving the stream
format to the fixed-ram code. When it comes time to write the data, we
need to call a version of qio_channel_write that can take an offset.

Usage on HMP is:

(qemu) stop
(qemu) migrate_set_capability multifd on
(qemu) migrate_set_capability fixed-ram on
(qemu) migrate_set_parameter max-bandwidth 0
(qemu) migrate_set_parameter multifd-channels 8
(qemu) migrate file:migfile

Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 13:30:15 -03:00
Fabiano Rosas
d5bce67e17 migration/ram: Ignore multifd flush when doing fixed-ram migration
Some functionalities of multifd are incompatible with the 'fixed-ram'
migration format.

The MULTIFD_FLUSH flag in particular is not used because in fixed-ram
there is no sinchronicity between migration source and destination so
there is not need for a sync packet. In fact, fixed-ram disables
packets in multifd as a whole.

Make sure RAM_SAVE_FLAG_MULTIFD_FLUSH is never emitted when fixed-ram
is enabled.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 13:30:14 -03:00
Fabiano Rosas
b00e0415ed migration/ram: Add a wrapper for fixed-ram shadow bitmap
We'll need to set the shadow_bmap bits from outside ram.c soon and
TARGET_PAGE_BITS is poisoned, so add a wrapper to it.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 13:30:14 -03:00
Fabiano Rosas
6822813ae9 migration/multifd: Allow receiving pages without packets
Currently multifd does not need to have knowledge of pages on the
receiving side because all the information needed is within the
packets that come in the stream.

We're about to add support to fixed-ram migration, which cannot use
packets because it expects the ramblock section in the migration file
to contain only the guest pages data.

Add a data structure to transfer pages between the ram migration code
and the multifd receiving threads.

We don't want to reuse MultiFDPages_t for two reasons:

a) multifd threads don't really need to know about the data they're
   receiving.

b) the receiving side has to be stopped to load the pages, which means
   we can experiment with larger granularities than page size when
   transferring data.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 13:30:14 -03:00
Fabiano Rosas
23e7e3fc41 migration/multifd: Decouple recv method from pages
Next patch will abstract the type of data being received by the
channels, so do some cleanup now to remove references to pages and
dependency on 'normal_num'.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 13:30:14 -03:00
Fabiano Rosas
a071d2f34e multifd: Rename MultiFDSendParams::data to compress_data
Use a more specific name for the compression data so we can use the
generic for the multifd core code.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 13:30:14 -03:00
Fabiano Rosas
ab194ba308 io: Add a pwritev/preadv version that takes a discontiguous iovec
For the upcoming support to fixed-ram migration with multifd, we need
to be able to accept an iovec array with non-contiguous data.

Add a pwritev and preadv version that splits the array into contiguous
segments before writing. With that we can have the ram code continue
to add pages in any order and the multifd code continue to send large
arrays for reading and writing.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
Since iovs can be non contiguous, we'd need a separate array on the
side to carry an extra file offset for each of them, so I'm relying on
the fact that iovs are all within a same host page and passing in an
encoded offset that takes the host page into account.
2023-11-14 13:30:14 -03:00
Fabiano Rosas
9954a41782 migration/multifd: Add incoming QIOChannelFile support
On the receiving side we don't need to differentiate between main
channel and threads, so whichever channel is defined first gets to be
the main one. And since there are no packets, use the atomic channel
count to index into the params array.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 13:30:13 -03:00
Fabiano Rosas
76798336e4 migration/multifd: Add outgoing QIOChannelFile support
Allow multifd to open file-backed channels. This will be used when
enabling the fixed-ram migration stream format which expects a
seekable transport.

The QIOChannel read and write methods will use the preadv/pwritev
versions which don't update the file offset at each call so we can
reuse the fd without re-opening for every channel.

Note that this is just setup code and multifd cannot yet make use of
the file channels.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 13:30:13 -03:00
Fabiano Rosas
ffd3e56398 migration/multifd: Allow multifd without packets
For the upcoming support to the new 'fixed-ram' migration stream
format, we cannot use multifd packets because each write into the
ramblock section in the migration file is expected to contain only the
guest pages. They are written at their respective offsets relative to
the ramblock section header.

There is no space for the packet information and the expected gains
from the new approach come partly from being able to write the pages
sequentially without extraneous data in between.

The new format also doesn't need the packets and all necessary
information can be taken from the standard migration headers with some
(future) changes to multifd code.

Use the presence of the fixed-ram capability to decide whether to send
packets. For now this has no effect as fixed-ram cannot yet be enabled
with multifd.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 13:30:13 -03:00
Nikolay Borisov
1c036fa56a tests/qtest: migration-test: Add tests for fixed-ram file-based migration
Add basic tests for 'fixed-ram' migration.

Signed-off-by: Nikolay Borisov <nborisov@suse.com>
Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 09:19:24 -03:00
Nikolay Borisov
3eb7f2ab75 migration/ram: Add support for 'fixed-ram' migration restore
Add the necessary code to parse the format changes for the 'fixed-ram'
capability.

One of the more notable changes in behavior is that in the 'fixed-ram'
case ram pages are restored in one go rather than constantly looping
through the migration stream.

Signed-off-by: Nikolay Borisov <nborisov@suse.com>
Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 09:19:24 -03:00
Nikolay Borisov
43ad5422c9 migration/ram: Add support for 'fixed-ram' outgoing migration
Implement the outgoing migration side for the 'fixed-ram' capability.

A bitmap is introduced to track which pages have been written in the
migration file. Pages are written at a fixed location for every
ramblock. Zero pages are ignored as they'd be zero in the destination
migration as well.

The migration stream is altered to put the dirty pages for a ramblock
after its header instead of having a sequential stream of pages that
follow the ramblock headers. Since all pages have a fixed location,
RAM_SAVE_FLAG_EOS is no longer generated on every migration iteration.

Without fixed-ram (current):        With fixed-ram (new):

 ---------------------               --------------------------------
 | ramblock 1 header |               | ramblock 1 header            |
 ---------------------               --------------------------------
 | ramblock 2 header |               | ramblock 1 fixed-ram header  |
 ---------------------               --------------------------------
 | ...               |               | padding to next 1MB boundary |
 ---------------------               | ...                          |
 | ramblock n header |               --------------------------------
 ---------------------               | ramblock 1 pages             |
 | RAM_SAVE_FLAG_EOS |               | ...                          |
 ---------------------               --------------------------------
 | stream of pages   |               | ramblock 2 header            |
 | (iter 1)          |               --------------------------------
 | ...               |               | ramblock 2 fixed-ram header  |
 ---------------------               --------------------------------
 | RAM_SAVE_FLAG_EOS |               | padding to next 1MB boundary |
 ---------------------               | ...                          |
 | stream of pages   |               --------------------------------
 | (iter 2)          |               | ramblock 2 pages             |
 | ...               |               | ...                          |
 ---------------------               --------------------------------
 | ...               |               | ...                          |
 ---------------------               --------------------------------
                                     | RAM_SAVE_FLAG_EOS            |
                                     --------------------------------
                                     | ...                          |
                                     -------------------------------

where:
 - ramblock header: the generic information for a ramblock, such as
   idstr, used_len, etc.

 - ramblock fixed-ram header: the new information added by this
   feature: bitmap of pages written, bitmap size and offset of pages
   in the migration file.

Signed-off-by: Nikolay Borisov <nborisov@suse.com>
Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 09:19:23 -03:00
Fabiano Rosas
909f4a40f6 migration: Add fixed-ram URI compatibility check
The fixed-ram migration format needs a channel that supports seeking
to be able to write each page to an arbitrary offset in the migration
stream.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2023-11-14 09:19:23 -03:00
Fabiano Rosas
07acf019b1 migration/ram: Introduce 'fixed-ram' migration capability
Add a new migration capability 'fixed-ram'.

The core of the feature is to ensure that each RAM page has a specific
offset in the resulting migration stream. The reasons why we'd want
such behavior are:

 - The resulting file will have a bounded size, since pages which are
   dirtied multiple times will always go to a fixed location in the
   file, rather than constantly being added to a sequential
   stream. This eliminates cases where a VM with, say, 1G of RAM can
   result in a migration file that's 10s of GBs, provided that the
   workload constantly redirties memory.

 - It paves the way to implement O_DIRECT-enabled save/restore of the
   migration stream as the pages are ensured to be written at aligned
   offsets.

 - It allows the usage of multifd so we can write RAM pages to the
   migration file in parallel.

For now, enabling the capability has no effect. The next couple of
patches implement the core functionality.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 09:19:23 -03:00
Fabiano Rosas
b018f49a5b migration/ram: Initialize bitmap with used_length
We don't allow changing the size of the ramblock during migration. Use
used_length instead of max_length when initializing the bitmap.

Suggested-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 09:19:23 -03:00
Nikolay Borisov
f9c6197b58 migration/qemu-file: add utility methods for working with seekable channels
Add utility methods that will be needed when implementing 'fixed-ram'
migration capability.

qemu_file_is_seekable
qemu_put_buffer_at
qemu_get_buffer_at
qemu_set_offset
qemu_get_offset

Signed-off-by: Nikolay Borisov <nborisov@suse.com>
Signed-off-by: Fabiano Rosas <farosas@suse.de>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2023-11-14 09:19:23 -03:00
Nikolay Borisov
1b1da54c69 io: implement io_pwritev/preadv for QIOChannelFile
The upcoming 'fixed-ram' feature will require qemu to write data to
(and restore from) specific offsets of the migration file.

Add a minimal implementation of pwritev/preadv and expose them via the
io_pwritev and io_preadv interfaces.

Signed-off-by: Nikolay Borisov <nborisov@suse.com>
Signed-off-by: Fabiano Rosas <farosas@suse.de>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2023-11-14 09:19:23 -03:00
Nikolay Borisov
d636220e69 io: Add generic pwritev/preadv interface
Introduce basic pwritev/preadv support in the generic channel layer.
Specific implementation will follow for the file channel as this is
required in order to support migration streams with fixed location of
each ram page.

Signed-off-by: Nikolay Borisov <nborisov@suse.com>
Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 09:19:23 -03:00
Nikolay Borisov
5e25355c4b io: add and implement QIO_CHANNEL_FEATURE_SEEKABLE for channel file
Add a generic QIOChannel feature SEEKABLE which would be used by the
qemu_file* apis. For the time being this will be only implemented for
file channels.

Signed-off-by: Nikolay Borisov <nborisov@suse.com>
Signed-off-by: Fabiano Rosas <farosas@suse.de>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2023-11-14 09:19:22 -03:00
Fabiano Rosas
f978a45734 tests/qtest: Re-enable multifd cancel test
We've found the source of flakiness in this test, so re-enable it.

Reviewed-by: Juan Quintela <quintela@redhat.com>
Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 09:19:22 -03:00
Fabiano Rosas
d969e2d0ff migration: Report error in incoming migration
We're not currently reporting the errors set with migrate_set_error()
when incoming migration fails.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-14 09:19:22 -03:00
Fabiano Rosas
87dcefce00 migration/multifd: Allow QIOTask error reporting without an object
The only way for the channel backend to report an error to the multifd
core during creation is by setting the QIOTask error. We must allow
the channel backend to set the error even if the QIOChannel has failed
to be created, which means the QIOTask source object would be NULL.

At multifd_new_send_channel_async() move the QOM casting of the
channel until after we have checked for the QIOTask error.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
context: When doing multifd + file, it's possible that we fail to open
the file. I'll use the empty QIOTask to report the error back to
multifd.
2023-11-14 09:19:22 -03:00
Fabiano Rosas
2b12bbcfed migration/multifd: Stop setting p->ioc before connecting
This is being shadowed but the assignments at
multifd_channel_connect() and multifd_tls_channel_connect() .

Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-13 10:59:23 -03:00
Fabiano Rosas
fd92544b1a migration/multifd: Fix multifd_pages_init argument
The 'size' argument is the number of pages that fit in a multifd
packet. Change it to uint32_t and rename.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-13 10:54:11 -03:00
Fabiano Rosas
19b0f579aa migration/multifd: Remove QEMUFile from where it is not needed
Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-13 10:54:11 -03:00
Fabiano Rosas
ae1ea5b13e migration/multifd: Remove MultiFDPages_t::packet_num
This was introduced by commit 34c55a94b1 ("migration: Create multipage
support") and never used.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-13 10:54:11 -03:00
Fabiano Rosas
f6a85fa7a4 tests/qtest/migration: Print migration incoming errors
We're currently just asserting when incoming migration fails. Let's
print the error message from QMP as well.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
2023-11-13 10:54:10 -03:00
Stefan Hajnoczi
69680740ea Merge tag 'qdev-array-prop' of https://repo.or.cz/qemu/kevin into staging
qdev: Make array properties user accessible again

# -----BEGIN PGP SIGNATURE-----
#
# iQJFBAABCAAvFiEE3D3rFZqa+V09dFb+fwmycsiPL9YFAmVOZicRHGt3b2xmQHJl
# ZGhhdC5jb20ACgkQfwmycsiPL9arpw/+NKGRhSMrSq9Az+z5+ANUfw5SNLJYf1hH
# jm5ITA1Gr9htqHtBfEOdkms2wef6m7onF72rHVUlBKdqCPNMGLme5B0oQ8PZ1X1t
# OxAZ8KYwlO98QvOYl617SA/8wxc0U4/zi192kJpbRkKF6KdbbMGtLKjHyEitA/Yv
# izx1vkKOgQyMFGF1JgIyG4R3WmsKQW1XLqb3emVNRzCqmJpkvMJZQG8tnyEAXlIS
# gkY69cTpaKVaM1OxdB45gjlKTGzLWC/3tTGH+u8q356fvgm/QIgrokCirCZFPIl0
# C8hvzPm/L8hkvWtUb3EZx0DLiunWcAGvoLgBNODHojKRtQ6X9TRTrjJ41ZCLXVqv
# tVJm+XGKC0CZ/WW5yqVOmnzfPH4z8ubzSoRv5ryz3xDb5B/Zr10+ScE+/Ee24wJ2
# HIehxc1LgVGGpikP88/Ns/nAlIVUQxxYvSJ23R5D1+UpP6FCy6Y1pKyRtZGzPCIe
# N4Y+52GtelBR8gOjay5INn/Yf8Fh6sFxX556BW0XKYcbQgvl2bxASe/KVnAVZ1NB
# 8DsaAWlK+hPGopwyp2lDRuGd4kusNbzQvIUZ0mr1g9HQ/iSnT/9RFdExsj+K6QTr
# pX42QCe4mWHPAKx38cez+Bhx4TEOw+GmHuTp/oLdBRuY8DPu/I0Ny364uiW+At/R
# 8jF+jt5uVZc=
# =MV6O
# -----END PGP SIGNATURE-----
# gpg: Signature made Sat 11 Nov 2023 01:19:35 HKT
# gpg:                using RSA key DC3DEB159A9AF95D3D7456FE7F09B272C88F2FD6
# gpg:                issuer "kwolf@redhat.com"
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" [full]
# Primary key fingerprint: DC3D EB15 9A9A F95D 3D74  56FE 7F09 B272 C88F 2FD6

* tag 'qdev-array-prop' of https://repo.or.cz/qemu/kevin:
  qdev: Rework array properties based on list visitor
  qdev: Make netdev properties work as list elements
  qom: Add object_property_set_default_list()
  hw/rx/rx62n: Use qdev_prop_set_array()
  hw/arm/xlnx-versal: Use qdev_prop_set_array()
  hw/arm/virt: Use qdev_prop_set_array()
  hw/arm/vexpress: Use qdev_prop_set_array()
  hw/arm/sbsa-ref: Use qdev_prop_set_array()
  hw/arm/mps2: Use qdev_prop_set_array()
  hw/arm/mps2-tz: Use qdev_prop_set_array()
  hw/i386/pc: Use qdev_prop_set_array()

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2023-11-11 11:23:25 +08:00
29 changed files with 1802 additions and 267 deletions

View File

@@ -572,6 +572,27 @@ Others (especially either older devices or system devices which for
some reason don't have a bus concept) make use of the ``instance id``
for otherwise identically named devices.
Fixed-ram format
----------------
When the ``fixed-ram`` capability is enabled, a slightly different
stream format is used for the RAM section. Instead of having a
sequential stream of pages that follow the RAMBlock headers, the dirty
pages for a RAMBlock follow its header. This ensures that each RAM
page has a fixed offset in the resulting migration file.
The ``fixed-ram`` capability must be enabled in both source and
destination with:
``migrate_set_capability fixed-ram on``
Since pages are written to their relatives offsets and out of order
(due to the memory dirtying patterns), streaming channels such as
sockets are not supported. A seekable channel such as a file is
required. This can be verified in the QIOChannel by the presence of
the QIO_CHANNEL_FEATURE_SEEKABLE. In more practical terms, this
migration format requires the ``file:`` URI when migrating.
Return path
-----------

View File

@@ -44,6 +44,14 @@ struct RAMBlock {
size_t page_size;
/* dirty bitmap used during migration */
unsigned long *bmap;
/* shadow dirty bitmap used when migrating to a file */
unsigned long *shadow_bmap;
/*
* offset in the file pages belonging to this ramblock are saved,
* used only during migration to a file.
*/
off_t bitmap_offset;
uint64_t pages_offset;
/* bitmap of already received pages in postcopy */
unsigned long *receivedmap;

View File

@@ -33,8 +33,10 @@ OBJECT_DECLARE_TYPE(QIOChannel, QIOChannelClass,
#define QIO_CHANNEL_ERR_BLOCK -2
#define QIO_CHANNEL_WRITE_FLAG_ZERO_COPY 0x1
#define QIO_CHANNEL_WRITE_FLAG_WITH_OFFSET 0x2
#define QIO_CHANNEL_READ_FLAG_MSG_PEEK 0x1
#define QIO_CHANNEL_READ_FLAG_WITH_OFFSET 0x2
typedef enum QIOChannelFeature QIOChannelFeature;
@@ -44,6 +46,7 @@ enum QIOChannelFeature {
QIO_CHANNEL_FEATURE_LISTEN,
QIO_CHANNEL_FEATURE_WRITE_ZERO_COPY,
QIO_CHANNEL_FEATURE_READ_MSG_PEEK,
QIO_CHANNEL_FEATURE_SEEKABLE,
};
@@ -130,6 +133,16 @@ struct QIOChannelClass {
Error **errp);
/* Optional callbacks */
ssize_t (*io_pwritev)(QIOChannel *ioc,
const struct iovec *iov,
size_t niov,
off_t offset,
Error **errp);
ssize_t (*io_preadv)(QIOChannel *ioc,
const struct iovec *iov,
size_t niov,
off_t offset,
Error **errp);
int (*io_shutdown)(QIOChannel *ioc,
QIOChannelShutdown how,
Error **errp);
@@ -528,6 +541,104 @@ void qio_channel_set_follow_coroutine_ctx(QIOChannel *ioc, bool enabled);
int qio_channel_close(QIOChannel *ioc,
Error **errp);
/**
* qio_channel_pwritev
* @ioc: the channel object
* @iov: the array of memory regions to write data from
* @niov: the length of the @iov array
* @offset: offset in the channel where writes should begin
* @errp: pointer to a NULL-initialized error object
*
* Not all implementations will support this facility, so may report
* an error. To avoid errors, the caller may check for the feature
* flag QIO_CHANNEL_FEATURE_SEEKABLE prior to calling this method.
*
* Behaves as qio_channel_writev_full, apart from not supporting
* sending of file handles as well as beginning the write at the
* passed @offset
*
*/
ssize_t qio_channel_pwritev(QIOChannel *ioc, const struct iovec *iov,
size_t niov, off_t offset, Error **errp);
/**
* qio_channel_pwritev_all:
* @ioc: the channel object
* @iov: the array of memory regions to write data from
* @niov: the length of the @iov array
* @offset: the iovec offset in the file where to write the data
* @errp: pointer to a NULL-initialized error object
*
* Returns: 0 if all bytes were written, or -1 on error
*/
int qio_channel_pwritev_all(QIOChannel *ioc, const struct iovec *iov,
size_t niov, off_t offset, Error **errp);
/**
* qio_channel_pwrite
* @ioc: the channel object
* @buf: the memory region to write data into
* @buflen: the number of bytes to @buf
* @offset: offset in the channel where writes should begin
* @errp: pointer to a NULL-initialized error object
*
* Not all implementations will support this facility, so may report
* an error. To avoid errors, the caller may check for the feature
* flag QIO_CHANNEL_FEATURE_SEEKABLE prior to calling this method.
*
*/
ssize_t qio_channel_pwrite(QIOChannel *ioc, char *buf, size_t buflen,
off_t offset, Error **errp);
/**
* qio_channel_preadv
* @ioc: the channel object
* @iov: the array of memory regions to read data into
* @niov: the length of the @iov array
* @offset: offset in the channel where writes should begin
* @errp: pointer to a NULL-initialized error object
*
* Not all implementations will support this facility, so may report
* an error. To avoid errors, the caller may check for the feature
* flag QIO_CHANNEL_FEATURE_SEEKABLE prior to calling this method.
*
* Behaves as qio_channel_readv_full, apart from not supporting
* receiving of file handles as well as beginning the read at the
* passed @offset
*
*/
ssize_t qio_channel_preadv(QIOChannel *ioc, const struct iovec *iov,
size_t niov, off_t offset, Error **errp);
/**
* qio_channel_preadv_all:
* @ioc: the channel object
* @iov: the array of memory regions to read data to
* @niov: the length of the @iov array
* @offset: the iovec offset in the file from where to read the data
* @errp: pointer to a NULL-initialized error object
*
* Returns: 0 if all bytes were read, or -1 on error
*/
int qio_channel_preadv_all(QIOChannel *ioc, const struct iovec *iov,
size_t niov, off_t offset, Error **errp);
/**
* qio_channel_pread
* @ioc: the channel object
* @buf: the memory region to write data into
* @buflen: the number of bytes to @buf
* @offset: offset in the channel where writes should begin
* @errp: pointer to a NULL-initialized error object
*
* Not all implementations will support this facility, so may report
* an error. To avoid errors, the caller may check for the feature
* flag QIO_CHANNEL_FEATURE_SEEKABLE prior to calling this method.
*
*/
ssize_t qio_channel_pread(QIOChannel *ioc, char *buf, size_t buflen,
off_t offset, Error **errp);
/**
* qio_channel_shutdown:
* @ioc: the channel object

View File

@@ -50,6 +50,8 @@ unsigned int qemu_get_be16(QEMUFile *f);
unsigned int qemu_get_be32(QEMUFile *f);
uint64_t qemu_get_be64(QEMUFile *f);
bool qemu_file_is_seekable(QEMUFile *f);
static inline void qemu_put_be64s(QEMUFile *f, const uint64_t *pv)
{
qemu_put_be64(f, *pv);

View File

@@ -67,6 +67,19 @@ static inline void clear_bit(long nr, unsigned long *addr)
*p &= ~mask;
}
/**
* clear_bit_atomic - Clears a bit in memory atomically
* @nr: Bit to clear
* @addr: Address to start counting from
*/
static inline void clear_bit_atomic(long nr, unsigned long *addr)
{
unsigned long mask = BIT_MASK(nr);
unsigned long *p = addr + BIT_WORD(nr);
return qatomic_and(p, ~mask);
}
/**
* change_bit - Toggle a bit in memory
* @nr: Bit to change

View File

@@ -597,6 +597,8 @@ int qemu_lock_fd_test(int fd, int64_t start, int64_t len, bool exclusive);
bool qemu_has_ofd_lock(void);
#endif
bool qemu_has_direct_io(void);
#if defined(__HAIKU__) && defined(__i386__)
#define FMT_pid "%ld"
#elif defined(WIN64)

View File

@@ -36,6 +36,10 @@ qio_channel_file_new_fd(int fd)
ioc->fd = fd;
if (lseek(fd, 0, SEEK_CUR) != (off_t)-1) {
qio_channel_set_feature(QIO_CHANNEL(ioc), QIO_CHANNEL_FEATURE_SEEKABLE);
}
trace_qio_channel_file_new_fd(ioc, fd);
return ioc;
@@ -60,6 +64,10 @@ qio_channel_file_new_path(const char *path,
return NULL;
}
if (lseek(ioc->fd, 0, SEEK_CUR) != (off_t)-1) {
qio_channel_set_feature(QIO_CHANNEL(ioc), QIO_CHANNEL_FEATURE_SEEKABLE);
}
trace_qio_channel_file_new_path(ioc, path, flags, mode, ioc->fd);
return ioc;
@@ -138,6 +146,56 @@ static ssize_t qio_channel_file_writev(QIOChannel *ioc,
return ret;
}
static ssize_t qio_channel_file_preadv(QIOChannel *ioc,
const struct iovec *iov,
size_t niov,
off_t offset,
Error **errp)
{
QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
ssize_t ret;
retry:
ret = preadv(fioc->fd, iov, niov, offset);
if (ret < 0) {
if (errno == EAGAIN) {
return QIO_CHANNEL_ERR_BLOCK;
}
if (errno == EINTR) {
goto retry;
}
error_setg_errno(errp, errno, "Unable to read from file");
return -1;
}
return ret;
}
static ssize_t qio_channel_file_pwritev(QIOChannel *ioc,
const struct iovec *iov,
size_t niov,
off_t offset,
Error **errp)
{
QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
ssize_t ret;
retry:
ret = pwritev(fioc->fd, iov, niov, offset);
if (ret <= 0) {
if (errno == EAGAIN) {
return QIO_CHANNEL_ERR_BLOCK;
}
if (errno == EINTR) {
goto retry;
}
error_setg_errno(errp, errno, "Unable to write to file");
return -1;
}
return ret;
}
static int qio_channel_file_set_blocking(QIOChannel *ioc,
bool enabled,
Error **errp)
@@ -223,6 +281,8 @@ static void qio_channel_file_class_init(ObjectClass *klass,
ioc_klass->io_writev = qio_channel_file_writev;
ioc_klass->io_readv = qio_channel_file_readv;
ioc_klass->io_set_blocking = qio_channel_file_set_blocking;
ioc_klass->io_pwritev = qio_channel_file_pwritev;
ioc_klass->io_preadv = qio_channel_file_preadv;
ioc_klass->io_seek = qio_channel_file_seek;
ioc_klass->io_close = qio_channel_file_close;
ioc_klass->io_create_watch = qio_channel_file_create_watch;

View File

@@ -454,6 +454,134 @@ GSource *qio_channel_add_watch_source(QIOChannel *ioc,
}
ssize_t qio_channel_pwritev(QIOChannel *ioc, const struct iovec *iov,
size_t niov, off_t offset, Error **errp)
{
QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
if (!klass->io_pwritev) {
error_setg(errp, "Channel does not support pwritev");
return -1;
}
if (!qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_SEEKABLE)) {
error_setg_errno(errp, EINVAL, "Requested channel is not seekable");
return -1;
}
return klass->io_pwritev(ioc, iov, niov, offset, errp);
}
static int qio_channel_preadv_pwritev_contiguous(QIOChannel *ioc,
const struct iovec *iov,
size_t niov, off_t offset,
bool is_write, Error **errp)
{
ssize_t ret;
int i, slice_idx, slice_num;
uint64_t base, next, file_offset;
size_t len;
slice_idx = 0;
slice_num = 1;
/*
* If the iov array doesn't have contiguous elements, we need to
* split it in slices because we only have one (file) 'offset' for
* the whole iov. Do this here so callers don't need to break the
* iov array themselves.
*/
for (i = 0; i < niov; i++, slice_num++) {
base = (uint64_t) iov[i].iov_base;
if (i != niov - 1) {
len = iov[i].iov_len;
next = (uint64_t) iov[i + 1].iov_base;
if (base + len == next) {
continue;
}
}
/*
* Use the offset of the first element of the segment that
* we're sending.
*/
file_offset = offset + (uint64_t) iov[slice_idx].iov_base;
if (is_write) {
ret = qio_channel_pwritev(ioc, &iov[slice_idx], slice_num,
file_offset, errp);
} else {
ret = qio_channel_preadv(ioc, &iov[slice_idx], slice_num,
file_offset, errp);
}
if (ret < 0) {
break;
}
slice_idx += slice_num;
slice_num = 0;
}
return (ret < 0) ? -1 : 0;
}
int qio_channel_pwritev_all(QIOChannel *ioc, const struct iovec *iov,
size_t niov, off_t offset, Error **errp)
{
return qio_channel_preadv_pwritev_contiguous(ioc, iov, niov,
offset, true, errp);
}
ssize_t qio_channel_pwrite(QIOChannel *ioc, char *buf, size_t buflen,
off_t offset, Error **errp)
{
struct iovec iov = {
.iov_base = buf,
.iov_len = buflen
};
return qio_channel_pwritev(ioc, &iov, 1, offset, errp);
}
ssize_t qio_channel_preadv(QIOChannel *ioc, const struct iovec *iov,
size_t niov, off_t offset, Error **errp)
{
QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
if (!klass->io_preadv) {
error_setg(errp, "Channel does not support preadv");
return -1;
}
if (!qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_SEEKABLE)) {
error_setg_errno(errp, EINVAL, "Requested channel is not seekable");
return -1;
}
return klass->io_preadv(ioc, iov, niov, offset, errp);
}
int qio_channel_preadv_all(QIOChannel *ioc, const struct iovec *iov,
size_t niov, off_t offset, Error **errp)
{
return qio_channel_preadv_pwritev_contiguous(ioc, iov, niov,
offset, false, errp);
}
ssize_t qio_channel_pread(QIOChannel *ioc, char *buf, size_t buflen,
off_t offset, Error **errp)
{
struct iovec iov = {
.iov_base = buf,
.iov_len = buflen
};
return qio_channel_preadv(ioc, &iov, 1, offset, errp);
}
int qio_channel_shutdown(QIOChannel *ioc,
QIOChannelShutdown how,
Error **errp)

View File

@@ -6,17 +6,28 @@
*/
#include "qemu/osdep.h"
#include "qemu/cutils.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-misc.h"
#include "qemu/cutils.h"
#include "qemu/error-report.h"
#include "channel.h"
#include "file.h"
#include "migration.h"
#include "io/channel-file.h"
#include "io/channel-util.h"
#include "monitor/monitor.h"
#include "options.h"
#include "trace.h"
#define OFFSET_OPTION ",offset="
static struct FileOutgoingArgs {
char *fname;
int flags;
int mode;
int64_t fdset_id;
} outgoing_args;
/* Remove the offset option from @filespec and return it in @offsetp. */
int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp)
@@ -36,6 +47,139 @@ int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp)
return 0;
}
/*
* If the open flags and file status flags from the file descriptors
* in the fdset don't match what QEMU expects, errno gets set to
* EACCES. Let's provide a more user-friendly message.
*/
static void file_fdset_error(int flags, Error **errp)
{
ERRP_GUARD();
if (errno == EACCES) {
/* ditch the previous error */
error_free(*errp);
*errp = NULL;
error_setg(errp, "Fdset is missing a file descriptor with flags: %#x\n",
flags);
}
}
static void file_remove_fdset(void)
{
if (outgoing_args.fdset_id != -1) {
qmp_remove_fd(outgoing_args.fdset_id, false, -1, NULL);
outgoing_args.fdset_id = -1;
}
}
/*
* Due to the behavior of the dup() system call, we need the fdset to
* have two non-duplicate fds so we can enable direct IO in the
* secondary channels without affecting the main channel.
*/
static bool file_parse_fdset(const char *filename, int64_t *fdset_id,
Error **errp)
{
FdsetInfoList *fds_info;
FdsetFdInfoList *fd_info;
const char *fdset_id_str;
int nfds = 0;
*fdset_id = -1;
if (!strstart(filename, "/dev/fdset/", &fdset_id_str)) {
return true;
}
if (!migrate_multifd()) {
error_setg(errp, "fdset is only supported with multifd");
return false;
}
*fdset_id = qemu_parse_fd(fdset_id_str);
for (fds_info = qmp_query_fdsets(NULL); fds_info;
fds_info = fds_info->next) {
if (*fdset_id != fds_info->value->fdset_id) {
continue;
}
for (fd_info = fds_info->value->fds; fd_info; fd_info = fd_info->next) {
if (nfds++ > 2) {
break;
}
}
}
if (nfds != 2) {
error_setg(errp, "Outgoing migration needs two fds in the fdset, "
"got %d", nfds);
qmp_remove_fd(*fdset_id, false, -1, NULL);
*fdset_id = -1;
return false;
}
return true;
}
static void qio_channel_file_connect_worker(QIOTask *task, gpointer opaque)
{
/* noop */
}
int file_send_channel_destroy(QIOChannel *ioc)
{
if (ioc) {
qio_channel_close(ioc, NULL);
object_unref(OBJECT(ioc));
}
g_free(outgoing_args.fname);
outgoing_args.fname = NULL;
file_remove_fdset();
return 0;
}
void file_send_channel_create(QIOTaskFunc f, void *data)
{
QIOChannelFile *ioc;
QIOTask *task;
Error *err = NULL;
int flags = outgoing_args.flags;
if (migrate_direct_io()) {
#ifdef O_DIRECT
/*
* Enable O_DIRECT for the secondary channels. These are used
* for sending ram pages and writes should be guaranteed to be
* aligned to at least page size.
*/
flags |= O_DIRECT;
#else
error_setg(&err, "System does not support O_DIRECT");
error_append_hint(&err,
"Try disabling direct-io migration capability\n");
#endif
}
if (!err) {
ioc = qio_channel_file_new_path(outgoing_args.fname, flags,
outgoing_args.mode, &err);
}
task = qio_task_new(OBJECT(ioc), f, (gpointer)data, NULL);
if (!ioc) {
file_fdset_error(flags, &err);
qio_task_set_error(task, err);
}
qio_task_run_in_thread(task, qio_channel_file_connect_worker,
(gpointer)data, NULL, NULL);
}
void file_start_outgoing_migration(MigrationState *s,
FileMigrationArgs *file_args, Error **errp)
{
@@ -43,12 +187,22 @@ void file_start_outgoing_migration(MigrationState *s,
g_autofree char *filename = g_strdup(file_args->filename);
uint64_t offset = file_args->offset;
QIOChannel *ioc;
int flags = O_CREAT | O_TRUNC | O_WRONLY;
mode_t mode = 0660;
trace_migration_file_outgoing(filename);
fioc = qio_channel_file_new_path(filename, O_CREAT | O_WRONLY | O_TRUNC,
0600, errp);
if (!file_parse_fdset(filename, &outgoing_args.fdset_id, errp)) {
return;
}
outgoing_args.fname = g_strdup(filename);
outgoing_args.flags = flags;
outgoing_args.mode = mode;
fioc = qio_channel_file_new_path(filename, flags, mode, errp);
if (!fioc) {
file_fdset_error(flags, errp);
return;
}
@@ -74,22 +228,40 @@ void file_start_incoming_migration(FileMigrationArgs *file_args, Error **errp)
g_autofree char *filename = g_strdup(file_args->filename);
QIOChannelFile *fioc = NULL;
uint64_t offset = file_args->offset;
QIOChannel *ioc;
int channels = 1;
int i = 0, fd, flags = O_RDONLY;
trace_migration_file_incoming(filename);
fioc = qio_channel_file_new_path(filename, O_RDONLY, 0, errp);
fioc = qio_channel_file_new_path(filename, flags, 0, errp);
if (!fioc) {
file_fdset_error(flags, errp);
return;
}
ioc = QIO_CHANNEL(fioc);
if (offset && qio_channel_io_seek(ioc, offset, SEEK_SET, errp) < 0) {
if (offset &&
qio_channel_io_seek(QIO_CHANNEL(fioc), offset, SEEK_SET, errp) < 0) {
return;
}
qio_channel_set_name(QIO_CHANNEL(ioc), "migration-file-incoming");
if (migrate_multifd()) {
channels += migrate_multifd_channels();
}
fd = fioc->fd;
do {
QIOChannel *ioc = QIO_CHANNEL(fioc);
qio_channel_set_name(ioc, "migration-file-incoming");
qio_channel_add_watch_full(ioc, G_IO_IN,
file_accept_incoming_migration,
NULL, NULL,
g_main_context_get_thread_default());
} while (++i < channels && (fioc = qio_channel_file_new_fd(fd)));
if (!fioc) {
error_setg(errp, "Error creating migration incoming channel");
return;
}
}

View File

@@ -9,10 +9,15 @@
#define QEMU_MIGRATION_FILE_H
#include "qapi/qapi-types-migration.h"
#include "io/task.h"
#include "channel.h"
void file_start_incoming_migration(FileMigrationArgs *file_args, Error **errp);
void file_start_outgoing_migration(MigrationState *s,
FileMigrationArgs *file_args, Error **errp);
int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp);
void file_send_channel_create(QIOTaskFunc f, void *data);
int file_send_channel_destroy(QIOChannel *ioc);
#endif

View File

@@ -392,6 +392,12 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
monitor_printf(mon, "%s: %s\n",
MigrationParameter_str(MIGRATION_PARAMETER_MODE),
qapi_enum_lookup(&MigMode_lookup, params->mode));
if (params->has_direct_io) {
monitor_printf(mon, "%s: %s\n",
MigrationParameter_str(MIGRATION_PARAMETER_DIRECT_IO),
params->direct_io ? "on" : "off");
}
}
qapi_free_MigrationParameters(params);
@@ -679,6 +685,10 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
p->has_mode = true;
visit_type_MigMode(v, param, &p->mode, &err);
break;
case MIGRATION_PARAMETER_DIRECT_IO:
p->has_direct_io = true;
visit_type_bool(v, param, &p->direct_io, &err);
break;
default:
assert(0);
}

View File

@@ -128,20 +128,43 @@ static bool migration_needs_multiple_sockets(void)
return migrate_multifd() || migrate_postcopy_preempt();
}
static bool transport_supports_multi_channels(SocketAddress *saddr)
static bool transport_supports_multi_channels(MigrationAddress *addr)
{
return saddr->type == SOCKET_ADDRESS_TYPE_INET ||
if (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET) {
SocketAddress *saddr = &addr->u.socket;
return (saddr->type == SOCKET_ADDRESS_TYPE_INET ||
saddr->type == SOCKET_ADDRESS_TYPE_UNIX ||
saddr->type == SOCKET_ADDRESS_TYPE_VSOCK;
saddr->type == SOCKET_ADDRESS_TYPE_VSOCK);
} else if (addr->transport == MIGRATION_ADDRESS_TYPE_FILE) {
return migrate_fixed_ram();
} else {
return false;
}
}
static bool migration_needs_seekable_channel(void)
{
return migrate_fixed_ram();
}
static bool transport_supports_seeking(MigrationAddress *addr)
{
return addr->transport == MIGRATION_ADDRESS_TYPE_FILE;
}
static bool
migration_channels_and_transport_compatible(MigrationAddress *addr,
Error **errp)
{
if (migration_needs_seekable_channel() &&
!transport_supports_seeking(addr)) {
error_setg(errp, "Migration requires seekable transport (e.g. file)");
return false;
}
if (migration_needs_multiple_sockets() &&
(addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET) &&
!transport_supports_multi_channels(&addr->u.socket)) {
!transport_supports_multi_channels(addr)) {
error_setg(errp, "Migration requires multi-channel URIs (e.g. tcp)");
return false;
}
@@ -698,6 +721,13 @@ process_incoming_migration_co(void *opaque)
}
if (ret < 0) {
MigrationState *s = migrate_get_current();
if (migrate_has_error(s)) {
WITH_QEMU_LOCK_GUARD(&s->error_mutex){
error_report_err(s->error);
}
}
error_report("load of migration failed: %s", strerror(-ret));
goto fail;
}
@@ -843,6 +873,8 @@ void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp)
}
default_channel = (channel_magic == cpu_to_be32(QEMU_VM_FILE_MAGIC));
} else if (migrate_multifd() && migrate_fixed_ram()) {
default_channel = multifd_recv_first_channel();
} else {
default_channel = !mis->from_src_file;
}

View File

@@ -69,7 +69,7 @@ static int zlib_send_setup(MultiFDSendParams *p, Error **errp)
err_msg = "out of memory for buf";
goto err_free_zbuff;
}
p->data = z;
p->compress_data = z;
return 0;
err_free_zbuff:
@@ -92,15 +92,15 @@ err_free_z:
*/
static void zlib_send_cleanup(MultiFDSendParams *p, Error **errp)
{
struct zlib_data *z = p->data;
struct zlib_data *z = p->compress_data;
deflateEnd(&z->zs);
g_free(z->zbuff);
z->zbuff = NULL;
g_free(z->buf);
z->buf = NULL;
g_free(p->data);
p->data = NULL;
g_free(p->compress_data);
p->compress_data = NULL;
}
/**
@@ -116,7 +116,7 @@ static void zlib_send_cleanup(MultiFDSendParams *p, Error **errp)
*/
static int zlib_send_prepare(MultiFDSendParams *p, Error **errp)
{
struct zlib_data *z = p->data;
struct zlib_data *z = p->compress_data;
z_stream *zs = &z->zs;
uint32_t out_size = 0;
int ret;
@@ -189,7 +189,7 @@ static int zlib_recv_setup(MultiFDRecvParams *p, Error **errp)
struct zlib_data *z = g_new0(struct zlib_data, 1);
z_stream *zs = &z->zs;
p->data = z;
p->compress_data = z;
zs->zalloc = Z_NULL;
zs->zfree = Z_NULL;
zs->opaque = Z_NULL;
@@ -219,13 +219,13 @@ static int zlib_recv_setup(MultiFDRecvParams *p, Error **errp)
*/
static void zlib_recv_cleanup(MultiFDRecvParams *p)
{
struct zlib_data *z = p->data;
struct zlib_data *z = p->compress_data;
inflateEnd(&z->zs);
g_free(z->zbuff);
z->zbuff = NULL;
g_free(p->data);
p->data = NULL;
g_free(p->compress_data);
p->compress_data = NULL;
}
/**
@@ -241,7 +241,7 @@ static void zlib_recv_cleanup(MultiFDRecvParams *p)
*/
static int zlib_recv_pages(MultiFDRecvParams *p, Error **errp)
{
struct zlib_data *z = p->data;
struct zlib_data *z = p->compress_data;
z_stream *zs = &z->zs;
uint32_t in_size = p->next_packet_size;
/* we measure the change of total_out */
@@ -314,7 +314,7 @@ static MultiFDMethods multifd_zlib_ops = {
.send_prepare = zlib_send_prepare,
.recv_setup = zlib_recv_setup,
.recv_cleanup = zlib_recv_cleanup,
.recv_pages = zlib_recv_pages
.recv_data = zlib_recv_pages
};
static void multifd_zlib_register(void)

View File

@@ -52,7 +52,7 @@ static int zstd_send_setup(MultiFDSendParams *p, Error **errp)
struct zstd_data *z = g_new0(struct zstd_data, 1);
int res;
p->data = z;
p->compress_data = z;
z->zcs = ZSTD_createCStream();
if (!z->zcs) {
g_free(z);
@@ -90,14 +90,14 @@ static int zstd_send_setup(MultiFDSendParams *p, Error **errp)
*/
static void zstd_send_cleanup(MultiFDSendParams *p, Error **errp)
{
struct zstd_data *z = p->data;
struct zstd_data *z = p->compress_data;
ZSTD_freeCStream(z->zcs);
z->zcs = NULL;
g_free(z->zbuff);
z->zbuff = NULL;
g_free(p->data);
p->data = NULL;
g_free(p->compress_data);
p->compress_data = NULL;
}
/**
@@ -113,7 +113,7 @@ static void zstd_send_cleanup(MultiFDSendParams *p, Error **errp)
*/
static int zstd_send_prepare(MultiFDSendParams *p, Error **errp)
{
struct zstd_data *z = p->data;
struct zstd_data *z = p->compress_data;
int ret;
uint32_t i;
@@ -178,7 +178,7 @@ static int zstd_recv_setup(MultiFDRecvParams *p, Error **errp)
struct zstd_data *z = g_new0(struct zstd_data, 1);
int ret;
p->data = z;
p->compress_data = z;
z->zds = ZSTD_createDStream();
if (!z->zds) {
g_free(z);
@@ -216,14 +216,14 @@ static int zstd_recv_setup(MultiFDRecvParams *p, Error **errp)
*/
static void zstd_recv_cleanup(MultiFDRecvParams *p)
{
struct zstd_data *z = p->data;
struct zstd_data *z = p->compress_data;
ZSTD_freeDStream(z->zds);
z->zds = NULL;
g_free(z->zbuff);
z->zbuff = NULL;
g_free(p->data);
p->data = NULL;
g_free(p->compress_data);
p->compress_data = NULL;
}
/**
@@ -243,7 +243,7 @@ static int zstd_recv_pages(MultiFDRecvParams *p, Error **errp)
uint32_t out_size = 0;
uint32_t expected_size = p->normal_num * p->page_size;
uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK;
struct zstd_data *z = p->data;
struct zstd_data *z = p->compress_data;
int ret;
int i;
@@ -305,7 +305,7 @@ static MultiFDMethods multifd_zstd_ops = {
.send_prepare = zstd_send_prepare,
.recv_setup = zstd_recv_setup,
.recv_cleanup = zstd_recv_cleanup,
.recv_pages = zstd_recv_pages
.recv_data = zstd_recv_pages
};
static void multifd_zstd_register(void)

View File

@@ -17,6 +17,7 @@
#include "exec/ramblock.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "file.h"
#include "ram.h"
#include "migration.h"
#include "migration-stats.h"
@@ -28,6 +29,7 @@
#include "threadinfo.h"
#include "options.h"
#include "qemu/yank.h"
#include "io/channel-file.h"
#include "io/channel-socket.h"
#include "yank_functions.h"
@@ -128,7 +130,7 @@ static void nocomp_recv_cleanup(MultiFDRecvParams *p)
}
/**
* nocomp_recv_pages: read the data from the channel into actual pages
* nocomp_recv_data: read the data from the channel
*
* For no compression we just need to read things into the correct place.
*
@@ -137,20 +139,40 @@ static void nocomp_recv_cleanup(MultiFDRecvParams *p)
* @p: Params for the channel that we are using
* @errp: pointer to an error
*/
static int nocomp_recv_pages(MultiFDRecvParams *p, Error **errp)
static int nocomp_recv_data(MultiFDRecvParams *p, Error **errp)
{
uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK;
ERRP_GUARD();
if (flags != MULTIFD_FLAG_NOCOMP) {
error_setg(errp, "multifd %u: flags received %x flags expected %x",
p->id, flags, MULTIFD_FLAG_NOCOMP);
return -1;
}
if (!migrate_multifd_packets()) {
MultiFDRecvData *data = p->data;
size_t ret;
ret = qio_channel_pread(p->c, (char *) data->opaque,
data->size, data->file_offset, errp);
if (ret != data->size) {
error_prepend(errp,
"multifd recv (%u): read %#lx expected %#lx. ",
p->id, ret, data->size);
return -1;
}
data->size = 0;
return 0;
} else {
for (int i = 0; i < p->normal_num; i++) {
p->iov[i].iov_base = p->host + p->normal[i];
p->iov[i].iov_len = p->page_size;
}
return qio_channel_readv_all(p->c, p->iov, p->normal_num, errp);
}
}
static MultiFDMethods multifd_nocomp_ops = {
@@ -159,7 +181,7 @@ static MultiFDMethods multifd_nocomp_ops = {
.send_prepare = nocomp_send_prepare,
.recv_setup = nocomp_recv_setup,
.recv_cleanup = nocomp_recv_cleanup,
.recv_pages = nocomp_recv_pages
.recv_data = nocomp_recv_data
};
static MultiFDMethods *multifd_ops[MULTIFD_COMPRESSION__MAX] = {
@@ -236,12 +258,12 @@ static int multifd_recv_initial_packet(QIOChannel *c, Error **errp)
return msg.id;
}
static MultiFDPages_t *multifd_pages_init(size_t size)
static MultiFDPages_t *multifd_pages_init(uint32_t n)
{
MultiFDPages_t *pages = g_new0(MultiFDPages_t, 1);
pages->allocated = size;
pages->offset = g_new0(ram_addr_t, size);
pages->allocated = n;
pages->offset = g_new0(ram_addr_t, n);
return pages;
}
@@ -250,13 +272,25 @@ static void multifd_pages_clear(MultiFDPages_t *pages)
{
pages->num = 0;
pages->allocated = 0;
pages->packet_num = 0;
pages->block = NULL;
g_free(pages->offset);
pages->offset = NULL;
g_free(pages);
}
static void multifd_set_file_bitmap(MultiFDSendParams *p)
{
MultiFDPages_t *pages = p->pages;
if (!pages->block) {
return;
}
for (int i = 0; i < p->normal_num; i++) {
ramblock_set_shadow_bmap_atomic(pages->block, pages->offset[i]);
}
}
static void multifd_send_fill_packet(MultiFDSendParams *p)
{
MultiFDPacket_t *packet = p->packet;
@@ -391,7 +425,7 @@ struct {
* false.
*/
static int multifd_send_pages(QEMUFile *f)
static int multifd_send_pages(void)
{
int i;
static int next_channel;
@@ -403,6 +437,7 @@ static int multifd_send_pages(QEMUFile *f)
}
qemu_sem_wait(&multifd_send_state->channels_ready);
/*
* next_channel can remain from a previous migration that was
* using more channels, so ensure it doesn't overflow if the
@@ -437,7 +472,7 @@ static int multifd_send_pages(QEMUFile *f)
return 1;
}
int multifd_queue_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset)
int multifd_queue_page(RAMBlock *block, ram_addr_t offset)
{
MultiFDPages_t *pages = multifd_send_state->pages;
bool changed = false;
@@ -457,12 +492,12 @@ int multifd_queue_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset)
changed = true;
}
if (multifd_send_pages(f) < 0) {
if (multifd_send_pages() < 0) {
return -1;
}
if (changed) {
return multifd_queue_page(f, block, offset);
return multifd_queue_page(block, offset);
}
return 1;
@@ -511,7 +546,11 @@ static void multifd_send_terminate_threads(Error *err)
static int multifd_send_channel_destroy(QIOChannel *send)
{
if (migrate_to_file()) {
return file_send_channel_destroy(send);
} else {
return socket_send_channel_destroy(send);
}
}
void multifd_save_cleanup(void)
@@ -584,7 +623,7 @@ static int multifd_zero_copy_flush(QIOChannel *c)
return ret;
}
int multifd_send_sync_main(QEMUFile *f)
int multifd_send_sync_main(void)
{
int i;
bool flush_zero_copy;
@@ -593,12 +632,40 @@ int multifd_send_sync_main(QEMUFile *f)
return 0;
}
if (multifd_send_state->pages->num) {
if (multifd_send_pages(f) < 0) {
if (multifd_send_pages() < 0) {
error_report("%s: multifd_send_pages fail", __func__);
return -1;
}
}
if (!migrate_multifd_packets()) {
/*
* There's no sync packet to send. Just make sure the sending
* above has finished.
*/
for (i = 0; i < migrate_multifd_channels(); i++) {
qemu_sem_wait(&multifd_send_state->channels_ready);
}
/* sanity check and release the channels */
for (i = 0; i < migrate_multifd_channels(); i++) {
MultiFDSendParams *p = &multifd_send_state->params[i];
qemu_mutex_lock(&p->mutex);
assert(!p->pending_job || p->quit);
if (p->quit) {
error_report("%s: channel %d has already quit!", __func__, i);
qemu_mutex_unlock(&p->mutex);
return -1;
}
qemu_mutex_unlock(&p->mutex);
qemu_sem_post(&p->sem);
}
return 0;
}
/*
* When using zero-copy, it's necessary to flush the pages before any of
* the pages can be sent again, so we'll make sure the new version of the
@@ -654,18 +721,22 @@ static void *multifd_send_thread(void *opaque)
Error *local_err = NULL;
int ret = 0;
bool use_zero_copy_send = migrate_zero_copy_send();
bool use_packets = migrate_multifd_packets();
thread = migration_threads_add(p->name, qemu_get_thread_id());
trace_multifd_send_thread_start(p->id);
rcu_register_thread();
if (use_packets) {
if (multifd_send_initial_packet(p, &local_err) < 0) {
ret = -1;
goto out;
}
/* initial packet */
p->num_packets = 1;
}
while (true) {
qemu_sem_post(&multifd_send_state->channels_ready);
@@ -677,11 +748,12 @@ static void *multifd_send_thread(void *opaque)
qemu_mutex_lock(&p->mutex);
if (p->pending_job) {
uint64_t packet_num = p->packet_num;
uint32_t flags;
uint64_t write_base;
p->normal_num = 0;
if (use_zero_copy_send) {
if (!use_packets || use_zero_copy_send) {
p->iovs_num = 0;
} else {
p->iovs_num = 1;
@@ -699,18 +771,32 @@ static void *multifd_send_thread(void *opaque)
break;
}
}
if (use_packets) {
multifd_send_fill_packet(p);
p->num_packets++;
} else {
multifd_set_file_bitmap(p);
/*
* If we subtract the host page now, we don't need to
* pass it into qio_channel_write_full_all() below.
*/
write_base = p->pages->block->pages_offset -
(uint64_t)p->pages->block->host;
}
flags = p->flags;
p->flags = 0;
p->num_packets++;
p->total_normal_pages += p->normal_num;
p->pages->num = 0;
p->pages->block = NULL;
qemu_mutex_unlock(&p->mutex);
trace_multifd_send(p->id, packet_num, p->normal_num, flags,
trace_multifd_send(p->id, p->packet_num, p->normal_num, flags,
p->next_packet_size);
if (use_packets) {
if (use_zero_copy_send) {
/* Send header first, without zerocopy */
ret = qio_channel_write_all(p->c, (void *)p->packet,
@@ -726,6 +812,11 @@ static void *multifd_send_thread(void *opaque)
ret = qio_channel_writev_full_all(p->c, p->iov, p->iovs_num, NULL,
0, p->write_flags, &local_err);
} else {
ret = qio_channel_pwritev_all(p->c, p->iov, p->iovs_num,
write_base, &local_err);
}
if (ret != 0) {
break;
}
@@ -858,8 +949,7 @@ static bool multifd_channel_connect(MultiFDSendParams *p,
return true;
}
static void multifd_new_send_channel_cleanup(MultiFDSendParams *p,
QIOChannel *ioc, Error *err)
static void multifd_new_send_channel_cleanup(MultiFDSendParams *p, Error *err)
{
migrate_set_error(migrate_get_current(), err);
/* Error happen, we need to tell who pay attention to me */
@@ -871,20 +961,20 @@ static void multifd_new_send_channel_cleanup(MultiFDSendParams *p,
* its status.
*/
p->quit = true;
object_unref(OBJECT(ioc));
error_free(err);
}
static void multifd_new_send_channel_async(QIOTask *task, gpointer opaque)
{
MultiFDSendParams *p = opaque;
QIOChannel *ioc = QIO_CHANNEL(qio_task_get_source(task));
Object *obj = qio_task_get_source(task);
Error *local_err = NULL;
trace_multifd_new_send_channel_async(p->id);
if (!qio_task_propagate_error(task, &local_err)) {
p->c = ioc;
qio_channel_set_delay(p->c, false);
QIOChannel *ioc = QIO_CHANNEL(obj);
qio_channel_set_delay(ioc, false);
p->running = true;
if (multifd_channel_connect(p, ioc, &local_err)) {
return;
@@ -892,18 +982,24 @@ static void multifd_new_send_channel_async(QIOTask *task, gpointer opaque)
}
trace_multifd_new_send_channel_async_error(p->id, local_err);
multifd_new_send_channel_cleanup(p, ioc, local_err);
multifd_new_send_channel_cleanup(p, local_err);
object_unref(obj);
}
static void multifd_new_send_channel_create(gpointer opaque)
{
if (migrate_to_file()) {
file_send_channel_create(multifd_new_send_channel_async, opaque);
} else {
socket_send_channel_create(multifd_new_send_channel_async, opaque);
}
}
int multifd_save_setup(Error **errp)
{
int thread_count;
uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size();
bool use_packets = migrate_multifd_packets();
uint8_t i;
if (!migrate_multifd()) {
@@ -928,14 +1024,20 @@ int multifd_save_setup(Error **errp)
p->pending_job = 0;
p->id = i;
p->pages = multifd_pages_init(page_count);
if (use_packets) {
p->packet_len = sizeof(MultiFDPacket_t)
+ sizeof(uint64_t) * page_count;
p->packet = g_malloc0(p->packet_len);
p->packet->magic = cpu_to_be32(MULTIFD_MAGIC);
p->packet->version = cpu_to_be32(MULTIFD_VERSION);
p->name = g_strdup_printf("multifdsend_%d", i);
/* We need one extra place for the packet header */
p->iov = g_new0(struct iovec, page_count + 1);
} else {
p->iov = g_new0(struct iovec, page_count);
}
p->name = g_strdup_printf("multifdsend_%d", i);
p->normal = g_new0(ram_addr_t, page_count);
p->page_size = qemu_target_page_size();
p->page_count = page_count;
@@ -965,6 +1067,7 @@ int multifd_save_setup(Error **errp)
struct {
MultiFDRecvParams *params;
MultiFDRecvData *data;
/* number of created threads */
int count;
/* syncs main thread and channels */
@@ -975,6 +1078,49 @@ struct {
MultiFDMethods *ops;
} *multifd_recv_state;
int multifd_recv(void)
{
int i;
static int next_recv_channel;
MultiFDRecvParams *p = NULL;
MultiFDRecvData *data = multifd_recv_state->data;
/*
* next_channel can remain from a previous migration that was
* using more channels, so ensure it doesn't overflow if the
* limit is lower now.
*/
next_recv_channel %= migrate_multifd_channels();
for (i = next_recv_channel;; i = (i + 1) % migrate_multifd_channels()) {
p = &multifd_recv_state->params[i];
qemu_mutex_lock(&p->mutex);
if (p->quit) {
error_report("%s: channel %d has already quit!", __func__, i);
qemu_mutex_unlock(&p->mutex);
return -1;
}
if (!p->pending_job) {
p->pending_job++;
next_recv_channel = (i + 1) % migrate_multifd_channels();
break;
}
qemu_mutex_unlock(&p->mutex);
}
multifd_recv_state->data = p->data;
p->data = data;
qemu_mutex_unlock(&p->mutex);
qemu_sem_post(&p->sem);
return 1;
}
MultiFDRecvData *multifd_get_recv_data(void)
{
return multifd_recv_state->data;
}
static void multifd_recv_terminate_threads(Error *err)
{
int i;
@@ -996,6 +1142,7 @@ static void multifd_recv_terminate_threads(Error *err)
qemu_mutex_lock(&p->mutex);
p->quit = true;
qemu_sem_post(&p->sem);
/*
* We could arrive here for two reasons:
* - normal quit, i.e. everything went fine, just finished
@@ -1045,6 +1192,7 @@ void multifd_load_cleanup(void)
p->c = NULL;
qemu_mutex_destroy(&p->mutex);
qemu_sem_destroy(&p->sem_sync);
qemu_sem_destroy(&p->sem);
g_free(p->name);
p->name = NULL;
p->packet_len = 0;
@@ -1059,6 +1207,8 @@ void multifd_load_cleanup(void)
qemu_sem_destroy(&multifd_recv_state->sem_sync);
g_free(multifd_recv_state->params);
multifd_recv_state->params = NULL;
g_free(multifd_recv_state->data);
multifd_recv_state->data = NULL;
g_free(multifd_recv_state);
multifd_recv_state = NULL;
}
@@ -1070,12 +1220,18 @@ void multifd_recv_sync_main(void)
if (!migrate_multifd()) {
return;
}
for (i = 0; i < migrate_multifd_channels(); i++) {
MultiFDRecvParams *p = &multifd_recv_state->params[i];
trace_multifd_recv_sync_main_wait(p->id);
qemu_sem_wait(&multifd_recv_state->sem_sync);
}
if (!migrate_multifd_packets()) {
return;
}
for (i = 0; i < migrate_multifd_channels(); i++) {
MultiFDRecvParams *p = &multifd_recv_state->params[i];
@@ -1094,18 +1250,21 @@ static void *multifd_recv_thread(void *opaque)
{
MultiFDRecvParams *p = opaque;
Error *local_err = NULL;
bool use_packets = migrate_multifd_packets();
int ret;
trace_multifd_recv_thread_start(p->id);
rcu_register_thread();
while (true) {
uint32_t flags;
uint32_t flags = 0;
bool has_data = false;
if (p->quit) {
break;
}
if (use_packets) {
ret = qio_channel_read_all_eof(p->c, (void *)p->packet,
p->packet_len, &local_err);
if (ret == 0 || ret == -1) { /* 0: EOF -1: Error */
@@ -1118,26 +1277,49 @@ static void *multifd_recv_thread(void *opaque)
qemu_mutex_unlock(&p->mutex);
break;
}
p->num_packets++;
flags = p->flags;
/* recv methods don't know how to handle the SYNC flag */
p->flags &= ~MULTIFD_FLAG_SYNC;
trace_multifd_recv(p->id, p->packet_num, p->normal_num, flags,
p->next_packet_size);
p->num_packets++;
p->total_normal_pages += p->normal_num;
has_data = !!p->normal_num;
} else {
/*
* No packets, so we need to wait for the vmstate code to
* give us work.
*/
qemu_sem_wait(&p->sem);
qemu_mutex_lock(&p->mutex);
if (!p->pending_job) {
qemu_mutex_unlock(&p->mutex);
break;
}
has_data = !!p->data->size;
}
qemu_mutex_unlock(&p->mutex);
if (p->normal_num) {
ret = multifd_recv_state->ops->recv_pages(p, &local_err);
if (has_data) {
ret = multifd_recv_state->ops->recv_data(p, &local_err);
if (ret != 0) {
break;
}
}
if (flags & MULTIFD_FLAG_SYNC) {
if (use_packets && (flags & MULTIFD_FLAG_SYNC)) {
qemu_sem_post(&multifd_recv_state->sem_sync);
qemu_sem_wait(&p->sem_sync);
} else {
qemu_mutex_lock(&p->mutex);
p->pending_job--;
qemu_mutex_unlock(&p->mutex);
/* migration thread needs to know when we're done */
qemu_sem_post(&multifd_recv_state->sem_sync);
}
}
@@ -1159,6 +1341,7 @@ int multifd_load_setup(Error **errp)
{
int thread_count;
uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size();
bool use_packets = migrate_multifd_packets();
uint8_t i;
/*
@@ -1172,6 +1355,10 @@ int multifd_load_setup(Error **errp)
thread_count = migrate_multifd_channels();
multifd_recv_state = g_malloc0(sizeof(*multifd_recv_state));
multifd_recv_state->params = g_new0(MultiFDRecvParams, thread_count);
multifd_recv_state->data = g_new0(MultiFDRecvData, 1);
multifd_recv_state->data->size = 0;
qatomic_set(&multifd_recv_state->count, 0);
qemu_sem_init(&multifd_recv_state->sem_sync, 0);
multifd_recv_state->ops = multifd_ops[migrate_multifd_compression()];
@@ -1181,11 +1368,19 @@ int multifd_load_setup(Error **errp)
qemu_mutex_init(&p->mutex);
qemu_sem_init(&p->sem_sync, 0);
qemu_sem_init(&p->sem, 0);
p->quit = false;
p->pending_job = 0;
p->id = i;
p->data = g_new0(MultiFDRecvData, 1);
p->data->size = 0;
if (use_packets) {
p->packet_len = sizeof(MultiFDPacket_t)
+ sizeof(uint64_t) * page_count;
p->packet = g_malloc0(p->packet_len);
}
p->name = g_strdup_printf("multifdrecv_%d", i);
p->iov = g_new0(struct iovec, page_count);
p->normal = g_new0(ram_addr_t, page_count);
@@ -1207,6 +1402,11 @@ int multifd_load_setup(Error **errp)
return 0;
}
bool multifd_recv_first_channel(void)
{
return !multifd_recv_state;
}
bool multifd_recv_all_channels_created(void)
{
int thread_count = migrate_multifd_channels();
@@ -1231,8 +1431,10 @@ void multifd_recv_new_channel(QIOChannel *ioc, Error **errp)
{
MultiFDRecvParams *p;
Error *local_err = NULL;
int id;
bool use_packets = migrate_multifd_packets();
int id, num_packets = 0;
if (use_packets) {
id = multifd_recv_initial_packet(ioc, &local_err);
if (id < 0) {
multifd_recv_terminate_threads(local_err);
@@ -1244,6 +1446,12 @@ void multifd_recv_new_channel(QIOChannel *ioc, Error **errp)
}
trace_multifd_recv_new_channel(id);
/* initial packet */
num_packets = 1;
} else {
id = qatomic_read(&multifd_recv_state->count);
}
p = &multifd_recv_state->params[id];
if (p->c != NULL) {
error_setg(&local_err, "multifd: received id '%d' already setup'",
@@ -1253,9 +1461,8 @@ void multifd_recv_new_channel(QIOChannel *ioc, Error **errp)
return;
}
p->c = ioc;
p->num_packets = num_packets;
object_ref(OBJECT(ioc));
/* initial packet */
p->num_packets = 1;
p->running = true;
qemu_thread_create(&p->thread, p->name, multifd_recv_thread, p,

View File

@@ -13,16 +13,21 @@
#ifndef QEMU_MIGRATION_MULTIFD_H
#define QEMU_MIGRATION_MULTIFD_H
typedef struct MultiFDRecvData MultiFDRecvData;
int multifd_save_setup(Error **errp);
void multifd_save_cleanup(void);
int multifd_load_setup(Error **errp);
void multifd_load_cleanup(void);
void multifd_load_shutdown(void);
bool multifd_recv_first_channel(void);
bool multifd_recv_all_channels_created(void);
void multifd_recv_new_channel(QIOChannel *ioc, Error **errp);
void multifd_recv_sync_main(void);
int multifd_send_sync_main(QEMUFile *f);
int multifd_queue_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset);
int multifd_send_sync_main(void);
int multifd_queue_page(RAMBlock *block, ram_addr_t offset);
int multifd_recv(void);
MultiFDRecvData *multifd_get_recv_data(void);
/* Multifd Compression flags */
#define MULTIFD_FLAG_SYNC (1 << 0)
@@ -58,13 +63,18 @@ typedef struct {
uint32_t num;
/* number of allocated pages */
uint32_t allocated;
/* global number of generated multifd packets */
uint64_t packet_num;
/* offset of each page */
ram_addr_t *offset;
RAMBlock *block;
} MultiFDPages_t;
struct MultiFDRecvData {
void *opaque;
size_t size;
/* for preadv */
off_t file_offset;
};
typedef struct {
/* Fields are only written at creating/deletion time */
/* No lock required for them, they are read only */
@@ -131,7 +141,7 @@ typedef struct {
/* num of non zero pages */
uint32_t normal_num;
/* used for compression methods */
void *data;
void *compress_data;
} MultiFDSendParams;
typedef struct {
@@ -155,6 +165,8 @@ typedef struct {
/* syncs main thread and channels */
QemuSemaphore sem_sync;
/* sem where to wait for more work */
QemuSemaphore sem;
/* this mutex protects the following parameters */
QemuMutex mutex;
@@ -166,6 +178,13 @@ typedef struct {
uint32_t flags;
/* global number of generated multifd packets */
uint64_t packet_num;
int pending_job;
/*
* The owner of 'data' depends of 'pending_job' value:
* pending_job == 0 -> migration_thread can use it.
* pending_job != 0 -> multifd_channel can use it.
*/
MultiFDRecvData *data;
/* thread local variables. No locking required */
@@ -188,7 +207,7 @@ typedef struct {
/* num of non zero pages */
uint32_t normal_num;
/* used for de-compression methods */
void *data;
void *compress_data;
} MultiFDRecvParams;
typedef struct {
@@ -202,11 +221,10 @@ typedef struct {
int (*recv_setup)(MultiFDRecvParams *p, Error **errp);
/* Cleanup for receiving side */
void (*recv_cleanup)(MultiFDRecvParams *p);
/* Read all pages */
int (*recv_pages)(MultiFDRecvParams *p, Error **errp);
/* Read all data */
int (*recv_data)(MultiFDRecvParams *p, Error **errp);
} MultiFDMethods;
void multifd_register_ops(int method, MultiFDMethods *ops);
#endif

View File

@@ -204,6 +204,7 @@ Property migration_properties[] = {
DEFINE_PROP_MIG_CAP("x-switchover-ack",
MIGRATION_CAPABILITY_SWITCHOVER_ACK),
DEFINE_PROP_MIG_CAP("x-dirty-limit", MIGRATION_CAPABILITY_DIRTY_LIMIT),
DEFINE_PROP_MIG_CAP("x-fixed-ram", MIGRATION_CAPABILITY_FIXED_RAM),
DEFINE_PROP_END_OF_LIST(),
};
@@ -263,6 +264,13 @@ bool migrate_events(void)
return s->capabilities[MIGRATION_CAPABILITY_EVENTS];
}
bool migrate_fixed_ram(void)
{
MigrationState *s = migrate_get_current();
return s->capabilities[MIGRATION_CAPABILITY_FIXED_RAM];
}
bool migrate_ignore_shared(void)
{
MigrationState *s = migrate_get_current();
@@ -377,6 +385,11 @@ bool migrate_multifd_flush_after_each_section(void)
return s->multifd_flush_after_each_section;
}
bool migrate_multifd_packets(void)
{
return !migrate_fixed_ram();
}
bool migrate_postcopy(void)
{
return migrate_postcopy_ram() || migrate_dirty_bitmaps();
@@ -396,6 +409,13 @@ bool migrate_tls(void)
return s->parameters.tls_creds && *s->parameters.tls_creds;
}
bool migrate_to_file(void)
{
MigrationState *s = migrate_get_current();
return qemu_file_is_seekable(s->to_dst_file);
}
typedef enum WriteTrackingSupport {
WT_SUPPORT_UNKNOWN = 0,
WT_SUPPORT_ABSENT,
@@ -645,6 +665,26 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp)
}
}
if (new_caps[MIGRATION_CAPABILITY_FIXED_RAM]) {
if (new_caps[MIGRATION_CAPABILITY_XBZRLE]) {
error_setg(errp,
"Fixed-ram migration is incompatible with xbzrle");
return false;
}
if (new_caps[MIGRATION_CAPABILITY_COMPRESS]) {
error_setg(errp,
"Fixed-ram migration is incompatible with compression");
return false;
}
if (new_caps[MIGRATION_CAPABILITY_POSTCOPY_RAM]) {
error_setg(errp,
"Fixed-ram migration is incompatible with postcopy ram");
return false;
}
}
return true;
}
@@ -795,6 +835,22 @@ int migrate_decompress_threads(void)
return s->parameters.decompress_threads;
}
bool migrate_direct_io(void)
{
MigrationState *s = migrate_get_current();
/* For now O_DIRECT is only supported with fixed-ram */
if (!s->capabilities[MIGRATION_CAPABILITY_FIXED_RAM]) {
return false;
}
if (s->parameters.has_direct_io) {
return s->parameters.direct_io;
}
return false;
}
uint64_t migrate_downtime_limit(void)
{
MigrationState *s = migrate_get_current();
@@ -1012,6 +1068,11 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
params->has_mode = true;
params->mode = s->parameters.mode;
if (s->parameters.has_direct_io) {
params->has_direct_io = true;
params->direct_io = s->parameters.direct_io;
}
return params;
}
@@ -1047,6 +1108,7 @@ void migrate_params_init(MigrationParameters *params)
params->has_x_vcpu_dirty_limit_period = true;
params->has_vcpu_dirty_limit = true;
params->has_mode = true;
params->has_direct_io = qemu_has_direct_io();
}
/*
@@ -1348,6 +1410,10 @@ static void migrate_params_test_apply(MigrateSetParameters *params,
if (params->has_mode) {
dest->mode = params->mode;
}
if (params->has_direct_io) {
dest->direct_io = params->direct_io;
}
}
static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
@@ -1492,6 +1558,10 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
if (params->has_mode) {
s->parameters.mode = params->mode;
}
if (params->has_direct_io) {
s->parameters.direct_io = params->direct_io;
}
}
void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp)

View File

@@ -31,6 +31,7 @@ bool migrate_compress(void);
bool migrate_dirty_bitmaps(void);
bool migrate_dirty_limit(void);
bool migrate_events(void);
bool migrate_fixed_ram(void);
bool migrate_ignore_shared(void);
bool migrate_late_block_activate(void);
bool migrate_multifd(void);
@@ -55,9 +56,11 @@ bool migrate_zero_copy_send(void);
*/
bool migrate_multifd_flush_after_each_section(void);
bool migrate_multifd_packets(void);
bool migrate_postcopy(void);
bool migrate_rdma(void);
bool migrate_tls(void);
bool migrate_to_file(void);
/* capabilities helpers */
@@ -78,6 +81,7 @@ uint8_t migrate_cpu_throttle_increment(void);
uint8_t migrate_cpu_throttle_initial(void);
bool migrate_cpu_throttle_tailslow(void);
int migrate_decompress_threads(void);
bool migrate_direct_io(void);
uint64_t migrate_downtime_limit(void);
uint8_t migrate_max_cpu_throttle(void);
uint64_t migrate_max_bandwidth(void);

View File

@@ -33,6 +33,7 @@
#include "options.h"
#include "qapi/error.h"
#include "rdma.h"
#include "io/channel-file.h"
#define IO_BUF_SIZE 32768
#define MAX_IOV_SIZE MIN_CONST(IOV_MAX, 64)
@@ -255,6 +256,10 @@ static void qemu_iovec_release_ram(QEMUFile *f)
memset(f->may_free, 0, sizeof(f->may_free));
}
bool qemu_file_is_seekable(QEMUFile *f)
{
return qio_channel_has_feature(f->ioc, QIO_CHANNEL_FEATURE_SEEKABLE);
}
/**
* Flushes QEMUFile buffer
@@ -447,6 +452,81 @@ void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, size_t size)
}
}
void qemu_put_buffer_at(QEMUFile *f, const uint8_t *buf, size_t buflen, off_t pos)
{
Error *err = NULL;
if (f->last_error) {
return;
}
qemu_fflush(f);
qio_channel_pwrite(f->ioc, (char *)buf, buflen, pos, &err);
if (err) {
qemu_file_set_error_obj(f, -EIO, err);
} else {
stat64_add(&mig_stats.qemu_file_transferred, buflen);
}
return;
}
size_t qemu_get_buffer_at(QEMUFile *f, const uint8_t *buf, size_t buflen, off_t pos)
{
Error *err = NULL;
ssize_t ret;
if (f->last_error) {
return 0;
}
ret = qio_channel_pread(f->ioc, (char *)buf, buflen, pos, &err);
if (ret == -1 || err) {
goto error;
}
return (size_t)ret;
error:
qemu_file_set_error_obj(f, -EIO, err);
return 0;
}
void qemu_set_offset(QEMUFile *f, off_t off, int whence)
{
Error *err = NULL;
off_t ret;
qemu_fflush(f);
if (!qemu_file_is_writable(f)) {
f->buf_index = 0;
f->buf_size = 0;
}
ret = qio_channel_io_seek(f->ioc, off, whence, &err);
if (ret == (off_t)-1) {
qemu_file_set_error_obj(f, -EIO, err);
}
}
off_t qemu_get_offset(QEMUFile *f)
{
Error *err = NULL;
off_t ret;
qemu_fflush(f);
ret = qio_channel_io_seek(f->ioc, 0, SEEK_CUR, &err);
if (ret == (off_t)-1) {
qemu_file_set_error_obj(f, -EIO, err);
}
return ret;
}
void qemu_put_byte(QEMUFile *f, int v)
{
if (f->last_error) {

View File

@@ -75,6 +75,10 @@ QEMUFile *qemu_file_get_return_path(QEMUFile *f);
int qemu_fflush(QEMUFile *f);
void qemu_file_set_blocking(QEMUFile *f, bool block);
int qemu_file_get_to_fd(QEMUFile *f, int fd, size_t size);
void qemu_set_offset(QEMUFile *f, off_t off, int whence);
off_t qemu_get_offset(QEMUFile *f);
void qemu_put_buffer_at(QEMUFile *f, const uint8_t *buf, size_t buflen, off_t pos);
size_t qemu_get_buffer_at(QEMUFile *f, const uint8_t *buf, size_t buflen, off_t pos);
QIOChannel *qemu_file_get_ioc(QEMUFile *file);

View File

@@ -94,6 +94,25 @@
#define RAM_SAVE_FLAG_MULTIFD_FLUSH 0x200
/* We can't use any flag that is bigger than 0x200 */
/*
* fixed-ram migration supports O_DIRECT, so we need to make sure the
* userspace buffer, the IO operation size and the file offset are
* aligned according to the underlying device's block size. The first
* two are already aligned to page size, but we need to add padding to
* the file to align the offset. We cannot read the block size
* dynamically because the migration file can be moved between
* different systems, so use 1M to cover most block sizes and to keep
* the file offset aligned at page size as well.
*/
#define FIXED_RAM_FILE_OFFSET_ALIGNMENT 0x100000
/*
* When doing fixed-ram migration, this is the amount we read from the
* pages region in the migration file at a time.
*/
#define FIXED_RAM_LOAD_BUF_SIZE 0x100000
#define FIXED_RAM_MULTIFD_LOAD_BUF_SIZE 0x100000
XBZRLECacheStats xbzrle_counters;
/* used by the search for pages to send */
@@ -1127,12 +1146,18 @@ static int save_zero_page(RAMState *rs, PageSearchStatus *pss,
return 0;
}
stat64_add(&mig_stats.zero_pages, 1);
if (migrate_fixed_ram()) {
/* zero pages are not transferred with fixed-ram */
clear_bit_atomic(offset >> TARGET_PAGE_BITS, pss->block->shadow_bmap);
return 1;
}
len += save_page_header(pss, file, pss->block, offset | RAM_SAVE_FLAG_ZERO);
qemu_put_byte(file, 0);
len += 1;
ram_release_page(pss->block->idstr, offset);
stat64_add(&mig_stats.zero_pages, 1);
ram_transferred_add(len);
/*
@@ -1190,6 +1215,11 @@ static int save_normal_page(PageSearchStatus *pss, RAMBlock *block,
{
QEMUFile *file = pss->pss_channel;
if (migrate_fixed_ram()) {
qemu_put_buffer_at(file, buf, TARGET_PAGE_SIZE,
block->pages_offset + offset);
set_bit(offset >> TARGET_PAGE_BITS, block->shadow_bmap);
} else {
ram_transferred_add(save_page_header(pss, pss->pss_channel, block,
offset | RAM_SAVE_FLAG_PAGE));
if (async) {
@@ -1199,6 +1229,7 @@ static int save_normal_page(PageSearchStatus *pss, RAMBlock *block,
} else {
qemu_put_buffer(file, buf, TARGET_PAGE_SIZE);
}
}
ram_transferred_add(TARGET_PAGE_SIZE);
stat64_add(&mig_stats.normal_pages, 1);
return 1;
@@ -1250,10 +1281,9 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss)
return pages;
}
static int ram_save_multifd_page(QEMUFile *file, RAMBlock *block,
ram_addr_t offset)
static int ram_save_multifd_page(RAMBlock *block, ram_addr_t offset)
{
if (multifd_queue_page(file, block, offset) < 0) {
if (multifd_queue_page(block, offset) < 0) {
return -1;
}
stat64_add(&mig_stats.normal_pages, 1);
@@ -1333,10 +1363,10 @@ static int find_dirty_block(RAMState *rs, PageSearchStatus *pss)
pss->page = 0;
pss->block = QLIST_NEXT_RCU(pss->block, next);
if (!pss->block) {
if (migrate_multifd() &&
if (!migrate_fixed_ram() && migrate_multifd() &&
!migrate_multifd_flush_after_each_section()) {
QEMUFile *f = rs->pss[RAM_CHANNEL_PRECOPY].pss_channel;
int ret = multifd_send_sync_main(f);
int ret = multifd_send_sync_main();
if (ret < 0) {
return ret;
}
@@ -2067,7 +2097,7 @@ static int ram_save_target_page_legacy(RAMState *rs, PageSearchStatus *pss)
* still see partially copied pages which is data corruption.
*/
if (migrate_multifd() && !migration_in_postcopy()) {
return ram_save_multifd_page(pss->pss_channel, block, offset);
return ram_save_multifd_page(block, offset);
}
return ram_save_page(rs, pss);
@@ -2768,7 +2798,7 @@ static void ram_list_init_bitmaps(void)
}
RAMBLOCK_FOREACH_NOT_IGNORED(block) {
pages = block->max_length >> TARGET_PAGE_BITS;
pages = block->used_length >> TARGET_PAGE_BITS;
/*
* The initial dirty bitmap for migration must be set with all
* ones to make sure we'll migrate every guest RAM page to
@@ -2780,6 +2810,7 @@ static void ram_list_init_bitmaps(void)
*/
block->bmap = bitmap_new(pages);
bitmap_set(block->bmap, 0, pages);
block->shadow_bmap = bitmap_new(pages);
block->clear_bmap_shift = shift;
block->clear_bmap = bitmap_new(clear_bmap_size(pages, shift));
}
@@ -2917,6 +2948,86 @@ void qemu_guest_free_page_hint(void *addr, size_t len)
}
}
#define FIXED_RAM_HDR_VERSION 1
struct FixedRamHeader {
uint32_t version;
/*
* The target's page size, so we know how many pages are in the
* bitmap.
*/
uint64_t page_size;
/*
* The offset in the migration file where the pages bitmap is
* found.
*/
uint64_t bitmap_offset;
/*
* The offset in the migration file where the actual pages (data)
* are found.
*/
uint64_t pages_offset;
/* end of v1 */
} QEMU_PACKED;
static void fixed_ram_insert_header(QEMUFile *file, RAMBlock *block)
{
g_autofree struct FixedRamHeader *header;
size_t header_size, bitmap_size;
long num_pages;
header = g_new0(struct FixedRamHeader, 1);
header_size = sizeof(struct FixedRamHeader);
num_pages = block->used_length >> TARGET_PAGE_BITS;
bitmap_size = BITS_TO_LONGS(num_pages) * sizeof(unsigned long);
/*
* Save the file offsets of where the bitmap and the pages should
* go as they are written at the end of migration and during the
* iterative phase, respectively.
*/
block->bitmap_offset = qemu_get_offset(file) + header_size;
block->pages_offset = ROUND_UP(block->bitmap_offset +
bitmap_size,
FIXED_RAM_FILE_OFFSET_ALIGNMENT);
header->version = cpu_to_be32(FIXED_RAM_HDR_VERSION);
header->page_size = cpu_to_be64(TARGET_PAGE_SIZE);
header->bitmap_offset = cpu_to_be64(block->bitmap_offset);
header->pages_offset = cpu_to_be64(block->pages_offset);
qemu_put_buffer(file, (uint8_t *) header, header_size);
}
static bool fixed_ram_read_header(QEMUFile *file, struct FixedRamHeader *header,
Error **errp)
{
size_t ret, header_size = sizeof(struct FixedRamHeader);
ret = qemu_get_buffer(file, (uint8_t *)header, header_size);
if (ret != header_size) {
error_setg(errp, "Could not read whole fixed-ram migration header "
"(expected %ld, got %ld)", header_size, ret);
return false;
}
/* migration stream is big-endian */
be32_to_cpus(&header->version);
if (header->version > FIXED_RAM_HDR_VERSION) {
error_setg(errp, "Migration fixed-ram capability version mismatch "
"(expected %d, got %d)", FIXED_RAM_HDR_VERSION,
header->version);
return false;
}
be64_to_cpus(&header->page_size);
be64_to_cpus(&header->bitmap_offset);
be64_to_cpus(&header->pages_offset);
return true;
}
/*
* Each of ram_save_setup, ram_save_iterate and ram_save_complete has
* long-running RCU critical section. When rcu-reclaims in the code
@@ -2966,6 +3077,12 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
if (migrate_ignore_shared()) {
qemu_put_be64(f, block->mr->addr);
}
if (migrate_fixed_ram()) {
fixed_ram_insert_header(f, block);
/* prepare offset for next ramblock */
qemu_set_offset(f, block->pages_offset + block->used_length, SEEK_SET);
}
}
}
@@ -2985,7 +3102,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
migration_ops->ram_save_target_page = ram_save_target_page_legacy;
qemu_mutex_unlock_iothread();
ret = multifd_send_sync_main(f);
ret = multifd_send_sync_main();
qemu_mutex_lock_iothread();
if (ret < 0) {
return ret;
@@ -2999,6 +3116,30 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
return qemu_fflush(f);
}
static void ram_save_shadow_bmap(QEMUFile *f)
{
RAMBlock *block;
RAMBLOCK_FOREACH_MIGRATABLE(block) {
long num_pages = block->used_length >> TARGET_PAGE_BITS;
long bitmap_size = BITS_TO_LONGS(num_pages) * sizeof(unsigned long);
qemu_put_buffer_at(f, (uint8_t *)block->shadow_bmap, bitmap_size,
block->bitmap_offset);
/*
* Free the bitmap here to catch any synchronization issues
* with multifd channels. No channels should be sending pages
* after we've written the bitmap to file.
*/
g_free(block->shadow_bmap);
block->shadow_bmap = NULL;
}
}
void ramblock_set_shadow_bmap_atomic(RAMBlock *block, ram_addr_t offset)
{
set_bit_atomic(offset >> TARGET_PAGE_BITS, block->shadow_bmap);
}
/**
* ram_save_iterate: iterative stage for migration
*
@@ -3109,7 +3250,7 @@ out:
if (ret >= 0
&& migration_is_setup_or_active(migrate_get_current()->state)) {
if (migrate_multifd() && migrate_multifd_flush_after_each_section()) {
ret = multifd_send_sync_main(rs->pss[RAM_CHANNEL_PRECOPY].pss_channel);
ret = multifd_send_sync_main();
if (ret < 0) {
return ret;
}
@@ -3183,7 +3324,7 @@ static int ram_save_complete(QEMUFile *f, void *opaque)
}
}
ret = multifd_send_sync_main(rs->pss[RAM_CHANNEL_PRECOPY].pss_channel);
ret = multifd_send_sync_main();
if (ret < 0) {
return ret;
}
@@ -3191,6 +3332,11 @@ static int ram_save_complete(QEMUFile *f, void *opaque)
if (migrate_multifd() && !migrate_multifd_flush_after_each_section()) {
qemu_put_be64(f, RAM_SAVE_FLAG_MULTIFD_FLUSH);
}
if (migrate_fixed_ram()) {
ram_save_shadow_bmap(f);
}
qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
return qemu_fflush(f);
}
@@ -3439,7 +3585,7 @@ int colo_init_ram_cache(void)
*/
if (ram_bytes_total()) {
RAMBLOCK_FOREACH_NOT_IGNORED(block) {
unsigned long pages = block->max_length >> TARGET_PAGE_BITS;
unsigned long pages = block->used_length >> TARGET_PAGE_BITS;
block->bmap = bitmap_new(pages);
}
}
@@ -3461,7 +3607,7 @@ void colo_incoming_start_dirty_log(void)
RAMBLOCK_FOREACH_NOT_IGNORED(block) {
ramblock_sync_dirty_bitmap(ram_state, block);
/* Discard this dirty bitmap record */
bitmap_zero(block->bmap, block->max_length >> TARGET_PAGE_BITS);
bitmap_zero(block->bmap, block->used_length >> TARGET_PAGE_BITS);
}
memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION);
}
@@ -3789,6 +3935,107 @@ void colo_flush_ram_cache(void)
trace_colo_flush_ram_cache_end();
}
static int ram_load_multifd_pages(RAMBlock *block, ram_addr_t start_offset,
size_t size)
{
MultiFDRecvData *data = multifd_get_recv_data();
/*
* Pointing the opaque directly to the host buffer, no
* preprocessing needed.
*/
data->opaque = block->host + start_offset;
data->file_offset = block->pages_offset + start_offset;
data->size = size;
if (multifd_recv() < 0) {
return -1;
}
return 1;
}
static void read_ramblock_fixed_ram(QEMUFile *f, RAMBlock *block,
long num_pages, unsigned long *bitmap)
{
unsigned long set_bit_idx, clear_bit_idx;
unsigned long len;
ram_addr_t offset;
void *host;
size_t read, completed, read_len;
for (set_bit_idx = find_first_bit(bitmap, num_pages);
set_bit_idx < num_pages;
set_bit_idx = find_next_bit(bitmap, num_pages, clear_bit_idx + 1)) {
clear_bit_idx = find_next_zero_bit(bitmap, num_pages, set_bit_idx + 1);
len = TARGET_PAGE_SIZE * (clear_bit_idx - set_bit_idx);
offset = set_bit_idx << TARGET_PAGE_BITS;
for (read = 0, completed = 0; completed < len; offset += read) {
host = host_from_ram_block_offset(block, offset);
if (migrate_multifd()) {
read_len = MIN(len, FIXED_RAM_MULTIFD_LOAD_BUF_SIZE);
ram_load_multifd_pages(block, offset, read_len);
read = read_len;
} else {
read_len = MIN(len, FIXED_RAM_LOAD_BUF_SIZE);
read = qemu_get_buffer_at(f, host, read_len,
block->pages_offset + offset);
}
completed += read;
}
}
}
static int parse_ramblock_fixed_ram(QEMUFile *f, RAMBlock *block,
ram_addr_t length, Error **errp)
{
g_autofree unsigned long *bitmap = NULL;
struct FixedRamHeader header;
size_t bitmap_size;
long num_pages;
if (!fixed_ram_read_header(f, &header, errp)) {
return -EINVAL;
}
block->pages_offset = header.pages_offset;
/*
* Check the alignment of the file region that contains pages. We
* don't enforce FIXED_RAM_FILE_OFFSET_ALIGNMENT to allow that
* value to change in the future. Do only a sanity check with page
* size alignment.
*/
if (!QEMU_IS_ALIGNED(block->pages_offset, TARGET_PAGE_SIZE)) {
error_setg(errp,
"Error reading ramblock %s pages, region has bad alignment",
block->idstr);
return -EINVAL;
}
num_pages = length / header.page_size;
bitmap_size = BITS_TO_LONGS(num_pages) * sizeof(unsigned long);
bitmap = g_malloc0(bitmap_size);
if (qemu_get_buffer_at(f, (uint8_t *)bitmap, bitmap_size,
header.bitmap_offset) != bitmap_size) {
error_setg(errp, "Error reading dirty bitmap");
return -EINVAL;
}
read_ramblock_fixed_ram(f, block, num_pages, bitmap);
/* Skip pages array */
qemu_set_offset(f, block->pages_offset + length, SEEK_SET);
return 0;
}
static int parse_ramblock(QEMUFile *f, RAMBlock *block, ram_addr_t length)
{
int ret = 0;
@@ -3797,6 +4044,16 @@ static int parse_ramblock(QEMUFile *f, RAMBlock *block, ram_addr_t length)
assert(block);
if (migrate_fixed_ram()) {
Error *local_err = NULL;
ret = parse_ramblock_fixed_ram(f, block, length, &local_err);
if (local_err) {
error_report_err(local_err);
}
return ret;
}
if (!qemu_ram_is_migratable(block)) {
error_report("block %s should not be migrated !", block->idstr);
return -EINVAL;

View File

@@ -75,6 +75,7 @@ bool ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *rb, Error **errp);
bool ramblock_page_is_discarded(RAMBlock *rb, ram_addr_t start);
void postcopy_preempt_shutdown_file(MigrationState *s);
void *postcopy_preempt_thread(void *opaque);
void ramblock_set_shadow_bmap_atomic(RAMBlock *block, ram_addr_t offset);
/* ram cache */
int colo_init_ram_cache(void);

View File

@@ -245,6 +245,7 @@ static bool should_validate_capability(int capability)
/* Validate only new capabilities to keep compatibility. */
switch (capability) {
case MIGRATION_CAPABILITY_X_IGNORE_SHARED:
case MIGRATION_CAPABILITY_FIXED_RAM:
return true;
default:
return false;

View File

@@ -173,9 +173,9 @@ static void monitor_fdset_cleanup(MonFdset *mon_fdset)
MonFdsetFd *mon_fdset_fd_next;
QLIST_FOREACH_SAFE(mon_fdset_fd, &mon_fdset->fds, next, mon_fdset_fd_next) {
if ((mon_fdset_fd->removed ||
(QLIST_EMPTY(&mon_fdset->dup_fds) && mon_refcount == 0)) &&
runstate_is_running()) {
if (mon_fdset_fd->removed ||
(QLIST_EMPTY(&mon_fdset->dup_fds) && mon_refcount == 0 &&
runstate_is_running())) {
close(mon_fdset_fd->fd);
g_free(mon_fdset_fd->opaque);
QLIST_REMOVE(mon_fdset_fd, next);
@@ -406,6 +406,23 @@ AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id,
return fdinfo;
}
static bool monitor_fdset_flags_match(int flags, int fd_flags)
{
bool match = false;
if ((flags & O_ACCMODE) == (fd_flags & O_ACCMODE)) {
match = true;
#ifdef O_DIRECT
if ((flags & O_DIRECT) != (fd_flags & O_DIRECT)) {
match = false;
}
#endif
}
return match;
}
int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags)
{
#ifdef _WIN32
@@ -431,7 +448,7 @@ int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags)
return -1;
}
if ((flags & O_ACCMODE) == (mon_fd_flags & O_ACCMODE)) {
if (monitor_fdset_flags_match(flags, mon_fd_flags)) {
fd = mon_fdset_fd->fd;
break;
}

View File

@@ -531,6 +531,10 @@
# and can result in more stable read performance. Requires KVM
# with accelerator property "dirty-ring-size" set. (Since 8.1)
#
# @fixed-ram: Migrate using fixed offsets for each RAM page. Requires
# a migration URI that supports seeking, such as a file. (since
# 8.2)
#
# Features:
#
# @deprecated: Member @block is deprecated. Use blockdev-mirror with
@@ -555,7 +559,7 @@
{ 'name': 'x-ignore-shared', 'features': [ 'unstable' ] },
'validate-uuid', 'background-snapshot',
'zero-copy-send', 'postcopy-preempt', 'switchover-ack',
'dirty-limit'] }
'dirty-limit', 'fixed-ram'] }
##
# @MigrationCapabilityStatus:
@@ -874,6 +878,9 @@
# @mode: Migration mode. See description in @MigMode. Default is 'normal'.
# (Since 8.2)
#
# @direct-io: Open migration files with O_DIRECT when possible. Not
# all migration transports support this. (since 8.1)
#
# Features:
#
# @deprecated: Member @block-incremental is deprecated. Use
@@ -907,7 +914,8 @@
'block-bitmap-mapping',
{ 'name': 'x-vcpu-dirty-limit-period', 'features': ['unstable'] },
'vcpu-dirty-limit',
'mode'] }
'mode',
'direct-io'] }
##
# @MigrateSetParameters:
@@ -1062,6 +1070,9 @@
# @mode: Migration mode. See description in @MigMode. Default is 'normal'.
# (Since 8.2)
#
# @direct-io: Open migration files with O_DIRECT when possible. Not
# all migration transports support this. (since 8.1)
#
# Features:
#
# @deprecated: Member @block-incremental is deprecated. Use
@@ -1115,7 +1126,8 @@
'*x-vcpu-dirty-limit-period': { 'type': 'uint64',
'features': [ 'unstable' ] },
'*vcpu-dirty-limit': 'uint64',
'*mode': 'MigMode'} }
'*mode': 'MigMode',
'*direct-io': 'bool' } }
##
# @migrate-set-parameters:
@@ -1290,6 +1302,9 @@
# @mode: Migration mode. See description in @MigMode. Default is 'normal'.
# (Since 8.2)
#
# @direct-io: Open migration files with O_DIRECT when possible. Not
# all migration transports support this. (since 8.1)
#
# Features:
#
# @deprecated: Member @block-incremental is deprecated. Use
@@ -1340,7 +1355,8 @@
'*x-vcpu-dirty-limit-period': { 'type': 'uint64',
'features': [ 'unstable' ] },
'*vcpu-dirty-limit': 'uint64',
'*mode': 'MigMode'} }
'*mode': 'MigMode',
'*direct-io': 'bool' } }
##
# @query-migrate-parameters:

View File

@@ -118,6 +118,12 @@ void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...)
rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
args);
if (!qdict_haskey(rsp, "return")) {
g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(rsp), true);
g_test_message("%s", s->str);
}
g_assert(qdict_haskey(rsp, "return"));
qobject_unref(rsp);
@@ -292,3 +298,77 @@ char *resolve_machine_version(const char *alias, const char *var1,
return find_common_machine_version(machine_name, var1, var2);
}
#ifdef O_DIRECT
/*
* Probe for O_DIRECT support on the filesystem. Since this is used
* for tests, be conservative, if anything fails, assume it's
* unsupported.
*/
bool probe_o_direct_support(const char *tmpfs)
{
g_autofree char *filename = g_strdup_printf("%s/probe-o-direct", tmpfs);
int fd, flags = O_CREAT | O_RDWR | O_TRUNC | O_DIRECT;
void *buf;
ssize_t ret, len;
uint64_t offset;
fd = open(filename, flags, 0660);
if (fd < 0) {
unlink(filename);
return false;
}
/*
* Assuming 4k should be enough to satisfy O_DIRECT alignment
* requirements. The migration code uses 1M to be conservative.
*/
len = 0x100000;
offset = 0x100000;
buf = aligned_alloc(len, len);
g_assert(buf);
ret = pwrite(fd, buf, len, offset);
unlink(filename);
g_free(buf);
if (ret < 0) {
return false;
}
return true;
}
#endif
typedef struct {
char *name;
void (*func)(void);
} MigrationTest;
static void migration_test_destroy(gpointer data)
{
MigrationTest *test = (MigrationTest *)data;
g_free(test->name);
g_free(test);
}
static void migration_test_wrapper(const void *data)
{
MigrationTest *test = (MigrationTest *)data;
g_test_message("Running /%s%s", qtest_get_arch(), test->name);
test->func();
}
void migration_test_add(const char *path, void (*fn)(void))
{
MigrationTest *test = g_new0(MigrationTest, 1);
test->func = fn;
test->name = g_strdup(path);
qtest_add_data_func_full(path, test, migration_test_wrapper,
migration_test_destroy);
}

View File

@@ -47,4 +47,6 @@ char *find_common_machine_version(const char *mtype, const char *var1,
const char *var2);
char *resolve_machine_version(const char *alias, const char *var1,
const char *var2);
bool probe_o_direct_support(const char *tmpfs);
void migration_test_add(const char *path, void (*fn)(void));
#endif /* MIGRATION_HELPERS_H */

View File

@@ -2135,6 +2135,14 @@ static void *test_mode_reboot_start(QTestState *from, QTestState *to)
return NULL;
}
static void *migrate_fixed_ram_start(QTestState *from, QTestState *to)
{
migrate_set_capability(from, "fixed-ram", true);
migrate_set_capability(to, "fixed-ram", true);
return NULL;
}
static void test_mode_reboot(void)
{
g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs,
@@ -2149,6 +2157,181 @@ static void test_mode_reboot(void)
test_file_common(&args, true);
}
static void test_precopy_file_fixed_ram_live(void)
{
g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs,
FILE_TEST_FILENAME);
MigrateCommon args = {
.connect_uri = uri,
.listen_uri = "defer",
.start_hook = migrate_fixed_ram_start,
};
test_file_common(&args, false);
}
static void test_precopy_file_fixed_ram(void)
{
g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs,
FILE_TEST_FILENAME);
MigrateCommon args = {
.connect_uri = uri,
.listen_uri = "defer",
.start_hook = migrate_fixed_ram_start,
};
test_file_common(&args, true);
}
static void *migrate_multifd_fixed_ram_start(QTestState *from, QTestState *to)
{
migrate_fixed_ram_start(from, to);
migrate_set_parameter_int(from, "multifd-channels", 4);
migrate_set_parameter_int(to, "multifd-channels", 4);
migrate_set_capability(from, "multifd", true);
migrate_set_capability(to, "multifd", true);
return NULL;
}
static void test_multifd_file_fixed_ram_live(void)
{
g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs,
FILE_TEST_FILENAME);
MigrateCommon args = {
.connect_uri = uri,
.listen_uri = "defer",
.start_hook = migrate_multifd_fixed_ram_start,
};
test_file_common(&args, false);
}
static void test_multifd_file_fixed_ram(void)
{
g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs,
FILE_TEST_FILENAME);
MigrateCommon args = {
.connect_uri = uri,
.listen_uri = "defer",
.start_hook = migrate_multifd_fixed_ram_start,
};
test_file_common(&args, true);
}
#ifdef O_DIRECT
static void *migrate_multifd_fixed_ram_dio_start(QTestState *from, QTestState *to)
{
migrate_multifd_fixed_ram_start(from, to);
migrate_set_parameter_bool(from, "direct-io", true);
migrate_set_parameter_bool(to, "direct-io", true);
return NULL;
}
static void test_multifd_file_fixed_ram_dio(void)
{
g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs,
FILE_TEST_FILENAME);
MigrateCommon args = {
.connect_uri = uri,
.listen_uri = "defer",
.start_hook = migrate_multifd_fixed_ram_dio_start,
};
if (!probe_o_direct_support(tmpfs)) {
g_test_skip("Filesystem does not support O_DIRECT");
return;
}
test_file_common(&args, true);
}
static void migrate_multifd_fixed_ram_fdset_dio_end(QTestState *from,
QTestState *to,
void *opaque)
{
QDict *resp;
QList *fdsets;
/*
* Check that we removed the fdsets after migration, otherwise a
* second migration would fail due to too many fdsets.
*/
resp = qtest_qmp(from, "{'execute': 'query-fdsets', "
"'arguments': {}}");
g_assert(qdict_haskey(resp, "return"));
fdsets = qdict_get_qlist(resp, "return");
g_assert(fdsets && qlist_empty(fdsets));
}
#endif /* O_DIRECT */
static void *migrate_multifd_fixed_ram_fdset(QTestState *from, QTestState *to)
{
g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME);
int fds[3];
int src_flags = O_CREAT|O_WRONLY;
int dst_flags = O_CREAT|O_RDONLY;
/* main outgoing channel: no O_DIRECT */
fds[0] = open(file, src_flags, 0660);
assert(fds[0] != -1);
#ifdef O_DIRECT
src_flags |= O_DIRECT;
#endif
/* secondary outgoing channels */
fds[1] = open(file, src_flags, 0660);
assert(fds[1] != -1);
qtest_qmp_fds_assert_success(from, &fds[0], 1, "{'execute': 'add-fd', "
"'arguments': {'fdset-id': 1}}");
qtest_qmp_fds_assert_success(from, &fds[1], 1, "{'execute': 'add-fd', "
"'arguments': {'fdset-id': 1}}");
/* incoming channel */
fds[2] = open(file, dst_flags, 0660);
assert(fds[2] != -1);
qtest_qmp_fds_assert_success(to, &fds[2], 1, "{'execute': 'add-fd', "
"'arguments': {'fdset-id': 1}}");
#ifdef O_DIRECT
migrate_multifd_fixed_ram_dio_start(from, to);
#else
migrate_multifd_fixed_ram_start(from, to);
#endif
return NULL;
}
static void test_multifd_file_fixed_ram_fdset(void)
{
g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=0x100");
MigrateCommon args = {
.connect_uri = uri,
.listen_uri = "defer",
.start_hook = migrate_multifd_fixed_ram_fdset,
#ifdef O_DIRECT
.finish_hook = migrate_multifd_fixed_ram_fdset_dio_end,
#endif
};
if (!probe_o_direct_support(tmpfs)) {
g_test_skip("Filesystem does not support O_DIRECT");
return;
}
test_file_common(&args, true);
}
static void test_precopy_tcp_plain(void)
{
MigrateCommon args = {
@@ -3339,49 +3522,52 @@ int main(int argc, char **argv)
module_call_init(MODULE_INIT_QOM);
if (has_uffd) {
qtest_add_func("/migration/postcopy/plain", test_postcopy);
qtest_add_func("/migration/postcopy/recovery/plain",
migration_test_add("/migration/postcopy/plain", test_postcopy);
migration_test_add("/migration/postcopy/recovery/plain",
test_postcopy_recovery);
qtest_add_func("/migration/postcopy/preempt/plain", test_postcopy_preempt);
qtest_add_func("/migration/postcopy/preempt/recovery/plain",
migration_test_add("/migration/postcopy/preempt/plain",
test_postcopy_preempt);
migration_test_add("/migration/postcopy/preempt/recovery/plain",
test_postcopy_preempt_recovery);
if (getenv("QEMU_TEST_FLAKY_TESTS")) {
qtest_add_func("/migration/postcopy/compress/plain",
migration_test_add("/migration/postcopy/compress/plain",
test_postcopy_compress);
qtest_add_func("/migration/postcopy/recovery/compress/plain",
migration_test_add("/migration/postcopy/recovery/compress/plain",
test_postcopy_recovery_compress);
}
#ifndef _WIN32
qtest_add_func("/migration/postcopy/recovery/double-failures",
migration_test_add("/migration/postcopy/recovery/double-failures",
test_postcopy_recovery_double_fail);
#endif /* _WIN32 */
}
qtest_add_func("/migration/bad_dest", test_baddest);
migration_test_add("/migration/bad_dest", test_baddest);
#ifndef _WIN32
if (!g_str_equal(arch, "s390x")) {
qtest_add_func("/migration/analyze-script", test_analyze_script);
migration_test_add("/migration/analyze-script", test_analyze_script);
}
#endif
qtest_add_func("/migration/precopy/unix/plain", test_precopy_unix_plain);
qtest_add_func("/migration/precopy/unix/xbzrle", test_precopy_unix_xbzrle);
migration_test_add("/migration/precopy/unix/plain",
test_precopy_unix_plain);
migration_test_add("/migration/precopy/unix/xbzrle",
test_precopy_unix_xbzrle);
/*
* Compression fails from time to time.
* Put test here but don't enable it until everything is fixed.
*/
if (getenv("QEMU_TEST_FLAKY_TESTS")) {
qtest_add_func("/migration/precopy/unix/compress/wait",
migration_test_add("/migration/precopy/unix/compress/wait",
test_precopy_unix_compress);
qtest_add_func("/migration/precopy/unix/compress/nowait",
migration_test_add("/migration/precopy/unix/compress/nowait",
test_precopy_unix_compress_nowait);
}
qtest_add_func("/migration/precopy/file",
migration_test_add("/migration/precopy/file",
test_precopy_file);
qtest_add_func("/migration/precopy/file/offset",
migration_test_add("/migration/precopy/file/offset",
test_precopy_file_offset);
qtest_add_func("/migration/precopy/file/offset/bad",
migration_test_add("/migration/precopy/file/offset/bad",
test_precopy_file_offset_bad);
/*
@@ -3389,11 +3575,28 @@ int main(int argc, char **argv)
* Don't run this test until we find a workaround.
*/
if (getenv("QEMU_TEST_FLAKY_TESTS")) {
qtest_add_func("/migration/mode/reboot", test_mode_reboot);
migration_test_add("/migration/mode/reboot", test_mode_reboot);
}
migration_test_add("/migration/precopy/file/fixed-ram",
test_precopy_file_fixed_ram);
migration_test_add("/migration/precopy/file/fixed-ram/live",
test_precopy_file_fixed_ram_live);
migration_test_add("/migration/multifd/file/fixed-ram",
test_multifd_file_fixed_ram);
migration_test_add("/migration/multifd/file/fixed-ram/live",
test_multifd_file_fixed_ram_live);
#ifdef O_DIRECT
migration_test_add("/migration/multifd/file/fixed-ram/dio",
test_multifd_file_fixed_ram_dio);
#endif
migration_test_add("/migration/multifd/file/fixed-ram/fdset",
test_multifd_file_fixed_ram_fdset);
#ifdef CONFIG_GNUTLS
qtest_add_func("/migration/precopy/unix/tls/psk",
migration_test_add("/migration/precopy/unix/tls/psk",
test_precopy_unix_tls_psk);
if (has_uffd) {
@@ -3402,109 +3605,111 @@ int main(int argc, char **argv)
* channels are tested under precopy. Here what we want to test is the
* general postcopy path that has TLS channel enabled.
*/
qtest_add_func("/migration/postcopy/tls/psk", test_postcopy_tls_psk);
qtest_add_func("/migration/postcopy/recovery/tls/psk",
migration_test_add("/migration/postcopy/tls/psk",
test_postcopy_tls_psk);
migration_test_add("/migration/postcopy/recovery/tls/psk",
test_postcopy_recovery_tls_psk);
qtest_add_func("/migration/postcopy/preempt/tls/psk",
migration_test_add("/migration/postcopy/preempt/tls/psk",
test_postcopy_preempt_tls_psk);
qtest_add_func("/migration/postcopy/preempt/recovery/tls/psk",
migration_test_add("/migration/postcopy/preempt/recovery/tls/psk",
test_postcopy_preempt_all);
}
#ifdef CONFIG_TASN1
qtest_add_func("/migration/precopy/unix/tls/x509/default-host",
migration_test_add("/migration/precopy/unix/tls/x509/default-host",
test_precopy_unix_tls_x509_default_host);
qtest_add_func("/migration/precopy/unix/tls/x509/override-host",
migration_test_add("/migration/precopy/unix/tls/x509/override-host",
test_precopy_unix_tls_x509_override_host);
#endif /* CONFIG_TASN1 */
#endif /* CONFIG_GNUTLS */
qtest_add_func("/migration/precopy/tcp/plain", test_precopy_tcp_plain);
migration_test_add("/migration/precopy/tcp/plain", test_precopy_tcp_plain);
qtest_add_func("/migration/precopy/tcp/plain/switchover-ack",
migration_test_add("/migration/precopy/tcp/plain/switchover-ack",
test_precopy_tcp_switchover_ack);
#ifdef CONFIG_GNUTLS
qtest_add_func("/migration/precopy/tcp/tls/psk/match",
migration_test_add("/migration/precopy/tcp/tls/psk/match",
test_precopy_tcp_tls_psk_match);
qtest_add_func("/migration/precopy/tcp/tls/psk/mismatch",
migration_test_add("/migration/precopy/tcp/tls/psk/mismatch",
test_precopy_tcp_tls_psk_mismatch);
#ifdef CONFIG_TASN1
qtest_add_func("/migration/precopy/tcp/tls/x509/default-host",
migration_test_add("/migration/precopy/tcp/tls/x509/default-host",
test_precopy_tcp_tls_x509_default_host);
qtest_add_func("/migration/precopy/tcp/tls/x509/override-host",
migration_test_add("/migration/precopy/tcp/tls/x509/override-host",
test_precopy_tcp_tls_x509_override_host);
qtest_add_func("/migration/precopy/tcp/tls/x509/mismatch-host",
migration_test_add("/migration/precopy/tcp/tls/x509/mismatch-host",
test_precopy_tcp_tls_x509_mismatch_host);
qtest_add_func("/migration/precopy/tcp/tls/x509/friendly-client",
migration_test_add("/migration/precopy/tcp/tls/x509/friendly-client",
test_precopy_tcp_tls_x509_friendly_client);
qtest_add_func("/migration/precopy/tcp/tls/x509/hostile-client",
migration_test_add("/migration/precopy/tcp/tls/x509/hostile-client",
test_precopy_tcp_tls_x509_hostile_client);
qtest_add_func("/migration/precopy/tcp/tls/x509/allow-anon-client",
migration_test_add("/migration/precopy/tcp/tls/x509/allow-anon-client",
test_precopy_tcp_tls_x509_allow_anon_client);
qtest_add_func("/migration/precopy/tcp/tls/x509/reject-anon-client",
migration_test_add("/migration/precopy/tcp/tls/x509/reject-anon-client",
test_precopy_tcp_tls_x509_reject_anon_client);
#endif /* CONFIG_TASN1 */
#endif /* CONFIG_GNUTLS */
/* qtest_add_func("/migration/ignore_shared", test_ignore_shared); */
/* migration_test_add("/migration/ignore_shared", test_ignore_shared); */
#ifndef _WIN32
qtest_add_func("/migration/fd_proto", test_migrate_fd_proto);
migration_test_add("/migration/fd_proto", test_migrate_fd_proto);
#endif
qtest_add_func("/migration/validate_uuid", test_validate_uuid);
qtest_add_func("/migration/validate_uuid_error", test_validate_uuid_error);
qtest_add_func("/migration/validate_uuid_src_not_set",
migration_test_add("/migration/validate_uuid", test_validate_uuid);
migration_test_add("/migration/validate_uuid_error",
test_validate_uuid_error);
migration_test_add("/migration/validate_uuid_src_not_set",
test_validate_uuid_src_not_set);
qtest_add_func("/migration/validate_uuid_dst_not_set",
migration_test_add("/migration/validate_uuid_dst_not_set",
test_validate_uuid_dst_not_set);
/*
* See explanation why this test is slow on function definition
*/
if (g_test_slow()) {
qtest_add_func("/migration/auto_converge", test_migrate_auto_converge);
migration_test_add("/migration/auto_converge",
test_migrate_auto_converge);
if (g_str_equal(arch, "x86_64") &&
has_kvm && kvm_dirty_ring_supported()) {
qtest_add_func("/migration/dirty_limit", test_migrate_dirty_limit);
migration_test_add("/migration/dirty_limit",
test_migrate_dirty_limit);
}
}
qtest_add_func("/migration/multifd/tcp/plain/none",
migration_test_add("/migration/multifd/tcp/plain/none",
test_multifd_tcp_none);
/*
* This test is flaky and sometimes fails in CI and otherwise:
* don't run unless user opts in via environment variable.
*/
if (getenv("QEMU_TEST_FLAKY_TESTS")) {
qtest_add_func("/migration/multifd/tcp/plain/cancel",
migration_test_add("/migration/multifd/tcp/plain/cancel",
test_multifd_tcp_cancel);
}
qtest_add_func("/migration/multifd/tcp/plain/zlib",
migration_test_add("/migration/multifd/tcp/plain/zlib",
test_multifd_tcp_zlib);
#ifdef CONFIG_ZSTD
qtest_add_func("/migration/multifd/tcp/plain/zstd",
migration_test_add("/migration/multifd/tcp/plain/zstd",
test_multifd_tcp_zstd);
#endif
#ifdef CONFIG_GNUTLS
qtest_add_func("/migration/multifd/tcp/tls/psk/match",
migration_test_add("/migration/multifd/tcp/tls/psk/match",
test_multifd_tcp_tls_psk_match);
qtest_add_func("/migration/multifd/tcp/tls/psk/mismatch",
migration_test_add("/migration/multifd/tcp/tls/psk/mismatch",
test_multifd_tcp_tls_psk_mismatch);
#ifdef CONFIG_TASN1
qtest_add_func("/migration/multifd/tcp/tls/x509/default-host",
migration_test_add("/migration/multifd/tcp/tls/x509/default-host",
test_multifd_tcp_tls_x509_default_host);
qtest_add_func("/migration/multifd/tcp/tls/x509/override-host",
migration_test_add("/migration/multifd/tcp/tls/x509/override-host",
test_multifd_tcp_tls_x509_override_host);
qtest_add_func("/migration/multifd/tcp/tls/x509/mismatch-host",
migration_test_add("/migration/multifd/tcp/tls/x509/mismatch-host",
test_multifd_tcp_tls_x509_mismatch_host);
qtest_add_func("/migration/multifd/tcp/tls/x509/allow-anon-client",
migration_test_add("/migration/multifd/tcp/tls/x509/allow-anon-client",
test_multifd_tcp_tls_x509_allow_anon_client);
qtest_add_func("/migration/multifd/tcp/tls/x509/reject-anon-client",
migration_test_add("/migration/multifd/tcp/tls/x509/reject-anon-client",
test_multifd_tcp_tls_x509_reject_anon_client);
#endif /* CONFIG_TASN1 */
#endif /* CONFIG_GNUTLS */
if (g_str_equal(arch, "x86_64") && has_kvm && kvm_dirty_ring_supported()) {
qtest_add_func("/migration/dirty_ring",
migration_test_add("/migration/dirty_ring",
test_precopy_unix_dirty_ring);
qtest_add_func("/migration/vcpu_dirty_limit",
migration_test_add("/migration/vcpu_dirty_limit",
test_vcpu_dirty_limit);
}

View File

@@ -277,6 +277,15 @@ int qemu_lock_fd_test(int fd, int64_t start, int64_t len, bool exclusive)
}
#endif
bool qemu_has_direct_io(void)
{
#ifdef O_DIRECT
return true;
#else
return false;
#endif
}
static int qemu_open_cloexec(const char *name, int flags, mode_t mode)
{
int ret;