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 /* 3655c5a25aSEmanuele Giuseppe Esposito * job_mutex protects the jobs list, but also makes the 3755c5a25aSEmanuele Giuseppe Esposito * struct job fields thread-safe. 3855c5a25aSEmanuele Giuseppe Esposito */ 3955c5a25aSEmanuele Giuseppe Esposito QemuMutex job_mutex; 4055c5a25aSEmanuele Giuseppe Esposito 41*afe1e8a7SEmanuele Giuseppe Esposito /* Protected by job_mutex */ 42e7c1d78bSKevin Wolf static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs); 43e7c1d78bSKevin Wolf 44a50c2ab8SKevin Wolf /* Job State Transition Table */ 45a50c2ab8SKevin Wolf bool JobSTT[JOB_STATUS__MAX][JOB_STATUS__MAX] = { 46a50c2ab8SKevin Wolf /* U, C, R, P, Y, S, W, D, X, E, N */ 47a50c2ab8SKevin Wolf /* U: */ [JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 48a50c2ab8SKevin Wolf /* C: */ [JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1}, 49a50c2ab8SKevin Wolf /* R: */ [JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0}, 50a50c2ab8SKevin Wolf /* P: */ [JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, 51a50c2ab8SKevin Wolf /* Y: */ [JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0}, 52a50c2ab8SKevin Wolf /* S: */ [JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, 53a50c2ab8SKevin Wolf /* W: */ [JOB_STATUS_WAITING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0}, 54a50c2ab8SKevin Wolf /* D: */ [JOB_STATUS_PENDING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, 55a50c2ab8SKevin Wolf /* X: */ [JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, 56a50c2ab8SKevin Wolf /* E: */ [JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, 57a50c2ab8SKevin Wolf /* N: */ [JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 58a50c2ab8SKevin Wolf }; 59a50c2ab8SKevin Wolf 60a50c2ab8SKevin Wolf bool JobVerbTable[JOB_VERB__MAX][JOB_STATUS__MAX] = { 61a50c2ab8SKevin Wolf /* U, C, R, P, Y, S, W, D, X, E, N */ 62a50c2ab8SKevin Wolf [JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, 63a50c2ab8SKevin Wolf [JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, 64a50c2ab8SKevin Wolf [JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, 65a50c2ab8SKevin Wolf [JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, 6653ddb9c8SMax Reitz [JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0}, 67a50c2ab8SKevin Wolf [JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, 68a50c2ab8SKevin Wolf [JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, 69a50c2ab8SKevin Wolf }; 70a50c2ab8SKevin Wolf 717eaa8fb5SKevin Wolf /* Transactional group of jobs */ 727eaa8fb5SKevin Wolf struct JobTxn { 737eaa8fb5SKevin Wolf 747eaa8fb5SKevin Wolf /* Is this txn being cancelled? */ 757eaa8fb5SKevin Wolf bool aborting; 767eaa8fb5SKevin Wolf 777eaa8fb5SKevin Wolf /* List of jobs */ 787eaa8fb5SKevin Wolf QLIST_HEAD(, Job) jobs; 797eaa8fb5SKevin Wolf 807eaa8fb5SKevin Wolf /* Reference count */ 817eaa8fb5SKevin Wolf int refcnt; 827eaa8fb5SKevin Wolf }; 837eaa8fb5SKevin Wolf 8455c5a25aSEmanuele Giuseppe Esposito void job_lock(void) 8555c5a25aSEmanuele Giuseppe Esposito { 8655c5a25aSEmanuele Giuseppe Esposito /* nop */ 8755c5a25aSEmanuele Giuseppe Esposito } 88da01ff7fSKevin Wolf 8955c5a25aSEmanuele Giuseppe Esposito void job_unlock(void) 9055c5a25aSEmanuele Giuseppe Esposito { 9155c5a25aSEmanuele Giuseppe Esposito /* nop */ 9255c5a25aSEmanuele Giuseppe Esposito } 9355c5a25aSEmanuele Giuseppe Esposito 9455c5a25aSEmanuele Giuseppe Esposito static void real_job_lock(void) 95da01ff7fSKevin Wolf { 96da01ff7fSKevin Wolf qemu_mutex_lock(&job_mutex); 97da01ff7fSKevin Wolf } 98da01ff7fSKevin Wolf 9955c5a25aSEmanuele Giuseppe Esposito static void real_job_unlock(void) 100da01ff7fSKevin Wolf { 101da01ff7fSKevin Wolf qemu_mutex_unlock(&job_mutex); 102da01ff7fSKevin Wolf } 103da01ff7fSKevin Wolf 104da01ff7fSKevin Wolf static void __attribute__((__constructor__)) job_init(void) 105da01ff7fSKevin Wolf { 106da01ff7fSKevin Wolf qemu_mutex_init(&job_mutex); 107da01ff7fSKevin Wolf } 108da01ff7fSKevin Wolf 1097eaa8fb5SKevin Wolf JobTxn *job_txn_new(void) 1107eaa8fb5SKevin Wolf { 1117eaa8fb5SKevin Wolf JobTxn *txn = g_new0(JobTxn, 1); 1127eaa8fb5SKevin Wolf QLIST_INIT(&txn->jobs); 1137eaa8fb5SKevin Wolf txn->refcnt = 1; 1147eaa8fb5SKevin Wolf return txn; 1157eaa8fb5SKevin Wolf } 1167eaa8fb5SKevin Wolf 117*afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 118*afe1e8a7SEmanuele Giuseppe Esposito static void job_txn_ref_locked(JobTxn *txn) 1197eaa8fb5SKevin Wolf { 1207eaa8fb5SKevin Wolf txn->refcnt++; 1217eaa8fb5SKevin Wolf } 1227eaa8fb5SKevin Wolf 123*afe1e8a7SEmanuele Giuseppe Esposito void job_txn_unref_locked(JobTxn *txn) 1247eaa8fb5SKevin Wolf { 1257eaa8fb5SKevin Wolf if (txn && --txn->refcnt == 0) { 1267eaa8fb5SKevin Wolf g_free(txn); 1277eaa8fb5SKevin Wolf } 1287eaa8fb5SKevin Wolf } 1297eaa8fb5SKevin Wolf 130*afe1e8a7SEmanuele Giuseppe Esposito void job_txn_unref(JobTxn *txn) 131*afe1e8a7SEmanuele Giuseppe Esposito { 132*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 133*afe1e8a7SEmanuele Giuseppe Esposito job_txn_unref_locked(txn); 134*afe1e8a7SEmanuele Giuseppe Esposito } 135*afe1e8a7SEmanuele Giuseppe Esposito 136544f4d52SEmanuele Giuseppe Esposito /** 137544f4d52SEmanuele Giuseppe Esposito * @txn: The transaction (may be NULL) 138544f4d52SEmanuele Giuseppe Esposito * @job: Job to add to the transaction 139544f4d52SEmanuele Giuseppe Esposito * 140544f4d52SEmanuele Giuseppe Esposito * Add @job to the transaction. The @job must not already be in a transaction. 141544f4d52SEmanuele Giuseppe Esposito * The caller must call either job_txn_unref() or job_completed() to release 142544f4d52SEmanuele Giuseppe Esposito * the reference that is automatically grabbed here. 143544f4d52SEmanuele Giuseppe Esposito * 144544f4d52SEmanuele Giuseppe Esposito * If @txn is NULL, the function does nothing. 145*afe1e8a7SEmanuele Giuseppe Esposito * 146*afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex held. 147544f4d52SEmanuele Giuseppe Esposito */ 148*afe1e8a7SEmanuele Giuseppe Esposito static void job_txn_add_job_locked(JobTxn *txn, Job *job) 1497eaa8fb5SKevin Wolf { 1507eaa8fb5SKevin Wolf if (!txn) { 1517eaa8fb5SKevin Wolf return; 1527eaa8fb5SKevin Wolf } 1537eaa8fb5SKevin Wolf 1547eaa8fb5SKevin Wolf assert(!job->txn); 1557eaa8fb5SKevin Wolf job->txn = txn; 1567eaa8fb5SKevin Wolf 1577eaa8fb5SKevin Wolf QLIST_INSERT_HEAD(&txn->jobs, job, txn_list); 158*afe1e8a7SEmanuele Giuseppe Esposito job_txn_ref_locked(txn); 1597eaa8fb5SKevin Wolf } 1607eaa8fb5SKevin Wolf 161*afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 162*afe1e8a7SEmanuele Giuseppe Esposito static void job_txn_del_job_locked(Job *job) 1637eaa8fb5SKevin Wolf { 1647eaa8fb5SKevin Wolf if (job->txn) { 1657eaa8fb5SKevin Wolf QLIST_REMOVE(job, txn_list); 166*afe1e8a7SEmanuele Giuseppe Esposito job_txn_unref_locked(job->txn); 1677eaa8fb5SKevin Wolf job->txn = NULL; 1687eaa8fb5SKevin Wolf } 1697eaa8fb5SKevin Wolf } 1707eaa8fb5SKevin Wolf 171*afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily. */ 172*afe1e8a7SEmanuele Giuseppe Esposito static int job_txn_apply_locked(Job *job, int fn(Job *)) 1737eaa8fb5SKevin Wolf { 174b660a84bSStefan Reiter AioContext *inner_ctx; 175b660a84bSStefan Reiter Job *other_job, *next; 176b660a84bSStefan Reiter JobTxn *txn = job->txn; 1777eaa8fb5SKevin Wolf int rc = 0; 1787eaa8fb5SKevin Wolf 179b660a84bSStefan Reiter /* 180b660a84bSStefan Reiter * Similar to job_completed_txn_abort, we take each job's lock before 181b660a84bSStefan Reiter * applying fn, but since we assume that outer_ctx is held by the caller, 182b660a84bSStefan Reiter * we need to release it here to avoid holding the lock twice - which would 183b660a84bSStefan Reiter * break AIO_WAIT_WHILE from within fn. 184b660a84bSStefan Reiter */ 185*afe1e8a7SEmanuele Giuseppe Esposito job_ref_locked(job); 186b660a84bSStefan Reiter aio_context_release(job->aio_context); 187b660a84bSStefan Reiter 188b660a84bSStefan Reiter QLIST_FOREACH_SAFE(other_job, &txn->jobs, txn_list, next) { 189b660a84bSStefan Reiter inner_ctx = other_job->aio_context; 190b660a84bSStefan Reiter aio_context_acquire(inner_ctx); 191b660a84bSStefan Reiter rc = fn(other_job); 192b660a84bSStefan Reiter aio_context_release(inner_ctx); 1937eaa8fb5SKevin Wolf if (rc) { 1947eaa8fb5SKevin Wolf break; 1957eaa8fb5SKevin Wolf } 1967eaa8fb5SKevin Wolf } 197b660a84bSStefan Reiter 198b660a84bSStefan Reiter /* 199b660a84bSStefan Reiter * Note that job->aio_context might have been changed by calling fn, so we 200b660a84bSStefan Reiter * can't use a local variable to cache it. 201b660a84bSStefan Reiter */ 202b660a84bSStefan Reiter aio_context_acquire(job->aio_context); 203*afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 2047eaa8fb5SKevin Wolf return rc; 2057eaa8fb5SKevin Wolf } 2067eaa8fb5SKevin Wolf 207456273b0SKevin Wolf bool job_is_internal(Job *job) 2081dac83f1SKevin Wolf { 2091dac83f1SKevin Wolf return (job->id == NULL); 2101dac83f1SKevin Wolf } 2111dac83f1SKevin Wolf 212*afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 213*afe1e8a7SEmanuele Giuseppe Esposito static void job_state_transition_locked(Job *job, JobStatus s1) 214a50c2ab8SKevin Wolf { 215a50c2ab8SKevin Wolf JobStatus s0 = job->status; 216c2032289SLiam Merwick assert(s1 >= 0 && s1 < JOB_STATUS__MAX); 2174ad35181SKevin Wolf trace_job_state_transition(job, job->ret, 218a50c2ab8SKevin Wolf JobSTT[s0][s1] ? "allowed" : "disallowed", 219a50c2ab8SKevin Wolf JobStatus_str(s0), JobStatus_str(s1)); 220a50c2ab8SKevin Wolf assert(JobSTT[s0][s1]); 221a50c2ab8SKevin Wolf job->status = s1; 2221dac83f1SKevin Wolf 2231dac83f1SKevin Wolf if (!job_is_internal(job) && s1 != s0) { 2243ab72385SPeter Xu qapi_event_send_job_status_change(job->id, job->status); 2251dac83f1SKevin Wolf } 226a50c2ab8SKevin Wolf } 227a50c2ab8SKevin Wolf 228*afe1e8a7SEmanuele Giuseppe Esposito int job_apply_verb_locked(Job *job, JobVerb verb, Error **errp) 229a50c2ab8SKevin Wolf { 230a50c2ab8SKevin Wolf JobStatus s0 = job->status; 231c2032289SLiam Merwick assert(verb >= 0 && verb < JOB_VERB__MAX); 232a50c2ab8SKevin Wolf trace_job_apply_verb(job, JobStatus_str(s0), JobVerb_str(verb), 233a50c2ab8SKevin Wolf JobVerbTable[verb][s0] ? "allowed" : "prohibited"); 234a50c2ab8SKevin Wolf if (JobVerbTable[verb][s0]) { 235a50c2ab8SKevin Wolf return 0; 236a50c2ab8SKevin Wolf } 237a50c2ab8SKevin Wolf error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'", 238a50c2ab8SKevin Wolf job->id, JobStatus_str(s0), JobVerb_str(verb)); 239a50c2ab8SKevin Wolf return -EPERM; 240a50c2ab8SKevin Wolf } 241a50c2ab8SKevin Wolf 242*afe1e8a7SEmanuele Giuseppe Esposito int job_apply_verb(Job *job, JobVerb verb, Error **errp) 243*afe1e8a7SEmanuele Giuseppe Esposito { 244*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 245*afe1e8a7SEmanuele Giuseppe Esposito return job_apply_verb_locked(job, verb, errp); 246*afe1e8a7SEmanuele Giuseppe Esposito } 247*afe1e8a7SEmanuele Giuseppe Esposito 248252291eaSKevin Wolf JobType job_type(const Job *job) 249252291eaSKevin Wolf { 250252291eaSKevin Wolf return job->driver->job_type; 251252291eaSKevin Wolf } 252252291eaSKevin Wolf 253252291eaSKevin Wolf const char *job_type_str(const Job *job) 254252291eaSKevin Wolf { 255252291eaSKevin Wolf return JobType_str(job_type(job)); 256252291eaSKevin Wolf } 257252291eaSKevin Wolf 258*afe1e8a7SEmanuele Giuseppe Esposito bool job_is_cancelled_locked(Job *job) 259daa7f2f9SKevin Wolf { 260a640fa0eSHanna Reitz /* force_cancel may be true only if cancelled is true, too */ 261a640fa0eSHanna Reitz assert(job->cancelled || !job->force_cancel); 262a640fa0eSHanna Reitz return job->force_cancel; 26308b83bffSHanna Reitz } 26408b83bffSHanna Reitz 265*afe1e8a7SEmanuele Giuseppe Esposito bool job_is_cancelled(Job *job) 266*afe1e8a7SEmanuele Giuseppe Esposito { 267*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 268*afe1e8a7SEmanuele Giuseppe Esposito return job_is_cancelled_locked(job); 269*afe1e8a7SEmanuele Giuseppe Esposito } 270*afe1e8a7SEmanuele Giuseppe Esposito 271*afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 272*afe1e8a7SEmanuele Giuseppe Esposito static bool job_cancel_requested_locked(Job *job) 27308b83bffSHanna Reitz { 274daa7f2f9SKevin Wolf return job->cancelled; 275daa7f2f9SKevin Wolf } 276daa7f2f9SKevin Wolf 277*afe1e8a7SEmanuele Giuseppe Esposito bool job_cancel_requested(Job *job) 278*afe1e8a7SEmanuele Giuseppe Esposito { 279*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 280*afe1e8a7SEmanuele Giuseppe Esposito return job_cancel_requested_locked(job); 281*afe1e8a7SEmanuele Giuseppe Esposito } 282*afe1e8a7SEmanuele Giuseppe Esposito 283*afe1e8a7SEmanuele Giuseppe Esposito bool job_is_ready_locked(Job *job) 284df956ae2SKevin Wolf { 285df956ae2SKevin Wolf switch (job->status) { 286df956ae2SKevin Wolf case JOB_STATUS_UNDEFINED: 287df956ae2SKevin Wolf case JOB_STATUS_CREATED: 288df956ae2SKevin Wolf case JOB_STATUS_RUNNING: 289df956ae2SKevin Wolf case JOB_STATUS_PAUSED: 290df956ae2SKevin Wolf case JOB_STATUS_WAITING: 291df956ae2SKevin Wolf case JOB_STATUS_PENDING: 292df956ae2SKevin Wolf case JOB_STATUS_ABORTING: 293df956ae2SKevin Wolf case JOB_STATUS_CONCLUDED: 294df956ae2SKevin Wolf case JOB_STATUS_NULL: 295df956ae2SKevin Wolf return false; 296df956ae2SKevin Wolf case JOB_STATUS_READY: 297df956ae2SKevin Wolf case JOB_STATUS_STANDBY: 298df956ae2SKevin Wolf return true; 299df956ae2SKevin Wolf default: 300df956ae2SKevin Wolf g_assert_not_reached(); 301df956ae2SKevin Wolf } 302df956ae2SKevin Wolf return false; 303df956ae2SKevin Wolf } 304df956ae2SKevin Wolf 305*afe1e8a7SEmanuele Giuseppe Esposito bool job_is_ready(Job *job) 306*afe1e8a7SEmanuele Giuseppe Esposito { 307*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 308*afe1e8a7SEmanuele Giuseppe Esposito return job_is_ready_locked(job); 309*afe1e8a7SEmanuele Giuseppe Esposito } 310*afe1e8a7SEmanuele Giuseppe Esposito 311*afe1e8a7SEmanuele Giuseppe Esposito bool job_is_completed_locked(Job *job) 312*afe1e8a7SEmanuele Giuseppe Esposito { 313*afe1e8a7SEmanuele Giuseppe Esposito switch (job->status) { 314*afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_UNDEFINED: 315*afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_CREATED: 316*afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_RUNNING: 317*afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_PAUSED: 318*afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_READY: 319*afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_STANDBY: 320*afe1e8a7SEmanuele Giuseppe Esposito return false; 321*afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_WAITING: 322*afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_PENDING: 323*afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_ABORTING: 324*afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_CONCLUDED: 325*afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_NULL: 326*afe1e8a7SEmanuele Giuseppe Esposito return true; 327*afe1e8a7SEmanuele Giuseppe Esposito default: 328*afe1e8a7SEmanuele Giuseppe Esposito g_assert_not_reached(); 329*afe1e8a7SEmanuele Giuseppe Esposito } 330*afe1e8a7SEmanuele Giuseppe Esposito return false; 331*afe1e8a7SEmanuele Giuseppe Esposito } 332*afe1e8a7SEmanuele Giuseppe Esposito 333dbe5e6c1SKevin Wolf bool job_is_completed(Job *job) 334dbe5e6c1SKevin Wolf { 335*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 336*afe1e8a7SEmanuele Giuseppe Esposito return job_is_completed_locked(job); 337dbe5e6c1SKevin Wolf } 338dbe5e6c1SKevin Wolf 339*afe1e8a7SEmanuele Giuseppe Esposito static bool job_started_locked(Job *job) 340da01ff7fSKevin Wolf { 341da01ff7fSKevin Wolf return job->co; 342da01ff7fSKevin Wolf } 343da01ff7fSKevin Wolf 344*afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 345*afe1e8a7SEmanuele Giuseppe Esposito static bool job_should_pause_locked(Job *job) 346da01ff7fSKevin Wolf { 347da01ff7fSKevin Wolf return job->pause_count > 0; 348da01ff7fSKevin Wolf } 349da01ff7fSKevin Wolf 350*afe1e8a7SEmanuele Giuseppe Esposito Job *job_next_locked(Job *job) 351e7c1d78bSKevin Wolf { 352e7c1d78bSKevin Wolf if (!job) { 353e7c1d78bSKevin Wolf return QLIST_FIRST(&jobs); 354e7c1d78bSKevin Wolf } 355e7c1d78bSKevin Wolf return QLIST_NEXT(job, job_list); 356e7c1d78bSKevin Wolf } 357e7c1d78bSKevin Wolf 358*afe1e8a7SEmanuele Giuseppe Esposito Job *job_next(Job *job) 359*afe1e8a7SEmanuele Giuseppe Esposito { 360*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 361*afe1e8a7SEmanuele Giuseppe Esposito return job_next_locked(job); 362*afe1e8a7SEmanuele Giuseppe Esposito } 363*afe1e8a7SEmanuele Giuseppe Esposito 364*afe1e8a7SEmanuele Giuseppe Esposito Job *job_get_locked(const char *id) 365e7c1d78bSKevin Wolf { 366e7c1d78bSKevin Wolf Job *job; 367e7c1d78bSKevin Wolf 368e7c1d78bSKevin Wolf QLIST_FOREACH(job, &jobs, job_list) { 369e7c1d78bSKevin Wolf if (job->id && !strcmp(id, job->id)) { 370e7c1d78bSKevin Wolf return job; 371e7c1d78bSKevin Wolf } 372e7c1d78bSKevin Wolf } 373e7c1d78bSKevin Wolf 374e7c1d78bSKevin Wolf return NULL; 375e7c1d78bSKevin Wolf } 376e7c1d78bSKevin Wolf 377*afe1e8a7SEmanuele Giuseppe Esposito Job *job_get(const char *id) 378*afe1e8a7SEmanuele Giuseppe Esposito { 379*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 380*afe1e8a7SEmanuele Giuseppe Esposito return job_get_locked(id); 381*afe1e8a7SEmanuele Giuseppe Esposito } 382*afe1e8a7SEmanuele Giuseppe Esposito 383*afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex *not* held. */ 3845d43e86eSKevin Wolf static void job_sleep_timer_cb(void *opaque) 3855d43e86eSKevin Wolf { 3865d43e86eSKevin Wolf Job *job = opaque; 3875d43e86eSKevin Wolf 3885d43e86eSKevin Wolf job_enter(job); 3895d43e86eSKevin Wolf } 3905d43e86eSKevin Wolf 3917eaa8fb5SKevin Wolf void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn, 3927eaa8fb5SKevin Wolf AioContext *ctx, int flags, BlockCompletionFunc *cb, 3937eaa8fb5SKevin Wolf void *opaque, Error **errp) 39433e9e9bdSKevin Wolf { 39533e9e9bdSKevin Wolf Job *job; 39633e9e9bdSKevin Wolf 397*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 398*afe1e8a7SEmanuele Giuseppe Esposito 39933e9e9bdSKevin Wolf if (job_id) { 400bb02b65cSKevin Wolf if (flags & JOB_INTERNAL) { 401bb02b65cSKevin Wolf error_setg(errp, "Cannot specify job ID for internal job"); 402bb02b65cSKevin Wolf return NULL; 403bb02b65cSKevin Wolf } 40433e9e9bdSKevin Wolf if (!id_wellformed(job_id)) { 40533e9e9bdSKevin Wolf error_setg(errp, "Invalid job ID '%s'", job_id); 40633e9e9bdSKevin Wolf return NULL; 40733e9e9bdSKevin Wolf } 408*afe1e8a7SEmanuele Giuseppe Esposito if (job_get_locked(job_id)) { 409e7c1d78bSKevin Wolf error_setg(errp, "Job ID '%s' already in use", job_id); 410e7c1d78bSKevin Wolf return NULL; 411e7c1d78bSKevin Wolf } 412bb02b65cSKevin Wolf } else if (!(flags & JOB_INTERNAL)) { 413bb02b65cSKevin Wolf error_setg(errp, "An explicit job ID is required"); 414bb02b65cSKevin Wolf return NULL; 41533e9e9bdSKevin Wolf } 41633e9e9bdSKevin Wolf 41733e9e9bdSKevin Wolf job = g_malloc0(driver->instance_size); 41833e9e9bdSKevin Wolf job->driver = driver; 41933e9e9bdSKevin Wolf job->id = g_strdup(job_id); 42080fa2c75SKevin Wolf job->refcnt = 1; 42108be6fe2SKevin Wolf job->aio_context = ctx; 422da01ff7fSKevin Wolf job->busy = false; 423da01ff7fSKevin Wolf job->paused = true; 424da01ff7fSKevin Wolf job->pause_count = 1; 425bb02b65cSKevin Wolf job->auto_finalize = !(flags & JOB_MANUAL_FINALIZE); 426bb02b65cSKevin Wolf job->auto_dismiss = !(flags & JOB_MANUAL_DISMISS); 4274ad35181SKevin Wolf job->cb = cb; 4284ad35181SKevin Wolf job->opaque = opaque; 42933e9e9bdSKevin Wolf 430a7b4f8fcSEmanuele Giuseppe Esposito progress_init(&job->progress); 431a7b4f8fcSEmanuele Giuseppe Esposito 432139a9f02SKevin Wolf notifier_list_init(&job->on_finalize_cancelled); 433139a9f02SKevin Wolf notifier_list_init(&job->on_finalize_completed); 434139a9f02SKevin Wolf notifier_list_init(&job->on_pending); 4352e1795b5SKevin Wolf notifier_list_init(&job->on_ready); 436252f4091SEmanuele Giuseppe Esposito notifier_list_init(&job->on_idle); 437139a9f02SKevin Wolf 438*afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_CREATED); 4395d43e86eSKevin Wolf aio_timer_init(qemu_get_aio_context(), &job->sleep_timer, 4405d43e86eSKevin Wolf QEMU_CLOCK_REALTIME, SCALE_NS, 4415d43e86eSKevin Wolf job_sleep_timer_cb, job); 442a50c2ab8SKevin Wolf 443e7c1d78bSKevin Wolf QLIST_INSERT_HEAD(&jobs, job, job_list); 444e7c1d78bSKevin Wolf 4457eaa8fb5SKevin Wolf /* Single jobs are modeled as single-job transactions for sake of 4467eaa8fb5SKevin Wolf * consolidating the job management logic */ 4477eaa8fb5SKevin Wolf if (!txn) { 4487eaa8fb5SKevin Wolf txn = job_txn_new(); 449*afe1e8a7SEmanuele Giuseppe Esposito job_txn_add_job_locked(txn, job); 450*afe1e8a7SEmanuele Giuseppe Esposito job_txn_unref_locked(txn); 4517eaa8fb5SKevin Wolf } else { 452*afe1e8a7SEmanuele Giuseppe Esposito job_txn_add_job_locked(txn, job); 4537eaa8fb5SKevin Wolf } 4547eaa8fb5SKevin Wolf 45533e9e9bdSKevin Wolf return job; 45633e9e9bdSKevin Wolf } 457fd61a701SKevin Wolf 458*afe1e8a7SEmanuele Giuseppe Esposito void job_ref_locked(Job *job) 459fd61a701SKevin Wolf { 46080fa2c75SKevin Wolf ++job->refcnt; 46180fa2c75SKevin Wolf } 46280fa2c75SKevin Wolf 463*afe1e8a7SEmanuele Giuseppe Esposito void job_ref(Job *job) 464*afe1e8a7SEmanuele Giuseppe Esposito { 465*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 466*afe1e8a7SEmanuele Giuseppe Esposito job_ref_locked(job); 467*afe1e8a7SEmanuele Giuseppe Esposito } 468*afe1e8a7SEmanuele Giuseppe Esposito 469*afe1e8a7SEmanuele Giuseppe Esposito void job_unref_locked(Job *job) 47080fa2c75SKevin Wolf { 471c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 472c70b8031SEmanuele Giuseppe Esposito 47380fa2c75SKevin Wolf if (--job->refcnt == 0) { 47480fa2c75SKevin Wolf assert(job->status == JOB_STATUS_NULL); 4755d43e86eSKevin Wolf assert(!timer_pending(&job->sleep_timer)); 4767eaa8fb5SKevin Wolf assert(!job->txn); 47780fa2c75SKevin Wolf 47880fa2c75SKevin Wolf if (job->driver->free) { 479*afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 48080fa2c75SKevin Wolf job->driver->free(job); 481*afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 48280fa2c75SKevin Wolf } 48380fa2c75SKevin Wolf 484e7c1d78bSKevin Wolf QLIST_REMOVE(job, job_list); 485e7c1d78bSKevin Wolf 486a7b4f8fcSEmanuele Giuseppe Esposito progress_destroy(&job->progress); 4873d1f8b07SJohn Snow error_free(job->err); 488fd61a701SKevin Wolf g_free(job->id); 489fd61a701SKevin Wolf g_free(job); 490fd61a701SKevin Wolf } 49180fa2c75SKevin Wolf } 4921908a559SKevin Wolf 493*afe1e8a7SEmanuele Giuseppe Esposito void job_unref(Job *job) 494*afe1e8a7SEmanuele Giuseppe Esposito { 495*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 496*afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 497*afe1e8a7SEmanuele Giuseppe Esposito } 498*afe1e8a7SEmanuele Giuseppe Esposito 49930a5c887SKevin Wolf void job_progress_update(Job *job, uint64_t done) 50030a5c887SKevin Wolf { 50101fe1ca9SVladimir Sementsov-Ogievskiy progress_work_done(&job->progress, done); 50230a5c887SKevin Wolf } 50330a5c887SKevin Wolf 50430a5c887SKevin Wolf void job_progress_set_remaining(Job *job, uint64_t remaining) 50530a5c887SKevin Wolf { 50601fe1ca9SVladimir Sementsov-Ogievskiy progress_set_remaining(&job->progress, remaining); 50730a5c887SKevin Wolf } 50830a5c887SKevin Wolf 50962f13600SMax Reitz void job_progress_increase_remaining(Job *job, uint64_t delta) 51062f13600SMax Reitz { 51101fe1ca9SVladimir Sementsov-Ogievskiy progress_increase_remaining(&job->progress, delta); 51262f13600SMax Reitz } 51362f13600SMax Reitz 514544f4d52SEmanuele Giuseppe Esposito /** 515544f4d52SEmanuele Giuseppe Esposito * To be called when a cancelled job is finalised. 516*afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex held. 517544f4d52SEmanuele Giuseppe Esposito */ 518*afe1e8a7SEmanuele Giuseppe Esposito static void job_event_cancelled_locked(Job *job) 519139a9f02SKevin Wolf { 520139a9f02SKevin Wolf notifier_list_notify(&job->on_finalize_cancelled, job); 521139a9f02SKevin Wolf } 522139a9f02SKevin Wolf 523544f4d52SEmanuele Giuseppe Esposito /** 524544f4d52SEmanuele Giuseppe Esposito * To be called when a successfully completed job is finalised. 525*afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex held. 526544f4d52SEmanuele Giuseppe Esposito */ 527*afe1e8a7SEmanuele Giuseppe Esposito static void job_event_completed_locked(Job *job) 528139a9f02SKevin Wolf { 529139a9f02SKevin Wolf notifier_list_notify(&job->on_finalize_completed, job); 530139a9f02SKevin Wolf } 531139a9f02SKevin Wolf 532*afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 533*afe1e8a7SEmanuele Giuseppe Esposito static void job_event_pending_locked(Job *job) 534139a9f02SKevin Wolf { 535139a9f02SKevin Wolf notifier_list_notify(&job->on_pending, job); 536139a9f02SKevin Wolf } 537139a9f02SKevin Wolf 538*afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 539*afe1e8a7SEmanuele Giuseppe Esposito static void job_event_ready_locked(Job *job) 5402e1795b5SKevin Wolf { 5412e1795b5SKevin Wolf notifier_list_notify(&job->on_ready, job); 5422e1795b5SKevin Wolf } 5432e1795b5SKevin Wolf 544*afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 545*afe1e8a7SEmanuele Giuseppe Esposito static void job_event_idle_locked(Job *job) 54634dc97b9SKevin Wolf { 54734dc97b9SKevin Wolf notifier_list_notify(&job->on_idle, job); 54834dc97b9SKevin Wolf } 54934dc97b9SKevin Wolf 550*afe1e8a7SEmanuele Giuseppe Esposito void job_enter_cond_locked(Job *job, bool(*fn)(Job *job)) 551da01ff7fSKevin Wolf { 552*afe1e8a7SEmanuele Giuseppe Esposito if (!job_started_locked(job)) { 553da01ff7fSKevin Wolf return; 554da01ff7fSKevin Wolf } 555da01ff7fSKevin Wolf if (job->deferred_to_main_loop) { 556da01ff7fSKevin Wolf return; 557da01ff7fSKevin Wolf } 558da01ff7fSKevin Wolf 55955c5a25aSEmanuele Giuseppe Esposito real_job_lock(); 560da01ff7fSKevin Wolf if (job->busy) { 56155c5a25aSEmanuele Giuseppe Esposito real_job_unlock(); 562da01ff7fSKevin Wolf return; 563da01ff7fSKevin Wolf } 564da01ff7fSKevin Wolf 565da01ff7fSKevin Wolf if (fn && !fn(job)) { 56655c5a25aSEmanuele Giuseppe Esposito real_job_unlock(); 567da01ff7fSKevin Wolf return; 568da01ff7fSKevin Wolf } 569da01ff7fSKevin Wolf 570da01ff7fSKevin Wolf assert(!job->deferred_to_main_loop); 571da01ff7fSKevin Wolf timer_del(&job->sleep_timer); 572da01ff7fSKevin Wolf job->busy = true; 57355c5a25aSEmanuele Giuseppe Esposito real_job_unlock(); 574*afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 57513726123SKevin Wolf aio_co_enter(job->aio_context, job->co); 576*afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 577*afe1e8a7SEmanuele Giuseppe Esposito } 578*afe1e8a7SEmanuele Giuseppe Esposito 579*afe1e8a7SEmanuele Giuseppe Esposito void job_enter_cond(Job *job, bool(*fn)(Job *job)) 580*afe1e8a7SEmanuele Giuseppe Esposito { 581*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 582*afe1e8a7SEmanuele Giuseppe Esposito job_enter_cond_locked(job, fn); 583da01ff7fSKevin Wolf } 584da01ff7fSKevin Wolf 5855d43e86eSKevin Wolf void job_enter(Job *job) 5865d43e86eSKevin Wolf { 587*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 588*afe1e8a7SEmanuele Giuseppe Esposito job_enter_cond_locked(job, NULL); 5895d43e86eSKevin Wolf } 5905d43e86eSKevin Wolf 591da01ff7fSKevin Wolf /* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds. 5923d70ff53SKevin Wolf * Reentering the job coroutine with job_enter() before the timer has expired 5933d70ff53SKevin Wolf * is allowed and cancels the timer. 594da01ff7fSKevin Wolf * 5953d70ff53SKevin Wolf * If @ns is (uint64_t) -1, no timer is scheduled and job_enter() must be 596*afe1e8a7SEmanuele Giuseppe Esposito * called explicitly. 597*afe1e8a7SEmanuele Giuseppe Esposito * 598*afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex held, but releases it temporarily. 599*afe1e8a7SEmanuele Giuseppe Esposito */ 600*afe1e8a7SEmanuele Giuseppe Esposito static void coroutine_fn job_do_yield_locked(Job *job, uint64_t ns) 601da01ff7fSKevin Wolf { 60255c5a25aSEmanuele Giuseppe Esposito real_job_lock(); 603da01ff7fSKevin Wolf if (ns != -1) { 604da01ff7fSKevin Wolf timer_mod(&job->sleep_timer, ns); 605da01ff7fSKevin Wolf } 606da01ff7fSKevin Wolf job->busy = false; 607*afe1e8a7SEmanuele Giuseppe Esposito job_event_idle_locked(job); 60855c5a25aSEmanuele Giuseppe Esposito real_job_unlock(); 609*afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 610da01ff7fSKevin Wolf qemu_coroutine_yield(); 611*afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 612da01ff7fSKevin Wolf 613da01ff7fSKevin Wolf /* Set by job_enter_cond() before re-entering the coroutine. */ 614da01ff7fSKevin Wolf assert(job->busy); 615da01ff7fSKevin Wolf } 616da01ff7fSKevin Wolf 617*afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily. */ 618*afe1e8a7SEmanuele Giuseppe Esposito static void coroutine_fn job_pause_point_locked(Job *job) 619da01ff7fSKevin Wolf { 620*afe1e8a7SEmanuele Giuseppe Esposito assert(job && job_started_locked(job)); 621da01ff7fSKevin Wolf 622*afe1e8a7SEmanuele Giuseppe Esposito if (!job_should_pause_locked(job)) { 623da01ff7fSKevin Wolf return; 624da01ff7fSKevin Wolf } 625*afe1e8a7SEmanuele Giuseppe Esposito if (job_is_cancelled_locked(job)) { 626da01ff7fSKevin Wolf return; 627da01ff7fSKevin Wolf } 628da01ff7fSKevin Wolf 629da01ff7fSKevin Wolf if (job->driver->pause) { 630*afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 631da01ff7fSKevin Wolf job->driver->pause(job); 632*afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 633da01ff7fSKevin Wolf } 634da01ff7fSKevin Wolf 635*afe1e8a7SEmanuele Giuseppe Esposito if (job_should_pause_locked(job) && !job_is_cancelled_locked(job)) { 636da01ff7fSKevin Wolf JobStatus status = job->status; 637*afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, status == JOB_STATUS_READY 638da01ff7fSKevin Wolf ? JOB_STATUS_STANDBY 639da01ff7fSKevin Wolf : JOB_STATUS_PAUSED); 640da01ff7fSKevin Wolf job->paused = true; 641*afe1e8a7SEmanuele Giuseppe Esposito job_do_yield_locked(job, -1); 642da01ff7fSKevin Wolf job->paused = false; 643*afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, status); 644da01ff7fSKevin Wolf } 645da01ff7fSKevin Wolf 646da01ff7fSKevin Wolf if (job->driver->resume) { 647*afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 648da01ff7fSKevin Wolf job->driver->resume(job); 649*afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 650da01ff7fSKevin Wolf } 651da01ff7fSKevin Wolf } 652da01ff7fSKevin Wolf 653*afe1e8a7SEmanuele Giuseppe Esposito void coroutine_fn job_pause_point(Job *job) 654*afe1e8a7SEmanuele Giuseppe Esposito { 655*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 656*afe1e8a7SEmanuele Giuseppe Esposito job_pause_point_locked(job); 657*afe1e8a7SEmanuele Giuseppe Esposito } 658*afe1e8a7SEmanuele Giuseppe Esposito 659*afe1e8a7SEmanuele Giuseppe Esposito static void coroutine_fn job_yield_locked(Job *job) 660*afe1e8a7SEmanuele Giuseppe Esposito { 661*afe1e8a7SEmanuele Giuseppe Esposito assert(job->busy); 662*afe1e8a7SEmanuele Giuseppe Esposito 663*afe1e8a7SEmanuele Giuseppe Esposito /* Check cancellation *before* setting busy = false, too! */ 664*afe1e8a7SEmanuele Giuseppe Esposito if (job_is_cancelled_locked(job)) { 665*afe1e8a7SEmanuele Giuseppe Esposito return; 666*afe1e8a7SEmanuele Giuseppe Esposito } 667*afe1e8a7SEmanuele Giuseppe Esposito 668*afe1e8a7SEmanuele Giuseppe Esposito if (!job_should_pause_locked(job)) { 669*afe1e8a7SEmanuele Giuseppe Esposito job_do_yield_locked(job, -1); 670*afe1e8a7SEmanuele Giuseppe Esposito } 671*afe1e8a7SEmanuele Giuseppe Esposito 672*afe1e8a7SEmanuele Giuseppe Esposito job_pause_point_locked(job); 673*afe1e8a7SEmanuele Giuseppe Esposito } 674*afe1e8a7SEmanuele Giuseppe Esposito 67506753a07SPaolo Bonzini void coroutine_fn job_yield(Job *job) 676198c49ccSKevin Wolf { 677*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 678*afe1e8a7SEmanuele Giuseppe Esposito job_yield_locked(job); 679198c49ccSKevin Wolf } 680198c49ccSKevin Wolf 6815d43e86eSKevin Wolf void coroutine_fn job_sleep_ns(Job *job, int64_t ns) 6825d43e86eSKevin Wolf { 683*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 6845d43e86eSKevin Wolf assert(job->busy); 6855d43e86eSKevin Wolf 6865d43e86eSKevin Wolf /* Check cancellation *before* setting busy = false, too! */ 687*afe1e8a7SEmanuele Giuseppe Esposito if (job_is_cancelled_locked(job)) { 6885d43e86eSKevin Wolf return; 6895d43e86eSKevin Wolf } 6905d43e86eSKevin Wolf 691*afe1e8a7SEmanuele Giuseppe Esposito if (!job_should_pause_locked(job)) { 692*afe1e8a7SEmanuele Giuseppe Esposito job_do_yield_locked(job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns); 6935d43e86eSKevin Wolf } 6945d43e86eSKevin Wolf 695*afe1e8a7SEmanuele Giuseppe Esposito job_pause_point_locked(job); 6965d43e86eSKevin Wolf } 6975d43e86eSKevin Wolf 698*afe1e8a7SEmanuele Giuseppe Esposito /* Assumes the job_mutex is held */ 699*afe1e8a7SEmanuele Giuseppe Esposito static bool job_timer_not_pending_locked(Job *job) 700b15de828SKevin Wolf { 701b15de828SKevin Wolf return !timer_pending(&job->sleep_timer); 702b15de828SKevin Wolf } 703b15de828SKevin Wolf 704*afe1e8a7SEmanuele Giuseppe Esposito void job_pause_locked(Job *job) 705b15de828SKevin Wolf { 706b15de828SKevin Wolf job->pause_count++; 7073ee1483bSVladimir Sementsov-Ogievskiy if (!job->paused) { 708*afe1e8a7SEmanuele Giuseppe Esposito job_enter_cond_locked(job, NULL); 7093ee1483bSVladimir Sementsov-Ogievskiy } 710b15de828SKevin Wolf } 711b15de828SKevin Wolf 712*afe1e8a7SEmanuele Giuseppe Esposito void job_pause(Job *job) 713*afe1e8a7SEmanuele Giuseppe Esposito { 714*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 715*afe1e8a7SEmanuele Giuseppe Esposito job_pause_locked(job); 716*afe1e8a7SEmanuele Giuseppe Esposito } 717*afe1e8a7SEmanuele Giuseppe Esposito 718*afe1e8a7SEmanuele Giuseppe Esposito void job_resume_locked(Job *job) 719b15de828SKevin Wolf { 720b15de828SKevin Wolf assert(job->pause_count > 0); 721b15de828SKevin Wolf job->pause_count--; 722b15de828SKevin Wolf if (job->pause_count) { 723b15de828SKevin Wolf return; 724b15de828SKevin Wolf } 725b15de828SKevin Wolf 726b15de828SKevin Wolf /* kick only if no timer is pending */ 727*afe1e8a7SEmanuele Giuseppe Esposito job_enter_cond_locked(job, job_timer_not_pending_locked); 728b15de828SKevin Wolf } 729b15de828SKevin Wolf 730*afe1e8a7SEmanuele Giuseppe Esposito void job_resume(Job *job) 731b15de828SKevin Wolf { 732*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 733*afe1e8a7SEmanuele Giuseppe Esposito job_resume_locked(job); 734*afe1e8a7SEmanuele Giuseppe Esposito } 735*afe1e8a7SEmanuele Giuseppe Esposito 736*afe1e8a7SEmanuele Giuseppe Esposito void job_user_pause_locked(Job *job, Error **errp) 737*afe1e8a7SEmanuele Giuseppe Esposito { 738*afe1e8a7SEmanuele Giuseppe Esposito if (job_apply_verb_locked(job, JOB_VERB_PAUSE, errp)) { 739b15de828SKevin Wolf return; 740b15de828SKevin Wolf } 741b15de828SKevin Wolf if (job->user_paused) { 742b15de828SKevin Wolf error_setg(errp, "Job is already paused"); 743b15de828SKevin Wolf return; 744b15de828SKevin Wolf } 745b15de828SKevin Wolf job->user_paused = true; 746*afe1e8a7SEmanuele Giuseppe Esposito job_pause_locked(job); 747b15de828SKevin Wolf } 748b15de828SKevin Wolf 749*afe1e8a7SEmanuele Giuseppe Esposito void job_user_pause(Job *job, Error **errp) 750*afe1e8a7SEmanuele Giuseppe Esposito { 751*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 752*afe1e8a7SEmanuele Giuseppe Esposito job_user_pause_locked(job, errp); 753*afe1e8a7SEmanuele Giuseppe Esposito } 754*afe1e8a7SEmanuele Giuseppe Esposito 755*afe1e8a7SEmanuele Giuseppe Esposito bool job_user_paused_locked(Job *job) 756b15de828SKevin Wolf { 757b15de828SKevin Wolf return job->user_paused; 758b15de828SKevin Wolf } 759b15de828SKevin Wolf 760*afe1e8a7SEmanuele Giuseppe Esposito bool job_user_paused(Job *job) 761*afe1e8a7SEmanuele Giuseppe Esposito { 762*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 763*afe1e8a7SEmanuele Giuseppe Esposito return job_user_paused_locked(job); 764*afe1e8a7SEmanuele Giuseppe Esposito } 765*afe1e8a7SEmanuele Giuseppe Esposito 766*afe1e8a7SEmanuele Giuseppe Esposito void job_user_resume_locked(Job *job, Error **errp) 767b15de828SKevin Wolf { 768b15de828SKevin Wolf assert(job); 769c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 770b15de828SKevin Wolf if (!job->user_paused || job->pause_count <= 0) { 771b15de828SKevin Wolf error_setg(errp, "Can't resume a job that was not paused"); 772b15de828SKevin Wolf return; 773b15de828SKevin Wolf } 774*afe1e8a7SEmanuele Giuseppe Esposito if (job_apply_verb_locked(job, JOB_VERB_RESUME, errp)) { 775b15de828SKevin Wolf return; 776b15de828SKevin Wolf } 777b15de828SKevin Wolf if (job->driver->user_resume) { 778*afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 779b15de828SKevin Wolf job->driver->user_resume(job); 780*afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 781b15de828SKevin Wolf } 782b15de828SKevin Wolf job->user_paused = false; 783*afe1e8a7SEmanuele Giuseppe Esposito job_resume_locked(job); 784b15de828SKevin Wolf } 785b15de828SKevin Wolf 786*afe1e8a7SEmanuele Giuseppe Esposito void job_user_resume(Job *job, Error **errp) 787*afe1e8a7SEmanuele Giuseppe Esposito { 788*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 789*afe1e8a7SEmanuele Giuseppe Esposito job_user_resume_locked(job, errp); 790*afe1e8a7SEmanuele Giuseppe Esposito } 791*afe1e8a7SEmanuele Giuseppe Esposito 792*afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily. */ 793*afe1e8a7SEmanuele Giuseppe Esposito static void job_do_dismiss_locked(Job *job) 7944ad35181SKevin Wolf { 7954ad35181SKevin Wolf assert(job); 7964ad35181SKevin Wolf job->busy = false; 7974ad35181SKevin Wolf job->paused = false; 7984ad35181SKevin Wolf job->deferred_to_main_loop = true; 7994ad35181SKevin Wolf 800*afe1e8a7SEmanuele Giuseppe Esposito job_txn_del_job_locked(job); 8014ad35181SKevin Wolf 802*afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_NULL); 803*afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 8044ad35181SKevin Wolf } 8054ad35181SKevin Wolf 806*afe1e8a7SEmanuele Giuseppe Esposito void job_dismiss_locked(Job **jobptr, Error **errp) 8075f9a6a08SKevin Wolf { 8085f9a6a08SKevin Wolf Job *job = *jobptr; 8095f9a6a08SKevin Wolf /* similarly to _complete, this is QMP-interface only. */ 8105f9a6a08SKevin Wolf assert(job->id); 811*afe1e8a7SEmanuele Giuseppe Esposito if (job_apply_verb_locked(job, JOB_VERB_DISMISS, errp)) { 8125f9a6a08SKevin Wolf return; 8135f9a6a08SKevin Wolf } 8145f9a6a08SKevin Wolf 815*afe1e8a7SEmanuele Giuseppe Esposito job_do_dismiss_locked(job); 8165f9a6a08SKevin Wolf *jobptr = NULL; 8175f9a6a08SKevin Wolf } 8185f9a6a08SKevin Wolf 819*afe1e8a7SEmanuele Giuseppe Esposito void job_dismiss(Job **jobptr, Error **errp) 820*afe1e8a7SEmanuele Giuseppe Esposito { 821*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 822*afe1e8a7SEmanuele Giuseppe Esposito job_dismiss_locked(jobptr, errp); 823*afe1e8a7SEmanuele Giuseppe Esposito } 824*afe1e8a7SEmanuele Giuseppe Esposito 8254ad35181SKevin Wolf void job_early_fail(Job *job) 8264ad35181SKevin Wolf { 827*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 8284ad35181SKevin Wolf assert(job->status == JOB_STATUS_CREATED); 829*afe1e8a7SEmanuele Giuseppe Esposito job_do_dismiss_locked(job); 8304ad35181SKevin Wolf } 8314ad35181SKevin Wolf 832*afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 833*afe1e8a7SEmanuele Giuseppe Esposito static void job_conclude_locked(Job *job) 8344ad35181SKevin Wolf { 835*afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_CONCLUDED); 836*afe1e8a7SEmanuele Giuseppe Esposito if (job->auto_dismiss || !job_started_locked(job)) { 837*afe1e8a7SEmanuele Giuseppe Esposito job_do_dismiss_locked(job); 8384ad35181SKevin Wolf } 8394ad35181SKevin Wolf } 8404ad35181SKevin Wolf 841*afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 842*afe1e8a7SEmanuele Giuseppe Esposito static void job_update_rc_locked(Job *job) 8434ad35181SKevin Wolf { 844*afe1e8a7SEmanuele Giuseppe Esposito if (!job->ret && job_is_cancelled_locked(job)) { 8454ad35181SKevin Wolf job->ret = -ECANCELED; 8464ad35181SKevin Wolf } 8474ad35181SKevin Wolf if (job->ret) { 8483d1f8b07SJohn Snow if (!job->err) { 8493d1f8b07SJohn Snow error_setg(&job->err, "%s", strerror(-job->ret)); 8501266c9b9SKevin Wolf } 851*afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_ABORTING); 8524ad35181SKevin Wolf } 8534ad35181SKevin Wolf } 8544ad35181SKevin Wolf 8554ad35181SKevin Wolf static void job_commit(Job *job) 8564ad35181SKevin Wolf { 8574ad35181SKevin Wolf assert(!job->ret); 858c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 8594ad35181SKevin Wolf if (job->driver->commit) { 8604ad35181SKevin Wolf job->driver->commit(job); 8614ad35181SKevin Wolf } 8624ad35181SKevin Wolf } 8634ad35181SKevin Wolf 8644ad35181SKevin Wolf static void job_abort(Job *job) 8654ad35181SKevin Wolf { 8664ad35181SKevin Wolf assert(job->ret); 867c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 8684ad35181SKevin Wolf if (job->driver->abort) { 8694ad35181SKevin Wolf job->driver->abort(job); 8704ad35181SKevin Wolf } 8714ad35181SKevin Wolf } 8724ad35181SKevin Wolf 8734ad35181SKevin Wolf static void job_clean(Job *job) 8744ad35181SKevin Wolf { 875c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 8764ad35181SKevin Wolf if (job->driver->clean) { 8774ad35181SKevin Wolf job->driver->clean(job); 8784ad35181SKevin Wolf } 8794ad35181SKevin Wolf } 8804ad35181SKevin Wolf 881*afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily */ 882*afe1e8a7SEmanuele Giuseppe Esposito static int job_finalize_single_locked(Job *job) 8834ad35181SKevin Wolf { 884*afe1e8a7SEmanuele Giuseppe Esposito int job_ret; 885*afe1e8a7SEmanuele Giuseppe Esposito 886*afe1e8a7SEmanuele Giuseppe Esposito assert(job_is_completed_locked(job)); 8874ad35181SKevin Wolf 8884ad35181SKevin Wolf /* Ensure abort is called for late-transactional failures */ 889*afe1e8a7SEmanuele Giuseppe Esposito job_update_rc_locked(job); 8904ad35181SKevin Wolf 891*afe1e8a7SEmanuele Giuseppe Esposito job_ret = job->ret; 892*afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 893*afe1e8a7SEmanuele Giuseppe Esposito 894*afe1e8a7SEmanuele Giuseppe Esposito if (!job_ret) { 8954ad35181SKevin Wolf job_commit(job); 8964ad35181SKevin Wolf } else { 8974ad35181SKevin Wolf job_abort(job); 8984ad35181SKevin Wolf } 8994ad35181SKevin Wolf job_clean(job); 9004ad35181SKevin Wolf 901*afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 902*afe1e8a7SEmanuele Giuseppe Esposito 9034ad35181SKevin Wolf if (job->cb) { 904*afe1e8a7SEmanuele Giuseppe Esposito job_ret = job->ret; 905*afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 906*afe1e8a7SEmanuele Giuseppe Esposito job->cb(job->opaque, job_ret); 907*afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 9084ad35181SKevin Wolf } 9094ad35181SKevin Wolf 9104ad35181SKevin Wolf /* Emit events only if we actually started */ 911*afe1e8a7SEmanuele Giuseppe Esposito if (job_started_locked(job)) { 912*afe1e8a7SEmanuele Giuseppe Esposito if (job_is_cancelled_locked(job)) { 913*afe1e8a7SEmanuele Giuseppe Esposito job_event_cancelled_locked(job); 9144ad35181SKevin Wolf } else { 915*afe1e8a7SEmanuele Giuseppe Esposito job_event_completed_locked(job); 9164ad35181SKevin Wolf } 9174ad35181SKevin Wolf } 9184ad35181SKevin Wolf 919*afe1e8a7SEmanuele Giuseppe Esposito job_txn_del_job_locked(job); 920*afe1e8a7SEmanuele Giuseppe Esposito job_conclude_locked(job); 9214ad35181SKevin Wolf return 0; 9224ad35181SKevin Wolf } 9234ad35181SKevin Wolf 924*afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily */ 925*afe1e8a7SEmanuele Giuseppe Esposito static void job_cancel_async_locked(Job *job, bool force) 9267eaa8fb5SKevin Wolf { 927c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 9289820933bSVladimir Sementsov-Ogievskiy if (job->driver->cancel) { 929*afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 93073895f38SHanna Reitz force = job->driver->cancel(job, force); 931*afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 93273895f38SHanna Reitz } else { 93373895f38SHanna Reitz /* No .cancel() means the job will behave as if force-cancelled */ 93473895f38SHanna Reitz force = true; 9359820933bSVladimir Sementsov-Ogievskiy } 93673895f38SHanna Reitz 9377eaa8fb5SKevin Wolf if (job->user_paused) { 9387eaa8fb5SKevin Wolf /* Do not call job_enter here, the caller will handle it. */ 9397eaa8fb5SKevin Wolf if (job->driver->user_resume) { 940*afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 9417eaa8fb5SKevin Wolf job->driver->user_resume(job); 942*afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 9437eaa8fb5SKevin Wolf } 944e321c059SJeff Cody job->user_paused = false; 9457eaa8fb5SKevin Wolf assert(job->pause_count > 0); 9467eaa8fb5SKevin Wolf job->pause_count--; 9477eaa8fb5SKevin Wolf } 948401dd096SHanna Reitz 949401dd096SHanna Reitz /* 950401dd096SHanna Reitz * Ignore soft cancel requests after the job is already done 951401dd096SHanna Reitz * (We will still invoke job->driver->cancel() above, but if the 952401dd096SHanna Reitz * job driver supports soft cancelling and the job is done, that 953401dd096SHanna Reitz * should be a no-op, too. We still call it so it can override 954401dd096SHanna Reitz * @force.) 955401dd096SHanna Reitz */ 956401dd096SHanna Reitz if (force || !job->deferred_to_main_loop) { 9577eaa8fb5SKevin Wolf job->cancelled = true; 9587eaa8fb5SKevin Wolf /* To prevent 'force == false' overriding a previous 'force == true' */ 9597eaa8fb5SKevin Wolf job->force_cancel |= force; 9607eaa8fb5SKevin Wolf } 961401dd096SHanna Reitz } 9627eaa8fb5SKevin Wolf 963*afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily. */ 964*afe1e8a7SEmanuele Giuseppe Esposito static void job_completed_txn_abort_locked(Job *job) 9657eaa8fb5SKevin Wolf { 9667eaa8fb5SKevin Wolf AioContext *ctx; 9677eaa8fb5SKevin Wolf JobTxn *txn = job->txn; 9687eaa8fb5SKevin Wolf Job *other_job; 9697eaa8fb5SKevin Wolf 9707eaa8fb5SKevin Wolf if (txn->aborting) { 9717eaa8fb5SKevin Wolf /* 9727eaa8fb5SKevin Wolf * We are cancelled by another job, which will handle everything. 9737eaa8fb5SKevin Wolf */ 9747eaa8fb5SKevin Wolf return; 9757eaa8fb5SKevin Wolf } 9767eaa8fb5SKevin Wolf txn->aborting = true; 977*afe1e8a7SEmanuele Giuseppe Esposito job_txn_ref_locked(txn); 9787eaa8fb5SKevin Wolf 979d4311314SHanna Reitz /* 980d4311314SHanna Reitz * We can only hold the single job's AioContext lock while calling 981644f3a29SKevin Wolf * job_finalize_single() because the finalization callbacks can involve 982d4311314SHanna Reitz * calls of AIO_WAIT_WHILE(), which could deadlock otherwise. 983d4311314SHanna Reitz * Note that the job's AioContext may change when it is finalized. 984d4311314SHanna Reitz */ 985*afe1e8a7SEmanuele Giuseppe Esposito job_ref_locked(job); 986d4311314SHanna Reitz aio_context_release(job->aio_context); 9877eaa8fb5SKevin Wolf 9887eaa8fb5SKevin Wolf /* Other jobs are effectively cancelled by us, set the status for 9897eaa8fb5SKevin Wolf * them; this job, however, may or may not be cancelled, depending 9907eaa8fb5SKevin Wolf * on the caller, so leave it. */ 9917eaa8fb5SKevin Wolf QLIST_FOREACH(other_job, &txn->jobs, txn_list) { 9927eaa8fb5SKevin Wolf if (other_job != job) { 993644f3a29SKevin Wolf ctx = other_job->aio_context; 994644f3a29SKevin Wolf aio_context_acquire(ctx); 9951d4a43e9SHanna Reitz /* 9961d4a43e9SHanna Reitz * This is a transaction: If one job failed, no result will matter. 9971d4a43e9SHanna Reitz * Therefore, pass force=true to terminate all other jobs as quickly 9981d4a43e9SHanna Reitz * as possible. 9991d4a43e9SHanna Reitz */ 1000*afe1e8a7SEmanuele Giuseppe Esposito job_cancel_async_locked(other_job, true); 1001644f3a29SKevin Wolf aio_context_release(ctx); 10027eaa8fb5SKevin Wolf } 10037eaa8fb5SKevin Wolf } 10047eaa8fb5SKevin Wolf while (!QLIST_EMPTY(&txn->jobs)) { 10057eaa8fb5SKevin Wolf other_job = QLIST_FIRST(&txn->jobs); 1006d4311314SHanna Reitz /* 1007d4311314SHanna Reitz * The job's AioContext may change, so store it in @ctx so we 1008d4311314SHanna Reitz * release the same context that we have acquired before. 1009d4311314SHanna Reitz */ 10107eaa8fb5SKevin Wolf ctx = other_job->aio_context; 1011644f3a29SKevin Wolf aio_context_acquire(ctx); 1012*afe1e8a7SEmanuele Giuseppe Esposito if (!job_is_completed_locked(other_job)) { 1013*afe1e8a7SEmanuele Giuseppe Esposito assert(job_cancel_requested_locked(other_job)); 1014*afe1e8a7SEmanuele Giuseppe Esposito job_finish_sync_locked(other_job, NULL, NULL); 10157eaa8fb5SKevin Wolf } 1016*afe1e8a7SEmanuele Giuseppe Esposito job_finalize_single_locked(other_job); 10177eaa8fb5SKevin Wolf aio_context_release(ctx); 10187eaa8fb5SKevin Wolf } 10197eaa8fb5SKevin Wolf 1020d4311314SHanna Reitz /* 1021d4311314SHanna Reitz * Use job_ref()/job_unref() so we can read the AioContext here 1022d4311314SHanna Reitz * even if the job went away during job_finalize_single(). 1023d4311314SHanna Reitz */ 1024d4311314SHanna Reitz aio_context_acquire(job->aio_context); 1025*afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 1026644f3a29SKevin Wolf 1027*afe1e8a7SEmanuele Giuseppe Esposito job_txn_unref_locked(txn); 10287eaa8fb5SKevin Wolf } 10297eaa8fb5SKevin Wolf 1030*afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily */ 1031*afe1e8a7SEmanuele Giuseppe Esposito static int job_prepare_locked(Job *job) 10327eaa8fb5SKevin Wolf { 1033*afe1e8a7SEmanuele Giuseppe Esposito int ret; 1034*afe1e8a7SEmanuele Giuseppe Esposito 1035c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 10367eaa8fb5SKevin Wolf if (job->ret == 0 && job->driver->prepare) { 1037*afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 1038*afe1e8a7SEmanuele Giuseppe Esposito ret = job->driver->prepare(job); 1039*afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 1040*afe1e8a7SEmanuele Giuseppe Esposito job->ret = ret; 1041*afe1e8a7SEmanuele Giuseppe Esposito job_update_rc_locked(job); 10427eaa8fb5SKevin Wolf } 10437eaa8fb5SKevin Wolf return job->ret; 10447eaa8fb5SKevin Wolf } 10457eaa8fb5SKevin Wolf 1046*afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held */ 1047*afe1e8a7SEmanuele Giuseppe Esposito static int job_needs_finalize_locked(Job *job) 10487eaa8fb5SKevin Wolf { 10497eaa8fb5SKevin Wolf return !job->auto_finalize; 10507eaa8fb5SKevin Wolf } 10517eaa8fb5SKevin Wolf 1052*afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held */ 1053*afe1e8a7SEmanuele Giuseppe Esposito static void job_do_finalize_locked(Job *job) 10547eaa8fb5SKevin Wolf { 10557eaa8fb5SKevin Wolf int rc; 10567eaa8fb5SKevin Wolf assert(job && job->txn); 10577eaa8fb5SKevin Wolf 10587eaa8fb5SKevin Wolf /* prepare the transaction to complete */ 1059*afe1e8a7SEmanuele Giuseppe Esposito rc = job_txn_apply_locked(job, job_prepare_locked); 10607eaa8fb5SKevin Wolf if (rc) { 1061*afe1e8a7SEmanuele Giuseppe Esposito job_completed_txn_abort_locked(job); 10627eaa8fb5SKevin Wolf } else { 1063*afe1e8a7SEmanuele Giuseppe Esposito job_txn_apply_locked(job, job_finalize_single_locked); 10647eaa8fb5SKevin Wolf } 10657eaa8fb5SKevin Wolf } 10667eaa8fb5SKevin Wolf 1067*afe1e8a7SEmanuele Giuseppe Esposito void job_finalize_locked(Job *job, Error **errp) 1068*afe1e8a7SEmanuele Giuseppe Esposito { 1069*afe1e8a7SEmanuele Giuseppe Esposito assert(job && job->id); 1070*afe1e8a7SEmanuele Giuseppe Esposito if (job_apply_verb_locked(job, JOB_VERB_FINALIZE, errp)) { 1071*afe1e8a7SEmanuele Giuseppe Esposito return; 1072*afe1e8a7SEmanuele Giuseppe Esposito } 1073*afe1e8a7SEmanuele Giuseppe Esposito job_do_finalize_locked(job); 1074*afe1e8a7SEmanuele Giuseppe Esposito } 1075*afe1e8a7SEmanuele Giuseppe Esposito 10767eaa8fb5SKevin Wolf void job_finalize(Job *job, Error **errp) 10777eaa8fb5SKevin Wolf { 1078*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1079*afe1e8a7SEmanuele Giuseppe Esposito job_finalize_locked(job, errp); 10807eaa8fb5SKevin Wolf } 10817eaa8fb5SKevin Wolf 1082*afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 1083*afe1e8a7SEmanuele Giuseppe Esposito static int job_transition_to_pending_locked(Job *job) 10847eaa8fb5SKevin Wolf { 1085*afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_PENDING); 10867eaa8fb5SKevin Wolf if (!job->auto_finalize) { 1087*afe1e8a7SEmanuele Giuseppe Esposito job_event_pending_locked(job); 10887eaa8fb5SKevin Wolf } 10897eaa8fb5SKevin Wolf return 0; 10907eaa8fb5SKevin Wolf } 10917eaa8fb5SKevin Wolf 10922e1795b5SKevin Wolf void job_transition_to_ready(Job *job) 10932e1795b5SKevin Wolf { 1094*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1095*afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_READY); 1096*afe1e8a7SEmanuele Giuseppe Esposito job_event_ready_locked(job); 10972e1795b5SKevin Wolf } 10982e1795b5SKevin Wolf 1099*afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 1100*afe1e8a7SEmanuele Giuseppe Esposito static void job_completed_txn_success_locked(Job *job) 11017eaa8fb5SKevin Wolf { 11027eaa8fb5SKevin Wolf JobTxn *txn = job->txn; 11037eaa8fb5SKevin Wolf Job *other_job; 11047eaa8fb5SKevin Wolf 1105*afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_WAITING); 11067eaa8fb5SKevin Wolf 11077eaa8fb5SKevin Wolf /* 11087eaa8fb5SKevin Wolf * Successful completion, see if there are other running jobs in this 11097eaa8fb5SKevin Wolf * txn. 11107eaa8fb5SKevin Wolf */ 11117eaa8fb5SKevin Wolf QLIST_FOREACH(other_job, &txn->jobs, txn_list) { 1112*afe1e8a7SEmanuele Giuseppe Esposito if (!job_is_completed_locked(other_job)) { 11137eaa8fb5SKevin Wolf return; 11147eaa8fb5SKevin Wolf } 11157eaa8fb5SKevin Wolf assert(other_job->ret == 0); 11167eaa8fb5SKevin Wolf } 11177eaa8fb5SKevin Wolf 1118*afe1e8a7SEmanuele Giuseppe Esposito job_txn_apply_locked(job, job_transition_to_pending_locked); 11197eaa8fb5SKevin Wolf 11207eaa8fb5SKevin Wolf /* If no jobs need manual finalization, automatically do so */ 1121*afe1e8a7SEmanuele Giuseppe Esposito if (job_txn_apply_locked(job, job_needs_finalize_locked) == 0) { 1122*afe1e8a7SEmanuele Giuseppe Esposito job_do_finalize_locked(job); 11237eaa8fb5SKevin Wolf } 11247eaa8fb5SKevin Wolf } 11257eaa8fb5SKevin Wolf 1126*afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 1127*afe1e8a7SEmanuele Giuseppe Esposito static void job_completed_locked(Job *job) 11283d70ff53SKevin Wolf { 1129*afe1e8a7SEmanuele Giuseppe Esposito assert(job && job->txn && !job_is_completed_locked(job)); 11301266c9b9SKevin Wolf 1131*afe1e8a7SEmanuele Giuseppe Esposito job_update_rc_locked(job); 1132404ff28dSJohn Snow trace_job_completed(job, job->ret); 11333d70ff53SKevin Wolf if (job->ret) { 1134*afe1e8a7SEmanuele Giuseppe Esposito job_completed_txn_abort_locked(job); 11353d70ff53SKevin Wolf } else { 1136*afe1e8a7SEmanuele Giuseppe Esposito job_completed_txn_success_locked(job); 11373d70ff53SKevin Wolf } 11383d70ff53SKevin Wolf } 11393d70ff53SKevin Wolf 1140*afe1e8a7SEmanuele Giuseppe Esposito /** 1141*afe1e8a7SEmanuele Giuseppe Esposito * Useful only as a type shim for aio_bh_schedule_oneshot. 1142*afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex *not* held. 1143*afe1e8a7SEmanuele Giuseppe Esposito */ 1144ccbfb331SJohn Snow static void job_exit(void *opaque) 1145ccbfb331SJohn Snow { 1146ccbfb331SJohn Snow Job *job = (Job *)opaque; 1147b660a84bSStefan Reiter AioContext *ctx; 1148*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1149d1756c78SKevin Wolf 1150*afe1e8a7SEmanuele Giuseppe Esposito job_ref_locked(job); 1151b660a84bSStefan Reiter aio_context_acquire(job->aio_context); 1152b5a7a057SKevin Wolf 1153b5a7a057SKevin Wolf /* This is a lie, we're not quiescent, but still doing the completion 1154b5a7a057SKevin Wolf * callbacks. However, completion callbacks tend to involve operations that 1155b5a7a057SKevin Wolf * drain block nodes, and if .drained_poll still returned true, we would 1156b5a7a057SKevin Wolf * deadlock. */ 1157b5a7a057SKevin Wolf job->busy = false; 1158*afe1e8a7SEmanuele Giuseppe Esposito job_event_idle_locked(job); 1159b5a7a057SKevin Wolf 1160*afe1e8a7SEmanuele Giuseppe Esposito job_completed_locked(job); 1161b5a7a057SKevin Wolf 1162b660a84bSStefan Reiter /* 1163b660a84bSStefan Reiter * Note that calling job_completed can move the job to a different 1164b660a84bSStefan Reiter * aio_context, so we cannot cache from above. job_txn_apply takes care of 1165b660a84bSStefan Reiter * acquiring the new lock, and we ref/unref to avoid job_completed freeing 1166b660a84bSStefan Reiter * the job underneath us. 1167b660a84bSStefan Reiter */ 1168b660a84bSStefan Reiter ctx = job->aio_context; 1169*afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 1170d1756c78SKevin Wolf aio_context_release(ctx); 1171ccbfb331SJohn Snow } 1172ccbfb331SJohn Snow 1173ccbfb331SJohn Snow /** 1174ccbfb331SJohn Snow * All jobs must allow a pause point before entering their job proper. This 1175ccbfb331SJohn Snow * ensures that jobs can be paused prior to being started, then resumed later. 1176ccbfb331SJohn Snow */ 1177ccbfb331SJohn Snow static void coroutine_fn job_co_entry(void *opaque) 1178ccbfb331SJohn Snow { 1179ccbfb331SJohn Snow Job *job = opaque; 1180*afe1e8a7SEmanuele Giuseppe Esposito int ret; 1181ccbfb331SJohn Snow 1182ccbfb331SJohn Snow assert(job && job->driver && job->driver->run); 1183*afe1e8a7SEmanuele Giuseppe Esposito WITH_JOB_LOCK_GUARD() { 1184c70b8031SEmanuele Giuseppe Esposito assert(job->aio_context == qemu_get_current_aio_context()); 1185*afe1e8a7SEmanuele Giuseppe Esposito job_pause_point_locked(job); 1186*afe1e8a7SEmanuele Giuseppe Esposito } 1187*afe1e8a7SEmanuele Giuseppe Esposito ret = job->driver->run(job, &job->err); 1188*afe1e8a7SEmanuele Giuseppe Esposito WITH_JOB_LOCK_GUARD() { 1189*afe1e8a7SEmanuele Giuseppe Esposito job->ret = ret; 1190ccbfb331SJohn Snow job->deferred_to_main_loop = true; 1191b5a7a057SKevin Wolf job->busy = true; 1192*afe1e8a7SEmanuele Giuseppe Esposito } 1193ccbfb331SJohn Snow aio_bh_schedule_oneshot(qemu_get_aio_context(), job_exit, job); 1194ccbfb331SJohn Snow } 1195ccbfb331SJohn Snow 1196ccbfb331SJohn Snow void job_start(Job *job) 1197ccbfb331SJohn Snow { 1198*afe1e8a7SEmanuele Giuseppe Esposito assert(qemu_in_main_thread()); 1199*afe1e8a7SEmanuele Giuseppe Esposito 1200*afe1e8a7SEmanuele Giuseppe Esposito WITH_JOB_LOCK_GUARD() { 1201*afe1e8a7SEmanuele Giuseppe Esposito assert(job && !job_started_locked(job) && job->paused && 1202ccbfb331SJohn Snow job->driver && job->driver->run); 1203ccbfb331SJohn Snow job->co = qemu_coroutine_create(job_co_entry, job); 1204ccbfb331SJohn Snow job->pause_count--; 1205ccbfb331SJohn Snow job->busy = true; 1206ccbfb331SJohn Snow job->paused = false; 1207*afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_RUNNING); 1208*afe1e8a7SEmanuele Giuseppe Esposito } 1209ccbfb331SJohn Snow aio_co_enter(job->aio_context, job->co); 1210ccbfb331SJohn Snow } 1211ccbfb331SJohn Snow 1212*afe1e8a7SEmanuele Giuseppe Esposito void job_cancel_locked(Job *job, bool force) 12133d70ff53SKevin Wolf { 12143d70ff53SKevin Wolf if (job->status == JOB_STATUS_CONCLUDED) { 1215*afe1e8a7SEmanuele Giuseppe Esposito job_do_dismiss_locked(job); 12163d70ff53SKevin Wolf return; 12173d70ff53SKevin Wolf } 1218*afe1e8a7SEmanuele Giuseppe Esposito job_cancel_async_locked(job, force); 1219*afe1e8a7SEmanuele Giuseppe Esposito if (!job_started_locked(job)) { 1220*afe1e8a7SEmanuele Giuseppe Esposito job_completed_locked(job); 12213d70ff53SKevin Wolf } else if (job->deferred_to_main_loop) { 1222401dd096SHanna Reitz /* 1223401dd096SHanna Reitz * job_cancel_async() ignores soft-cancel requests for jobs 1224401dd096SHanna Reitz * that are already done (i.e. deferred to the main loop). We 1225401dd096SHanna Reitz * have to check again whether the job is really cancelled. 122608b83bffSHanna Reitz * (job_cancel_requested() and job_is_cancelled() are equivalent 122708b83bffSHanna Reitz * here, because job_cancel_async() will make soft-cancel 122808b83bffSHanna Reitz * requests no-ops when deferred_to_main_loop is true. We 122908b83bffSHanna Reitz * choose to call job_is_cancelled() to show that we invoke 123008b83bffSHanna Reitz * job_completed_txn_abort() only for force-cancelled jobs.) 1231401dd096SHanna Reitz */ 1232*afe1e8a7SEmanuele Giuseppe Esposito if (job_is_cancelled_locked(job)) { 1233*afe1e8a7SEmanuele Giuseppe Esposito job_completed_txn_abort_locked(job); 1234401dd096SHanna Reitz } 12353d70ff53SKevin Wolf } else { 1236*afe1e8a7SEmanuele Giuseppe Esposito job_enter_cond_locked(job, NULL); 12373d70ff53SKevin Wolf } 12383d70ff53SKevin Wolf } 12393d70ff53SKevin Wolf 1240*afe1e8a7SEmanuele Giuseppe Esposito void job_cancel(Job *job, bool force) 1241*afe1e8a7SEmanuele Giuseppe Esposito { 1242*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1243*afe1e8a7SEmanuele Giuseppe Esposito job_cancel_locked(job, force); 1244*afe1e8a7SEmanuele Giuseppe Esposito } 1245*afe1e8a7SEmanuele Giuseppe Esposito 1246*afe1e8a7SEmanuele Giuseppe Esposito void job_user_cancel_locked(Job *job, bool force, Error **errp) 1247*afe1e8a7SEmanuele Giuseppe Esposito { 1248*afe1e8a7SEmanuele Giuseppe Esposito if (job_apply_verb_locked(job, JOB_VERB_CANCEL, errp)) { 1249*afe1e8a7SEmanuele Giuseppe Esposito return; 1250*afe1e8a7SEmanuele Giuseppe Esposito } 1251*afe1e8a7SEmanuele Giuseppe Esposito job_cancel_locked(job, force); 1252*afe1e8a7SEmanuele Giuseppe Esposito } 1253*afe1e8a7SEmanuele Giuseppe Esposito 12543d70ff53SKevin Wolf void job_user_cancel(Job *job, bool force, Error **errp) 12553d70ff53SKevin Wolf { 1256*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1257*afe1e8a7SEmanuele Giuseppe Esposito job_user_cancel_locked(job, force, errp); 12583d70ff53SKevin Wolf } 12593d70ff53SKevin Wolf 12603d70ff53SKevin Wolf /* A wrapper around job_cancel() taking an Error ** parameter so it may be 12613d70ff53SKevin Wolf * used with job_finish_sync() without the need for (rather nasty) function 1262*afe1e8a7SEmanuele Giuseppe Esposito * pointer casts there. 1263*afe1e8a7SEmanuele Giuseppe Esposito * 1264*afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex held. 1265*afe1e8a7SEmanuele Giuseppe Esposito */ 1266*afe1e8a7SEmanuele Giuseppe Esposito static void job_cancel_err_locked(Job *job, Error **errp) 12673d70ff53SKevin Wolf { 1268*afe1e8a7SEmanuele Giuseppe Esposito job_cancel_locked(job, false); 12693d70ff53SKevin Wolf } 12703d70ff53SKevin Wolf 12714cfb3f05SHanna Reitz /** 12724cfb3f05SHanna Reitz * Same as job_cancel_err(), but force-cancel. 1273*afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex held. 12744cfb3f05SHanna Reitz */ 1275*afe1e8a7SEmanuele Giuseppe Esposito static void job_force_cancel_err_locked(Job *job, Error **errp) 12763d70ff53SKevin Wolf { 1277*afe1e8a7SEmanuele Giuseppe Esposito job_cancel_locked(job, true); 1278*afe1e8a7SEmanuele Giuseppe Esposito } 1279*afe1e8a7SEmanuele Giuseppe Esposito 1280*afe1e8a7SEmanuele Giuseppe Esposito int job_cancel_sync_locked(Job *job, bool force) 1281*afe1e8a7SEmanuele Giuseppe Esposito { 1282*afe1e8a7SEmanuele Giuseppe Esposito if (force) { 1283*afe1e8a7SEmanuele Giuseppe Esposito return job_finish_sync_locked(job, &job_force_cancel_err_locked, NULL); 1284*afe1e8a7SEmanuele Giuseppe Esposito } else { 1285*afe1e8a7SEmanuele Giuseppe Esposito return job_finish_sync_locked(job, &job_cancel_err_locked, NULL); 1286*afe1e8a7SEmanuele Giuseppe Esposito } 12874cfb3f05SHanna Reitz } 12884cfb3f05SHanna Reitz 12894cfb3f05SHanna Reitz int job_cancel_sync(Job *job, bool force) 12904cfb3f05SHanna Reitz { 1291*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1292*afe1e8a7SEmanuele Giuseppe Esposito return job_cancel_sync_locked(job, force); 12934cfb3f05SHanna Reitz } 12943d70ff53SKevin Wolf 12953d70ff53SKevin Wolf void job_cancel_sync_all(void) 12963d70ff53SKevin Wolf { 12973d70ff53SKevin Wolf Job *job; 12983d70ff53SKevin Wolf AioContext *aio_context; 1299*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 13003d70ff53SKevin Wolf 1301*afe1e8a7SEmanuele Giuseppe Esposito while ((job = job_next_locked(NULL))) { 13023d70ff53SKevin Wolf aio_context = job->aio_context; 13033d70ff53SKevin Wolf aio_context_acquire(aio_context); 1304*afe1e8a7SEmanuele Giuseppe Esposito job_cancel_sync_locked(job, true); 13053d70ff53SKevin Wolf aio_context_release(aio_context); 13063d70ff53SKevin Wolf } 13073d70ff53SKevin Wolf } 13083d70ff53SKevin Wolf 1309*afe1e8a7SEmanuele Giuseppe Esposito int job_complete_sync_locked(Job *job, Error **errp) 13103d70ff53SKevin Wolf { 1311*afe1e8a7SEmanuele Giuseppe Esposito return job_finish_sync_locked(job, job_complete_locked, errp); 13123d70ff53SKevin Wolf } 13133d70ff53SKevin Wolf 1314*afe1e8a7SEmanuele Giuseppe Esposito int job_complete_sync(Job *job, Error **errp) 1315*afe1e8a7SEmanuele Giuseppe Esposito { 1316*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1317*afe1e8a7SEmanuele Giuseppe Esposito return job_complete_sync_locked(job, errp); 1318*afe1e8a7SEmanuele Giuseppe Esposito } 1319*afe1e8a7SEmanuele Giuseppe Esposito 1320*afe1e8a7SEmanuele Giuseppe Esposito void job_complete_locked(Job *job, Error **errp) 13213453d972SKevin Wolf { 13223453d972SKevin Wolf /* Should not be reachable via external interface for internal jobs */ 13233453d972SKevin Wolf assert(job->id); 1324c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 1325*afe1e8a7SEmanuele Giuseppe Esposito if (job_apply_verb_locked(job, JOB_VERB_COMPLETE, errp)) { 13263453d972SKevin Wolf return; 13273453d972SKevin Wolf } 1328*afe1e8a7SEmanuele Giuseppe Esposito if (job_cancel_requested_locked(job) || !job->driver->complete) { 13293453d972SKevin Wolf error_setg(errp, "The active block job '%s' cannot be completed", 13303453d972SKevin Wolf job->id); 13313453d972SKevin Wolf return; 13323453d972SKevin Wolf } 13333453d972SKevin Wolf 1334*afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 13353453d972SKevin Wolf job->driver->complete(job, errp); 1336*afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 13373453d972SKevin Wolf } 13383453d972SKevin Wolf 1339*afe1e8a7SEmanuele Giuseppe Esposito void job_complete(Job *job, Error **errp) 1340*afe1e8a7SEmanuele Giuseppe Esposito { 1341*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1342*afe1e8a7SEmanuele Giuseppe Esposito job_complete_locked(job, errp); 1343*afe1e8a7SEmanuele Giuseppe Esposito } 1344*afe1e8a7SEmanuele Giuseppe Esposito 1345*afe1e8a7SEmanuele Giuseppe Esposito int job_finish_sync_locked(Job *job, 1346*afe1e8a7SEmanuele Giuseppe Esposito void (*finish)(Job *, Error **errp), 1347*afe1e8a7SEmanuele Giuseppe Esposito Error **errp) 13486a74c075SKevin Wolf { 13496a74c075SKevin Wolf Error *local_err = NULL; 13506a74c075SKevin Wolf int ret; 13516a74c075SKevin Wolf 1352*afe1e8a7SEmanuele Giuseppe Esposito job_ref_locked(job); 13536a74c075SKevin Wolf 13546a74c075SKevin Wolf if (finish) { 13556a74c075SKevin Wolf finish(job, &local_err); 13566a74c075SKevin Wolf } 13576a74c075SKevin Wolf if (local_err) { 13586a74c075SKevin Wolf error_propagate(errp, local_err); 1359*afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 13606a74c075SKevin Wolf return -EBUSY; 13616a74c075SKevin Wolf } 1362de0fbe64SKevin Wolf 1363*afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 1364cfe29d82SKevin Wolf AIO_WAIT_WHILE(job->aio_context, 1365bb0c9409SVladimir Sementsov-Ogievskiy (job_enter(job), !job_is_completed(job))); 1366*afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 1367de0fbe64SKevin Wolf 1368*afe1e8a7SEmanuele Giuseppe Esposito ret = (job_is_cancelled_locked(job) && job->ret == 0) 1369*afe1e8a7SEmanuele Giuseppe Esposito ? -ECANCELED : job->ret; 1370*afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 13716a74c075SKevin Wolf return ret; 13726a74c075SKevin Wolf } 1373*afe1e8a7SEmanuele Giuseppe Esposito 1374*afe1e8a7SEmanuele Giuseppe Esposito int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp) 1375*afe1e8a7SEmanuele Giuseppe Esposito { 1376*afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1377*afe1e8a7SEmanuele Giuseppe Esposito return job_finish_sync_locked(job, finish, errp); 1378*afe1e8a7SEmanuele Giuseppe Esposito } 1379