Blame SOURCES/kvm-job-Move-single-job-finalisation-to-Job.patch

357786
From adcfa2a0ddbf68a34b24afcbfaaf03a19d1406ca Mon Sep 17 00:00:00 2001
357786
From: Kevin Wolf <kwolf@redhat.com>
357786
Date: Tue, 26 Jun 2018 09:48:16 +0200
357786
Subject: [PATCH 47/89] job: Move single job finalisation to Job
357786
357786
RH-Author: Kevin Wolf <kwolf@redhat.com>
357786
Message-id: <20180626094856.6924-34-kwolf@redhat.com>
357786
Patchwork-id: 81070
357786
O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH v2 33/73] job: Move single job finalisation to Job
357786
Bugzilla: 1513543
357786
RH-Acked-by: Jeffrey Cody <jcody@redhat.com>
357786
RH-Acked-by: Max Reitz <mreitz@redhat.com>
357786
RH-Acked-by: Fam Zheng <famz@redhat.com>
357786
357786
This moves the finalisation of a single job from BlockJob to Job.
357786
357786
Some part of this code depends on job transactions, and job transactions
357786
call this code, we introduce some temporary calls from Job functions to
357786
BlockJob ones. This will be fixed once transactions move to Job, too.
357786
357786
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
357786
Reviewed-by: Max Reitz <mreitz@redhat.com>
357786
(cherry picked from commit 4ad351819b974d724e926fd23cdd66bec3c9768e)
357786
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
357786
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
357786
---
357786
 block/backup.c               |  22 +++----
357786
 block/commit.c               |   2 +-
357786
 block/mirror.c               |   2 +-
357786
 blockjob.c                   | 142 ++++++++-----------------------------------
357786
 include/block/blockjob.h     |   9 ---
357786
 include/block/blockjob_int.h |  36 -----------
357786
 include/qemu/job.h           |  53 +++++++++++++++-
357786
 job.c                        | 100 +++++++++++++++++++++++++++++-
357786
 qemu-img.c                   |   2 +-
357786
 tests/test-blockjob.c        |  10 +--
357786
 10 files changed, 194 insertions(+), 184 deletions(-)
357786
357786
diff --git a/block/backup.c b/block/backup.c
357786
index 4d011d5..bd31282 100644
357786
--- a/block/backup.c
357786
+++ b/block/backup.c
357786
@@ -207,25 +207,25 @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
357786
     }
357786
 }
357786
 
357786
-static void backup_commit(BlockJob *job)
357786
+static void backup_commit(Job *job)
357786
 {
357786
-    BackupBlockJob *s = container_of(job, BackupBlockJob, common);
357786
+    BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
357786
     if (s->sync_bitmap) {
357786
         backup_cleanup_sync_bitmap(s, 0);
357786
     }
357786
 }
357786
 
357786
-static void backup_abort(BlockJob *job)
357786
+static void backup_abort(Job *job)
357786
 {
357786
-    BackupBlockJob *s = container_of(job, BackupBlockJob, common);
357786
+    BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
357786
     if (s->sync_bitmap) {
357786
         backup_cleanup_sync_bitmap(s, -1);
357786
     }
357786
 }
357786
 
357786
-static void backup_clean(BlockJob *job)
357786
+static void backup_clean(Job *job)
357786
 {
357786
-    BackupBlockJob *s = container_of(job, BackupBlockJob, common);
357786
+    BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
357786
     assert(s->target);
357786
     blk_unref(s->target);
357786
     s->target = NULL;
357786
@@ -530,10 +530,10 @@ static const BlockJobDriver backup_job_driver = {
357786
         .free                   = block_job_free,
357786
         .user_resume            = block_job_user_resume,
357786
         .start                  = backup_run,
357786
+        .commit                 = backup_commit,
357786
+        .abort                  = backup_abort,
357786
+        .clean                  = backup_clean,
357786
     },
357786
-    .commit                 = backup_commit,
357786
-    .abort                  = backup_abort,
357786
-    .clean                  = backup_clean,
357786
     .attached_aio_context   = backup_attached_aio_context,
357786
     .drain                  = backup_drain,
357786
 };
357786
@@ -678,8 +678,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
357786
         bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL);
357786
     }
357786
     if (job) {
357786
-        backup_clean(&job->common);
357786
-        block_job_early_fail(&job->common);
357786
+        backup_clean(&job->common.job);
357786
+        job_early_fail(&job->common.job);
357786
     }
357786
 
357786
     return NULL;
357786
diff --git a/block/commit.c b/block/commit.c
357786
index 7a6ae59..e53b2d7 100644
357786
--- a/block/commit.c
357786
+++ b/block/commit.c
357786
@@ -385,7 +385,7 @@ fail:
357786
     if (commit_top_bs) {
357786
         bdrv_replace_node(commit_top_bs, top, &error_abort);
357786
     }
357786
-    block_job_early_fail(&s->common);
357786
+    job_early_fail(&s->common.job);
357786
 }
357786
 
357786
 
357786
diff --git a/block/mirror.c b/block/mirror.c
357786
index 5091e72..e9a90ea 100644
357786
--- a/block/mirror.c
357786
+++ b/block/mirror.c
357786
@@ -1257,7 +1257,7 @@ fail:
357786
 
357786
         g_free(s->replaces);
357786
         blk_unref(s->target);
357786
-        block_job_early_fail(&s->common);
357786
+        job_early_fail(&s->common.job);
357786
     }
357786
 
357786
     bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL,
357786
diff --git a/blockjob.c b/blockjob.c
357786
index 05d7921..34c57da 100644
357786
--- a/blockjob.c
357786
+++ b/blockjob.c
357786
@@ -127,7 +127,7 @@ void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job)
357786
     block_job_txn_ref(txn);
357786
 }
357786
 
357786
-static void block_job_txn_del_job(BlockJob *job)
357786
+void block_job_txn_del_job(BlockJob *job)
357786
 {
357786
     if (job->txn) {
357786
         QLIST_REMOVE(job, txn_list);
357786
@@ -262,101 +262,12 @@ const BlockJobDriver *block_job_driver(BlockJob *job)
357786
     return job->driver;
357786
 }
357786
 
357786
-static void block_job_decommission(BlockJob *job)
357786
-{
357786
-    assert(job);
357786
-    job->job.busy = false;
357786
-    job->job.paused = false;
357786
-    job->job.deferred_to_main_loop = true;
357786
-    block_job_txn_del_job(job);
357786
-    job_state_transition(&job->job, JOB_STATUS_NULL);
357786
-    job_unref(&job->job);
357786
-}
357786
-
357786
-static void block_job_do_dismiss(BlockJob *job)
357786
-{
357786
-    block_job_decommission(job);
357786
-}
357786
-
357786
-static void block_job_conclude(BlockJob *job)
357786
-{
357786
-    job_state_transition(&job->job, JOB_STATUS_CONCLUDED);
357786
-    if (job->job.auto_dismiss || !job_started(&job->job)) {
357786
-        block_job_do_dismiss(job);
357786
-    }
357786
-}
357786
-
357786
-static void block_job_update_rc(BlockJob *job)
357786
-{
357786
-    if (!job->ret && job_is_cancelled(&job->job)) {
357786
-        job->ret = -ECANCELED;
357786
-    }
357786
-    if (job->ret) {
357786
-        job_state_transition(&job->job, JOB_STATUS_ABORTING);
357786
-    }
357786
-}
357786
-
357786
 static int block_job_prepare(BlockJob *job)
357786
 {
357786
-    if (job->ret == 0 && job->driver->prepare) {
357786
-        job->ret = job->driver->prepare(job);
357786
-    }
357786
-    return job->ret;
357786
-}
357786
-
357786
-static void block_job_commit(BlockJob *job)
357786
-{
357786
-    assert(!job->ret);
357786
-    if (job->driver->commit) {
357786
-        job->driver->commit(job);
357786
-    }
357786
-}
357786
-
357786
-static void block_job_abort(BlockJob *job)
357786
-{
357786
-    assert(job->ret);
357786
-    if (job->driver->abort) {
357786
-        job->driver->abort(job);
357786
-    }
357786
-}
357786
-
357786
-static void block_job_clean(BlockJob *job)
357786
-{
357786
-    if (job->driver->clean) {
357786
-        job->driver->clean(job);
357786
+    if (job->job.ret == 0 && job->driver->prepare) {
357786
+        job->job.ret = job->driver->prepare(job);
357786
     }
357786
-}
357786
-
357786
-static int block_job_finalize_single(BlockJob *job)
357786
-{
357786
-    assert(job_is_completed(&job->job));
357786
-
357786
-    /* Ensure abort is called for late-transactional failures */
357786
-    block_job_update_rc(job);
357786
-
357786
-    if (!job->ret) {
357786
-        block_job_commit(job);
357786
-    } else {
357786
-        block_job_abort(job);
357786
-    }
357786
-    block_job_clean(job);
357786
-
357786
-    if (job->cb) {
357786
-        job->cb(job->opaque, job->ret);
357786
-    }
357786
-
357786
-    /* Emit events only if we actually started */
357786
-    if (job_started(&job->job)) {
357786
-        if (job_is_cancelled(&job->job)) {
357786
-            job_event_cancelled(&job->job);
357786
-        } else {
357786
-            job_event_completed(&job->job);
357786
-        }
357786
-    }
357786
-
357786
-    block_job_txn_del_job(job);
357786
-    block_job_conclude(job);
357786
-    return 0;
357786
+    return job->job.ret;
357786
 }
357786
 
357786
 static void block_job_cancel_async(BlockJob *job, bool force)
357786
@@ -424,8 +335,8 @@ static int block_job_finish_sync(BlockJob *job,
357786
     while (!job_is_completed(&job->job)) {
357786
         aio_poll(qemu_get_aio_context(), true);
357786
     }
357786
-    ret = (job_is_cancelled(&job->job) && job->ret == 0)
357786
-          ? -ECANCELED : job->ret;
357786
+    ret = (job_is_cancelled(&job->job) && job->job.ret == 0)
357786
+          ? -ECANCELED : job->job.ret;
357786
     job_unref(&job->job);
357786
     return ret;
357786
 }
357786
@@ -466,7 +377,7 @@ static void block_job_completed_txn_abort(BlockJob *job)
357786
             assert(job_is_cancelled(&other_job->job));
357786
             block_job_finish_sync(other_job, NULL, NULL);
357786
         }
357786
-        block_job_finalize_single(other_job);
357786
+        job_finalize_single(&other_job->job);
357786
         aio_context_release(ctx);
357786
     }
357786
 
357786
@@ -478,6 +389,11 @@ static int block_job_needs_finalize(BlockJob *job)
357786
     return !job->job.auto_finalize;
357786
 }
357786
 
357786
+static int block_job_finalize_single(BlockJob *job)
357786
+{
357786
+    return job_finalize_single(&job->job);
357786
+}
357786
+
357786
 static void block_job_do_finalize(BlockJob *job)
357786
 {
357786
     int rc;
357786
@@ -516,7 +432,7 @@ static void block_job_completed_txn_success(BlockJob *job)
357786
         if (!job_is_completed(&other_job->job)) {
357786
             return;
357786
         }
357786
-        assert(other_job->ret == 0);
357786
+        assert(other_job->job.ret == 0);
357786
     }
357786
 
357786
     block_job_txn_apply(txn, block_job_transition_to_pending, false);
357786
@@ -601,14 +517,14 @@ void block_job_dismiss(BlockJob **jobptr, Error **errp)
357786
         return;
357786
     }
357786
 
357786
-    block_job_do_dismiss(job);
357786
+    job_do_dismiss(&job->job);
357786
     *jobptr = NULL;
357786
 }
357786
 
357786
 void block_job_cancel(BlockJob *job, bool force)
357786
 {
357786
     if (job->job.status == JOB_STATUS_CONCLUDED) {
357786
-        block_job_do_dismiss(job);
357786
+        job_do_dismiss(&job->job);
357786
         return;
357786
     }
357786
     block_job_cancel_async(job, force);
357786
@@ -691,8 +607,8 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
357786
     info->status    = job->job.status;
357786
     info->auto_finalize = job->job.auto_finalize;
357786
     info->auto_dismiss  = job->job.auto_dismiss;
357786
-    info->has_error = job->ret != 0;
357786
-    info->error     = job->ret ? g_strdup(strerror(-job->ret)) : NULL;
357786
+    info->has_error = job->job.ret != 0;
357786
+    info->error     = job->job.ret ? g_strdup(strerror(-job->job.ret)) : NULL;
357786
     return info;
357786
 }
357786
 
357786
@@ -729,8 +645,8 @@ static void block_job_event_completed(Notifier *n, void *opaque)
357786
         return;
357786
     }
357786
 
357786
-    if (job->ret < 0) {
357786
-        msg = strerror(-job->ret);
357786
+    if (job->job.ret < 0) {
357786
+        msg = strerror(-job->job.ret);
357786
     }
357786
 
357786
     qapi_event_send_block_job_completed(job_type(&job->job),
357786
@@ -787,7 +703,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
357786
     }
357786
 
357786
     job = job_create(job_id, &driver->job_driver, blk_get_aio_context(blk),
357786
-                     flags, errp);
357786
+                     flags, cb, opaque, errp);
357786
     if (job == NULL) {
357786
         blk_unref(blk);
357786
         return NULL;
357786
@@ -799,8 +715,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
357786
 
357786
     job->driver        = driver;
357786
     job->blk           = blk;
357786
-    job->cb            = cb;
357786
-    job->opaque        = opaque;
357786
 
357786
     job->finalize_cancelled_notifier.notify = block_job_event_cancelled;
357786
     job->finalize_completed_notifier.notify = block_job_event_completed;
357786
@@ -828,7 +742,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
357786
 
357786
         block_job_set_speed(job, speed, &local_err);
357786
         if (local_err) {
357786
-            block_job_early_fail(job);
357786
+            job_early_fail(&job->job);
357786
             error_propagate(errp, local_err);
357786
             return NULL;
357786
         }
357786
@@ -847,20 +761,14 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
357786
     return job;
357786
 }
357786
 
357786
-void block_job_early_fail(BlockJob *job)
357786
-{
357786
-    assert(job->job.status == JOB_STATUS_CREATED);
357786
-    block_job_decommission(job);
357786
-}
357786
-
357786
 void block_job_completed(BlockJob *job, int ret)
357786
 {
357786
     assert(job && job->txn && !job_is_completed(&job->job));
357786
     assert(blk_bs(job->blk)->job == job);
357786
-    job->ret = ret;
357786
-    block_job_update_rc(job);
357786
-    trace_block_job_completed(job, ret, job->ret);
357786
-    if (job->ret) {
357786
+    job->job.ret = ret;
357786
+    job_update_rc(&job->job);
357786
+    trace_block_job_completed(job, ret, job->job.ret);
357786
+    if (job->job.ret) {
357786
         block_job_completed_txn_abort(job);
357786
     } else {
357786
         block_job_completed_txn_success(job);
357786
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
357786
index aef0629..3f405d1 100644
357786
--- a/include/block/blockjob.h
357786
+++ b/include/block/blockjob.h
357786
@@ -76,9 +76,6 @@ typedef struct BlockJob {
357786
     /** Rate limiting data structure for implementing @speed. */
357786
     RateLimit limit;
357786
 
357786
-    /** The completion function that will be called when the job completes.  */
357786
-    BlockCompletionFunc *cb;
357786
-
357786
     /** Block other operations when block job is running */
357786
     Error *blocker;
357786
 
357786
@@ -94,12 +91,6 @@ typedef struct BlockJob {
357786
     /** BlockDriverStates that are involved in this block job */
357786
     GSList *nodes;
357786
 
357786
-    /** The opaque value that is passed to the completion function.  */
357786
-    void *opaque;
357786
-
357786
-    /** ret code passed to block_job_completed. */
357786
-    int ret;
357786
-
357786
     BlockJobTxn *txn;
357786
     QLIST_ENTRY(BlockJob) txn_list;
357786
 } BlockJob;
357786
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
357786
index 88639f7..bf2b762 100644
357786
--- a/include/block/blockjob_int.h
357786
+++ b/include/block/blockjob_int.h
357786
@@ -54,34 +54,6 @@ struct BlockJobDriver {
357786
      */
357786
     int (*prepare)(BlockJob *job);
357786
 
357786
-    /**
357786
-     * If the callback is not NULL, it will be invoked when all the jobs
357786
-     * belonging to the same transaction complete; or upon this job's
357786
-     * completion if it is not in a transaction. Skipped if NULL.
357786
-     *
357786
-     * All jobs will complete with a call to either .commit() or .abort() but
357786
-     * never both.
357786
-     */
357786
-    void (*commit)(BlockJob *job);
357786
-
357786
-    /**
357786
-     * If the callback is not NULL, it will be invoked when any job in the
357786
-     * same transaction fails; or upon this job's failure (due to error or
357786
-     * cancellation) if it is not in a transaction. Skipped if NULL.
357786
-     *
357786
-     * All jobs will complete with a call to either .commit() or .abort() but
357786
-     * never both.
357786
-     */
357786
-    void (*abort)(BlockJob *job);
357786
-
357786
-    /**
357786
-     * If the callback is not NULL, it will be invoked after a call to either
357786
-     * .commit() or .abort(). Regardless of which callback is invoked after
357786
-     * completion, .clean() will always be called, even if the job does not
357786
-     * belong to a transaction group.
357786
-     */
357786
-    void (*clean)(BlockJob *job);
357786
-
357786
     /*
357786
      * If the callback is not NULL, it will be invoked before the job is
357786
      * resumed in a new AioContext.  This is the place to move any resources
357786
@@ -156,14 +128,6 @@ void block_job_yield(BlockJob *job);
357786
 int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n);
357786
 
357786
 /**
357786
- * block_job_early_fail:
357786
- * @bs: The block device.
357786
- *
357786
- * The block job could not be started, free it.
357786
- */
357786
-void block_job_early_fail(BlockJob *job);
357786
-
357786
-/**
357786
  * block_job_completed:
357786
  * @job: The job being completed.
357786
  * @ret: The status code.
357786
diff --git a/include/qemu/job.h b/include/qemu/job.h
357786
index 14d9377..3e817be 100644
357786
--- a/include/qemu/job.h
357786
+++ b/include/qemu/job.h
357786
@@ -29,6 +29,7 @@
357786
 #include "qapi/qapi-types-block-core.h"
357786
 #include "qemu/queue.h"
357786
 #include "qemu/coroutine.h"
357786
+#include "block/aio.h"
357786
 
357786
 typedef struct JobDriver JobDriver;
357786
 
357786
@@ -105,6 +106,15 @@ typedef struct Job {
357786
     /** True if this job should automatically dismiss itself */
357786
     bool auto_dismiss;
357786
 
357786
+    /** ret code passed to block_job_completed. */
357786
+    int ret;
357786
+
357786
+    /** The completion function that will be called when the job completes.  */
357786
+    BlockCompletionFunc *cb;
357786
+
357786
+    /** The opaque value that is passed to the completion function.  */
357786
+    void *opaque;
357786
+
357786
     /** Notifiers called when a cancelled job is finalised */
357786
     NotifierList on_finalize_cancelled;
357786
 
357786
@@ -151,6 +161,35 @@ struct JobDriver {
357786
      */
357786
     void (*user_resume)(Job *job);
357786
 
357786
+    /**
357786
+     * If the callback is not NULL, it will be invoked when all the jobs
357786
+     * belonging to the same transaction complete; or upon this job's
357786
+     * completion if it is not in a transaction. Skipped if NULL.
357786
+     *
357786
+     * All jobs will complete with a call to either .commit() or .abort() but
357786
+     * never both.
357786
+     */
357786
+    void (*commit)(Job *job);
357786
+
357786
+    /**
357786
+     * If the callback is not NULL, it will be invoked when any job in the
357786
+     * same transaction fails; or upon this job's failure (due to error or
357786
+     * cancellation) if it is not in a transaction. Skipped if NULL.
357786
+     *
357786
+     * All jobs will complete with a call to either .commit() or .abort() but
357786
+     * never both.
357786
+     */
357786
+    void (*abort)(Job *job);
357786
+
357786
+    /**
357786
+     * If the callback is not NULL, it will be invoked after a call to either
357786
+     * .commit() or .abort(). Regardless of which callback is invoked after
357786
+     * completion, .clean() will always be called, even if the job does not
357786
+     * belong to a transaction group.
357786
+     */
357786
+    void (*clean)(Job *job);
357786
+
357786
+
357786
     /** Called when the job is freed */
357786
     void (*free)(Job *job);
357786
 };
357786
@@ -174,10 +213,12 @@ typedef enum JobCreateFlags {
357786
  * @driver: The class object for the newly-created job.
357786
  * @ctx: The AioContext to run the job coroutine in.
357786
  * @flags: Creation flags for the job. See @JobCreateFlags.
357786
+ * @cb: Completion function for the job.
357786
+ * @opaque: Opaque pointer value passed to @cb.
357786
  * @errp: Error object.
357786
  */
357786
 void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
357786
-                 int flags, Error **errp);
357786
+                 int flags, BlockCompletionFunc *cb, void *opaque, Error **errp);
357786
 
357786
 /**
357786
  * Add a reference to Job refcnt, it will be decreased with job_unref, and then
357786
@@ -300,6 +341,10 @@ Job *job_get(const char *id);
357786
  */
357786
 int job_apply_verb(Job *job, JobVerb verb, Error **errp);
357786
 
357786
+/** The @job could not be started, free it. */
357786
+void job_early_fail(Job *job);
357786
+
357786
+
357786
 typedef void JobDeferToMainLoopFn(Job *job, void *opaque);
357786
 
357786
 /**
357786
@@ -322,5 +367,11 @@ void job_state_transition(Job *job, JobStatus s1);
357786
 void coroutine_fn job_do_yield(Job *job, uint64_t ns);
357786
 bool job_should_pause(Job *job);
357786
 bool job_started(Job *job);
357786
+void job_do_dismiss(Job *job);
357786
+int job_finalize_single(Job *job);
357786
+void job_update_rc(Job *job);
357786
+
357786
+typedef struct BlockJob BlockJob;
357786
+void block_job_txn_del_job(BlockJob *job);
357786
 
357786
 #endif
357786
diff --git a/job.c b/job.c
357786
index 817c3b4..64b64da 100644
357786
--- a/job.c
357786
+++ b/job.c
357786
@@ -85,7 +85,7 @@ void job_state_transition(Job *job, JobStatus s1)
357786
 {
357786
     JobStatus s0 = job->status;
357786
     assert(s1 >= 0 && s1 <= JOB_STATUS__MAX);
357786
-    trace_job_state_transition(job, /* TODO re-enable: job->ret */ 0,
357786
+    trace_job_state_transition(job, job->ret,
357786
                                JobSTT[s0][s1] ? "allowed" : "disallowed",
357786
                                JobStatus_str(s0), JobStatus_str(s1));
357786
     assert(JobSTT[s0][s1]);
357786
@@ -182,7 +182,7 @@ static void job_sleep_timer_cb(void *opaque)
357786
 }
357786
 
357786
 void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
357786
-                 int flags, Error **errp)
357786
+                 int flags, BlockCompletionFunc *cb, void *opaque, Error **errp)
357786
 {
357786
     Job *job;
357786
 
357786
@@ -214,6 +214,8 @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
357786
     job->pause_count   = 1;
357786
     job->auto_finalize = !(flags & JOB_MANUAL_FINALIZE);
357786
     job->auto_dismiss  = !(flags & JOB_MANUAL_DISMISS);
357786
+    job->cb            = cb;
357786
+    job->opaque        = opaque;
357786
 
357786
     notifier_list_init(&job->on_finalize_cancelled);
357786
     notifier_list_init(&job->on_finalize_completed);
357786
@@ -449,6 +451,100 @@ void job_user_resume(Job *job, Error **errp)
357786
     job_resume(job);
357786
 }
357786
 
357786
+void job_do_dismiss(Job *job)
357786
+{
357786
+    assert(job);
357786
+    job->busy = false;
357786
+    job->paused = false;
357786
+    job->deferred_to_main_loop = true;
357786
+
357786
+    /* TODO Don't assume it's a BlockJob */
357786
+    block_job_txn_del_job((BlockJob*) job);
357786
+
357786
+    job_state_transition(job, JOB_STATUS_NULL);
357786
+    job_unref(job);
357786
+}
357786
+
357786
+void job_early_fail(Job *job)
357786
+{
357786
+    assert(job->status == JOB_STATUS_CREATED);
357786
+    job_do_dismiss(job);
357786
+}
357786
+
357786
+static void job_conclude(Job *job)
357786
+{
357786
+    job_state_transition(job, JOB_STATUS_CONCLUDED);
357786
+    if (job->auto_dismiss || !job_started(job)) {
357786
+        job_do_dismiss(job);
357786
+    }
357786
+}
357786
+
357786
+void job_update_rc(Job *job)
357786
+{
357786
+    if (!job->ret && job_is_cancelled(job)) {
357786
+        job->ret = -ECANCELED;
357786
+    }
357786
+    if (job->ret) {
357786
+        job_state_transition(job, JOB_STATUS_ABORTING);
357786
+    }
357786
+}
357786
+
357786
+static void job_commit(Job *job)
357786
+{
357786
+    assert(!job->ret);
357786
+    if (job->driver->commit) {
357786
+        job->driver->commit(job);
357786
+    }
357786
+}
357786
+
357786
+static void job_abort(Job *job)
357786
+{
357786
+    assert(job->ret);
357786
+    if (job->driver->abort) {
357786
+        job->driver->abort(job);
357786
+    }
357786
+}
357786
+
357786
+static void job_clean(Job *job)
357786
+{
357786
+    if (job->driver->clean) {
357786
+        job->driver->clean(job);
357786
+    }
357786
+}
357786
+
357786
+int job_finalize_single(Job *job)
357786
+{
357786
+    assert(job_is_completed(job));
357786
+
357786
+    /* Ensure abort is called for late-transactional failures */
357786
+    job_update_rc(job);
357786
+
357786
+    if (!job->ret) {
357786
+        job_commit(job);
357786
+    } else {
357786
+        job_abort(job);
357786
+    }
357786
+    job_clean(job);
357786
+
357786
+    if (job->cb) {
357786
+        job->cb(job->opaque, job->ret);
357786
+    }
357786
+
357786
+    /* Emit events only if we actually started */
357786
+    if (job_started(job)) {
357786
+        if (job_is_cancelled(job)) {
357786
+            job_event_cancelled(job);
357786
+        } else {
357786
+            job_event_completed(job);
357786
+        }
357786
+    }
357786
+
357786
+    /* TODO Don't assume it's a BlockJob */
357786
+    block_job_txn_del_job((BlockJob*) job);
357786
+    job_conclude(job);
357786
+    return 0;
357786
+}
357786
+
357786
 
357786
 typedef struct {
357786
     Job *job;
357786
diff --git a/qemu-img.c b/qemu-img.c
357786
index 843dc6a..91b3151 100644
357786
--- a/qemu-img.c
357786
+++ b/qemu-img.c
357786
@@ -883,7 +883,7 @@ static void run_block_job(BlockJob *job, Error **errp)
357786
     if (!job_is_completed(&job->job)) {
357786
         ret = block_job_complete_sync(job, errp);
357786
     } else {
357786
-        ret = job->ret;
357786
+        ret = job->job.ret;
357786
     }
357786
     job_unref(&job->job);
357786
     aio_context_release(aio_context);
357786
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
357786
index 8bb0aa8..1fe6803 100644
357786
--- a/tests/test-blockjob.c
357786
+++ b/tests/test-blockjob.c
357786
@@ -128,11 +128,11 @@ static void test_job_ids(void)
357786
     job[1] = do_test_id(blk[1], "id0", false);
357786
 
357786
     /* But once job[0] finishes we can reuse its ID */
357786
-    block_job_early_fail(job[0]);
357786
+    job_early_fail(&job[0]->job);
357786
     job[1] = do_test_id(blk[1], "id0", true);
357786
 
357786
     /* No job ID specified, defaults to the backend name ('drive1') */
357786
-    block_job_early_fail(job[1]);
357786
+    job_early_fail(&job[1]->job);
357786
     job[1] = do_test_id(blk[1], NULL, true);
357786
 
357786
     /* Duplicate job ID */
357786
@@ -145,9 +145,9 @@ static void test_job_ids(void)
357786
     /* This one is valid */
357786
     job[2] = do_test_id(blk[2], "id_2", true);
357786
 
357786
-    block_job_early_fail(job[0]);
357786
-    block_job_early_fail(job[1]);
357786
-    block_job_early_fail(job[2]);
357786
+    job_early_fail(&job[0]->job);
357786
+    job_early_fail(&job[1]->job);
357786
+    job_early_fail(&job[2]->job);
357786
 
357786
     destroy_blk(blk[0]);
357786
     destroy_blk(blk[1]);
357786
-- 
357786
1.8.3.1
357786