xref: /qemu/job.c (revision 9bd4d3c2)
133e9e9bdSKevin Wolf /*
233e9e9bdSKevin Wolf  * Background jobs (long-running operations)
333e9e9bdSKevin Wolf  *
433e9e9bdSKevin Wolf  * Copyright (c) 2011 IBM Corp.
533e9e9bdSKevin Wolf  * Copyright (c) 2012, 2018 Red Hat, Inc.
633e9e9bdSKevin Wolf  *
733e9e9bdSKevin Wolf  * Permission is hereby granted, free of charge, to any person obtaining a copy
833e9e9bdSKevin Wolf  * of this software and associated documentation files (the "Software"), to deal
933e9e9bdSKevin Wolf  * in the Software without restriction, including without limitation the rights
1033e9e9bdSKevin Wolf  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1133e9e9bdSKevin Wolf  * copies of the Software, and to permit persons to whom the Software is
1233e9e9bdSKevin Wolf  * furnished to do so, subject to the following conditions:
1333e9e9bdSKevin Wolf  *
1433e9e9bdSKevin Wolf  * The above copyright notice and this permission notice shall be included in
1533e9e9bdSKevin Wolf  * all copies or substantial portions of the Software.
1633e9e9bdSKevin Wolf  *
1733e9e9bdSKevin Wolf  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1833e9e9bdSKevin Wolf  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1933e9e9bdSKevin Wolf  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
2033e9e9bdSKevin Wolf  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2133e9e9bdSKevin Wolf  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2233e9e9bdSKevin Wolf  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2333e9e9bdSKevin Wolf  * THE SOFTWARE.
2433e9e9bdSKevin Wolf  */
2533e9e9bdSKevin Wolf 
2633e9e9bdSKevin Wolf #include "qemu/osdep.h"
2733e9e9bdSKevin Wolf #include "qapi/error.h"
2833e9e9bdSKevin Wolf #include "qemu/job.h"
2933e9e9bdSKevin Wolf #include "qemu/id.h"
301908a559SKevin Wolf #include "qemu/main-loop.h"
31de0fbe64SKevin Wolf #include "block/aio-wait.h"
32243af022SPaolo Bonzini #include "trace/trace-root.h"
331dac83f1SKevin Wolf #include "qapi/qapi-events-job.h"
3433e9e9bdSKevin Wolf 
3555c5a25aSEmanuele Giuseppe Esposito /*
36bf61c583SEmanuele Giuseppe Esposito  * The job API is composed of two categories of functions.
37bf61c583SEmanuele Giuseppe Esposito  *
38bf61c583SEmanuele Giuseppe Esposito  * The first includes functions used by the monitor.  The monitor is
39bf61c583SEmanuele Giuseppe Esposito  * peculiar in that it accesses the job list with job_get, and
40bf61c583SEmanuele Giuseppe Esposito  * therefore needs consistency across job_get and the actual operation
41bf61c583SEmanuele Giuseppe Esposito  * (e.g. job_user_cancel). To achieve this consistency, the caller
42bf61c583SEmanuele Giuseppe Esposito  * calls job_lock/job_unlock itself around the whole operation.
43bf61c583SEmanuele Giuseppe Esposito  *
44bf61c583SEmanuele Giuseppe Esposito  *
45bf61c583SEmanuele Giuseppe Esposito  * The second includes functions used by the job drivers and sometimes
46bf61c583SEmanuele Giuseppe Esposito  * by the core block layer. These delegate the locking to the callee instead.
47bf61c583SEmanuele Giuseppe Esposito  */
48bf61c583SEmanuele Giuseppe Esposito 
49bf61c583SEmanuele Giuseppe Esposito /*
5055c5a25aSEmanuele Giuseppe Esposito  * job_mutex protects the jobs list, but also makes the
5155c5a25aSEmanuele Giuseppe Esposito  * struct job fields thread-safe.
5255c5a25aSEmanuele Giuseppe Esposito  */
5355c5a25aSEmanuele Giuseppe Esposito QemuMutex job_mutex;
5455c5a25aSEmanuele Giuseppe Esposito 
55afe1e8a7SEmanuele Giuseppe Esposito /* Protected by job_mutex */
56e7c1d78bSKevin Wolf static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs);
57e7c1d78bSKevin Wolf 
58a50c2ab8SKevin Wolf /* Job State Transition Table */
59a50c2ab8SKevin Wolf bool JobSTT[JOB_STATUS__MAX][JOB_STATUS__MAX] = {
60a50c2ab8SKevin Wolf                                     /* U, C, R, P, Y, S, W, D, X, E, N */
61a50c2ab8SKevin Wolf     /* U: */ [JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
62a50c2ab8SKevin Wolf     /* C: */ [JOB_STATUS_CREATED]   = {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1},
63a50c2ab8SKevin Wolf     /* R: */ [JOB_STATUS_RUNNING]   = {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0},
64a50c2ab8SKevin Wolf     /* P: */ [JOB_STATUS_PAUSED]    = {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
65a50c2ab8SKevin Wolf     /* Y: */ [JOB_STATUS_READY]     = {0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0},
66a50c2ab8SKevin Wolf     /* S: */ [JOB_STATUS_STANDBY]   = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
67a50c2ab8SKevin Wolf     /* W: */ [JOB_STATUS_WAITING]   = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0},
68a50c2ab8SKevin Wolf     /* D: */ [JOB_STATUS_PENDING]   = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
69a50c2ab8SKevin Wolf     /* X: */ [JOB_STATUS_ABORTING]  = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
70a50c2ab8SKevin Wolf     /* E: */ [JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
71a50c2ab8SKevin Wolf     /* N: */ [JOB_STATUS_NULL]      = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
72a50c2ab8SKevin Wolf };
73a50c2ab8SKevin Wolf 
74a50c2ab8SKevin Wolf bool JobVerbTable[JOB_VERB__MAX][JOB_STATUS__MAX] = {
75a50c2ab8SKevin Wolf                                     /* U, C, R, P, Y, S, W, D, X, E, N */
76a50c2ab8SKevin Wolf     [JOB_VERB_CANCEL]               = {0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
77a50c2ab8SKevin Wolf     [JOB_VERB_PAUSE]                = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
78a50c2ab8SKevin Wolf     [JOB_VERB_RESUME]               = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
79a50c2ab8SKevin Wolf     [JOB_VERB_SET_SPEED]            = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
8053ddb9c8SMax Reitz     [JOB_VERB_COMPLETE]             = {0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0},
81a50c2ab8SKevin Wolf     [JOB_VERB_FINALIZE]             = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
82a50c2ab8SKevin Wolf     [JOB_VERB_DISMISS]              = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
83a50c2ab8SKevin Wolf };
84a50c2ab8SKevin Wolf 
857eaa8fb5SKevin Wolf /* Transactional group of jobs */
867eaa8fb5SKevin Wolf struct JobTxn {
877eaa8fb5SKevin Wolf 
887eaa8fb5SKevin Wolf     /* Is this txn being cancelled? */
897eaa8fb5SKevin Wolf     bool aborting;
907eaa8fb5SKevin Wolf 
917eaa8fb5SKevin Wolf     /* List of jobs */
927eaa8fb5SKevin Wolf     QLIST_HEAD(, Job) jobs;
937eaa8fb5SKevin Wolf 
947eaa8fb5SKevin Wolf     /* Reference count */
957eaa8fb5SKevin Wolf     int refcnt;
967eaa8fb5SKevin Wolf };
977eaa8fb5SKevin Wolf 
9855c5a25aSEmanuele Giuseppe Esposito void job_lock(void)
9955c5a25aSEmanuele Giuseppe Esposito {
100da01ff7fSKevin Wolf     qemu_mutex_lock(&job_mutex);
101da01ff7fSKevin Wolf }
102da01ff7fSKevin Wolf 
1036f592e5aSEmanuele Giuseppe Esposito void job_unlock(void)
104da01ff7fSKevin Wolf {
105da01ff7fSKevin Wolf     qemu_mutex_unlock(&job_mutex);
106da01ff7fSKevin Wolf }
107da01ff7fSKevin Wolf 
108da01ff7fSKevin Wolf static void __attribute__((__constructor__)) job_init(void)
109da01ff7fSKevin Wolf {
110da01ff7fSKevin Wolf     qemu_mutex_init(&job_mutex);
111da01ff7fSKevin Wolf }
112da01ff7fSKevin Wolf 
1137eaa8fb5SKevin Wolf JobTxn *job_txn_new(void)
1147eaa8fb5SKevin Wolf {
1157eaa8fb5SKevin Wolf     JobTxn *txn = g_new0(JobTxn, 1);
1167eaa8fb5SKevin Wolf     QLIST_INIT(&txn->jobs);
1177eaa8fb5SKevin Wolf     txn->refcnt = 1;
1187eaa8fb5SKevin Wolf     return txn;
1197eaa8fb5SKevin Wolf }
1207eaa8fb5SKevin Wolf 
121afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */
122afe1e8a7SEmanuele Giuseppe Esposito static void job_txn_ref_locked(JobTxn *txn)
1237eaa8fb5SKevin Wolf {
1247eaa8fb5SKevin Wolf     txn->refcnt++;
1257eaa8fb5SKevin Wolf }
1267eaa8fb5SKevin Wolf 
127afe1e8a7SEmanuele Giuseppe Esposito void job_txn_unref_locked(JobTxn *txn)
1287eaa8fb5SKevin Wolf {
1297eaa8fb5SKevin Wolf     if (txn && --txn->refcnt == 0) {
1307eaa8fb5SKevin Wolf         g_free(txn);
1317eaa8fb5SKevin Wolf     }
1327eaa8fb5SKevin Wolf }
1337eaa8fb5SKevin Wolf 
134afe1e8a7SEmanuele Giuseppe Esposito void job_txn_unref(JobTxn *txn)
135afe1e8a7SEmanuele Giuseppe Esposito {
136afe1e8a7SEmanuele Giuseppe Esposito     JOB_LOCK_GUARD();
137afe1e8a7SEmanuele Giuseppe Esposito     job_txn_unref_locked(txn);
138afe1e8a7SEmanuele Giuseppe Esposito }
139afe1e8a7SEmanuele Giuseppe Esposito 
140544f4d52SEmanuele Giuseppe Esposito /**
141544f4d52SEmanuele Giuseppe Esposito  * @txn: The transaction (may be NULL)
142544f4d52SEmanuele Giuseppe Esposito  * @job: Job to add to the transaction
143544f4d52SEmanuele Giuseppe Esposito  *
144544f4d52SEmanuele Giuseppe Esposito  * Add @job to the transaction.  The @job must not already be in a transaction.
145544f4d52SEmanuele Giuseppe Esposito  * The caller must call either job_txn_unref() or job_completed() to release
146544f4d52SEmanuele Giuseppe Esposito  * the reference that is automatically grabbed here.
147544f4d52SEmanuele Giuseppe Esposito  *
148544f4d52SEmanuele Giuseppe Esposito  * If @txn is NULL, the function does nothing.
149afe1e8a7SEmanuele Giuseppe Esposito  *
150afe1e8a7SEmanuele Giuseppe Esposito  * Called with job_mutex held.
151544f4d52SEmanuele Giuseppe Esposito  */
152afe1e8a7SEmanuele Giuseppe Esposito static void job_txn_add_job_locked(JobTxn *txn, Job *job)
1537eaa8fb5SKevin Wolf {
1547eaa8fb5SKevin Wolf     if (!txn) {
1557eaa8fb5SKevin Wolf         return;
1567eaa8fb5SKevin Wolf     }
1577eaa8fb5SKevin Wolf 
1587eaa8fb5SKevin Wolf     assert(!job->txn);
1597eaa8fb5SKevin Wolf     job->txn = txn;
1607eaa8fb5SKevin Wolf 
1617eaa8fb5SKevin Wolf     QLIST_INSERT_HEAD(&txn->jobs, job, txn_list);
162afe1e8a7SEmanuele Giuseppe Esposito     job_txn_ref_locked(txn);
1637eaa8fb5SKevin Wolf }
1647eaa8fb5SKevin Wolf 
165afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */
166afe1e8a7SEmanuele Giuseppe Esposito static void job_txn_del_job_locked(Job *job)
1677eaa8fb5SKevin Wolf {
1687eaa8fb5SKevin Wolf     if (job->txn) {
1697eaa8fb5SKevin Wolf         QLIST_REMOVE(job, txn_list);
170afe1e8a7SEmanuele Giuseppe Esposito         job_txn_unref_locked(job->txn);
1717eaa8fb5SKevin Wolf         job->txn = NULL;
1727eaa8fb5SKevin Wolf     }
1737eaa8fb5SKevin Wolf }
1747eaa8fb5SKevin Wolf 
175afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily. */
176afe1e8a7SEmanuele Giuseppe Esposito static int job_txn_apply_locked(Job *job, int fn(Job *))
1777eaa8fb5SKevin Wolf {
178b660a84bSStefan Reiter     Job *other_job, *next;
179b660a84bSStefan Reiter     JobTxn *txn = job->txn;
1807eaa8fb5SKevin Wolf     int rc = 0;
1817eaa8fb5SKevin Wolf 
182b660a84bSStefan Reiter     /*
183b660a84bSStefan Reiter      * Similar to job_completed_txn_abort, we take each job's lock before
184b660a84bSStefan Reiter      * applying fn, but since we assume that outer_ctx is held by the caller,
185b660a84bSStefan Reiter      * we need to release it here to avoid holding the lock twice - which would
186b660a84bSStefan Reiter      * break AIO_WAIT_WHILE from within fn.
187b660a84bSStefan Reiter      */
188afe1e8a7SEmanuele Giuseppe Esposito     job_ref_locked(job);
189b660a84bSStefan Reiter 
190b660a84bSStefan Reiter     QLIST_FOREACH_SAFE(other_job, &txn->jobs, txn_list, next) {
191b660a84bSStefan Reiter         rc = fn(other_job);
1927eaa8fb5SKevin Wolf         if (rc) {
1937eaa8fb5SKevin Wolf             break;
1947eaa8fb5SKevin Wolf         }
1957eaa8fb5SKevin Wolf     }
196b660a84bSStefan Reiter 
197afe1e8a7SEmanuele Giuseppe Esposito     job_unref_locked(job);
1987eaa8fb5SKevin Wolf     return rc;
1997eaa8fb5SKevin Wolf }
2007eaa8fb5SKevin Wolf 
201456273b0SKevin Wolf bool job_is_internal(Job *job)
2021dac83f1SKevin Wolf {
2031dac83f1SKevin Wolf     return (job->id == NULL);
2041dac83f1SKevin Wolf }
2051dac83f1SKevin Wolf 
206afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */
207afe1e8a7SEmanuele Giuseppe Esposito static void job_state_transition_locked(Job *job, JobStatus s1)
208a50c2ab8SKevin Wolf {
209a50c2ab8SKevin Wolf     JobStatus s0 = job->status;
210c2032289SLiam Merwick     assert(s1 >= 0 && s1 < JOB_STATUS__MAX);
2114ad35181SKevin Wolf     trace_job_state_transition(job, job->ret,
212a50c2ab8SKevin Wolf                                JobSTT[s0][s1] ? "allowed" : "disallowed",
213a50c2ab8SKevin Wolf                                JobStatus_str(s0), JobStatus_str(s1));
214a50c2ab8SKevin Wolf     assert(JobSTT[s0][s1]);
215a50c2ab8SKevin Wolf     job->status = s1;
2161dac83f1SKevin Wolf 
2171dac83f1SKevin Wolf     if (!job_is_internal(job) && s1 != s0) {
2183ab72385SPeter Xu         qapi_event_send_job_status_change(job->id, job->status);
2191dac83f1SKevin Wolf     }
220a50c2ab8SKevin Wolf }
221a50c2ab8SKevin Wolf 
222afe1e8a7SEmanuele Giuseppe Esposito int job_apply_verb_locked(Job *job, JobVerb verb, Error **errp)
223a50c2ab8SKevin Wolf {
224a50c2ab8SKevin Wolf     JobStatus s0 = job->status;
225c2032289SLiam Merwick     assert(verb >= 0 && verb < JOB_VERB__MAX);
226a50c2ab8SKevin Wolf     trace_job_apply_verb(job, JobStatus_str(s0), JobVerb_str(verb),
227a50c2ab8SKevin Wolf                          JobVerbTable[verb][s0] ? "allowed" : "prohibited");
228a50c2ab8SKevin Wolf     if (JobVerbTable[verb][s0]) {
229a50c2ab8SKevin Wolf         return 0;
230a50c2ab8SKevin Wolf     }
231a50c2ab8SKevin Wolf     error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'",
232a50c2ab8SKevin Wolf                job->id, JobStatus_str(s0), JobVerb_str(verb));
233a50c2ab8SKevin Wolf     return -EPERM;
234a50c2ab8SKevin Wolf }
235a50c2ab8SKevin Wolf 
236252291eaSKevin Wolf JobType job_type(const Job *job)
237252291eaSKevin Wolf {
238252291eaSKevin Wolf     return job->driver->job_type;
239252291eaSKevin Wolf }
240252291eaSKevin Wolf 
241252291eaSKevin Wolf const char *job_type_str(const Job *job)
242252291eaSKevin Wolf {
243252291eaSKevin Wolf     return JobType_str(job_type(job));
244252291eaSKevin Wolf }
245252291eaSKevin Wolf 
246afe1e8a7SEmanuele Giuseppe Esposito bool job_is_cancelled_locked(Job *job)
247daa7f2f9SKevin Wolf {
248a640fa0eSHanna Reitz     /* force_cancel may be true only if cancelled is true, too */
249a640fa0eSHanna Reitz     assert(job->cancelled || !job->force_cancel);
250a640fa0eSHanna Reitz     return job->force_cancel;
25108b83bffSHanna Reitz }
25208b83bffSHanna Reitz 
253afe1e8a7SEmanuele Giuseppe Esposito bool job_is_cancelled(Job *job)
254afe1e8a7SEmanuele Giuseppe Esposito {
255afe1e8a7SEmanuele Giuseppe Esposito     JOB_LOCK_GUARD();
256afe1e8a7SEmanuele Giuseppe Esposito     return job_is_cancelled_locked(job);
257afe1e8a7SEmanuele Giuseppe Esposito }
258afe1e8a7SEmanuele Giuseppe Esposito 
259afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */
260afe1e8a7SEmanuele Giuseppe Esposito static bool job_cancel_requested_locked(Job *job)
26108b83bffSHanna Reitz {
262daa7f2f9SKevin Wolf     return job->cancelled;
263daa7f2f9SKevin Wolf }
264daa7f2f9SKevin Wolf 
265afe1e8a7SEmanuele Giuseppe Esposito bool job_cancel_requested(Job *job)
266afe1e8a7SEmanuele Giuseppe Esposito {
267afe1e8a7SEmanuele Giuseppe Esposito     JOB_LOCK_GUARD();
268afe1e8a7SEmanuele Giuseppe Esposito     return job_cancel_requested_locked(job);
269afe1e8a7SEmanuele Giuseppe Esposito }
270afe1e8a7SEmanuele Giuseppe Esposito 
271afe1e8a7SEmanuele Giuseppe Esposito bool job_is_ready_locked(Job *job)
272df956ae2SKevin Wolf {
273df956ae2SKevin Wolf     switch (job->status) {
274df956ae2SKevin Wolf     case JOB_STATUS_UNDEFINED:
275df956ae2SKevin Wolf     case JOB_STATUS_CREATED:
276df956ae2SKevin Wolf     case JOB_STATUS_RUNNING:
277df956ae2SKevin Wolf     case JOB_STATUS_PAUSED:
278df956ae2SKevin Wolf     case JOB_STATUS_WAITING:
279df956ae2SKevin Wolf     case JOB_STATUS_PENDING:
280df956ae2SKevin Wolf     case JOB_STATUS_ABORTING:
281df956ae2SKevin Wolf     case JOB_STATUS_CONCLUDED:
282df956ae2SKevin Wolf     case JOB_STATUS_NULL:
283df956ae2SKevin Wolf         return false;
284df956ae2SKevin Wolf     case JOB_STATUS_READY:
285df956ae2SKevin Wolf     case JOB_STATUS_STANDBY:
286df956ae2SKevin Wolf         return true;
287df956ae2SKevin Wolf     default:
288df956ae2SKevin Wolf         g_assert_not_reached();
289df956ae2SKevin Wolf     }
290df956ae2SKevin Wolf     return false;
291df956ae2SKevin Wolf }
292df956ae2SKevin Wolf 
293afe1e8a7SEmanuele Giuseppe Esposito bool job_is_ready(Job *job)
294afe1e8a7SEmanuele Giuseppe Esposito {
295afe1e8a7SEmanuele Giuseppe Esposito     JOB_LOCK_GUARD();
296afe1e8a7SEmanuele Giuseppe Esposito     return job_is_ready_locked(job);
297afe1e8a7SEmanuele Giuseppe Esposito }
298afe1e8a7SEmanuele Giuseppe Esposito 
299afe1e8a7SEmanuele Giuseppe Esposito bool job_is_completed_locked(Job *job)
300afe1e8a7SEmanuele Giuseppe Esposito {
301afe1e8a7SEmanuele Giuseppe Esposito     switch (job->status) {
302afe1e8a7SEmanuele Giuseppe Esposito     case JOB_STATUS_UNDEFINED:
303afe1e8a7SEmanuele Giuseppe Esposito     case JOB_STATUS_CREATED:
304afe1e8a7SEmanuele Giuseppe Esposito     case JOB_STATUS_RUNNING:
305afe1e8a7SEmanuele Giuseppe Esposito     case JOB_STATUS_PAUSED:
306afe1e8a7SEmanuele Giuseppe Esposito     case JOB_STATUS_READY:
307afe1e8a7SEmanuele Giuseppe Esposito     case JOB_STATUS_STANDBY:
308afe1e8a7SEmanuele Giuseppe Esposito         return false;
309afe1e8a7SEmanuele Giuseppe Esposito     case JOB_STATUS_WAITING:
310afe1e8a7SEmanuele Giuseppe Esposito     case JOB_STATUS_PENDING:
311afe1e8a7SEmanuele Giuseppe Esposito     case JOB_STATUS_ABORTING:
312afe1e8a7SEmanuele Giuseppe Esposito     case JOB_STATUS_CONCLUDED:
313afe1e8a7SEmanuele Giuseppe Esposito     case JOB_STATUS_NULL:
314afe1e8a7SEmanuele Giuseppe Esposito         return true;
315afe1e8a7SEmanuele Giuseppe Esposito     default:
316afe1e8a7SEmanuele Giuseppe Esposito         g_assert_not_reached();
317afe1e8a7SEmanuele Giuseppe Esposito     }
318afe1e8a7SEmanuele Giuseppe Esposito     return false;
319afe1e8a7SEmanuele Giuseppe Esposito }
320afe1e8a7SEmanuele Giuseppe Esposito 
3219bd4d3c2SEmanuele Giuseppe Esposito static bool job_is_completed(Job *job)
322dbe5e6c1SKevin Wolf {
323afe1e8a7SEmanuele Giuseppe Esposito     JOB_LOCK_GUARD();
324afe1e8a7SEmanuele Giuseppe Esposito     return job_is_completed_locked(job);
325dbe5e6c1SKevin Wolf }
326dbe5e6c1SKevin Wolf 
327afe1e8a7SEmanuele Giuseppe Esposito static bool job_started_locked(Job *job)
328da01ff7fSKevin Wolf {
329da01ff7fSKevin Wolf     return job->co;
330da01ff7fSKevin Wolf }
331da01ff7fSKevin Wolf 
332afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */
333afe1e8a7SEmanuele Giuseppe Esposito static bool job_should_pause_locked(Job *job)
334da01ff7fSKevin Wolf {
335da01ff7fSKevin Wolf     return job->pause_count > 0;
336da01ff7fSKevin Wolf }
337da01ff7fSKevin Wolf 
338afe1e8a7SEmanuele Giuseppe Esposito Job *job_next_locked(Job *job)
339e7c1d78bSKevin Wolf {
340e7c1d78bSKevin Wolf     if (!job) {
341e7c1d78bSKevin Wolf         return QLIST_FIRST(&jobs);
342e7c1d78bSKevin Wolf     }
343e7c1d78bSKevin Wolf     return QLIST_NEXT(job, job_list);
344e7c1d78bSKevin Wolf }
345e7c1d78bSKevin Wolf 
346afe1e8a7SEmanuele Giuseppe Esposito Job *job_next(Job *job)
347afe1e8a7SEmanuele Giuseppe Esposito {
348afe1e8a7SEmanuele Giuseppe Esposito     JOB_LOCK_GUARD();
349afe1e8a7SEmanuele Giuseppe Esposito     return job_next_locked(job);
350afe1e8a7SEmanuele Giuseppe Esposito }
351afe1e8a7SEmanuele Giuseppe Esposito 
352afe1e8a7SEmanuele Giuseppe Esposito Job *job_get_locked(const char *id)
353e7c1d78bSKevin Wolf {
354e7c1d78bSKevin Wolf     Job *job;
355e7c1d78bSKevin Wolf 
356e7c1d78bSKevin Wolf     QLIST_FOREACH(job, &jobs, job_list) {
357e7c1d78bSKevin Wolf         if (job->id && !strcmp(id, job->id)) {
358e7c1d78bSKevin Wolf             return job;
359e7c1d78bSKevin Wolf         }
360e7c1d78bSKevin Wolf     }
361e7c1d78bSKevin Wolf 
362e7c1d78bSKevin Wolf     return NULL;
363e7c1d78bSKevin Wolf }
364e7c1d78bSKevin Wolf 
3653ed4f708SEmanuele Giuseppe Esposito void job_set_aio_context(Job *job, AioContext *ctx)
3663ed4f708SEmanuele Giuseppe Esposito {
3673ed4f708SEmanuele Giuseppe Esposito     /* protect against read in job_finish_sync_locked and job_start */
3683ed4f708SEmanuele Giuseppe Esposito     GLOBAL_STATE_CODE();
3693ed4f708SEmanuele Giuseppe Esposito     /* protect against read in job_do_yield_locked */
3703ed4f708SEmanuele Giuseppe Esposito     JOB_LOCK_GUARD();
3713ed4f708SEmanuele Giuseppe Esposito     /* ensure the job is quiescent while the AioContext is changed */
3723ed4f708SEmanuele Giuseppe Esposito     assert(job->paused || job_is_completed_locked(job));
3733ed4f708SEmanuele Giuseppe Esposito     job->aio_context = ctx;
3743ed4f708SEmanuele Giuseppe Esposito }
3753ed4f708SEmanuele Giuseppe Esposito 
376afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex *not* held. */
3775d43e86eSKevin Wolf static void job_sleep_timer_cb(void *opaque)
3785d43e86eSKevin Wolf {
3795d43e86eSKevin Wolf     Job *job = opaque;
3805d43e86eSKevin Wolf 
3815d43e86eSKevin Wolf     job_enter(job);
3825d43e86eSKevin Wolf }
3835d43e86eSKevin Wolf 
3847eaa8fb5SKevin Wolf void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn,
3857eaa8fb5SKevin Wolf                  AioContext *ctx, int flags, BlockCompletionFunc *cb,
3867eaa8fb5SKevin Wolf                  void *opaque, Error **errp)
38733e9e9bdSKevin Wolf {
38833e9e9bdSKevin Wolf     Job *job;
38933e9e9bdSKevin Wolf 
390afe1e8a7SEmanuele Giuseppe Esposito     JOB_LOCK_GUARD();
391afe1e8a7SEmanuele Giuseppe Esposito 
39233e9e9bdSKevin Wolf     if (job_id) {
393bb02b65cSKevin Wolf         if (flags & JOB_INTERNAL) {
394bb02b65cSKevin Wolf             error_setg(errp, "Cannot specify job ID for internal job");
395bb02b65cSKevin Wolf             return NULL;
396bb02b65cSKevin Wolf         }
39733e9e9bdSKevin Wolf         if (!id_wellformed(job_id)) {
39833e9e9bdSKevin Wolf             error_setg(errp, "Invalid job ID '%s'", job_id);
39933e9e9bdSKevin Wolf             return NULL;
40033e9e9bdSKevin Wolf         }
401afe1e8a7SEmanuele Giuseppe Esposito         if (job_get_locked(job_id)) {
402e7c1d78bSKevin Wolf             error_setg(errp, "Job ID '%s' already in use", job_id);
403e7c1d78bSKevin Wolf             return NULL;
404e7c1d78bSKevin Wolf         }
405bb02b65cSKevin Wolf     } else if (!(flags & JOB_INTERNAL)) {
406bb02b65cSKevin Wolf         error_setg(errp, "An explicit job ID is required");
407bb02b65cSKevin Wolf         return NULL;
40833e9e9bdSKevin Wolf     }
40933e9e9bdSKevin Wolf 
41033e9e9bdSKevin Wolf     job = g_malloc0(driver->instance_size);
41133e9e9bdSKevin Wolf     job->driver        = driver;
41233e9e9bdSKevin Wolf     job->id            = g_strdup(job_id);
41380fa2c75SKevin Wolf     job->refcnt        = 1;
41408be6fe2SKevin Wolf     job->aio_context   = ctx;
415da01ff7fSKevin Wolf     job->busy          = false;
416da01ff7fSKevin Wolf     job->paused        = true;
417da01ff7fSKevin Wolf     job->pause_count   = 1;
418bb02b65cSKevin Wolf     job->auto_finalize = !(flags & JOB_MANUAL_FINALIZE);
419bb02b65cSKevin Wolf     job->auto_dismiss  = !(flags & JOB_MANUAL_DISMISS);
4204ad35181SKevin Wolf     job->cb            = cb;
4214ad35181SKevin Wolf     job->opaque        = opaque;
42233e9e9bdSKevin Wolf 
423a7b4f8fcSEmanuele Giuseppe Esposito     progress_init(&job->progress);
424a7b4f8fcSEmanuele Giuseppe Esposito 
425139a9f02SKevin Wolf     notifier_list_init(&job->on_finalize_cancelled);
426139a9f02SKevin Wolf     notifier_list_init(&job->on_finalize_completed);
427139a9f02SKevin Wolf     notifier_list_init(&job->on_pending);
4282e1795b5SKevin Wolf     notifier_list_init(&job->on_ready);
429252f4091SEmanuele Giuseppe Esposito     notifier_list_init(&job->on_idle);
430139a9f02SKevin Wolf 
431afe1e8a7SEmanuele Giuseppe Esposito     job_state_transition_locked(job, JOB_STATUS_CREATED);
4325d43e86eSKevin Wolf     aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
4335d43e86eSKevin Wolf                    QEMU_CLOCK_REALTIME, SCALE_NS,
4345d43e86eSKevin Wolf                    job_sleep_timer_cb, job);
435a50c2ab8SKevin Wolf 
436e7c1d78bSKevin Wolf     QLIST_INSERT_HEAD(&jobs, job, job_list);
437e7c1d78bSKevin Wolf 
4387eaa8fb5SKevin Wolf     /* Single jobs are modeled as single-job transactions for sake of
4397eaa8fb5SKevin Wolf      * consolidating the job management logic */
4407eaa8fb5SKevin Wolf     if (!txn) {
4417eaa8fb5SKevin Wolf         txn = job_txn_new();
442afe1e8a7SEmanuele Giuseppe Esposito         job_txn_add_job_locked(txn, job);
443afe1e8a7SEmanuele Giuseppe Esposito         job_txn_unref_locked(txn);
4447eaa8fb5SKevin Wolf     } else {
445afe1e8a7SEmanuele Giuseppe Esposito         job_txn_add_job_locked(txn, job);
4467eaa8fb5SKevin Wolf     }
4477eaa8fb5SKevin Wolf 
44833e9e9bdSKevin Wolf     return job;
44933e9e9bdSKevin Wolf }
450fd61a701SKevin Wolf 
451afe1e8a7SEmanuele Giuseppe Esposito void job_ref_locked(Job *job)
452fd61a701SKevin Wolf {
45380fa2c75SKevin Wolf     ++job->refcnt;
45480fa2c75SKevin Wolf }
45580fa2c75SKevin Wolf 
456afe1e8a7SEmanuele Giuseppe Esposito void job_unref_locked(Job *job)
45780fa2c75SKevin Wolf {
458c70b8031SEmanuele Giuseppe Esposito     GLOBAL_STATE_CODE();
459c70b8031SEmanuele Giuseppe Esposito 
46080fa2c75SKevin Wolf     if (--job->refcnt == 0) {
46180fa2c75SKevin Wolf         assert(job->status == JOB_STATUS_NULL);
4625d43e86eSKevin Wolf         assert(!timer_pending(&job->sleep_timer));
4637eaa8fb5SKevin Wolf         assert(!job->txn);
46480fa2c75SKevin Wolf 
46580fa2c75SKevin Wolf         if (job->driver->free) {
4666f592e5aSEmanuele Giuseppe Esposito             AioContext *aio_context = job->aio_context;
467afe1e8a7SEmanuele Giuseppe Esposito             job_unlock();
4686f592e5aSEmanuele Giuseppe Esposito             /* FIXME: aiocontext lock is required because cb calls blk_unref */
4696f592e5aSEmanuele Giuseppe Esposito             aio_context_acquire(aio_context);
47080fa2c75SKevin Wolf             job->driver->free(job);
4716f592e5aSEmanuele Giuseppe Esposito             aio_context_release(aio_context);
472afe1e8a7SEmanuele Giuseppe Esposito             job_lock();
47380fa2c75SKevin Wolf         }
47480fa2c75SKevin Wolf 
475e7c1d78bSKevin Wolf         QLIST_REMOVE(job, job_list);
476e7c1d78bSKevin Wolf 
477a7b4f8fcSEmanuele Giuseppe Esposito         progress_destroy(&job->progress);
4783d1f8b07SJohn Snow         error_free(job->err);
479fd61a701SKevin Wolf         g_free(job->id);
480fd61a701SKevin Wolf         g_free(job);
481fd61a701SKevin Wolf     }
48280fa2c75SKevin Wolf }
4831908a559SKevin Wolf 
48430a5c887SKevin Wolf void job_progress_update(Job *job, uint64_t done)
48530a5c887SKevin Wolf {
48601fe1ca9SVladimir Sementsov-Ogievskiy     progress_work_done(&job->progress, done);
48730a5c887SKevin Wolf }
48830a5c887SKevin Wolf 
48930a5c887SKevin Wolf void job_progress_set_remaining(Job *job, uint64_t remaining)
49030a5c887SKevin Wolf {
49101fe1ca9SVladimir Sementsov-Ogievskiy     progress_set_remaining(&job->progress, remaining);
49230a5c887SKevin Wolf }
49330a5c887SKevin Wolf 
49462f13600SMax Reitz void job_progress_increase_remaining(Job *job, uint64_t delta)
49562f13600SMax Reitz {
49601fe1ca9SVladimir Sementsov-Ogievskiy     progress_increase_remaining(&job->progress, delta);
49762f13600SMax Reitz }
49862f13600SMax Reitz 
499544f4d52SEmanuele Giuseppe Esposito /**
500544f4d52SEmanuele Giuseppe Esposito  * To be called when a cancelled job is finalised.
501afe1e8a7SEmanuele Giuseppe Esposito  * Called with job_mutex held.
502544f4d52SEmanuele Giuseppe Esposito  */
503afe1e8a7SEmanuele Giuseppe Esposito static void job_event_cancelled_locked(Job *job)
504139a9f02SKevin Wolf {
505139a9f02SKevin Wolf     notifier_list_notify(&job->on_finalize_cancelled, job);
506139a9f02SKevin Wolf }
507139a9f02SKevin Wolf 
508544f4d52SEmanuele Giuseppe Esposito /**
509544f4d52SEmanuele Giuseppe Esposito  * To be called when a successfully completed job is finalised.
510afe1e8a7SEmanuele Giuseppe Esposito  * Called with job_mutex held.
511544f4d52SEmanuele Giuseppe Esposito  */
512afe1e8a7SEmanuele Giuseppe Esposito static void job_event_completed_locked(Job *job)
513139a9f02SKevin Wolf {
514139a9f02SKevin Wolf     notifier_list_notify(&job->on_finalize_completed, job);
515139a9f02SKevin Wolf }
516139a9f02SKevin Wolf 
517afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */
518afe1e8a7SEmanuele Giuseppe Esposito static void job_event_pending_locked(Job *job)
519139a9f02SKevin Wolf {
520139a9f02SKevin Wolf     notifier_list_notify(&job->on_pending, job);
521139a9f02SKevin Wolf }
522139a9f02SKevin Wolf 
523afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */
524afe1e8a7SEmanuele Giuseppe Esposito static void job_event_ready_locked(Job *job)
5252e1795b5SKevin Wolf {
5262e1795b5SKevin Wolf     notifier_list_notify(&job->on_ready, job);
5272e1795b5SKevin Wolf }
5282e1795b5SKevin Wolf 
529afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */
530afe1e8a7SEmanuele Giuseppe Esposito static void job_event_idle_locked(Job *job)
53134dc97b9SKevin Wolf {
53234dc97b9SKevin Wolf     notifier_list_notify(&job->on_idle, job);
53334dc97b9SKevin Wolf }
53434dc97b9SKevin Wolf 
535afe1e8a7SEmanuele Giuseppe Esposito void job_enter_cond_locked(Job *job, bool(*fn)(Job *job))
536da01ff7fSKevin Wolf {
537afe1e8a7SEmanuele Giuseppe Esposito     if (!job_started_locked(job)) {
538da01ff7fSKevin Wolf         return;
539da01ff7fSKevin Wolf     }
540da01ff7fSKevin Wolf     if (job->deferred_to_main_loop) {
541da01ff7fSKevin Wolf         return;
542da01ff7fSKevin Wolf     }
543da01ff7fSKevin Wolf 
544da01ff7fSKevin Wolf     if (job->busy) {
545da01ff7fSKevin Wolf         return;
546da01ff7fSKevin Wolf     }
547da01ff7fSKevin Wolf 
548da01ff7fSKevin Wolf     if (fn && !fn(job)) {
549da01ff7fSKevin Wolf         return;
550da01ff7fSKevin Wolf     }
551da01ff7fSKevin Wolf 
552da01ff7fSKevin Wolf     assert(!job->deferred_to_main_loop);
553da01ff7fSKevin Wolf     timer_del(&job->sleep_timer);
554da01ff7fSKevin Wolf     job->busy = true;
555afe1e8a7SEmanuele Giuseppe Esposito     job_unlock();
556ef02dac2SPaolo Bonzini     aio_co_wake(job->co);
557afe1e8a7SEmanuele Giuseppe Esposito     job_lock();
558afe1e8a7SEmanuele Giuseppe Esposito }
559afe1e8a7SEmanuele Giuseppe Esposito 
5605d43e86eSKevin Wolf void job_enter(Job *job)
5615d43e86eSKevin Wolf {
562afe1e8a7SEmanuele Giuseppe Esposito     JOB_LOCK_GUARD();
563afe1e8a7SEmanuele Giuseppe Esposito     job_enter_cond_locked(job, NULL);
5645d43e86eSKevin Wolf }
5655d43e86eSKevin Wolf 
566da01ff7fSKevin Wolf /* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds.
5673d70ff53SKevin Wolf  * Reentering the job coroutine with job_enter() before the timer has expired
5683d70ff53SKevin Wolf  * is allowed and cancels the timer.
569da01ff7fSKevin Wolf  *
5703d70ff53SKevin Wolf  * If @ns is (uint64_t) -1, no timer is scheduled and job_enter() must be
571afe1e8a7SEmanuele Giuseppe Esposito  * called explicitly.
572afe1e8a7SEmanuele Giuseppe Esposito  *
573afe1e8a7SEmanuele Giuseppe Esposito  * Called with job_mutex held, but releases it temporarily.
574afe1e8a7SEmanuele Giuseppe Esposito  */
575afe1e8a7SEmanuele Giuseppe Esposito static void coroutine_fn job_do_yield_locked(Job *job, uint64_t ns)
576da01ff7fSKevin Wolf {
577ef02dac2SPaolo Bonzini     AioContext *next_aio_context;
578ef02dac2SPaolo Bonzini 
579da01ff7fSKevin Wolf     if (ns != -1) {
580da01ff7fSKevin Wolf         timer_mod(&job->sleep_timer, ns);
581da01ff7fSKevin Wolf     }
582da01ff7fSKevin Wolf     job->busy = false;
583afe1e8a7SEmanuele Giuseppe Esposito     job_event_idle_locked(job);
584afe1e8a7SEmanuele Giuseppe Esposito     job_unlock();
585da01ff7fSKevin Wolf     qemu_coroutine_yield();
586afe1e8a7SEmanuele Giuseppe Esposito     job_lock();
587da01ff7fSKevin Wolf 
588ef02dac2SPaolo Bonzini     next_aio_context = job->aio_context;
589ef02dac2SPaolo Bonzini     /*
590ef02dac2SPaolo Bonzini      * Coroutine has resumed, but in the meanwhile the job AioContext
591ef02dac2SPaolo Bonzini      * might have changed via bdrv_try_set_aio_context(), so we need to move
592ef02dac2SPaolo Bonzini      * the coroutine too in the new aiocontext.
593ef02dac2SPaolo Bonzini      */
594ef02dac2SPaolo Bonzini     while (qemu_get_current_aio_context() != next_aio_context) {
595ef02dac2SPaolo Bonzini         job_unlock();
596ef02dac2SPaolo Bonzini         aio_co_reschedule_self(next_aio_context);
597ef02dac2SPaolo Bonzini         job_lock();
598ef02dac2SPaolo Bonzini         next_aio_context = job->aio_context;
599ef02dac2SPaolo Bonzini     }
600ef02dac2SPaolo Bonzini 
601ef02dac2SPaolo Bonzini     /* Set by job_enter_cond_locked() before re-entering the coroutine.  */
602da01ff7fSKevin Wolf     assert(job->busy);
603da01ff7fSKevin Wolf }
604da01ff7fSKevin Wolf 
605afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily. */
606afe1e8a7SEmanuele Giuseppe Esposito static void coroutine_fn job_pause_point_locked(Job *job)
607da01ff7fSKevin Wolf {
608afe1e8a7SEmanuele Giuseppe Esposito     assert(job && job_started_locked(job));
609da01ff7fSKevin Wolf 
610afe1e8a7SEmanuele Giuseppe Esposito     if (!job_should_pause_locked(job)) {
611da01ff7fSKevin Wolf         return;
612da01ff7fSKevin Wolf     }
613afe1e8a7SEmanuele Giuseppe Esposito     if (job_is_cancelled_locked(job)) {
614da01ff7fSKevin Wolf         return;
615da01ff7fSKevin Wolf     }
616da01ff7fSKevin Wolf 
617da01ff7fSKevin Wolf     if (job->driver->pause) {
618afe1e8a7SEmanuele Giuseppe Esposito         job_unlock();
619da01ff7fSKevin Wolf         job->driver->pause(job);
620afe1e8a7SEmanuele Giuseppe Esposito         job_lock();
621da01ff7fSKevin Wolf     }
622da01ff7fSKevin Wolf 
623afe1e8a7SEmanuele Giuseppe Esposito     if (job_should_pause_locked(job) && !job_is_cancelled_locked(job)) {
624da01ff7fSKevin Wolf         JobStatus status = job->status;
625afe1e8a7SEmanuele Giuseppe Esposito         job_state_transition_locked(job, status == JOB_STATUS_READY
626da01ff7fSKevin Wolf                                     ? JOB_STATUS_STANDBY
627da01ff7fSKevin Wolf                                     : JOB_STATUS_PAUSED);
628da01ff7fSKevin Wolf         job->paused = true;
629afe1e8a7SEmanuele Giuseppe Esposito         job_do_yield_locked(job, -1);
630da01ff7fSKevin Wolf         job->paused = false;
631afe1e8a7SEmanuele Giuseppe Esposito         job_state_transition_locked(job, status);
632da01ff7fSKevin Wolf     }
633da01ff7fSKevin Wolf 
634da01ff7fSKevin Wolf     if (job->driver->resume) {
635afe1e8a7SEmanuele Giuseppe Esposito         job_unlock();
636da01ff7fSKevin Wolf         job->driver->resume(job);
637afe1e8a7SEmanuele Giuseppe Esposito         job_lock();
638da01ff7fSKevin Wolf     }
639da01ff7fSKevin Wolf }
640da01ff7fSKevin Wolf 
641afe1e8a7SEmanuele Giuseppe Esposito void coroutine_fn job_pause_point(Job *job)
642afe1e8a7SEmanuele Giuseppe Esposito {
643afe1e8a7SEmanuele Giuseppe Esposito     JOB_LOCK_GUARD();
644afe1e8a7SEmanuele Giuseppe Esposito     job_pause_point_locked(job);
645afe1e8a7SEmanuele Giuseppe Esposito }
646afe1e8a7SEmanuele Giuseppe Esposito 
6479bd4d3c2SEmanuele Giuseppe Esposito void coroutine_fn job_yield(Job *job)
648afe1e8a7SEmanuele Giuseppe Esposito {
6499bd4d3c2SEmanuele Giuseppe Esposito     JOB_LOCK_GUARD();
650afe1e8a7SEmanuele Giuseppe Esposito     assert(job->busy);
651afe1e8a7SEmanuele Giuseppe Esposito 
652afe1e8a7SEmanuele Giuseppe Esposito     /* Check cancellation *before* setting busy = false, too!  */
653afe1e8a7SEmanuele Giuseppe Esposito     if (job_is_cancelled_locked(job)) {
654afe1e8a7SEmanuele Giuseppe Esposito         return;
655afe1e8a7SEmanuele Giuseppe Esposito     }
656afe1e8a7SEmanuele Giuseppe Esposito 
657afe1e8a7SEmanuele Giuseppe Esposito     if (!job_should_pause_locked(job)) {
658afe1e8a7SEmanuele Giuseppe Esposito         job_do_yield_locked(job, -1);
659afe1e8a7SEmanuele Giuseppe Esposito     }
660afe1e8a7SEmanuele Giuseppe Esposito 
661afe1e8a7SEmanuele Giuseppe Esposito     job_pause_point_locked(job);
662afe1e8a7SEmanuele Giuseppe Esposito }
663afe1e8a7SEmanuele Giuseppe Esposito 
6645d43e86eSKevin Wolf void coroutine_fn job_sleep_ns(Job *job, int64_t ns)
6655d43e86eSKevin Wolf {
666afe1e8a7SEmanuele Giuseppe Esposito     JOB_LOCK_GUARD();
6675d43e86eSKevin Wolf     assert(job->busy);
6685d43e86eSKevin Wolf 
6695d43e86eSKevin Wolf     /* Check cancellation *before* setting busy = false, too!  */
670afe1e8a7SEmanuele Giuseppe Esposito     if (job_is_cancelled_locked(job)) {
6715d43e86eSKevin Wolf         return;
6725d43e86eSKevin Wolf     }
6735d43e86eSKevin Wolf 
674afe1e8a7SEmanuele Giuseppe Esposito     if (!job_should_pause_locked(job)) {
675afe1e8a7SEmanuele Giuseppe Esposito         job_do_yield_locked(job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns);
6765d43e86eSKevin Wolf     }
6775d43e86eSKevin Wolf 
678afe1e8a7SEmanuele Giuseppe Esposito     job_pause_point_locked(job);
6795d43e86eSKevin Wolf }
6805d43e86eSKevin Wolf 
681afe1e8a7SEmanuele Giuseppe Esposito /* Assumes the job_mutex is held */
682afe1e8a7SEmanuele Giuseppe Esposito static bool job_timer_not_pending_locked(Job *job)
683b15de828SKevin Wolf {
684b15de828SKevin Wolf     return !timer_pending(&job->sleep_timer);
685b15de828SKevin Wolf }
686b15de828SKevin Wolf 
687afe1e8a7SEmanuele Giuseppe Esposito void job_pause_locked(Job *job)
688b15de828SKevin Wolf {
689b15de828SKevin Wolf     job->pause_count++;
6903ee1483bSVladimir Sementsov-Ogievskiy     if (!job->paused) {
691afe1e8a7SEmanuele Giuseppe Esposito         job_enter_cond_locked(job, NULL);
6923ee1483bSVladimir Sementsov-Ogievskiy     }
693b15de828SKevin Wolf }
694b15de828SKevin Wolf 
695afe1e8a7SEmanuele Giuseppe Esposito void job_pause(Job *job)
696afe1e8a7SEmanuele Giuseppe Esposito {
697afe1e8a7SEmanuele Giuseppe Esposito     JOB_LOCK_GUARD();
698afe1e8a7SEmanuele Giuseppe Esposito     job_pause_locked(job);
699afe1e8a7SEmanuele Giuseppe Esposito }
700afe1e8a7SEmanuele Giuseppe Esposito 
701afe1e8a7SEmanuele Giuseppe Esposito void job_resume_locked(Job *job)
702b15de828SKevin Wolf {
703b15de828SKevin Wolf     assert(job->pause_count > 0);
704b15de828SKevin Wolf     job->pause_count--;
705b15de828SKevin Wolf     if (job->pause_count) {
706b15de828SKevin Wolf         return;
707b15de828SKevin Wolf     }
708b15de828SKevin Wolf 
709b15de828SKevin Wolf     /* kick only if no timer is pending */
710afe1e8a7SEmanuele Giuseppe Esposito     job_enter_cond_locked(job, job_timer_not_pending_locked);
711b15de828SKevin Wolf }
712b15de828SKevin Wolf 
713afe1e8a7SEmanuele Giuseppe Esposito void job_resume(Job *job)
714b15de828SKevin Wolf {
715afe1e8a7SEmanuele Giuseppe Esposito     JOB_LOCK_GUARD();
716afe1e8a7SEmanuele Giuseppe Esposito     job_resume_locked(job);
717afe1e8a7SEmanuele Giuseppe Esposito }
718afe1e8a7SEmanuele Giuseppe Esposito 
719afe1e8a7SEmanuele Giuseppe Esposito void job_user_pause_locked(Job *job, Error **errp)
720afe1e8a7SEmanuele Giuseppe Esposito {
721afe1e8a7SEmanuele Giuseppe Esposito     if (job_apply_verb_locked(job, JOB_VERB_PAUSE, errp)) {
722b15de828SKevin Wolf         return;
723b15de828SKevin Wolf     }
724b15de828SKevin Wolf     if (job->user_paused) {
725b15de828SKevin Wolf         error_setg(errp, "Job is already paused");
726b15de828SKevin Wolf         return;
727b15de828SKevin Wolf     }
728b15de828SKevin Wolf     job->user_paused = true;
729afe1e8a7SEmanuele Giuseppe Esposito     job_pause_locked(job);
730b15de828SKevin Wolf }
731b15de828SKevin Wolf 
732afe1e8a7SEmanuele Giuseppe Esposito bool job_user_paused_locked(Job *job)
733b15de828SKevin Wolf {
734b15de828SKevin Wolf     return job->user_paused;
735b15de828SKevin Wolf }
736b15de828SKevin Wolf 
737afe1e8a7SEmanuele Giuseppe Esposito void job_user_resume_locked(Job *job, Error **errp)
738b15de828SKevin Wolf {
739b15de828SKevin Wolf     assert(job);
740c70b8031SEmanuele Giuseppe Esposito     GLOBAL_STATE_CODE();
741b15de828SKevin Wolf     if (!job->user_paused || job->pause_count <= 0) {
742b15de828SKevin Wolf         error_setg(errp, "Can't resume a job that was not paused");
743b15de828SKevin Wolf         return;
744b15de828SKevin Wolf     }
745afe1e8a7SEmanuele Giuseppe Esposito     if (job_apply_verb_locked(job, JOB_VERB_RESUME, errp)) {
746b15de828SKevin Wolf         return;
747b15de828SKevin Wolf     }
748b15de828SKevin Wolf     if (job->driver->user_resume) {
749afe1e8a7SEmanuele Giuseppe Esposito         job_unlock();
750b15de828SKevin Wolf         job->driver->user_resume(job);
751afe1e8a7SEmanuele Giuseppe Esposito         job_lock();
752b15de828SKevin Wolf     }
753b15de828SKevin Wolf     job->user_paused = false;
754afe1e8a7SEmanuele Giuseppe Esposito     job_resume_locked(job);
755b15de828SKevin Wolf }
756b15de828SKevin Wolf 
757afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily. */
758afe1e8a7SEmanuele Giuseppe Esposito static void job_do_dismiss_locked(Job *job)
7594ad35181SKevin Wolf {
7604ad35181SKevin Wolf     assert(job);
7614ad35181SKevin Wolf     job->busy = false;
7624ad35181SKevin Wolf     job->paused = false;
7634ad35181SKevin Wolf     job->deferred_to_main_loop = true;
7644ad35181SKevin Wolf 
765afe1e8a7SEmanuele Giuseppe Esposito     job_txn_del_job_locked(job);
7664ad35181SKevin Wolf 
767afe1e8a7SEmanuele Giuseppe Esposito     job_state_transition_locked(job, JOB_STATUS_NULL);
768afe1e8a7SEmanuele Giuseppe Esposito     job_unref_locked(job);
7694ad35181SKevin Wolf }
7704ad35181SKevin Wolf 
771afe1e8a7SEmanuele Giuseppe Esposito void job_dismiss_locked(Job **jobptr, Error **errp)
7725f9a6a08SKevin Wolf {
7735f9a6a08SKevin Wolf     Job *job = *jobptr;
7745f9a6a08SKevin Wolf     /* similarly to _complete, this is QMP-interface only. */
7755f9a6a08SKevin Wolf     assert(job->id);
776afe1e8a7SEmanuele Giuseppe Esposito     if (job_apply_verb_locked(job, JOB_VERB_DISMISS, errp)) {
7775f9a6a08SKevin Wolf         return;
7785f9a6a08SKevin Wolf     }
7795f9a6a08SKevin Wolf 
780afe1e8a7SEmanuele Giuseppe Esposito     job_do_dismiss_locked(job);
7815f9a6a08SKevin Wolf     *jobptr = NULL;
7825f9a6a08SKevin Wolf }
7835f9a6a08SKevin Wolf 
7844ad35181SKevin Wolf void job_early_fail(Job *job)
7854ad35181SKevin Wolf {
786afe1e8a7SEmanuele Giuseppe Esposito     JOB_LOCK_GUARD();
7874ad35181SKevin Wolf     assert(job->status == JOB_STATUS_CREATED);
788afe1e8a7SEmanuele Giuseppe Esposito     job_do_dismiss_locked(job);
7894ad35181SKevin Wolf }
7904ad35181SKevin Wolf 
791afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */
792afe1e8a7SEmanuele Giuseppe Esposito static void job_conclude_locked(Job *job)
7934ad35181SKevin Wolf {
794afe1e8a7SEmanuele Giuseppe Esposito     job_state_transition_locked(job, JOB_STATUS_CONCLUDED);
795afe1e8a7SEmanuele Giuseppe Esposito     if (job->auto_dismiss || !job_started_locked(job)) {
796afe1e8a7SEmanuele Giuseppe Esposito         job_do_dismiss_locked(job);
7974ad35181SKevin Wolf     }
7984ad35181SKevin Wolf }
7994ad35181SKevin Wolf 
800afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */
801afe1e8a7SEmanuele Giuseppe Esposito static void job_update_rc_locked(Job *job)
8024ad35181SKevin Wolf {
803afe1e8a7SEmanuele Giuseppe Esposito     if (!job->ret && job_is_cancelled_locked(job)) {
8044ad35181SKevin Wolf         job->ret = -ECANCELED;
8054ad35181SKevin Wolf     }
8064ad35181SKevin Wolf     if (job->ret) {
8073d1f8b07SJohn Snow         if (!job->err) {
8083d1f8b07SJohn Snow             error_setg(&job->err, "%s", strerror(-job->ret));
8091266c9b9SKevin Wolf         }
810afe1e8a7SEmanuele Giuseppe Esposito         job_state_transition_locked(job, JOB_STATUS_ABORTING);
8114ad35181SKevin Wolf     }
8124ad35181SKevin Wolf }
8134ad35181SKevin Wolf 
8144ad35181SKevin Wolf static void job_commit(Job *job)
8154ad35181SKevin Wolf {
8164ad35181SKevin Wolf     assert(!job->ret);
817c70b8031SEmanuele Giuseppe Esposito     GLOBAL_STATE_CODE();
8184ad35181SKevin Wolf     if (job->driver->commit) {
8194ad35181SKevin Wolf         job->driver->commit(job);
8204ad35181SKevin Wolf     }
8214ad35181SKevin Wolf }
8224ad35181SKevin Wolf 
8234ad35181SKevin Wolf static void job_abort(Job *job)
8244ad35181SKevin Wolf {
8254ad35181SKevin Wolf     assert(job->ret);
826c70b8031SEmanuele Giuseppe Esposito     GLOBAL_STATE_CODE();
8274ad35181SKevin Wolf     if (job->driver->abort) {
8284ad35181SKevin Wolf         job->driver->abort(job);
8294ad35181SKevin Wolf     }
8304ad35181SKevin Wolf }
8314ad35181SKevin Wolf 
8324ad35181SKevin Wolf static void job_clean(Job *job)
8334ad35181SKevin Wolf {
834c70b8031SEmanuele Giuseppe Esposito     GLOBAL_STATE_CODE();
8354ad35181SKevin Wolf     if (job->driver->clean) {
8364ad35181SKevin Wolf         job->driver->clean(job);
8374ad35181SKevin Wolf     }
8384ad35181SKevin Wolf }
8394ad35181SKevin Wolf 
8406f592e5aSEmanuele Giuseppe Esposito /*
8416f592e5aSEmanuele Giuseppe Esposito  * Called with job_mutex held, but releases it temporarily.
8426f592e5aSEmanuele Giuseppe Esposito  * Takes AioContext lock internally to invoke a job->driver callback.
8436f592e5aSEmanuele Giuseppe Esposito  */
844afe1e8a7SEmanuele Giuseppe Esposito static int job_finalize_single_locked(Job *job)
8454ad35181SKevin Wolf {
846afe1e8a7SEmanuele Giuseppe Esposito     int job_ret;
8476f592e5aSEmanuele Giuseppe Esposito     AioContext *ctx = job->aio_context;
848afe1e8a7SEmanuele Giuseppe Esposito 
849afe1e8a7SEmanuele Giuseppe Esposito     assert(job_is_completed_locked(job));
8504ad35181SKevin Wolf 
8514ad35181SKevin Wolf     /* Ensure abort is called for late-transactional failures */
852afe1e8a7SEmanuele Giuseppe Esposito     job_update_rc_locked(job);
8534ad35181SKevin Wolf 
854afe1e8a7SEmanuele Giuseppe Esposito     job_ret = job->ret;
855afe1e8a7SEmanuele Giuseppe Esposito     job_unlock();
8566f592e5aSEmanuele Giuseppe Esposito     aio_context_acquire(ctx);
857afe1e8a7SEmanuele Giuseppe Esposito 
858afe1e8a7SEmanuele Giuseppe Esposito     if (!job_ret) {
8594ad35181SKevin Wolf         job_commit(job);
8604ad35181SKevin Wolf     } else {
8614ad35181SKevin Wolf         job_abort(job);
8624ad35181SKevin Wolf     }
8634ad35181SKevin Wolf     job_clean(job);
8644ad35181SKevin Wolf 
8654ad35181SKevin Wolf     if (job->cb) {
866afe1e8a7SEmanuele Giuseppe Esposito         job->cb(job->opaque, job_ret);
8674ad35181SKevin Wolf     }
8684ad35181SKevin Wolf 
8696f592e5aSEmanuele Giuseppe Esposito     aio_context_release(ctx);
8706f592e5aSEmanuele Giuseppe Esposito     job_lock();
8716f592e5aSEmanuele Giuseppe Esposito 
8724ad35181SKevin Wolf     /* Emit events only if we actually started */
873afe1e8a7SEmanuele Giuseppe Esposito     if (job_started_locked(job)) {
874afe1e8a7SEmanuele Giuseppe Esposito         if (job_is_cancelled_locked(job)) {
875afe1e8a7SEmanuele Giuseppe Esposito             job_event_cancelled_locked(job);
8764ad35181SKevin Wolf         } else {
877afe1e8a7SEmanuele Giuseppe Esposito             job_event_completed_locked(job);
8784ad35181SKevin Wolf         }
8794ad35181SKevin Wolf     }
8804ad35181SKevin Wolf 
881afe1e8a7SEmanuele Giuseppe Esposito     job_txn_del_job_locked(job);
882afe1e8a7SEmanuele Giuseppe Esposito     job_conclude_locked(job);
8834ad35181SKevin Wolf     return 0;
8844ad35181SKevin Wolf }
8854ad35181SKevin Wolf 
8866f592e5aSEmanuele Giuseppe Esposito /*
8876f592e5aSEmanuele Giuseppe Esposito  * Called with job_mutex held, but releases it temporarily.
8886f592e5aSEmanuele Giuseppe Esposito  * Takes AioContext lock internally to invoke a job->driver callback.
8896f592e5aSEmanuele Giuseppe Esposito  */
890afe1e8a7SEmanuele Giuseppe Esposito static void job_cancel_async_locked(Job *job, bool force)
8917eaa8fb5SKevin Wolf {
8926f592e5aSEmanuele Giuseppe Esposito     AioContext *ctx = job->aio_context;
893c70b8031SEmanuele Giuseppe Esposito     GLOBAL_STATE_CODE();
8949820933bSVladimir Sementsov-Ogievskiy     if (job->driver->cancel) {
895afe1e8a7SEmanuele Giuseppe Esposito         job_unlock();
8966f592e5aSEmanuele Giuseppe Esposito         aio_context_acquire(ctx);
89773895f38SHanna Reitz         force = job->driver->cancel(job, force);
8986f592e5aSEmanuele Giuseppe Esposito         aio_context_release(ctx);
899afe1e8a7SEmanuele Giuseppe Esposito         job_lock();
90073895f38SHanna Reitz     } else {
90173895f38SHanna Reitz         /* No .cancel() means the job will behave as if force-cancelled */
90273895f38SHanna Reitz         force = true;
9039820933bSVladimir Sementsov-Ogievskiy     }
90473895f38SHanna Reitz 
9057eaa8fb5SKevin Wolf     if (job->user_paused) {
9067eaa8fb5SKevin Wolf         /* Do not call job_enter here, the caller will handle it.  */
9077eaa8fb5SKevin Wolf         if (job->driver->user_resume) {
908afe1e8a7SEmanuele Giuseppe Esposito             job_unlock();
9097eaa8fb5SKevin Wolf             job->driver->user_resume(job);
910afe1e8a7SEmanuele Giuseppe Esposito             job_lock();
9117eaa8fb5SKevin Wolf         }
912e321c059SJeff Cody         job->user_paused = false;
9137eaa8fb5SKevin Wolf         assert(job->pause_count > 0);
9147eaa8fb5SKevin Wolf         job->pause_count--;
9157eaa8fb5SKevin Wolf     }
916401dd096SHanna Reitz 
917401dd096SHanna Reitz     /*
918401dd096SHanna Reitz      * Ignore soft cancel requests after the job is already done
919401dd096SHanna Reitz      * (We will still invoke job->driver->cancel() above, but if the
920401dd096SHanna Reitz      * job driver supports soft cancelling and the job is done, that
921401dd096SHanna Reitz      * should be a no-op, too.  We still call it so it can override
922401dd096SHanna Reitz      * @force.)
923401dd096SHanna Reitz      */
924401dd096SHanna Reitz     if (force || !job->deferred_to_main_loop) {
9257eaa8fb5SKevin Wolf         job->cancelled = true;
9267eaa8fb5SKevin Wolf         /* To prevent 'force == false' overriding a previous 'force == true' */
9277eaa8fb5SKevin Wolf         job->force_cancel |= force;
9287eaa8fb5SKevin Wolf     }
929401dd096SHanna Reitz }
9307eaa8fb5SKevin Wolf 
9316f592e5aSEmanuele Giuseppe Esposito /*
9326f592e5aSEmanuele Giuseppe Esposito  * Called with job_mutex held, but releases it temporarily.
9336f592e5aSEmanuele Giuseppe Esposito  * Takes AioContext lock internally to invoke a job->driver callback.
9346f592e5aSEmanuele Giuseppe Esposito  */
935afe1e8a7SEmanuele Giuseppe Esposito static void job_completed_txn_abort_locked(Job *job)
9367eaa8fb5SKevin Wolf {
9377eaa8fb5SKevin Wolf     JobTxn *txn = job->txn;
9387eaa8fb5SKevin Wolf     Job *other_job;
9397eaa8fb5SKevin Wolf 
9407eaa8fb5SKevin Wolf     if (txn->aborting) {
9417eaa8fb5SKevin Wolf         /*
9427eaa8fb5SKevin Wolf          * We are cancelled by another job, which will handle everything.
9437eaa8fb5SKevin Wolf          */
9447eaa8fb5SKevin Wolf         return;
9457eaa8fb5SKevin Wolf     }
9467eaa8fb5SKevin Wolf     txn->aborting = true;
947afe1e8a7SEmanuele Giuseppe Esposito     job_txn_ref_locked(txn);
9487eaa8fb5SKevin Wolf 
949afe1e8a7SEmanuele Giuseppe Esposito     job_ref_locked(job);
9507eaa8fb5SKevin Wolf 
9517eaa8fb5SKevin Wolf     /* Other jobs are effectively cancelled by us, set the status for
9527eaa8fb5SKevin Wolf      * them; this job, however, may or may not be cancelled, depending
9537eaa8fb5SKevin Wolf      * on the caller, so leave it. */
9547eaa8fb5SKevin Wolf     QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
9557eaa8fb5SKevin Wolf         if (other_job != job) {
9561d4a43e9SHanna Reitz             /*
9571d4a43e9SHanna Reitz              * This is a transaction: If one job failed, no result will matter.
9581d4a43e9SHanna Reitz              * Therefore, pass force=true to terminate all other jobs as quickly
9591d4a43e9SHanna Reitz              * as possible.
9601d4a43e9SHanna Reitz              */
961afe1e8a7SEmanuele Giuseppe Esposito             job_cancel_async_locked(other_job, true);
9627eaa8fb5SKevin Wolf         }
9637eaa8fb5SKevin Wolf     }
9647eaa8fb5SKevin Wolf     while (!QLIST_EMPTY(&txn->jobs)) {
9657eaa8fb5SKevin Wolf         other_job = QLIST_FIRST(&txn->jobs);
966afe1e8a7SEmanuele Giuseppe Esposito         if (!job_is_completed_locked(other_job)) {
967afe1e8a7SEmanuele Giuseppe Esposito             assert(job_cancel_requested_locked(other_job));
968afe1e8a7SEmanuele Giuseppe Esposito             job_finish_sync_locked(other_job, NULL, NULL);
9697eaa8fb5SKevin Wolf         }
970afe1e8a7SEmanuele Giuseppe Esposito         job_finalize_single_locked(other_job);
9717eaa8fb5SKevin Wolf     }
9727eaa8fb5SKevin Wolf 
973afe1e8a7SEmanuele Giuseppe Esposito     job_unref_locked(job);
974afe1e8a7SEmanuele Giuseppe Esposito     job_txn_unref_locked(txn);
9757eaa8fb5SKevin Wolf }
9767eaa8fb5SKevin Wolf 
977afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily */
978afe1e8a7SEmanuele Giuseppe Esposito static int job_prepare_locked(Job *job)
9797eaa8fb5SKevin Wolf {
980afe1e8a7SEmanuele Giuseppe Esposito     int ret;
9816f592e5aSEmanuele Giuseppe Esposito     AioContext *ctx = job->aio_context;
982afe1e8a7SEmanuele Giuseppe Esposito 
983c70b8031SEmanuele Giuseppe Esposito     GLOBAL_STATE_CODE();
9846f592e5aSEmanuele Giuseppe Esposito 
9857eaa8fb5SKevin Wolf     if (job->ret == 0 && job->driver->prepare) {
986afe1e8a7SEmanuele Giuseppe Esposito         job_unlock();
9876f592e5aSEmanuele Giuseppe Esposito         aio_context_acquire(ctx);
988afe1e8a7SEmanuele Giuseppe Esposito         ret = job->driver->prepare(job);
9896f592e5aSEmanuele Giuseppe Esposito         aio_context_release(ctx);
990afe1e8a7SEmanuele Giuseppe Esposito         job_lock();
991afe1e8a7SEmanuele Giuseppe Esposito         job->ret = ret;
992afe1e8a7SEmanuele Giuseppe Esposito         job_update_rc_locked(job);
9937eaa8fb5SKevin Wolf     }
9946f592e5aSEmanuele Giuseppe Esposito 
9957eaa8fb5SKevin Wolf     return job->ret;
9967eaa8fb5SKevin Wolf }
9977eaa8fb5SKevin Wolf 
998afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held */
999afe1e8a7SEmanuele Giuseppe Esposito static int job_needs_finalize_locked(Job *job)
10007eaa8fb5SKevin Wolf {
10017eaa8fb5SKevin Wolf     return !job->auto_finalize;
10027eaa8fb5SKevin Wolf }
10037eaa8fb5SKevin Wolf 
1004afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held */
1005afe1e8a7SEmanuele Giuseppe Esposito static void job_do_finalize_locked(Job *job)
10067eaa8fb5SKevin Wolf {
10077eaa8fb5SKevin Wolf     int rc;
10087eaa8fb5SKevin Wolf     assert(job && job->txn);
10097eaa8fb5SKevin Wolf 
10107eaa8fb5SKevin Wolf     /* prepare the transaction to complete */
1011afe1e8a7SEmanuele Giuseppe Esposito     rc = job_txn_apply_locked(job, job_prepare_locked);
10127eaa8fb5SKevin Wolf     if (rc) {
1013afe1e8a7SEmanuele Giuseppe Esposito         job_completed_txn_abort_locked(job);
10147eaa8fb5SKevin Wolf     } else {
1015afe1e8a7SEmanuele Giuseppe Esposito         job_txn_apply_locked(job, job_finalize_single_locked);
10167eaa8fb5SKevin Wolf     }
10177eaa8fb5SKevin Wolf }
10187eaa8fb5SKevin Wolf 
1019afe1e8a7SEmanuele Giuseppe Esposito void job_finalize_locked(Job *job, Error **errp)
1020afe1e8a7SEmanuele Giuseppe Esposito {
1021afe1e8a7SEmanuele Giuseppe Esposito     assert(job && job->id);
1022afe1e8a7SEmanuele Giuseppe Esposito     if (job_apply_verb_locked(job, JOB_VERB_FINALIZE, errp)) {
1023afe1e8a7SEmanuele Giuseppe Esposito         return;
1024afe1e8a7SEmanuele Giuseppe Esposito     }
1025afe1e8a7SEmanuele Giuseppe Esposito     job_do_finalize_locked(job);
1026afe1e8a7SEmanuele Giuseppe Esposito }
1027afe1e8a7SEmanuele Giuseppe Esposito 
1028afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */
1029afe1e8a7SEmanuele Giuseppe Esposito static int job_transition_to_pending_locked(Job *job)
10307eaa8fb5SKevin Wolf {
1031afe1e8a7SEmanuele Giuseppe Esposito     job_state_transition_locked(job, JOB_STATUS_PENDING);
10327eaa8fb5SKevin Wolf     if (!job->auto_finalize) {
1033afe1e8a7SEmanuele Giuseppe Esposito         job_event_pending_locked(job);
10347eaa8fb5SKevin Wolf     }
10357eaa8fb5SKevin Wolf     return 0;
10367eaa8fb5SKevin Wolf }
10377eaa8fb5SKevin Wolf 
10382e1795b5SKevin Wolf void job_transition_to_ready(Job *job)
10392e1795b5SKevin Wolf {
1040afe1e8a7SEmanuele Giuseppe Esposito     JOB_LOCK_GUARD();
1041afe1e8a7SEmanuele Giuseppe Esposito     job_state_transition_locked(job, JOB_STATUS_READY);
1042afe1e8a7SEmanuele Giuseppe Esposito     job_event_ready_locked(job);
10432e1795b5SKevin Wolf }
10442e1795b5SKevin Wolf 
1045afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */
1046afe1e8a7SEmanuele Giuseppe Esposito static void job_completed_txn_success_locked(Job *job)
10477eaa8fb5SKevin Wolf {
10487eaa8fb5SKevin Wolf     JobTxn *txn = job->txn;
10497eaa8fb5SKevin Wolf     Job *other_job;
10507eaa8fb5SKevin Wolf 
1051afe1e8a7SEmanuele Giuseppe Esposito     job_state_transition_locked(job, JOB_STATUS_WAITING);
10527eaa8fb5SKevin Wolf 
10537eaa8fb5SKevin Wolf     /*
10547eaa8fb5SKevin Wolf      * Successful completion, see if there are other running jobs in this
10557eaa8fb5SKevin Wolf      * txn.
10567eaa8fb5SKevin Wolf      */
10577eaa8fb5SKevin Wolf     QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
1058afe1e8a7SEmanuele Giuseppe Esposito         if (!job_is_completed_locked(other_job)) {
10597eaa8fb5SKevin Wolf             return;
10607eaa8fb5SKevin Wolf         }
10617eaa8fb5SKevin Wolf         assert(other_job->ret == 0);
10627eaa8fb5SKevin Wolf     }
10637eaa8fb5SKevin Wolf 
1064afe1e8a7SEmanuele Giuseppe Esposito     job_txn_apply_locked(job, job_transition_to_pending_locked);
10657eaa8fb5SKevin Wolf 
10667eaa8fb5SKevin Wolf     /* If no jobs need manual finalization, automatically do so */
1067afe1e8a7SEmanuele Giuseppe Esposito     if (job_txn_apply_locked(job, job_needs_finalize_locked) == 0) {
1068afe1e8a7SEmanuele Giuseppe Esposito         job_do_finalize_locked(job);
10697eaa8fb5SKevin Wolf     }
10707eaa8fb5SKevin Wolf }
10717eaa8fb5SKevin Wolf 
1072afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */
1073afe1e8a7SEmanuele Giuseppe Esposito static void job_completed_locked(Job *job)
10743d70ff53SKevin Wolf {
1075afe1e8a7SEmanuele Giuseppe Esposito     assert(job && job->txn && !job_is_completed_locked(job));
10761266c9b9SKevin Wolf 
1077afe1e8a7SEmanuele Giuseppe Esposito     job_update_rc_locked(job);
1078404ff28dSJohn Snow     trace_job_completed(job, job->ret);
10793d70ff53SKevin Wolf     if (job->ret) {
1080afe1e8a7SEmanuele Giuseppe Esposito         job_completed_txn_abort_locked(job);
10813d70ff53SKevin Wolf     } else {
1082afe1e8a7SEmanuele Giuseppe Esposito         job_completed_txn_success_locked(job);
10833d70ff53SKevin Wolf     }
10843d70ff53SKevin Wolf }
10853d70ff53SKevin Wolf 
1086afe1e8a7SEmanuele Giuseppe Esposito /**
1087afe1e8a7SEmanuele Giuseppe Esposito  * Useful only as a type shim for aio_bh_schedule_oneshot.
1088afe1e8a7SEmanuele Giuseppe Esposito  * Called with job_mutex *not* held.
1089afe1e8a7SEmanuele Giuseppe Esposito  */
1090ccbfb331SJohn Snow static void job_exit(void *opaque)
1091ccbfb331SJohn Snow {
1092ccbfb331SJohn Snow     Job *job = (Job *)opaque;
1093afe1e8a7SEmanuele Giuseppe Esposito     JOB_LOCK_GUARD();
1094afe1e8a7SEmanuele Giuseppe Esposito     job_ref_locked(job);
1095b5a7a057SKevin Wolf 
1096b5a7a057SKevin Wolf     /* This is a lie, we're not quiescent, but still doing the completion
1097b5a7a057SKevin Wolf      * callbacks. However, completion callbacks tend to involve operations that
1098b5a7a057SKevin Wolf      * drain block nodes, and if .drained_poll still returned true, we would
1099b5a7a057SKevin Wolf      * deadlock. */
1100b5a7a057SKevin Wolf     job->busy = false;
1101afe1e8a7SEmanuele Giuseppe Esposito     job_event_idle_locked(job);
1102b5a7a057SKevin Wolf 
1103afe1e8a7SEmanuele Giuseppe Esposito     job_completed_locked(job);
1104afe1e8a7SEmanuele Giuseppe Esposito     job_unref_locked(job);
1105ccbfb331SJohn Snow }
1106ccbfb331SJohn Snow 
1107ccbfb331SJohn Snow /**
1108ccbfb331SJohn Snow  * All jobs must allow a pause point before entering their job proper. This
1109ccbfb331SJohn Snow  * ensures that jobs can be paused prior to being started, then resumed later.
1110ccbfb331SJohn Snow  */
1111ccbfb331SJohn Snow static void coroutine_fn job_co_entry(void *opaque)
1112ccbfb331SJohn Snow {
1113ccbfb331SJohn Snow     Job *job = opaque;
1114afe1e8a7SEmanuele Giuseppe Esposito     int ret;
1115ccbfb331SJohn Snow 
1116ccbfb331SJohn Snow     assert(job && job->driver && job->driver->run);
1117afe1e8a7SEmanuele Giuseppe Esposito     WITH_JOB_LOCK_GUARD() {
1118c70b8031SEmanuele Giuseppe Esposito         assert(job->aio_context == qemu_get_current_aio_context());
1119afe1e8a7SEmanuele Giuseppe Esposito         job_pause_point_locked(job);
1120afe1e8a7SEmanuele Giuseppe Esposito     }
1121afe1e8a7SEmanuele Giuseppe Esposito     ret = job->driver->run(job, &job->err);
1122afe1e8a7SEmanuele Giuseppe Esposito     WITH_JOB_LOCK_GUARD() {
1123afe1e8a7SEmanuele Giuseppe Esposito         job->ret = ret;
1124ccbfb331SJohn Snow         job->deferred_to_main_loop = true;
1125b5a7a057SKevin Wolf         job->busy = true;
1126afe1e8a7SEmanuele Giuseppe Esposito     }
1127ccbfb331SJohn Snow     aio_bh_schedule_oneshot(qemu_get_aio_context(), job_exit, job);
1128ccbfb331SJohn Snow }
1129ccbfb331SJohn Snow 
1130ccbfb331SJohn Snow void job_start(Job *job)
1131ccbfb331SJohn Snow {
1132afe1e8a7SEmanuele Giuseppe Esposito     assert(qemu_in_main_thread());
1133afe1e8a7SEmanuele Giuseppe Esposito 
1134afe1e8a7SEmanuele Giuseppe Esposito     WITH_JOB_LOCK_GUARD() {
1135afe1e8a7SEmanuele Giuseppe Esposito         assert(job && !job_started_locked(job) && job->paused &&
1136ccbfb331SJohn Snow             job->driver && job->driver->run);
1137ccbfb331SJohn Snow         job->co = qemu_coroutine_create(job_co_entry, job);
1138ccbfb331SJohn Snow         job->pause_count--;
1139ccbfb331SJohn Snow         job->busy = true;
1140ccbfb331SJohn Snow         job->paused = false;
1141afe1e8a7SEmanuele Giuseppe Esposito         job_state_transition_locked(job, JOB_STATUS_RUNNING);
1142afe1e8a7SEmanuele Giuseppe Esposito     }
1143ccbfb331SJohn Snow     aio_co_enter(job->aio_context, job->co);
1144ccbfb331SJohn Snow }
1145ccbfb331SJohn Snow 
1146afe1e8a7SEmanuele Giuseppe Esposito void job_cancel_locked(Job *job, bool force)
11473d70ff53SKevin Wolf {
11483d70ff53SKevin Wolf     if (job->status == JOB_STATUS_CONCLUDED) {
1149afe1e8a7SEmanuele Giuseppe Esposito         job_do_dismiss_locked(job);
11503d70ff53SKevin Wolf         return;
11513d70ff53SKevin Wolf     }
1152afe1e8a7SEmanuele Giuseppe Esposito     job_cancel_async_locked(job, force);
1153afe1e8a7SEmanuele Giuseppe Esposito     if (!job_started_locked(job)) {
1154afe1e8a7SEmanuele Giuseppe Esposito         job_completed_locked(job);
11553d70ff53SKevin Wolf     } else if (job->deferred_to_main_loop) {
1156401dd096SHanna Reitz         /*
1157401dd096SHanna Reitz          * job_cancel_async() ignores soft-cancel requests for jobs
1158401dd096SHanna Reitz          * that are already done (i.e. deferred to the main loop).  We
1159401dd096SHanna Reitz          * have to check again whether the job is really cancelled.
116008b83bffSHanna Reitz          * (job_cancel_requested() and job_is_cancelled() are equivalent
116108b83bffSHanna Reitz          * here, because job_cancel_async() will make soft-cancel
116208b83bffSHanna Reitz          * requests no-ops when deferred_to_main_loop is true.  We
116308b83bffSHanna Reitz          * choose to call job_is_cancelled() to show that we invoke
116408b83bffSHanna Reitz          * job_completed_txn_abort() only for force-cancelled jobs.)
1165401dd096SHanna Reitz          */
1166afe1e8a7SEmanuele Giuseppe Esposito         if (job_is_cancelled_locked(job)) {
1167afe1e8a7SEmanuele Giuseppe Esposito             job_completed_txn_abort_locked(job);
1168401dd096SHanna Reitz         }
11693d70ff53SKevin Wolf     } else {
1170afe1e8a7SEmanuele Giuseppe Esposito         job_enter_cond_locked(job, NULL);
11713d70ff53SKevin Wolf     }
11723d70ff53SKevin Wolf }
11733d70ff53SKevin Wolf 
1174afe1e8a7SEmanuele Giuseppe Esposito void job_user_cancel_locked(Job *job, bool force, Error **errp)
1175afe1e8a7SEmanuele Giuseppe Esposito {
1176afe1e8a7SEmanuele Giuseppe Esposito     if (job_apply_verb_locked(job, JOB_VERB_CANCEL, errp)) {
1177afe1e8a7SEmanuele Giuseppe Esposito         return;
1178afe1e8a7SEmanuele Giuseppe Esposito     }
1179afe1e8a7SEmanuele Giuseppe Esposito     job_cancel_locked(job, force);
1180afe1e8a7SEmanuele Giuseppe Esposito }
1181afe1e8a7SEmanuele Giuseppe Esposito 
11829bd4d3c2SEmanuele Giuseppe Esposito /* A wrapper around job_cancel_locked() taking an Error ** parameter so it may
11839bd4d3c2SEmanuele Giuseppe Esposito  * be used with job_finish_sync_locked() without the need for (rather nasty)
11849bd4d3c2SEmanuele Giuseppe Esposito  * function pointer casts there.
1185afe1e8a7SEmanuele Giuseppe Esposito  *
1186afe1e8a7SEmanuele Giuseppe Esposito  * Called with job_mutex held.
1187afe1e8a7SEmanuele Giuseppe Esposito  */
1188afe1e8a7SEmanuele Giuseppe Esposito static void job_cancel_err_locked(Job *job, Error **errp)
11893d70ff53SKevin Wolf {
1190afe1e8a7SEmanuele Giuseppe Esposito     job_cancel_locked(job, false);
11913d70ff53SKevin Wolf }
11923d70ff53SKevin Wolf 
11934cfb3f05SHanna Reitz /**
11944cfb3f05SHanna Reitz  * Same as job_cancel_err(), but force-cancel.
1195afe1e8a7SEmanuele Giuseppe Esposito  * Called with job_mutex held.
11964cfb3f05SHanna Reitz  */
1197afe1e8a7SEmanuele Giuseppe Esposito static void job_force_cancel_err_locked(Job *job, Error **errp)
11983d70ff53SKevin Wolf {
1199afe1e8a7SEmanuele Giuseppe Esposito     job_cancel_locked(job, true);
1200afe1e8a7SEmanuele Giuseppe Esposito }
1201afe1e8a7SEmanuele Giuseppe Esposito 
1202afe1e8a7SEmanuele Giuseppe Esposito int job_cancel_sync_locked(Job *job, bool force)
1203afe1e8a7SEmanuele Giuseppe Esposito {
1204afe1e8a7SEmanuele Giuseppe Esposito     if (force) {
1205afe1e8a7SEmanuele Giuseppe Esposito         return job_finish_sync_locked(job, &job_force_cancel_err_locked, NULL);
1206afe1e8a7SEmanuele Giuseppe Esposito     } else {
1207afe1e8a7SEmanuele Giuseppe Esposito         return job_finish_sync_locked(job, &job_cancel_err_locked, NULL);
1208afe1e8a7SEmanuele Giuseppe Esposito     }
12094cfb3f05SHanna Reitz }
12104cfb3f05SHanna Reitz 
12114cfb3f05SHanna Reitz int job_cancel_sync(Job *job, bool force)
12124cfb3f05SHanna Reitz {
1213afe1e8a7SEmanuele Giuseppe Esposito     JOB_LOCK_GUARD();
1214afe1e8a7SEmanuele Giuseppe Esposito     return job_cancel_sync_locked(job, force);
12154cfb3f05SHanna Reitz }
12163d70ff53SKevin Wolf 
12173d70ff53SKevin Wolf void job_cancel_sync_all(void)
12183d70ff53SKevin Wolf {
12193d70ff53SKevin Wolf     Job *job;
1220afe1e8a7SEmanuele Giuseppe Esposito     JOB_LOCK_GUARD();
12213d70ff53SKevin Wolf 
1222afe1e8a7SEmanuele Giuseppe Esposito     while ((job = job_next_locked(NULL))) {
1223afe1e8a7SEmanuele Giuseppe Esposito         job_cancel_sync_locked(job, true);
12243d70ff53SKevin Wolf     }
12253d70ff53SKevin Wolf }
12263d70ff53SKevin Wolf 
1227afe1e8a7SEmanuele Giuseppe Esposito int job_complete_sync_locked(Job *job, Error **errp)
12283d70ff53SKevin Wolf {
1229afe1e8a7SEmanuele Giuseppe Esposito     return job_finish_sync_locked(job, job_complete_locked, errp);
12303d70ff53SKevin Wolf }
12313d70ff53SKevin Wolf 
1232afe1e8a7SEmanuele Giuseppe Esposito void job_complete_locked(Job *job, Error **errp)
12333453d972SKevin Wolf {
12343453d972SKevin Wolf     /* Should not be reachable via external interface for internal jobs */
12353453d972SKevin Wolf     assert(job->id);
1236c70b8031SEmanuele Giuseppe Esposito     GLOBAL_STATE_CODE();
1237afe1e8a7SEmanuele Giuseppe Esposito     if (job_apply_verb_locked(job, JOB_VERB_COMPLETE, errp)) {
12383453d972SKevin Wolf         return;
12393453d972SKevin Wolf     }
1240afe1e8a7SEmanuele Giuseppe Esposito     if (job_cancel_requested_locked(job) || !job->driver->complete) {
12413453d972SKevin Wolf         error_setg(errp, "The active block job '%s' cannot be completed",
12423453d972SKevin Wolf                    job->id);
12433453d972SKevin Wolf         return;
12443453d972SKevin Wolf     }
12453453d972SKevin Wolf 
1246afe1e8a7SEmanuele Giuseppe Esposito     job_unlock();
12473453d972SKevin Wolf     job->driver->complete(job, errp);
1248afe1e8a7SEmanuele Giuseppe Esposito     job_lock();
12493453d972SKevin Wolf }
12503453d972SKevin Wolf 
1251afe1e8a7SEmanuele Giuseppe Esposito int job_finish_sync_locked(Job *job,
1252afe1e8a7SEmanuele Giuseppe Esposito                            void (*finish)(Job *, Error **errp),
1253afe1e8a7SEmanuele Giuseppe Esposito                            Error **errp)
12546a74c075SKevin Wolf {
12556a74c075SKevin Wolf     Error *local_err = NULL;
12566a74c075SKevin Wolf     int ret;
12573ed4f708SEmanuele Giuseppe Esposito     GLOBAL_STATE_CODE();
12586a74c075SKevin Wolf 
1259afe1e8a7SEmanuele Giuseppe Esposito     job_ref_locked(job);
12606a74c075SKevin Wolf 
12616a74c075SKevin Wolf     if (finish) {
12626a74c075SKevin Wolf         finish(job, &local_err);
12636a74c075SKevin Wolf     }
12646a74c075SKevin Wolf     if (local_err) {
12656a74c075SKevin Wolf         error_propagate(errp, local_err);
1266afe1e8a7SEmanuele Giuseppe Esposito         job_unref_locked(job);
12676a74c075SKevin Wolf         return -EBUSY;
12686a74c075SKevin Wolf     }
1269de0fbe64SKevin Wolf 
1270afe1e8a7SEmanuele Giuseppe Esposito     job_unlock();
12716f592e5aSEmanuele Giuseppe Esposito     AIO_WAIT_WHILE_UNLOCKED(job->aio_context,
1272bb0c9409SVladimir Sementsov-Ogievskiy                             (job_enter(job), !job_is_completed(job)));
1273afe1e8a7SEmanuele Giuseppe Esposito     job_lock();
1274de0fbe64SKevin Wolf 
1275afe1e8a7SEmanuele Giuseppe Esposito     ret = (job_is_cancelled_locked(job) && job->ret == 0)
1276afe1e8a7SEmanuele Giuseppe Esposito           ? -ECANCELED : job->ret;
1277afe1e8a7SEmanuele Giuseppe Esposito     job_unref_locked(job);
12786a74c075SKevin Wolf     return ret;
12796a74c075SKevin Wolf }
1280