Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index 832b2946e0..ea6f311109 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -814,6 +814,72 @@ qemuMigrationSrcNBDStorageCopyReady(virDomainObj *vm,
}
}

+/*
+ * Refresh status of migrating mirror jobs from QEMU and synthesize pending
+ * terminal transitions for concluded jobs so synchronous cancellation polling
+ * can process and dismiss them.
+ */
+static int
+qemuMigrationSrcNBDCopyRefreshConcluded(virDomainObj *vm,
+ virDomainAsyncJob asyncJob)
+{
+ qemuDomainObjPrivate *priv = vm->privateData;
+ qemuMonitorJobInfo **jobinfo = NULL;
+ size_t njobinfo = 0;
+ size_t i;
+ int ret = -1;
+ int rc;
+
+ if (qemuDomainObjEnterMonitorAsync(vm, asyncJob) < 0)
+ return -1;
+
+ rc = qemuMonitorGetJobInfo(priv->mon, &jobinfo, &njobinfo);
+ qemuDomainObjExitMonitor(vm);
+ if (rc < 0)
+ goto cleanup;
+
+ for (i = 0; i < vm->def->ndisks; i++) {
+ virDomainDiskDef *disk = vm->def->disks[i];
+ qemuDomainDiskPrivate *diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk);
+ qemuBlockJobData *job;
+ size_t j;
+
+ if (!diskPriv->migrating)
+ continue;
+
+ if (!(job = qemuBlockJobDiskGetJob(disk)))
+ continue;
+
+ for (j = 0; j < njobinfo; j++) {
+ if (STRNEQ_NULLABLE(job->name, jobinfo[j]->id))
+ continue;
+
+ if (jobinfo[j]->status == QEMU_MONITOR_JOB_STATUS_CONCLUDED &&
+ job->newstate == -1) {
+ g_free(job->errmsg);
+ job->errmsg = g_strdup(jobinfo[j]->error);
+
+ if (job->errmsg)
+ job->newstate = QEMU_BLOCKJOB_STATE_FAILED;
+ else
+ job->newstate = QEMU_BLOCKJOB_STATE_COMPLETED;
+ }
+
+ break;
+ }
+
+ virObjectUnref(job);
+ }
+
+ ret = 0;
+
+ cleanup:
+ for (i = 0; i < njobinfo; i++)
+ qemuMonitorJobInfoFree(jobinfo[i]);
+ VIR_FREE(jobinfo);
+
+ return ret;
+}

/*
* If @abortMigration is false, the function will report an error and return a
@@ -840,6 +906,8 @@ qemuMigrationSrcNBDCopyCancelled(virDomainObj *vm,
active = 0;
completed = 0;

+ ignore_value(qemuMigrationSrcNBDCopyRefreshConcluded(vm, asyncJob));
+
for (i = 0; i < vm->def->ndisks; i++) {
virDomainDiskDef *disk = vm->def->disks[i];
qemuDomainDiskPrivate *diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk);
@@ -848,8 +916,10 @@ qemuMigrationSrcNBDCopyCancelled(virDomainObj *vm,
if (!diskPriv->migrating)
continue;

- if (!(job = qemuBlockJobDiskGetJob(disk)))
+ if (!(job = qemuBlockJobDiskGetJob(disk))) {
+ diskPriv->migrating = false;
continue;
+ }

qemuBlockJobUpdate(vm, job, asyncJob);
switch (job->state) {
@@ -940,6 +1010,8 @@ qemuMigrationSrcNBDCopyCancelOne(virDomainObj *vm,
if (rv < 0)
return -1;

+ job->state = QEMU_BLOCKJOB_STATE_ABORTING;
+
return 0;
}

@@ -981,6 +1053,14 @@ qemuMigrationSrcNBDCopyCancel(virDomainObj *vm,
diskPriv->migrating = false;

if (!diskPriv->migrating) {
+ /* A disk may stop being marked as migrating while the tracked
+ * block job still has a pending state transition. Process it to
+ * ensure concluded jobs are dismissed and unregistered, otherwise
+ * stale jobs may remain in QEMU and block subsequent migrations.
+ */
+ if (job && job->newstate != -1)
+ qemuBlockJobUpdate(vm, job, asyncJob);
+
virObjectUnref(job);
continue;
}
9 changes: 8 additions & 1 deletion images/packages/libvirt/patches/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,11 @@ When this environment variable is set, `virtqemud` will **only accept socket con
This feature enhances security by preventing unauthorized access to the socket and mitigating the risk of privilege escalation attacks. It provides a way to control access to the daemon based on the PID of the connecting process, without the need for additional command-line flags.

## 003-treat-getpeercon-eintval-as-success.patch
`getpeercon` from libselinux uses `getsockopt()` syscall. Some implementations of `getsockopts()` return `EINVAL` errno for unsupported valopt argument instead of `ENOPROTOOPT` errno. This fix makes libvirt work with such broken implementations.

`getpeercon` from libselinux uses `getsockopt()` syscall. Some implementations of `getsockopts()` return `EINVAL` errno for unsupported valopt argument instead of `ENOPROTOOPT` errno. This fix makes libvirt work with such broken implementations.

## 004-fix-migration-cancel-concluded-mirror-jobs.patch

Fixes a non-shared storage migration cancel race in libvirt/QEMU.

After `AbortJob`, mirror block jobs may become `concluded` in QEMU while libvirt still polls synchronous migration mirrors. The patch refreshes monitor job status and drives pending terminal transitions so concluded jobs are dismissed/unregistered and do not block subsequent migrations.
Loading