Migration pull 2019-08-15

Marcel's vmxnet3 live migraiton fix (that breaks vmxnet3 compatibility
 but makes it work)
 
 Error description improvements from Yury.
 
 Multifd fixes from Ivan and Juan.
 
 A load of small cleanups from Wei.
 
 A small cleanup from Marc-André for a future patch.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEERfXHG0oMt/uXep+pBRYzHrxb/ecFAl1UTEcACgkQBRYzHrxb
 /ecSxg//bH7hrYMSYR2r4NxAaiR7E/iYz3cd6+zSs15rp2UKDP+sfAj0ovtiYsIu
 dEHlLiZdS9jBobJ7olebEng0LFgLtRyjt79Toa91aY7LEH1PkPXBl+kH6I52cKMr
 Ee9AQWDHwiEB3sfGkF9Dogxxeu/VeYgJrhXBgm5+cFRRmUvEr26fzVzPvk7WVzRV
 MtzGEjpXGCZAVBCz9EnDuSaBPI5cmFCqZUDg1itMX7HU2fRlwByMRVwGI/uIEPR6
 j7U3qShyVeLVW+ctYeA1cyyb1UDKRw+D3ahlMIEID/4MxWyRHf6K5h7xUf6x7Wh4
 laI+Cm1oxTmPvDkwkGhh/UO3TyNSIPg9uS+XSh/7R3evinJECW9MXFLL7H1fLjNQ
 K6moR2nUJxYSOqj3AXMRvMGPpeht437fR+7QwCOHZ0Xj/+wHxLTWP5L56qZnOlhX
 /Fl5vt9bZ3A9T/HwdU6RQ8XHlHdbYF52FYiWmfC7WkCY/TLg9s2RDb6kLCf6Go1f
 BriuA5ISZ34OfOJOIGNpif8ycvWpQo00vDSqahllbqUqAWZW24HxdsLlzbq9XZU8
 1RTnxKPuMJW9RApCVYVkNsH8l7pXhCJXkvsRQwDEs0VW8h7JAGn+ma1gO4ODmocL
 FwF9ZydOFLpsrvjNBw5DEX6Pogj+jtJq0J1SCjknK7/SmO7Zu1U=
 =METp
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/dgilbert/tags/pull-migration-20190814a' into staging

Migration pull 2019-08-15

Marcel's vmxnet3 live migraiton fix (that breaks vmxnet3 compatibility
but makes it work)

Error description improvements from Yury.

Multifd fixes from Ivan and Juan.

A load of small cleanups from Wei.

A small cleanup from Marc-André for a future patch.

# gpg: Signature made Wed 14 Aug 2019 19:00:39 BST
# gpg:                using RSA key 45F5C71B4A0CB7FB977A9FA90516331EBC5BFDE7
# gpg: Good signature from "Dr. David Alan Gilbert (RH2) <dgilbert@redhat.com>" [full]
# Primary key fingerprint: 45F5 C71B 4A0C B7FB 977A  9FA9 0516 331E BC5B FDE7

* remotes/dgilbert/tags/pull-migration-20190814a: (33 commits)
  migration: add some multifd traces
  migration: Make global sem_sync semaphore by channel
  migration: Add traces for multifd terminate threads
  qemu-file: move qemu_{get,put}_counted_string() declarations
  migration/postcopy: use mis->bh instead of allocating a QEMUBH
  migration: rename migration_bitmap_sync_range to ramblock_sync_dirty_bitmap
  migration: update ram_counters for multifd sync packet
  migration: add speed limit for multifd migration
  migration: add qemu_file_update_transfer interface
  migration: always initialise ram_counters for a new migration
  migration: remove unused field bytes_xfer
  hmp: Remove migration capabilities from "info migrate"
  migration/postcopy: use QEMU_IS_ALIGNED to replace host_offset
  migration/postcopy: simplify calculation of run_start and fixup_start_addr
  migration/postcopy: make PostcopyDiscardState a static variable
  migration: extract ram_load_precopy
  migration: return -EINVAL directly when version_id mismatch
  migration: equation is more proper than and to check LOADVM_QUIT
  migration: just pass RAMBlock is enough
  migration: use migration_in_postcopy() to check POSTCOPY_ACTIVE
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-08-16 12:00:18 +01:00
commit 27608c7c66
14 changed files with 350 additions and 330 deletions

View file

@ -2141,21 +2141,6 @@ vmxnet3_cleanup_msi(VMXNET3State *s)
msi_uninit(d); msi_uninit(d);
} }
static void
vmxnet3_msix_save(QEMUFile *f, void *opaque)
{
PCIDevice *d = PCI_DEVICE(opaque);
msix_save(d, f);
}
static int
vmxnet3_msix_load(QEMUFile *f, void *opaque, int version_id)
{
PCIDevice *d = PCI_DEVICE(opaque);
msix_load(d, f);
return 0;
}
static const MemoryRegionOps b0_ops = { static const MemoryRegionOps b0_ops = {
.read = vmxnet3_io_bar0_read, .read = vmxnet3_io_bar0_read,
.write = vmxnet3_io_bar0_write, .write = vmxnet3_io_bar0_write,
@ -2176,11 +2161,6 @@ static const MemoryRegionOps b1_ops = {
}, },
}; };
static SaveVMHandlers savevm_vmxnet3_msix = {
.save_state = vmxnet3_msix_save,
.load_state = vmxnet3_msix_load,
};
static uint64_t vmxnet3_device_serial_num(VMXNET3State *s) static uint64_t vmxnet3_device_serial_num(VMXNET3State *s)
{ {
uint64_t dsn_payload; uint64_t dsn_payload;
@ -2203,7 +2183,6 @@ static uint64_t vmxnet3_device_serial_num(VMXNET3State *s)
static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp) static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp)
{ {
DeviceState *dev = DEVICE(pci_dev);
VMXNET3State *s = VMXNET3(pci_dev); VMXNET3State *s = VMXNET3(pci_dev);
int ret; int ret;
@ -2249,8 +2228,6 @@ static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp)
pcie_dev_ser_num_init(pci_dev, VMXNET3_DSN_OFFSET, pcie_dev_ser_num_init(pci_dev, VMXNET3_DSN_OFFSET,
vmxnet3_device_serial_num(s)); vmxnet3_device_serial_num(s));
} }
register_savevm_live(dev, "vmxnet3-msix", -1, 1, &savevm_vmxnet3_msix, s);
} }
static void vmxnet3_instance_init(Object *obj) static void vmxnet3_instance_init(Object *obj)
@ -2440,29 +2417,6 @@ static const VMStateDescription vmstate_vmxnet3_int_state = {
} }
}; };
static bool vmxnet3_vmstate_need_pcie_device(void *opaque)
{
VMXNET3State *s = VMXNET3(opaque);
return !(s->compat_flags & VMXNET3_COMPAT_FLAG_DISABLE_PCIE);
}
static bool vmxnet3_vmstate_test_pci_device(void *opaque, int version_id)
{
return !vmxnet3_vmstate_need_pcie_device(opaque);
}
static const VMStateDescription vmstate_vmxnet3_pcie_device = {
.name = "vmxnet3/pcie",
.version_id = 1,
.minimum_version_id = 1,
.needed = vmxnet3_vmstate_need_pcie_device,
.fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(parent_obj, VMXNET3State),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_vmxnet3 = { static const VMStateDescription vmstate_vmxnet3 = {
.name = "vmxnet3", .name = "vmxnet3",
.version_id = 1, .version_id = 1,
@ -2470,9 +2424,8 @@ static const VMStateDescription vmstate_vmxnet3 = {
.pre_save = vmxnet3_pre_save, .pre_save = vmxnet3_pre_save,
.post_load = vmxnet3_post_load, .post_load = vmxnet3_post_load,
.fields = (VMStateField[]) { .fields = (VMStateField[]) {
VMSTATE_STRUCT_TEST(parent_obj, VMXNET3State, VMSTATE_PCI_DEVICE(parent_obj, VMXNET3State),
vmxnet3_vmstate_test_pci_device, 0, VMSTATE_MSIX(parent_obj, VMXNET3State),
vmstate_pci_device, PCIDevice),
VMSTATE_BOOL(rx_packets_compound, VMXNET3State), VMSTATE_BOOL(rx_packets_compound, VMXNET3State),
VMSTATE_BOOL(rx_vlan_stripping, VMXNET3State), VMSTATE_BOOL(rx_vlan_stripping, VMXNET3State),
VMSTATE_BOOL(lro_supported, VMXNET3State), VMSTATE_BOOL(lro_supported, VMXNET3State),
@ -2508,7 +2461,6 @@ static const VMStateDescription vmstate_vmxnet3 = {
}, },
.subsections = (const VMStateDescription*[]) { .subsections = (const VMStateDescription*[]) {
&vmxstate_vmxnet3_mcast_list, &vmxstate_vmxnet3_mcast_list,
&vmstate_vmxnet3_pcie_device,
NULL NULL
} }
}; };

View file

@ -161,6 +161,10 @@ static inline void qemu_get_sbe64s(QEMUFile *f, int64_t *pv)
qemu_get_be64s(f, (uint64_t *)pv); qemu_get_be64s(f, (uint64_t *)pv);
} }
size_t qemu_get_counted_string(QEMUFile *f, char buf[256]);
void qemu_put_counted_string(QEMUFile *f, const char *name);
int qemu_file_rate_limit(QEMUFile *f); int qemu_file_rate_limit(QEMUFile *f);
#endif #endif

View file

@ -823,6 +823,25 @@ bool migration_is_setup_or_active(int state)
} }
} }
static void populate_time_info(MigrationInfo *info, MigrationState *s)
{
info->has_status = true;
info->has_setup_time = true;
info->setup_time = s->setup_time;
if (s->state == MIGRATION_STATUS_COMPLETED) {
info->has_total_time = true;
info->total_time = s->total_time;
info->has_downtime = true;
info->downtime = s->downtime;
} else {
info->has_total_time = true;
info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) -
s->start_time;
info->has_expected_downtime = true;
info->expected_downtime = s->expected_downtime;
}
}
static void populate_ram_info(MigrationInfo *info, MigrationState *s) static void populate_ram_info(MigrationInfo *info, MigrationState *s)
{ {
info->has_ram = true; info->has_ram = true;
@ -908,16 +927,8 @@ static void fill_source_migration_info(MigrationInfo *info)
case MIGRATION_STATUS_DEVICE: case MIGRATION_STATUS_DEVICE:
case MIGRATION_STATUS_POSTCOPY_PAUSED: case MIGRATION_STATUS_POSTCOPY_PAUSED:
case MIGRATION_STATUS_POSTCOPY_RECOVER: case MIGRATION_STATUS_POSTCOPY_RECOVER:
/* TODO add some postcopy stats */ /* TODO add some postcopy stats */
info->has_status = true; populate_time_info(info, s);
info->has_total_time = true;
info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
- s->start_time;
info->has_expected_downtime = true;
info->expected_downtime = s->expected_downtime;
info->has_setup_time = true;
info->setup_time = s->setup_time;
populate_ram_info(info, s); populate_ram_info(info, s);
populate_disk_info(info); populate_disk_info(info);
break; break;
@ -926,14 +937,7 @@ static void fill_source_migration_info(MigrationInfo *info)
/* TODO: display COLO specific information (checkpoint info etc.) */ /* TODO: display COLO specific information (checkpoint info etc.) */
break; break;
case MIGRATION_STATUS_COMPLETED: case MIGRATION_STATUS_COMPLETED:
info->has_status = true; populate_time_info(info, s);
info->has_total_time = true;
info->total_time = s->total_time;
info->has_downtime = true;
info->downtime = s->downtime;
info->has_setup_time = true;
info->setup_time = s->setup_time;
populate_ram_info(info, s); populate_ram_info(info, s);
break; break;
case MIGRATION_STATUS_FAILED: case MIGRATION_STATUS_FAILED:
@ -1695,7 +1699,6 @@ void migrate_init(MigrationState *s)
* parameters/capabilities that the user set, and * parameters/capabilities that the user set, and
* locks. * locks.
*/ */
s->bytes_xfer = 0;
s->cleanup_bh = 0; s->cleanup_bh = 0;
s->to_dst_file = NULL; s->to_dst_file = NULL;
s->state = MIGRATION_STATUS_NONE; s->state = MIGRATION_STATUS_NONE;
@ -1908,6 +1911,11 @@ static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc,
} }
migrate_init(s); migrate_init(s);
/*
* set ram_counters memory to zero for a
* new migration
*/
memset(&ram_counters, 0, sizeof(ram_counters));
return true; return true;
} }
@ -2963,6 +2971,7 @@ static MigThrError migration_detect_error(MigrationState *s)
{ {
int ret; int ret;
int state = s->state; int state = s->state;
Error *local_error = NULL;
if (state == MIGRATION_STATUS_CANCELLING || if (state == MIGRATION_STATUS_CANCELLING ||
state == MIGRATION_STATUS_CANCELLED) { state == MIGRATION_STATUS_CANCELLED) {
@ -2971,13 +2980,18 @@ static MigThrError migration_detect_error(MigrationState *s)
} }
/* Try to detect any file errors */ /* Try to detect any file errors */
ret = qemu_file_get_error(s->to_dst_file); ret = qemu_file_get_error_obj(s->to_dst_file, &local_error);
if (!ret) { if (!ret) {
/* Everything is fine */ /* Everything is fine */
assert(!local_error);
return MIG_THR_ERR_NONE; return MIG_THR_ERR_NONE;
} }
if (local_error) {
migrate_set_error(s, local_error);
error_free(local_error);
}
if (state == MIGRATION_STATUS_POSTCOPY_ACTIVE && ret == -EIO) { if (state == MIGRATION_STATUS_POSTCOPY_ACTIVE && ret == -EIO) {
/* /*
* For postcopy, we allow the network to be down for a * For postcopy, we allow the network to be down for a
@ -3025,6 +3039,17 @@ static void migration_calculate_complete(MigrationState *s)
} }
} }
static void update_iteration_initial_status(MigrationState *s)
{
/*
* Update these three fields at the same time to avoid mismatch info lead
* wrong speed calculation.
*/
s->iteration_start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
s->iteration_initial_bytes = migration_total_bytes(s);
s->iteration_initial_pages = ram_get_total_transferred_pages();
}
static void migration_update_counters(MigrationState *s, static void migration_update_counters(MigrationState *s,
int64_t current_time) int64_t current_time)
{ {
@ -3060,9 +3085,7 @@ static void migration_update_counters(MigrationState *s,
qemu_file_reset_rate_limit(s->to_dst_file); qemu_file_reset_rate_limit(s->to_dst_file);
s->iteration_start_time = current_time; update_iteration_initial_status(s);
s->iteration_initial_bytes = current_bytes;
s->iteration_initial_pages = ram_get_total_transferred_pages();
trace_migrate_transferred(transferred, time_spent, trace_migrate_transferred(transferred, time_spent,
bandwidth, s->threshold_size); bandwidth, s->threshold_size);
@ -3093,8 +3116,7 @@ static MigIterateState migration_iteration_run(MigrationState *s)
if (pending_size && pending_size >= s->threshold_size) { if (pending_size && pending_size >= s->threshold_size) {
/* Still a significant amount to transfer */ /* Still a significant amount to transfer */
if (migrate_postcopy() && !in_postcopy && if (!in_postcopy && pend_pre <= s->threshold_size &&
pend_pre <= s->threshold_size &&
atomic_read(&s->start_postcopy)) { atomic_read(&s->start_postcopy)) {
if (postcopy_start(s)) { if (postcopy_start(s)) {
error_report("%s: postcopy failed to start", __func__); error_report("%s: postcopy failed to start", __func__);
@ -3186,7 +3208,7 @@ static void *migration_thread(void *opaque)
rcu_register_thread(); rcu_register_thread();
object_ref(OBJECT(s)); object_ref(OBJECT(s));
s->iteration_start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); update_iteration_initial_status(s);
qemu_savevm_state_header(s->to_dst_file); qemu_savevm_state_header(s->to_dst_file);
@ -3251,8 +3273,7 @@ static void *migration_thread(void *opaque)
* the local variables. This is important to avoid * the local variables. This is important to avoid
* breaking transferred_bytes and bandwidth calculation * breaking transferred_bytes and bandwidth calculation
*/ */
s->iteration_start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); update_iteration_initial_status(s);
s->iteration_initial_bytes = 0;
} }
current_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); current_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);

View file

@ -132,7 +132,6 @@ struct MigrationState
DeviceState parent_obj; DeviceState parent_obj;
/*< public >*/ /*< public >*/
size_t bytes_xfer;
QemuThread thread; QemuThread thread;
QEMUBH *cleanup_bh; QEMUBH *cleanup_bh;
QEMUFile *to_dst_file; QEMUFile *to_dst_file;

View file

@ -1377,22 +1377,16 @@ void postcopy_fault_thread_notify(MigrationIncomingState *mis)
* asking to discard individual ranges. * asking to discard individual ranges.
* *
* @ms: The current migration state. * @ms: The current migration state.
* @offset: the bitmap offset of the named RAMBlock in the migration * @offset: the bitmap offset of the named RAMBlock in the migration bitmap.
* bitmap.
* @name: RAMBlock that discards will operate on. * @name: RAMBlock that discards will operate on.
*
* returns: a new PDS.
*/ */
PostcopyDiscardState *postcopy_discard_send_init(MigrationState *ms, static PostcopyDiscardState pds = {0};
const char *name) void postcopy_discard_send_init(MigrationState *ms, const char *name)
{ {
PostcopyDiscardState *res = g_malloc0(sizeof(PostcopyDiscardState)); pds.ramblock_name = name;
pds.cur_entry = 0;
if (res) { pds.nsentwords = 0;
res->ramblock_name = name; pds.nsentcmds = 0;
}
return res;
} }
/** /**
@ -1401,30 +1395,29 @@ PostcopyDiscardState *postcopy_discard_send_init(MigrationState *ms,
* be sent later. * be sent later.
* *
* @ms: Current migration state. * @ms: Current migration state.
* @pds: Structure initialised by postcopy_discard_send_init().
* @start,@length: a range of pages in the migration bitmap in the * @start,@length: a range of pages in the migration bitmap in the
* RAM block passed to postcopy_discard_send_init() (length=1 is one page) * RAM block passed to postcopy_discard_send_init() (length=1 is one page)
*/ */
void postcopy_discard_send_range(MigrationState *ms, PostcopyDiscardState *pds, void postcopy_discard_send_range(MigrationState *ms, unsigned long start,
unsigned long start, unsigned long length) unsigned long length)
{ {
size_t tp_size = qemu_target_page_size(); size_t tp_size = qemu_target_page_size();
/* Convert to byte offsets within the RAM block */ /* Convert to byte offsets within the RAM block */
pds->start_list[pds->cur_entry] = start * tp_size; pds.start_list[pds.cur_entry] = start * tp_size;
pds->length_list[pds->cur_entry] = length * tp_size; pds.length_list[pds.cur_entry] = length * tp_size;
trace_postcopy_discard_send_range(pds->ramblock_name, start, length); trace_postcopy_discard_send_range(pds.ramblock_name, start, length);
pds->cur_entry++; pds.cur_entry++;
pds->nsentwords++; pds.nsentwords++;
if (pds->cur_entry == MAX_DISCARDS_PER_COMMAND) { if (pds.cur_entry == MAX_DISCARDS_PER_COMMAND) {
/* Full set, ship it! */ /* Full set, ship it! */
qemu_savevm_send_postcopy_ram_discard(ms->to_dst_file, qemu_savevm_send_postcopy_ram_discard(ms->to_dst_file,
pds->ramblock_name, pds.ramblock_name,
pds->cur_entry, pds.cur_entry,
pds->start_list, pds.start_list,
pds->length_list); pds.length_list);
pds->nsentcmds++; pds.nsentcmds++;
pds->cur_entry = 0; pds.cur_entry = 0;
} }
} }
@ -1433,24 +1426,21 @@ void postcopy_discard_send_range(MigrationState *ms, PostcopyDiscardState *pds,
* bitmap code. Sends any outstanding discard messages, frees the PDS * bitmap code. Sends any outstanding discard messages, frees the PDS
* *
* @ms: Current migration state. * @ms: Current migration state.
* @pds: Structure initialised by postcopy_discard_send_init().
*/ */
void postcopy_discard_send_finish(MigrationState *ms, PostcopyDiscardState *pds) void postcopy_discard_send_finish(MigrationState *ms)
{ {
/* Anything unsent? */ /* Anything unsent? */
if (pds->cur_entry) { if (pds.cur_entry) {
qemu_savevm_send_postcopy_ram_discard(ms->to_dst_file, qemu_savevm_send_postcopy_ram_discard(ms->to_dst_file,
pds->ramblock_name, pds.ramblock_name,
pds->cur_entry, pds.cur_entry,
pds->start_list, pds.start_list,
pds->length_list); pds.length_list);
pds->nsentcmds++; pds.nsentcmds++;
} }
trace_postcopy_discard_send_finish(pds->ramblock_name, pds->nsentwords, trace_postcopy_discard_send_finish(pds.ramblock_name, pds.nsentwords,
pds->nsentcmds); pds.nsentcmds);
g_free(pds);
} }
/* /*

View file

@ -43,10 +43,8 @@ int postcopy_ram_prepare_discard(MigrationIncomingState *mis);
/* /*
* Called at the start of each RAMBlock by the bitmap code. * Called at the start of each RAMBlock by the bitmap code.
* Returns a new PDS
*/ */
PostcopyDiscardState *postcopy_discard_send_init(MigrationState *ms, void postcopy_discard_send_init(MigrationState *ms, const char *name);
const char *name);
/* /*
* Called by the bitmap code for each chunk to discard. * Called by the bitmap code for each chunk to discard.
@ -55,15 +53,14 @@ PostcopyDiscardState *postcopy_discard_send_init(MigrationState *ms,
* @start,@length: a range of pages in the migration bitmap in the * @start,@length: a range of pages in the migration bitmap in the
* RAM block passed to postcopy_discard_send_init() (length=1 is one page) * RAM block passed to postcopy_discard_send_init() (length=1 is one page)
*/ */
void postcopy_discard_send_range(MigrationState *ms, PostcopyDiscardState *pds, void postcopy_discard_send_range(MigrationState *ms, unsigned long start,
unsigned long start, unsigned long length); unsigned long length);
/* /*
* Called at the end of each RAMBlock by the bitmap code. * Called at the end of each RAMBlock by the bitmap code.
* Sends any outstanding discard messages, frees the PDS. * Sends any outstanding discard messages.
*/ */
void postcopy_discard_send_finish(MigrationState *ms, void postcopy_discard_send_finish(MigrationState *ms);
PostcopyDiscardState *pds);
/* /*
* Place a page (from) at (host) efficiently * Place a page (from) at (host) efficiently

View file

@ -33,7 +33,8 @@
static ssize_t channel_writev_buffer(void *opaque, static ssize_t channel_writev_buffer(void *opaque,
struct iovec *iov, struct iovec *iov,
int iovcnt, int iovcnt,
int64_t pos) int64_t pos,
Error **errp)
{ {
QIOChannel *ioc = QIO_CHANNEL(opaque); QIOChannel *ioc = QIO_CHANNEL(opaque);
ssize_t done = 0; ssize_t done = 0;
@ -47,7 +48,7 @@ static ssize_t channel_writev_buffer(void *opaque,
while (nlocal_iov > 0) { while (nlocal_iov > 0) {
ssize_t len; ssize_t len;
len = qio_channel_writev(ioc, local_iov, nlocal_iov, NULL); len = qio_channel_writev(ioc, local_iov, nlocal_iov, errp);
if (len == QIO_CHANNEL_ERR_BLOCK) { if (len == QIO_CHANNEL_ERR_BLOCK) {
if (qemu_in_coroutine()) { if (qemu_in_coroutine()) {
qio_channel_yield(ioc, G_IO_OUT); qio_channel_yield(ioc, G_IO_OUT);
@ -57,7 +58,6 @@ static ssize_t channel_writev_buffer(void *opaque,
continue; continue;
} }
if (len < 0) { if (len < 0) {
/* XXX handle Error objects */
done = -EIO; done = -EIO;
goto cleanup; goto cleanup;
} }
@ -75,13 +75,14 @@ static ssize_t channel_writev_buffer(void *opaque,
static ssize_t channel_get_buffer(void *opaque, static ssize_t channel_get_buffer(void *opaque,
uint8_t *buf, uint8_t *buf,
int64_t pos, int64_t pos,
size_t size) size_t size,
Error **errp)
{ {
QIOChannel *ioc = QIO_CHANNEL(opaque); QIOChannel *ioc = QIO_CHANNEL(opaque);
ssize_t ret; ssize_t ret;
do { do {
ret = qio_channel_read(ioc, (char *)buf, size, NULL); ret = qio_channel_read(ioc, (char *)buf, size, errp);
if (ret < 0) { if (ret < 0) {
if (ret == QIO_CHANNEL_ERR_BLOCK) { if (ret == QIO_CHANNEL_ERR_BLOCK) {
if (qemu_in_coroutine()) { if (qemu_in_coroutine()) {
@ -90,7 +91,6 @@ static ssize_t channel_get_buffer(void *opaque,
qio_channel_wait(ioc, G_IO_IN); qio_channel_wait(ioc, G_IO_IN);
} }
} else { } else {
/* XXX handle Error * object */
return -EIO; return -EIO;
} }
} }
@ -100,18 +100,20 @@ static ssize_t channel_get_buffer(void *opaque,
} }
static int channel_close(void *opaque) static int channel_close(void *opaque, Error **errp)
{ {
int ret;
QIOChannel *ioc = QIO_CHANNEL(opaque); QIOChannel *ioc = QIO_CHANNEL(opaque);
qio_channel_close(ioc, NULL); ret = qio_channel_close(ioc, errp);
object_unref(OBJECT(ioc)); object_unref(OBJECT(ioc));
return 0; return ret;
} }
static int channel_shutdown(void *opaque, static int channel_shutdown(void *opaque,
bool rd, bool rd,
bool wr) bool wr,
Error **errp)
{ {
QIOChannel *ioc = QIO_CHANNEL(opaque); QIOChannel *ioc = QIO_CHANNEL(opaque);
@ -125,8 +127,7 @@ static int channel_shutdown(void *opaque,
} else { } else {
mode = QIO_CHANNEL_SHUTDOWN_WRITE; mode = QIO_CHANNEL_SHUTDOWN_WRITE;
} }
if (qio_channel_shutdown(ioc, mode, NULL) < 0) { if (qio_channel_shutdown(ioc, mode, errp) < 0) {
/* XXX handler Error * object */
return -EIO; return -EIO;
} }
} }
@ -135,11 +136,12 @@ static int channel_shutdown(void *opaque,
static int channel_set_blocking(void *opaque, static int channel_set_blocking(void *opaque,
bool enabled) bool enabled,
Error **errp)
{ {
QIOChannel *ioc = QIO_CHANNEL(opaque); QIOChannel *ioc = QIO_CHANNEL(opaque);
if (qio_channel_set_blocking(ioc, enabled, NULL) < 0) { if (qio_channel_set_blocking(ioc, enabled, errp) < 0) {
return -1; return -1;
} }
return 0; return 0;

View file

@ -28,6 +28,7 @@
#include "migration.h" #include "migration.h"
#include "qemu-file.h" #include "qemu-file.h"
#include "trace.h" #include "trace.h"
#include "qapi/error.h"
#define IO_BUF_SIZE 32768 #define IO_BUF_SIZE 32768
#define MAX_IOV_SIZE MIN(IOV_MAX, 64) #define MAX_IOV_SIZE MIN(IOV_MAX, 64)
@ -51,6 +52,7 @@ struct QEMUFile {
unsigned int iovcnt; unsigned int iovcnt;
int last_error; int last_error;
Error *last_error_obj;
}; };
/* /*
@ -62,7 +64,7 @@ int qemu_file_shutdown(QEMUFile *f)
if (!f->ops->shut_down) { if (!f->ops->shut_down) {
return -ENOSYS; return -ENOSYS;
} }
return f->ops->shut_down(f->opaque, true, true); return f->ops->shut_down(f->opaque, true, true, NULL);
} }
/* /*
@ -106,6 +108,36 @@ void qemu_file_set_hooks(QEMUFile *f, const QEMUFileHooks *hooks)
f->hooks = hooks; f->hooks = hooks;
} }
/*
* Get last error for stream f with optional Error*
*
* Return negative error value if there has been an error on previous
* operations, return 0 if no error happened.
* Optional, it returns Error* in errp, but it may be NULL even if return value
* is not 0.
*
*/
int qemu_file_get_error_obj(QEMUFile *f, Error **errp)
{
if (errp) {
*errp = f->last_error_obj ? error_copy(f->last_error_obj) : NULL;
}
return f->last_error;
}
/*
* Set the last error for stream f with optional Error*
*/
void qemu_file_set_error_obj(QEMUFile *f, int ret, Error *err)
{
if (f->last_error == 0 && ret) {
f->last_error = ret;
error_propagate(&f->last_error_obj, err);
} else if (err) {
error_report_err(err);
}
}
/* /*
* Get last error for stream f * Get last error for stream f
* *
@ -115,14 +147,15 @@ void qemu_file_set_hooks(QEMUFile *f, const QEMUFileHooks *hooks)
*/ */
int qemu_file_get_error(QEMUFile *f) int qemu_file_get_error(QEMUFile *f)
{ {
return f->last_error; return qemu_file_get_error_obj(f, NULL);
} }
/*
* Set the last error for stream f
*/
void qemu_file_set_error(QEMUFile *f, int ret) void qemu_file_set_error(QEMUFile *f, int ret)
{ {
if (f->last_error == 0) { qemu_file_set_error_obj(f, ret, NULL);
f->last_error = ret;
}
} }
bool qemu_file_is_writable(QEMUFile *f) bool qemu_file_is_writable(QEMUFile *f)
@ -176,6 +209,7 @@ void qemu_fflush(QEMUFile *f)
{ {
ssize_t ret = 0; ssize_t ret = 0;
ssize_t expect = 0; ssize_t expect = 0;
Error *local_error = NULL;
if (!qemu_file_is_writable(f)) { if (!qemu_file_is_writable(f)) {
return; return;
@ -183,7 +217,8 @@ void qemu_fflush(QEMUFile *f)
if (f->iovcnt > 0) { if (f->iovcnt > 0) {
expect = iov_size(f->iov, f->iovcnt); expect = iov_size(f->iov, f->iovcnt);
ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt, f->pos); ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt, f->pos,
&local_error);
qemu_iovec_release_ram(f); qemu_iovec_release_ram(f);
} }
@ -195,7 +230,7 @@ void qemu_fflush(QEMUFile *f)
* data set we requested, so sanity check that. * data set we requested, so sanity check that.
*/ */
if (ret != expect) { if (ret != expect) {
qemu_file_set_error(f, ret < 0 ? ret : -EIO); qemu_file_set_error_obj(f, ret < 0 ? ret : -EIO, local_error);
} }
f->buf_index = 0; f->buf_index = 0;
f->iovcnt = 0; f->iovcnt = 0;
@ -283,6 +318,7 @@ static ssize_t qemu_fill_buffer(QEMUFile *f)
{ {
int len; int len;
int pending; int pending;
Error *local_error = NULL;
assert(!qemu_file_is_writable(f)); assert(!qemu_file_is_writable(f));
@ -294,14 +330,16 @@ static ssize_t qemu_fill_buffer(QEMUFile *f)
f->buf_size = pending; f->buf_size = pending;
len = f->ops->get_buffer(f->opaque, f->buf + pending, f->pos, len = f->ops->get_buffer(f->opaque, f->buf + pending, f->pos,
IO_BUF_SIZE - pending); IO_BUF_SIZE - pending, &local_error);
if (len > 0) { if (len > 0) {
f->buf_size += len; f->buf_size += len;
f->pos += len; f->pos += len;
} else if (len == 0) { } else if (len == 0) {
qemu_file_set_error(f, -EIO); qemu_file_set_error_obj(f, -EIO, local_error);
} else if (len != -EAGAIN) { } else if (len != -EAGAIN) {
qemu_file_set_error(f, len); qemu_file_set_error_obj(f, len, local_error);
} else {
error_free(local_error);
} }
return len; return len;
@ -327,7 +365,7 @@ int qemu_fclose(QEMUFile *f)
ret = qemu_file_get_error(f); ret = qemu_file_get_error(f);
if (f->ops->close) { if (f->ops->close) {
int ret2 = f->ops->close(f->opaque); int ret2 = f->ops->close(f->opaque, NULL);
if (ret >= 0) { if (ret >= 0) {
ret = ret2; ret = ret2;
} }
@ -338,6 +376,7 @@ int qemu_fclose(QEMUFile *f)
if (f->last_error) { if (f->last_error) {
ret = f->last_error; ret = f->last_error;
} }
error_free(f->last_error_obj);
g_free(f); g_free(f);
trace_qemu_file_fclose(); trace_qemu_file_fclose();
return ret; return ret;
@ -615,6 +654,11 @@ void qemu_file_reset_rate_limit(QEMUFile *f)
f->bytes_xfer = 0; f->bytes_xfer = 0;
} }
void qemu_file_update_transfer(QEMUFile *f, int64_t len)
{
f->bytes_xfer += len;
}
void qemu_put_be16(QEMUFile *f, unsigned int v) void qemu_put_be16(QEMUFile *f, unsigned int v)
{ {
qemu_put_byte(f, v >> 8); qemu_put_byte(f, v >> 8);
@ -783,6 +827,6 @@ void qemu_put_counted_string(QEMUFile *f, const char *str)
void qemu_file_set_blocking(QEMUFile *f, bool block) void qemu_file_set_blocking(QEMUFile *f, bool block)
{ {
if (f->ops->set_blocking) { if (f->ops->set_blocking) {
f->ops->set_blocking(f->opaque, block); f->ops->set_blocking(f->opaque, block, NULL);
} }
} }

View file

@ -32,7 +32,8 @@
* bytes actually read should be returned. * bytes actually read should be returned.
*/ */
typedef ssize_t (QEMUFileGetBufferFunc)(void *opaque, uint8_t *buf, typedef ssize_t (QEMUFileGetBufferFunc)(void *opaque, uint8_t *buf,
int64_t pos, size_t size); int64_t pos, size_t size,
Error **errp);
/* Close a file /* Close a file
* *
@ -41,7 +42,7 @@ typedef ssize_t (QEMUFileGetBufferFunc)(void *opaque, uint8_t *buf,
* The meaning of return value on success depends on the specific back-end being * The meaning of return value on success depends on the specific back-end being
* used. * used.
*/ */
typedef int (QEMUFileCloseFunc)(void *opaque); typedef int (QEMUFileCloseFunc)(void *opaque, Error **errp);
/* Called to return the OS file descriptor associated to the QEMUFile. /* Called to return the OS file descriptor associated to the QEMUFile.
*/ */
@ -49,14 +50,15 @@ typedef int (QEMUFileGetFD)(void *opaque);
/* Called to change the blocking mode of the file /* Called to change the blocking mode of the file
*/ */
typedef int (QEMUFileSetBlocking)(void *opaque, bool enabled); typedef int (QEMUFileSetBlocking)(void *opaque, bool enabled, Error **errp);
/* /*
* This function writes an iovec to file. The handler must write all * This function writes an iovec to file. The handler must write all
* of the data or return a negative errno value. * of the data or return a negative errno value.
*/ */
typedef ssize_t (QEMUFileWritevBufferFunc)(void *opaque, struct iovec *iov, typedef ssize_t (QEMUFileWritevBufferFunc)(void *opaque, struct iovec *iov,
int iovcnt, int64_t pos); int iovcnt, int64_t pos,
Error **errp);
/* /*
* This function provides hooks around different * This function provides hooks around different
@ -97,7 +99,8 @@ typedef QEMUFile *(QEMURetPathFunc)(void *opaque);
* Existing blocking reads/writes must be woken * Existing blocking reads/writes must be woken
* Returns 0 on success, -err on error * Returns 0 on success, -err on error
*/ */
typedef int (QEMUFileShutdownFunc)(void *opaque, bool rd, bool wr); typedef int (QEMUFileShutdownFunc)(void *opaque, bool rd, bool wr,
Error **errp);
typedef struct QEMUFileOps { typedef struct QEMUFileOps {
QEMUFileGetBufferFunc *get_buffer; QEMUFileGetBufferFunc *get_buffer;
@ -147,16 +150,17 @@ int qemu_peek_byte(QEMUFile *f, int offset);
void qemu_file_skip(QEMUFile *f, int size); void qemu_file_skip(QEMUFile *f, int size);
void qemu_update_position(QEMUFile *f, size_t size); void qemu_update_position(QEMUFile *f, size_t size);
void qemu_file_reset_rate_limit(QEMUFile *f); void qemu_file_reset_rate_limit(QEMUFile *f);
void qemu_file_update_transfer(QEMUFile *f, int64_t len);
void qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate); void qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate);
int64_t qemu_file_get_rate_limit(QEMUFile *f); int64_t qemu_file_get_rate_limit(QEMUFile *f);
int qemu_file_get_error_obj(QEMUFile *f, Error **errp);
void qemu_file_set_error_obj(QEMUFile *f, int ret, Error *err);
void qemu_file_set_error(QEMUFile *f, int ret); void qemu_file_set_error(QEMUFile *f, int ret);
int qemu_file_shutdown(QEMUFile *f); int qemu_file_shutdown(QEMUFile *f);
QEMUFile *qemu_file_get_return_path(QEMUFile *f); QEMUFile *qemu_file_get_return_path(QEMUFile *f);
void qemu_fflush(QEMUFile *f); void qemu_fflush(QEMUFile *f);
void qemu_file_set_blocking(QEMUFile *f, bool block); void qemu_file_set_blocking(QEMUFile *f, bool block);
size_t qemu_get_counted_string(QEMUFile *f, char buf[256]);
void ram_control_before_iterate(QEMUFile *f, uint64_t flags); void ram_control_before_iterate(QEMUFile *f, uint64_t flags);
void ram_control_after_iterate(QEMUFile *f, uint64_t flags); void ram_control_after_iterate(QEMUFile *f, uint64_t flags);
void ram_control_load_hook(QEMUFile *f, uint64_t flags, void *data); void ram_control_load_hook(QEMUFile *f, uint64_t flags, void *data);
@ -175,6 +179,4 @@ size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset,
ram_addr_t offset, size_t size, ram_addr_t offset, size_t size,
uint64_t *bytes_sent); uint64_t *bytes_sent);
void qemu_put_counted_string(QEMUFile *f, const char *name);
#endif #endif

View file

@ -661,6 +661,8 @@ typedef struct {
uint64_t num_packets; uint64_t num_packets;
/* pages sent through this channel */ /* pages sent through this channel */
uint64_t num_pages; uint64_t num_pages;
/* syncs main thread and channels */
QemuSemaphore sem_sync;
} MultiFDSendParams; } MultiFDSendParams;
typedef struct { typedef struct {
@ -896,8 +898,6 @@ struct {
MultiFDSendParams *params; MultiFDSendParams *params;
/* array of pages to sent */ /* array of pages to sent */
MultiFDPages_t *pages; MultiFDPages_t *pages;
/* syncs main thread and channels */
QemuSemaphore sem_sync;
/* global number of generated multifd packets */ /* global number of generated multifd packets */
uint64_t packet_num; uint64_t packet_num;
/* send channels ready */ /* send channels ready */
@ -922,7 +922,7 @@ struct {
* false. * false.
*/ */
static int multifd_send_pages(void) static int multifd_send_pages(RAMState *rs)
{ {
int i; int i;
static int next_channel; static int next_channel;
@ -954,6 +954,7 @@ static int multifd_send_pages(void)
multifd_send_state->pages = p->pages; multifd_send_state->pages = p->pages;
p->pages = pages; p->pages = pages;
transferred = ((uint64_t) pages->used) * TARGET_PAGE_SIZE + p->packet_len; transferred = ((uint64_t) pages->used) * TARGET_PAGE_SIZE + p->packet_len;
qemu_file_update_transfer(rs->f, transferred);
ram_counters.multifd_bytes += transferred; ram_counters.multifd_bytes += transferred;
ram_counters.transferred += transferred;; ram_counters.transferred += transferred;;
qemu_mutex_unlock(&p->mutex); qemu_mutex_unlock(&p->mutex);
@ -962,7 +963,7 @@ static int multifd_send_pages(void)
return 1; return 1;
} }
static int multifd_queue_page(RAMBlock *block, ram_addr_t offset) static int multifd_queue_page(RAMState *rs, RAMBlock *block, ram_addr_t offset)
{ {
MultiFDPages_t *pages = multifd_send_state->pages; MultiFDPages_t *pages = multifd_send_state->pages;
@ -981,12 +982,12 @@ static int multifd_queue_page(RAMBlock *block, ram_addr_t offset)
} }
} }
if (multifd_send_pages() < 0) { if (multifd_send_pages(rs) < 0) {
return -1; return -1;
} }
if (pages->block != block) { if (pages->block != block) {
return multifd_queue_page(block, offset); return multifd_queue_page(rs, block, offset);
} }
return 1; return 1;
@ -996,6 +997,8 @@ static void multifd_send_terminate_threads(Error *err)
{ {
int i; int i;
trace_multifd_send_terminate_threads(err != NULL);
if (err) { if (err) {
MigrationState *s = migrate_get_current(); MigrationState *s = migrate_get_current();
migrate_set_error(s, err); migrate_set_error(s, err);
@ -1036,6 +1039,7 @@ void multifd_save_cleanup(void)
p->c = NULL; p->c = NULL;
qemu_mutex_destroy(&p->mutex); qemu_mutex_destroy(&p->mutex);
qemu_sem_destroy(&p->sem); qemu_sem_destroy(&p->sem);
qemu_sem_destroy(&p->sem_sync);
g_free(p->name); g_free(p->name);
p->name = NULL; p->name = NULL;
multifd_pages_clear(p->pages); multifd_pages_clear(p->pages);
@ -1045,7 +1049,6 @@ void multifd_save_cleanup(void)
p->packet = NULL; p->packet = NULL;
} }
qemu_sem_destroy(&multifd_send_state->channels_ready); qemu_sem_destroy(&multifd_send_state->channels_ready);
qemu_sem_destroy(&multifd_send_state->sem_sync);
g_free(multifd_send_state->params); g_free(multifd_send_state->params);
multifd_send_state->params = NULL; multifd_send_state->params = NULL;
multifd_pages_clear(multifd_send_state->pages); multifd_pages_clear(multifd_send_state->pages);
@ -1054,7 +1057,7 @@ void multifd_save_cleanup(void)
multifd_send_state = NULL; multifd_send_state = NULL;
} }
static void multifd_send_sync_main(void) static void multifd_send_sync_main(RAMState *rs)
{ {
int i; int i;
@ -1062,7 +1065,7 @@ static void multifd_send_sync_main(void)
return; return;
} }
if (multifd_send_state->pages->used) { if (multifd_send_state->pages->used) {
if (multifd_send_pages() < 0) { if (multifd_send_pages(rs) < 0) {
error_report("%s: multifd_send_pages fail", __func__); error_report("%s: multifd_send_pages fail", __func__);
return; return;
} }
@ -1083,6 +1086,9 @@ static void multifd_send_sync_main(void)
p->packet_num = multifd_send_state->packet_num++; p->packet_num = multifd_send_state->packet_num++;
p->flags |= MULTIFD_FLAG_SYNC; p->flags |= MULTIFD_FLAG_SYNC;
p->pending_job++; p->pending_job++;
qemu_file_update_transfer(rs->f, p->packet_len);
ram_counters.multifd_bytes += p->packet_len;
ram_counters.transferred += p->packet_len;
qemu_mutex_unlock(&p->mutex); qemu_mutex_unlock(&p->mutex);
qemu_sem_post(&p->sem); qemu_sem_post(&p->sem);
} }
@ -1090,7 +1096,7 @@ static void multifd_send_sync_main(void)
MultiFDSendParams *p = &multifd_send_state->params[i]; MultiFDSendParams *p = &multifd_send_state->params[i];
trace_multifd_send_sync_main_wait(p->id); trace_multifd_send_sync_main_wait(p->id);
qemu_sem_wait(&multifd_send_state->sem_sync); qemu_sem_wait(&p->sem_sync);
} }
trace_multifd_send_sync_main(multifd_send_state->packet_num); trace_multifd_send_sync_main(multifd_send_state->packet_num);
} }
@ -1150,7 +1156,7 @@ static void *multifd_send_thread(void *opaque)
qemu_mutex_unlock(&p->mutex); qemu_mutex_unlock(&p->mutex);
if (flags & MULTIFD_FLAG_SYNC) { if (flags & MULTIFD_FLAG_SYNC) {
qemu_sem_post(&multifd_send_state->sem_sync); qemu_sem_post(&p->sem_sync);
} }
qemu_sem_post(&multifd_send_state->channels_ready); qemu_sem_post(&multifd_send_state->channels_ready);
} else if (p->quit) { } else if (p->quit) {
@ -1164,6 +1170,7 @@ static void *multifd_send_thread(void *opaque)
out: out:
if (local_err) { if (local_err) {
trace_multifd_send_error(p->id);
multifd_send_terminate_threads(local_err); multifd_send_terminate_threads(local_err);
} }
@ -1173,7 +1180,7 @@ out:
*/ */
if (ret != 0) { if (ret != 0) {
if (flags & MULTIFD_FLAG_SYNC) { if (flags & MULTIFD_FLAG_SYNC) {
qemu_sem_post(&multifd_send_state->sem_sync); qemu_sem_post(&p->sem_sync);
} }
qemu_sem_post(&multifd_send_state->channels_ready); qemu_sem_post(&multifd_send_state->channels_ready);
} }
@ -1194,6 +1201,7 @@ static void multifd_new_send_channel_async(QIOTask *task, gpointer opaque)
QIOChannel *sioc = QIO_CHANNEL(qio_task_get_source(task)); QIOChannel *sioc = QIO_CHANNEL(qio_task_get_source(task));
Error *local_err = NULL; Error *local_err = NULL;
trace_multifd_new_send_channel_async(p->id);
if (qio_task_propagate_error(task, &local_err)) { if (qio_task_propagate_error(task, &local_err)) {
migrate_set_error(migrate_get_current(), local_err); migrate_set_error(migrate_get_current(), local_err);
multifd_save_cleanup(); multifd_save_cleanup();
@ -1219,7 +1227,6 @@ int multifd_save_setup(void)
multifd_send_state = g_malloc0(sizeof(*multifd_send_state)); multifd_send_state = g_malloc0(sizeof(*multifd_send_state));
multifd_send_state->params = g_new0(MultiFDSendParams, thread_count); multifd_send_state->params = g_new0(MultiFDSendParams, thread_count);
multifd_send_state->pages = multifd_pages_init(page_count); multifd_send_state->pages = multifd_pages_init(page_count);
qemu_sem_init(&multifd_send_state->sem_sync, 0);
qemu_sem_init(&multifd_send_state->channels_ready, 0); qemu_sem_init(&multifd_send_state->channels_ready, 0);
for (i = 0; i < thread_count; i++) { for (i = 0; i < thread_count; i++) {
@ -1227,6 +1234,7 @@ int multifd_save_setup(void)
qemu_mutex_init(&p->mutex); qemu_mutex_init(&p->mutex);
qemu_sem_init(&p->sem, 0); qemu_sem_init(&p->sem, 0);
qemu_sem_init(&p->sem_sync, 0);
p->quit = false; p->quit = false;
p->pending_job = 0; p->pending_job = 0;
p->id = i; p->id = i;
@ -1254,6 +1262,8 @@ static void multifd_recv_terminate_threads(Error *err)
{ {
int i; int i;
trace_multifd_recv_terminate_threads(err != NULL);
if (err) { if (err) {
MigrationState *s = migrate_get_current(); MigrationState *s = migrate_get_current();
migrate_set_error(s, err); migrate_set_error(s, err);
@ -1478,6 +1488,7 @@ bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp)
atomic_read(&multifd_recv_state->count)); atomic_read(&multifd_recv_state->count));
return false; return false;
} }
trace_multifd_recv_new_channel(id);
p = &multifd_recv_state->params[id]; p = &multifd_recv_state->params[id];
if (p->c != NULL) { if (p->c != NULL) {
@ -1748,11 +1759,10 @@ static inline bool migration_bitmap_clear_dirty(RAMState *rs,
} }
/* Called with RCU critical section */ /* Called with RCU critical section */
static void migration_bitmap_sync_range(RAMState *rs, RAMBlock *rb, static void ramblock_sync_dirty_bitmap(RAMState *rs, RAMBlock *rb)
ram_addr_t length)
{ {
rs->migration_dirty_pages += rs->migration_dirty_pages +=
cpu_physical_memory_sync_dirty_bitmap(rb, 0, length, cpu_physical_memory_sync_dirty_bitmap(rb, 0, rb->used_length,
&rs->num_dirty_pages_period); &rs->num_dirty_pages_period);
} }
@ -1841,7 +1851,7 @@ static void migration_bitmap_sync(RAMState *rs)
qemu_mutex_lock(&rs->bitmap_mutex); qemu_mutex_lock(&rs->bitmap_mutex);
rcu_read_lock(); rcu_read_lock();
RAMBLOCK_FOREACH_NOT_IGNORED(block) { RAMBLOCK_FOREACH_NOT_IGNORED(block) {
migration_bitmap_sync_range(rs, block, block->used_length); ramblock_sync_dirty_bitmap(rs, block);
} }
ram_counters.remaining = ram_bytes_remaining(); ram_counters.remaining = ram_bytes_remaining();
rcu_read_unlock(); rcu_read_unlock();
@ -2079,7 +2089,7 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage)
static int ram_save_multifd_page(RAMState *rs, RAMBlock *block, static int ram_save_multifd_page(RAMState *rs, RAMBlock *block,
ram_addr_t offset) ram_addr_t offset)
{ {
if (multifd_queue_page(block, offset) < 0) { if (multifd_queue_page(rs, block, offset) < 0) {
return -1; return -1;
} }
ram_counters.normal++; ram_counters.normal++;
@ -2851,12 +2861,9 @@ void ram_postcopy_migrated_memory_release(MigrationState *ms)
* with the dirtymap; so a '1' means it's either dirty or unsent. * with the dirtymap; so a '1' means it's either dirty or unsent.
* *
* @ms: current migration state * @ms: current migration state
* @pds: state for postcopy
* @block: RAMBlock to discard * @block: RAMBlock to discard
*/ */
static int postcopy_send_discard_bm_ram(MigrationState *ms, static int postcopy_send_discard_bm_ram(MigrationState *ms, RAMBlock *block)
PostcopyDiscardState *pds,
RAMBlock *block)
{ {
unsigned long end = block->used_length >> TARGET_PAGE_BITS; unsigned long end = block->used_length >> TARGET_PAGE_BITS;
unsigned long current; unsigned long current;
@ -2864,23 +2871,21 @@ static int postcopy_send_discard_bm_ram(MigrationState *ms,
for (current = 0; current < end; ) { for (current = 0; current < end; ) {
unsigned long one = find_next_bit(unsentmap, end, current); unsigned long one = find_next_bit(unsentmap, end, current);
unsigned long zero, discard_length;
if (one <= end) { if (one >= end) {
unsigned long zero = find_next_zero_bit(unsentmap, end, one + 1); break;
unsigned long discard_length;
if (zero >= end) {
discard_length = end - one;
} else {
discard_length = zero - one;
}
if (discard_length) {
postcopy_discard_send_range(ms, pds, one, discard_length);
}
current = one + discard_length;
} else {
current = one;
} }
zero = find_next_zero_bit(unsentmap, end, one + 1);
if (zero >= end) {
discard_length = end - one;
} else {
discard_length = zero - one;
}
postcopy_discard_send_range(ms, one, discard_length);
current = one + discard_length;
} }
return 0; return 0;
@ -2905,16 +2910,15 @@ static int postcopy_each_ram_send_discard(MigrationState *ms)
int ret; int ret;
RAMBLOCK_FOREACH_NOT_IGNORED(block) { RAMBLOCK_FOREACH_NOT_IGNORED(block) {
PostcopyDiscardState *pds = postcopy_discard_send_init(ms, block->idstr);
postcopy_discard_send_init(ms, block->idstr);
/* /*
* Postcopy sends chunks of bitmap over the wire, but it * Postcopy sends chunks of bitmap over the wire, but it
* just needs indexes at this point, avoids it having * just needs indexes at this point, avoids it having
* target page specific code. * target page specific code.
*/ */
ret = postcopy_send_discard_bm_ram(ms, pds, block); ret = postcopy_send_discard_bm_ram(ms, block);
postcopy_discard_send_finish(ms, pds); postcopy_discard_send_finish(ms);
if (ret) { if (ret) {
return ret; return ret;
} }
@ -2937,11 +2941,9 @@ static int postcopy_each_ram_send_discard(MigrationState *ms)
* @unsent_pass: if true we need to canonicalize partially unsent host pages * @unsent_pass: if true we need to canonicalize partially unsent host pages
* otherwise we need to canonicalize partially dirty host pages * otherwise we need to canonicalize partially dirty host pages
* @block: block that contains the page we want to canonicalize * @block: block that contains the page we want to canonicalize
* @pds: state for postcopy
*/ */
static void postcopy_chunk_hostpages_pass(MigrationState *ms, bool unsent_pass, static void postcopy_chunk_hostpages_pass(MigrationState *ms, bool unsent_pass,
RAMBlock *block, RAMBlock *block)
PostcopyDiscardState *pds)
{ {
RAMState *rs = ram_state; RAMState *rs = ram_state;
unsigned long *bitmap = block->bmap; unsigned long *bitmap = block->bmap;
@ -2964,54 +2966,30 @@ static void postcopy_chunk_hostpages_pass(MigrationState *ms, bool unsent_pass,
} }
while (run_start < pages) { while (run_start < pages) {
bool do_fixup = false;
unsigned long fixup_start_addr;
unsigned long host_offset;
/* /*
* If the start of this run of pages is in the middle of a host * If the start of this run of pages is in the middle of a host
* page, then we need to fixup this host page. * page, then we need to fixup this host page.
*/ */
host_offset = run_start % host_ratio; if (QEMU_IS_ALIGNED(run_start, host_ratio)) {
if (host_offset) {
do_fixup = true;
run_start -= host_offset;
fixup_start_addr = run_start;
/* For the next pass */
run_start = run_start + host_ratio;
} else {
/* Find the end of this run */ /* Find the end of this run */
unsigned long run_end;
if (unsent_pass) { if (unsent_pass) {
run_end = find_next_bit(unsentmap, pages, run_start + 1); run_start = find_next_bit(unsentmap, pages, run_start + 1);
} else { } else {
run_end = find_next_zero_bit(bitmap, pages, run_start + 1); run_start = find_next_zero_bit(bitmap, pages, run_start + 1);
} }
/* /*
* If the end isn't at the start of a host page, then the * If the end isn't at the start of a host page, then the
* run doesn't finish at the end of a host page * run doesn't finish at the end of a host page
* and we need to discard. * and we need to discard.
*/ */
host_offset = run_end % host_ratio;
if (host_offset) {
do_fixup = true;
fixup_start_addr = run_end - host_offset;
/*
* This host page has gone, the next loop iteration starts
* from after the fixup
*/
run_start = fixup_start_addr + host_ratio;
} else {
/*
* No discards on this iteration, next loop starts from
* next sent/dirty page
*/
run_start = run_end + 1;
}
} }
if (do_fixup) { if (!QEMU_IS_ALIGNED(run_start, host_ratio)) {
unsigned long page; unsigned long page;
unsigned long fixup_start_addr = QEMU_ALIGN_DOWN(run_start,
host_ratio);
run_start = QEMU_ALIGN_UP(run_start, host_ratio);
/* Tell the destination to discard this page */ /* Tell the destination to discard this page */
if (unsent_pass || !test_bit(fixup_start_addr, unsentmap)) { if (unsent_pass || !test_bit(fixup_start_addr, unsentmap)) {
@ -3022,8 +3000,7 @@ static void postcopy_chunk_hostpages_pass(MigrationState *ms, bool unsent_pass,
* (any partially sent pages were already discarded * (any partially sent pages were already discarded
* by the previous unsent_pass) * by the previous unsent_pass)
*/ */
postcopy_discard_send_range(ms, pds, fixup_start_addr, postcopy_discard_send_range(ms, fixup_start_addr, host_ratio);
host_ratio);
} }
/* Clean up the bitmap */ /* Clean up the bitmap */
@ -3066,18 +3043,17 @@ static void postcopy_chunk_hostpages_pass(MigrationState *ms, bool unsent_pass,
*/ */
static int postcopy_chunk_hostpages(MigrationState *ms, RAMBlock *block) static int postcopy_chunk_hostpages(MigrationState *ms, RAMBlock *block)
{ {
PostcopyDiscardState *pds = postcopy_discard_send_init(ms, block->idstr);
postcopy_discard_send_init(ms, block->idstr);
/* First pass: Discard all partially sent host pages */ /* First pass: Discard all partially sent host pages */
postcopy_chunk_hostpages_pass(ms, true, block, pds); postcopy_chunk_hostpages_pass(ms, true, block);
/* /*
* Second pass: Ensure that all partially dirty host pages are made * Second pass: Ensure that all partially dirty host pages are made
* fully dirty. * fully dirty.
*/ */
postcopy_chunk_hostpages_pass(ms, false, block, pds); postcopy_chunk_hostpages_pass(ms, false, block);
postcopy_discard_send_finish(ms, pds); postcopy_discard_send_finish(ms);
return 0; return 0;
} }
@ -3482,7 +3458,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
ram_control_before_iterate(f, RAM_CONTROL_SETUP); ram_control_before_iterate(f, RAM_CONTROL_SETUP);
ram_control_after_iterate(f, RAM_CONTROL_SETUP); ram_control_after_iterate(f, RAM_CONTROL_SETUP);
multifd_send_sync_main(); multifd_send_sync_main(*rsp);
qemu_put_be64(f, RAM_SAVE_FLAG_EOS); qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
qemu_fflush(f); qemu_fflush(f);
@ -3570,7 +3546,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)
ram_control_after_iterate(f, RAM_CONTROL_ROUND); ram_control_after_iterate(f, RAM_CONTROL_ROUND);
out: out:
multifd_send_sync_main(); multifd_send_sync_main(rs);
qemu_put_be64(f, RAM_SAVE_FLAG_EOS); qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
qemu_fflush(f); qemu_fflush(f);
ram_counters.transferred += 8; ram_counters.transferred += 8;
@ -3629,7 +3605,7 @@ static int ram_save_complete(QEMUFile *f, void *opaque)
rcu_read_unlock(); rcu_read_unlock();
multifd_send_sync_main(); multifd_send_sync_main(rs);
qemu_put_be64(f, RAM_SAVE_FLAG_EOS); qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
qemu_fflush(f); qemu_fflush(f);
@ -4296,7 +4272,7 @@ static void colo_flush_ram_cache(void)
memory_global_dirty_log_sync(); memory_global_dirty_log_sync();
rcu_read_lock(); rcu_read_lock();
RAMBLOCK_FOREACH_NOT_IGNORED(block) { RAMBLOCK_FOREACH_NOT_IGNORED(block) {
migration_bitmap_sync_range(ram_state, block, block->used_length); ramblock_sync_dirty_bitmap(ram_state, block);
} }
rcu_read_unlock(); rcu_read_unlock();
@ -4322,40 +4298,26 @@ static void colo_flush_ram_cache(void)
trace_colo_flush_ram_cache_end(); trace_colo_flush_ram_cache_end();
} }
static int ram_load(QEMUFile *f, void *opaque, int version_id) /**
* ram_load_precopy: load pages in precopy case
*
* Returns 0 for success or -errno in case of error
*
* Called in precopy mode by ram_load().
* rcu_read_lock is taken prior to this being called.
*
* @f: QEMUFile where to send the data
*/
static int ram_load_precopy(QEMUFile *f)
{ {
int flags = 0, ret = 0, invalid_flags = 0; int flags = 0, ret = 0, invalid_flags = 0, len = 0;
static uint64_t seq_iter;
int len = 0;
/*
* If system is running in postcopy mode, page inserts to host memory must
* be atomic
*/
bool postcopy_running = postcopy_is_running();
/* ADVISE is earlier, it shows the source has the postcopy capability on */ /* ADVISE is earlier, it shows the source has the postcopy capability on */
bool postcopy_advised = postcopy_is_advised(); bool postcopy_advised = postcopy_is_advised();
seq_iter++;
if (version_id != 4) {
ret = -EINVAL;
}
if (!migrate_use_compression()) { if (!migrate_use_compression()) {
invalid_flags |= RAM_SAVE_FLAG_COMPRESS_PAGE; invalid_flags |= RAM_SAVE_FLAG_COMPRESS_PAGE;
} }
/* This RCU critical section can be very long running.
* When RCU reclaims in the code start to become numerous,
* it will be necessary to reduce the granularity of this
* critical section.
*/
rcu_read_lock();
if (postcopy_running) { while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) {
ret = ram_load_postcopy(f);
}
while (!postcopy_running && !ret && !(flags & RAM_SAVE_FLAG_EOS)) {
ram_addr_t addr, total_ram_bytes; ram_addr_t addr, total_ram_bytes;
void *host = NULL; void *host = NULL;
uint8_t ch; uint8_t ch;
@ -4506,6 +4468,39 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
} }
} }
return ret;
}
static int ram_load(QEMUFile *f, void *opaque, int version_id)
{
int ret = 0;
static uint64_t seq_iter;
/*
* If system is running in postcopy mode, page inserts to host memory must
* be atomic
*/
bool postcopy_running = postcopy_is_running();
seq_iter++;
if (version_id != 4) {
return -EINVAL;
}
/*
* This RCU critical section can be very long running.
* When RCU reclaims in the code start to become numerous,
* it will be necessary to reduce the granularity of this
* critical section.
*/
rcu_read_lock();
if (postcopy_running) {
ret = ram_load_postcopy(f);
} else {
ret = ram_load_precopy(f);
}
ret |= wait_for_decompress_done(); ret |= wait_for_decompress_done();
rcu_read_unlock(); rcu_read_unlock();
trace_ram_load_complete(ret, seq_iter); trace_ram_load_complete(ret, seq_iter);

View file

@ -3140,7 +3140,7 @@ static size_t qemu_rdma_save_page(QEMUFile *f, void *opaque,
CHECK_ERROR_STATE(); CHECK_ERROR_STATE();
if (migrate_get_current()->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { if (migration_in_postcopy()) {
rcu_read_unlock(); rcu_read_unlock();
return RAM_SAVE_CONTROL_NOT_SUPP; return RAM_SAVE_CONTROL_NOT_SUPP;
} }
@ -3775,7 +3775,7 @@ static int qemu_rdma_registration_start(QEMUFile *f, void *opaque,
CHECK_ERROR_STATE(); CHECK_ERROR_STATE();
if (migrate_get_current()->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { if (migration_in_postcopy()) {
rcu_read_unlock(); rcu_read_unlock();
return 0; return 0;
} }
@ -3810,7 +3810,7 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque,
CHECK_ERROR_STATE(); CHECK_ERROR_STATE();
if (migrate_get_current()->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { if (migration_in_postcopy()) {
rcu_read_unlock(); rcu_read_unlock();
return 0; return 0;
} }

View file

@ -124,7 +124,7 @@ static struct mig_cmd_args {
/* savevm/loadvm support */ /* savevm/loadvm support */
static ssize_t block_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, static ssize_t block_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
int64_t pos) int64_t pos, Error **errp)
{ {
int ret; int ret;
QEMUIOVector qiov; QEMUIOVector qiov;
@ -139,12 +139,12 @@ static ssize_t block_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
} }
static ssize_t block_get_buffer(void *opaque, uint8_t *buf, int64_t pos, static ssize_t block_get_buffer(void *opaque, uint8_t *buf, int64_t pos,
size_t size) size_t size, Error **errp)
{ {
return bdrv_load_vmstate(opaque, buf, pos, size); return bdrv_load_vmstate(opaque, buf, pos, size);
} }
static int bdrv_fclose(void *opaque) static int bdrv_fclose(void *opaque, Error **errp)
{ {
return bdrv_flush(opaque); return bdrv_flush(opaque);
} }
@ -1246,29 +1246,16 @@ void qemu_savevm_state_complete_postcopy(QEMUFile *f)
qemu_fflush(f); qemu_fflush(f);
} }
int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only, static
bool inactivate_disks) int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy)
{ {
QJSON *vmdesc;
int vmdesc_len;
SaveStateEntry *se; SaveStateEntry *se;
int ret; int ret;
bool in_postcopy = migration_in_postcopy();
Error *local_err = NULL;
if (precopy_notify(PRECOPY_NOTIFY_COMPLETE, &local_err)) {
error_report_err(local_err);
}
trace_savevm_state_complete_precopy();
cpu_synchronize_all_states();
QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
if (!se->ops || if (!se->ops ||
(in_postcopy && se->ops->has_postcopy && (in_postcopy && se->ops->has_postcopy &&
se->ops->has_postcopy(se->opaque)) || se->ops->has_postcopy(se->opaque)) ||
(in_postcopy && !iterable_only) ||
!se->ops->save_live_complete_precopy) { !se->ops->save_live_complete_precopy) {
continue; continue;
} }
@ -1291,9 +1278,18 @@ int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only,
} }
} }
if (iterable_only) { return 0;
return 0; }
}
static
int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f,
bool in_postcopy,
bool inactivate_disks)
{
QJSON *vmdesc;
int vmdesc_len;
SaveStateEntry *se;
int ret;
vmdesc = qjson_new(); vmdesc = qjson_new();
json_prop_int(vmdesc, "page_size", qemu_target_page_size()); json_prop_int(vmdesc, "page_size", qemu_target_page_size());
@ -1353,6 +1349,42 @@ int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only,
} }
qjson_destroy(vmdesc); qjson_destroy(vmdesc);
return 0;
}
int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only,
bool inactivate_disks)
{
int ret;
Error *local_err = NULL;
bool in_postcopy = migration_in_postcopy();
if (precopy_notify(PRECOPY_NOTIFY_COMPLETE, &local_err)) {
error_report_err(local_err);
}
trace_savevm_state_complete_precopy();
cpu_synchronize_all_states();
if (!in_postcopy || iterable_only) {
ret = qemu_savevm_state_complete_precopy_iterable(f, in_postcopy);
if (ret) {
return ret;
}
}
if (iterable_only) {
goto flush;
}
ret = qemu_savevm_state_complete_precopy_non_iterable(f, in_postcopy,
inactivate_disks);
if (ret) {
return ret;
}
flush:
qemu_fflush(f); qemu_fflush(f);
return 0; return 0;
} }
@ -1424,6 +1456,7 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp)
} }
migrate_init(ms); migrate_init(ms);
memset(&ram_counters, 0, sizeof(ram_counters));
ms->to_dst_file = f; ms->to_dst_file = f;
qemu_mutex_unlock_iothread(); qemu_mutex_unlock_iothread();
@ -1616,8 +1649,6 @@ static int loadvm_postcopy_handle_advise(MigrationIncomingState *mis,
return -1; return -1;
} }
postcopy_state_set(POSTCOPY_INCOMING_ADVISE);
return 0; return 0;
} }
@ -1835,16 +1866,10 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis)
return 0; return 0;
} }
typedef struct {
QEMUBH *bh;
} HandleRunBhData;
static void loadvm_postcopy_handle_run_bh(void *opaque) static void loadvm_postcopy_handle_run_bh(void *opaque)
{ {
Error *local_err = NULL; Error *local_err = NULL;
HandleRunBhData *data = opaque; MigrationIncomingState *mis = opaque;
MigrationIncomingState *mis = migration_incoming_get_current();
/* TODO we should move all of this lot into postcopy_ram.c or a shared code /* TODO we should move all of this lot into postcopy_ram.c or a shared code
* in migration.c * in migration.c
@ -1876,15 +1901,13 @@ static void loadvm_postcopy_handle_run_bh(void *opaque)
runstate_set(RUN_STATE_PAUSED); runstate_set(RUN_STATE_PAUSED);
} }
qemu_bh_delete(data->bh); qemu_bh_delete(mis->bh);
g_free(data);
} }
/* After all discards we can start running and asking for pages */ /* After all discards we can start running and asking for pages */
static int loadvm_postcopy_handle_run(MigrationIncomingState *mis) static int loadvm_postcopy_handle_run(MigrationIncomingState *mis)
{ {
PostcopyState ps = postcopy_state_set(POSTCOPY_INCOMING_RUNNING); PostcopyState ps = postcopy_state_set(POSTCOPY_INCOMING_RUNNING);
HandleRunBhData *data;
trace_loadvm_postcopy_handle_run(); trace_loadvm_postcopy_handle_run();
if (ps != POSTCOPY_INCOMING_LISTENING) { if (ps != POSTCOPY_INCOMING_LISTENING) {
@ -1892,9 +1915,8 @@ static int loadvm_postcopy_handle_run(MigrationIncomingState *mis)
return -1; return -1;
} }
data = g_new(HandleRunBhData, 1); mis->bh = qemu_bh_new(loadvm_postcopy_handle_run_bh, mis);
data->bh = qemu_bh_new(loadvm_postcopy_handle_run_bh, data); qemu_bh_schedule(mis->bh);
qemu_bh_schedule(data->bh);
/* We need to finish reading the stream from the package /* We need to finish reading the stream from the package
* and also stop reading anything more from the stream that loaded the * and also stop reading anything more from the stream that loaded the
@ -2407,7 +2429,7 @@ retry:
case QEMU_VM_COMMAND: case QEMU_VM_COMMAND:
ret = loadvm_process_command(f); ret = loadvm_process_command(f);
trace_qemu_loadvm_state_section_command(ret); trace_qemu_loadvm_state_section_command(ret);
if ((ret < 0) || (ret & LOADVM_QUIT)) { if ((ret < 0) || (ret == LOADVM_QUIT)) {
goto out; goto out;
} }
break; break;

View file

@ -81,16 +81,22 @@ migration_bitmap_sync_start(void) ""
migration_bitmap_sync_end(uint64_t dirty_pages) "dirty_pages %" PRIu64 migration_bitmap_sync_end(uint64_t dirty_pages) "dirty_pages %" PRIu64
migration_bitmap_clear_dirty(char *str, uint64_t start, uint64_t size, unsigned long page) "rb %s start 0x%"PRIx64" size 0x%"PRIx64" page 0x%lx" migration_bitmap_clear_dirty(char *str, uint64_t start, uint64_t size, unsigned long page) "rb %s start 0x%"PRIx64" size 0x%"PRIx64" page 0x%lx"
migration_throttle(void) "" migration_throttle(void) ""
multifd_new_send_channel_async(uint8_t id) "channel %d"
multifd_recv(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags, uint32_t next_packet_size) "channel %d packet_num %" PRIu64 " pages %d flags 0x%x next packet size %d" multifd_recv(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags, uint32_t next_packet_size) "channel %d packet_num %" PRIu64 " pages %d flags 0x%x next packet size %d"
multifd_recv_new_channel(uint8_t id) "channel %d"
multifd_recv_sync_main(long packet_num) "packet num %ld" multifd_recv_sync_main(long packet_num) "packet num %ld"
multifd_recv_sync_main_signal(uint8_t id) "channel %d" multifd_recv_sync_main_signal(uint8_t id) "channel %d"
multifd_recv_sync_main_wait(uint8_t id) "channel %d" multifd_recv_sync_main_wait(uint8_t id) "channel %d"
multifd_recv_terminate_threads(bool error) "error %d"
multifd_recv_thread_end(uint8_t id, uint64_t packets, uint64_t pages) "channel %d packets %" PRIu64 " pages %" PRIu64 multifd_recv_thread_end(uint8_t id, uint64_t packets, uint64_t pages) "channel %d packets %" PRIu64 " pages %" PRIu64
multifd_recv_thread_start(uint8_t id) "%d" multifd_recv_thread_start(uint8_t id) "%d"
multifd_save_setup_wait(uint8_t id) "%d"
multifd_send(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags, uint32_t next_packet_size) "channel %d packet_num %" PRIu64 " pages %d flags 0x%x next packet size %d" multifd_send(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags, uint32_t next_packet_size) "channel %d packet_num %" PRIu64 " pages %d flags 0x%x next packet size %d"
multifd_send_error(uint8_t id) "channel %d"
multifd_send_sync_main(long packet_num) "packet num %ld" multifd_send_sync_main(long packet_num) "packet num %ld"
multifd_send_sync_main_signal(uint8_t id) "channel %d" multifd_send_sync_main_signal(uint8_t id) "channel %d"
multifd_send_sync_main_wait(uint8_t id) "channel %d" multifd_send_sync_main_wait(uint8_t id) "channel %d"
multifd_send_terminate_threads(bool error) "error %d"
multifd_send_thread_end(uint8_t id, uint64_t packets, uint64_t pages) "channel %d packets %" PRIu64 " pages %" PRIu64 multifd_send_thread_end(uint8_t id, uint64_t packets, uint64_t pages) "channel %d packets %" PRIu64 " pages %" PRIu64
multifd_send_thread_start(uint8_t id) "%d" multifd_send_thread_start(uint8_t id) "%d"
ram_discard_range(const char *rbname, uint64_t start, size_t len) "%s: start: %" PRIx64 " %zx" ram_discard_range(const char *rbname, uint64_t start, size_t len) "%s: start: %" PRIx64 " %zx"

View file

@ -220,24 +220,11 @@ static char *SocketAddress_to_str(SocketAddress *addr)
void hmp_info_migrate(Monitor *mon, const QDict *qdict) void hmp_info_migrate(Monitor *mon, const QDict *qdict)
{ {
MigrationInfo *info; MigrationInfo *info;
MigrationCapabilityStatusList *caps, *cap;
info = qmp_query_migrate(NULL); info = qmp_query_migrate(NULL);
caps = qmp_query_migrate_capabilities(NULL);
migration_global_dump(mon); migration_global_dump(mon);
/* do not display parameters during setup */
if (info->has_status && caps) {
monitor_printf(mon, "capabilities: ");
for (cap = caps; cap; cap = cap->next) {
monitor_printf(mon, "%s: %s ",
MigrationCapability_str(cap->value->capability),
cap->value->state ? "on" : "off");
}
monitor_printf(mon, "\n");
}
if (info->has_status) { if (info->has_status) {
monitor_printf(mon, "Migration status: %s", monitor_printf(mon, "Migration status: %s",
MigrationStatus_str(info->status)); MigrationStatus_str(info->status));
@ -370,7 +357,6 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict)
monitor_printf(mon, "]\n"); monitor_printf(mon, "]\n");
} }
qapi_free_MigrationInfo(info); qapi_free_MigrationInfo(info);
qapi_free_MigrationCapabilityStatusList(caps);
} }
void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict) void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict)