diff --git a/block/meson.build b/block/meson.build index 01861e1545..ef1ba3d973 100644 --- a/block/meson.build +++ b/block/meson.build @@ -13,6 +13,7 @@ block_ss.add(files( 'commit.c', 'copy-on-read.c', 'preallocate.c', + 'progress_meter.c', 'create.c', 'crypto.c', 'dirty-bitmap.c', diff --git a/block/progress_meter.c b/block/progress_meter.c new file mode 100644 index 0000000000..aa2e60248c --- /dev/null +++ b/block/progress_meter.c @@ -0,0 +1,64 @@ +/* + * Helper functionality for some process progress tracking. + * + * Copyright (c) 2011 IBM Corp. + * Copyright (c) 2012, 2018 Red Hat, Inc. + * Copyright (c) 2020 Virtuozzo International GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu/osdep.h" +#include "qemu/progress_meter.h" + +void progress_init(ProgressMeter *pm) +{ + qemu_mutex_init(&pm->lock); +} + +void progress_destroy(ProgressMeter *pm) +{ + qemu_mutex_destroy(&pm->lock); +} + +void progress_get_snapshot(ProgressMeter *pm, uint64_t *current, + uint64_t *total) +{ + QEMU_LOCK_GUARD(&pm->lock); + + *current = pm->current; + *total = pm->total; +} + +void progress_work_done(ProgressMeter *pm, uint64_t done) +{ + QEMU_LOCK_GUARD(&pm->lock); + pm->current += done; +} + +void progress_set_remaining(ProgressMeter *pm, uint64_t remaining) +{ + QEMU_LOCK_GUARD(&pm->lock); + pm->total = pm->current + remaining; +} + +void progress_increase_remaining(ProgressMeter *pm, uint64_t delta) +{ + QEMU_LOCK_GUARD(&pm->lock); + pm->total += delta; +} diff --git a/blockjob.c b/blockjob.c index 22e5bb9b1f..4bad1408cb 100644 --- a/blockjob.c +++ b/blockjob.c @@ -306,18 +306,23 @@ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n) BlockJobInfo *block_job_query(BlockJob *job, Error **errp) { BlockJobInfo *info; + uint64_t progress_current, progress_total; if (block_job_is_internal(job)) { error_setg(errp, "Cannot query QEMU internal jobs"); return NULL; } + + progress_get_snapshot(&job->job.progress, &progress_current, + &progress_total); + info = g_new0(BlockJobInfo, 1); info->type = g_strdup(job_type_str(&job->job)); info->device = g_strdup(job->job.id); info->busy = qatomic_read(&job->job.busy); info->paused = job->job.pause_count > 0; - info->offset = job->job.progress.current; - info->len = job->job.progress.total; + info->offset = progress_current; + info->len = progress_total; info->speed = job->speed; info->io_status = job->iostatus; info->ready = job_is_ready(&job->job), @@ -344,15 +349,19 @@ static void block_job_iostatus_set_err(BlockJob *job, int error) static void block_job_event_cancelled(Notifier *n, void *opaque) { BlockJob *job = opaque; + uint64_t progress_current, progress_total; if (block_job_is_internal(job)) { return; } + progress_get_snapshot(&job->job.progress, &progress_current, + &progress_total); + qapi_event_send_block_job_cancelled(job_type(&job->job), job->job.id, - job->job.progress.total, - job->job.progress.current, + progress_total, + progress_current, job->speed); } @@ -360,6 +369,7 @@ static void block_job_event_completed(Notifier *n, void *opaque) { BlockJob *job = opaque; const char *msg = NULL; + uint64_t progress_current, progress_total; if (block_job_is_internal(job)) { return; @@ -369,10 +379,13 @@ static void block_job_event_completed(Notifier *n, void *opaque) msg = error_get_pretty(job->job.err); } + progress_get_snapshot(&job->job.progress, &progress_current, + &progress_total); + qapi_event_send_block_job_completed(job_type(&job->job), job->job.id, - job->job.progress.total, - job->job.progress.current, + progress_total, + progress_current, job->speed, !!msg, msg); @@ -393,15 +406,19 @@ static void block_job_event_pending(Notifier *n, void *opaque) static void block_job_event_ready(Notifier *n, void *opaque) { BlockJob *job = opaque; + uint64_t progress_current, progress_total; if (block_job_is_internal(job)) { return; } + progress_get_snapshot(&job->job.progress, &progress_current, + &progress_total); + qapi_event_send_block_job_ready(job_type(&job->job), job->job.id, - job->job.progress.total, - job->job.progress.current, + progress_total, + progress_current, job->speed); } diff --git a/include/qemu/progress_meter.h b/include/qemu/progress_meter.h index 9a23ff071c..dadf822bbf 100644 --- a/include/qemu/progress_meter.h +++ b/include/qemu/progress_meter.h @@ -27,6 +27,8 @@ #ifndef QEMU_PROGRESS_METER_H #define QEMU_PROGRESS_METER_H +#include "qemu/lockable.h" + typedef struct ProgressMeter { /** * Current progress. The unit is arbitrary as long as the ratio between @@ -37,22 +39,24 @@ typedef struct ProgressMeter { /** Estimated current value at the completion of the process */ uint64_t total; + + QemuMutex lock; /* protects concurrent access to above fields */ } ProgressMeter; -static inline void progress_work_done(ProgressMeter *pm, uint64_t done) -{ - pm->current += done; -} +void progress_init(ProgressMeter *pm); +void progress_destroy(ProgressMeter *pm); -static inline void progress_set_remaining(ProgressMeter *pm, uint64_t remaining) -{ - pm->total = pm->current + remaining; -} +/* Get a snapshot of internal current and total values */ +void progress_get_snapshot(ProgressMeter *pm, uint64_t *current, + uint64_t *total); -static inline void progress_increase_remaining(ProgressMeter *pm, - uint64_t delta) -{ - pm->total += delta; -} +/* Increases the amount of work done so far by @done */ +void progress_work_done(ProgressMeter *pm, uint64_t done); + +/* Sets how much work has to be done to complete to @remaining */ +void progress_set_remaining(ProgressMeter *pm, uint64_t remaining); + +/* Increases the total work to do by @delta */ +void progress_increase_remaining(ProgressMeter *pm, uint64_t delta); #endif /* QEMU_PROGRESS_METER_H */ diff --git a/job-qmp.c b/job-qmp.c index 34c4da094f..829a28aa70 100644 --- a/job-qmp.c +++ b/job-qmp.c @@ -144,16 +144,20 @@ void qmp_job_dismiss(const char *id, Error **errp) static JobInfo *job_query_single(Job *job, Error **errp) { JobInfo *info; + uint64_t progress_current; + uint64_t progress_total; assert(!job_is_internal(job)); + progress_get_snapshot(&job->progress, &progress_current, + &progress_total); info = g_new(JobInfo, 1); *info = (JobInfo) { .id = g_strdup(job->id), .type = job_type(job), .status = job->status, - .current_progress = job->progress.current, - .total_progress = job->progress.total, + .current_progress = progress_current, + .total_progress = progress_total, .has_error = !!job->err, .error = job->err ? \ g_strdup(error_get_pretty(job->err)) : NULL, diff --git a/job.c b/job.c index 8775c1803b..e7a5d28854 100644 --- a/job.c +++ b/job.c @@ -339,6 +339,8 @@ void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn, job->cb = cb; job->opaque = opaque; + progress_init(&job->progress); + notifier_list_init(&job->on_finalize_cancelled); notifier_list_init(&job->on_finalize_completed); notifier_list_init(&job->on_pending); @@ -382,6 +384,7 @@ void job_unref(Job *job) QLIST_REMOVE(job, job_list); + progress_destroy(&job->progress); error_free(job->err); g_free(job->id); g_free(job); diff --git a/qemu-img.c b/qemu-img.c index a5993682aa..7956a89965 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -900,6 +900,7 @@ static void common_block_job_cb(void *opaque, int ret) static void run_block_job(BlockJob *job, Error **errp) { + uint64_t progress_current, progress_total; AioContext *aio_context = blk_get_aio_context(job->blk); int ret = 0; @@ -908,9 +909,11 @@ static void run_block_job(BlockJob *job, Error **errp) do { float progress = 0.0f; aio_poll(aio_context, true); - if (job->job.progress.total) { - progress = (float)job->job.progress.current / - job->job.progress.total * 100.f; + + progress_get_snapshot(&job->job.progress, &progress_current, + &progress_total); + if (progress_total) { + progress = (float)progress_current / progress_total * 100.f; } qemu_progress_print(progress, 0); } while (!job_is_ready(&job->job) && !job_is_completed(&job->job));