aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--block/mirror.c10
-rw-r--r--blockdev.c4
-rw-r--r--blockjob.c16
-rw-r--r--hmp-commands.hx3
-rw-r--r--include/block/blockjob.h12
-rw-r--r--qapi/block-core.json5
-rw-r--r--tests/test-blockjob-txn.c8
7 files changed, 34 insertions, 24 deletions
diff --git a/block/mirror.c b/block/mirror.c
index 76fddb3838..820f512c7b 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -869,11 +869,8 @@ static void coroutine_fn mirror_run(void *opaque)
ret = 0;
trace_mirror_before_sleep(s, cnt, s->synced, delay_ns);
- if (!s->synced) {
- block_job_sleep_ns(&s->common, delay_ns);
- if (block_job_is_cancelled(&s->common)) {
- break;
- }
+ if (block_job_is_cancelled(&s->common) && s->common.force) {
+ break;
} else if (!should_complete) {
delay_ns = (s->in_flight == 0 && cnt == 0 ? SLICE_TIME : 0);
block_job_sleep_ns(&s->common, delay_ns);
@@ -887,7 +884,8 @@ immediate_exit:
* or it was cancelled prematurely so that we do not guarantee that
* the target is a copy of the source.
*/
- assert(ret < 0 || (!s->synced && block_job_is_cancelled(&s->common)));
+ assert(ret < 0 || ((s->common.force || !s->synced) &&
+ block_job_is_cancelled(&s->common)));
assert(need_drain);
mirror_wait_for_all_io(s);
}
diff --git a/blockdev.c b/blockdev.c
index 348903234a..a6758c1220 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -150,7 +150,7 @@ void blockdev_mark_auto_del(BlockBackend *blk)
aio_context_acquire(aio_context);
if (bs->job) {
- block_job_cancel(bs->job);
+ block_job_cancel(bs->job, false);
}
aio_context_release(aio_context);
@@ -3850,7 +3850,7 @@ void qmp_block_job_cancel(const char *device,
}
trace_qmp_block_job_cancel(job);
- block_job_user_cancel(job, errp);
+ block_job_user_cancel(job, force, errp);
out:
aio_context_release(aio_context);
}
diff --git a/blockjob.c b/blockjob.c
index bc7517c0d0..ef3ed69ff1 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -487,7 +487,7 @@ static int block_job_finalize_single(BlockJob *job)
return 0;
}
-static void block_job_cancel_async(BlockJob *job)
+static void block_job_cancel_async(BlockJob *job, bool force)
{
if (job->iostatus != BLOCK_DEVICE_IO_STATUS_OK) {
block_job_iostatus_reset(job);
@@ -498,6 +498,8 @@ static void block_job_cancel_async(BlockJob *job)
job->pause_count--;
}
job->cancelled = true;
+ /* To prevent 'force == false' overriding a previous 'force == true' */
+ job->force |= force;
}
static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock)
@@ -581,7 +583,7 @@ static void block_job_completed_txn_abort(BlockJob *job)
* on the caller, so leave it. */
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
if (other_job != job) {
- block_job_cancel_async(other_job);
+ block_job_cancel_async(other_job, false);
}
}
while (!QLIST_EMPTY(&txn->jobs)) {
@@ -747,13 +749,13 @@ void block_job_user_resume(BlockJob *job, Error **errp)
block_job_resume(job);
}
-void block_job_cancel(BlockJob *job)
+void block_job_cancel(BlockJob *job, bool force)
{
if (job->status == BLOCK_JOB_STATUS_CONCLUDED) {
block_job_do_dismiss(job);
return;
}
- block_job_cancel_async(job);
+ block_job_cancel_async(job, force);
if (!block_job_started(job)) {
block_job_completed(job, -ECANCELED);
} else if (job->deferred_to_main_loop) {
@@ -763,12 +765,12 @@ void block_job_cancel(BlockJob *job)
}
}
-void block_job_user_cancel(BlockJob *job, Error **errp)
+void block_job_user_cancel(BlockJob *job, bool force, Error **errp)
{
if (block_job_apply_verb(job, BLOCK_JOB_VERB_CANCEL, errp)) {
return;
}
- block_job_cancel(job);
+ block_job_cancel(job, force);
}
/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be
@@ -776,7 +778,7 @@ void block_job_user_cancel(BlockJob *job, Error **errp)
* function pointer casts there. */
static void block_job_cancel_err(BlockJob *job, Error **errp)
{
- block_job_cancel(job);
+ block_job_cancel(job, false);
}
int block_job_cancel_sync(BlockJob *job)
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 1723cbe1df..35d862a5d2 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -106,7 +106,8 @@ ETEXI
.args_type = "force:-f,device:B",
.params = "[-f] device",
.help = "stop an active background block operation (use -f"
- "\n\t\t\t if the operation is currently paused)",
+ "\n\t\t\t if you want to abort the operation immediately"
+ "\n\t\t\t instead of keep running until data is in sync)",
.cmd = hmp_block_job_cancel,
},
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
index 978274ed2b..fc645dac68 100644
--- a/include/block/blockjob.h
+++ b/include/block/blockjob.h
@@ -63,6 +63,12 @@ typedef struct BlockJob {
bool cancelled;
/**
+ * Set to true if the job should abort immediately without waiting
+ * for data to be in sync.
+ */
+ bool force;
+
+ /**
* Counter for pause request. If non-zero, the block job is either paused,
* or if busy == true will pause itself as soon as possible.
*/
@@ -230,10 +236,11 @@ void block_job_start(BlockJob *job);
/**
* block_job_cancel:
* @job: The job to be canceled.
+ * @force: Quit a job without waiting for data to be in sync.
*
* Asynchronously cancel the specified job.
*/
-void block_job_cancel(BlockJob *job);
+void block_job_cancel(BlockJob *job, bool force);
/**
* block_job_complete:
@@ -307,11 +314,12 @@ void block_job_user_resume(BlockJob *job, Error **errp);
/**
* block_job_user_cancel:
* @job: The job to be cancelled.
+ * @force: Quit a job without waiting for data to be in sync.
*
* Cancels the specified job, but may refuse to do so if the
* operation isn't currently meaningful.
*/
-void block_job_user_cancel(BlockJob *job, Error **errp);
+void block_job_user_cancel(BlockJob *job, bool force, Error **errp);
/**
* block_job_cancel_sync:
diff --git a/qapi/block-core.json b/qapi/block-core.json
index a565c173fd..5b0ad1a8b7 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -2207,8 +2207,9 @@
# the name of the parameter), but since QEMU 2.7 it can have
# other values.
#
-# @force: whether to allow cancellation of a paused job (default
-# false). Since 1.3.
+# @force: If true, and the job has already emitted the event BLOCK_JOB_READY,
+# abandon the job immediately (even if it is paused) instead of waiting
+# for the destination to complete its final synchronization (since 1.3)
#
# Returns: Nothing on success
# If no background operation is active on this device, DeviceNotActive
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
index 34f09ef8c1..5789893dda 100644
--- a/tests/test-blockjob-txn.c
+++ b/tests/test-blockjob-txn.c
@@ -124,7 +124,7 @@ static void test_single_job(int expected)
block_job_start(job);
if (expected == -ECANCELED) {
- block_job_cancel(job);
+ block_job_cancel(job, false);
}
while (result == -EINPROGRESS) {
@@ -170,10 +170,10 @@ static void test_pair_jobs(int expected1, int expected2)
block_job_txn_unref(txn);
if (expected1 == -ECANCELED) {
- block_job_cancel(job1);
+ block_job_cancel(job1, false);
}
if (expected2 == -ECANCELED) {
- block_job_cancel(job2);
+ block_job_cancel(job2, false);
}
while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) {
@@ -226,7 +226,7 @@ static void test_pair_jobs_fail_cancel_race(void)
block_job_start(job1);
block_job_start(job2);
- block_job_cancel(job1);
+ block_job_cancel(job1, false);
/* Now make job2 finish before the main loop kicks jobs. This simulates
* the race between a pending kick and another job completing.