qemu_blockjob.c 9.3 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 40 41 42 43

#define VIR_FROM_THIS VIR_FROM_QEMU

VIR_LOG_INIT("qemu.qemu_blockjob");

44

45 46 47 48 49 50 51 52 53 54 55
void
qemuBlockJobDataFree(qemuBlockJobDataPtr job)
{
    if (!job)
        return;

    VIR_FREE(job->errmsg);
    VIR_FREE(job);
}


56 57 58 59
/**
 * qemuBlockJobEmitEvents:
 *
 * Emits the VIR_DOMAIN_EVENT_ID_BLOCK_JOB and VIR_DOMAIN_EVENT_ID_BLOCK_JOB_2
60
 * for a block job. The former event is emitted only for local disks.
61 62 63 64 65 66 67 68 69 70 71
 */
static void
qemuBlockJobEmitEvents(virQEMUDriverPtr driver,
                       virDomainObjPtr vm,
                       virDomainDiskDefPtr disk,
                       virDomainBlockJobType type,
                       virConnectDomainEventBlockJobStatus status)
{
    virObjectEventPtr event = NULL;
    virObjectEventPtr event2 = NULL;

72 73 74 75 76 77
    if (virStorageSourceIsLocalStorage(disk->src) &&
        !virStorageSourceIsEmpty(disk->src)) {
        event = virDomainEventBlockJobNewFromObj(vm, virDomainDiskGetSource(disk),
                                                 type, status);
        virObjectEventStateQueue(driver->domainEventState, event);
    }
78 79 80 81 82 83

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


84 85 86 87 88 89 90 91 92 93 94 95
/**
 * qemuBlockJobEventProcess:
 * @driver: qemu driver
 * @vm: domain
 * @disk: domain disk
 * @type: block job type
 * @status: block job status
 *
 * 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.
 */
96
static void
97 98 99
qemuBlockJobEventProcess(virQEMUDriverPtr driver,
                         virDomainObjPtr vm,
                         virDomainDiskDefPtr disk,
100
                         int asyncJob,
101 102 103 104 105
                         int type,
                         int status)
{
    virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
    virDomainDiskDefPtr persistDisk = NULL;
106
    qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk);
107

108 109 110 111 112 113
    VIR_DEBUG("disk=%s, mirrorState=%s, type=%d, status=%d",
              disk->dst,
              NULLSTR(virDomainDiskMirrorStateTypeToString(disk->mirrorState)),
              type,
              status);

114 115 116
    if (type == VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT &&
        disk->mirrorJob == VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT)
        type = disk->mirrorJob;
117 118

    qemuBlockJobEmitEvents(driver, vm, disk, type, status);
119 120 121 122 123 124 125 126 127

    /* If we completed a block pull or commit, then update the XML
     * to match.  */
    switch ((virConnectDomainEventBlockJobStatus) status) {
    case VIR_DOMAIN_BLOCK_JOB_COMPLETED:
        if (disk->mirrorState == VIR_DOMAIN_DISK_MIRROR_STATE_PIVOT) {
            if (vm->newDef) {
                virStorageSourcePtr copy = NULL;

128 129
                if ((persistDisk = virDomainDiskByName(vm->newDef,
                                                       disk->dst, false))) {
130
                    copy = virStorageSourceCopy(disk->mirror, false);
131 132
                    if (!copy ||
                        virStorageSourceInitChainElement(copy,
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
                                                         persistDisk->src,
                                                         true) < 0) {
                        VIR_WARN("Unable to update persistent definition "
                                 "on vm %s after block job",
                                 vm->def->name);
                        virStorageSourceFree(copy);
                        copy = NULL;
                        persistDisk = NULL;
                    }
                }
                if (copy) {
                    virStorageSourceFree(persistDisk->src);
                    persistDisk->src = copy;
                }
            }

149 150 151 152 153 154
            /* 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);
155 156 157
            virStorageSourceFree(disk->src);
            disk->src = disk->mirror;
        } else {
158 159 160 161
            if (disk->mirror) {
                virDomainLockImageDetach(driver->lockManager, vm, disk->mirror);
                virStorageSourceFree(disk->mirror);
            }
162 163 164 165 166 167 168 169 170
        }

        /* 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;
171
        disk->src->id = 0;
172 173
        virStorageSourceBackingStoreClear(disk->src);
        ignore_value(qemuDomainDetermineDiskChain(driver, vm, disk, true));
174
        ignore_value(qemuBlockNodeNamesDetect(driver, vm, asyncJob));
175
        diskPriv->blockjob->started = false;
176 177 178 179 180 181 182 183
        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:
184 185 186 187 188
        if (disk->mirror) {
            virDomainLockImageDetach(driver->lockManager, vm, disk->mirror);
            virStorageSourceFree(disk->mirror);
            disk->mirror = NULL;
        }
189
        disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_NONE;
190
        disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN;
191
        diskPriv->blockjob->started = false;
192 193 194 195 196 197
        break;

    case VIR_DOMAIN_BLOCK_JOB_LAST:
        break;
    }

198 199 200
    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);

201 202 203 204 205
    if (status == VIR_DOMAIN_BLOCK_JOB_COMPLETED && vm->newDef) {
        if (virDomainSaveConfig(cfg->configDir, driver->caps, vm->newDef) < 0)
            VIR_WARN("Unable to update persistent definition on vm %s "
                     "after block job", vm->def->name);
    }
206 207 208

    virObjectUnref(cfg);
}
209 210


211
/**
212
 * qemuBlockJobUpdateDisk:
213 214 215 216 217 218 219 220 221 222
 * @vm: domain
 * @disk: domain disk
 * @error: error (output parameter)
 *
 * 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
223
qemuBlockJobUpdateDisk(virDomainObjPtr vm,
224
                       int asyncJob,
225 226
                       virDomainDiskDefPtr disk,
                       char **error)
227
{
228
    qemuBlockJobDataPtr job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob;
229
    qemuDomainObjPrivatePtr priv = vm->privateData;
230
    int status = job->status;
231 232 233 234 235 236

    if (error)
        *error = NULL;

    if (status != -1) {
        qemuBlockJobEventProcess(priv->driver, vm, disk, asyncJob,
237 238
                                 job->type, status);
        job->status = -1;
239
        if (error)
240
            VIR_STEAL_PTR(*error, job->errmsg);
241
        else
242
            VIR_FREE(job->errmsg);
243 244 245 246 247 248
    }

    return status;
}


249
/**
250
 * qemuBlockJobSyncBeginDisk:
251 252 253
 * @disk: domain disk
 *
 * Begin a new synchronous block job for @disk. The synchronous
254
 * block job is ended by a call to qemuBlockJobSyncEndDisk, or by
255 256 257 258
 * the guest quitting.
 *
 * During a synchronous block job, a block job event for @disk
 * will not be processed asynchronously. Instead, it will be
259
 * processed only when qemuBlockJobUpdateDisk or qemuBlockJobSyncEndDisk
260
 * is called.
261 262
 */
void
263
qemuBlockJobSyncBeginDisk(virDomainDiskDefPtr disk)
264
{
265
    qemuBlockJobDataPtr job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob;
266

267
    VIR_DEBUG("disk=%s", disk->dst);
268 269
    job->synchronous = true;
    job->status = -1;
270 271 272 273
}


/**
274
 * qemuBlockJobSyncEndDisk:
275 276 277 278
 * @vm: domain
 * @disk: domain disk
 *
 * End a synchronous block job for @disk. Any pending block job event
279
 * for the disk is processed.
280 281
 */
void
282
qemuBlockJobSyncEndDisk(virDomainObjPtr vm,
283
                        int asyncJob,
284
                        virDomainDiskDefPtr disk)
285
{
286
    VIR_DEBUG("disk=%s", disk->dst);
287
    qemuBlockJobUpdateDisk(vm, asyncJob, disk, NULL);
288
    QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob->synchronous = false;
289
}