[libvirt] [PATCHv2 1/2] qemu: new GRACEFUL flag for virDomainDestroy w/ QEMU support

Daniel Veillard veillard at redhat.com
Fri Feb 3 08:12:18 UTC 2012


On Thu, Feb 02, 2012 at 12:54:28PM -0500, Laine Stump wrote:
> When libvirt's virDomainDestroy API is shutting down the qemu process,
> it first sends SIGTERM, then waits for 1.6 seconds and, if it sees the
> process still there, sends a SIGKILL.
> 
> There have been reports that this behavior can lead to data loss
> because the guest running in qemu doesn't have time to flush its disk
> cache buffers before it's unceremoniously whacked.
> 
> This patch maintains that default behavior, but provides a new flag
> VIR_DOMAIN_DESTROY_GRACEFUL to alter the behavior. If this flag is set
> in the call to virDomainDestroyFlags, SIGKILL will never be sent to
> the qemu process; instead, if the timeout is reached and the qemu
> process still exists, virDomainDestroy will return an error.
> 
> Once this patch is in, the recommended method for applications to call
> virDomainDestroyFlags will be with VIR_DOMAIN_DESTROY_GRACEFUL
> included. If that fails, then the application can decide if and when
> to call virDomainDestroyFlags again without
> VIR_DOMAIN_DESTROY_GRACEFUL (to force the issue with SIGKILL).
> 
> (Note that this does not address the issue of existing applications
> that have not yet been modified to use VIR_DOMAIN_DESTROY_GRACEFUL.
> That is a separate patch.)
> ---
>  include/libvirt/libvirt.h.in |   12 ++++----
>  src/libvirt.c                |   20 ++++++++++--
>  src/qemu/qemu_driver.c       |   12 ++++++-
>  src/qemu/qemu_process.c      |   68 +++++++++++++++++++++++++++---------------
>  src/qemu/qemu_process.h      |    9 ++++-
>  5 files changed, 83 insertions(+), 38 deletions(-)
> 
> diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
> index d9b9b95..9440751 100644
> --- a/include/libvirt/libvirt.h.in
> +++ b/include/libvirt/libvirt.h.in
> @@ -1186,12 +1186,6 @@ virConnectPtr           virDomainGetConnect     (virDomainPtr domain);
>   * Domain creation and destruction
>   */
>  
> -
> -/*
> - * typedef enum {
> - * } virDomainDestroyFlagsValues;
> - */
> -
>  virDomainPtr            virDomainCreateXML      (virConnectPtr conn,
>                                                   const char *xmlDesc,
>                                                   unsigned int flags);
> @@ -1226,6 +1220,12 @@ int                     virDomainReset          (virDomainPtr domain,
>                                                   unsigned int flags);
>  
>  int                     virDomainDestroy        (virDomainPtr domain);

Please add here a standard description comment for the datatype:

/**
 * virDomainDestroyFlagsValues:
 *
 * Flags used to provide specific behaviour to the
 * virDomainDestroyFlags() function
 */

for automatic extraction :-)

> +
> +typedef enum {
> +    VIR_DOMAIN_DESTROY_DEFAULT   = 0,      /* Default behavior - could lead to data loss!! */
> +    VIR_DOMAIN_DESTROY_GRACEFUL  = 1 << 0, /* only SIGTERM, no SIGKILL */
> +} virDomainDestroyFlagsValues;
> +
>  int                     virDomainDestroyFlags   (virDomainPtr domain,
>                                                   unsigned int flags);
>  int                     virDomainRef            (virDomainPtr domain);
> diff --git a/src/libvirt.c b/src/libvirt.c
> index c609202..5833d8d 100644
> --- a/src/libvirt.c
> +++ b/src/libvirt.c
> @@ -2233,10 +2233,22 @@ error:
>   * This does not free the associated virDomainPtr object.
>   * This function may require privileged access.
>   *
> - * Calling this function with no @flags set (equal to zero)
> - * is equivalent to calling virDomainDestroy.  Using virDomainShutdown()
> - * may produce cleaner results for the guest's disks, but depends on guest
> - * support.
> + * Calling this function with no @flags set (equal to zero) is
> + * equivalent to calling virDomainDestroy, and after a reasonable
> + * timeout will forcefully terminate the guest (e.g. SIGKILL) if
> + * necessary (which may produce undesirable results, for example
> + * unflushed disk cache in the guest). Including
> + * VIR_DOMAIN_DESTROY_GRACEFUL in the flags will prevent the forceful
> + * termination of the guest, and virDomainDestroyFlags will instead
> + * return an error if the guest doesn't terminate by the end of the
> + * timeout; at that time, the management application can decide if
> + * calling again without VIR_DOMAIN_DESTROY_GRACEFUL is appropriate.
> + *
> + * Another alternative which may produce cleaner results for the
> + * guest's disks is to use virDomainShutdown() instead, but that
> + * depends on guest support (some hypervisor/guest combinations may
> + * ignore the shutdown request).
> + *
>   *
>   * Returns 0 in case of success and -1 in case of failure.
>   */
> diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
> index 1b147a9..ad81e64 100644
> --- a/src/qemu/qemu_driver.c
> +++ b/src/qemu/qemu_driver.c
> @@ -1754,7 +1754,7 @@ qemuDomainDestroyFlags(virDomainPtr dom,
>      virDomainEventPtr event = NULL;
>      qemuDomainObjPrivatePtr priv;
>  
> -    virCheckFlags(0, -1);
> +    virCheckFlags(VIR_DOMAIN_DESTROY_GRACEFUL, -1);
>  
>      qemuDriverLock(driver);
>      vm  = virDomainFindByUUID(&driver->domains, dom->uuid);
> @@ -1775,7 +1775,15 @@ qemuDomainDestroyFlags(virDomainPtr dom,
>       * can kill the process even if a job is active. Killing
>       * it now means the job will be released
>       */
> -    qemuProcessKill(vm, false);
> +    if (flags & VIR_DOMAIN_DESTROY_GRACEFUL) {
> +        if (qemuProcessKill(vm, 0) < 0) {
> +            qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +                            _("failed to kill qemu process with SIGTERM"));
> +            goto cleanup;
> +        }
> +    } else {
> +        ignore_value(qemuProcessKill(vm, VIR_QEMU_PROCESS_KILL_FORCE));
> +    }
>  
>      /* We need to prevent monitor EOF callback from doing our work (and sending
>       * misleading events) while the vm is unlocked inside BeginJob API
> diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
> index 116a828..1bbb55c 100644
> --- a/src/qemu/qemu_process.c
> +++ b/src/qemu/qemu_process.c
> @@ -1,7 +1,7 @@
>  /*
>   * qemu_process.h: QEMU process management
>   *
> - * Copyright (C) 2006-2011 Red Hat, Inc.
> + * Copyright (C) 2006-2012 Red Hat, Inc.
>   *
>   * This library is free software; you can redistribute it and/or
>   * modify it under the terms of the GNU Lesser General Public
> @@ -587,7 +587,7 @@ endjob:
>  cleanup:
>      if (vm) {
>          if (ret == -1)
> -            qemuProcessKill(vm, false);
> +            ignore_value(qemuProcessKill(vm, VIR_QEMU_PROCESS_KILL_FORCE));
>          if (virDomainObjUnref(vm) > 0)
>              virDomainObjUnlock(vm);
>      }
> @@ -612,12 +612,12 @@ qemuProcessShutdownOrReboot(struct qemud_driver *driver,
>                              qemuProcessFakeReboot,
>                              vm) < 0) {
>              VIR_ERROR(_("Failed to create reboot thread, killing domain"));
> -            qemuProcessKill(vm, true);
> +            ignore_value(qemuProcessKill(vm, VIR_QEMU_PROCESS_KILL_NOWAIT));
>              /* Safe to ignore value since ref count was incremented above */
>              ignore_value(virDomainObjUnref(vm));
>          }
>      } else {
> -        qemuProcessKill(vm, true);
> +        ignore_value(qemuProcessKill(vm, VIR_QEMU_PROCESS_KILL_NOWAIT));
>      }
>  }
>  
> @@ -3532,45 +3532,65 @@ cleanup:
>  }
>  
>  
> -void qemuProcessKill(virDomainObjPtr vm, bool gracefully)
> +int qemuProcessKill(virDomainObjPtr vm, unsigned int flags)
>  {
>      int i;
> -    VIR_DEBUG("vm=%s pid=%d gracefully=%d",
> -              vm->def->name, vm->pid, gracefully);
> +    const char *signame = "TERM";
> +
> +    VIR_DEBUG("vm=%s pid=%d flags=%x",
> +              vm->def->name, vm->pid, flags);
>  
>      if (!virDomainObjIsActive(vm)) {
>          VIR_DEBUG("VM '%s' not active", vm->def->name);
> -        return;
> +        return 0;
>      }
>  
> -    /* This loop sends SIGTERM, then waits a few iterations
> -     * (1.6 seconds) to see if it dies. If still alive then
> -     * it does SIGKILL, and waits a few more iterations (1.6
> -     * seconds more) to confirm that it has really gone.
> +    /* This loop sends SIGTERM (or SIGKILL if flags has
> +     * VIR_QEMU_PROCESS_KILL_FORCE and VIR_QEMU_PROCESS_KILL_NOWAIT),
> +     * then waits a few iterations (3 seconds) to see if it
> +     * dies. Halfway through this wait, if the qemu process still
> +     * hasn't exited, and VIR_QEMU_PROCESS_KILL_FORCE is requested, a
> +     * SIGKILL will be sent.  Note that the FORCE mode could result
> +     * in lost data in the guest, so it should only be used if the
> +     * guest is hung and can't be destroyed in any other manner.
>       */
> -    for (i = 0 ; i < 15 ; i++) {
> +    for (i = 0 ; i < 15; i++) {
>          int signum;
> -        if (i == 0)
> -            signum = SIGTERM;
> -        else if (i == 8)
> -            signum = SIGKILL;
> -        else
> +        if (i == 0) {
> +            if ((flags & VIR_QEMU_PROCESS_KILL_FORCE) &&
> +                (flags & VIR_QEMU_PROCESS_KILL_NOWAIT)) {
> +                signum = SIGKILL; /* nuke it immediately */

  s/nuke/kill/ :-)

> +                signame="KILL";
> +            } else {
> +                signum = SIGTERM; /* kindly suggest it should exit */
> +            }
> +        } else if ((i == 8) & (flags & VIR_QEMU_PROCESS_KILL_FORCE)) {
> +            VIR_WARN("Timed out waiting after SIG%s to process %d, "
> +                     "sending SIGKILL", signame, vm->pid);
> +            signum = SIGKILL; /* nuke it after a grace period */

  idem :-)

> +            signame="KILL";
> +        } else {
>              signum = 0; /* Just check for existence */
> +        }
>  
>          if (virKillProcess(vm->pid, signum) < 0) {
>              if (errno != ESRCH) {
>                  char ebuf[1024];
> -                VIR_WARN("Failed to kill process %d %s",
> -                         vm->pid, virStrerror(errno, ebuf, sizeof ebuf));
> +                VIR_WARN("Failed to terminate process %d with SIG%s: %s",
> +                         vm->pid, signame,
> +                         virStrerror(errno, ebuf, sizeof ebuf));
> +                return -1;
>              }
> -            break;
> +            return 0; /* process is dead */
>          }
>  
> -        if (i == 0 && gracefully)
> -            break;
> +        if (i == 0 && (flags & VIR_QEMU_PROCESS_KILL_NOWAIT))
> +            return 0;
>  
>          usleep(200 * 1000);
>      }
> +    VIR_WARN("Timed out waiting after SIG%s to process %d", signame, vm->pid);
> +    return -1;
>  }
>  
>  
> @@ -3659,7 +3679,7 @@ void qemuProcessStop(struct qemud_driver *driver,
>      }
>  
>      /* shut it off for sure */
> -    qemuProcessKill(vm, false);
> +    ignore_value(qemuProcessKill(vm, VIR_QEMU_PROCESS_KILL_FORCE));
>  
>      /* Stop autodestroy in case guest is restarted */
>      qemuProcessAutoDestroyRemove(driver, vm);
> diff --git a/src/qemu/qemu_process.h b/src/qemu/qemu_process.h
> index 062d79e..4ae6b4b 100644
> --- a/src/qemu/qemu_process.h
> +++ b/src/qemu/qemu_process.h
> @@ -1,7 +1,7 @@
>  /*
>   * qemu_process.c: QEMU process management
>   *
> - * Copyright (C) 2006-2011 Red Hat, Inc.
> + * Copyright (C) 2006-2012 Red Hat, Inc.
>   *
>   * This library is free software; you can redistribute it and/or
>   * modify it under the terms of the GNU Lesser General Public
> @@ -68,7 +68,12 @@ int qemuProcessAttach(virConnectPtr conn,
>                        virDomainChrSourceDefPtr monConfig,
>                        bool monJSON);
>  
> -void qemuProcessKill(virDomainObjPtr vm, bool gracefully);
> +typedef enum {
> +   VIR_QEMU_PROCESS_KILL_FORCE  = 1 << 0,
> +   VIR_QEMU_PROCESS_KILL_NOWAIT = 1 << 1,
> +} virQemuProcessKillMode;
> +
> +int qemuProcessKill(virDomainObjPtr vm, unsigned int flags);
>  
>  int qemuProcessAutoDestroyInit(struct qemud_driver *driver);
>  void qemuProcessAutoDestroyRun(struct qemud_driver *driver,
> -- 
> 1.7.7.5

  Except for the 2 nits that looks fine to me

  ACK

Daniel

-- 
Daniel Veillard      | libxml Gnome XML XSLT toolkit  http://xmlsoft.org/
daniel at veillard.com  | Rpmfind RPM search engine http://rpmfind.net/
http://veillard.com/ | virtualization library  http://libvirt.org/




More information about the libvir-list mailing list