From 05fce20d6d1d758021f6a3c7af03d116a2cb61a4 Mon Sep 17 00:00:00 2001 From: Jeff Cody Date: Tue, 13 Sep 2016 11:35:38 -0400 Subject: [PATCH 01/14] qapi: add release designator to gluster logfile option The "logfile" option to BlockdevOptionsGluster will not be in QEMU until 2.8. Update comment to indicate this. Reported-by: Eric Blake Signed-off-by: Jeff Cody --- qapi/block-core.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index 5af040b740..bcd3b9effe 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 ## From e38f643a1de9b5afc1d133b12bcfdf205bc27654 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Sat, 15 Oct 2016 16:26:13 +0800 Subject: [PATCH 02/14] rbd: make the code more readable Make it a bit clearer and more readable. Signed-off-by: Xiubo Li Reviewed-by: Stefan Hajnoczi Reviewed-by: John Snow Reviewed-by: Jeff Cody Message-id: 1476519973-6436-1-git-send-email-lixiubo@cmss.chinamobile.com CC: John Snow Signed-off-by: Jeff Cody --- block/rbd.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/block/rbd.c b/block/rbd.c index f6e1d4bc11..a57b3e3c5d 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; } From d9b789745b88df367674e45c55df29e9c7de8d8a Mon Sep 17 00:00:00 2001 From: Jeff Cody Date: Fri, 7 Oct 2016 17:48:12 -0400 Subject: [PATCH 03/14] block: add gluster ifdef guard checks for SEEK_DATA/SEEK_HOLE support Add checks to see if the system compiling QEMU has support for SEEK_HOLE/SEEK_DATA. If the system does not, we will flag that seek data is unsupported in gluster. Note: this is not a check on whether the gluster server itself supports SEEK_DATA (that is already done during runtime), but rather if the compilation environment supports SEEK_DATA. Reviewed-by: Eric Blake Signed-off-by: Jeff Cody Tested-by: Eric Blake Message-id: 00370bce5c98140d6c56ad5145635ec6551265cc.1475876377.git.jcody@redhat.com Signed-off-by: Jeff Cody --- block/gluster.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/block/gluster.c b/block/gluster.c index af76d7d59a..1735d12254 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -668,7 +668,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 +681,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); } @@ -1178,12 +1183,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 +1254,10 @@ static int find_allocation(BlockDriverState *bs, off_t start, /* D1 and H1 */ return -EBUSY; +#endif + +exit: + return -ENOTSUP; } /* From 6349c15410361d3fe52c9beee309954d606f8ccd Mon Sep 17 00:00:00 2001 From: Prasanna Kumar Kalever Date: Thu, 27 Oct 2016 20:54:50 +0530 Subject: [PATCH 04/14] block/gluster: memory usage: use one glfs instance per volume Currently, for every drive accessed via gfapi we create a new glfs instance (call glfs_new() followed by glfs_init()) which could consume memory in few 100 MB's, from the table below it looks like for each instance ~300 MB VSZ was consumed Before: ------- Disks VSZ RSS 1 1098728 187756 2 1430808 198656 3 1764932 199704 4 2084728 202684 This patch maintains a list of pre-opened glfs objects. On adding a new drive belonging to the same gluster volume, we just reuse the existing glfs object by updating its refcount. With this approch we shrink up the unwanted memory consumption and glfs_new/glfs_init calls for accessing a disk (file) if belongs to same volume. From below table notice that the memory usage after adding a disk (which will reuse the existing glfs object hence) is in negligible compared to before. After: ------ Disks VSZ RSS 1 1101964 185768 2 1109604 194920 3 1114012 196036 4 1114496 199868 Disks: number of -drive VSZ: virtual memory size of the process in KiB RSS: resident set size, the non-swapped physical memory (in kiloBytes) VSZ and RSS are analyzed using 'ps aux' utility. Signed-off-by: Prasanna Kumar Kalever Reviewed-by: Jeff Cody Message-id: 1477581890-4811-1-git-send-email-prasanna.kalever@redhat.com Signed-off-by: Jeff Cody --- block/gluster.c | 94 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 80 insertions(+), 14 deletions(-) diff --git a/block/gluster.c b/block/gluster.c index 1735d12254..40bd29c781 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -56,6 +56,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), @@ -194,6 +207,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; @@ -331,11 +395,18 @@ static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf, int old_errno; GlusterServerList *server; + 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, @@ -387,7 +458,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; @@ -767,9 +838,9 @@ out: if (s->fd) { glfs_close(s->fd); } - if (s->glfs) { - glfs_fini(s->glfs); - } + + glfs_clear_preopened(s->glfs); + return ret; } @@ -836,9 +907,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; @@ -863,9 +933,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; @@ -989,9 +1057,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; } @@ -1064,7 +1130,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) From 6f13acf97efb8aade2ff7e0444c15512f9e65812 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 27 Sep 2016 19:14:52 +0800 Subject: [PATCH 05/14] block: Turn on "unmap" in active commit We already specified BDRV_O_UNMAP when opening images in 'qemu-img commit', but didn't turn on the "unmap" in the active commit job. This patch fixes that so that zeroed clusters in top image can be discarded which is desired in the virt-sparsify use case, where a temporary overlay is created and fstrim'ed before commiting back, to free space in the original image. This also enables it for block-commit. Signed-off-by: Fam Zheng Reviewed-by: Eric Blake Message-id: 1474974892-5031-1-git-send-email-famz@redhat.com Signed-off-by: Jeff Cody --- block/mirror.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/mirror.c b/block/mirror.c index 7e99f3a880..82a9529138 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1081,7 +1081,7 @@ void commit_active_start(const char *job_id, BlockDriverState *bs, mirror_start_job(job_id, bs, 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); From c56ac33b7a7bee960dd43f7744aaca47886c3960 Mon Sep 17 00:00:00 2001 From: Prasanna Kumar Kalever Date: Tue, 9 Aug 2016 14:20:09 +0530 Subject: [PATCH 06/14] block/gluster: improve defense over string to int conversion using atoi() for converting string to int may be error prone in case if string supplied in the argument is not a fold of numerical number, This is not a bug because in the existing code, static QemuOptsList runtime_tcp_opts = { .name = "gluster_tcp", .head = QTAILQ_HEAD_INITIALIZER(runtime_tcp_opts.head), .desc = { ... { .name = GLUSTER_OPT_PORT, .type = QEMU_OPT_NUMBER, .help = "port number ...", }, ... }; port type is QEMU_OPT_NUMBER, before we actually reaches atoi() port is already defended by parse_option_number() However It is a good practice to use function like parse_uint_full() over atoi() to keep port self defended Note: As now the port string to int conversion has its defence code set, and also we understand that port argument is actually a string type, in the follow up patch let's move port type from QEMU_OPT_NUMBER to QEMU_OPT_STRING [Jeff Cody: removed spurious parenthesis] Signed-off-by: Prasanna Kumar Kalever Reviewed-by: Markus Armbruster Signed-off-by: Jeff Cody --- block/gluster.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/block/gluster.c b/block/gluster.c index 40bd29c781..98a91323b6 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" @@ -394,6 +395,7 @@ 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) { @@ -413,10 +415,17 @@ static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf, 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) { From 53d9837fb8906b8512143d5eaacc4d6a65b29cc6 Mon Sep 17 00:00:00 2001 From: Prasanna Kumar Kalever Date: Tue, 9 Aug 2016 15:18:14 +0530 Subject: [PATCH 07/14] block/gluster: fix port type in the QAPI options list After introduction of qapi schema in gluster block driver code, the port type is now string as per InetSocketAddress { 'struct': 'InetSocketAddress', 'data': { 'host': 'str', 'port': 'str', '*to': 'uint16', '*ipv4': 'bool', '*ipv6': 'bool' } } but the current code still treats it as QEMU_OPT_NUMBER, hence fixing port to accept QEMU_OPT_STRING. Suggested-by: Markus Armbruster Signed-off-by: Prasanna Kumar Kalever Reviewed-by: Jeff Cody Signed-off-by: Jeff Cody --- block/gluster.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/gluster.c b/block/gluster.c index 98a91323b6..0ce15f7adc 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -186,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)", }, { From 559b935f8c5c3139c2e275651da719ffa425cc14 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 27 Oct 2016 12:06:55 -0400 Subject: [PATCH 08/14] blockjobs: hide internal jobs from management API If jobs are not created directly by the user, do not allow them to be seen by the user/management utility. At the moment, 'internal' jobs are those that do not have an ID. As of this patch it is impossible to create such jobs. Signed-off-by: John Snow Message-id: 1477584421-1399-2-git-send-email-jsnow@redhat.com Signed-off-by: Jeff Cody --- blockdev.c | 17 +++++++++++++---- blockjob.c | 41 +++++++++++++++++++++++++++++++++------- include/block/blockjob.h | 12 ++++++++++-- 3 files changed, 57 insertions(+), 13 deletions(-) diff --git a/blockdev.c b/blockdev.c index ded13268f7..d57cb0c7d6 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3946,13 +3946,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 422851fde5..84d4f754b4 100644 --- a/blockjob.c +++ b/blockjob.c @@ -66,7 +66,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; } } @@ -188,6 +188,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; @@ -330,6 +335,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); @@ -510,9 +517,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; @@ -535,6 +548,10 @@ static void block_job_iostatus_set_err(BlockJob *job, int error) 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, @@ -545,6 +562,10 @@ void block_job_event_cancelled(BlockJob *job) 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 +580,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,10 +614,12 @@ 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; diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 4dfb16b43f..a1b75022c3 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -114,7 +114,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; @@ -354,7 +354,7 @@ bool block_job_is_cancelled(BlockJob *job); * * Return information about a job. */ -BlockJobInfo *block_job_query(BlockJob *job); +BlockJobInfo *block_job_query(BlockJob *job, Error **errp); /** * block_job_pause_point: @@ -525,4 +525,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 From f81e0b453275f7b59a4018093eb93d2173790665 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 27 Oct 2016 12:06:56 -0400 Subject: [PATCH 09/14] blockjobs: Allow creating internal jobs Add the ability to create jobs without an ID. Signed-off-by: John Snow Reviewed-by: Kevin Wolf Reviewed-by: Jeff Cody Message-id: 1477584421-1399-3-git-send-email-jsnow@redhat.com Signed-off-by: Jeff Cody --- block/backup.c | 2 +- block/commit.c | 2 +- block/mirror.c | 3 ++- block/stream.c | 2 +- blockjob.c | 25 ++++++++++++++++--------- include/block/blockjob.h | 7 ++++++- tests/test-blockjob-txn.c | 3 ++- tests/test-blockjob.c | 2 +- 8 files changed, 30 insertions(+), 16 deletions(-) diff --git a/block/backup.c b/block/backup.c index 44c7ff3d16..3877d93da6 100644 --- a/block/backup.c +++ b/block/backup.c @@ -612,7 +612,7 @@ void backup_start(const char *job_id, BlockDriverState *bs, } job = block_job_create(job_id, &backup_job_driver, bs, speed, - cb, opaque, errp); + BLOCK_JOB_DEFAULT, cb, opaque, errp); if (!job) { goto error; } diff --git a/block/commit.c b/block/commit.c index a5e17f610f..0740a41596 100644 --- a/block/commit.c +++ b/block/commit.c @@ -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, cb, opaque, errp); if (!s) { return; } diff --git a/block/mirror.c b/block/mirror.c index 82a9529138..e9fba9be06 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -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, + BLOCK_JOB_DEFAULT, cb, opaque, errp); if (!s) { return; } diff --git a/block/stream.c b/block/stream.c index b8ab89a105..09ce9ef500 100644 --- a/block/stream.c +++ b/block/stream.c @@ -230,7 +230,7 @@ void stream_start(const char *job_id, BlockDriverState *bs, int orig_bs_flags; s = block_job_create(job_id, &stream_job_driver, bs, speed, - cb, opaque, errp); + BLOCK_JOB_DEFAULT, cb, opaque, errp); if (!s) { return; } diff --git a/blockjob.c b/blockjob.c index 84d4f754b4..c286fc3a98 100644 --- a/blockjob.c +++ b/blockjob.c @@ -121,7 +121,7 @@ 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; @@ -133,7 +133,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, 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 +141,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(); diff --git a/include/block/blockjob.h b/include/block/blockjob.h index a1b75022c3..d0d93337f6 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -210,6 +210,11 @@ struct BlockJob { QLIST_ENTRY(BlockJob) txn_list; }; +typedef enum BlockJobCreateFlags { + BLOCK_JOB_DEFAULT = 0x00, + BLOCK_JOB_INTERNAL = 0x01, +} BlockJobCreateFlags; + /** * block_job_next: * @job: A block job, or %NULL. @@ -252,7 +257,7 @@ BlockJob *block_job_get(const char *id); * 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, + BlockDriverState *bs, int64_t speed, int flags, BlockCompletionFunc *cb, void *opaque, Error **errp); /** diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c index d049cba8a3..b79e0c68e1 100644 --- a/tests/test-blockjob-txn.c +++ b/tests/test-blockjob-txn.c @@ -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 5b0e934a0c..18bf85017a 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -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); From 47970dfb0a611f6468a0ba44781b4610525d1af1 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 27 Oct 2016 12:06:57 -0400 Subject: [PATCH 10/14] Replication/Blockjobs: Create replication jobs as internal Bubble up the internal interface to commit and backup jobs, then switch replication tasks over to using this methodology. Signed-off-by: John Snow Reviewed-by: Kevin Wolf Reviewed-by: Jeff Cody Message-id: 1477584421-1399-4-git-send-email-jsnow@redhat.com Signed-off-by: Jeff Cody --- block/backup.c | 3 ++- block/mirror.c | 21 ++++++++++----------- block/replication.c | 14 +++++++------- blockdev.c | 11 +++++++---- include/block/block_int.h | 9 +++++++-- qemu-img.c | 5 +++-- 6 files changed, 36 insertions(+), 27 deletions(-) diff --git a/block/backup.c b/block/backup.c index 3877d93da6..2a369e6aff 100644 --- a/block/backup.c +++ b/block/backup.c @@ -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, - BLOCK_JOB_DEFAULT, cb, opaque, errp); + creation_flags, cb, opaque, errp); if (!job) { goto error; } diff --git a/block/mirror.c b/block/mirror.c index e9fba9be06..fa4184955a 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -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,8 +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, - BLOCK_JOB_DEFAULT, cb, opaque, errp); + s = block_job_create(job_id, driver, bs, speed, creation_flags, + cb, opaque, errp); if (!s) { return; } @@ -1031,17 +1031,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, &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; @@ -1080,7 +1079,7 @@ 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, true, cb, opaque, &local_err, &commit_active_job_driver, false, base, auto_complete); diff --git a/block/replication.c b/block/replication.c index 02aeaaf7d0..d5e2b0f497 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/blockdev.c b/blockdev.c index d57cb0c7d6..9c483bd78d 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3110,8 +3110,9 @@ 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, block_job_cb, + bs, &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)) { @@ -3239,7 +3240,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, + block_job_cb, bs, txn, &local_err); bdrv_unref(target_bs); if (local_err != NULL) { error_propagate(errp, local_err); @@ -3309,7 +3311,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, + block_job_cb, bs, txn, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); } diff --git a/include/block/block_int.h b/include/block/block_int.h index e7ff58419c..348d457a65 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -706,6 +706,8 @@ void commit_start(const char *job_id, BlockDriverState *bs, * 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 +717,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); /* @@ -765,6 +767,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 +782,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/qemu-img.c b/qemu-img.c index ac7f40d91a..6949b73ca5 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; From 8254b6d9534ba6a7d078a717e777fefe75ec27b6 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 27 Oct 2016 12:06:58 -0400 Subject: [PATCH 11/14] blockjob: centralize QMP event emissions There's no reason to leave this to blockdev; we can do it in blockjobs directly and get rid of an extra callback for most users. All non-internal events, even those created outside of QMP, will consistently emit events. Signed-off-by: John Snow Reviewed-by: Kevin Wolf Reviewed-by: Jeff Cody Message-id: 1477584421-1399-5-git-send-email-jsnow@redhat.com Signed-off-by: Jeff Cody --- block/commit.c | 8 ++++---- block/mirror.c | 6 ++---- block/stream.c | 7 +++---- block/trace-events | 5 ++--- blockdev.c | 42 ++++++++------------------------------- blockjob.c | 23 +++++++++++++++++---- include/block/block_int.h | 17 ++++------------ include/block/blockjob.h | 17 ---------------- 8 files changed, 42 insertions(+), 83 deletions(-) diff --git a/block/commit.c b/block/commit.c index 0740a41596..18ec57851e 100644 --- a/block/commit.c +++ b/block/commit.c @@ -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, - BLOCK_JOB_DEFAULT, 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/mirror.c b/block/mirror.c index fa4184955a..aa60bcc44b 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1018,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; @@ -1033,7 +1031,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs, base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL; 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); } diff --git a/block/stream.c b/block/stream.c index 09ce9ef500..152c1bef5f 100644 --- a/block/stream.c +++ b/block/stream.c @@ -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, - BLOCK_JOB_DEFAULT, 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 aff8a9674d..882c9034c2 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 9c483bd78d..b6a7491604 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; @@ -3111,16 +3086,16 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, goto out; } commit_active_start(has_job_id ? job_id : NULL, bs, base_bs, - BLOCK_JOB_DEFAULT, speed, on_error, block_job_cb, - bs, &local_err, false); + 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); @@ -3241,7 +3216,7 @@ 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_DEFAULT, - block_job_cb, bs, txn, &local_err); + NULL, NULL, txn, &local_err); bdrv_unref(target_bs); if (local_err != NULL) { error_propagate(errp, local_err); @@ -3312,7 +3287,7 @@ 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_DEFAULT, - block_job_cb, bs, txn, &local_err); + NULL, NULL, txn, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); } @@ -3391,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) diff --git a/blockjob.c b/blockjob.c index c286fc3a98..309ef9a35c 100644 --- a/blockjob.c +++ b/blockjob.c @@ -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 { @@ -127,7 +130,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, BlockBackend *blk; BlockJob *job; - assert(cb); if (bs->job) { error_setg(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bs)); return NULL; @@ -239,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); } @@ -553,7 +568,7 @@ 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; @@ -567,7 +582,7 @@ 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; diff --git a/include/block/block_int.h b/include/block/block_int.h index 348d457a65..b02abbd618 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,16 +687,14 @@ 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 @@ -737,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 @@ -752,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: diff --git a/include/block/blockjob.h b/include/block/blockjob.h index d0d93337f6..c031fe78cc 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -394,23 +394,6 @@ void block_job_resume(BlockJob *job); */ void block_job_enter(BlockJob *job); -/** - * block_job_event_cancelled: - * @job: The job whose information is requested. - * - * Send a BLOCK_JOB_CANCELLED event for the specified job. - */ -void block_job_event_cancelled(BlockJob *job); - -/** - * block_job_ready: - * @job: The job which is now ready to complete. - * @msg: Error message. Only present on failure. - * - * Send a BLOCK_JOB_COMPLETED event for the specified job. - */ -void block_job_event_completed(BlockJob *job, const char *msg); - /** * block_job_ready: * @job: The job which is now ready to complete. From 0df4ba58631772dd391314c1119863b574ebbaf9 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 27 Oct 2016 12:06:59 -0400 Subject: [PATCH 12/14] Blockjobs: Internalize user_pause logic BlockJobs will begin hiding their state in preparation for some refactorings anyway, so let's internalize the user_pause mechanism instead of leaving it to callers to correctly manage. Signed-off-by: John Snow Reviewed-by: Kevin Wolf Reviewed-by: Jeff Cody Message-id: 1477584421-1399-6-git-send-email-jsnow@redhat.com Signed-off-by: Jeff Cody --- blockdev.c | 12 +++++------- blockjob.c | 22 ++++++++++++++++++++-- include/block/blockjob.h | 26 ++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 9 deletions(-) diff --git a/blockdev.c b/blockdev.c index b6a7491604..102ca9fe01 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3610,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; @@ -3627,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); } @@ -3642,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); } diff --git a/blockjob.c b/blockjob.c index 309ef9a35c..d880ad2bd5 100644 --- a/blockjob.c +++ b/blockjob.c @@ -373,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)) { @@ -414,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) { @@ -644,8 +663,7 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, } 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/blockjob.h b/include/block/blockjob.h index c031fe78cc..d31ea43116 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -378,6 +378,23 @@ void coroutine_fn block_job_pause_point(BlockJob *job); */ void block_job_pause(BlockJob *job); +/** + * block_job_user_pause: + * @job: The job to be paused. + * + * Asynchronously pause the specified job. + * Do not allow a resume until a matching call to block_job_user_resume. + */ +void block_job_user_pause(BlockJob *job); + +/** + * block_job_paused: + * @job: The job to query. + * + * Returns true if the job is user-paused. + */ +bool block_job_user_paused(BlockJob *job); + /** * block_job_resume: * @job: The job to be resumed. @@ -386,6 +403,15 @@ void block_job_pause(BlockJob *job); */ void block_job_resume(BlockJob *job); +/** + * block_job_user_resume: + * @job: The job to be resumed. + * + * Resume the specified job. + * Must be paired with a preceding block_job_user_pause. + */ +void block_job_user_resume(BlockJob *job); + /** * block_job_enter: * @job: The job to enter. From c87621ea68e37b87dde82d1da80f9f8d2e13ffae Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 27 Oct 2016 12:07:00 -0400 Subject: [PATCH 13/14] blockjobs: split interface into public/private, Part 1 To make it a little more obvious which functions are intended to be public interface and which are intended to be for use only by jobs themselves, split the interface into "public" and "private" files. Convert blockjobs (e.g. block/backup) to using the private interface. Leave blockdev and others on the public interface. There are remaining uses of private state by qemu-img, and several cases in blockdev.c and block/io.c where we grab job->blk for the purposes of acquiring an AIOContext. These will be corrected in future patches. Signed-off-by: John Snow Reviewed-by: Kevin Wolf Reviewed-by: Jeff Cody Message-id: 1477584421-1399-7-git-send-email-jsnow@redhat.com Signed-off-by: Jeff Cody --- block/backup.c | 2 +- block/commit.c | 2 +- block/mirror.c | 2 +- block/stream.c | 2 +- blockjob.c | 2 +- include/block/block.h | 3 +- include/block/blockjob.h | 212 +------------------------------ include/block/blockjob_int.h | 239 +++++++++++++++++++++++++++++++++++ tests/test-blockjob-txn.c | 2 +- tests/test-blockjob.c | 2 +- 10 files changed, 251 insertions(+), 217 deletions(-) create mode 100644 include/block/blockjob_int.h diff --git a/block/backup.c b/block/backup.c index 2a369e6aff..7b5d8a3757 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" diff --git a/block/commit.c b/block/commit.c index 18ec57851e..e1eda8908b 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" diff --git a/block/mirror.c b/block/mirror.c index aa60bcc44b..b2c1fb855b 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" diff --git a/block/stream.c b/block/stream.c index 152c1bef5f..b05856bd65 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" diff --git a/blockjob.c b/blockjob.c index d880ad2bd5..4aa14a4974 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" diff --git a/include/block/block.h b/include/block/block.h index b81a3e35ce..49bb0b239a 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/blockjob.h b/include/block/blockjob.h index d31ea43116..356cacf004 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; @@ -208,7 +138,7 @@ 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, @@ -236,30 +166,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, int flags, - BlockCompletionFunc *cb, void *opaque, Error **errp); - /** * block_job_add_bdrv: * @job: A block job @@ -271,52 +177,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. @@ -345,14 +205,6 @@ 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. @@ -361,15 +213,6 @@ bool block_job_is_cancelled(BlockJob *job); */ BlockJobInfo *block_job_query(BlockJob *job, Error **errp); -/** - * 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_pause: * @job: The job to be paused. @@ -412,22 +255,6 @@ void block_job_resume(BlockJob *job); */ void block_job_user_resume(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_ready: - * @job: The job which is now ready to complete. - * - * Send a BLOCK_JOB_READY event for the specified job. - */ -void block_job_event_ready(BlockJob *job); - /** * block_job_cancel_sync: * @job: The job to be canceled. @@ -473,37 +300,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: * diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h new file mode 100644 index 0000000000..2b1e859953 --- /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_ready: + * @job: The job which is now ready to complete. + * + * 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/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c index b79e0c68e1..f9afc3be41 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 { diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index 18bf85017a..60b78a3342 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 = { From d8996368106fbf133a6e52561a34f6d0f5080446 Mon Sep 17 00:00:00 2001 From: John Snow Date: Thu, 27 Oct 2016 12:07:01 -0400 Subject: [PATCH 14/14] blockjobs: fix documentation (Trivial) Fix wrong function names in documentation. Signed-off-by: John Snow Reviewed-by: Kevin Wolf Reviewed-by: Jeff Cody Message-id: 1477584421-1399-8-git-send-email-jsnow@redhat.com Signed-off-by: Jeff Cody --- include/block/blockjob_int.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index 2b1e859953..40275e4437 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -198,8 +198,8 @@ void coroutine_fn block_job_pause_point(BlockJob *job); void block_job_enter(BlockJob *job); /** - * block_job_ready: - * @job: The job which is now ready to complete. + * block_job_event_ready: + * @job: The job which is now ready to be completed. * * Send a BLOCK_JOB_READY event for the specified job. */