Hello,
I found a problem that libvirtd on destination crash frequently while migrating 
vms concurrently. For example, if I migrate 10 vms concurrently ceaselessly, 
then after about 30 minutes the libvirtd on destination will crash. So I 
analyzed and found two bugs during migration process.
First, during migration prepare phase on destination, libvirtd assigns ports to 
qemu to be startd on destination. But the port increase operation is not aomic, 
so there's a chance that multi vms get the same port, and only the first one 
can start successfully, others will fail to start. I've applied a patch to 
solve this bug, and I test it, it works well. If only this bug exists, libvirtd 
will not crash. The second bug is fatal.
Second, I found the libvirtd crash because of segment fault which is produced 
by accessing vm released. Apparently it's caused by multi-thread operation, 
thread A access vm data which has released by thread B. At last I proved my 
thought right.

Step 1. Because of bug one, the port is already occupied, so qemu on 
destination failed to start and sent a HANGUP signal to libvirtd, then libvirtd 
received this VIR_EVENT_HANDLE_HANGUP event, thread A dealing with events 
called qemuProcessHandleMonitorEOF as following:

#0  qemuProcessHandleMonitorEOF (mon=0x7f4dcd9c3130, vm=0x7f4dcd9c9780)
    at qemu/qemu_process.c:399
#1  0x00007f4dc18d9e87 in qemuMonitorIO (watch=68, fd=27, events=8,
    opaque=0x7f4dcd9c3130) at qemu/qemu_monitor.c:668
#2  0x00007f4dccae6604 in virEventPollDispatchHandles (nfds=18,
    fds=0x7f4db4017e70) at util/vireventpoll.c:500
#3  0x00007f4dccae7ff2 in virEventPollRunOnce () at util/vireventpoll.c:646
#4  0x00007f4dccae60e4 in virEventRunDefaultImpl () at util/virevent.c:273
#5  0x00007f4dccc40b25 in virNetServerRun (srv=0x7f4dcd8d26b0)
    at rpc/virnetserver.c:1106
#6  0x00007f4dcd6164c9 in main (argc=3, argv=0x7fff8d8f9f88)
    at libvirtd.c:1518


static int virEventPollDispatchHandles(int nfds, struct pollfd *fds) {
        ......
        /*
deleted flag is still false now, so we pass through to 
qemuProcessHandleMonitorEOF
*/
        if (eventLoop.handles[i].deleted) {
            EVENT_DEBUG("Skip deleted n=%d w=%d f=%d", i,
                        eventLoop.handles[i].watch, eventLoop.handles[i].fd);
            continue;
        }


Step 2: Thread B dealing with migration on destination set deleted flag in 
virEventPollRemoveHandle as following:

#0  virEventPollRemoveHandle (watch=74) at util/vireventpoll.c:176
#1  0x00007f4dccae5e6f in virEventRemoveHandle (watch=74)
    at util/virevent.c:97
#2  0x00007f4dc18d8ca8 in qemuMonitorClose (mon=0x7f4dbc030910)
    at qemu/qemu_monitor.c:831
#3  0x00007f4dc18bec63 in qemuProcessStop (driver=0x7f4dcd9bd400,
    vm=0x7f4dbc00ed20, reason=VIR_DOMAIN_SHUTOFF_FAILED, flags=0)
    at qemu/qemu_process.c:4302
#4  0x00007f4dc18c1a83 in qemuProcessStart (conn=0x7f4dbc031020,
    driver=0x7f4dcd9bd400, vm=0x7f4dbc00ed20,
    migrateFrom=0x7f4dbc01af90 "tcp:[::]:49152", stdin_fd=-1,
    stdin_path=0x0, snapshot=0x0,
    vmop=VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_START, flags=6)
    at qemu/qemu_process.c:4145
#5  0x00007f4dc18cc688 in qemuMigrationPrepareAny (driver=0x7f4dcd9bd400,

Step 3: Thread B cleanup vm in qemuMigrationPrepareAny after qemuProcessStart 
failed.

#0  virDomainObjDispose (obj=0x7f4dcd9c9780) at conf/domain_conf.c:2009
#1  0x00007f4dccb0ccd9 in virObjectUnref (anyobj=0x7f4dcd9c9780)
    at util/virobject.c:266
#2  0x00007f4dccb42340 in virDomainObjListRemove (doms=0x7f4dcd9bd4f0,
    dom=0x7f4dcd9c9780) at conf/domain_conf.c:2342
#3  0x00007f4dc189ac33 in qemuDomainRemoveInactive (driver=0x7f4dcd9bd400,
    vm=0x7f4dcd9c9780) at qemu/qemu_domain.c:1993
#4  0x00007f4dc18ccad5 in qemuMigrationPrepareAny (driver=0x7f4dcd9bd400,

Step 4: Thread A access priv which is released by thread B before, then 
libvirtd crash, bomb!

static void
qemuProcessHandleMonitorEOF(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
                            virDomainObjPtr vm)
{
    virQEMUDriverPtr driver = qemu_driver;
    virDomainEventPtr event = NULL;
    qemuDomainObjPrivatePtr priv;
    int eventReason = VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN;
    int stopReason = VIR_DOMAIN_SHUTOFF_SHUTDOWN;
    const char *auditReason = "shutdown";

    VIR_DEBUG("Received EOF on %p '%s'", vm, vm->def->name);

    virObjectLock(vm);

    priv = vm->privateData;
(gdb) p priv
$1 = (qemuDomainObjPrivatePtr) 0x0
    if (priv->beingDestroyed) {

    At last if anything bad happened to make qemuProcessStart failed during 
migration on destination, we'll be in the big trouble that accessing some 
memory freed. I didn't find any locks or flags exist could stop this happening. 
Please help me out, thanks a lot.

Best Regards,
-WangYufei



--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list

Reply via email to