@@ -998,8 +998,11 @@ struct MemoryListener {
* active at that time.
*
* @listener: The #MemoryListener.
+ * @errp: pointer to Error*, to store an error if it happens.
+ *
+ * Return: true on success, else false setting @errp with error.
*/
- void (*log_global_start)(MemoryListener *listener);
+ bool (*log_global_start)(MemoryListener *listener, Error **errp);
/**
* @log_global_stop:
@@ -457,11 +457,12 @@ static void xen_log_sync(MemoryListener *listener, MemoryRegionSection *section)
int128_get64(section->size));
}
-static void xen_log_global_start(MemoryListener *listener)
+static bool xen_log_global_start(MemoryListener *listener, Error **errp)
{
if (xen_enabled()) {
xen_in_migration = true;
}
+ return true;
}
static void xen_log_global_stop(MemoryListener *listener)
@@ -1066,7 +1066,8 @@ out:
return ret;
}
-static void vfio_listener_log_global_start(MemoryListener *listener)
+static bool vfio_listener_log_global_start(MemoryListener *listener,
+ Error **errp)
{
VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase,
listener);
@@ -1083,6 +1084,7 @@ static void vfio_listener_log_global_start(MemoryListener *listener)
ret, strerror(-ret));
vfio_set_migration_error(ret);
}
+ return !ret;
}
static void vfio_listener_log_global_stop(MemoryListener *listener)
@@ -1044,7 +1044,7 @@ check_dev_state:
return r;
}
-static void vhost_log_global_start(MemoryListener *listener)
+static bool vhost_log_global_start(MemoryListener *listener, Error **errp)
{
int r;
@@ -1052,6 +1052,7 @@ static void vhost_log_global_start(MemoryListener *listener)
if (r < 0) {
abort();
}
+ return true;
}
static void vhost_log_global_stop(MemoryListener *listener)
@@ -2914,9 +2914,33 @@ static unsigned int postponed_stop_flags;
static VMChangeStateEntry *vmstate_change;
static void memory_global_dirty_log_stop_postponed_run(void);
+static bool memory_global_dirty_log_do_start(Error **errp)
+{
+ MemoryListener *listener;
+
+ QTAILQ_FOREACH(listener, &memory_listeners, link) {
+ if (listener->log_global_start) {
+ if (!listener->log_global_start(listener, errp)) {
+ goto err;
+ }
+ }
+ }
+ return true;
+
+err:
+ while ((listener = QTAILQ_PREV(listener, link)) != NULL) {
+ if (listener->log_global_stop) {
+ listener->log_global_stop(listener);
+ }
+ }
+
+ return false;
+}
+
void memory_global_dirty_log_start(unsigned int flags)
{
unsigned int old_flags;
+ Error *local_err = NULL;
assert(flags && !(flags & (~GLOBAL_DIRTY_MASK)));
@@ -2936,7 +2960,13 @@ void memory_global_dirty_log_start(unsigned int flags)
trace_global_dirty_changed(global_dirty_tracking);
if (!old_flags) {
- MEMORY_LISTENER_CALL_GLOBAL(log_global_start, Forward);
+ if (!memory_global_dirty_log_do_start(&local_err)) {
+ global_dirty_tracking &= ~flags;
+ trace_global_dirty_changed(global_dirty_tracking);
+ error_report_err(local_err);
+ return;
+ }
+
memory_region_transaction_begin();
memory_region_update_pending = true;
memory_region_transaction_commit();
@@ -3014,8 +3044,15 @@ static void listener_add_address_space(MemoryListener *listener,
listener->begin(listener);
}
if (global_dirty_tracking) {
+ /*
+ * Currently only VFIO can fail log_global_start(), and it's not
+ * yet allowed to hotplug any PCI device during migration. So this
+ * should never fail when invoked, guard it with error_abort. If
+ * it can start to fail in the future, we need to be able to fail
+ * the whole listener_add_address_space() and its callers.
+ */
if (listener->log_global_start) {
- listener->log_global_start(listener);
+ listener->log_global_start(listener, &error_abort);
}
}