Migration pull 2019-09-12

New feature:
   UUID validation check from Yury Kotov
 
 plus a bunch of fixes.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEERfXHG0oMt/uXep+pBRYzHrxb/ecFAl16TKwACgkQBRYzHrxb
 /eeeXg//X0n45bzl6yPYELiN1WQdXgLLcvJXioFNEfTCE/XbA5Dkmt0TDYHvmzzJ
 sO8YDb8mq+5d7+bedaZa53Whzn4CkMJ1bT7812sXRemx25qegdwPobGC4GOKR1Co
 2Vd4YEOQfV+OAf3tPWddKtit3mtR0FXpOMauMHbjFC/tbFV9dL6ikeTUsprNYBrY
 dbJb2I7TyIPv1OjjazmybA3zH00EUYac7Ds6S7Q+gw8K7CfTsECYCm4dfPpiQDu0
 eZiNPp+bH0YD2J47pLIfuI1bb0zUtSMRaJ4KJZtO2/dm6mDgG95R63iaSe4DQCO9
 lekX/xBOKdJgySUcsLcmMiqRLL3AB/lR8+8FsoVyrGbhcy1N54izPtupwq8tU5bZ
 +39BUbHcsPCBcXwVVHUQimoH5g/FYAii+KrjDCnSZqFvjmBGnJbcVLwO/f61Sghi
 ehqfvEiqe6SGnbsxCUcoc1Akz1P/DOYxaTGaAn1wtMrQrkRJhTbrX1pedmTViD5m
 v31J93AnROGHWi/slsxrO2jXghlo0W7a5TdKh0bul/N/IbGCTFZH1EbNXAJqkxkV
 4cKbb86vVJRozsqUCbqrs/WZgQrPXyHaXpN1bUQuA5ofUOBlynj2hrnJXMiCITvW
 MckOnKp0tgijdUefgIWmmGNCeSEPSZ25Nd6QGqPXdehoT0JBb2U=
 =W+5Q
 -----END PGP SIGNATURE-----

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

Migration pull 2019-09-12

New feature:
  UUID validation check from Yury Kotov

plus a bunch of fixes.

# gpg: Signature made Thu 12 Sep 2019 14:48:28 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-20190912a:
  migration: fix one typo in comment of function migration_total_bytes()
  migration/qemu-file: fix potential buf waste for extra buf_index adjustment
  migration/qemu-file: remove check on writev_buffer in qemu_put_compression_data
  migration: Fix postcopy bw for recovery
  tests/migration: Add a test for validate-uuid capability
  tests/libqtest: Allow setting expected exit status
  migration: Add validate-uuid capability
  qemu-file: Rework old qemu_fflush comment
  migration: register_savevm_live doesn't need dev
  hw/net/vmxnet3: Fix leftover unregister_savevm
  migration: cleanup check on ops in savevm.handlers iterations
  migration: multifd_send_thread always post p->sem_sync when error happen

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-09-13 14:37:48 +01:00
commit 85182c96de
19 changed files with 248 additions and 115 deletions

View file

@ -183,8 +183,7 @@ another to load the state back.
.. code:: c .. code:: c
int register_savevm_live(DeviceState *dev, int register_savevm_live(const char *idstr,
const char *idstr,
int instance_id, int instance_id,
int version_id, int version_id,
SaveVMHandlers *ops, SaveVMHandlers *ops,

View file

@ -2242,13 +2242,10 @@ static void vmxnet3_instance_init(Object *obj)
static void vmxnet3_pci_uninit(PCIDevice *pci_dev) static void vmxnet3_pci_uninit(PCIDevice *pci_dev)
{ {
DeviceState *dev = DEVICE(pci_dev);
VMXNET3State *s = VMXNET3(pci_dev); VMXNET3State *s = VMXNET3(pci_dev);
VMW_CBPRN("Starting uninit..."); VMW_CBPRN("Starting uninit...");
unregister_savevm(dev, "vmxnet3-msix", s);
vmxnet3_net_uninit(s); vmxnet3_net_uninit(s);
vmxnet3_cleanup_msix(s); vmxnet3_cleanup_msix(s);

View file

@ -3078,7 +3078,7 @@ static void spapr_machine_init(MachineState *machine)
* interface, this is a legacy from the sPAPREnvironment structure * interface, this is a legacy from the sPAPREnvironment structure
* which predated MachineState but had a similar function */ * which predated MachineState but had a similar function */
vmstate_register(NULL, 0, &vmstate_spapr, spapr); vmstate_register(NULL, 0, &vmstate_spapr, spapr);
register_savevm_live(NULL, "spapr/htab", -1, 1, register_savevm_live("spapr/htab", -1, 1,
&savevm_htab_handlers, spapr); &savevm_htab_handlers, spapr);
qbus_set_hotplug_handler(sysbus_get_default(), OBJECT(machine), qbus_set_hotplug_handler(sysbus_get_default(), OBJECT(machine),

View file

@ -389,7 +389,7 @@ static inline void s390_skeys_set_migration_enabled(Object *obj, bool value,
ss->migration_enabled = value; ss->migration_enabled = value;
if (ss->migration_enabled) { if (ss->migration_enabled) {
register_savevm_live(NULL, TYPE_S390_SKEYS, 0, 1, register_savevm_live(TYPE_S390_SKEYS, 0, 1,
&savevm_s390_storage_keys, ss); &savevm_s390_storage_keys, ss);
} else { } else {
unregister_savevm(DEVICE(ss), TYPE_S390_SKEYS, ss); unregister_savevm(DEVICE(ss), TYPE_S390_SKEYS, ss);

View file

@ -381,7 +381,7 @@ static void s390_stattrib_instance_init(Object *obj)
{ {
S390StAttribState *sas = S390_STATTRIB(obj); S390StAttribState *sas = S390_STATTRIB(obj);
register_savevm_live(NULL, TYPE_S390_STATTRIB, 0, 0, register_savevm_live(TYPE_S390_STATTRIB, 0, 0,
&savevm_s390_stattrib_handlers, sas); &savevm_s390_stattrib_handlers, sas);
object_property_add_bool(obj, "migration-enabled", object_property_add_bool(obj, "migration-enabled",

View file

@ -101,7 +101,7 @@ static void s390_tod_realize(DeviceState *dev, Error **errp)
S390TODState *td = S390_TOD(dev); S390TODState *td = S390_TOD(dev);
/* Legacy migration interface */ /* Legacy migration interface */
register_savevm_live(NULL, "todclock", 0, 1, &savevm_tod, td); register_savevm_live("todclock", 0, 1, &savevm_tod, td);
} }
static void s390_tod_class_init(ObjectClass *oc, void *data) static void s390_tod_class_init(ObjectClass *oc, void *data)

View file

@ -68,8 +68,7 @@ typedef struct SaveVMHandlers {
int (*resume_prepare)(MigrationState *s, void *opaque); int (*resume_prepare)(MigrationState *s, void *opaque);
} SaveVMHandlers; } SaveVMHandlers;
int register_savevm_live(DeviceState *dev, int register_savevm_live(const char *idstr,
const char *idstr,
int instance_id, int instance_id,
int version_id, int version_id,
const SaveVMHandlers *ops, const SaveVMHandlers *ops,

View file

@ -733,7 +733,7 @@ void dirty_bitmap_mig_init(void)
{ {
QSIMPLEQ_INIT(&dirty_bitmap_mig_state.dbms_list); QSIMPLEQ_INIT(&dirty_bitmap_mig_state.dbms_list);
register_savevm_live(NULL, "dirty-bitmap", 0, 1, register_savevm_live("dirty-bitmap", 0, 1,
&savevm_dirty_bitmap_handlers, &savevm_dirty_bitmap_handlers,
&dirty_bitmap_mig_state); &dirty_bitmap_mig_state);
} }

View file

@ -1030,6 +1030,6 @@ void blk_mig_init(void)
QSIMPLEQ_INIT(&block_mig_state.blk_list); QSIMPLEQ_INIT(&block_mig_state.blk_list);
qemu_mutex_init(&block_mig_state.lock); qemu_mutex_init(&block_mig_state.lock);
register_savevm_live(NULL, "block", 0, 1, &savevm_block_handlers, register_savevm_live("block", 0, 1, &savevm_block_handlers,
&block_mig_state); &block_mig_state);
} }

View file

@ -2140,6 +2140,15 @@ bool migrate_ignore_shared(void)
return s->enabled_capabilities[MIGRATION_CAPABILITY_X_IGNORE_SHARED]; return s->enabled_capabilities[MIGRATION_CAPABILITY_X_IGNORE_SHARED];
} }
bool migrate_validate_uuid(void)
{
MigrationState *s;
s = migrate_get_current();
return s->enabled_capabilities[MIGRATION_CAPABILITY_VALIDATE_UUID];
}
bool migrate_use_events(void) bool migrate_use_events(void)
{ {
MigrationState *s; MigrationState *s;
@ -3016,7 +3025,7 @@ static MigThrError migration_detect_error(MigrationState *s)
} }
} }
/* How many bytes have we transferred since the beggining of the migration */ /* How many bytes have we transferred since the beginning of the migration */
static uint64_t migration_total_bytes(MigrationState *s) static uint64_t migration_total_bytes(MigrationState *s)
{ {
return qemu_ftell(s->to_dst_file) + ram_counters.multifd_bytes; return qemu_ftell(s->to_dst_file) + ram_counters.multifd_bytes;
@ -3327,7 +3336,8 @@ void migrate_fd_connect(MigrationState *s, Error *error_in)
if (resume) { if (resume) {
/* This is a resumed migration */ /* This is a resumed migration */
rate_limit = INT64_MAX; rate_limit = s->parameters.max_postcopy_bandwidth /
XFER_LIMIT_RATIO;
} else { } else {
/* This is a fresh new migration */ /* This is a fresh new migration */
rate_limit = s->parameters.max_bandwidth / XFER_LIMIT_RATIO; rate_limit = s->parameters.max_bandwidth / XFER_LIMIT_RATIO;

View file

@ -290,6 +290,7 @@ bool migrate_postcopy_ram(void);
bool migrate_zero_blocks(void); bool migrate_zero_blocks(void);
bool migrate_dirty_bitmaps(void); bool migrate_dirty_bitmaps(void);
bool migrate_ignore_shared(void); bool migrate_ignore_shared(void);
bool migrate_validate_uuid(void);
bool migrate_auto_converge(void); bool migrate_auto_converge(void);
bool migrate_use_multifd(void); bool migrate_use_multifd(void);

View file

@ -201,9 +201,8 @@ static void qemu_iovec_release_ram(QEMUFile *f)
/** /**
* Flushes QEMUFile buffer * Flushes QEMUFile buffer
* *
* If there is writev_buffer QEMUFileOps it uses it otherwise uses * This will flush all pending data. If data was only partially flushed, it
* put_buffer ops. This will flush all pending data. If data was * will set an error state.
* only partially flushed, it will set an error state.
*/ */
void qemu_fflush(QEMUFile *f) void qemu_fflush(QEMUFile *f)
{ {
@ -382,8 +381,16 @@ int qemu_fclose(QEMUFile *f)
return ret; return ret;
} }
static void add_to_iovec(QEMUFile *f, const uint8_t *buf, size_t size, /*
bool may_free) * Add buf to iovec. Do flush if iovec is full.
*
* Return values:
* 1 iovec is full and flushed
* 0 iovec is not flushed
*
*/
static int add_to_iovec(QEMUFile *f, const uint8_t *buf, size_t size,
bool may_free)
{ {
/* check for adjacent buffer and coalesce them */ /* check for adjacent buffer and coalesce them */
if (f->iovcnt > 0 && buf == f->iov[f->iovcnt - 1].iov_base + if (f->iovcnt > 0 && buf == f->iov[f->iovcnt - 1].iov_base +
@ -401,6 +408,19 @@ static void add_to_iovec(QEMUFile *f, const uint8_t *buf, size_t size,
if (f->iovcnt >= MAX_IOV_SIZE) { if (f->iovcnt >= MAX_IOV_SIZE) {
qemu_fflush(f); qemu_fflush(f);
return 1;
}
return 0;
}
static void add_buf_to_iovec(QEMUFile *f, size_t len)
{
if (!add_to_iovec(f, f->buf + f->buf_index, len, false)) {
f->buf_index += len;
if (f->buf_index == IO_BUF_SIZE) {
qemu_fflush(f);
}
} }
} }
@ -430,11 +450,7 @@ void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, size_t size)
} }
memcpy(f->buf + f->buf_index, buf, l); memcpy(f->buf + f->buf_index, buf, l);
f->bytes_xfer += l; f->bytes_xfer += l;
add_to_iovec(f, f->buf + f->buf_index, l, false); add_buf_to_iovec(f, l);
f->buf_index += l;
if (f->buf_index == IO_BUF_SIZE) {
qemu_fflush(f);
}
if (qemu_file_get_error(f)) { if (qemu_file_get_error(f)) {
break; break;
} }
@ -451,11 +467,7 @@ void qemu_put_byte(QEMUFile *f, int v)
f->buf[f->buf_index] = v; f->buf[f->buf_index] = v;
f->bytes_xfer++; f->bytes_xfer++;
add_to_iovec(f, f->buf + f->buf_index, 1, false); add_buf_to_iovec(f, 1);
f->buf_index++;
if (f->buf_index == IO_BUF_SIZE) {
qemu_fflush(f);
}
} }
void qemu_file_skip(QEMUFile *f, int size) void qemu_file_skip(QEMUFile *f, int size)
@ -761,13 +773,7 @@ ssize_t qemu_put_compression_data(QEMUFile *f, z_stream *stream,
} }
qemu_put_be32(f, blen); qemu_put_be32(f, blen);
if (f->ops->writev_buffer) { add_buf_to_iovec(f, blen);
add_to_iovec(f, f->buf + f->buf_index, blen, false);
}
f->buf_index += blen;
if (f->buf_index == IO_BUF_SIZE) {
qemu_fflush(f);
}
return blen + sizeof(int32_t); return blen + sizeof(int32_t);
} }

View file

@ -1112,6 +1112,7 @@ static void *multifd_send_thread(void *opaque)
rcu_register_thread(); rcu_register_thread();
if (multifd_send_initial_packet(p, &local_err) < 0) { if (multifd_send_initial_packet(p, &local_err) < 0) {
ret = -1;
goto out; goto out;
} }
/* initial packet */ /* initial packet */
@ -1179,9 +1180,7 @@ out:
* who pay attention to me. * who pay attention to me.
*/ */
if (ret != 0) { if (ret != 0) {
if (flags & MULTIFD_FLAG_SYNC) { qemu_sem_post(&p->sem_sync);
qemu_sem_post(&p->sem_sync);
}
qemu_sem_post(&multifd_send_state->channels_ready); qemu_sem_post(&multifd_send_state->channels_ready);
} }
@ -4676,5 +4675,5 @@ static SaveVMHandlers savevm_ram_handlers = {
void ram_mig_init(void) void ram_mig_init(void)
{ {
qemu_mutex_init(&XBZRLE.lock); qemu_mutex_init(&XBZRLE.lock);
register_savevm_live(NULL, "ram", 0, 4, &savevm_ram_handlers, &ram_state); register_savevm_live("ram", 0, 4, &savevm_ram_handlers, &ram_state);
} }

View file

@ -256,6 +256,7 @@ typedef struct SaveState {
uint32_t target_page_bits; uint32_t target_page_bits;
uint32_t caps_count; uint32_t caps_count;
MigrationCapability *capabilities; MigrationCapability *capabilities;
QemuUUID uuid;
} SaveState; } SaveState;
static SaveState savevm_state = { static SaveState savevm_state = {
@ -307,6 +308,7 @@ static int configuration_pre_save(void *opaque)
state->capabilities[j++] = i; state->capabilities[j++] = i;
} }
} }
state->uuid = qemu_uuid;
return 0; return 0;
} }
@ -464,6 +466,48 @@ static const VMStateDescription vmstate_capabilites = {
} }
}; };
static bool vmstate_uuid_needed(void *opaque)
{
return qemu_uuid_set && migrate_validate_uuid();
}
static int vmstate_uuid_post_load(void *opaque, int version_id)
{
SaveState *state = opaque;
char uuid_src[UUID_FMT_LEN + 1];
char uuid_dst[UUID_FMT_LEN + 1];
if (!qemu_uuid_set) {
/*
* It's warning because user might not know UUID in some cases,
* e.g. load an old snapshot
*/
qemu_uuid_unparse(&state->uuid, uuid_src);
warn_report("UUID is received %s, but local uuid isn't set",
uuid_src);
return 0;
}
if (!qemu_uuid_is_equal(&state->uuid, &qemu_uuid)) {
qemu_uuid_unparse(&state->uuid, uuid_src);
qemu_uuid_unparse(&qemu_uuid, uuid_dst);
error_report("UUID received is %s and local is %s", uuid_src, uuid_dst);
return -EINVAL;
}
return 0;
}
static const VMStateDescription vmstate_uuid = {
.name = "configuration/uuid",
.version_id = 1,
.minimum_version_id = 1,
.needed = vmstate_uuid_needed,
.post_load = vmstate_uuid_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT8_ARRAY_V(uuid.data, SaveState, sizeof(QemuUUID), 1),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_configuration = { static const VMStateDescription vmstate_configuration = {
.name = "configuration", .name = "configuration",
.version_id = 1, .version_id = 1,
@ -478,6 +522,7 @@ static const VMStateDescription vmstate_configuration = {
.subsections = (const VMStateDescription*[]) { .subsections = (const VMStateDescription*[]) {
&vmstate_target_page_bits, &vmstate_target_page_bits,
&vmstate_capabilites, &vmstate_capabilites,
&vmstate_uuid,
NULL NULL
} }
}; };
@ -684,8 +729,7 @@ static void savevm_state_handler_insert(SaveStateEntry *nse)
of the system, so instance_id should be removed/replaced. of the system, so instance_id should be removed/replaced.
Meanwhile pass -1 as instance_id if you do not already have a clearly Meanwhile pass -1 as instance_id if you do not already have a clearly
distinguishing id for all instances of your device class. */ distinguishing id for all instances of your device class. */
int register_savevm_live(DeviceState *dev, int register_savevm_live(const char *idstr,
const char *idstr,
int instance_id, int instance_id,
int version_id, int version_id,
const SaveVMHandlers *ops, const SaveVMHandlers *ops,
@ -704,26 +748,6 @@ int register_savevm_live(DeviceState *dev,
se->is_ram = 1; se->is_ram = 1;
} }
if (dev) {
char *id = qdev_get_dev_path(dev);
if (id) {
if (snprintf(se->idstr, sizeof(se->idstr), "%s/", id) >=
sizeof(se->idstr)) {
error_report("Path too long for VMState (%s)", id);
g_free(id);
g_free(se);
return -1;
}
g_free(id);
se->compat = g_new0(CompatEntry, 1);
pstrcpy(se->compat->idstr, sizeof(se->compat->idstr), idstr);
se->compat->instance_id = instance_id == -1 ?
calculate_compat_instance_id(idstr) : instance_id;
instance_id = -1;
}
}
pstrcat(se->idstr, sizeof(se->idstr), idstr); pstrcat(se->idstr, sizeof(se->idstr), idstr);
if (instance_id == -1) { if (instance_id == -1) {
@ -1100,7 +1124,7 @@ void qemu_savevm_state_setup(QEMUFile *f)
if (!se->ops || !se->ops->save_setup) { if (!se->ops || !se->ops->save_setup) {
continue; continue;
} }
if (se->ops && se->ops->is_active) { if (se->ops->is_active) {
if (!se->ops->is_active(se->opaque)) { if (!se->ops->is_active(se->opaque)) {
continue; continue;
} }
@ -1131,7 +1155,7 @@ int qemu_savevm_state_resume_prepare(MigrationState *s)
if (!se->ops || !se->ops->resume_prepare) { if (!se->ops || !se->ops->resume_prepare) {
continue; continue;
} }
if (se->ops && se->ops->is_active) { if (se->ops->is_active) {
if (!se->ops->is_active(se->opaque)) { if (!se->ops->is_active(se->opaque)) {
continue; continue;
} }
@ -1227,7 +1251,7 @@ void qemu_savevm_state_complete_postcopy(QEMUFile *f)
if (!se->ops || !se->ops->save_live_complete_postcopy) { if (!se->ops || !se->ops->save_live_complete_postcopy) {
continue; continue;
} }
if (se->ops && se->ops->is_active) { if (se->ops->is_active) {
if (!se->ops->is_active(se->opaque)) { if (!se->ops->is_active(se->opaque)) {
continue; continue;
} }
@ -1264,7 +1288,7 @@ int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy)
continue; continue;
} }
if (se->ops && se->ops->is_active) { if (se->ops->is_active) {
if (!se->ops->is_active(se->opaque)) { if (!se->ops->is_active(se->opaque)) {
continue; continue;
} }
@ -1413,7 +1437,7 @@ void qemu_savevm_state_pending(QEMUFile *f, uint64_t threshold_size,
if (!se->ops || !se->ops->save_live_pending) { if (!se->ops || !se->ops->save_live_pending) {
continue; continue;
} }
if (se->ops && se->ops->is_active) { if (se->ops->is_active) {
if (!se->ops->is_active(se->opaque)) { if (!se->ops->is_active(se->opaque)) {
continue; continue;
} }
@ -2334,7 +2358,7 @@ static int qemu_loadvm_state_setup(QEMUFile *f)
if (!se->ops || !se->ops->load_setup) { if (!se->ops || !se->ops->load_setup) {
continue; continue;
} }
if (se->ops && se->ops->is_active) { if (se->ops->is_active) {
if (!se->ops->is_active(se->opaque)) { if (!se->ops->is_active(se->opaque)) {
continue; continue;
} }

View file

@ -576,7 +576,7 @@ static int net_slirp_init(NetClientState *peer, const char *model,
* specific version? * specific version?
*/ */
g_assert(slirp_state_version() == 4); g_assert(slirp_state_version() == 4);
register_savevm_live(NULL, "slirp", 0, slirp_state_version(), register_savevm_live("slirp", 0, slirp_state_version(),
&savevm_slirp_state, s->slirp); &savevm_slirp_state, s->slirp);
s->poll_notifier.notify = net_slirp_poll_notify; s->poll_notifier.notify = net_slirp_poll_notify;

View file

@ -415,6 +415,9 @@
# #
# @x-ignore-shared: If enabled, QEMU will not migrate shared memory (since 4.0) # @x-ignore-shared: If enabled, QEMU will not migrate shared memory (since 4.0)
# #
# @validate-uuid: Send the UUID of the source to allow the destination
# to ensure it is the same. (since 4.2)
#
# Since: 1.2 # Since: 1.2
## ##
{ 'enum': 'MigrationCapability', { 'enum': 'MigrationCapability',
@ -422,7 +425,7 @@
'compress', 'events', 'postcopy-ram', 'x-colo', 'release-ram', 'compress', 'events', 'postcopy-ram', 'x-colo', 'release-ram',
'block', 'return-path', 'pause-before-switchover', 'multifd', 'block', 'return-path', 'pause-before-switchover', 'multifd',
'dirty-bitmaps', 'postcopy-blocktime', 'late-block-activate', 'dirty-bitmaps', 'postcopy-blocktime', 'late-block-activate',
'x-ignore-shared' ] } 'x-ignore-shared', 'validate-uuid' ] }
## ##
# @MigrationCapabilityStatus: # @MigrationCapabilityStatus:

View file

@ -41,6 +41,7 @@ struct QTestState
int qmp_fd; int qmp_fd;
pid_t qemu_pid; /* our child QEMU process */ pid_t qemu_pid; /* our child QEMU process */
int wstatus; int wstatus;
int expected_status;
bool big_endian; bool big_endian;
bool irq_level[MAX_IRQ]; bool irq_level[MAX_IRQ];
GString *rx; GString *rx;
@ -111,6 +112,11 @@ bool qtest_probe_child(QTestState *s)
return false; return false;
} }
void qtest_set_expected_status(QTestState *s, int status)
{
s->expected_status = status;
}
static void kill_qemu(QTestState *s) static void kill_qemu(QTestState *s)
{ {
pid_t pid = s->qemu_pid; pid_t pid = s->qemu_pid;
@ -124,24 +130,23 @@ static void kill_qemu(QTestState *s)
} }
/* /*
* We expect qemu to exit with status 0; anything else is * Check whether qemu exited with expected exit status; anything else is
* fishy and should be logged with as much detail as possible. * fishy and should be logged with as much detail as possible.
*/ */
wstatus = s->wstatus; wstatus = s->wstatus;
if (wstatus) { if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != s->expected_status) {
if (WIFEXITED(wstatus)) { fprintf(stderr, "%s:%d: kill_qemu() tried to terminate QEMU "
fprintf(stderr, "%s:%d: kill_qemu() tried to terminate QEMU " "process but encountered exit status %d (expected %d)\n",
"process but encountered exit status %d\n", __FILE__, __LINE__, WEXITSTATUS(wstatus), s->expected_status);
__FILE__, __LINE__, WEXITSTATUS(wstatus)); abort();
} else if (WIFSIGNALED(wstatus)) { } else if (WIFSIGNALED(wstatus)) {
int sig = WTERMSIG(wstatus); int sig = WTERMSIG(wstatus);
const char *signame = strsignal(sig) ?: "unknown ???"; const char *signame = strsignal(sig) ?: "unknown ???";
const char *dump = WCOREDUMP(wstatus) ? " (core dumped)" : ""; const char *dump = WCOREDUMP(wstatus) ? " (core dumped)" : "";
fprintf(stderr, "%s:%d: kill_qemu() detected QEMU death " fprintf(stderr, "%s:%d: kill_qemu() detected QEMU death "
"from signal %d (%s)%s\n", "from signal %d (%s)%s\n",
__FILE__, __LINE__, sig, signame, dump); __FILE__, __LINE__, sig, signame, dump);
}
abort(); abort();
} }
} }
@ -246,6 +251,7 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args)
g_test_message("starting QEMU: %s", command); g_test_message("starting QEMU: %s", command);
s->wstatus = 0; s->wstatus = 0;
s->expected_status = 0;
s->qemu_pid = fork(); s->qemu_pid = fork();
if (s->qemu_pid == 0) { if (s->qemu_pid == 0) {
setenv("QEMU_AUDIO_DRV", "none", true); setenv("QEMU_AUDIO_DRV", "none", true);

View file

@ -708,4 +708,13 @@ void qmp_assert_error_class(QDict *rsp, const char *class);
*/ */
bool qtest_probe_child(QTestState *s); bool qtest_probe_child(QTestState *s);
/**
* qtest_set_expected_status:
* @s: QTestState instance to operate on.
* @status: an expected exit status.
*
* Set expected exit status of the child.
*/
void qtest_set_expected_status(QTestState *s, int status);
#endif #endif

View file

@ -512,7 +512,8 @@ static void migrate_postcopy_start(QTestState *from, QTestState *to)
static int test_migrate_start(QTestState **from, QTestState **to, static int test_migrate_start(QTestState **from, QTestState **to,
const char *uri, bool hide_stderr, const char *uri, bool hide_stderr,
bool use_shmem) bool use_shmem, const char *opts_src,
const char *opts_dst)
{ {
gchar *cmd_src, *cmd_dst; gchar *cmd_src, *cmd_dst;
char *bootpath = NULL; char *bootpath = NULL;
@ -521,6 +522,9 @@ static int test_migrate_start(QTestState **from, QTestState **to,
const char *arch = qtest_get_arch(); const char *arch = qtest_get_arch();
const char *accel = "kvm:tcg"; const char *accel = "kvm:tcg";
opts_src = opts_src ? opts_src : "";
opts_dst = opts_dst ? opts_dst : "";
if (use_shmem) { if (use_shmem) {
if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) { if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) {
g_test_skip("/dev/shm is not supported"); g_test_skip("/dev/shm is not supported");
@ -539,16 +543,16 @@ static int test_migrate_start(QTestState **from, QTestState **to,
cmd_src = g_strdup_printf("-machine accel=%s -m 150M" cmd_src = g_strdup_printf("-machine accel=%s -m 150M"
" -name source,debug-threads=on" " -name source,debug-threads=on"
" -serial file:%s/src_serial" " -serial file:%s/src_serial"
" -drive file=%s,format=raw %s", " -drive file=%s,format=raw %s %s",
accel, tmpfs, bootpath, accel, tmpfs, bootpath,
extra_opts ? extra_opts : ""); extra_opts ? extra_opts : "", opts_src);
cmd_dst = g_strdup_printf("-machine accel=%s -m 150M" cmd_dst = g_strdup_printf("-machine accel=%s -m 150M"
" -name target,debug-threads=on" " -name target,debug-threads=on"
" -serial file:%s/dest_serial" " -serial file:%s/dest_serial"
" -drive file=%s,format=raw" " -drive file=%s,format=raw"
" -incoming %s %s", " -incoming %s %s %s",
accel, tmpfs, bootpath, uri, accel, tmpfs, bootpath, uri,
extra_opts ? extra_opts : ""); extra_opts ? extra_opts : "", opts_dst);
start_address = X86_TEST_MEM_START; start_address = X86_TEST_MEM_START;
end_address = X86_TEST_MEM_END; end_address = X86_TEST_MEM_END;
} else if (g_str_equal(arch, "s390x")) { } else if (g_str_equal(arch, "s390x")) {
@ -556,15 +560,15 @@ static int test_migrate_start(QTestState **from, QTestState **to,
extra_opts = use_shmem ? get_shmem_opts("128M", shmem_path) : NULL; extra_opts = use_shmem ? get_shmem_opts("128M", shmem_path) : NULL;
cmd_src = g_strdup_printf("-machine accel=%s -m 128M" cmd_src = g_strdup_printf("-machine accel=%s -m 128M"
" -name source,debug-threads=on" " -name source,debug-threads=on"
" -serial file:%s/src_serial -bios %s %s", " -serial file:%s/src_serial -bios %s %s %s",
accel, tmpfs, bootpath, accel, tmpfs, bootpath,
extra_opts ? extra_opts : ""); extra_opts ? extra_opts : "", opts_src);
cmd_dst = g_strdup_printf("-machine accel=%s -m 128M" cmd_dst = g_strdup_printf("-machine accel=%s -m 128M"
" -name target,debug-threads=on" " -name target,debug-threads=on"
" -serial file:%s/dest_serial -bios %s" " -serial file:%s/dest_serial -bios %s"
" -incoming %s %s", " -incoming %s %s %s",
accel, tmpfs, bootpath, uri, accel, tmpfs, bootpath, uri,
extra_opts ? extra_opts : ""); extra_opts ? extra_opts : "", opts_dst);
start_address = S390_TEST_MEM_START; start_address = S390_TEST_MEM_START;
end_address = S390_TEST_MEM_END; end_address = S390_TEST_MEM_END;
} else if (strcmp(arch, "ppc64") == 0) { } else if (strcmp(arch, "ppc64") == 0) {
@ -575,14 +579,15 @@ static int test_migrate_start(QTestState **from, QTestState **to,
" -prom-env 'use-nvramrc?=true' -prom-env " " -prom-env 'use-nvramrc?=true' -prom-env "
"'nvramrc=hex .\" _\" begin %x %x " "'nvramrc=hex .\" _\" begin %x %x "
"do i c@ 1 + i c! 1000 +loop .\" B\" 0 " "do i c@ 1 + i c! 1000 +loop .\" B\" 0 "
"until' %s", accel, tmpfs, end_address, "until' %s %s", accel, tmpfs, end_address,
start_address, extra_opts ? extra_opts : ""); start_address, extra_opts ? extra_opts : "",
opts_src);
cmd_dst = g_strdup_printf("-machine accel=%s -m 256M" cmd_dst = g_strdup_printf("-machine accel=%s -m 256M"
" -name target,debug-threads=on" " -name target,debug-threads=on"
" -serial file:%s/dest_serial" " -serial file:%s/dest_serial"
" -incoming %s %s", " -incoming %s %s %s",
accel, tmpfs, uri, accel, tmpfs, uri,
extra_opts ? extra_opts : ""); extra_opts ? extra_opts : "", opts_dst);
start_address = PPC_TEST_MEM_START; start_address = PPC_TEST_MEM_START;
end_address = PPC_TEST_MEM_END; end_address = PPC_TEST_MEM_END;
@ -592,16 +597,16 @@ static int test_migrate_start(QTestState **from, QTestState **to,
cmd_src = g_strdup_printf("-machine virt,accel=%s,gic-version=max " cmd_src = g_strdup_printf("-machine virt,accel=%s,gic-version=max "
"-name vmsource,debug-threads=on -cpu max " "-name vmsource,debug-threads=on -cpu max "
"-m 150M -serial file:%s/src_serial " "-m 150M -serial file:%s/src_serial "
"-kernel %s %s", "-kernel %s %s %s",
accel, tmpfs, bootpath, accel, tmpfs, bootpath,
extra_opts ? extra_opts : ""); extra_opts ? extra_opts : "", opts_src);
cmd_dst = g_strdup_printf("-machine virt,accel=%s,gic-version=max " cmd_dst = g_strdup_printf("-machine virt,accel=%s,gic-version=max "
"-name vmdest,debug-threads=on -cpu max " "-name vmdest,debug-threads=on -cpu max "
"-m 150M -serial file:%s/dest_serial " "-m 150M -serial file:%s/dest_serial "
"-kernel %s " "-kernel %s "
"-incoming %s %s", "-incoming %s %s %s",
accel, tmpfs, bootpath, uri, accel, tmpfs, bootpath, uri,
extra_opts ? extra_opts : ""); extra_opts ? extra_opts : "", opts_dst);
start_address = ARM_TEST_MEM_START; start_address = ARM_TEST_MEM_START;
end_address = ARM_TEST_MEM_END; end_address = ARM_TEST_MEM_END;
@ -731,7 +736,7 @@ static int migrate_postcopy_prepare(QTestState **from_ptr,
char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
QTestState *from, *to; QTestState *from, *to;
if (test_migrate_start(&from, &to, uri, hide_error, false)) { if (test_migrate_start(&from, &to, uri, hide_error, false, NULL, NULL)) {
return -1; return -1;
} }
@ -841,20 +846,16 @@ static void test_postcopy_recovery(void)
migrate_postcopy_complete(from, to); migrate_postcopy_complete(from, to);
} }
static void test_baddest(void) static void wait_for_migration_fail(QTestState *from, bool allow_active)
{ {
QTestState *from, *to;
QDict *rsp_return; QDict *rsp_return;
char *status; char *status;
bool failed; bool failed;
if (test_migrate_start(&from, &to, "tcp:0:0", true, false)) {
return;
}
migrate(from, "tcp:0:0", "{}");
do { do {
status = migrate_query_status(from); status = migrate_query_status(from);
g_assert(!strcmp(status, "setup") || !(strcmp(status, "failed"))); g_assert(!strcmp(status, "setup") || !strcmp(status, "failed") ||
(allow_active && !strcmp(status, "active")));
failed = !strcmp(status, "failed"); failed = !strcmp(status, "failed");
g_free(status); g_free(status);
} while (!failed); } while (!failed);
@ -864,7 +865,17 @@ static void test_baddest(void)
g_assert(qdict_haskey(rsp_return, "running")); g_assert(qdict_haskey(rsp_return, "running"));
g_assert(qdict_get_bool(rsp_return, "running")); g_assert(qdict_get_bool(rsp_return, "running"));
qobject_unref(rsp_return); qobject_unref(rsp_return);
}
static void test_baddest(void)
{
QTestState *from, *to;
if (test_migrate_start(&from, &to, "tcp:0:0", true, false, NULL, NULL)) {
return;
}
migrate(from, "tcp:0:0", "{}");
wait_for_migration_fail(from, false);
test_migrate_end(from, to, false); test_migrate_end(from, to, false);
} }
@ -873,7 +884,7 @@ static void test_precopy_unix(void)
char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
QTestState *from, *to; QTestState *from, *to;
if (test_migrate_start(&from, &to, uri, false, false)) { if (test_migrate_start(&from, &to, uri, false, false, NULL, NULL)) {
return; return;
} }
@ -916,7 +927,7 @@ static void test_ignore_shared(void)
char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
QTestState *from, *to; QTestState *from, *to;
if (test_migrate_start(&from, &to, uri, false, true)) { if (test_migrate_start(&from, &to, uri, false, true, NULL, NULL)) {
return; return;
} }
@ -951,7 +962,7 @@ static void test_xbzrle(const char *uri)
{ {
QTestState *from, *to; QTestState *from, *to;
if (test_migrate_start(&from, &to, uri, false, false)) { if (test_migrate_start(&from, &to, uri, false, false, NULL, NULL)) {
return; return;
} }
@ -1003,7 +1014,8 @@ static void test_precopy_tcp(void)
char *uri; char *uri;
QTestState *from, *to; QTestState *from, *to;
if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", false, false)) { if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", false, false,
NULL, NULL)) {
return; return;
} }
@ -1049,7 +1061,7 @@ static void test_migrate_fd_proto(void)
QDict *rsp; QDict *rsp;
const char *error_desc; const char *error_desc;
if (test_migrate_start(&from, &to, "defer", false, false)) { if (test_migrate_start(&from, &to, "defer", false, false, NULL, NULL)) {
return; return;
} }
@ -1125,6 +1137,68 @@ static void test_migrate_fd_proto(void)
test_migrate_end(from, to, true); test_migrate_end(from, to, true);
} }
static void do_test_validate_uuid(const char *uuid_arg_src,
const char *uuid_arg_dst,
bool should_fail, bool hide_stderr)
{
char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
QTestState *from, *to;
if (test_migrate_start(&from, &to, uri, hide_stderr, false,
uuid_arg_src, uuid_arg_dst)) {
return;
}
/*
* UUID validation is at the begin of migration. So, the main process of
* migration is not interesting for us here. Thus, set huge downtime for
* very fast migration.
*/
migrate_set_parameter_int(from, "downtime-limit", 1000000);
migrate_set_capability(from, "validate-uuid", true);
/* Wait for the first serial output from the source */
wait_for_serial("src_serial");
migrate(from, uri, "{}");
if (should_fail) {
qtest_set_expected_status(to, 1);
wait_for_migration_fail(from, true);
} else {
wait_for_migration_complete(from);
}
test_migrate_end(from, to, false);
g_free(uri);
}
static void test_validate_uuid(void)
{
do_test_validate_uuid("-uuid 11111111-1111-1111-1111-111111111111",
"-uuid 11111111-1111-1111-1111-111111111111",
false, false);
}
static void test_validate_uuid_error(void)
{
do_test_validate_uuid("-uuid 11111111-1111-1111-1111-111111111111",
"-uuid 22222222-2222-2222-2222-222222222222",
true, true);
}
static void test_validate_uuid_src_not_set(void)
{
do_test_validate_uuid(NULL, "-uuid 11111111-1111-1111-1111-111111111111",
false, true);
}
static void test_validate_uuid_dst_not_set(void)
{
do_test_validate_uuid("-uuid 11111111-1111-1111-1111-111111111111", NULL,
false, true);
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
char template[] = "/tmp/migration-test-XXXXXX"; char template[] = "/tmp/migration-test-XXXXXX";
@ -1180,6 +1254,12 @@ int main(int argc, char **argv)
/* qtest_add_func("/migration/ignore_shared", test_ignore_shared); */ /* qtest_add_func("/migration/ignore_shared", test_ignore_shared); */
qtest_add_func("/migration/xbzrle/unix", test_xbzrle_unix); qtest_add_func("/migration/xbzrle/unix", test_xbzrle_unix);
qtest_add_func("/migration/fd_proto", test_migrate_fd_proto); qtest_add_func("/migration/fd_proto", test_migrate_fd_proto);
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",
test_validate_uuid_src_not_set);
qtest_add_func("/migration/validate_uuid_dst_not_set",
test_validate_uuid_dst_not_set);
ret = g_test_run(); ret = g_test_run();