Skip to content

Commit eb309a4

Browse files
fix(libvirt): handle concluded mirror jobs after migration cancel
Signed-off-by: Yaroslav Borbat <yaroslav.borbat@flant.com>
1 parent 6ff05e7 commit eb309a4

2 files changed

Lines changed: 130 additions & 1 deletion

File tree

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
2+
index 832b2946e0..ea6f311109 100644
3+
--- a/src/qemu/qemu_migration.c
4+
+++ b/src/qemu/qemu_migration.c
5+
@@ -814,6 +814,72 @@ qemuMigrationSrcNBDStorageCopyReady(virDomainObj *vm,
6+
}
7+
}
8+
9+
+/*
10+
+ * Refresh status of migrating mirror jobs from QEMU and synthesize pending
11+
+ * terminal transitions for concluded jobs so synchronous cancellation polling
12+
+ * can process and dismiss them.
13+
+ */
14+
+static int
15+
+qemuMigrationSrcNBDCopyRefreshConcluded(virDomainObj *vm,
16+
+ virDomainAsyncJob asyncJob)
17+
+{
18+
+ qemuDomainObjPrivate *priv = vm->privateData;
19+
+ qemuMonitorJobInfo **jobinfo = NULL;
20+
+ size_t njobinfo = 0;
21+
+ size_t i;
22+
+ int ret = -1;
23+
+ int rc;
24+
+
25+
+ if (qemuDomainObjEnterMonitorAsync(vm, asyncJob) < 0)
26+
+ return -1;
27+
+
28+
+ rc = qemuMonitorGetJobInfo(priv->mon, &jobinfo, &njobinfo);
29+
+ qemuDomainObjExitMonitor(vm);
30+
+ if (rc < 0)
31+
+ goto cleanup;
32+
+
33+
+ for (i = 0; i < vm->def->ndisks; i++) {
34+
+ virDomainDiskDef *disk = vm->def->disks[i];
35+
+ qemuDomainDiskPrivate *diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk);
36+
+ qemuBlockJobData *job;
37+
+ size_t j;
38+
+
39+
+ if (!diskPriv->migrating)
40+
+ continue;
41+
+
42+
+ if (!(job = qemuBlockJobDiskGetJob(disk)))
43+
+ continue;
44+
+
45+
+ for (j = 0; j < njobinfo; j++) {
46+
+ if (STRNEQ_NULLABLE(job->name, jobinfo[j]->id))
47+
+ continue;
48+
+
49+
+ if (jobinfo[j]->status == QEMU_MONITOR_JOB_STATUS_CONCLUDED &&
50+
+ job->newstate == -1) {
51+
+ g_free(job->errmsg);
52+
+ job->errmsg = g_strdup(jobinfo[j]->error);
53+
+
54+
+ if (job->errmsg)
55+
+ job->newstate = QEMU_BLOCKJOB_STATE_FAILED;
56+
+ else
57+
+ job->newstate = QEMU_BLOCKJOB_STATE_COMPLETED;
58+
+ }
59+
+
60+
+ break;
61+
+ }
62+
+
63+
+ virObjectUnref(job);
64+
+ }
65+
+
66+
+ ret = 0;
67+
+
68+
+ cleanup:
69+
+ for (i = 0; i < njobinfo; i++)
70+
+ qemuMonitorJobInfoFree(jobinfo[i]);
71+
+ VIR_FREE(jobinfo);
72+
+
73+
+ return ret;
74+
+}
75+
76+
/*
77+
* If @abortMigration is false, the function will report an error and return a
78+
@@ -840,6 +906,8 @@ qemuMigrationSrcNBDCopyCancelled(virDomainObj *vm,
79+
active = 0;
80+
completed = 0;
81+
82+
+ ignore_value(qemuMigrationSrcNBDCopyRefreshConcluded(vm, asyncJob));
83+
+
84+
for (i = 0; i < vm->def->ndisks; i++) {
85+
virDomainDiskDef *disk = vm->def->disks[i];
86+
qemuDomainDiskPrivate *diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk);
87+
@@ -848,8 +916,10 @@ qemuMigrationSrcNBDCopyCancelled(virDomainObj *vm,
88+
if (!diskPriv->migrating)
89+
continue;
90+
91+
- if (!(job = qemuBlockJobDiskGetJob(disk)))
92+
+ if (!(job = qemuBlockJobDiskGetJob(disk))) {
93+
+ diskPriv->migrating = false;
94+
continue;
95+
+ }
96+
97+
qemuBlockJobUpdate(vm, job, asyncJob);
98+
switch (job->state) {
99+
@@ -940,6 +1010,8 @@ qemuMigrationSrcNBDCopyCancelOne(virDomainObj *vm,
100+
if (rv < 0)
101+
return -1;
102+
103+
+ job->state = QEMU_BLOCKJOB_STATE_ABORTING;
104+
+
105+
return 0;
106+
}
107+
108+
@@ -981,6 +1053,14 @@ qemuMigrationSrcNBDCopyCancel(virDomainObj *vm,
109+
diskPriv->migrating = false;
110+
111+
if (!diskPriv->migrating) {
112+
+ /* A disk may stop being marked as migrating while the tracked
113+
+ * block job still has a pending state transition. Process it to
114+
+ * ensure concluded jobs are dismissed and unregistered, otherwise
115+
+ * stale jobs may remain in QEMU and block subsequent migrations.
116+
+ */
117+
+ if (job && job->newstate != -1)
118+
+ qemuBlockJobUpdate(vm, job, asyncJob);
119+
+
120+
virObjectUnref(job);
121+
continue;
122+
}

images/packages/libvirt/patches/README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,11 @@ When this environment variable is set, `virtqemud` will **only accept socket con
3333
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.
3434

3535
## 003-treat-getpeercon-eintval-as-success.patch
36-
`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.
36+
37+
`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.
38+
39+
## 004-fix-migration-cancel-concluded-mirror-jobs.patch
40+
41+
Fixes a non-shared storage migration cancel race in libvirt/QEMU.
42+
43+
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.

0 commit comments

Comments
 (0)