On Wed, Jan 08, 2025 at 19:42:42 +0000, Daniel P. Berrangé wrote:
The auto shutdown code can currently only perform managed save,
which may fail in some cases, for example when PCI devices are
assigned. On failure, shutdown inhibitors remain in place which
may be undesirable.
This expands the logic to try a sequence of operations
* Managed save
* Graceful shutdown
* Forced poweroff
Each of these operations can be enabled or disabled, but they
are always applied in this order.
With the shutdown option, a configurable time is allowed for
shutdown to complete, defaulting to 30 seconds, before moving
onto the forced poweroff phase.
Signed-off-by: Daniel P. Berrangé <berrange(a)redhat.com>
---
src/hypervisor/domain_driver.c | 113 +++++++++++++++++++++++++++------
src/hypervisor/domain_driver.h | 4 ++
src/qemu/qemu_driver.c | 3 +
3 files changed, 100 insertions(+), 20 deletions(-)
diff --git a/src/hypervisor/domain_driver.c b/src/hypervisor/domain_driver.c
index 949e3d01f1..ea3f1cbfcd 100644
--- a/src/hypervisor/domain_driver.c
+++ b/src/hypervisor/domain_driver.c
Noticed while reviewing last patch:
@@ -726,31 +743,87 @@
virDomainDriverAutoShutdown(virDomainDriverAutoShutdownConfig *cfg)
VIR_CONNECT_LIST_DOMAINS_ACTIVE)) <
0)
goto cleanup;
- flags = g_new0(unsigned int, numDomains);
+ VIR_DEBUG("Auto shutdown with %d running domains", numDomains);
+ if (cfg->trySave) {
+ g_autofree unsigned int *flags = g_new0(unsigned int, numDomains);
+ for (i = 0; i < numDomains; i++) {
+ int state;
+ /*
+ * Pause all VMs to make them stop dirtying pages,
+ * so save is quicker. We remember if any VMs were
+ * paused so we can restore that on resume.
+ */
+ flags[i] = VIR_DOMAIN_SAVE_RUNNING;
+ if (virDomainGetState(domains[i], &state, NULL, 0) == 0) {
+ if (state == VIR_DOMAIN_PAUSED)
+ flags[i] = VIR_DOMAIN_SAVE_PAUSED;
+ }
+ virDomainSuspend(domains[i]);
Any VM where we attempt 'save' is paused ...
+ }
+
+ for (i = 0; i < numDomains; i++) {
+ if (virDomainManagedSave(domains[i], flags[i]) < 0) {
+ VIR_WARN("Unable to perform managed save of '%s':
%s",
+ virDomainGetName(domains[i]),
+ virGetLastErrorMessage());
+ continue;
... but not unpaused if saving fails ...
+ }
+ virObjectUnref(domains[i]);
+ domains[i] = NULL;
+ }
+ }
+
+ if (cfg->tryShutdown) {
+ GTimer *timer = NULL;
+ for (i = 0; i < numDomains; i++) {
+ if (domains[i] == NULL)
+ continue;
+ if (virDomainShutdown(domains[i]) < 0) {
... so if we then request a graceful shutdown the guest OS can't respond
to it.
+ VIR_WARN("Unable to perform graceful shutdown
of '%s': %s",
+ virDomainGetName(domains[i]),
+ virGetLastErrorMessage());
+ break;
+ }
+ }