mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-05 12:35:20 +00:00
qemu: add a monitor to /proc/$pid when killing times out
In cases when a QEMU process takes longer than the time sigterm and sigkill are issued to kill the process do not simply fail and leave the VM in state VIR_DOMAIN_SHUTDOWN until the daemon stops. Instead set up an fd on /proc/$pid and get notified when the QEMU process finally has terminated to cleanup the VM state. Resolves: https://issues.redhat.com/browse/RHEL-28819 Signed-off-by: Boris Fiuczynski <fiuczy@linux.ibm.com> Signed-off-by: Michal Privoznik <mprivozn@redhat.com> Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
parent
e5eb64e9fd
commit
e62c26a20d
@ -1889,6 +1889,11 @@ qemuDomainObjPrivateFree(void *data)
|
|||||||
|
|
||||||
virChrdevFree(priv->devs);
|
virChrdevFree(priv->devs);
|
||||||
|
|
||||||
|
if (priv->pidMonitored >= 0) {
|
||||||
|
virEventRemoveHandle(priv->pidMonitored);
|
||||||
|
priv->pidMonitored = -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* This should never be non-NULL if we get here, but just in case... */
|
/* This should never be non-NULL if we get here, but just in case... */
|
||||||
if (priv->mon) {
|
if (priv->mon) {
|
||||||
VIR_ERROR(_("Unexpected QEMU monitor still active during domain deletion"));
|
VIR_ERROR(_("Unexpected QEMU monitor still active during domain deletion"));
|
||||||
@ -1934,6 +1939,8 @@ qemuDomainObjPrivateAlloc(void *opaque)
|
|||||||
priv->blockjobs = virHashNew(virObjectUnref);
|
priv->blockjobs = virHashNew(virObjectUnref);
|
||||||
priv->fds = virHashNew(g_object_unref);
|
priv->fds = virHashNew(g_object_unref);
|
||||||
|
|
||||||
|
priv->pidMonitored = -1;
|
||||||
|
|
||||||
/* agent commands block by default, user can choose different behavior */
|
/* agent commands block by default, user can choose different behavior */
|
||||||
priv->agentTimeout = VIR_DOMAIN_AGENT_RESPONSE_TIMEOUT_BLOCK;
|
priv->agentTimeout = VIR_DOMAIN_AGENT_RESPONSE_TIMEOUT_BLOCK;
|
||||||
priv->migMaxBandwidth = QEMU_DOMAIN_MIG_BANDWIDTH_MAX;
|
priv->migMaxBandwidth = QEMU_DOMAIN_MIG_BANDWIDTH_MAX;
|
||||||
@ -11680,6 +11687,7 @@ qemuProcessEventFree(struct qemuProcessEvent *event)
|
|||||||
case QEMU_PROCESS_EVENT_RESET:
|
case QEMU_PROCESS_EVENT_RESET:
|
||||||
case QEMU_PROCESS_EVENT_NBDKIT_EXITED:
|
case QEMU_PROCESS_EVENT_NBDKIT_EXITED:
|
||||||
case QEMU_PROCESS_EVENT_MONITOR_EOF:
|
case QEMU_PROCESS_EVENT_MONITOR_EOF:
|
||||||
|
case QEMU_PROCESS_EVENT_SHUTDOWN_COMPLETED:
|
||||||
case QEMU_PROCESS_EVENT_LAST:
|
case QEMU_PROCESS_EVENT_LAST:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -119,6 +119,7 @@ struct _qemuDomainObjPrivate {
|
|||||||
|
|
||||||
bool beingDestroyed;
|
bool beingDestroyed;
|
||||||
char *pidfile;
|
char *pidfile;
|
||||||
|
int pidMonitored;
|
||||||
|
|
||||||
virDomainPCIAddressSet *pciaddrs;
|
virDomainPCIAddressSet *pciaddrs;
|
||||||
virDomainUSBAddressSet *usbaddrs;
|
virDomainUSBAddressSet *usbaddrs;
|
||||||
@ -469,6 +470,7 @@ typedef enum {
|
|||||||
QEMU_PROCESS_EVENT_UNATTENDED_MIGRATION,
|
QEMU_PROCESS_EVENT_UNATTENDED_MIGRATION,
|
||||||
QEMU_PROCESS_EVENT_RESET,
|
QEMU_PROCESS_EVENT_RESET,
|
||||||
QEMU_PROCESS_EVENT_NBDKIT_EXITED,
|
QEMU_PROCESS_EVENT_NBDKIT_EXITED,
|
||||||
|
QEMU_PROCESS_EVENT_SHUTDOWN_COMPLETED,
|
||||||
|
|
||||||
QEMU_PROCESS_EVENT_LAST
|
QEMU_PROCESS_EVENT_LAST
|
||||||
} qemuProcessEventType;
|
} qemuProcessEventType;
|
||||||
|
@ -4041,6 +4041,22 @@ processNbdkitExitedEvent(virDomainObj *vm,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
processShutdownCompletedEvent(virQEMUDriver *driver,
|
||||||
|
virDomainObj *vm)
|
||||||
|
{
|
||||||
|
if (qemuProcessBeginStopJob(vm, VIR_JOB_DESTROY, true) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (virDomainObjIsActive(vm)) {
|
||||||
|
qemuProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_UNKNOWN,
|
||||||
|
VIR_ASYNC_JOB_NONE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
qemuProcessEndStopJob(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void qemuProcessEventHandler(void *data, void *opaque)
|
static void qemuProcessEventHandler(void *data, void *opaque)
|
||||||
{
|
{
|
||||||
struct qemuProcessEvent *processEvent = data;
|
struct qemuProcessEvent *processEvent = data;
|
||||||
@ -4101,6 +4117,9 @@ static void qemuProcessEventHandler(void *data, void *opaque)
|
|||||||
case QEMU_PROCESS_EVENT_NBDKIT_EXITED:
|
case QEMU_PROCESS_EVENT_NBDKIT_EXITED:
|
||||||
processNbdkitExitedEvent(vm, processEvent->data);
|
processNbdkitExitedEvent(vm, processEvent->data);
|
||||||
break;
|
break;
|
||||||
|
case QEMU_PROCESS_EVENT_SHUTDOWN_COMPLETED:
|
||||||
|
processShutdownCompletedEvent(driver, vm);
|
||||||
|
break;
|
||||||
case QEMU_PROCESS_EVENT_LAST:
|
case QEMU_PROCESS_EVENT_LAST:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,9 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#if WITH_SYS_SYSCALL_H
|
||||||
|
# include <sys/syscall.h>
|
||||||
|
#endif
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
# include <linux/capability.h>
|
# include <linux/capability.h>
|
||||||
#elif defined(__FreeBSD__)
|
#elif defined(__FreeBSD__)
|
||||||
@ -8387,9 +8390,114 @@ qemuProcessCreatePretendCmdBuild(virDomainObj *vm,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if WITH_SYS_SYSCALL_H && defined(SYS_pidfd_open)
|
||||||
|
typedef struct {
|
||||||
|
virDomainObj *vm;
|
||||||
|
int pidfd;
|
||||||
|
} qemuProcessInShutdownEventData;
|
||||||
|
|
||||||
|
|
||||||
|
static qemuProcessInShutdownEventData*
|
||||||
|
qemuProcessInShutdownEventDataNew(virDomainObj *vm, int pidfd)
|
||||||
|
{
|
||||||
|
qemuProcessInShutdownEventData *d = g_new(qemuProcessInShutdownEventData, 1);
|
||||||
|
d->vm = virObjectRef(vm);
|
||||||
|
d->pidfd = pidfd;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
qemuProcessInShutdownEventDataFree(qemuProcessInShutdownEventData *d)
|
||||||
|
{
|
||||||
|
virObjectUnref(d->vm);
|
||||||
|
VIR_FORCE_CLOSE(d->pidfd);
|
||||||
|
g_free(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
qemuProcessInShutdownPidfdCb(int watch,
|
||||||
|
int fd,
|
||||||
|
int events G_GNUC_UNUSED,
|
||||||
|
void *opaque)
|
||||||
|
{
|
||||||
|
qemuProcessInShutdownEventData *data = opaque;
|
||||||
|
virDomainObj *vm = data->vm;
|
||||||
|
|
||||||
|
VIR_DEBUG("vm=%p name=%s pid=%lld fd=%d",
|
||||||
|
vm, vm->def->name, (long long)vm->pid, fd);
|
||||||
|
|
||||||
|
virEventRemoveHandle(watch);
|
||||||
|
|
||||||
|
virObjectLock(vm);
|
||||||
|
|
||||||
|
VIR_INFO("QEMU process %lld finally completed termination",
|
||||||
|
(long long)vm->pid);
|
||||||
|
|
||||||
|
QEMU_DOMAIN_PRIVATE(vm)->pidMonitored = -1;
|
||||||
|
qemuProcessEventSubmit(vm, QEMU_PROCESS_EVENT_SHUTDOWN_COMPLETED,
|
||||||
|
0, 0, NULL);
|
||||||
|
|
||||||
|
virObjectUnlock(vm);
|
||||||
|
}
|
||||||
|
#endif /* WITH_SYS_SYSCALL_H && defined(SYS_pidfd_open) */
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
qemuProcessInShutdownStartMonitor(virDomainObj *vm)
|
||||||
|
{
|
||||||
|
#if WITH_SYS_SYSCALL_H && defined(SYS_pidfd_open)
|
||||||
|
qemuDomainObjPrivate *priv = vm->privateData;
|
||||||
|
qemuProcessInShutdownEventData *data;
|
||||||
|
int pidfd;
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
VIR_DEBUG("vm=%p name=%s pid=%lld pidMonitored=%d",
|
||||||
|
vm, vm->def->name, (long long)vm->pid,
|
||||||
|
priv->pidMonitored);
|
||||||
|
|
||||||
|
if (priv->pidMonitored >= 0) {
|
||||||
|
VIR_DEBUG("Monitoring qemu in-shutdown process %i already set up", vm->pid);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
pidfd = syscall(SYS_pidfd_open, vm->pid, 0);
|
||||||
|
if (pidfd < 0) {
|
||||||
|
if (errno == ESRCH) /* process has already terminated */
|
||||||
|
ret = 1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = qemuProcessInShutdownEventDataNew(vm, pidfd);
|
||||||
|
if ((priv->pidMonitored = virEventAddHandle(pidfd,
|
||||||
|
VIR_EVENT_HANDLE_READABLE,
|
||||||
|
qemuProcessInShutdownPidfdCb,
|
||||||
|
data,
|
||||||
|
(virFreeCallback)qemuProcessInShutdownEventDataFree)) < 0) {
|
||||||
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||||
|
_("failed to monitor qemu in-shutdown process %1$i"),
|
||||||
|
vm->pid);
|
||||||
|
qemuProcessInShutdownEventDataFree(data);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
VIR_DEBUG("Monitoring qemu in-shutdown process %i for termination", vm->pid);
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
return ret;
|
||||||
|
#else /* !WITH_SYS_SYSCALL_H || !defined(SYS_pidfd_open) */
|
||||||
|
VIR_DEBUG("Monitoring qemu process %i not implemented", vm->pid);
|
||||||
|
return -1;
|
||||||
|
#endif /* !WITH_SYS_SYSCALL_H || !defined(SYS_pidfd_open) */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
qemuProcessKill(virDomainObj *vm, unsigned int flags)
|
qemuProcessKill(virDomainObj *vm, unsigned int flags)
|
||||||
{
|
{
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
VIR_DEBUG("vm=%p name=%s pid=%lld flags=0x%x",
|
VIR_DEBUG("vm=%p name=%s pid=%lld flags=0x%x",
|
||||||
vm, vm->def->name,
|
vm, vm->def->name,
|
||||||
(long long)vm->pid, flags);
|
(long long)vm->pid, flags);
|
||||||
@ -8410,10 +8518,19 @@ qemuProcessKill(virDomainObj *vm, unsigned int flags)
|
|||||||
|
|
||||||
/* Request an extra delay of two seconds per current nhostdevs
|
/* Request an extra delay of two seconds per current nhostdevs
|
||||||
* to be safe against stalls by the kernel freeing up the resources */
|
* to be safe against stalls by the kernel freeing up the resources */
|
||||||
return virProcessKillPainfullyDelay(vm->pid,
|
ret = virProcessKillPainfullyDelay(vm->pid,
|
||||||
!!(flags & VIR_QEMU_PROCESS_KILL_FORCE),
|
!!(flags & VIR_QEMU_PROCESS_KILL_FORCE),
|
||||||
vm->def->nhostdevs * 2,
|
vm->def->nhostdevs * 2,
|
||||||
false);
|
false);
|
||||||
|
|
||||||
|
if (ret < 0 && (flags & VIR_QEMU_PROCESS_KILL_MONITOR_ON_ERROR)) {
|
||||||
|
if (qemuProcessInShutdownStartMonitor(vm) == 1) {
|
||||||
|
/* process termination detected */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -8438,7 +8555,7 @@ qemuProcessBeginStopJob(virDomainObj *vm,
|
|||||||
* cleared inside qemuProcessStop */
|
* cleared inside qemuProcessStop */
|
||||||
priv->beingDestroyed = true;
|
priv->beingDestroyed = true;
|
||||||
|
|
||||||
if (qemuProcessKill(vm, killFlags) < 0)
|
if (qemuProcessKill(vm, killFlags|VIR_QEMU_PROCESS_KILL_MONITOR_ON_ERROR) < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
/* Wake up anything waiting on domain condition */
|
/* Wake up anything waiting on domain condition */
|
||||||
|
@ -180,6 +180,7 @@ typedef enum {
|
|||||||
VIR_QEMU_PROCESS_KILL_FORCE = 1 << 0,
|
VIR_QEMU_PROCESS_KILL_FORCE = 1 << 0,
|
||||||
VIR_QEMU_PROCESS_KILL_NOWAIT = 1 << 1,
|
VIR_QEMU_PROCESS_KILL_NOWAIT = 1 << 1,
|
||||||
VIR_QEMU_PROCESS_KILL_NOCHECK = 1 << 2, /* bypass the running vm check */
|
VIR_QEMU_PROCESS_KILL_NOCHECK = 1 << 2, /* bypass the running vm check */
|
||||||
|
VIR_QEMU_PROCESS_KILL_MONITOR_ON_ERROR = 1 << 3, /* on error enable process monitor */
|
||||||
} virQemuProcessKillMode;
|
} virQemuProcessKillMode;
|
||||||
|
|
||||||
int qemuProcessKill(virDomainObj *vm, unsigned int flags);
|
int qemuProcessKill(virDomainObj *vm, unsigned int flags);
|
||||||
|
Loading…
Reference in New Issue
Block a user