S: Supported
F: blockjob.c
F: include/block/blockjob.h
+F: job.c
+F: include/block/job.h
F: block/backup.c
F: block/commit.c
F: block/stream.c
# block-obj-y is code used by both qemu system emulation and qemu-img
block-obj-y += nbd/
-block-obj-y += block.o blockjob.o
+block-obj-y += block.o blockjob.o job.o
block-obj-y += block/ scsi/
block-obj-y += qemu-io-cmds.o
block-obj-$(CONFIG_REPLICATION) += replication.o
}
static const BlockJobDriver backup_job_driver = {
- .instance_size = sizeof(BackupBlockJob),
+ .job_driver = {
+ .instance_size = sizeof(BackupBlockJob),
+ },
.job_type = BLOCK_JOB_TYPE_BACKUP,
.start = backup_run,
.commit = backup_commit,
}
static const BlockJobDriver commit_job_driver = {
- .instance_size = sizeof(CommitBlockJob),
+ .job_driver = {
+ .instance_size = sizeof(CommitBlockJob),
+ },
.job_type = BLOCK_JOB_TYPE_COMMIT,
.start = commit_run,
};
if (!s->synced) {
error_setg(errp, "The active block job '%s' cannot be completed",
- job->id);
+ job->job.id);
return;
}
}
static const BlockJobDriver mirror_job_driver = {
- .instance_size = sizeof(MirrorBlockJob),
+ .job_driver = {
+ .instance_size = sizeof(MirrorBlockJob),
+ },
.job_type = BLOCK_JOB_TYPE_MIRROR,
.start = mirror_run,
.complete = mirror_complete,
};
static const BlockJobDriver commit_active_job_driver = {
- .instance_size = sizeof(MirrorBlockJob),
+ .job_driver = {
+ .instance_size = sizeof(MirrorBlockJob),
+ },
.job_type = BLOCK_JOB_TYPE_COMMIT,
.start = mirror_run,
.complete = mirror_complete,
}
static const BlockJobDriver stream_job_driver = {
- .instance_size = sizeof(StreamBlockJob),
+ .job_driver = {
+ .instance_size = sizeof(StreamBlockJob),
+ },
.job_type = BLOCK_JOB_TYPE_STREAM,
.start = stream_run,
};
#include "qapi/qapi-events-block-core.h"
#include "qapi/qmp/qerror.h"
#include "qemu/coroutine.h"
-#include "qemu/id.h"
#include "qemu/timer.h"
/* Right now, this mutex is only needed to synchronize accesses to job->busy
return 0;
}
error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'",
- job->id, BlockJobStatus_str(job->status), BlockJobVerb_str(bv));
+ job->job.id, BlockJobStatus_str(job->status),
+ BlockJobVerb_str(bv));
return -EPERM;
}
BlockJob *job;
QLIST_FOREACH(job, &block_jobs, job_list) {
- if (job->id && !strcmp(id, job->id)) {
+ if (job->job.id && !strcmp(id, job->job.id)) {
return job;
}
}
block_job_detach_aio_context, job);
blk_unref(job->blk);
error_free(job->blocker);
- g_free(job->id);
+ g_free(job->job.id);
assert(!timer_pending(&job->sleep_timer));
g_free(job);
}
BlockJob *job = c->opaque;
return g_strdup_printf("%s job '%s'",
BlockJobType_str(job->driver->job_type),
- job->id);
+ job->job.id);
}
static void child_job_drained_begin(BdrvChild *c)
bool block_job_is_internal(BlockJob *job)
{
- return (job->id == NULL);
+ return (job->job.id == NULL);
}
static bool block_job_started(BlockJob *job)
void block_job_complete(BlockJob *job, Error **errp)
{
/* Should not be reachable via external interface for internal jobs */
- assert(job->id);
+ assert(job->job.id);
if (block_job_apply_verb(job, BLOCK_JOB_VERB_COMPLETE, errp)) {
return;
}
if (job->pause_count || job->cancelled || !job->driver->complete) {
error_setg(errp, "The active block job '%s' cannot be completed",
- job->id);
+ job->job.id);
return;
}
void block_job_finalize(BlockJob *job, Error **errp)
{
- assert(job && job->id);
+ assert(job && job->job.id);
if (block_job_apply_verb(job, BLOCK_JOB_VERB_FINALIZE, errp)) {
return;
}
{
BlockJob *job = *jobptr;
/* similarly to _complete, this is QMP-interface only. */
- assert(job->id);
+ assert(job->job.id);
if (block_job_apply_verb(job, BLOCK_JOB_VERB_DISMISS, errp)) {
return;
}
}
info = g_new0(BlockJobInfo, 1);
info->type = g_strdup(BlockJobType_str(job->driver->job_type));
- info->device = g_strdup(job->id);
+ info->device = g_strdup(job->job.id);
info->len = job->len;
info->busy = atomic_read(&job->busy);
info->paused = job->pause_count > 0;
}
qapi_event_send_block_job_cancelled(job->driver->job_type,
- job->id,
+ job->job.id,
job->len,
job->offset,
job->speed,
}
qapi_event_send_block_job_completed(job->driver->job_type,
- job->id,
+ job->job.id,
job->len,
job->offset,
job->speed,
block_job_state_transition(job, BLOCK_JOB_STATUS_PENDING);
if (!job->auto_finalize && !block_job_is_internal(job)) {
qapi_event_send_block_job_pending(job->driver->job_type,
- job->id,
+ job->job.id,
&error_abort);
}
return 0;
error_setg(errp, "Cannot specify job ID for internal block job");
return NULL;
}
-
- if (!id_wellformed(job_id)) {
- error_setg(errp, "Invalid job ID '%s'", job_id);
- return NULL;
- }
-
if (block_job_get(job_id)) {
error_setg(errp, "Job ID '%s' already in use", job_id);
return NULL;
return NULL;
}
- job = g_malloc0(driver->instance_size);
+ job = job_create(job_id, &driver->job_driver, errp);
+ if (job == NULL) {
+ blk_unref(blk);
+ return NULL;
+ }
+
job->driver = driver;
- job->id = g_strdup(job_id);
job->blk = blk;
job->cb = cb;
job->opaque = opaque;
}
qapi_event_send_block_job_ready(job->driver->job_type,
- job->id,
+ job->job.id,
job->len,
job->offset,
job->speed, &error_abort);
abort();
}
if (!block_job_is_internal(job)) {
- qapi_event_send_block_job_error(job->id,
+ qapi_event_send_block_job_error(job->job.id,
is_read ? IO_OPERATION_TYPE_READ :
IO_OPERATION_TYPE_WRITE,
action, &error_abort);
#ifndef BLOCKJOB_H
#define BLOCKJOB_H
+#include "qemu/job.h"
#include "block/block.h"
#include "qemu/ratelimit.h"
* Long-running operation on a BlockDriverState.
*/
typedef struct BlockJob {
+ /** Data belonging to the generic Job infrastructure */
+ Job job;
+
/** The job type, including the job vtable. */
const BlockJobDriver *driver;
/** The block device on which the job is operating. */
BlockBackend *blk;
- /**
- * The ID of the block job. May be NULL for internal jobs.
- */
- char *id;
-
/**
* The coroutine that executes the job. If not NULL, it is
* reentered when busy is false and the job is cancelled.
* A class type for block job driver.
*/
struct BlockJobDriver {
- /** Derived BlockJob struct size */
- size_t instance_size;
+ /** Generic JobDriver callbacks and settings */
+ JobDriver job_driver;
/** String describing the operation, part of query-block-jobs QMP API */
BlockJobType job_type;
--- /dev/null
+/*
+ * Declarations for background jobs
+ *
+ * Copyright (c) 2011 IBM Corp.
+ * Copyright (c) 2012, 2018 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef JOB_H
+#define JOB_H
+
+typedef struct JobDriver JobDriver;
+
+/**
+ * Long-running operation.
+ */
+typedef struct Job {
+ /** The ID of the job. May be NULL for internal jobs. */
+ char *id;
+
+ /** The type of this job. */
+ const JobDriver *driver;
+} Job;
+
+/**
+ * Callbacks and other information about a Job driver.
+ */
+struct JobDriver {
+ /** Derived Job struct size */
+ size_t instance_size;
+};
+
+
+/**
+ * Create a new long-running job and return it.
+ *
+ * @job_id: The id of the newly-created job, or %NULL for internal jobs
+ * @driver: The class object for the newly-created job.
+ * @errp: Error object.
+ */
+void *job_create(const char *job_id, const JobDriver *driver, Error **errp);
+
+#endif
--- /dev/null
+/*
+ * Background jobs (long-running operations)
+ *
+ * Copyright (c) 2011 IBM Corp.
+ * Copyright (c) 2012, 2018 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qapi/error.h"
+#include "qemu/job.h"
+#include "qemu/id.h"
+
+void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
+{
+ Job *job;
+
+ if (job_id) {
+ if (!id_wellformed(job_id)) {
+ error_setg(errp, "Invalid job ID '%s'", job_id);
+ return NULL;
+ }
+ }
+
+ job = g_malloc0(driver->instance_size);
+ job->driver = driver;
+ job->id = g_strdup(job_id);
+
+ return job;
+}
}
BlockJobDriver test_job_driver = {
- .instance_size = sizeof(TestBlockJob),
+ .job_driver = {
+ .instance_size = sizeof(TestBlockJob),
+ },
.start = test_job_start,
.complete = test_job_complete,
};
}
static const BlockJobDriver test_block_job_driver = {
- .instance_size = sizeof(TestBlockJob),
+ .job_driver = {
+ .instance_size = sizeof(TestBlockJob),
+ },
.start = test_block_job_run,
};
#include "sysemu/block-backend.h"
static const BlockJobDriver test_block_job_driver = {
- .instance_size = sizeof(BlockJob),
+ .job_driver = {
+ .instance_size = sizeof(BlockJob),
+ },
};
static void block_job_cb(void *opaque, int ret)
g_assert_null(errp);
g_assert_nonnull(job);
if (id) {
- g_assert_cmpstr(job->id, ==, id);
+ g_assert_cmpstr(job->job.id, ==, id);
} else {
- g_assert_cmpstr(job->id, ==, blk_name(blk));
+ g_assert_cmpstr(job->job.id, ==, blk_name(blk));
}
} else {
g_assert_nonnull(errp);
}
static const BlockJobDriver test_cancel_driver = {
- .instance_size = sizeof(CancelJob),
+ .job_driver = {
+ .instance_size = sizeof(CancelJob),
+ },
.start = cancel_job_start,
.complete = cancel_job_complete,
};