diff mbox series

[4/6] progressmeter: protect with a mutex

Message ID 20210510085941.22769-5-eesposit@redhat.com
State New
Headers show
Series block-copy: make helper APIs thread safe | expand

Commit Message

Emanuele Giuseppe Esposito May 10, 2021, 8:59 a.m. UTC
Progressmeter is protected by the AioContext mutex, which
is taken by the block jobs and their caller (like blockdev).

We would like to remove the dependency of block layer code on the
AioContext mutex, since most drivers and the core I/O code are already
not relying on it.

The simplest thing to do is to add a mutex to be able to provide
an accurate snapshot of the progress values to the caller.

Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
---
 blockjob.c                    | 33 +++++++++++++++++++++++++--------
 include/qemu/progress_meter.h | 31 +++++++++++++++++++++++++++++++
 job-qmp.c                     |  8 ++++++--
 job.c                         |  3 +++
 qemu-img.c                    |  9 ++++++---
 5 files changed, 71 insertions(+), 13 deletions(-)

Comments

Vladimir Sementsov-Ogievskiy May 10, 2021, 11:28 a.m. UTC | #1
10.05.2021 11:59, Emanuele Giuseppe Esposito wrote:
> Progressmeter is protected by the AioContext mutex, which
> is taken by the block jobs and their caller (like blockdev).
> 
> We would like to remove the dependency of block layer code on the
> AioContext mutex, since most drivers and the core I/O code are already
> not relying on it.
> 
> The simplest thing to do is to add a mutex to be able to provide
> an accurate snapshot of the progress values to the caller.
> 
> Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
> ---
>   blockjob.c                    | 33 +++++++++++++++++++++++++--------

[..]

> --- 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,21 +39,50 @@ typedef struct ProgressMeter {
>   
>       /** Estimated current value at the completion of the process */
>       uint64_t total;
> +
> +    QemuMutex lock;
>   } ProgressMeter;
>   
> +static inline void progress_init(ProgressMeter *pm)
> +{
> +    qemu_mutex_init(&pm->lock);
> +}
> +
> +static inline void progress_destroy(ProgressMeter *pm)
> +{
> +    qemu_mutex_destroy(&pm->lock);
> +}


Could we instead add a c file and add the structure private? Then we'll have progress_new() and progress_free() APIs instead.

This way, it would be a lot simpler to control that nobady use structure fields directly.



> +
> +static inline void progress_get_snapshot(ProgressMeter *pm,
> +                                         uint64_t *current, uint64_t *total)
> +{
> +    QEMU_LOCK_GUARD(&pm->lock);
> +
> +    if (current) {
> +        *current = pm->current;
> +    }
> +
> +    if (total) {
> +        *total = pm->total;
> +    }
> +}

We don't have caller that pass only one pointer. So we can just do

*current = pm->current;
*total = pm->total;

implicitly requiring both pointers to be non NULL.
Emanuele Giuseppe Esposito May 10, 2021, 4:52 p.m. UTC | #2
On 10/05/2021 13:28, Vladimir Sementsov-Ogievskiy wrote:
> 10.05.2021 11:59, Emanuele Giuseppe Esposito wrote:
>> Progressmeter is protected by the AioContext mutex, which
>> is taken by the block jobs and their caller (like blockdev).
>>
>> We would like to remove the dependency of block layer code on the
>> AioContext mutex, since most drivers and the core I/O code are already
>> not relying on it.
>>
>> The simplest thing to do is to add a mutex to be able to provide
>> an accurate snapshot of the progress values to the caller.
>>
>> Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
>> ---
>>   blockjob.c                    | 33 +++++++++++++++++++++++++--------
> 
> [..]
> 
>> --- 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,21 +39,50 @@ typedef struct ProgressMeter {
>>       /** Estimated current value at the completion of the process */
>>       uint64_t total;
>> +
>> +    QemuMutex lock;
>>   } ProgressMeter;
>> +static inline void progress_init(ProgressMeter *pm)
>> +{
>> +    qemu_mutex_init(&pm->lock);
>> +}
>> +
>> +static inline void progress_destroy(ProgressMeter *pm)
>> +{
>> +    qemu_mutex_destroy(&pm->lock);
>> +}
> 
> 
> Could we instead add a c file and add the structure private? Then we'll 
> have progress_new() and progress_free() APIs instead.
> 
> This way, it would be a lot simpler to control that nobady use structure 
> fields directly.
> 
Makes sense, I'll do it.
> 
> 
>> +
>> +static inline void progress_get_snapshot(ProgressMeter *pm,
>> +                                         uint64_t *current, uint64_t 
>> *total)
>> +{
>> +    QEMU_LOCK_GUARD(&pm->lock);
>> +
>> +    if (current) {
>> +        *current = pm->current;
>> +    }
>> +
>> +    if (total) {
>> +        *total = pm->total;
>> +    }
>> +}
> 
> We don't have caller that pass only one pointer. So we can just do
> 
> *current = pm->current;
> *total = pm->total;
> 
> implicitly requiring both pointers to be non NULL.

Is it so performance critical that we need to skip these safety checks?
IMHO we can keep it as it is.

Thank you,
Emanuele
> 
> 
>
Vladimir Sementsov-Ogievskiy May 10, 2021, 5:17 p.m. UTC | #3
10.05.2021 19:52, Emanuele Giuseppe Esposito wrote:
> 
> 
> On 10/05/2021 13:28, Vladimir Sementsov-Ogievskiy wrote:
>> 10.05.2021 11:59, Emanuele Giuseppe Esposito wrote:
>>> Progressmeter is protected by the AioContext mutex, which
>>> is taken by the block jobs and their caller (like blockdev).
>>>
>>> We would like to remove the dependency of block layer code on the
>>> AioContext mutex, since most drivers and the core I/O code are already
>>> not relying on it.
>>>
>>> The simplest thing to do is to add a mutex to be able to provide
>>> an accurate snapshot of the progress values to the caller.
>>>
>>> Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
>>> ---
>>>   blockjob.c                    | 33 +++++++++++++++++++++++++--------
>>
>> [..]
>>
>>> --- 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,21 +39,50 @@ typedef struct ProgressMeter {
>>>       /** Estimated current value at the completion of the process */
>>>       uint64_t total;
>>> +
>>> +    QemuMutex lock;
>>>   } ProgressMeter;
>>> +static inline void progress_init(ProgressMeter *pm)
>>> +{
>>> +    qemu_mutex_init(&pm->lock);
>>> +}
>>> +
>>> +static inline void progress_destroy(ProgressMeter *pm)
>>> +{
>>> +    qemu_mutex_destroy(&pm->lock);
>>> +}
>>
>>
>> Could we instead add a c file and add the structure private? Then we'll have progress_new() and progress_free() APIs instead.
>>
>> This way, it would be a lot simpler to control that nobady use structure fields directly.
>>
> Makes sense, I'll do it.
>>
>>
>>> +
>>> +static inline void progress_get_snapshot(ProgressMeter *pm,
>>> +                                         uint64_t *current, uint64_t *total)
>>> +{
>>> +    QEMU_LOCK_GUARD(&pm->lock);
>>> +
>>> +    if (current) {
>>> +        *current = pm->current;
>>> +    }
>>> +
>>> +    if (total) {
>>> +        *total = pm->total;
>>> +    }
>>> +}
>>
>> We don't have caller that pass only one pointer. So we can just do
>>
>> *current = pm->current;
>> *total = pm->total;
>>
>> implicitly requiring both pointers to be non NULL.
> 
> Is it so performance critical that we need to skip these safety checks?
> IMHO we can keep it as it is.


Not performance. It's just less code. You propose more complex interface (pointers may be valid pointers or NULL), implemented by more complex code (extra if blocks). And there no users for this. So, I don't see any reason for extra logic and code lines.

What kind of safety you want? All current callers pass non-zero pointers, it's obvious. If someone will create new call, he should look first at function itself, not blindly pass NULL pointers. And if not, he will get clean crash on zero pointer dereference.
Emanuele Giuseppe Esposito May 10, 2021, 5:57 p.m. UTC | #4
>>>
>>> We don't have caller that pass only one pointer. So we can just do
>>>
>>> *current = pm->current;
>>> *total = pm->total;
>>>
>>> implicitly requiring both pointers to be non NULL.
>>
>> Is it so performance critical that we need to skip these safety checks?
>> IMHO we can keep it as it is.
> 
> 
> Not performance. It's just less code. You propose more complex interface 
> (pointers may be valid pointers or NULL), implemented by more complex 
> code (extra if blocks). And there no users for this. So, I don't see any 
> reason for extra logic and code lines.
> 
> What kind of safety you want? All current callers pass non-zero 
> pointers, it's obvious. If someone will create new call, he should look 
> first at function itself, not blindly pass NULL pointers. And if not, he 
> will get clean crash on zero pointer dereference.

Ok, makes sense. Will remove the ifs.

Thank you,
Emanuele
Paolo Bonzini May 11, 2021, 12:28 p.m. UTC | #5
On 10/05/21 13:28, Vladimir Sementsov-Ogievskiy wrote:
> 
> 
> Could we instead add a c file and add the structure private? Then we'll 
> have progress_new() and progress_free() APIs instead.
> 
> This way, it would be a lot simpler to control that nobady use structure 
> fields directly.

I don't know...  I prefer embedding structs to heap allocation.

Paolo
Vladimir Sementsov-Ogievskiy May 12, 2021, 7:09 a.m. UTC | #6
11.05.2021 15:28, Paolo Bonzini wrote:
> On 10/05/21 13:28, Vladimir Sementsov-Ogievskiy wrote:
>>
>>
>> Could we instead add a c file and add the structure private? Then we'll have progress_new() and progress_free() APIs instead.
>>
>> This way, it would be a lot simpler to control that nobady use structure fields directly.
> 
> I don't know...  I prefer embedding structs to heap allocation.
> 


I agree that embedding looks better from allocation point of view.. But IMHO encapsulation guarantee of private structure is better feature. Especially when we have getter/setter methods. Even the patch itself is example: to review this carefully, I should somehow check that every use of the structure is updated (grepping the code, or maybe change field name and recompile, to catch every occurrence of it).. And if patch makes structure private and adds setters/getters, it can be reviewed without applying.

And we have to call _init/_destroy anyway, so it's not more complex to call _new/_free instead.
Stefan Hajnoczi May 12, 2021, 3:53 p.m. UTC | #7
On Mon, May 10, 2021 at 10:59:39AM +0200, Emanuele Giuseppe Esposito wrote:
> Progressmeter is protected by the AioContext mutex, which
> is taken by the block jobs and their caller (like blockdev).
> 
> We would like to remove the dependency of block layer code on the
> AioContext mutex, since most drivers and the core I/O code are already
> not relying on it.
> 
> The simplest thing to do is to add a mutex to be able to provide
> an accurate snapshot of the progress values to the caller.
> 
> Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
> ---
>  blockjob.c                    | 33 +++++++++++++++++++++++++--------
>  include/qemu/progress_meter.h | 31 +++++++++++++++++++++++++++++++
>  job-qmp.c                     |  8 ++++++--
>  job.c                         |  3 +++
>  qemu-img.c                    |  9 ++++++---
>  5 files changed, 71 insertions(+), 13 deletions(-)

Unlike the next two patches where I had comments, here adding the lock
makes sense to me - the point of the progress meter is to report numbers
to the monitor thread so thread-safety is a key part of the API's value.

Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
diff mbox series

Patch

diff --git a/blockjob.c b/blockjob.c
index 046c1bcd66..98bb12f015 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..81f79770d2 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,21 +39,50 @@  typedef struct ProgressMeter {
 
     /** Estimated current value at the completion of the process */
     uint64_t total;
+
+    QemuMutex lock;
 } ProgressMeter;
 
+static inline void progress_init(ProgressMeter *pm)
+{
+    qemu_mutex_init(&pm->lock);
+}
+
+static inline void progress_destroy(ProgressMeter *pm)
+{
+    qemu_mutex_destroy(&pm->lock);
+}
+
+static inline void progress_get_snapshot(ProgressMeter *pm,
+                                         uint64_t *current, uint64_t *total)
+{
+    QEMU_LOCK_GUARD(&pm->lock);
+
+    if (current) {
+        *current = pm->current;
+    }
+
+    if (total) {
+        *total = pm->total;
+    }
+}
+
 static inline void progress_work_done(ProgressMeter *pm, uint64_t done)
 {
+    QEMU_LOCK_GUARD(&pm->lock);
     pm->current += done;
 }
 
 static inline void progress_set_remaining(ProgressMeter *pm, uint64_t remaining)
 {
+    QEMU_LOCK_GUARD(&pm->lock);
     pm->total = pm->current + remaining;
 }
 
 static inline void progress_increase_remaining(ProgressMeter *pm,
                                                uint64_t delta)
 {
+    QEMU_LOCK_GUARD(&pm->lock);
     pm->total += delta;
 }
 
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 4aff13d95a..00b0560052 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));