qemu_blockjob.c 13.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
/*
 * qemu_blockjob.c: helper functions for QEMU block jobs
 *
 * Copyright (C) 2006-2015 Red Hat, Inc.
 * Copyright (C) 2006 Daniel P. Berrange
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see
 * <http://www.gnu.org/licenses/>.
 */

#include <config.h>

#include "internal.h"

#include "qemu_blockjob.h"
27
#include "qemu_block.h"
28 29 30 31 32 33 34
#include "qemu_domain.h"

#include "conf/domain_conf.h"
#include "conf/domain_event.h"

#include "virlog.h"
#include "virstoragefile.h"
35 36
#include "virthread.h"
#include "virtime.h"
37
#include "locking/domain_lock.h"
38
#include "viralloc.h"
39
#include "virstring.h"
40
#include "qemu_security.h"
41 42 43 44 45

#define VIR_FROM_THIS VIR_FROM_QEMU

VIR_LOG_INIT("qemu.qemu_blockjob");

46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
/* Note that qemuBlockjobState and qemuBlockjobType values are formatted into
 * the status XML */
VIR_ENUM_IMPL(qemuBlockjobState,
              QEMU_BLOCKJOB_STATE_LAST,
              "completed",
              "failed",
              "cancelled",
              "ready",
              "new",
              "running");

VIR_ENUM_IMPL(qemuBlockjob,
              QEMU_BLOCKJOB_TYPE_LAST,
              "",
              "pull",
              "copy",
              "commit",
              "active-commit",
              "");
65

66 67 68 69 70
static virClassPtr qemuBlockJobDataClass;


static void
qemuBlockJobDataDispose(void *obj)
71
{
72
    qemuBlockJobDataPtr job = obj;
73

74
    VIR_FREE(job->name);
75
    VIR_FREE(job->errmsg);
76 77 78 79 80 81 82 83 84 85 86 87 88
}


static int
qemuBlockJobDataOnceInit(void)
{
    if (!VIR_CLASS_NEW(qemuBlockJobData, virClassForObject()))
        return -1;

    return 0;
}


89
VIR_ONCE_GLOBAL_INIT(qemuBlockJobData);
90

91
qemuBlockJobDataPtr
92 93
qemuBlockJobDataNew(qemuBlockJobType type,
                    const char *name)
94
{
95
    VIR_AUTOUNREF(qemuBlockJobDataPtr) job = NULL;
96

97 98 99
    if (qemuBlockJobDataInitialize() < 0)
        return NULL;

100 101
    if (!(job = virObjectNew(qemuBlockJobDataClass)))
        return NULL;
102

103
    if (VIR_STRDUP(job->name, name) < 0)
104
        return NULL;
105

106
    job->state = QEMU_BLOCKJOB_STATE_NEW;
107
    job->newstate = -1;
108 109
    job->type = type;

110
    VIR_RETURN_PTR(job);
111 112 113
}


114
int
115
qemuBlockJobRegister(qemuBlockJobDataPtr job,
116
                     virDomainObjPtr vm,
117 118
                     virDomainDiskDefPtr disk,
                     bool savestatus)
119
{
120 121 122 123 124 125 126
    qemuDomainObjPrivatePtr priv = vm->privateData;

    if (virHashAddEntry(priv->blockjobs, job->name, virObjectRef(job)) < 0) {
        virObjectUnref(job);
        return -1;
    }

127 128 129 130 131
    if (disk) {
        job->disk = disk;
        QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob = virObjectRef(job);
    }

132 133 134
    if (savestatus)
        qemuDomainSaveStatus(vm);

135 136 137 138 139
    return 0;
}


static void
140 141
qemuBlockJobUnregister(qemuBlockJobDataPtr job,
                       virDomainObjPtr vm)
142
{
143
    qemuDomainObjPrivatePtr priv = vm->privateData;
144 145 146 147 148 149 150 151 152 153 154 155
    qemuDomainDiskPrivatePtr diskPriv;

    if (job->disk) {
        diskPriv = QEMU_DOMAIN_DISK_PRIVATE(job->disk);

        if (job == diskPriv->blockjob) {
            virObjectUnref(diskPriv->blockjob);
            diskPriv->blockjob = NULL;
        }

        job->disk = NULL;
    }
156 157 158

    /* this may remove the last reference of 'job' */
    virHashRemoveEntry(priv->blockjobs, job->name);
159 160

    qemuDomainSaveStatus(vm);
161 162 163
}


164 165 166 167 168 169 170 171 172
/**
 * qemuBlockJobDiskNew:
 * @disk: disk definition
 *
 * Start/associate a new blockjob with @disk.
 *
 * Returns 0 on success and -1 on failure.
 */
qemuBlockJobDataPtr
173 174
qemuBlockJobDiskNew(virDomainObjPtr vm,
                    virDomainDiskDefPtr disk,
175 176
                    qemuBlockJobType type,
                    const char *jobname)
177
{
178
    VIR_AUTOUNREF(qemuBlockJobDataPtr) job = NULL;
179

180
    if (!(job = qemuBlockJobDataNew(type, jobname)))
181
        return NULL;
182

183
    if (qemuBlockJobRegister(job, vm, disk, true) < 0)
184
        return NULL;
185

186
    VIR_RETURN_PTR(job);
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
}


/**
 * qemuBlockJobDiskGetJob:
 * @disk: disk definition
 *
 * Get a reference to the block job data object associated with @disk.
 */
qemuBlockJobDataPtr
qemuBlockJobDiskGetJob(virDomainDiskDefPtr disk)
{
    qemuBlockJobDataPtr job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob;

    if (!job)
        return NULL;

    return virObjectRef(job);
}


/**
 * qemuBlockJobStarted:
 * @job: job data
 *
 * Mark @job as started in qemu.
 */
void
215 216
qemuBlockJobStarted(qemuBlockJobDataPtr job,
                    virDomainObjPtr vm)
217
{
218 219
    if (job->state == QEMU_BLOCKJOB_STATE_NEW)
        job->state = QEMU_BLOCKJOB_STATE_RUNNING;
220 221

    qemuDomainSaveStatus(vm);
222 223 224 225 226 227 228 229 230 231 232 233
}


/**
 * qemuBlockJobStartupFinalize:
 * @job: job being started
 *
 * Cancels and clears the job private data if the job was not started with
 * qemu (see qemuBlockJobStarted) or just clears up the local reference
 * to @job if it was started.
 */
void
234 235
qemuBlockJobStartupFinalize(virDomainObjPtr vm,
                            qemuBlockJobDataPtr job)
236 237 238 239
{
    if (!job)
        return;

240
    if (job->state == QEMU_BLOCKJOB_STATE_NEW)
241
        qemuBlockJobUnregister(job, vm);
242 243 244 245 246

    virObjectUnref(job);
}


247 248 249 250 251 252 253 254
bool
qemuBlockJobIsRunning(qemuBlockJobDataPtr job)
{
    return job->state == QEMU_BLOCKJOB_STATE_RUNNING ||
           job->state == QEMU_BLOCKJOB_STATE_READY;
}


255 256 257 258
/**
 * qemuBlockJobEmitEvents:
 *
 * Emits the VIR_DOMAIN_EVENT_ID_BLOCK_JOB and VIR_DOMAIN_EVENT_ID_BLOCK_JOB_2
259
 * for a block job. The former event is emitted only for local disks.
260 261 262 263 264 265 266 267 268 269 270
 */
static void
qemuBlockJobEmitEvents(virQEMUDriverPtr driver,
                       virDomainObjPtr vm,
                       virDomainDiskDefPtr disk,
                       virDomainBlockJobType type,
                       virConnectDomainEventBlockJobStatus status)
{
    virObjectEventPtr event = NULL;
    virObjectEventPtr event2 = NULL;

271 272 273 274
    /* don't emit events for jobs without disk */
    if (!disk)
        return;

275 276 277 278 279
    /* don't emit events for internal jobs and states */
    if (type >= VIR_DOMAIN_BLOCK_JOB_TYPE_LAST ||
        status >= VIR_DOMAIN_BLOCK_JOB_LAST)
        return;

280 281 282 283 284 285
    if (virStorageSourceIsLocalStorage(disk->src) &&
        !virStorageSourceIsEmpty(disk->src)) {
        event = virDomainEventBlockJobNewFromObj(vm, virDomainDiskGetSource(disk),
                                                 type, status);
        virObjectEventStateQueue(driver->domainEventState, event);
    }
286 287 288 289 290 291

    event2 = virDomainEventBlockJob2NewFromObj(vm, disk->dst, type, status);
    virObjectEventStateQueue(driver->domainEventState, event2);
}


292 293 294
static void
qemuBlockJobEventProcessLegacyCompleted(virQEMUDriverPtr driver,
                                        virDomainObjPtr vm,
295
                                        qemuBlockJobDataPtr job,
296 297
                                        int asyncJob)
{
298
    virDomainDiskDefPtr disk = job->disk;
299 300
    virDomainDiskDefPtr persistDisk = NULL;

301 302 303
    if (!disk)
        return;

304 305 306 307 308 309 310 311 312 313 314 315 316 317
    if (disk->mirrorState == VIR_DOMAIN_DISK_MIRROR_STATE_PIVOT) {
        if (vm->newDef) {
            virStorageSourcePtr copy = NULL;

            if ((persistDisk = virDomainDiskByName(vm->newDef,
                                                   disk->dst, false))) {
                copy = virStorageSourceCopy(disk->mirror, false);
                if (!copy ||
                    virStorageSourceInitChainElement(copy,
                                                     persistDisk->src,
                                                     true) < 0) {
                    VIR_WARN("Unable to update persistent definition "
                             "on vm %s after block job",
                             vm->def->name);
318
                    virObjectUnref(copy);
319 320 321 322 323
                    copy = NULL;
                    persistDisk = NULL;
                }
            }
            if (copy) {
324
                virObjectUnref(persistDisk->src);
325 326 327 328 329 330 331 332 333 334
                persistDisk->src = copy;
            }
        }

        /* XXX We want to revoke security labels as well as audit that
         * revocation, before dropping the original source.  But it gets
         * tricky if both source and mirror share common backing files (we
         * want to only revoke the non-shared portion of the chain); so for
         * now, we leak the access to the original.  */
        virDomainLockImageDetach(driver->lockManager, vm, disk->src);
335 336 337 338 339

        /* Move secret driver metadata */
        if (qemuSecurityMoveImageMetadata(driver, vm, disk->src, disk->mirror) < 0)
            VIR_WARN("Unable to move disk metadata on vm %s", vm->def->name);

340
        virObjectUnref(disk->src);
341 342 343 344
        disk->src = disk->mirror;
    } else {
        if (disk->mirror) {
            virDomainLockImageDetach(driver->lockManager, vm, disk->mirror);
345
            virObjectUnref(disk->mirror);
346 347 348 349 350 351 352 353 354 355 356 357
        }
    }

    /* Recompute the cached backing chain to match our
     * updates.  Better would be storing the chain ourselves
     * rather than reprobing, but we haven't quite completed
     * that conversion to use our XML tracking. */
    disk->mirror = NULL;
    disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_NONE;
    disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN;
    disk->src->id = 0;
    virStorageSourceBackingStoreClear(disk->src);
358
    ignore_value(qemuDomainDetermineDiskChain(driver, vm, disk, NULL, true));
359
    ignore_value(qemuBlockNodeNamesDetect(driver, vm, asyncJob));
360
    qemuBlockJobUnregister(job, vm);
361
    qemuDomainSaveConfig(vm);
362 363 364
}


365
/**
366
 * qemuBlockJobEventProcessLegacy:
367 368
 * @driver: qemu driver
 * @vm: domain
369
 * @job: job to process events for
370 371 372 373 374
 *
 * Update disk's mirror state in response to a block job event
 * from QEMU. For mirror state's that must survive libvirt
 * restart, also update the domain's status XML.
 */
375
static void
376 377
qemuBlockJobEventProcessLegacy(virQEMUDriverPtr driver,
                               virDomainObjPtr vm,
378 379
                               qemuBlockJobDataPtr job,
                               int asyncJob)
380
{
381
    VIR_AUTOUNREF(virQEMUDriverConfigPtr) cfg = virQEMUDriverGetConfig(driver);
382
    virDomainDiskDefPtr disk = job->disk;
383

384
    VIR_DEBUG("disk=%s, mirrorState=%s, type=%d, state=%d, newstate=%d",
385 386
              disk->dst,
              NULLSTR(virDomainDiskMirrorStateTypeToString(disk->mirrorState)),
387
              job->type,
388
              job->state,
389
              job->newstate);
390

391 392 393
    if (job->newstate == -1)
        return;

394
    qemuBlockJobEmitEvents(driver, vm, disk, job->type, job->newstate);
395

396 397 398
    job->state = job->newstate;
    job->newstate = -1;

399 400
    /* If we completed a block pull or commit, then update the XML
     * to match.  */
401
    switch ((virConnectDomainEventBlockJobStatus) job->state) {
402
    case VIR_DOMAIN_BLOCK_JOB_COMPLETED:
403
        qemuBlockJobEventProcessLegacyCompleted(driver, vm, job, asyncJob);
404 405 406 407 408 409 410 411
        break;

    case VIR_DOMAIN_BLOCK_JOB_READY:
        disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_READY;
        break;

    case VIR_DOMAIN_BLOCK_JOB_FAILED:
    case VIR_DOMAIN_BLOCK_JOB_CANCELED:
412 413
        if (disk->mirror) {
            virDomainLockImageDetach(driver->lockManager, vm, disk->mirror);
414
            virObjectUnref(disk->mirror);
415 416
            disk->mirror = NULL;
        }
417
        disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_NONE;
418
        disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN;
419
        qemuBlockJobUnregister(job, vm);
420 421 422 423 424 425
        break;

    case VIR_DOMAIN_BLOCK_JOB_LAST:
        break;
    }

426 427
    if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->caps) < 0)
        VIR_WARN("Unable to save status on vm %s after block job", vm->def->name);
428
}
429 430


431
/**
432
 * qemuBlockJobUpdate:
433
 * @vm: domain
434 435
 * @job: job data
 * @asyncJob: current qemu asynchronous job type
436 437 438 439 440 441 442
 *
 * Update disk's mirror state in response to a block job event stored in
 * blockJobStatus by qemuProcessHandleBlockJob event handler.
 *
 * Returns the block job event processed or -1 if there was no pending event.
 */
int
443 444 445
qemuBlockJobUpdate(virDomainObjPtr vm,
                   qemuBlockJobDataPtr job,
                   int asyncJob)
446 447 448
{
    qemuDomainObjPrivatePtr priv = vm->privateData;

449 450 451 452
    if (job->newstate == -1)
        return -1;

    qemuBlockJobEventProcessLegacy(priv->driver, vm, job, asyncJob);
453

454
    return job->state;
455 456 457
}


458
/**
459 460
 * qemuBlockJobSyncBegin:
 * @job: block job data
461 462 463
 * @disk: domain disk
 *
 * Begin a new synchronous block job for @disk. The synchronous
464
 * block job is ended by a call to qemuBlockJobSyncEnd, or by
465 466 467 468
 * the guest quitting.
 *
 * During a synchronous block job, a block job event for @disk
 * will not be processed asynchronously. Instead, it will be
469
 * processed only when qemuBlockJobUpdate or qemuBlockJobSyncEnd
470
 * is called.
471 472
 */
void
473
qemuBlockJobSyncBegin(qemuBlockJobDataPtr job)
474
{
475
    const char *diskdst = NULL;
476

477 478 479 480
    if (job->disk)
        diskdst = job->disk->dst;

    VIR_DEBUG("disk=%s", NULLSTR(diskdst));
481
    job->synchronous = true;
482 483 484 485
}


/**
486
 * qemuBlockJobSyncEnd:
487 488 489 490
 * @vm: domain
 * @disk: domain disk
 *
 * End a synchronous block job for @disk. Any pending block job event
491 492 493
 * for the disk is processed. Note that it's not necessary to call this function
 * in case the block job was not started successfully if
 * qemuBlockJobStartupFinalize will be called.
494 495
 */
void
496 497 498
qemuBlockJobSyncEnd(virDomainObjPtr vm,
                    qemuBlockJobDataPtr job,
                    int asyncJob)
499
{
500
    const char *diskdst = NULL;
501

502 503
    if (job->disk)
        diskdst = job->disk->dst;
504

505
    VIR_DEBUG("disk=%s", NULLSTR(diskdst));
506 507
    qemuBlockJobUpdate(vm, job, asyncJob);
    job->synchronous = false;
508
}
509 510 511 512 513 514 515 516 517 518 519 520


qemuBlockJobDataPtr
qemuBlockJobGetByDisk(virDomainDiskDefPtr disk)
{
    qemuBlockJobDataPtr job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob;

    if (!job)
        return NULL;

    return virObjectRef(job);
}