]> git.proxmox.com Git - mirror_qemu.git/blobdiff - job.c
Merge remote-tracking branch 'remotes/juanquintela/tags/check/20180822' into staging
[mirror_qemu.git] / job.c
diff --git a/job.c b/job.c
index 7cd36027823ef728dec3c11498626b77bc067867..e36ebaafd81c2cb2cc5a7ed7e46a20704f96fe6e 100644 (file)
--- a/job.c
+++ b/job.c
@@ -30,6 +30,7 @@
 #include "qemu/id.h"
 #include "qemu/main-loop.h"
 #include "trace-root.h"
+#include "qapi/qapi-events-job.h"
 
 static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs);
 
@@ -157,9 +158,12 @@ static int job_txn_apply(JobTxn *txn, int fn(Job *), bool lock)
     return rc;
 }
 
+bool job_is_internal(Job *job)
+{
+    return (job->id == NULL);
+}
 
-/* TODO Make static once the whole state machine is in job.c */
-void job_state_transition(Job *job, JobStatus s1)
+static void job_state_transition(Job *job, JobStatus s1)
 {
     JobStatus s0 = job->status;
     assert(s1 >= 0 && s1 <= JOB_STATUS__MAX);
@@ -168,6 +172,10 @@ void job_state_transition(Job *job, JobStatus s1)
                                JobStatus_str(s0), JobStatus_str(s1));
     assert(JobSTT[s0][s1]);
     job->status = s1;
+
+    if (!job_is_internal(job) && s1 != s0) {
+        qapi_event_send_job_status_change(job->id, job->status, &error_abort);
+    }
 }
 
 int job_apply_verb(Job *job, JobVerb verb, Error **errp)
@@ -199,6 +207,28 @@ bool job_is_cancelled(Job *job)
     return job->cancelled;
 }
 
+bool job_is_ready(Job *job)
+{
+    switch (job->status) {
+    case JOB_STATUS_UNDEFINED:
+    case JOB_STATUS_CREATED:
+    case JOB_STATUS_RUNNING:
+    case JOB_STATUS_PAUSED:
+    case JOB_STATUS_WAITING:
+    case JOB_STATUS_PENDING:
+    case JOB_STATUS_ABORTING:
+    case JOB_STATUS_CONCLUDED:
+    case JOB_STATUS_NULL:
+        return false;
+    case JOB_STATUS_READY:
+    case JOB_STATUS_STANDBY:
+        return true;
+    default:
+        g_assert_not_reached();
+    }
+    return false;
+}
+
 bool job_is_completed(Job *job)
 {
     switch (job->status) {
@@ -299,6 +329,7 @@ void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn,
     notifier_list_init(&job->on_finalize_cancelled);
     notifier_list_init(&job->on_finalize_completed);
     notifier_list_init(&job->on_pending);
+    notifier_list_init(&job->on_ready);
 
     job_state_transition(job, JOB_STATUS_CREATED);
     aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
@@ -338,11 +369,27 @@ void job_unref(Job *job)
 
         QLIST_REMOVE(job, job_list);
 
+        g_free(job->error);
         g_free(job->id);
         g_free(job);
     }
 }
 
+void job_progress_update(Job *job, uint64_t done)
+{
+    job->progress_current += done;
+}
+
+void job_progress_set_remaining(Job *job, uint64_t remaining)
+{
+    job->progress_total = job->progress_current + remaining;
+}
+
+void job_progress_increase_remaining(Job *job, uint64_t delta)
+{
+    job->progress_total += delta;
+}
+
 void job_event_cancelled(Job *job)
 {
     notifier_list_notify(&job->on_finalize_cancelled, job);
@@ -358,6 +405,11 @@ static void job_event_pending(Job *job)
     notifier_list_notify(&job->on_pending, job);
 }
 
+static void job_event_ready(Job *job)
+{
+    notifier_list_notify(&job->on_ready, job);
+}
+
 void job_enter_cond(Job *job, bool(*fn)(Job *job))
 {
     if (!job_started(job)) {
@@ -614,6 +666,9 @@ static void job_update_rc(Job *job)
         job->ret = -ECANCELED;
     }
     if (job->ret) {
+        if (!job->error) {
+            job->error = g_strdup(strerror(-job->ret));
+        }
         job_state_transition(job, JOB_STATUS_ABORTING);
     }
 }
@@ -677,10 +732,10 @@ static void job_cancel_async(Job *job, bool force)
 {
     if (job->user_paused) {
         /* Do not call job_enter here, the caller will handle it.  */
-        job->user_paused = false;
         if (job->driver->user_resume) {
             job->driver->user_resume(job);
         }
+        job->user_paused = false;
         assert(job->pause_count > 0);
         job->pause_count--;
     }
@@ -736,6 +791,7 @@ static int job_prepare(Job *job)
 {
     if (job->ret == 0 && job->driver->prepare) {
         job->ret = job->driver->prepare(job);
+        job_update_rc(job);
     }
     return job->ret;
 }
@@ -777,6 +833,12 @@ static int job_transition_to_pending(Job *job)
     return 0;
 }
 
+void job_transition_to_ready(Job *job)
+{
+    job_state_transition(job, JOB_STATUS_READY);
+    job_event_ready(job);
+}
+
 static void job_completed_txn_success(Job *job)
 {
     JobTxn *txn = job->txn;
@@ -803,10 +865,17 @@ static void job_completed_txn_success(Job *job)
     }
 }
 
-void job_completed(Job *job, int ret)
+void job_completed(Job *job, int ret, Error *error)
 {
     assert(job && job->txn && !job_is_completed(job));
+
     job->ret = ret;
+    if (error) {
+        assert(job->ret < 0);
+        job->error = g_strdup(error_get_pretty(error));
+        error_free(error);
+    }
+
     job_update_rc(job);
     trace_job_completed(job, ret, job->ret);
     if (job->ret) {
@@ -824,7 +893,7 @@ void job_cancel(Job *job, bool force)
     }
     job_cancel_async(job, force);
     if (!job_started(job)) {
-        job_completed(job, -ECANCELED);
+        job_completed(job, -ECANCELED, NULL);
     } else if (job->deferred_to_main_loop) {
         job_completed_txn_abort(job);
     } else {