aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2016-11-01 14:27:05 +0000
committerPeter Maydell <peter.maydell@linaro.org>2016-11-01 14:27:05 +0000
commitc46ef897dbb75590af94b6e3dca6a9c5f9a1ea1e (patch)
tree5413270f5e465e029c17967c15e968f3df5ae591
parent39542105bbb19c690219d2f22844d8dfbd9bba05 (diff)
parentd8996368106fbf133a6e52561a34f6d0f5080446 (diff)
downloadqemu-c46ef897dbb75590af94b6e3dca6a9c5f9a1ea1e.zip
qemu-c46ef897dbb75590af94b6e3dca6a9c5f9a1ea1e.tar.gz
qemu-c46ef897dbb75590af94b6e3dca6a9c5f9a1ea1e.tar.bz2
Merge remote-tracking branch 'remotes/cody/tags/block-pull-request' into staging
# gpg: Signature made Tue 01 Nov 2016 12:47:36 GMT # gpg: using RSA key 0xBDBE7B27C0DE3057 # gpg: Good signature from "Jeffrey Cody <jcody@redhat.com>" # gpg: aka "Jeffrey Cody <jeff@codyprime.org>" # gpg: aka "Jeffrey Cody <codyprime@gmail.com>" # Primary key fingerprint: 9957 4B4D 3474 90E7 9D98 D624 BDBE 7B27 C0DE 3057 * remotes/cody/tags/block-pull-request: blockjobs: fix documentation blockjobs: split interface into public/private, Part 1 Blockjobs: Internalize user_pause logic blockjob: centralize QMP event emissions Replication/Blockjobs: Create replication jobs as internal blockjobs: Allow creating internal jobs blockjobs: hide internal jobs from management API block/gluster: fix port type in the QAPI options list block/gluster: improve defense over string to int conversion block: Turn on "unmap" in active commit block/gluster: memory usage: use one glfs instance per volume block: add gluster ifdef guard checks for SEEK_DATA/SEEK_HOLE support rbd: make the code more readable qapi: add release designator to gluster logfile option Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--block/backup.c5
-rw-r--r--block/commit.c10
-rw-r--r--block/gluster.c124
-rw-r--r--block/mirror.c30
-rw-r--r--block/rbd.c25
-rw-r--r--block/replication.c14
-rw-r--r--block/stream.c9
-rw-r--r--block/trace-events5
-rw-r--r--blockdev.c74
-rw-r--r--blockjob.c113
-rw-r--r--include/block/block.h3
-rw-r--r--include/block/block_int.h26
-rw-r--r--include/block/blockjob.h256
-rw-r--r--include/block/blockjob_int.h239
-rw-r--r--qapi/block-core.json2
-rw-r--r--qemu-img.c5
-rw-r--r--tests/test-blockjob-txn.c5
-rw-r--r--tests/test-blockjob.c4
18 files changed, 568 insertions, 381 deletions
diff --git a/block/backup.c b/block/backup.c
index 44c7ff3..7b5d8a3 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -16,7 +16,7 @@
#include "trace.h"
#include "block/block.h"
#include "block/block_int.h"
-#include "block/blockjob.h"
+#include "block/blockjob_int.h"
#include "block/block_backup.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
@@ -543,6 +543,7 @@ void backup_start(const char *job_id, BlockDriverState *bs,
bool compress,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error,
+ int creation_flags,
BlockCompletionFunc *cb, void *opaque,
BlockJobTxn *txn, Error **errp)
{
@@ -612,7 +613,7 @@ void backup_start(const char *job_id, BlockDriverState *bs,
}
job = block_job_create(job_id, &backup_job_driver, bs, speed,
- cb, opaque, errp);
+ creation_flags, cb, opaque, errp);
if (!job) {
goto error;
}
diff --git a/block/commit.c b/block/commit.c
index a5e17f6..e1eda89 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -15,7 +15,7 @@
#include "qemu/osdep.h"
#include "trace.h"
#include "block/block_int.h"
-#include "block/blockjob.h"
+#include "block/blockjob_int.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "qemu/ratelimit.h"
@@ -209,8 +209,8 @@ static const BlockJobDriver commit_job_driver = {
void commit_start(const char *job_id, BlockDriverState *bs,
BlockDriverState *base, BlockDriverState *top, int64_t speed,
- BlockdevOnError on_error, BlockCompletionFunc *cb,
- void *opaque, const char *backing_file_str, Error **errp)
+ BlockdevOnError on_error, const char *backing_file_str,
+ Error **errp)
{
CommitBlockJob *s;
BlockReopenQueue *reopen_queue = NULL;
@@ -234,7 +234,7 @@ void commit_start(const char *job_id, BlockDriverState *bs,
}
s = block_job_create(job_id, &commit_job_driver, bs, speed,
- cb, opaque, errp);
+ BLOCK_JOB_DEFAULT, NULL, NULL, errp);
if (!s) {
return;
}
@@ -290,7 +290,7 @@ void commit_start(const char *job_id, BlockDriverState *bs,
s->on_error = on_error;
s->common.co = qemu_coroutine_create(commit_run, s);
- trace_commit_start(bs, base, top, s, s->common.co, opaque);
+ trace_commit_start(bs, base, top, s, s->common.co);
qemu_coroutine_enter(s->common.co);
}
diff --git a/block/gluster.c b/block/gluster.c
index af76d7d..0ce15f7 100644
--- a/block/gluster.c
+++ b/block/gluster.c
@@ -14,6 +14,7 @@
#include "qapi/qmp/qerror.h"
#include "qemu/uri.h"
#include "qemu/error-report.h"
+#include "qemu/cutils.h"
#define GLUSTER_OPT_FILENAME "filename"
#define GLUSTER_OPT_VOLUME "volume"
@@ -56,6 +57,19 @@ typedef struct BDRVGlusterReopenState {
} BDRVGlusterReopenState;
+typedef struct GlfsPreopened {
+ char *volume;
+ glfs_t *fs;
+ int ref;
+} GlfsPreopened;
+
+typedef struct ListElement {
+ QLIST_ENTRY(ListElement) list;
+ GlfsPreopened saved;
+} ListElement;
+
+static QLIST_HEAD(glfs_list, ListElement) glfs_list;
+
static QemuOptsList qemu_gluster_create_opts = {
.name = "qemu-gluster-create-opts",
.head = QTAILQ_HEAD_INITIALIZER(qemu_gluster_create_opts.head),
@@ -172,7 +186,7 @@ static QemuOptsList runtime_tcp_opts = {
},
{
.name = GLUSTER_OPT_PORT,
- .type = QEMU_OPT_NUMBER,
+ .type = QEMU_OPT_STRING,
.help = "port number on which glusterd is listening (default 24007)",
},
{
@@ -194,6 +208,57 @@ static QemuOptsList runtime_tcp_opts = {
},
};
+static void glfs_set_preopened(const char *volume, glfs_t *fs)
+{
+ ListElement *entry = NULL;
+
+ entry = g_new(ListElement, 1);
+
+ entry->saved.volume = g_strdup(volume);
+
+ entry->saved.fs = fs;
+ entry->saved.ref = 1;
+
+ QLIST_INSERT_HEAD(&glfs_list, entry, list);
+}
+
+static glfs_t *glfs_find_preopened(const char *volume)
+{
+ ListElement *entry = NULL;
+
+ QLIST_FOREACH(entry, &glfs_list, list) {
+ if (strcmp(entry->saved.volume, volume) == 0) {
+ entry->saved.ref++;
+ return entry->saved.fs;
+ }
+ }
+
+ return NULL;
+}
+
+static void glfs_clear_preopened(glfs_t *fs)
+{
+ ListElement *entry = NULL;
+
+ if (fs == NULL) {
+ return;
+ }
+
+ QLIST_FOREACH(entry, &glfs_list, list) {
+ if (entry->saved.fs == fs) {
+ if (--entry->saved.ref) {
+ return;
+ }
+
+ QLIST_REMOVE(entry, list);
+
+ glfs_fini(entry->saved.fs);
+ g_free(entry->saved.volume);
+ g_free(entry);
+ }
+ }
+}
+
static int parse_volume_options(BlockdevOptionsGluster *gconf, char *path)
{
char *p, *q;
@@ -330,22 +395,37 @@ static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf,
int ret;
int old_errno;
GlusterServerList *server;
+ unsigned long long port;
+
+ glfs = glfs_find_preopened(gconf->volume);
+ if (glfs) {
+ return glfs;
+ }
glfs = glfs_new(gconf->volume);
if (!glfs) {
goto out;
}
+ glfs_set_preopened(gconf->volume, glfs);
+
for (server = gconf->server; server; server = server->next) {
if (server->value->type == GLUSTER_TRANSPORT_UNIX) {
ret = glfs_set_volfile_server(glfs,
GlusterTransport_lookup[server->value->type],
server->value->u.q_unix.path, 0);
} else {
+ if (parse_uint_full(server->value->u.tcp.port, &port, 10) < 0 ||
+ port > 65535) {
+ error_setg(errp, "'%s' is not a valid port number",
+ server->value->u.tcp.port);
+ errno = EINVAL;
+ goto out;
+ }
ret = glfs_set_volfile_server(glfs,
GlusterTransport_lookup[server->value->type],
server->value->u.tcp.host,
- atoi(server->value->u.tcp.port));
+ (int)port);
}
if (ret < 0) {
@@ -387,7 +467,7 @@ static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf,
out:
if (glfs) {
old_errno = errno;
- glfs_fini(glfs);
+ glfs_clear_preopened(glfs);
errno = old_errno;
}
return NULL;
@@ -668,7 +748,10 @@ static void qemu_gluster_parse_flags(int bdrv_flags, int *open_flags)
*/
static bool qemu_gluster_test_seek(struct glfs_fd *fd)
{
- off_t ret, eof;
+ off_t ret = 0;
+
+#if defined SEEK_HOLE && defined SEEK_DATA
+ off_t eof;
eof = glfs_lseek(fd, 0, SEEK_END);
if (eof < 0) {
@@ -678,6 +761,8 @@ static bool qemu_gluster_test_seek(struct glfs_fd *fd)
/* this should always fail with ENXIO if SEEK_DATA is supported */
ret = glfs_lseek(fd, eof, SEEK_DATA);
+#endif
+
return (ret < 0) && (errno == ENXIO);
}
@@ -762,9 +847,9 @@ out:
if (s->fd) {
glfs_close(s->fd);
}
- if (s->glfs) {
- glfs_fini(s->glfs);
- }
+
+ glfs_clear_preopened(s->glfs);
+
return ret;
}
@@ -831,9 +916,8 @@ static void qemu_gluster_reopen_commit(BDRVReopenState *state)
if (s->fd) {
glfs_close(s->fd);
}
- if (s->glfs) {
- glfs_fini(s->glfs);
- }
+
+ glfs_clear_preopened(s->glfs);
/* use the newly opened image / connection */
s->fd = reop_s->fd;
@@ -858,9 +942,7 @@ static void qemu_gluster_reopen_abort(BDRVReopenState *state)
glfs_close(reop_s->fd);
}
- if (reop_s->glfs) {
- glfs_fini(reop_s->glfs);
- }
+ glfs_clear_preopened(reop_s->glfs);
g_free(state->opaque);
state->opaque = NULL;
@@ -984,9 +1066,7 @@ static int qemu_gluster_create(const char *filename,
out:
g_free(tmp);
qapi_free_BlockdevOptionsGluster(gconf);
- if (glfs) {
- glfs_fini(glfs);
- }
+ glfs_clear_preopened(glfs);
return ret;
}
@@ -1059,7 +1139,7 @@ static void qemu_gluster_close(BlockDriverState *bs)
glfs_close(s->fd);
s->fd = NULL;
}
- glfs_fini(s->glfs);
+ glfs_clear_preopened(s->glfs);
}
static coroutine_fn int qemu_gluster_co_flush_to_disk(BlockDriverState *bs)
@@ -1178,12 +1258,14 @@ static int find_allocation(BlockDriverState *bs, off_t start,
off_t *data, off_t *hole)
{
BDRVGlusterState *s = bs->opaque;
- off_t offs;
if (!s->supports_seek_data) {
- return -ENOTSUP;
+ goto exit;
}
+#if defined SEEK_HOLE && defined SEEK_DATA
+ off_t offs;
+
/*
* SEEK_DATA cases:
* D1. offs == start: start is in data
@@ -1247,6 +1329,10 @@ static int find_allocation(BlockDriverState *bs, off_t start,
/* D1 and H1 */
return -EBUSY;
+#endif
+
+exit:
+ return -ENOTSUP;
}
/*
diff --git a/block/mirror.c b/block/mirror.c
index 7e99f3a..b2c1fb8 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -13,7 +13,7 @@
#include "qemu/osdep.h"
#include "trace.h"
-#include "block/blockjob.h"
+#include "block/blockjob_int.h"
#include "block/block_int.h"
#include "sysemu/block-backend.h"
#include "qapi/error.h"
@@ -937,9 +937,9 @@ static const BlockJobDriver commit_active_job_driver = {
};
static void mirror_start_job(const char *job_id, BlockDriverState *bs,
- BlockDriverState *target, const char *replaces,
- int64_t speed, uint32_t granularity,
- int64_t buf_size,
+ int creation_flags, BlockDriverState *target,
+ const char *replaces, int64_t speed,
+ uint32_t granularity, int64_t buf_size,
BlockMirrorBackingMode backing_mode,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error,
@@ -967,7 +967,8 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
buf_size = DEFAULT_MIRROR_BUF_SIZE;
}
- s = block_job_create(job_id, driver, bs, speed, cb, opaque, errp);
+ s = block_job_create(job_id, driver, bs, speed, creation_flags,
+ cb, opaque, errp);
if (!s) {
return;
}
@@ -1017,9 +1018,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
MirrorSyncMode mode, BlockMirrorBackingMode backing_mode,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error,
- bool unmap,
- BlockCompletionFunc *cb,
- void *opaque, Error **errp)
+ bool unmap, Error **errp)
{
bool is_none_mode;
BlockDriverState *base;
@@ -1030,17 +1029,16 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
}
is_none_mode = mode == MIRROR_SYNC_MODE_NONE;
base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL;
- mirror_start_job(job_id, bs, target, replaces,
+ mirror_start_job(job_id, bs, BLOCK_JOB_DEFAULT, target, replaces,
speed, granularity, buf_size, backing_mode,
- on_source_error, on_target_error, unmap, cb, opaque, errp,
+ on_source_error, on_target_error, unmap, NULL, NULL, errp,
&mirror_job_driver, is_none_mode, base, false);
}
void commit_active_start(const char *job_id, BlockDriverState *bs,
- BlockDriverState *base, int64_t speed,
- BlockdevOnError on_error,
- BlockCompletionFunc *cb,
- void *opaque, Error **errp,
+ BlockDriverState *base, int creation_flags,
+ int64_t speed, BlockdevOnError on_error,
+ BlockCompletionFunc *cb, void *opaque, Error **errp,
bool auto_complete)
{
int64_t length, base_length;
@@ -1079,9 +1077,9 @@ void commit_active_start(const char *job_id, BlockDriverState *bs,
}
}
- mirror_start_job(job_id, bs, base, NULL, speed, 0, 0,
+ mirror_start_job(job_id, bs, creation_flags, base, NULL, speed, 0, 0,
MIRROR_LEAVE_BACKING_CHAIN,
- on_error, on_error, false, cb, opaque, &local_err,
+ on_error, on_error, true, cb, opaque, &local_err,
&commit_active_job_driver, false, base, auto_complete);
if (local_err) {
error_propagate(errp, local_err);
diff --git a/block/rbd.c b/block/rbd.c
index f6e1d4b..a57b3e3 100644
--- a/block/rbd.c
+++ b/block/rbd.c
@@ -365,45 +365,44 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp)
rados_conf_read_file(cluster, NULL);
} else if (conf[0] != '\0' &&
qemu_rbd_set_conf(cluster, conf, true, &local_err) < 0) {
- rados_shutdown(cluster);
error_propagate(errp, local_err);
- return -EIO;
+ ret = -EIO;
+ goto shutdown;
}
if (conf[0] != '\0' &&
qemu_rbd_set_conf(cluster, conf, false, &local_err) < 0) {
- rados_shutdown(cluster);
error_propagate(errp, local_err);
- return -EIO;
+ ret = -EIO;
+ goto shutdown;
}
if (qemu_rbd_set_auth(cluster, secretid, errp) < 0) {
- rados_shutdown(cluster);
- return -EIO;
+ ret = -EIO;
+ goto shutdown;
}
ret = rados_connect(cluster);
if (ret < 0) {
error_setg_errno(errp, -ret, "error connecting");
- rados_shutdown(cluster);
- return ret;
+ goto shutdown;
}
ret = rados_ioctx_create(cluster, pool, &io_ctx);
if (ret < 0) {
error_setg_errno(errp, -ret, "error opening pool %s", pool);
- rados_shutdown(cluster);
- return ret;
+ goto shutdown;
}
ret = rbd_create(io_ctx, name, bytes, &obj_order);
- rados_ioctx_destroy(io_ctx);
- rados_shutdown(cluster);
if (ret < 0) {
error_setg_errno(errp, -ret, "error rbd create");
- return ret;
}
+ rados_ioctx_destroy(io_ctx);
+
+shutdown:
+ rados_shutdown(cluster);
return ret;
}
diff --git a/block/replication.c b/block/replication.c
index 02aeaaf..d5e2b0f 100644
--- a/block/replication.c
+++ b/block/replication.c
@@ -508,10 +508,11 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
bdrv_op_block_all(top_bs, s->blocker);
bdrv_op_unblock(top_bs, BLOCK_OP_TYPE_DATAPLANE, s->blocker);
- backup_start("replication-backup", s->secondary_disk->bs,
- s->hidden_disk->bs, 0, MIRROR_SYNC_MODE_NONE, NULL, false,
+ backup_start(NULL, s->secondary_disk->bs, s->hidden_disk->bs, 0,
+ MIRROR_SYNC_MODE_NONE, NULL, false,
BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT,
- backup_job_completed, bs, NULL, &local_err);
+ BLOCK_JOB_INTERNAL, backup_job_completed, bs,
+ NULL, &local_err);
if (local_err) {
error_propagate(errp, local_err);
backup_job_cleanup(bs);
@@ -633,10 +634,9 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
}
s->replication_state = BLOCK_REPLICATION_FAILOVER;
- commit_active_start("replication-commit", s->active_disk->bs,
- s->secondary_disk->bs, 0, BLOCKDEV_ON_ERROR_REPORT,
- replication_done,
- bs, errp, true);
+ commit_active_start(NULL, s->active_disk->bs, s->secondary_disk->bs,
+ BLOCK_JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT,
+ replication_done, bs, errp, true);
break;
default:
aio_context_release(aio_context);
diff --git a/block/stream.c b/block/stream.c
index b8ab89a..b05856b 100644
--- a/block/stream.c
+++ b/block/stream.c
@@ -14,7 +14,7 @@
#include "qemu/osdep.h"
#include "trace.h"
#include "block/block_int.h"
-#include "block/blockjob.h"
+#include "block/blockjob_int.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "qemu/ratelimit.h"
@@ -222,15 +222,14 @@ static const BlockJobDriver stream_job_driver = {
void stream_start(const char *job_id, BlockDriverState *bs,
BlockDriverState *base, const char *backing_file_str,
- int64_t speed, BlockdevOnError on_error,
- BlockCompletionFunc *cb, void *opaque, Error **errp)
+ int64_t speed, BlockdevOnError on_error, Error **errp)
{
StreamBlockJob *s;
BlockDriverState *iter;
int orig_bs_flags;
s = block_job_create(job_id, &stream_job_driver, bs, speed,
- cb, opaque, errp);
+ BLOCK_JOB_DEFAULT, NULL, NULL, errp);
if (!s) {
return;
}
@@ -256,6 +255,6 @@ void stream_start(const char *job_id, BlockDriverState *bs,
s->on_error = on_error;
s->common.co = qemu_coroutine_create(stream_run, s);
- trace_stream_start(bs, base, s, s->common.co, opaque);
+ trace_stream_start(bs, base, s, s->common.co);
qemu_coroutine_enter(s->common.co);
}
diff --git a/block/trace-events b/block/trace-events
index aff8a96..882c903 100644
--- a/block/trace-events
+++ b/block/trace-events
@@ -19,11 +19,11 @@ bdrv_co_do_copy_on_readv(void *bs, int64_t offset, unsigned int bytes, int64_t c
# block/stream.c
stream_one_iteration(void *s, int64_t sector_num, int nb_sectors, int is_allocated) "s %p sector_num %"PRId64" nb_sectors %d is_allocated %d"
-stream_start(void *bs, void *base, void *s, void *co, void *opaque) "bs %p base %p s %p co %p opaque %p"
+stream_start(void *bs, void *base, void *s, void *co) "bs %p base %p s %p co %p"
# block/commit.c
commit_one_iteration(void *s, int64_t sector_num, int nb_sectors, int is_allocated) "s %p sector_num %"PRId64" nb_sectors %d is_allocated %d"
-commit_start(void *bs, void *base, void *top, void *s, void *co, void *opaque) "bs %p base %p top %p s %p co %p opaque %p"
+commit_start(void *bs, void *base, void *top, void *s, void *co) "bs %p base %p top %p s %p co %p"
# block/mirror.c
mirror_start(void *bs, void *s, void *co, void *opaque) "bs %p s %p co %p opaque %p"
@@ -51,7 +51,6 @@ qmp_block_job_cancel(void *job) "job %p"
qmp_block_job_pause(void *job) "job %p"
qmp_block_job_resume(void *job) "job %p"
qmp_block_job_complete(void *job) "job %p"
-block_job_cb(void *bs, void *job, int ret) "bs %p job %p ret %d"
qmp_block_stream(void *bs, void *job) "bs %p job %p"
# block/raw-win32.c
diff --git a/blockdev.c b/blockdev.c
index ded1326..102ca9f 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -2905,31 +2905,6 @@ out:
aio_context_release(aio_context);
}
-static void block_job_cb(void *opaque, int ret)
-{
- /* Note that this function may be executed from another AioContext besides
- * the QEMU main loop. If you need to access anything that assumes the
- * QEMU global mutex, use a BH or introduce a mutex.
- */
-
- BlockDriverState *bs = opaque;
- const char *msg = NULL;
-
- trace_block_job_cb(bs, bs->job, ret);
-
- assert(bs->job);
-
- if (ret < 0) {
- msg = strerror(-ret);
- }
-
- if (block_job_is_cancelled(bs->job)) {
- block_job_event_cancelled(bs->job);
- } else {
- block_job_event_completed(bs->job, msg);
- }
-}
-
void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
bool has_base, const char *base,
bool has_base_node, const char *base_node,
@@ -3005,7 +2980,7 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
base_name = has_backing_file ? backing_file : base_name;
stream_start(has_job_id ? job_id : NULL, bs, base_bs, base_name,
- has_speed ? speed : 0, on_error, block_job_cb, bs, &local_err);
+ has_speed ? speed : 0, on_error, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto out;
@@ -3110,16 +3085,17 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
" but 'top' is the active layer");
goto out;
}
- commit_active_start(has_job_id ? job_id : NULL, bs, base_bs, speed,
- on_error, block_job_cb, bs, &local_err, false);
+ commit_active_start(has_job_id ? job_id : NULL, bs, base_bs,
+ BLOCK_JOB_DEFAULT, speed, on_error, NULL, NULL,
+ &local_err, false);
} else {
BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs);
if (bdrv_op_is_blocked(overlay_bs, BLOCK_OP_TYPE_COMMIT_TARGET, errp)) {
goto out;
}
commit_start(has_job_id ? job_id : NULL, bs, base_bs, top_bs, speed,
- on_error, block_job_cb, bs,
- has_backing_file ? backing_file : NULL, &local_err);
+ on_error, has_backing_file ? backing_file : NULL,
+ &local_err);
}
if (local_err != NULL) {
error_propagate(errp, local_err);
@@ -3239,7 +3215,8 @@ static void do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, Error **errp)
backup_start(backup->job_id, bs, target_bs, backup->speed, backup->sync,
bmap, backup->compress, backup->on_source_error,
- backup->on_target_error, block_job_cb, bs, txn, &local_err);
+ backup->on_target_error, BLOCK_JOB_DEFAULT,
+ NULL, NULL, txn, &local_err);
bdrv_unref(target_bs);
if (local_err != NULL) {
error_propagate(errp, local_err);
@@ -3309,7 +3286,8 @@ void do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, Error **errp)
}
backup_start(backup->job_id, bs, target_bs, backup->speed, backup->sync,
NULL, backup->compress, backup->on_source_error,
- backup->on_target_error, block_job_cb, bs, txn, &local_err);
+ backup->on_target_error, BLOCK_JOB_DEFAULT,
+ NULL, NULL, txn, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
}
@@ -3388,8 +3366,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
mirror_start(job_id, bs, target,
has_replaces ? replaces : NULL,
speed, granularity, buf_size, sync, backing_mode,
- on_source_error, on_target_error, unmap,
- block_job_cb, bs, errp);
+ on_source_error, on_target_error, unmap, errp);
}
void qmp_drive_mirror(DriveMirror *arg, Error **errp)
@@ -3633,7 +3610,7 @@ void qmp_block_job_cancel(const char *device,
force = false;
}
- if (job->user_paused && !force) {
+ if (block_job_user_paused(job) && !force) {
error_setg(errp, "The block job for device '%s' is currently paused",
device);
goto out;
@@ -3650,13 +3627,12 @@ void qmp_block_job_pause(const char *device, Error **errp)
AioContext *aio_context;
BlockJob *job = find_block_job(device, &aio_context, errp);
- if (!job || job->user_paused) {
+ if (!job || block_job_user_paused(job)) {
return;
}
- job->user_paused = true;
trace_qmp_block_job_pause(job);
- block_job_pause(job);
+ block_job_user_pause(job);
aio_context_release(aio_context);
}
@@ -3665,14 +3641,13 @@ void qmp_block_job_resume(const char *device, Error **errp)
AioContext *aio_context;
BlockJob *job = find_block_job(device, &aio_context, errp);
- if (!job || !job->user_paused) {
+ if (!job || !block_job_user_paused(job)) {
return;
}
- job->user_paused = false;
trace_qmp_block_job_resume(job);
block_job_iostatus_reset(job);
- block_job_resume(job);
+ block_job_user_resume(job);
aio_context_release(aio_context);
}
@@ -3946,13 +3921,22 @@ BlockJobInfoList *qmp_query_block_jobs(Error **errp)
BlockJob *job;
for (job = block_job_next(NULL); job; job = block_job_next(job)) {
- BlockJobInfoList *elem = g_new0(BlockJobInfoList, 1);
- AioContext *aio_context = blk_get_aio_context(job->blk);
+ BlockJobInfoList *elem;
+ AioContext *aio_context;
+ if (block_job_is_internal(job)) {
+ continue;
+ }
+ elem = g_new0(BlockJobInfoList, 1);
+ aio_context = blk_get_aio_context(job->blk);
aio_context_acquire(aio_context);
- elem->value = block_job_query(job);
+ elem->value = block_job_query(job, errp);
aio_context_release(aio_context);
-
+ if (!elem->value) {
+ g_free(elem);
+ qapi_free_BlockJobInfoList(head);
+ return NULL;
+ }
*p_next = elem;
p_next = &elem->next;
}
diff --git a/blockjob.c b/blockjob.c
index 422851f..4aa14a4 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -27,7 +27,7 @@
#include "qemu-common.h"
#include "trace.h"
#include "block/block.h"
-#include "block/blockjob.h"
+#include "block/blockjob_int.h"
#include "block/block_int.h"
#include "sysemu/block-backend.h"
#include "qapi/qmp/qerror.h"
@@ -38,6 +38,9 @@
#include "qemu/timer.h"
#include "qapi-event.h"
+static void block_job_event_cancelled(BlockJob *job);
+static void block_job_event_completed(BlockJob *job, const char *msg);
+
/* Transactional group of block jobs */
struct BlockJobTxn {
@@ -66,7 +69,7 @@ BlockJob *block_job_get(const char *id)
BlockJob *job;
QLIST_FOREACH(job, &block_jobs, job_list) {
- if (!strcmp(id, job->id)) {
+ if (job->id && !strcmp(id, job->id)) {
return job;
}
}
@@ -121,19 +124,18 @@ void block_job_add_bdrv(BlockJob *job, BlockDriverState *bs)
}
void *block_job_create(const char *job_id, const BlockJobDriver *driver,
- BlockDriverState *bs, int64_t speed,
+ BlockDriverState *bs, int64_t speed, int flags,
BlockCompletionFunc *cb, void *opaque, Error **errp)
{
BlockBackend *blk;
BlockJob *job;
- assert(cb);
if (bs->job) {
error_setg(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bs));
return NULL;
}
- if (job_id == NULL) {
+ if (job_id == NULL && !(flags & BLOCK_JOB_INTERNAL)) {
job_id = bdrv_get_device_name(bs);
if (!*job_id) {
error_setg(errp, "An explicit job ID is required for this node");
@@ -141,14 +143,21 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
}
}
- if (!id_wellformed(job_id)) {
- error_setg(errp, "Invalid job ID '%s'", job_id);
- return NULL;
- }
+ if (job_id) {
+ if (flags & BLOCK_JOB_INTERNAL) {
+ error_setg(errp, "Cannot specify job ID for internal block job");
+ return NULL;
+ }
- if (block_job_get(job_id)) {
- error_setg(errp, "Job ID '%s' already in use", job_id);
- 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;
+ }
}
blk = blk_new();
@@ -188,6 +197,11 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
return job;
}
+bool block_job_is_internal(BlockJob *job)
+{
+ return (job->id == NULL);
+}
+
void block_job_ref(BlockJob *job)
{
++job->refcnt;
@@ -227,7 +241,20 @@ static void block_job_completed_single(BlockJob *job)
job->driver->abort(job);
}
}
- job->cb(job->opaque, job->ret);
+
+ if (job->cb) {
+ job->cb(job->opaque, job->ret);
+ }
+ if (block_job_is_cancelled(job)) {
+ block_job_event_cancelled(job);
+ } else {
+ const char *msg = NULL;
+ if (job->ret < 0) {
+ msg = strerror(-job->ret);
+ }
+ block_job_event_completed(job, msg);
+ }
+
if (job->txn) {
block_job_txn_unref(job->txn);
}
@@ -330,6 +357,8 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
void block_job_complete(BlockJob *job, Error **errp)
{
+ /* Should not be reachable via external interface for internal jobs */
+ assert(job->id);
if (job->pause_count || job->cancelled || !job->driver->complete) {
error_setg(errp, "The active block job '%s' cannot be completed",
job->id);
@@ -344,11 +373,22 @@ void block_job_pause(BlockJob *job)
job->pause_count++;
}
+void block_job_user_pause(BlockJob *job)
+{
+ job->user_paused = true;
+ block_job_pause(job);
+}
+
static bool block_job_should_pause(BlockJob *job)
{
return job->pause_count > 0;
}
+bool block_job_user_paused(BlockJob *job)
+{
+ return job ? job->user_paused : 0;
+}
+
void coroutine_fn block_job_pause_point(BlockJob *job)
{
if (!block_job_should_pause(job)) {
@@ -385,6 +425,14 @@ void block_job_resume(BlockJob *job)
block_job_enter(job);
}
+void block_job_user_resume(BlockJob *job)
+{
+ if (job && job->user_paused && job->pause_count > 0) {
+ job->user_paused = false;
+ block_job_resume(job);
+ }
+}
+
void block_job_enter(BlockJob *job)
{
if (job->co && !job->busy) {
@@ -510,9 +558,15 @@ void block_job_yield(BlockJob *job)
block_job_pause_point(job);
}
-BlockJobInfo *block_job_query(BlockJob *job)
+BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
{
- BlockJobInfo *info = g_new0(BlockJobInfo, 1);
+ BlockJobInfo *info;
+
+ if (block_job_is_internal(job)) {
+ error_setg(errp, "Cannot query QEMU internal jobs");
+ return NULL;
+ }
+ info = g_new0(BlockJobInfo, 1);
info->type = g_strdup(BlockJobType_lookup[job->driver->job_type]);
info->device = g_strdup(job->id);
info->len = job->len;
@@ -533,8 +587,12 @@ static void block_job_iostatus_set_err(BlockJob *job, int error)
}
}
-void block_job_event_cancelled(BlockJob *job)
+static void block_job_event_cancelled(BlockJob *job)
{
+ if (block_job_is_internal(job)) {
+ return;
+ }
+
qapi_event_send_block_job_cancelled(job->driver->job_type,
job->id,
job->len,
@@ -543,8 +601,12 @@ void block_job_event_cancelled(BlockJob *job)
&error_abort);
}
-void block_job_event_completed(BlockJob *job, const char *msg)
+static void block_job_event_completed(BlockJob *job, const char *msg)
{
+ if (block_job_is_internal(job)) {
+ return;
+ }
+
qapi_event_send_block_job_completed(job->driver->job_type,
job->id,
job->len,
@@ -559,6 +621,10 @@ void block_job_event_ready(BlockJob *job)
{
job->ready = true;
+ if (block_job_is_internal(job)) {
+ return;
+ }
+
qapi_event_send_block_job_ready(job->driver->job_type,
job->id,
job->len,
@@ -589,14 +655,15 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err,
default:
abort();
}
- qapi_event_send_block_job_error(job->id,
- is_read ? IO_OPERATION_TYPE_READ :
- IO_OPERATION_TYPE_WRITE,
- action, &error_abort);
+ if (!block_job_is_internal(job)) {
+ qapi_event_send_block_job_error(job->id,
+ is_read ? IO_OPERATION_TYPE_READ :
+ IO_OPERATION_TYPE_WRITE,
+ action, &error_abort);
+ }
if (action == BLOCK_ERROR_ACTION_STOP) {
/* make the pause user visible, which will be resumed from QMP. */
- job->user_paused = true;
- block_job_pause(job);
+ block_job_user_pause(job);
block_job_iostatus_set_err(job, error);
}
return action;
diff --git a/include/block/block.h b/include/block/block.h
index b81a3e3..49bb0b2 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -7,16 +7,15 @@
#include "qemu/coroutine.h"
#include "block/accounting.h"
#include "block/dirty-bitmap.h"
+#include "block/blockjob.h"
#include "qapi/qmp/qobject.h"
#include "qapi-types.h"
#include "qemu/hbitmap.h"
/* block.c */
typedef struct BlockDriver BlockDriver;
-typedef struct BlockJob BlockJob;
typedef struct BdrvChild BdrvChild;
typedef struct BdrvChildRole BdrvChildRole;
-typedef struct BlockJobTxn BlockJobTxn;
typedef struct BlockDriverInfo {
/* in bytes, 0 if irrelevant */
diff --git a/include/block/block_int.h b/include/block/block_int.h
index e7ff584..b02abbd 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -665,8 +665,6 @@ int is_windows_drive(const char *filename);
* the new backing file if the job completes. Ignored if @base is %NULL.
* @speed: The maximum speed, in bytes per second, or 0 for unlimited.
* @on_error: The action to take upon error.
- * @cb: Completion function for the job.
- * @opaque: Opaque pointer value passed to @cb.
* @errp: Error object.
*
* Start a streaming operation on @bs. Clusters that are unallocated
@@ -678,8 +676,7 @@ int is_windows_drive(const char *filename);
*/
void stream_start(const char *job_id, BlockDriverState *bs,
BlockDriverState *base, const char *backing_file_str,
- int64_t speed, BlockdevOnError on_error,
- BlockCompletionFunc *cb, void *opaque, Error **errp);
+ int64_t speed, BlockdevOnError on_error, Error **errp);
/**
* commit_start:
@@ -690,22 +687,22 @@ void stream_start(const char *job_id, BlockDriverState *bs,
* @base: Block device that will be written into, and become the new top.
* @speed: The maximum speed, in bytes per second, or 0 for unlimited.
* @on_error: The action to take upon error.
- * @cb: Completion function for the job.
- * @opaque: Opaque pointer value passed to @cb.
* @backing_file_str: String to use as the backing file in @top's overlay
* @errp: Error object.
*
*/
void commit_start(const char *job_id, BlockDriverState *bs,
BlockDriverState *base, BlockDriverState *top, int64_t speed,
- BlockdevOnError on_error, BlockCompletionFunc *cb,
- void *opaque, const char *backing_file_str, Error **errp);
+ BlockdevOnError on_error, const char *backing_file_str,
+ Error **errp);
/**
* commit_active_start:
* @job_id: The id of the newly-created job, or %NULL to use the
* device name of @bs.
* @bs: Active block device to be committed.
* @base: Block device that will be written into, and become the new top.
+ * @creation_flags: Flags that control the behavior of the Job lifetime.
+ * See @BlockJobCreateFlags
* @speed: The maximum speed, in bytes per second, or 0 for unlimited.
* @on_error: The action to take upon error.
* @cb: Completion function for the job.
@@ -715,8 +712,8 @@ void commit_start(const char *job_id, BlockDriverState *bs,
*
*/
void commit_active_start(const char *job_id, BlockDriverState *bs,
- BlockDriverState *base, int64_t speed,
- BlockdevOnError on_error,
+ BlockDriverState *base, int creation_flags,
+ int64_t speed, BlockdevOnError on_error,
BlockCompletionFunc *cb,
void *opaque, Error **errp, bool auto_complete);
/*
@@ -735,8 +732,6 @@ void commit_active_start(const char *job_id, BlockDriverState *bs,
* @on_source_error: The action to take upon error reading from the source.
* @on_target_error: The action to take upon error writing to the target.
* @unmap: Whether to unmap target where source sectors only contain zeroes.
- * @cb: Completion function for the job.
- * @opaque: Opaque pointer value passed to @cb.
* @errp: Error object.
*
* Start a mirroring operation on @bs. Clusters that are allocated
@@ -750,9 +745,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
MirrorSyncMode mode, BlockMirrorBackingMode backing_mode,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error,
- bool unmap,
- BlockCompletionFunc *cb,
- void *opaque, Error **errp);
+ bool unmap, Error **errp);
/*
* backup_start:
@@ -765,6 +758,8 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
* @sync_bitmap: The dirty bitmap if sync_mode is MIRROR_SYNC_MODE_INCREMENTAL.
* @on_source_error: The action to take upon error reading from the source.
* @on_target_error: The action to take upon error writing to the target.
+ * @creation_flags: Flags that control the behavior of the Job lifetime.
+ * See @BlockJobCreateFlags
* @cb: Completion function for the job.
* @opaque: Opaque pointer value passed to @cb.
* @txn: Transaction that this job is part of (may be NULL).
@@ -778,6 +773,7 @@ void backup_start(const char *job_id, BlockDriverState *bs,
bool compress,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error,
+ int creation_flags,
BlockCompletionFunc *cb, void *opaque,
BlockJobTxn *txn, Error **errp);
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
index 4dfb16b..356cacf 100644
--- a/include/block/blockjob.h
+++ b/include/block/blockjob.h
@@ -28,85 +28,15 @@
#include "block/block.h"
-/**
- * BlockJobDriver:
- *
- * A class type for block job driver.
- */
-typedef struct BlockJobDriver {
- /** Derived BlockJob struct size */
- size_t instance_size;
-
- /** String describing the operation, part of query-block-jobs QMP API */
- BlockJobType job_type;
-
- /** Optional callback for job types that support setting a speed limit */
- void (*set_speed)(BlockJob *job, int64_t speed, Error **errp);
-
- /** Optional callback for job types that need to forward I/O status reset */
- void (*iostatus_reset)(BlockJob *job);
-
- /**
- * Optional callback for job types whose completion must be triggered
- * manually.
- */
- void (*complete)(BlockJob *job, Error **errp);
-
- /**
- * If the callback is not NULL, it will be invoked when all the jobs
- * belonging to the same transaction complete; or upon this job's
- * completion if it is not in a transaction. Skipped if NULL.
- *
- * All jobs will complete with a call to either .commit() or .abort() but
- * never both.
- */
- void (*commit)(BlockJob *job);
-
- /**
- * If the callback is not NULL, it will be invoked when any job in the
- * same transaction fails; or upon this job's failure (due to error or
- * cancellation) if it is not in a transaction. Skipped if NULL.
- *
- * All jobs will complete with a call to either .commit() or .abort() but
- * never both.
- */
- void (*abort)(BlockJob *job);
-
- /**
- * If the callback is not NULL, it will be invoked when the job transitions
- * into the paused state. Paused jobs must not perform any asynchronous
- * I/O or event loop activity. This callback is used to quiesce jobs.
- */
- void coroutine_fn (*pause)(BlockJob *job);
-
- /**
- * If the callback is not NULL, it will be invoked when the job transitions
- * out of the paused state. Any asynchronous I/O or event loop activity
- * should be restarted from this callback.
- */
- void coroutine_fn (*resume)(BlockJob *job);
-
- /*
- * If the callback is not NULL, it will be invoked before the job is
- * resumed in a new AioContext. This is the place to move any resources
- * besides job->blk to the new AioContext.
- */
- void (*attached_aio_context)(BlockJob *job, AioContext *new_context);
-
- /*
- * If the callback is not NULL, it will be invoked when the job has to be
- * synchronously cancelled or completed; it should drain BlockDriverStates
- * as required to ensure progress.
- */
- void (*drain)(BlockJob *job);
-} BlockJobDriver;
+typedef struct BlockJobDriver BlockJobDriver;
+typedef struct BlockJobTxn BlockJobTxn;
/**
* BlockJob:
*
* Long-running operation on a BlockDriverState.
*/
-struct BlockJob {
+typedef struct BlockJob {
/** The job type, including the job vtable. */
const BlockJobDriver *driver;
@@ -114,7 +44,7 @@ struct BlockJob {
BlockBackend *blk;
/**
- * The ID of the block job.
+ * The ID of the block job. May be NULL for internal jobs.
*/
char *id;
@@ -208,7 +138,12 @@ struct BlockJob {
/** Non-NULL if this job is part of a transaction */
BlockJobTxn *txn;
QLIST_ENTRY(BlockJob) txn_list;
-};
+} BlockJob;
+
+typedef enum BlockJobCreateFlags {
+ BLOCK_JOB_DEFAULT = 0x00,
+ BLOCK_JOB_INTERNAL = 0x01,
+} BlockJobCreateFlags;
/**
* block_job_next:
@@ -232,30 +167,6 @@ BlockJob *block_job_next(BlockJob *job);
BlockJob *block_job_get(const char *id);
/**
- * block_job_create:
- * @job_id: The id of the newly-created job, or %NULL to have one
- * generated automatically.
- * @job_type: The class object for the newly-created job.
- * @bs: The block
- * @speed: The maximum speed, in bytes per second, or 0 for unlimited.
- * @cb: Completion function for the job.
- * @opaque: Opaque pointer value passed to @cb.
- * @errp: Error object.
- *
- * Create a new long-running block device job and return it. The job
- * will call @cb asynchronously when the job completes. Note that
- * @bs may have been closed at the time the @cb it is called. If
- * this is the case, the job may be reported as either cancelled or
- * completed.
- *
- * This function is not part of the public job interface; it should be
- * called from a wrapper that is specific to the job type.
- */
-void *block_job_create(const char *job_id, const BlockJobDriver *driver,
- BlockDriverState *bs, int64_t speed,
- BlockCompletionFunc *cb, void *opaque, Error **errp);
-
-/**
* block_job_add_bdrv:
* @job: A block job
* @bs: A BlockDriverState that is involved in @job
@@ -267,52 +178,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
void block_job_add_bdrv(BlockJob *job, BlockDriverState *bs);
/**
- * block_job_sleep_ns:
- * @job: The job that calls the function.
- * @clock: The clock to sleep on.
- * @ns: How many nanoseconds to stop for.
- *
- * Put the job to sleep (assuming that it wasn't canceled) for @ns
- * nanoseconds. Canceling the job will interrupt the wait immediately.
- */
-void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns);
-
-/**
- * block_job_yield:
- * @job: The job that calls the function.
- *
- * Yield the block job coroutine.
- */
-void block_job_yield(BlockJob *job);
-
-/**
- * block_job_ref:
- * @bs: The block device.
- *
- * Grab a reference to the block job. Should be paired with block_job_unref.
- */
-void block_job_ref(BlockJob *job);
-
-/**
- * block_job_unref:
- * @bs: The block device.
- *
- * Release reference to the block job and release resources if it is the last
- * reference.
- */
-void block_job_unref(BlockJob *job);
-
-/**
- * block_job_completed:
- * @job: The job being completed.
- * @ret: The status code.
- *
- * Call the completion function that was registered at creation time, and
- * free @job.
- */
-void block_job_completed(BlockJob *job, int ret);
-
-/**
* block_job_set_speed:
* @job: The job to set the speed for.
* @speed: The new value
@@ -341,29 +206,12 @@ void block_job_cancel(BlockJob *job);
void block_job_complete(BlockJob *job, Error **errp);
/**
- * block_job_is_cancelled:
- * @job: The job being queried.
- *
- * Returns whether the job is scheduled for cancellation.
- */
-bool block_job_is_cancelled(BlockJob *job);
-
-/**
* block_job_query:
* @job: The job to get information about.
*
* Return information about a job.
*/
-BlockJobInfo *block_job_query(BlockJob *job);
-
-/**
- * block_job_pause_point:
- * @job: The job that is ready to pause.
- *
- * Pause now if block_job_pause() has been called. Block jobs that perform
- * lots of I/O must call this between requests so that the job can be paused.
- */
-void coroutine_fn block_job_pause_point(BlockJob *job);
+BlockJobInfo *block_job_query(BlockJob *job, Error **errp);
/**
* block_job_pause:
@@ -374,45 +222,38 @@ void coroutine_fn block_job_pause_point(BlockJob *job);
void block_job_pause(BlockJob *job);
/**
- * block_job_resume:
- * @job: The job to be resumed.
- *
- * Resume the specified job. Must be paired with a preceding block_job_pause.
- */
-void block_job_resume(BlockJob *job);
-
-/**
- * block_job_enter:
- * @job: The job to enter.
+ * block_job_user_pause:
+ * @job: The job to be paused.
*
- * Continue the specified job by entering the coroutine.
+ * Asynchronously pause the specified job.
+ * Do not allow a resume until a matching call to block_job_user_resume.
*/
-void block_job_enter(BlockJob *job);
+void block_job_user_pause(BlockJob *job);
/**
- * block_job_event_cancelled:
- * @job: The job whose information is requested.
+ * block_job_paused:
+ * @job: The job to query.
*
- * Send a BLOCK_JOB_CANCELLED event for the specified job.
+ * Returns true if the job is user-paused.
*/
-void block_job_event_cancelled(BlockJob *job);
+bool block_job_user_paused(BlockJob *job);
/**
- * block_job_ready:
- * @job: The job which is now ready to complete.
- * @msg: Error message. Only present on failure.
+ * block_job_resume:
+ * @job: The job to be resumed.
*
- * Send a BLOCK_JOB_COMPLETED event for the specified job.
+ * Resume the specified job. Must be paired with a preceding block_job_pause.
*/
-void block_job_event_completed(BlockJob *job, const char *msg);
+void block_job_resume(BlockJob *job);
/**
- * block_job_ready:
- * @job: The job which is now ready to complete.
+ * block_job_user_resume:
+ * @job: The job to be resumed.
*
- * Send a BLOCK_JOB_READY event for the specified job.
+ * Resume the specified job.
+ * Must be paired with a preceding block_job_user_pause.
*/
-void block_job_event_ready(BlockJob *job);
+void block_job_user_resume(BlockJob *job);
/**
* block_job_cancel_sync:
@@ -460,37 +301,6 @@ int block_job_complete_sync(BlockJob *job, Error **errp);
void block_job_iostatus_reset(BlockJob *job);
/**
- * block_job_error_action:
- * @job: The job to signal an error for.
- * @on_err: The error action setting.
- * @is_read: Whether the operation was a read.
- * @error: The error that was reported.
- *
- * Report an I/O error for a block job and possibly stop the VM. Return the
- * action that was selected based on @on_err and @error.
- */
-BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err,
- int is_read, int error);
-
-typedef void BlockJobDeferToMainLoopFn(BlockJob *job, void *opaque);
-
-/**
- * block_job_defer_to_main_loop:
- * @job: The job
- * @fn: The function to run in the main loop
- * @opaque: The opaque value that is passed to @fn
- *
- * Execute a given function in the main loop with the BlockDriverState
- * AioContext acquired. Block jobs must call bdrv_unref(), bdrv_close(), and
- * anything that uses bdrv_drain_all() in the main loop.
- *
- * The @job AioContext is held while @fn executes.
- */
-void block_job_defer_to_main_loop(BlockJob *job,
- BlockJobDeferToMainLoopFn *fn,
- void *opaque);
-
-/**
* block_job_txn_new:
*
* Allocate and return a new block job transaction. Jobs can be added to the
@@ -525,4 +335,12 @@ void block_job_txn_unref(BlockJobTxn *txn);
*/
void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job);
+/**
+ * block_job_is_internal:
+ * @job: The job to determine if it is user-visible or not.
+ *
+ * Returns true if the job should not be visible to the management layer.
+ */
+bool block_job_is_internal(BlockJob *job);
+
#endif
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
new file mode 100644
index 0000000..40275e4
--- /dev/null
+++ b/include/block/blockjob_int.h
@@ -0,0 +1,239 @@
+/*
+ * Declarations for long-running block device operations
+ *
+ * Copyright (c) 2011 IBM Corp.
+ * Copyright (c) 2012 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 BLOCKJOB_INT_H
+#define BLOCKJOB_INT_H
+
+#include "block/blockjob.h"
+#include "block/block.h"
+
+/**
+ * BlockJobDriver:
+ *
+ * A class type for block job driver.
+ */
+struct BlockJobDriver {
+ /** Derived BlockJob struct size */
+ size_t instance_size;
+
+ /** String describing the operation, part of query-block-jobs QMP API */
+ BlockJobType job_type;
+
+ /** Optional callback for job types that support setting a speed limit */
+ void (*set_speed)(BlockJob *job, int64_t speed, Error **errp);
+
+ /** Optional callback for job types that need to forward I/O status reset */
+ void (*iostatus_reset)(BlockJob *job);
+
+ /**
+ * Optional callback for job types whose completion must be triggered
+ * manually.
+ */
+ void (*complete)(BlockJob *job, Error **errp);
+
+ /**
+ * If the callback is not NULL, it will be invoked when all the jobs
+ * belonging to the same transaction complete; or upon this job's
+ * completion if it is not in a transaction. Skipped if NULL.
+ *
+ * All jobs will complete with a call to either .commit() or .abort() but
+ * never both.
+ */
+ void (*commit)(BlockJob *job);
+
+ /**
+ * If the callback is not NULL, it will be invoked when any job in the
+ * same transaction fails; or upon this job's failure (due to error or
+ * cancellation) if it is not in a transaction. Skipped if NULL.
+ *
+ * All jobs will complete with a call to either .commit() or .abort() but
+ * never both.
+ */
+ void (*abort)(BlockJob *job);
+
+ /**
+ * If the callback is not NULL, it will be invoked when the job transitions
+ * into the paused state. Paused jobs must not perform any asynchronous
+ * I/O or event loop activity. This callback is used to quiesce jobs.
+ */
+ void coroutine_fn (*pause)(BlockJob *job);
+
+ /**
+ * If the callback is not NULL, it will be invoked when the job transitions
+ * out of the paused state. Any asynchronous I/O or event loop activity
+ * should be restarted from this callback.
+ */
+ void coroutine_fn (*resume)(BlockJob *job);
+
+ /*
+ * If the callback is not NULL, it will be invoked before the job is
+ * resumed in a new AioContext. This is the place to move any resources
+ * besides job->blk to the new AioContext.
+ */
+ void (*attached_aio_context)(BlockJob *job, AioContext *new_context);
+
+ /*
+ * If the callback is not NULL, it will be invoked when the job has to be
+ * synchronously cancelled or completed; it should drain BlockDriverStates
+ * as required to ensure progress.
+ */
+ void (*drain)(BlockJob *job);
+};
+
+/**
+ * block_job_create:
+ * @job_id: The id of the newly-created job, or %NULL to have one
+ * generated automatically.
+ * @job_type: The class object for the newly-created job.
+ * @bs: The block
+ * @speed: The maximum speed, in bytes per second, or 0 for unlimited.
+ * @cb: Completion function for the job.
+ * @opaque: Opaque pointer value passed to @cb.
+ * @errp: Error object.
+ *
+ * Create a new long-running block device job and return it. The job
+ * will call @cb asynchronously when the job completes. Note that
+ * @bs may have been closed at the time the @cb it is called. If
+ * this is the case, the job may be reported as either cancelled or
+ * completed.
+ *
+ * This function is not part of the public job interface; it should be
+ * called from a wrapper that is specific to the job type.
+ */
+void *block_job_create(const char *job_id, const BlockJobDriver *driver,
+ BlockDriverState *bs, int64_t speed, int flags,
+ BlockCompletionFunc *cb, void *opaque, Error **errp);
+
+/**
+ * block_job_sleep_ns:
+ * @job: The job that calls the function.
+ * @clock: The clock to sleep on.
+ * @ns: How many nanoseconds to stop for.
+ *
+ * Put the job to sleep (assuming that it wasn't canceled) for @ns
+ * nanoseconds. Canceling the job will interrupt the wait immediately.
+ */
+void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns);
+
+/**
+ * block_job_yield:
+ * @job: The job that calls the function.
+ *
+ * Yield the block job coroutine.
+ */
+void block_job_yield(BlockJob *job);
+
+/**
+ * block_job_ref:
+ * @bs: The block device.
+ *
+ * Grab a reference to the block job. Should be paired with block_job_unref.
+ */
+void block_job_ref(BlockJob *job);
+
+/**
+ * block_job_unref:
+ * @bs: The block device.
+ *
+ * Release reference to the block job and release resources if it is the last
+ * reference.
+ */
+void block_job_unref(BlockJob *job);
+
+/**
+ * block_job_completed:
+ * @job: The job being completed.
+ * @ret: The status code.
+ *
+ * Call the completion function that was registered at creation time, and
+ * free @job.
+ */
+void block_job_completed(BlockJob *job, int ret);
+
+/**
+ * block_job_is_cancelled:
+ * @job: The job being queried.
+ *
+ * Returns whether the job is scheduled for cancellation.
+ */
+bool block_job_is_cancelled(BlockJob *job);
+
+/**
+ * block_job_pause_point:
+ * @job: The job that is ready to pause.
+ *
+ * Pause now if block_job_pause() has been called. Block jobs that perform
+ * lots of I/O must call this between requests so that the job can be paused.
+ */
+void coroutine_fn block_job_pause_point(BlockJob *job);
+
+/**
+ * block_job_enter:
+ * @job: The job to enter.
+ *
+ * Continue the specified job by entering the coroutine.
+ */
+void block_job_enter(BlockJob *job);
+
+/**
+ * block_job_event_ready:
+ * @job: The job which is now ready to be completed.
+ *
+ * Send a BLOCK_JOB_READY event for the specified job.
+ */
+void block_job_event_ready(BlockJob *job);
+
+/**
+ * block_job_error_action:
+ * @job: The job to signal an error for.
+ * @on_err: The error action setting.
+ * @is_read: Whether the operation was a read.
+ * @error: The error that was reported.
+ *
+ * Report an I/O error for a block job and possibly stop the VM. Return the
+ * action that was selected based on @on_err and @error.
+ */
+BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err,
+ int is_read, int error);
+
+typedef void BlockJobDeferToMainLoopFn(BlockJob *job, void *opaque);
+
+/**
+ * block_job_defer_to_main_loop:
+ * @job: The job
+ * @fn: The function to run in the main loop
+ * @opaque: The opaque value that is passed to @fn
+ *
+ * Execute a given function in the main loop with the BlockDriverState
+ * AioContext acquired. Block jobs must call bdrv_unref(), bdrv_close(), and
+ * anything that uses bdrv_drain_all() in the main loop.
+ *
+ * The @job AioContext is held while @fn executes.
+ */
+void block_job_defer_to_main_loop(BlockJob *job,
+ BlockJobDeferToMainLoopFn *fn,
+ void *opaque);
+
+#endif
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 5af040b..bcd3b9e 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -2197,7 +2197,7 @@
#
# @debug-level: #optional libgfapi log level (default '4' which is Error)
#
-# @logfile: #optional libgfapi log file (default /dev/stderr)
+# @logfile: #optional libgfapi log file (default /dev/stderr) (Since 2.8)
#
# Since: 2.7
##
diff --git a/qemu-img.c b/qemu-img.c
index ac7f40d..6949b73 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -933,8 +933,9 @@ static int img_commit(int argc, char **argv)
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
- commit_active_start("commit", bs, base_bs, 0, BLOCKDEV_ON_ERROR_REPORT,
- common_block_job_cb, &cbi, &local_err, false);
+ commit_active_start("commit", bs, base_bs, BLOCK_JOB_DEFAULT, 0,
+ BLOCKDEV_ON_ERROR_REPORT, common_block_job_cb, &cbi,
+ &local_err, false);
aio_context_release(aio_context);
if (local_err) {
goto done;
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
index d049cba..f9afc3b 100644
--- a/tests/test-blockjob-txn.c
+++ b/tests/test-blockjob-txn.c
@@ -13,7 +13,7 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qemu/main-loop.h"
-#include "block/blockjob.h"
+#include "block/blockjob_int.h"
#include "sysemu/block-backend.h"
typedef struct {
@@ -98,7 +98,8 @@ static BlockJob *test_block_job_start(unsigned int iterations,
bs = bdrv_new();
snprintf(job_id, sizeof(job_id), "job%u", counter++);
s = block_job_create(job_id, &test_block_job_driver, bs, 0,
- test_block_job_cb, data, &error_abort);
+ BLOCK_JOB_DEFAULT, test_block_job_cb,
+ data, &error_abort);
s->iterations = iterations;
s->use_timer = use_timer;
s->rc = rc;
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
index 5b0e934..60b78a3 100644
--- a/tests/test-blockjob.c
+++ b/tests/test-blockjob.c
@@ -13,7 +13,7 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qemu/main-loop.h"
-#include "block/blockjob.h"
+#include "block/blockjob_int.h"
#include "sysemu/block-backend.h"
static const BlockJobDriver test_block_job_driver = {
@@ -31,7 +31,7 @@ static BlockJob *do_test_id(BlockBackend *blk, const char *id,
Error *errp = NULL;
job = block_job_create(id, &test_block_job_driver, blk_bs(blk), 0,
- block_job_cb, NULL, &errp);
+ BLOCK_JOB_DEFAULT, block_job_cb, NULL, &errp);
if (should_succeed) {
g_assert_null(errp);
g_assert_nonnull(job);