mirror: stop cancelling in-flight requests on non-force cancel in READY
authorVladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Wed, 21 Apr 2021 07:58:58 +0000 (10:58 +0300)
committerMax Reitz <mreitz@redhat.com>
Fri, 14 May 2021 14:14:10 +0000 (16:14 +0200)
If mirror is READY than cancel operation is not discarding the whole
result of the operation, but instead it's a documented way get a
point-in-time snapshot of source disk.

So, we should not cancel any requests if mirror is READ and
force=false. Let's fix that case.

Note, that bug that we have before this commit is not critical, as the
only .bdrv_cancel_in_flight implementation is nbd_cancel_in_flight()
and it cancels only requests waiting for reconnection, so it should be
rare case.

Fixes: 521ff8b779b11c394dbdc43f02e158dd99df308a
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Message-Id: <20210421075858.40197-1-vsementsov@virtuozzo.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
block/backup.c
block/mirror.c
include/block/block_int.h
include/qemu/job.h
job.c
tests/qemu-iotests/264

index 6cf2f974aa2ae109dc543243072b461fe9f06952..bd3614ce707084e332576d11655bdb317fe0b884 100644 (file)
@@ -331,7 +331,7 @@ static void coroutine_fn backup_set_speed(BlockJob *job, int64_t speed)
     }
 }
 
-static void backup_cancel(Job *job)
+static void backup_cancel(Job *job, bool force)
 {
     BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
 
index 840b8e8c15438c06ab92ee9f1020c8a8aadc51fb..019f6deaa5d2e62de009123f02ec2cbe8320865a 100644 (file)
@@ -1178,12 +1178,14 @@ static bool mirror_drained_poll(BlockJob *job)
     return !!s->in_flight;
 }
 
-static void mirror_cancel(Job *job)
+static void mirror_cancel(Job *job, bool force)
 {
     MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
     BlockDriverState *target = blk_bs(s->target);
 
-    bdrv_cancel_in_flight(target);
+    if (force || !job_is_ready(job)) {
+        bdrv_cancel_in_flight(target);
+    }
 }
 
 static const BlockJobDriver mirror_job_driver = {
index c823f5b1b3630d098f79f30773dec9ec3c6e4116..731ffedb279dea9652f03387f809604b14b581f9 100644 (file)
@@ -357,7 +357,7 @@ struct BlockDriver {
      * of in-flight requests, so don't waste the time if possible.
      *
      * One example usage is to avoid waiting for an nbd target node reconnect
-     * timeout during job-cancel.
+     * timeout during job-cancel with force=true.
      */
     void (*bdrv_cancel_in_flight)(BlockDriverState *bs);
 
index efc6fa754498475659220d96e66b929bfcbadef0..41162ed494ccba30b2fb7ab127466ed0799b4bc7 100644 (file)
@@ -254,7 +254,7 @@ struct JobDriver {
     /**
      * If the callback is not NULL, it will be invoked in job_cancel_async
      */
-    void (*cancel)(Job *job);
+    void (*cancel)(Job *job, bool force);
 
 
     /** Called when the job is freed */
diff --git a/job.c b/job.c
index 4aff13d95ab4d202ccb03c3f81d889d3d4fa64b7..8775c1803b1b3e9d720e68c11b2e8b210b422ea9 100644 (file)
--- a/job.c
+++ b/job.c
@@ -716,7 +716,7 @@ static int job_finalize_single(Job *job)
 static void job_cancel_async(Job *job, bool force)
 {
     if (job->driver->cancel) {
-        job->driver->cancel(job);
+        job->driver->cancel(job, force);
     }
     if (job->user_paused) {
         /* Do not call job_enter here, the caller will handle it.  */
index 4f96825a22c2e2abe250e7006d11ce6d52419fa3..bc431d1a19bcb232e57cfeb053b79309ec3393e7 100755 (executable)
@@ -95,7 +95,7 @@ class TestNbdReconnect(iotests.QMPTestCase):
             self.assert_qmp(result, 'return', {})
 
     def cancel_job(self):
-        result = self.vm.qmp('block-job-cancel', device='drive0')
+        result = self.vm.qmp('block-job-cancel', device='drive0', force=True)
         self.assert_qmp(result, 'return', {})
 
         start_t = time.time()