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 * TODO Actually make this true 49bf61c583SEmanuele Giuseppe Esposito */ 50bf61c583SEmanuele Giuseppe Esposito 51bf61c583SEmanuele Giuseppe Esposito /* 5255c5a25aSEmanuele Giuseppe Esposito * job_mutex protects the jobs list, but also makes the 5355c5a25aSEmanuele Giuseppe Esposito * struct job fields thread-safe. 5455c5a25aSEmanuele Giuseppe Esposito */ 5555c5a25aSEmanuele Giuseppe Esposito QemuMutex job_mutex; 5655c5a25aSEmanuele Giuseppe Esposito 57afe1e8a7SEmanuele Giuseppe Esposito /* Protected by job_mutex */ 58e7c1d78bSKevin Wolf static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs); 59e7c1d78bSKevin Wolf 60a50c2ab8SKevin Wolf /* Job State Transition Table */ 61a50c2ab8SKevin Wolf bool JobSTT[JOB_STATUS__MAX][JOB_STATUS__MAX] = { 62a50c2ab8SKevin Wolf /* U, C, R, P, Y, S, W, D, X, E, N */ 63a50c2ab8SKevin Wolf /* U: */ [JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 64a50c2ab8SKevin Wolf /* C: */ [JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1}, 65a50c2ab8SKevin Wolf /* R: */ [JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0}, 66a50c2ab8SKevin Wolf /* P: */ [JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, 67a50c2ab8SKevin Wolf /* Y: */ [JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0}, 68a50c2ab8SKevin Wolf /* S: */ [JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, 69a50c2ab8SKevin Wolf /* W: */ [JOB_STATUS_WAITING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0}, 70a50c2ab8SKevin Wolf /* D: */ [JOB_STATUS_PENDING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, 71a50c2ab8SKevin Wolf /* X: */ [JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, 72a50c2ab8SKevin Wolf /* E: */ [JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, 73a50c2ab8SKevin Wolf /* N: */ [JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 74a50c2ab8SKevin Wolf }; 75a50c2ab8SKevin Wolf 76a50c2ab8SKevin Wolf bool JobVerbTable[JOB_VERB__MAX][JOB_STATUS__MAX] = { 77a50c2ab8SKevin Wolf /* U, C, R, P, Y, S, W, D, X, E, N */ 78a50c2ab8SKevin Wolf [JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, 79a50c2ab8SKevin Wolf [JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, 80a50c2ab8SKevin Wolf [JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, 81a50c2ab8SKevin Wolf [JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, 8253ddb9c8SMax Reitz [JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0}, 83a50c2ab8SKevin Wolf [JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, 84a50c2ab8SKevin Wolf [JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, 85a50c2ab8SKevin Wolf }; 86a50c2ab8SKevin Wolf 877eaa8fb5SKevin Wolf /* Transactional group of jobs */ 887eaa8fb5SKevin Wolf struct JobTxn { 897eaa8fb5SKevin Wolf 907eaa8fb5SKevin Wolf /* Is this txn being cancelled? */ 917eaa8fb5SKevin Wolf bool aborting; 927eaa8fb5SKevin Wolf 937eaa8fb5SKevin Wolf /* List of jobs */ 947eaa8fb5SKevin Wolf QLIST_HEAD(, Job) jobs; 957eaa8fb5SKevin Wolf 967eaa8fb5SKevin Wolf /* Reference count */ 977eaa8fb5SKevin Wolf int refcnt; 987eaa8fb5SKevin Wolf }; 997eaa8fb5SKevin Wolf 10055c5a25aSEmanuele Giuseppe Esposito void job_lock(void) 10155c5a25aSEmanuele Giuseppe Esposito { 10255c5a25aSEmanuele Giuseppe Esposito /* nop */ 10355c5a25aSEmanuele Giuseppe Esposito } 104da01ff7fSKevin Wolf 10555c5a25aSEmanuele Giuseppe Esposito void job_unlock(void) 10655c5a25aSEmanuele Giuseppe Esposito { 10755c5a25aSEmanuele Giuseppe Esposito /* nop */ 10855c5a25aSEmanuele Giuseppe Esposito } 10955c5a25aSEmanuele Giuseppe Esposito 11055c5a25aSEmanuele Giuseppe Esposito static void real_job_lock(void) 111da01ff7fSKevin Wolf { 112da01ff7fSKevin Wolf qemu_mutex_lock(&job_mutex); 113da01ff7fSKevin Wolf } 114da01ff7fSKevin Wolf 11555c5a25aSEmanuele Giuseppe Esposito static void real_job_unlock(void) 116da01ff7fSKevin Wolf { 117da01ff7fSKevin Wolf qemu_mutex_unlock(&job_mutex); 118da01ff7fSKevin Wolf } 119da01ff7fSKevin Wolf 120da01ff7fSKevin Wolf static void __attribute__((__constructor__)) job_init(void) 121da01ff7fSKevin Wolf { 122da01ff7fSKevin Wolf qemu_mutex_init(&job_mutex); 123da01ff7fSKevin Wolf } 124da01ff7fSKevin Wolf 1257eaa8fb5SKevin Wolf JobTxn *job_txn_new(void) 1267eaa8fb5SKevin Wolf { 1277eaa8fb5SKevin Wolf JobTxn *txn = g_new0(JobTxn, 1); 1287eaa8fb5SKevin Wolf QLIST_INIT(&txn->jobs); 1297eaa8fb5SKevin Wolf txn->refcnt = 1; 1307eaa8fb5SKevin Wolf return txn; 1317eaa8fb5SKevin Wolf } 1327eaa8fb5SKevin Wolf 133afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 134afe1e8a7SEmanuele Giuseppe Esposito static void job_txn_ref_locked(JobTxn *txn) 1357eaa8fb5SKevin Wolf { 1367eaa8fb5SKevin Wolf txn->refcnt++; 1377eaa8fb5SKevin Wolf } 1387eaa8fb5SKevin Wolf 139afe1e8a7SEmanuele Giuseppe Esposito void job_txn_unref_locked(JobTxn *txn) 1407eaa8fb5SKevin Wolf { 1417eaa8fb5SKevin Wolf if (txn && --txn->refcnt == 0) { 1427eaa8fb5SKevin Wolf g_free(txn); 1437eaa8fb5SKevin Wolf } 1447eaa8fb5SKevin Wolf } 1457eaa8fb5SKevin Wolf 146afe1e8a7SEmanuele Giuseppe Esposito void job_txn_unref(JobTxn *txn) 147afe1e8a7SEmanuele Giuseppe Esposito { 148afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 149afe1e8a7SEmanuele Giuseppe Esposito job_txn_unref_locked(txn); 150afe1e8a7SEmanuele Giuseppe Esposito } 151afe1e8a7SEmanuele Giuseppe Esposito 152544f4d52SEmanuele Giuseppe Esposito /** 153544f4d52SEmanuele Giuseppe Esposito * @txn: The transaction (may be NULL) 154544f4d52SEmanuele Giuseppe Esposito * @job: Job to add to the transaction 155544f4d52SEmanuele Giuseppe Esposito * 156544f4d52SEmanuele Giuseppe Esposito * Add @job to the transaction. The @job must not already be in a transaction. 157544f4d52SEmanuele Giuseppe Esposito * The caller must call either job_txn_unref() or job_completed() to release 158544f4d52SEmanuele Giuseppe Esposito * the reference that is automatically grabbed here. 159544f4d52SEmanuele Giuseppe Esposito * 160544f4d52SEmanuele Giuseppe Esposito * If @txn is NULL, the function does nothing. 161afe1e8a7SEmanuele Giuseppe Esposito * 162afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex held. 163544f4d52SEmanuele Giuseppe Esposito */ 164afe1e8a7SEmanuele Giuseppe Esposito static void job_txn_add_job_locked(JobTxn *txn, Job *job) 1657eaa8fb5SKevin Wolf { 1667eaa8fb5SKevin Wolf if (!txn) { 1677eaa8fb5SKevin Wolf return; 1687eaa8fb5SKevin Wolf } 1697eaa8fb5SKevin Wolf 1707eaa8fb5SKevin Wolf assert(!job->txn); 1717eaa8fb5SKevin Wolf job->txn = txn; 1727eaa8fb5SKevin Wolf 1737eaa8fb5SKevin Wolf QLIST_INSERT_HEAD(&txn->jobs, job, txn_list); 174afe1e8a7SEmanuele Giuseppe Esposito job_txn_ref_locked(txn); 1757eaa8fb5SKevin Wolf } 1767eaa8fb5SKevin Wolf 177afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 178afe1e8a7SEmanuele Giuseppe Esposito static void job_txn_del_job_locked(Job *job) 1797eaa8fb5SKevin Wolf { 1807eaa8fb5SKevin Wolf if (job->txn) { 1817eaa8fb5SKevin Wolf QLIST_REMOVE(job, txn_list); 182afe1e8a7SEmanuele Giuseppe Esposito job_txn_unref_locked(job->txn); 1837eaa8fb5SKevin Wolf job->txn = NULL; 1847eaa8fb5SKevin Wolf } 1857eaa8fb5SKevin Wolf } 1867eaa8fb5SKevin Wolf 187afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily. */ 188afe1e8a7SEmanuele Giuseppe Esposito static int job_txn_apply_locked(Job *job, int fn(Job *)) 1897eaa8fb5SKevin Wolf { 190b660a84bSStefan Reiter AioContext *inner_ctx; 191b660a84bSStefan Reiter Job *other_job, *next; 192b660a84bSStefan Reiter JobTxn *txn = job->txn; 1937eaa8fb5SKevin Wolf int rc = 0; 1947eaa8fb5SKevin Wolf 195b660a84bSStefan Reiter /* 196b660a84bSStefan Reiter * Similar to job_completed_txn_abort, we take each job's lock before 197b660a84bSStefan Reiter * applying fn, but since we assume that outer_ctx is held by the caller, 198b660a84bSStefan Reiter * we need to release it here to avoid holding the lock twice - which would 199b660a84bSStefan Reiter * break AIO_WAIT_WHILE from within fn. 200b660a84bSStefan Reiter */ 201afe1e8a7SEmanuele Giuseppe Esposito job_ref_locked(job); 202b660a84bSStefan Reiter aio_context_release(job->aio_context); 203b660a84bSStefan Reiter 204b660a84bSStefan Reiter QLIST_FOREACH_SAFE(other_job, &txn->jobs, txn_list, next) { 205b660a84bSStefan Reiter inner_ctx = other_job->aio_context; 206b660a84bSStefan Reiter aio_context_acquire(inner_ctx); 207b660a84bSStefan Reiter rc = fn(other_job); 208b660a84bSStefan Reiter aio_context_release(inner_ctx); 2097eaa8fb5SKevin Wolf if (rc) { 2107eaa8fb5SKevin Wolf break; 2117eaa8fb5SKevin Wolf } 2127eaa8fb5SKevin Wolf } 213b660a84bSStefan Reiter 214b660a84bSStefan Reiter /* 215b660a84bSStefan Reiter * Note that job->aio_context might have been changed by calling fn, so we 216b660a84bSStefan Reiter * can't use a local variable to cache it. 217b660a84bSStefan Reiter */ 218b660a84bSStefan Reiter aio_context_acquire(job->aio_context); 219afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 2207eaa8fb5SKevin Wolf return rc; 2217eaa8fb5SKevin Wolf } 2227eaa8fb5SKevin Wolf 223456273b0SKevin Wolf bool job_is_internal(Job *job) 2241dac83f1SKevin Wolf { 2251dac83f1SKevin Wolf return (job->id == NULL); 2261dac83f1SKevin Wolf } 2271dac83f1SKevin Wolf 228afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 229afe1e8a7SEmanuele Giuseppe Esposito static void job_state_transition_locked(Job *job, JobStatus s1) 230a50c2ab8SKevin Wolf { 231a50c2ab8SKevin Wolf JobStatus s0 = job->status; 232c2032289SLiam Merwick assert(s1 >= 0 && s1 < JOB_STATUS__MAX); 2334ad35181SKevin Wolf trace_job_state_transition(job, job->ret, 234a50c2ab8SKevin Wolf JobSTT[s0][s1] ? "allowed" : "disallowed", 235a50c2ab8SKevin Wolf JobStatus_str(s0), JobStatus_str(s1)); 236a50c2ab8SKevin Wolf assert(JobSTT[s0][s1]); 237a50c2ab8SKevin Wolf job->status = s1; 2381dac83f1SKevin Wolf 2391dac83f1SKevin Wolf if (!job_is_internal(job) && s1 != s0) { 2403ab72385SPeter Xu qapi_event_send_job_status_change(job->id, job->status); 2411dac83f1SKevin Wolf } 242a50c2ab8SKevin Wolf } 243a50c2ab8SKevin Wolf 244afe1e8a7SEmanuele Giuseppe Esposito int job_apply_verb_locked(Job *job, JobVerb verb, Error **errp) 245a50c2ab8SKevin Wolf { 246a50c2ab8SKevin Wolf JobStatus s0 = job->status; 247c2032289SLiam Merwick assert(verb >= 0 && verb < JOB_VERB__MAX); 248a50c2ab8SKevin Wolf trace_job_apply_verb(job, JobStatus_str(s0), JobVerb_str(verb), 249a50c2ab8SKevin Wolf JobVerbTable[verb][s0] ? "allowed" : "prohibited"); 250a50c2ab8SKevin Wolf if (JobVerbTable[verb][s0]) { 251a50c2ab8SKevin Wolf return 0; 252a50c2ab8SKevin Wolf } 253a50c2ab8SKevin Wolf error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'", 254a50c2ab8SKevin Wolf job->id, JobStatus_str(s0), JobVerb_str(verb)); 255a50c2ab8SKevin Wolf return -EPERM; 256a50c2ab8SKevin Wolf } 257a50c2ab8SKevin Wolf 258afe1e8a7SEmanuele Giuseppe Esposito int job_apply_verb(Job *job, JobVerb verb, Error **errp) 259afe1e8a7SEmanuele Giuseppe Esposito { 260afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 261afe1e8a7SEmanuele Giuseppe Esposito return job_apply_verb_locked(job, verb, errp); 262afe1e8a7SEmanuele Giuseppe Esposito } 263afe1e8a7SEmanuele Giuseppe Esposito 264252291eaSKevin Wolf JobType job_type(const Job *job) 265252291eaSKevin Wolf { 266252291eaSKevin Wolf return job->driver->job_type; 267252291eaSKevin Wolf } 268252291eaSKevin Wolf 269252291eaSKevin Wolf const char *job_type_str(const Job *job) 270252291eaSKevin Wolf { 271252291eaSKevin Wolf return JobType_str(job_type(job)); 272252291eaSKevin Wolf } 273252291eaSKevin Wolf 274afe1e8a7SEmanuele Giuseppe Esposito bool job_is_cancelled_locked(Job *job) 275daa7f2f9SKevin Wolf { 276a640fa0eSHanna Reitz /* force_cancel may be true only if cancelled is true, too */ 277a640fa0eSHanna Reitz assert(job->cancelled || !job->force_cancel); 278a640fa0eSHanna Reitz return job->force_cancel; 27908b83bffSHanna Reitz } 28008b83bffSHanna Reitz 281afe1e8a7SEmanuele Giuseppe Esposito bool job_is_cancelled(Job *job) 282afe1e8a7SEmanuele Giuseppe Esposito { 283afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 284afe1e8a7SEmanuele Giuseppe Esposito return job_is_cancelled_locked(job); 285afe1e8a7SEmanuele Giuseppe Esposito } 286afe1e8a7SEmanuele Giuseppe Esposito 287afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 288afe1e8a7SEmanuele Giuseppe Esposito static bool job_cancel_requested_locked(Job *job) 28908b83bffSHanna Reitz { 290daa7f2f9SKevin Wolf return job->cancelled; 291daa7f2f9SKevin Wolf } 292daa7f2f9SKevin Wolf 293afe1e8a7SEmanuele Giuseppe Esposito bool job_cancel_requested(Job *job) 294afe1e8a7SEmanuele Giuseppe Esposito { 295afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 296afe1e8a7SEmanuele Giuseppe Esposito return job_cancel_requested_locked(job); 297afe1e8a7SEmanuele Giuseppe Esposito } 298afe1e8a7SEmanuele Giuseppe Esposito 299afe1e8a7SEmanuele Giuseppe Esposito bool job_is_ready_locked(Job *job) 300df956ae2SKevin Wolf { 301df956ae2SKevin Wolf switch (job->status) { 302df956ae2SKevin Wolf case JOB_STATUS_UNDEFINED: 303df956ae2SKevin Wolf case JOB_STATUS_CREATED: 304df956ae2SKevin Wolf case JOB_STATUS_RUNNING: 305df956ae2SKevin Wolf case JOB_STATUS_PAUSED: 306df956ae2SKevin Wolf case JOB_STATUS_WAITING: 307df956ae2SKevin Wolf case JOB_STATUS_PENDING: 308df956ae2SKevin Wolf case JOB_STATUS_ABORTING: 309df956ae2SKevin Wolf case JOB_STATUS_CONCLUDED: 310df956ae2SKevin Wolf case JOB_STATUS_NULL: 311df956ae2SKevin Wolf return false; 312df956ae2SKevin Wolf case JOB_STATUS_READY: 313df956ae2SKevin Wolf case JOB_STATUS_STANDBY: 314df956ae2SKevin Wolf return true; 315df956ae2SKevin Wolf default: 316df956ae2SKevin Wolf g_assert_not_reached(); 317df956ae2SKevin Wolf } 318df956ae2SKevin Wolf return false; 319df956ae2SKevin Wolf } 320df956ae2SKevin Wolf 321afe1e8a7SEmanuele Giuseppe Esposito bool job_is_ready(Job *job) 322afe1e8a7SEmanuele Giuseppe Esposito { 323afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 324afe1e8a7SEmanuele Giuseppe Esposito return job_is_ready_locked(job); 325afe1e8a7SEmanuele Giuseppe Esposito } 326afe1e8a7SEmanuele Giuseppe Esposito 327afe1e8a7SEmanuele Giuseppe Esposito bool job_is_completed_locked(Job *job) 328afe1e8a7SEmanuele Giuseppe Esposito { 329afe1e8a7SEmanuele Giuseppe Esposito switch (job->status) { 330afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_UNDEFINED: 331afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_CREATED: 332afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_RUNNING: 333afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_PAUSED: 334afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_READY: 335afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_STANDBY: 336afe1e8a7SEmanuele Giuseppe Esposito return false; 337afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_WAITING: 338afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_PENDING: 339afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_ABORTING: 340afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_CONCLUDED: 341afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_NULL: 342afe1e8a7SEmanuele Giuseppe Esposito return true; 343afe1e8a7SEmanuele Giuseppe Esposito default: 344afe1e8a7SEmanuele Giuseppe Esposito g_assert_not_reached(); 345afe1e8a7SEmanuele Giuseppe Esposito } 346afe1e8a7SEmanuele Giuseppe Esposito return false; 347afe1e8a7SEmanuele Giuseppe Esposito } 348afe1e8a7SEmanuele Giuseppe Esposito 349dbe5e6c1SKevin Wolf bool job_is_completed(Job *job) 350dbe5e6c1SKevin Wolf { 351afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 352afe1e8a7SEmanuele Giuseppe Esposito return job_is_completed_locked(job); 353dbe5e6c1SKevin Wolf } 354dbe5e6c1SKevin Wolf 355afe1e8a7SEmanuele Giuseppe Esposito static bool job_started_locked(Job *job) 356da01ff7fSKevin Wolf { 357da01ff7fSKevin Wolf return job->co; 358da01ff7fSKevin Wolf } 359da01ff7fSKevin Wolf 360afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 361afe1e8a7SEmanuele Giuseppe Esposito static bool job_should_pause_locked(Job *job) 362da01ff7fSKevin Wolf { 363da01ff7fSKevin Wolf return job->pause_count > 0; 364da01ff7fSKevin Wolf } 365da01ff7fSKevin Wolf 366afe1e8a7SEmanuele Giuseppe Esposito Job *job_next_locked(Job *job) 367e7c1d78bSKevin Wolf { 368e7c1d78bSKevin Wolf if (!job) { 369e7c1d78bSKevin Wolf return QLIST_FIRST(&jobs); 370e7c1d78bSKevin Wolf } 371e7c1d78bSKevin Wolf return QLIST_NEXT(job, job_list); 372e7c1d78bSKevin Wolf } 373e7c1d78bSKevin Wolf 374afe1e8a7SEmanuele Giuseppe Esposito Job *job_next(Job *job) 375afe1e8a7SEmanuele Giuseppe Esposito { 376afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 377afe1e8a7SEmanuele Giuseppe Esposito return job_next_locked(job); 378afe1e8a7SEmanuele Giuseppe Esposito } 379afe1e8a7SEmanuele Giuseppe Esposito 380afe1e8a7SEmanuele Giuseppe Esposito Job *job_get_locked(const char *id) 381e7c1d78bSKevin Wolf { 382e7c1d78bSKevin Wolf Job *job; 383e7c1d78bSKevin Wolf 384e7c1d78bSKevin Wolf QLIST_FOREACH(job, &jobs, job_list) { 385e7c1d78bSKevin Wolf if (job->id && !strcmp(id, job->id)) { 386e7c1d78bSKevin Wolf return job; 387e7c1d78bSKevin Wolf } 388e7c1d78bSKevin Wolf } 389e7c1d78bSKevin Wolf 390e7c1d78bSKevin Wolf return NULL; 391e7c1d78bSKevin Wolf } 392e7c1d78bSKevin Wolf 393afe1e8a7SEmanuele Giuseppe Esposito Job *job_get(const char *id) 394afe1e8a7SEmanuele Giuseppe Esposito { 395afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 396afe1e8a7SEmanuele Giuseppe Esposito return job_get_locked(id); 397afe1e8a7SEmanuele Giuseppe Esposito } 398afe1e8a7SEmanuele Giuseppe Esposito 399afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex *not* held. */ 4005d43e86eSKevin Wolf static void job_sleep_timer_cb(void *opaque) 4015d43e86eSKevin Wolf { 4025d43e86eSKevin Wolf Job *job = opaque; 4035d43e86eSKevin Wolf 4045d43e86eSKevin Wolf job_enter(job); 4055d43e86eSKevin Wolf } 4065d43e86eSKevin Wolf 4077eaa8fb5SKevin Wolf void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn, 4087eaa8fb5SKevin Wolf AioContext *ctx, int flags, BlockCompletionFunc *cb, 4097eaa8fb5SKevin Wolf void *opaque, Error **errp) 41033e9e9bdSKevin Wolf { 41133e9e9bdSKevin Wolf Job *job; 41233e9e9bdSKevin Wolf 413afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 414afe1e8a7SEmanuele Giuseppe Esposito 41533e9e9bdSKevin Wolf if (job_id) { 416bb02b65cSKevin Wolf if (flags & JOB_INTERNAL) { 417bb02b65cSKevin Wolf error_setg(errp, "Cannot specify job ID for internal job"); 418bb02b65cSKevin Wolf return NULL; 419bb02b65cSKevin Wolf } 42033e9e9bdSKevin Wolf if (!id_wellformed(job_id)) { 42133e9e9bdSKevin Wolf error_setg(errp, "Invalid job ID '%s'", job_id); 42233e9e9bdSKevin Wolf return NULL; 42333e9e9bdSKevin Wolf } 424afe1e8a7SEmanuele Giuseppe Esposito if (job_get_locked(job_id)) { 425e7c1d78bSKevin Wolf error_setg(errp, "Job ID '%s' already in use", job_id); 426e7c1d78bSKevin Wolf return NULL; 427e7c1d78bSKevin Wolf } 428bb02b65cSKevin Wolf } else if (!(flags & JOB_INTERNAL)) { 429bb02b65cSKevin Wolf error_setg(errp, "An explicit job ID is required"); 430bb02b65cSKevin Wolf return NULL; 43133e9e9bdSKevin Wolf } 43233e9e9bdSKevin Wolf 43333e9e9bdSKevin Wolf job = g_malloc0(driver->instance_size); 43433e9e9bdSKevin Wolf job->driver = driver; 43533e9e9bdSKevin Wolf job->id = g_strdup(job_id); 43680fa2c75SKevin Wolf job->refcnt = 1; 43708be6fe2SKevin Wolf job->aio_context = ctx; 438da01ff7fSKevin Wolf job->busy = false; 439da01ff7fSKevin Wolf job->paused = true; 440da01ff7fSKevin Wolf job->pause_count = 1; 441bb02b65cSKevin Wolf job->auto_finalize = !(flags & JOB_MANUAL_FINALIZE); 442bb02b65cSKevin Wolf job->auto_dismiss = !(flags & JOB_MANUAL_DISMISS); 4434ad35181SKevin Wolf job->cb = cb; 4444ad35181SKevin Wolf job->opaque = opaque; 44533e9e9bdSKevin Wolf 446a7b4f8fcSEmanuele Giuseppe Esposito progress_init(&job->progress); 447a7b4f8fcSEmanuele Giuseppe Esposito 448139a9f02SKevin Wolf notifier_list_init(&job->on_finalize_cancelled); 449139a9f02SKevin Wolf notifier_list_init(&job->on_finalize_completed); 450139a9f02SKevin Wolf notifier_list_init(&job->on_pending); 4512e1795b5SKevin Wolf notifier_list_init(&job->on_ready); 452252f4091SEmanuele Giuseppe Esposito notifier_list_init(&job->on_idle); 453139a9f02SKevin Wolf 454afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_CREATED); 4555d43e86eSKevin Wolf aio_timer_init(qemu_get_aio_context(), &job->sleep_timer, 4565d43e86eSKevin Wolf QEMU_CLOCK_REALTIME, SCALE_NS, 4575d43e86eSKevin Wolf job_sleep_timer_cb, job); 458a50c2ab8SKevin Wolf 459e7c1d78bSKevin Wolf QLIST_INSERT_HEAD(&jobs, job, job_list); 460e7c1d78bSKevin Wolf 4617eaa8fb5SKevin Wolf /* Single jobs are modeled as single-job transactions for sake of 4627eaa8fb5SKevin Wolf * consolidating the job management logic */ 4637eaa8fb5SKevin Wolf if (!txn) { 4647eaa8fb5SKevin Wolf txn = job_txn_new(); 465afe1e8a7SEmanuele Giuseppe Esposito job_txn_add_job_locked(txn, job); 466afe1e8a7SEmanuele Giuseppe Esposito job_txn_unref_locked(txn); 4677eaa8fb5SKevin Wolf } else { 468afe1e8a7SEmanuele Giuseppe Esposito job_txn_add_job_locked(txn, job); 4697eaa8fb5SKevin Wolf } 4707eaa8fb5SKevin Wolf 47133e9e9bdSKevin Wolf return job; 47233e9e9bdSKevin Wolf } 473fd61a701SKevin Wolf 474afe1e8a7SEmanuele Giuseppe Esposito void job_ref_locked(Job *job) 475fd61a701SKevin Wolf { 47680fa2c75SKevin Wolf ++job->refcnt; 47780fa2c75SKevin Wolf } 47880fa2c75SKevin Wolf 479afe1e8a7SEmanuele Giuseppe Esposito void job_ref(Job *job) 480afe1e8a7SEmanuele Giuseppe Esposito { 481afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 482afe1e8a7SEmanuele Giuseppe Esposito job_ref_locked(job); 483afe1e8a7SEmanuele Giuseppe Esposito } 484afe1e8a7SEmanuele Giuseppe Esposito 485afe1e8a7SEmanuele Giuseppe Esposito void job_unref_locked(Job *job) 48680fa2c75SKevin Wolf { 487c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 488c70b8031SEmanuele Giuseppe Esposito 48980fa2c75SKevin Wolf if (--job->refcnt == 0) { 49080fa2c75SKevin Wolf assert(job->status == JOB_STATUS_NULL); 4915d43e86eSKevin Wolf assert(!timer_pending(&job->sleep_timer)); 4927eaa8fb5SKevin Wolf assert(!job->txn); 49380fa2c75SKevin Wolf 49480fa2c75SKevin Wolf if (job->driver->free) { 495afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 49680fa2c75SKevin Wolf job->driver->free(job); 497afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 49880fa2c75SKevin Wolf } 49980fa2c75SKevin Wolf 500e7c1d78bSKevin Wolf QLIST_REMOVE(job, job_list); 501e7c1d78bSKevin Wolf 502a7b4f8fcSEmanuele Giuseppe Esposito progress_destroy(&job->progress); 5033d1f8b07SJohn Snow error_free(job->err); 504fd61a701SKevin Wolf g_free(job->id); 505fd61a701SKevin Wolf g_free(job); 506fd61a701SKevin Wolf } 50780fa2c75SKevin Wolf } 5081908a559SKevin Wolf 509afe1e8a7SEmanuele Giuseppe Esposito void job_unref(Job *job) 510afe1e8a7SEmanuele Giuseppe Esposito { 511afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 512afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 513afe1e8a7SEmanuele Giuseppe Esposito } 514afe1e8a7SEmanuele Giuseppe Esposito 51530a5c887SKevin Wolf void job_progress_update(Job *job, uint64_t done) 51630a5c887SKevin Wolf { 51701fe1ca9SVladimir Sementsov-Ogievskiy progress_work_done(&job->progress, done); 51830a5c887SKevin Wolf } 51930a5c887SKevin Wolf 52030a5c887SKevin Wolf void job_progress_set_remaining(Job *job, uint64_t remaining) 52130a5c887SKevin Wolf { 52201fe1ca9SVladimir Sementsov-Ogievskiy progress_set_remaining(&job->progress, remaining); 52330a5c887SKevin Wolf } 52430a5c887SKevin Wolf 52562f13600SMax Reitz void job_progress_increase_remaining(Job *job, uint64_t delta) 52662f13600SMax Reitz { 52701fe1ca9SVladimir Sementsov-Ogievskiy progress_increase_remaining(&job->progress, delta); 52862f13600SMax Reitz } 52962f13600SMax Reitz 530544f4d52SEmanuele Giuseppe Esposito /** 531544f4d52SEmanuele Giuseppe Esposito * To be called when a cancelled job is finalised. 532afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex held. 533544f4d52SEmanuele Giuseppe Esposito */ 534afe1e8a7SEmanuele Giuseppe Esposito static void job_event_cancelled_locked(Job *job) 535139a9f02SKevin Wolf { 536139a9f02SKevin Wolf notifier_list_notify(&job->on_finalize_cancelled, job); 537139a9f02SKevin Wolf } 538139a9f02SKevin Wolf 539544f4d52SEmanuele Giuseppe Esposito /** 540544f4d52SEmanuele Giuseppe Esposito * To be called when a successfully completed job is finalised. 541afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex held. 542544f4d52SEmanuele Giuseppe Esposito */ 543afe1e8a7SEmanuele Giuseppe Esposito static void job_event_completed_locked(Job *job) 544139a9f02SKevin Wolf { 545139a9f02SKevin Wolf notifier_list_notify(&job->on_finalize_completed, job); 546139a9f02SKevin Wolf } 547139a9f02SKevin Wolf 548afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 549afe1e8a7SEmanuele Giuseppe Esposito static void job_event_pending_locked(Job *job) 550139a9f02SKevin Wolf { 551139a9f02SKevin Wolf notifier_list_notify(&job->on_pending, job); 552139a9f02SKevin Wolf } 553139a9f02SKevin Wolf 554afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 555afe1e8a7SEmanuele Giuseppe Esposito static void job_event_ready_locked(Job *job) 5562e1795b5SKevin Wolf { 5572e1795b5SKevin Wolf notifier_list_notify(&job->on_ready, job); 5582e1795b5SKevin Wolf } 5592e1795b5SKevin Wolf 560afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 561afe1e8a7SEmanuele Giuseppe Esposito static void job_event_idle_locked(Job *job) 56234dc97b9SKevin Wolf { 56334dc97b9SKevin Wolf notifier_list_notify(&job->on_idle, job); 56434dc97b9SKevin Wolf } 56534dc97b9SKevin Wolf 566afe1e8a7SEmanuele Giuseppe Esposito void job_enter_cond_locked(Job *job, bool(*fn)(Job *job)) 567da01ff7fSKevin Wolf { 568afe1e8a7SEmanuele Giuseppe Esposito if (!job_started_locked(job)) { 569da01ff7fSKevin Wolf return; 570da01ff7fSKevin Wolf } 571da01ff7fSKevin Wolf if (job->deferred_to_main_loop) { 572da01ff7fSKevin Wolf return; 573da01ff7fSKevin Wolf } 574da01ff7fSKevin Wolf 57555c5a25aSEmanuele Giuseppe Esposito real_job_lock(); 576da01ff7fSKevin Wolf if (job->busy) { 57755c5a25aSEmanuele Giuseppe Esposito real_job_unlock(); 578da01ff7fSKevin Wolf return; 579da01ff7fSKevin Wolf } 580da01ff7fSKevin Wolf 581da01ff7fSKevin Wolf if (fn && !fn(job)) { 58255c5a25aSEmanuele Giuseppe Esposito real_job_unlock(); 583da01ff7fSKevin Wolf return; 584da01ff7fSKevin Wolf } 585da01ff7fSKevin Wolf 586da01ff7fSKevin Wolf assert(!job->deferred_to_main_loop); 587da01ff7fSKevin Wolf timer_del(&job->sleep_timer); 588da01ff7fSKevin Wolf job->busy = true; 58955c5a25aSEmanuele Giuseppe Esposito real_job_unlock(); 590afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 59113726123SKevin Wolf aio_co_enter(job->aio_context, job->co); 592afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 593afe1e8a7SEmanuele Giuseppe Esposito } 594afe1e8a7SEmanuele Giuseppe Esposito 595afe1e8a7SEmanuele Giuseppe Esposito void job_enter_cond(Job *job, bool(*fn)(Job *job)) 596afe1e8a7SEmanuele Giuseppe Esposito { 597afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 598afe1e8a7SEmanuele Giuseppe Esposito job_enter_cond_locked(job, fn); 599da01ff7fSKevin Wolf } 600da01ff7fSKevin Wolf 6015d43e86eSKevin Wolf void job_enter(Job *job) 6025d43e86eSKevin Wolf { 603afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 604afe1e8a7SEmanuele Giuseppe Esposito job_enter_cond_locked(job, NULL); 6055d43e86eSKevin Wolf } 6065d43e86eSKevin Wolf 607da01ff7fSKevin Wolf /* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds. 6083d70ff53SKevin Wolf * Reentering the job coroutine with job_enter() before the timer has expired 6093d70ff53SKevin Wolf * is allowed and cancels the timer. 610da01ff7fSKevin Wolf * 6113d70ff53SKevin Wolf * If @ns is (uint64_t) -1, no timer is scheduled and job_enter() must be 612afe1e8a7SEmanuele Giuseppe Esposito * called explicitly. 613afe1e8a7SEmanuele Giuseppe Esposito * 614afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex held, but releases it temporarily. 615afe1e8a7SEmanuele Giuseppe Esposito */ 616afe1e8a7SEmanuele Giuseppe Esposito static void coroutine_fn job_do_yield_locked(Job *job, uint64_t ns) 617da01ff7fSKevin Wolf { 61855c5a25aSEmanuele Giuseppe Esposito real_job_lock(); 619da01ff7fSKevin Wolf if (ns != -1) { 620da01ff7fSKevin Wolf timer_mod(&job->sleep_timer, ns); 621da01ff7fSKevin Wolf } 622da01ff7fSKevin Wolf job->busy = false; 623afe1e8a7SEmanuele Giuseppe Esposito job_event_idle_locked(job); 62455c5a25aSEmanuele Giuseppe Esposito real_job_unlock(); 625afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 626da01ff7fSKevin Wolf qemu_coroutine_yield(); 627afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 628da01ff7fSKevin Wolf 629da01ff7fSKevin Wolf /* Set by job_enter_cond() before re-entering the coroutine. */ 630da01ff7fSKevin Wolf assert(job->busy); 631da01ff7fSKevin Wolf } 632da01ff7fSKevin Wolf 633afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily. */ 634afe1e8a7SEmanuele Giuseppe Esposito static void coroutine_fn job_pause_point_locked(Job *job) 635da01ff7fSKevin Wolf { 636afe1e8a7SEmanuele Giuseppe Esposito assert(job && job_started_locked(job)); 637da01ff7fSKevin Wolf 638afe1e8a7SEmanuele Giuseppe Esposito if (!job_should_pause_locked(job)) { 639da01ff7fSKevin Wolf return; 640da01ff7fSKevin Wolf } 641afe1e8a7SEmanuele Giuseppe Esposito if (job_is_cancelled_locked(job)) { 642da01ff7fSKevin Wolf return; 643da01ff7fSKevin Wolf } 644da01ff7fSKevin Wolf 645da01ff7fSKevin Wolf if (job->driver->pause) { 646afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 647da01ff7fSKevin Wolf job->driver->pause(job); 648afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 649da01ff7fSKevin Wolf } 650da01ff7fSKevin Wolf 651afe1e8a7SEmanuele Giuseppe Esposito if (job_should_pause_locked(job) && !job_is_cancelled_locked(job)) { 652da01ff7fSKevin Wolf JobStatus status = job->status; 653afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, status == JOB_STATUS_READY 654da01ff7fSKevin Wolf ? JOB_STATUS_STANDBY 655da01ff7fSKevin Wolf : JOB_STATUS_PAUSED); 656da01ff7fSKevin Wolf job->paused = true; 657afe1e8a7SEmanuele Giuseppe Esposito job_do_yield_locked(job, -1); 658da01ff7fSKevin Wolf job->paused = false; 659afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, status); 660da01ff7fSKevin Wolf } 661da01ff7fSKevin Wolf 662da01ff7fSKevin Wolf if (job->driver->resume) { 663afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 664da01ff7fSKevin Wolf job->driver->resume(job); 665afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 666da01ff7fSKevin Wolf } 667da01ff7fSKevin Wolf } 668da01ff7fSKevin Wolf 669afe1e8a7SEmanuele Giuseppe Esposito void coroutine_fn job_pause_point(Job *job) 670afe1e8a7SEmanuele Giuseppe Esposito { 671afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 672afe1e8a7SEmanuele Giuseppe Esposito job_pause_point_locked(job); 673afe1e8a7SEmanuele Giuseppe Esposito } 674afe1e8a7SEmanuele Giuseppe Esposito 675afe1e8a7SEmanuele Giuseppe Esposito static void coroutine_fn job_yield_locked(Job *job) 676afe1e8a7SEmanuele Giuseppe Esposito { 677afe1e8a7SEmanuele Giuseppe Esposito assert(job->busy); 678afe1e8a7SEmanuele Giuseppe Esposito 679afe1e8a7SEmanuele Giuseppe Esposito /* Check cancellation *before* setting busy = false, too! */ 680afe1e8a7SEmanuele Giuseppe Esposito if (job_is_cancelled_locked(job)) { 681afe1e8a7SEmanuele Giuseppe Esposito return; 682afe1e8a7SEmanuele Giuseppe Esposito } 683afe1e8a7SEmanuele Giuseppe Esposito 684afe1e8a7SEmanuele Giuseppe Esposito if (!job_should_pause_locked(job)) { 685afe1e8a7SEmanuele Giuseppe Esposito job_do_yield_locked(job, -1); 686afe1e8a7SEmanuele Giuseppe Esposito } 687afe1e8a7SEmanuele Giuseppe Esposito 688afe1e8a7SEmanuele Giuseppe Esposito job_pause_point_locked(job); 689afe1e8a7SEmanuele Giuseppe Esposito } 690afe1e8a7SEmanuele Giuseppe Esposito 69106753a07SPaolo Bonzini void coroutine_fn job_yield(Job *job) 692198c49ccSKevin Wolf { 693afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 694afe1e8a7SEmanuele Giuseppe Esposito job_yield_locked(job); 695198c49ccSKevin Wolf } 696198c49ccSKevin Wolf 6975d43e86eSKevin Wolf void coroutine_fn job_sleep_ns(Job *job, int64_t ns) 6985d43e86eSKevin Wolf { 699afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 7005d43e86eSKevin Wolf assert(job->busy); 7015d43e86eSKevin Wolf 7025d43e86eSKevin Wolf /* Check cancellation *before* setting busy = false, too! */ 703afe1e8a7SEmanuele Giuseppe Esposito if (job_is_cancelled_locked(job)) { 7045d43e86eSKevin Wolf return; 7055d43e86eSKevin Wolf } 7065d43e86eSKevin Wolf 707afe1e8a7SEmanuele Giuseppe Esposito if (!job_should_pause_locked(job)) { 708afe1e8a7SEmanuele Giuseppe Esposito job_do_yield_locked(job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns); 7095d43e86eSKevin Wolf } 7105d43e86eSKevin Wolf 711afe1e8a7SEmanuele Giuseppe Esposito job_pause_point_locked(job); 7125d43e86eSKevin Wolf } 7135d43e86eSKevin Wolf 714afe1e8a7SEmanuele Giuseppe Esposito /* Assumes the job_mutex is held */ 715afe1e8a7SEmanuele Giuseppe Esposito static bool job_timer_not_pending_locked(Job *job) 716b15de828SKevin Wolf { 717b15de828SKevin Wolf return !timer_pending(&job->sleep_timer); 718b15de828SKevin Wolf } 719b15de828SKevin Wolf 720afe1e8a7SEmanuele Giuseppe Esposito void job_pause_locked(Job *job) 721b15de828SKevin Wolf { 722b15de828SKevin Wolf job->pause_count++; 7233ee1483bSVladimir Sementsov-Ogievskiy if (!job->paused) { 724afe1e8a7SEmanuele Giuseppe Esposito job_enter_cond_locked(job, NULL); 7253ee1483bSVladimir Sementsov-Ogievskiy } 726b15de828SKevin Wolf } 727b15de828SKevin Wolf 728afe1e8a7SEmanuele Giuseppe Esposito void job_pause(Job *job) 729afe1e8a7SEmanuele Giuseppe Esposito { 730afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 731afe1e8a7SEmanuele Giuseppe Esposito job_pause_locked(job); 732afe1e8a7SEmanuele Giuseppe Esposito } 733afe1e8a7SEmanuele Giuseppe Esposito 734afe1e8a7SEmanuele Giuseppe Esposito void job_resume_locked(Job *job) 735b15de828SKevin Wolf { 736b15de828SKevin Wolf assert(job->pause_count > 0); 737b15de828SKevin Wolf job->pause_count--; 738b15de828SKevin Wolf if (job->pause_count) { 739b15de828SKevin Wolf return; 740b15de828SKevin Wolf } 741b15de828SKevin Wolf 742b15de828SKevin Wolf /* kick only if no timer is pending */ 743afe1e8a7SEmanuele Giuseppe Esposito job_enter_cond_locked(job, job_timer_not_pending_locked); 744b15de828SKevin Wolf } 745b15de828SKevin Wolf 746afe1e8a7SEmanuele Giuseppe Esposito void job_resume(Job *job) 747b15de828SKevin Wolf { 748afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 749afe1e8a7SEmanuele Giuseppe Esposito job_resume_locked(job); 750afe1e8a7SEmanuele Giuseppe Esposito } 751afe1e8a7SEmanuele Giuseppe Esposito 752afe1e8a7SEmanuele Giuseppe Esposito void job_user_pause_locked(Job *job, Error **errp) 753afe1e8a7SEmanuele Giuseppe Esposito { 754afe1e8a7SEmanuele Giuseppe Esposito if (job_apply_verb_locked(job, JOB_VERB_PAUSE, errp)) { 755b15de828SKevin Wolf return; 756b15de828SKevin Wolf } 757b15de828SKevin Wolf if (job->user_paused) { 758b15de828SKevin Wolf error_setg(errp, "Job is already paused"); 759b15de828SKevin Wolf return; 760b15de828SKevin Wolf } 761b15de828SKevin Wolf job->user_paused = true; 762afe1e8a7SEmanuele Giuseppe Esposito job_pause_locked(job); 763b15de828SKevin Wolf } 764b15de828SKevin Wolf 765afe1e8a7SEmanuele Giuseppe Esposito void job_user_pause(Job *job, Error **errp) 766afe1e8a7SEmanuele Giuseppe Esposito { 767afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 768afe1e8a7SEmanuele Giuseppe Esposito job_user_pause_locked(job, errp); 769afe1e8a7SEmanuele Giuseppe Esposito } 770afe1e8a7SEmanuele Giuseppe Esposito 771afe1e8a7SEmanuele Giuseppe Esposito bool job_user_paused_locked(Job *job) 772b15de828SKevin Wolf { 773b15de828SKevin Wolf return job->user_paused; 774b15de828SKevin Wolf } 775b15de828SKevin Wolf 776afe1e8a7SEmanuele Giuseppe Esposito bool job_user_paused(Job *job) 777afe1e8a7SEmanuele Giuseppe Esposito { 778afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 779afe1e8a7SEmanuele Giuseppe Esposito return job_user_paused_locked(job); 780afe1e8a7SEmanuele Giuseppe Esposito } 781afe1e8a7SEmanuele Giuseppe Esposito 782afe1e8a7SEmanuele Giuseppe Esposito void job_user_resume_locked(Job *job, Error **errp) 783b15de828SKevin Wolf { 784b15de828SKevin Wolf assert(job); 785c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 786b15de828SKevin Wolf if (!job->user_paused || job->pause_count <= 0) { 787b15de828SKevin Wolf error_setg(errp, "Can't resume a job that was not paused"); 788b15de828SKevin Wolf return; 789b15de828SKevin Wolf } 790afe1e8a7SEmanuele Giuseppe Esposito if (job_apply_verb_locked(job, JOB_VERB_RESUME, errp)) { 791b15de828SKevin Wolf return; 792b15de828SKevin Wolf } 793b15de828SKevin Wolf if (job->driver->user_resume) { 794afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 795b15de828SKevin Wolf job->driver->user_resume(job); 796afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 797b15de828SKevin Wolf } 798b15de828SKevin Wolf job->user_paused = false; 799afe1e8a7SEmanuele Giuseppe Esposito job_resume_locked(job); 800b15de828SKevin Wolf } 801b15de828SKevin Wolf 802afe1e8a7SEmanuele Giuseppe Esposito void job_user_resume(Job *job, Error **errp) 803afe1e8a7SEmanuele Giuseppe Esposito { 804afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 805afe1e8a7SEmanuele Giuseppe Esposito job_user_resume_locked(job, errp); 806afe1e8a7SEmanuele Giuseppe Esposito } 807afe1e8a7SEmanuele Giuseppe Esposito 808afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily. */ 809afe1e8a7SEmanuele Giuseppe Esposito static void job_do_dismiss_locked(Job *job) 8104ad35181SKevin Wolf { 8114ad35181SKevin Wolf assert(job); 8124ad35181SKevin Wolf job->busy = false; 8134ad35181SKevin Wolf job->paused = false; 8144ad35181SKevin Wolf job->deferred_to_main_loop = true; 8154ad35181SKevin Wolf 816afe1e8a7SEmanuele Giuseppe Esposito job_txn_del_job_locked(job); 8174ad35181SKevin Wolf 818afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_NULL); 819afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 8204ad35181SKevin Wolf } 8214ad35181SKevin Wolf 822afe1e8a7SEmanuele Giuseppe Esposito void job_dismiss_locked(Job **jobptr, Error **errp) 8235f9a6a08SKevin Wolf { 8245f9a6a08SKevin Wolf Job *job = *jobptr; 8255f9a6a08SKevin Wolf /* similarly to _complete, this is QMP-interface only. */ 8265f9a6a08SKevin Wolf assert(job->id); 827afe1e8a7SEmanuele Giuseppe Esposito if (job_apply_verb_locked(job, JOB_VERB_DISMISS, errp)) { 8285f9a6a08SKevin Wolf return; 8295f9a6a08SKevin Wolf } 8305f9a6a08SKevin Wolf 831afe1e8a7SEmanuele Giuseppe Esposito job_do_dismiss_locked(job); 8325f9a6a08SKevin Wolf *jobptr = NULL; 8335f9a6a08SKevin Wolf } 8345f9a6a08SKevin Wolf 835afe1e8a7SEmanuele Giuseppe Esposito void job_dismiss(Job **jobptr, Error **errp) 836afe1e8a7SEmanuele Giuseppe Esposito { 837afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 838afe1e8a7SEmanuele Giuseppe Esposito job_dismiss_locked(jobptr, errp); 839afe1e8a7SEmanuele Giuseppe Esposito } 840afe1e8a7SEmanuele Giuseppe Esposito 8414ad35181SKevin Wolf void job_early_fail(Job *job) 8424ad35181SKevin Wolf { 843afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 8444ad35181SKevin Wolf assert(job->status == JOB_STATUS_CREATED); 845afe1e8a7SEmanuele Giuseppe Esposito job_do_dismiss_locked(job); 8464ad35181SKevin Wolf } 8474ad35181SKevin Wolf 848afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 849afe1e8a7SEmanuele Giuseppe Esposito static void job_conclude_locked(Job *job) 8504ad35181SKevin Wolf { 851afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_CONCLUDED); 852afe1e8a7SEmanuele Giuseppe Esposito if (job->auto_dismiss || !job_started_locked(job)) { 853afe1e8a7SEmanuele Giuseppe Esposito job_do_dismiss_locked(job); 8544ad35181SKevin Wolf } 8554ad35181SKevin Wolf } 8564ad35181SKevin Wolf 857afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 858afe1e8a7SEmanuele Giuseppe Esposito static void job_update_rc_locked(Job *job) 8594ad35181SKevin Wolf { 860afe1e8a7SEmanuele Giuseppe Esposito if (!job->ret && job_is_cancelled_locked(job)) { 8614ad35181SKevin Wolf job->ret = -ECANCELED; 8624ad35181SKevin Wolf } 8634ad35181SKevin Wolf if (job->ret) { 8643d1f8b07SJohn Snow if (!job->err) { 8653d1f8b07SJohn Snow error_setg(&job->err, "%s", strerror(-job->ret)); 8661266c9b9SKevin Wolf } 867afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_ABORTING); 8684ad35181SKevin Wolf } 8694ad35181SKevin Wolf } 8704ad35181SKevin Wolf 8714ad35181SKevin Wolf static void job_commit(Job *job) 8724ad35181SKevin Wolf { 8734ad35181SKevin Wolf assert(!job->ret); 874c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 8754ad35181SKevin Wolf if (job->driver->commit) { 8764ad35181SKevin Wolf job->driver->commit(job); 8774ad35181SKevin Wolf } 8784ad35181SKevin Wolf } 8794ad35181SKevin Wolf 8804ad35181SKevin Wolf static void job_abort(Job *job) 8814ad35181SKevin Wolf { 8824ad35181SKevin Wolf assert(job->ret); 883c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 8844ad35181SKevin Wolf if (job->driver->abort) { 8854ad35181SKevin Wolf job->driver->abort(job); 8864ad35181SKevin Wolf } 8874ad35181SKevin Wolf } 8884ad35181SKevin Wolf 8894ad35181SKevin Wolf static void job_clean(Job *job) 8904ad35181SKevin Wolf { 891c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 8924ad35181SKevin Wolf if (job->driver->clean) { 8934ad35181SKevin Wolf job->driver->clean(job); 8944ad35181SKevin Wolf } 8954ad35181SKevin Wolf } 8964ad35181SKevin Wolf 897afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily */ 898afe1e8a7SEmanuele Giuseppe Esposito static int job_finalize_single_locked(Job *job) 8994ad35181SKevin Wolf { 900afe1e8a7SEmanuele Giuseppe Esposito int job_ret; 901afe1e8a7SEmanuele Giuseppe Esposito 902afe1e8a7SEmanuele Giuseppe Esposito assert(job_is_completed_locked(job)); 9034ad35181SKevin Wolf 9044ad35181SKevin Wolf /* Ensure abort is called for late-transactional failures */ 905afe1e8a7SEmanuele Giuseppe Esposito job_update_rc_locked(job); 9064ad35181SKevin Wolf 907afe1e8a7SEmanuele Giuseppe Esposito job_ret = job->ret; 908afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 909afe1e8a7SEmanuele Giuseppe Esposito 910afe1e8a7SEmanuele Giuseppe Esposito if (!job_ret) { 9114ad35181SKevin Wolf job_commit(job); 9124ad35181SKevin Wolf } else { 9134ad35181SKevin Wolf job_abort(job); 9144ad35181SKevin Wolf } 9154ad35181SKevin Wolf job_clean(job); 9164ad35181SKevin Wolf 917afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 918afe1e8a7SEmanuele Giuseppe Esposito 9194ad35181SKevin Wolf if (job->cb) { 920afe1e8a7SEmanuele Giuseppe Esposito job_ret = job->ret; 921afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 922afe1e8a7SEmanuele Giuseppe Esposito job->cb(job->opaque, job_ret); 923afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 9244ad35181SKevin Wolf } 9254ad35181SKevin Wolf 9264ad35181SKevin Wolf /* Emit events only if we actually started */ 927afe1e8a7SEmanuele Giuseppe Esposito if (job_started_locked(job)) { 928afe1e8a7SEmanuele Giuseppe Esposito if (job_is_cancelled_locked(job)) { 929afe1e8a7SEmanuele Giuseppe Esposito job_event_cancelled_locked(job); 9304ad35181SKevin Wolf } else { 931afe1e8a7SEmanuele Giuseppe Esposito job_event_completed_locked(job); 9324ad35181SKevin Wolf } 9334ad35181SKevin Wolf } 9344ad35181SKevin Wolf 935afe1e8a7SEmanuele Giuseppe Esposito job_txn_del_job_locked(job); 936afe1e8a7SEmanuele Giuseppe Esposito job_conclude_locked(job); 9374ad35181SKevin Wolf return 0; 9384ad35181SKevin Wolf } 9394ad35181SKevin Wolf 940afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily */ 941afe1e8a7SEmanuele Giuseppe Esposito static void job_cancel_async_locked(Job *job, bool force) 9427eaa8fb5SKevin Wolf { 943c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 9449820933bSVladimir Sementsov-Ogievskiy if (job->driver->cancel) { 945afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 94673895f38SHanna Reitz force = job->driver->cancel(job, force); 947afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 94873895f38SHanna Reitz } else { 94973895f38SHanna Reitz /* No .cancel() means the job will behave as if force-cancelled */ 95073895f38SHanna Reitz force = true; 9519820933bSVladimir Sementsov-Ogievskiy } 95273895f38SHanna Reitz 9537eaa8fb5SKevin Wolf if (job->user_paused) { 9547eaa8fb5SKevin Wolf /* Do not call job_enter here, the caller will handle it. */ 9557eaa8fb5SKevin Wolf if (job->driver->user_resume) { 956afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 9577eaa8fb5SKevin Wolf job->driver->user_resume(job); 958afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 9597eaa8fb5SKevin Wolf } 960e321c059SJeff Cody job->user_paused = false; 9617eaa8fb5SKevin Wolf assert(job->pause_count > 0); 9627eaa8fb5SKevin Wolf job->pause_count--; 9637eaa8fb5SKevin Wolf } 964401dd096SHanna Reitz 965401dd096SHanna Reitz /* 966401dd096SHanna Reitz * Ignore soft cancel requests after the job is already done 967401dd096SHanna Reitz * (We will still invoke job->driver->cancel() above, but if the 968401dd096SHanna Reitz * job driver supports soft cancelling and the job is done, that 969401dd096SHanna Reitz * should be a no-op, too. We still call it so it can override 970401dd096SHanna Reitz * @force.) 971401dd096SHanna Reitz */ 972401dd096SHanna Reitz if (force || !job->deferred_to_main_loop) { 9737eaa8fb5SKevin Wolf job->cancelled = true; 9747eaa8fb5SKevin Wolf /* To prevent 'force == false' overriding a previous 'force == true' */ 9757eaa8fb5SKevin Wolf job->force_cancel |= force; 9767eaa8fb5SKevin Wolf } 977401dd096SHanna Reitz } 9787eaa8fb5SKevin Wolf 979afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily. */ 980afe1e8a7SEmanuele Giuseppe Esposito static void job_completed_txn_abort_locked(Job *job) 9817eaa8fb5SKevin Wolf { 9827eaa8fb5SKevin Wolf AioContext *ctx; 9837eaa8fb5SKevin Wolf JobTxn *txn = job->txn; 9847eaa8fb5SKevin Wolf Job *other_job; 9857eaa8fb5SKevin Wolf 9867eaa8fb5SKevin Wolf if (txn->aborting) { 9877eaa8fb5SKevin Wolf /* 9887eaa8fb5SKevin Wolf * We are cancelled by another job, which will handle everything. 9897eaa8fb5SKevin Wolf */ 9907eaa8fb5SKevin Wolf return; 9917eaa8fb5SKevin Wolf } 9927eaa8fb5SKevin Wolf txn->aborting = true; 993afe1e8a7SEmanuele Giuseppe Esposito job_txn_ref_locked(txn); 9947eaa8fb5SKevin Wolf 995d4311314SHanna Reitz /* 996d4311314SHanna Reitz * We can only hold the single job's AioContext lock while calling 997644f3a29SKevin Wolf * job_finalize_single() because the finalization callbacks can involve 998d4311314SHanna Reitz * calls of AIO_WAIT_WHILE(), which could deadlock otherwise. 999d4311314SHanna Reitz * Note that the job's AioContext may change when it is finalized. 1000d4311314SHanna Reitz */ 1001afe1e8a7SEmanuele Giuseppe Esposito job_ref_locked(job); 1002d4311314SHanna Reitz aio_context_release(job->aio_context); 10037eaa8fb5SKevin Wolf 10047eaa8fb5SKevin Wolf /* Other jobs are effectively cancelled by us, set the status for 10057eaa8fb5SKevin Wolf * them; this job, however, may or may not be cancelled, depending 10067eaa8fb5SKevin Wolf * on the caller, so leave it. */ 10077eaa8fb5SKevin Wolf QLIST_FOREACH(other_job, &txn->jobs, txn_list) { 10087eaa8fb5SKevin Wolf if (other_job != job) { 1009644f3a29SKevin Wolf ctx = other_job->aio_context; 1010644f3a29SKevin Wolf aio_context_acquire(ctx); 10111d4a43e9SHanna Reitz /* 10121d4a43e9SHanna Reitz * This is a transaction: If one job failed, no result will matter. 10131d4a43e9SHanna Reitz * Therefore, pass force=true to terminate all other jobs as quickly 10141d4a43e9SHanna Reitz * as possible. 10151d4a43e9SHanna Reitz */ 1016afe1e8a7SEmanuele Giuseppe Esposito job_cancel_async_locked(other_job, true); 1017644f3a29SKevin Wolf aio_context_release(ctx); 10187eaa8fb5SKevin Wolf } 10197eaa8fb5SKevin Wolf } 10207eaa8fb5SKevin Wolf while (!QLIST_EMPTY(&txn->jobs)) { 10217eaa8fb5SKevin Wolf other_job = QLIST_FIRST(&txn->jobs); 1022d4311314SHanna Reitz /* 1023d4311314SHanna Reitz * The job's AioContext may change, so store it in @ctx so we 1024d4311314SHanna Reitz * release the same context that we have acquired before. 1025d4311314SHanna Reitz */ 10267eaa8fb5SKevin Wolf ctx = other_job->aio_context; 1027644f3a29SKevin Wolf aio_context_acquire(ctx); 1028afe1e8a7SEmanuele Giuseppe Esposito if (!job_is_completed_locked(other_job)) { 1029afe1e8a7SEmanuele Giuseppe Esposito assert(job_cancel_requested_locked(other_job)); 1030afe1e8a7SEmanuele Giuseppe Esposito job_finish_sync_locked(other_job, NULL, NULL); 10317eaa8fb5SKevin Wolf } 1032afe1e8a7SEmanuele Giuseppe Esposito job_finalize_single_locked(other_job); 10337eaa8fb5SKevin Wolf aio_context_release(ctx); 10347eaa8fb5SKevin Wolf } 10357eaa8fb5SKevin Wolf 1036d4311314SHanna Reitz /* 1037d4311314SHanna Reitz * Use job_ref()/job_unref() so we can read the AioContext here 1038d4311314SHanna Reitz * even if the job went away during job_finalize_single(). 1039d4311314SHanna Reitz */ 1040d4311314SHanna Reitz aio_context_acquire(job->aio_context); 1041afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 1042644f3a29SKevin Wolf 1043afe1e8a7SEmanuele Giuseppe Esposito job_txn_unref_locked(txn); 10447eaa8fb5SKevin Wolf } 10457eaa8fb5SKevin Wolf 1046afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily */ 1047afe1e8a7SEmanuele Giuseppe Esposito static int job_prepare_locked(Job *job) 10487eaa8fb5SKevin Wolf { 1049afe1e8a7SEmanuele Giuseppe Esposito int ret; 1050afe1e8a7SEmanuele Giuseppe Esposito 1051c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 10527eaa8fb5SKevin Wolf if (job->ret == 0 && job->driver->prepare) { 1053afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 1054afe1e8a7SEmanuele Giuseppe Esposito ret = job->driver->prepare(job); 1055afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 1056afe1e8a7SEmanuele Giuseppe Esposito job->ret = ret; 1057afe1e8a7SEmanuele Giuseppe Esposito job_update_rc_locked(job); 10587eaa8fb5SKevin Wolf } 10597eaa8fb5SKevin Wolf return job->ret; 10607eaa8fb5SKevin Wolf } 10617eaa8fb5SKevin Wolf 1062afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held */ 1063afe1e8a7SEmanuele Giuseppe Esposito static int job_needs_finalize_locked(Job *job) 10647eaa8fb5SKevin Wolf { 10657eaa8fb5SKevin Wolf return !job->auto_finalize; 10667eaa8fb5SKevin Wolf } 10677eaa8fb5SKevin Wolf 1068afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held */ 1069afe1e8a7SEmanuele Giuseppe Esposito static void job_do_finalize_locked(Job *job) 10707eaa8fb5SKevin Wolf { 10717eaa8fb5SKevin Wolf int rc; 10727eaa8fb5SKevin Wolf assert(job && job->txn); 10737eaa8fb5SKevin Wolf 10747eaa8fb5SKevin Wolf /* prepare the transaction to complete */ 1075afe1e8a7SEmanuele Giuseppe Esposito rc = job_txn_apply_locked(job, job_prepare_locked); 10767eaa8fb5SKevin Wolf if (rc) { 1077afe1e8a7SEmanuele Giuseppe Esposito job_completed_txn_abort_locked(job); 10787eaa8fb5SKevin Wolf } else { 1079afe1e8a7SEmanuele Giuseppe Esposito job_txn_apply_locked(job, job_finalize_single_locked); 10807eaa8fb5SKevin Wolf } 10817eaa8fb5SKevin Wolf } 10827eaa8fb5SKevin Wolf 1083afe1e8a7SEmanuele Giuseppe Esposito void job_finalize_locked(Job *job, Error **errp) 1084afe1e8a7SEmanuele Giuseppe Esposito { 1085afe1e8a7SEmanuele Giuseppe Esposito assert(job && job->id); 1086afe1e8a7SEmanuele Giuseppe Esposito if (job_apply_verb_locked(job, JOB_VERB_FINALIZE, errp)) { 1087afe1e8a7SEmanuele Giuseppe Esposito return; 1088afe1e8a7SEmanuele Giuseppe Esposito } 1089afe1e8a7SEmanuele Giuseppe Esposito job_do_finalize_locked(job); 1090afe1e8a7SEmanuele Giuseppe Esposito } 1091afe1e8a7SEmanuele Giuseppe Esposito 10927eaa8fb5SKevin Wolf void job_finalize(Job *job, Error **errp) 10937eaa8fb5SKevin Wolf { 1094afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1095afe1e8a7SEmanuele Giuseppe Esposito job_finalize_locked(job, errp); 10967eaa8fb5SKevin Wolf } 10977eaa8fb5SKevin Wolf 1098afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 1099afe1e8a7SEmanuele Giuseppe Esposito static int job_transition_to_pending_locked(Job *job) 11007eaa8fb5SKevin Wolf { 1101afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_PENDING); 11027eaa8fb5SKevin Wolf if (!job->auto_finalize) { 1103afe1e8a7SEmanuele Giuseppe Esposito job_event_pending_locked(job); 11047eaa8fb5SKevin Wolf } 11057eaa8fb5SKevin Wolf return 0; 11067eaa8fb5SKevin Wolf } 11077eaa8fb5SKevin Wolf 11082e1795b5SKevin Wolf void job_transition_to_ready(Job *job) 11092e1795b5SKevin Wolf { 1110afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1111afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_READY); 1112afe1e8a7SEmanuele Giuseppe Esposito job_event_ready_locked(job); 11132e1795b5SKevin Wolf } 11142e1795b5SKevin Wolf 1115afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 1116afe1e8a7SEmanuele Giuseppe Esposito static void job_completed_txn_success_locked(Job *job) 11177eaa8fb5SKevin Wolf { 11187eaa8fb5SKevin Wolf JobTxn *txn = job->txn; 11197eaa8fb5SKevin Wolf Job *other_job; 11207eaa8fb5SKevin Wolf 1121afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_WAITING); 11227eaa8fb5SKevin Wolf 11237eaa8fb5SKevin Wolf /* 11247eaa8fb5SKevin Wolf * Successful completion, see if there are other running jobs in this 11257eaa8fb5SKevin Wolf * txn. 11267eaa8fb5SKevin Wolf */ 11277eaa8fb5SKevin Wolf QLIST_FOREACH(other_job, &txn->jobs, txn_list) { 1128afe1e8a7SEmanuele Giuseppe Esposito if (!job_is_completed_locked(other_job)) { 11297eaa8fb5SKevin Wolf return; 11307eaa8fb5SKevin Wolf } 11317eaa8fb5SKevin Wolf assert(other_job->ret == 0); 11327eaa8fb5SKevin Wolf } 11337eaa8fb5SKevin Wolf 1134afe1e8a7SEmanuele Giuseppe Esposito job_txn_apply_locked(job, job_transition_to_pending_locked); 11357eaa8fb5SKevin Wolf 11367eaa8fb5SKevin Wolf /* If no jobs need manual finalization, automatically do so */ 1137afe1e8a7SEmanuele Giuseppe Esposito if (job_txn_apply_locked(job, job_needs_finalize_locked) == 0) { 1138afe1e8a7SEmanuele Giuseppe Esposito job_do_finalize_locked(job); 11397eaa8fb5SKevin Wolf } 11407eaa8fb5SKevin Wolf } 11417eaa8fb5SKevin Wolf 1142afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 1143afe1e8a7SEmanuele Giuseppe Esposito static void job_completed_locked(Job *job) 11443d70ff53SKevin Wolf { 1145afe1e8a7SEmanuele Giuseppe Esposito assert(job && job->txn && !job_is_completed_locked(job)); 11461266c9b9SKevin Wolf 1147afe1e8a7SEmanuele Giuseppe Esposito job_update_rc_locked(job); 1148404ff28dSJohn Snow trace_job_completed(job, job->ret); 11493d70ff53SKevin Wolf if (job->ret) { 1150afe1e8a7SEmanuele Giuseppe Esposito job_completed_txn_abort_locked(job); 11513d70ff53SKevin Wolf } else { 1152afe1e8a7SEmanuele Giuseppe Esposito job_completed_txn_success_locked(job); 11533d70ff53SKevin Wolf } 11543d70ff53SKevin Wolf } 11553d70ff53SKevin Wolf 1156afe1e8a7SEmanuele Giuseppe Esposito /** 1157afe1e8a7SEmanuele Giuseppe Esposito * Useful only as a type shim for aio_bh_schedule_oneshot. 1158afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex *not* held. 1159afe1e8a7SEmanuele Giuseppe Esposito */ 1160ccbfb331SJohn Snow static void job_exit(void *opaque) 1161ccbfb331SJohn Snow { 1162ccbfb331SJohn Snow Job *job = (Job *)opaque; 1163b660a84bSStefan Reiter AioContext *ctx; 1164afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1165d1756c78SKevin Wolf 1166afe1e8a7SEmanuele Giuseppe Esposito job_ref_locked(job); 1167b660a84bSStefan Reiter aio_context_acquire(job->aio_context); 1168b5a7a057SKevin Wolf 1169b5a7a057SKevin Wolf /* This is a lie, we're not quiescent, but still doing the completion 1170b5a7a057SKevin Wolf * callbacks. However, completion callbacks tend to involve operations that 1171b5a7a057SKevin Wolf * drain block nodes, and if .drained_poll still returned true, we would 1172b5a7a057SKevin Wolf * deadlock. */ 1173b5a7a057SKevin Wolf job->busy = false; 1174afe1e8a7SEmanuele Giuseppe Esposito job_event_idle_locked(job); 1175b5a7a057SKevin Wolf 1176afe1e8a7SEmanuele Giuseppe Esposito job_completed_locked(job); 1177b5a7a057SKevin Wolf 1178b660a84bSStefan Reiter /* 1179b660a84bSStefan Reiter * Note that calling job_completed can move the job to a different 1180b660a84bSStefan Reiter * aio_context, so we cannot cache from above. job_txn_apply takes care of 1181b660a84bSStefan Reiter * acquiring the new lock, and we ref/unref to avoid job_completed freeing 1182b660a84bSStefan Reiter * the job underneath us. 1183b660a84bSStefan Reiter */ 1184b660a84bSStefan Reiter ctx = job->aio_context; 1185afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 1186d1756c78SKevin Wolf aio_context_release(ctx); 1187ccbfb331SJohn Snow } 1188ccbfb331SJohn Snow 1189ccbfb331SJohn Snow /** 1190ccbfb331SJohn Snow * All jobs must allow a pause point before entering their job proper. This 1191ccbfb331SJohn Snow * ensures that jobs can be paused prior to being started, then resumed later. 1192ccbfb331SJohn Snow */ 1193ccbfb331SJohn Snow static void coroutine_fn job_co_entry(void *opaque) 1194ccbfb331SJohn Snow { 1195ccbfb331SJohn Snow Job *job = opaque; 1196afe1e8a7SEmanuele Giuseppe Esposito int ret; 1197ccbfb331SJohn Snow 1198ccbfb331SJohn Snow assert(job && job->driver && job->driver->run); 1199afe1e8a7SEmanuele Giuseppe Esposito WITH_JOB_LOCK_GUARD() { 1200c70b8031SEmanuele Giuseppe Esposito assert(job->aio_context == qemu_get_current_aio_context()); 1201afe1e8a7SEmanuele Giuseppe Esposito job_pause_point_locked(job); 1202afe1e8a7SEmanuele Giuseppe Esposito } 1203afe1e8a7SEmanuele Giuseppe Esposito ret = job->driver->run(job, &job->err); 1204afe1e8a7SEmanuele Giuseppe Esposito WITH_JOB_LOCK_GUARD() { 1205afe1e8a7SEmanuele Giuseppe Esposito job->ret = ret; 1206ccbfb331SJohn Snow job->deferred_to_main_loop = true; 1207b5a7a057SKevin Wolf job->busy = true; 1208afe1e8a7SEmanuele Giuseppe Esposito } 1209ccbfb331SJohn Snow aio_bh_schedule_oneshot(qemu_get_aio_context(), job_exit, job); 1210ccbfb331SJohn Snow } 1211ccbfb331SJohn Snow 1212ccbfb331SJohn Snow void job_start(Job *job) 1213ccbfb331SJohn Snow { 1214afe1e8a7SEmanuele Giuseppe Esposito assert(qemu_in_main_thread()); 1215afe1e8a7SEmanuele Giuseppe Esposito 1216afe1e8a7SEmanuele Giuseppe Esposito WITH_JOB_LOCK_GUARD() { 1217afe1e8a7SEmanuele Giuseppe Esposito assert(job && !job_started_locked(job) && job->paused && 1218ccbfb331SJohn Snow job->driver && job->driver->run); 1219ccbfb331SJohn Snow job->co = qemu_coroutine_create(job_co_entry, job); 1220ccbfb331SJohn Snow job->pause_count--; 1221ccbfb331SJohn Snow job->busy = true; 1222ccbfb331SJohn Snow job->paused = false; 1223afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_RUNNING); 1224afe1e8a7SEmanuele Giuseppe Esposito } 1225ccbfb331SJohn Snow aio_co_enter(job->aio_context, job->co); 1226ccbfb331SJohn Snow } 1227ccbfb331SJohn Snow 1228afe1e8a7SEmanuele Giuseppe Esposito void job_cancel_locked(Job *job, bool force) 12293d70ff53SKevin Wolf { 12303d70ff53SKevin Wolf if (job->status == JOB_STATUS_CONCLUDED) { 1231afe1e8a7SEmanuele Giuseppe Esposito job_do_dismiss_locked(job); 12323d70ff53SKevin Wolf return; 12333d70ff53SKevin Wolf } 1234afe1e8a7SEmanuele Giuseppe Esposito job_cancel_async_locked(job, force); 1235afe1e8a7SEmanuele Giuseppe Esposito if (!job_started_locked(job)) { 1236afe1e8a7SEmanuele Giuseppe Esposito job_completed_locked(job); 12373d70ff53SKevin Wolf } else if (job->deferred_to_main_loop) { 1238401dd096SHanna Reitz /* 1239401dd096SHanna Reitz * job_cancel_async() ignores soft-cancel requests for jobs 1240401dd096SHanna Reitz * that are already done (i.e. deferred to the main loop). We 1241401dd096SHanna Reitz * have to check again whether the job is really cancelled. 124208b83bffSHanna Reitz * (job_cancel_requested() and job_is_cancelled() are equivalent 124308b83bffSHanna Reitz * here, because job_cancel_async() will make soft-cancel 124408b83bffSHanna Reitz * requests no-ops when deferred_to_main_loop is true. We 124508b83bffSHanna Reitz * choose to call job_is_cancelled() to show that we invoke 124608b83bffSHanna Reitz * job_completed_txn_abort() only for force-cancelled jobs.) 1247401dd096SHanna Reitz */ 1248afe1e8a7SEmanuele Giuseppe Esposito if (job_is_cancelled_locked(job)) { 1249afe1e8a7SEmanuele Giuseppe Esposito job_completed_txn_abort_locked(job); 1250401dd096SHanna Reitz } 12513d70ff53SKevin Wolf } else { 1252afe1e8a7SEmanuele Giuseppe Esposito job_enter_cond_locked(job, NULL); 12533d70ff53SKevin Wolf } 12543d70ff53SKevin Wolf } 12553d70ff53SKevin Wolf 1256afe1e8a7SEmanuele Giuseppe Esposito void job_cancel(Job *job, bool force) 1257afe1e8a7SEmanuele Giuseppe Esposito { 1258afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1259afe1e8a7SEmanuele Giuseppe Esposito job_cancel_locked(job, force); 1260afe1e8a7SEmanuele Giuseppe Esposito } 1261afe1e8a7SEmanuele Giuseppe Esposito 1262afe1e8a7SEmanuele Giuseppe Esposito void job_user_cancel_locked(Job *job, bool force, Error **errp) 1263afe1e8a7SEmanuele Giuseppe Esposito { 1264afe1e8a7SEmanuele Giuseppe Esposito if (job_apply_verb_locked(job, JOB_VERB_CANCEL, errp)) { 1265afe1e8a7SEmanuele Giuseppe Esposito return; 1266afe1e8a7SEmanuele Giuseppe Esposito } 1267afe1e8a7SEmanuele Giuseppe Esposito job_cancel_locked(job, force); 1268afe1e8a7SEmanuele Giuseppe Esposito } 1269afe1e8a7SEmanuele Giuseppe Esposito 12703d70ff53SKevin Wolf void job_user_cancel(Job *job, bool force, Error **errp) 12713d70ff53SKevin Wolf { 1272afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1273afe1e8a7SEmanuele Giuseppe Esposito job_user_cancel_locked(job, force, errp); 12743d70ff53SKevin Wolf } 12753d70ff53SKevin Wolf 12763d70ff53SKevin Wolf /* A wrapper around job_cancel() taking an Error ** parameter so it may be 12773d70ff53SKevin Wolf * used with job_finish_sync() without the need for (rather nasty) function 1278afe1e8a7SEmanuele Giuseppe Esposito * pointer casts there. 1279afe1e8a7SEmanuele Giuseppe Esposito * 1280afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex held. 1281afe1e8a7SEmanuele Giuseppe Esposito */ 1282afe1e8a7SEmanuele Giuseppe Esposito static void job_cancel_err_locked(Job *job, Error **errp) 12833d70ff53SKevin Wolf { 1284afe1e8a7SEmanuele Giuseppe Esposito job_cancel_locked(job, false); 12853d70ff53SKevin Wolf } 12863d70ff53SKevin Wolf 12874cfb3f05SHanna Reitz /** 12884cfb3f05SHanna Reitz * Same as job_cancel_err(), but force-cancel. 1289afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex held. 12904cfb3f05SHanna Reitz */ 1291afe1e8a7SEmanuele Giuseppe Esposito static void job_force_cancel_err_locked(Job *job, Error **errp) 12923d70ff53SKevin Wolf { 1293afe1e8a7SEmanuele Giuseppe Esposito job_cancel_locked(job, true); 1294afe1e8a7SEmanuele Giuseppe Esposito } 1295afe1e8a7SEmanuele Giuseppe Esposito 1296afe1e8a7SEmanuele Giuseppe Esposito int job_cancel_sync_locked(Job *job, bool force) 1297afe1e8a7SEmanuele Giuseppe Esposito { 1298afe1e8a7SEmanuele Giuseppe Esposito if (force) { 1299afe1e8a7SEmanuele Giuseppe Esposito return job_finish_sync_locked(job, &job_force_cancel_err_locked, NULL); 1300afe1e8a7SEmanuele Giuseppe Esposito } else { 1301afe1e8a7SEmanuele Giuseppe Esposito return job_finish_sync_locked(job, &job_cancel_err_locked, NULL); 1302afe1e8a7SEmanuele Giuseppe Esposito } 13034cfb3f05SHanna Reitz } 13044cfb3f05SHanna Reitz 13054cfb3f05SHanna Reitz int job_cancel_sync(Job *job, bool force) 13064cfb3f05SHanna Reitz { 1307afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1308afe1e8a7SEmanuele Giuseppe Esposito return job_cancel_sync_locked(job, force); 13094cfb3f05SHanna Reitz } 13103d70ff53SKevin Wolf 13113d70ff53SKevin Wolf void job_cancel_sync_all(void) 13123d70ff53SKevin Wolf { 13133d70ff53SKevin Wolf Job *job; 13143d70ff53SKevin Wolf AioContext *aio_context; 1315afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 13163d70ff53SKevin Wolf 1317afe1e8a7SEmanuele Giuseppe Esposito while ((job = job_next_locked(NULL))) { 13183d70ff53SKevin Wolf aio_context = job->aio_context; 13193d70ff53SKevin Wolf aio_context_acquire(aio_context); 1320afe1e8a7SEmanuele Giuseppe Esposito job_cancel_sync_locked(job, true); 13213d70ff53SKevin Wolf aio_context_release(aio_context); 13223d70ff53SKevin Wolf } 13233d70ff53SKevin Wolf } 13243d70ff53SKevin Wolf 1325afe1e8a7SEmanuele Giuseppe Esposito int job_complete_sync_locked(Job *job, Error **errp) 13263d70ff53SKevin Wolf { 1327afe1e8a7SEmanuele Giuseppe Esposito return job_finish_sync_locked(job, job_complete_locked, errp); 13283d70ff53SKevin Wolf } 13293d70ff53SKevin Wolf 1330afe1e8a7SEmanuele Giuseppe Esposito int job_complete_sync(Job *job, Error **errp) 1331afe1e8a7SEmanuele Giuseppe Esposito { 1332afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1333afe1e8a7SEmanuele Giuseppe Esposito return job_complete_sync_locked(job, errp); 1334afe1e8a7SEmanuele Giuseppe Esposito } 1335afe1e8a7SEmanuele Giuseppe Esposito 1336afe1e8a7SEmanuele Giuseppe Esposito void job_complete_locked(Job *job, Error **errp) 13373453d972SKevin Wolf { 13383453d972SKevin Wolf /* Should not be reachable via external interface for internal jobs */ 13393453d972SKevin Wolf assert(job->id); 1340c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 1341afe1e8a7SEmanuele Giuseppe Esposito if (job_apply_verb_locked(job, JOB_VERB_COMPLETE, errp)) { 13423453d972SKevin Wolf return; 13433453d972SKevin Wolf } 1344afe1e8a7SEmanuele Giuseppe Esposito if (job_cancel_requested_locked(job) || !job->driver->complete) { 13453453d972SKevin Wolf error_setg(errp, "The active block job '%s' cannot be completed", 13463453d972SKevin Wolf job->id); 13473453d972SKevin Wolf return; 13483453d972SKevin Wolf } 13493453d972SKevin Wolf 1350afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 13513453d972SKevin Wolf job->driver->complete(job, errp); 1352afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 13533453d972SKevin Wolf } 13543453d972SKevin Wolf 1355afe1e8a7SEmanuele Giuseppe Esposito void job_complete(Job *job, Error **errp) 1356afe1e8a7SEmanuele Giuseppe Esposito { 1357afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1358afe1e8a7SEmanuele Giuseppe Esposito job_complete_locked(job, errp); 1359afe1e8a7SEmanuele Giuseppe Esposito } 1360afe1e8a7SEmanuele Giuseppe Esposito 1361afe1e8a7SEmanuele Giuseppe Esposito int job_finish_sync_locked(Job *job, 1362afe1e8a7SEmanuele Giuseppe Esposito void (*finish)(Job *, Error **errp), 1363afe1e8a7SEmanuele Giuseppe Esposito Error **errp) 13646a74c075SKevin Wolf { 13656a74c075SKevin Wolf Error *local_err = NULL; 13666a74c075SKevin Wolf int ret; 13676a74c075SKevin Wolf 1368afe1e8a7SEmanuele Giuseppe Esposito job_ref_locked(job); 13696a74c075SKevin Wolf 13706a74c075SKevin Wolf if (finish) { 13716a74c075SKevin Wolf finish(job, &local_err); 13726a74c075SKevin Wolf } 13736a74c075SKevin Wolf if (local_err) { 13746a74c075SKevin Wolf error_propagate(errp, local_err); 1375afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 13766a74c075SKevin Wolf return -EBUSY; 13776a74c075SKevin Wolf } 1378de0fbe64SKevin Wolf 1379afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 1380cfe29d82SKevin Wolf AIO_WAIT_WHILE(job->aio_context, 1381bb0c9409SVladimir Sementsov-Ogievskiy (job_enter(job), !job_is_completed(job))); 1382afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 1383de0fbe64SKevin Wolf 1384afe1e8a7SEmanuele Giuseppe Esposito ret = (job_is_cancelled_locked(job) && job->ret == 0) 1385afe1e8a7SEmanuele Giuseppe Esposito ? -ECANCELED : job->ret; 1386afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 13876a74c075SKevin Wolf return ret; 13886a74c075SKevin Wolf } 1389afe1e8a7SEmanuele Giuseppe Esposito 1390afe1e8a7SEmanuele Giuseppe Esposito int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp) 1391afe1e8a7SEmanuele Giuseppe Esposito { 1392afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1393afe1e8a7SEmanuele Giuseppe Esposito return job_finish_sync_locked(job, finish, errp); 1394afe1e8a7SEmanuele Giuseppe Esposito } 1395