133e9e9bdSKevin Wolf /* 233e9e9bdSKevin Wolf * Background jobs (long-running operations) 333e9e9bdSKevin Wolf * 433e9e9bdSKevin Wolf * Copyright (c) 2011 IBM Corp. 533e9e9bdSKevin Wolf * Copyright (c) 2012, 2018 Red Hat, Inc. 633e9e9bdSKevin Wolf * 733e9e9bdSKevin Wolf * Permission is hereby granted, free of charge, to any person obtaining a copy 833e9e9bdSKevin Wolf * of this software and associated documentation files (the "Software"), to deal 933e9e9bdSKevin Wolf * in the Software without restriction, including without limitation the rights 1033e9e9bdSKevin Wolf * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 1133e9e9bdSKevin Wolf * copies of the Software, and to permit persons to whom the Software is 1233e9e9bdSKevin Wolf * furnished to do so, subject to the following conditions: 1333e9e9bdSKevin Wolf * 1433e9e9bdSKevin Wolf * The above copyright notice and this permission notice shall be included in 1533e9e9bdSKevin Wolf * all copies or substantial portions of the Software. 1633e9e9bdSKevin Wolf * 1733e9e9bdSKevin Wolf * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1833e9e9bdSKevin Wolf * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1933e9e9bdSKevin Wolf * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 2033e9e9bdSKevin Wolf * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 2133e9e9bdSKevin Wolf * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 2233e9e9bdSKevin Wolf * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 2333e9e9bdSKevin Wolf * THE SOFTWARE. 2433e9e9bdSKevin Wolf */ 2533e9e9bdSKevin Wolf 2633e9e9bdSKevin Wolf #include "qemu/osdep.h" 2733e9e9bdSKevin Wolf #include "qapi/error.h" 2833e9e9bdSKevin Wolf #include "qemu/job.h" 2933e9e9bdSKevin Wolf #include "qemu/id.h" 301908a559SKevin Wolf #include "qemu/main-loop.h" 31de0fbe64SKevin Wolf #include "block/aio-wait.h" 32243af022SPaolo Bonzini #include "trace/trace-root.h" 331dac83f1SKevin Wolf #include "qapi/qapi-events-job.h" 3433e9e9bdSKevin Wolf 3555c5a25aSEmanuele Giuseppe Esposito /* 36bf61c583SEmanuele Giuseppe Esposito * The job API is composed of two categories of functions. 37bf61c583SEmanuele Giuseppe Esposito * 38bf61c583SEmanuele Giuseppe Esposito * The first includes functions used by the monitor. The monitor is 39bf61c583SEmanuele Giuseppe Esposito * peculiar in that it accesses the job list with job_get, and 40bf61c583SEmanuele Giuseppe Esposito * therefore needs consistency across job_get and the actual operation 41bf61c583SEmanuele Giuseppe Esposito * (e.g. job_user_cancel). To achieve this consistency, the caller 42bf61c583SEmanuele Giuseppe Esposito * calls job_lock/job_unlock itself around the whole operation. 43bf61c583SEmanuele Giuseppe Esposito * 44bf61c583SEmanuele Giuseppe Esposito * 45bf61c583SEmanuele Giuseppe Esposito * The second includes functions used by the job drivers and sometimes 46bf61c583SEmanuele Giuseppe Esposito * by the core block layer. These delegate the locking to the callee instead. 47bf61c583SEmanuele Giuseppe Esposito */ 48bf61c583SEmanuele Giuseppe Esposito 49bf61c583SEmanuele Giuseppe Esposito /* 5055c5a25aSEmanuele Giuseppe Esposito * job_mutex protects the jobs list, but also makes the 5155c5a25aSEmanuele Giuseppe Esposito * struct job fields thread-safe. 5255c5a25aSEmanuele Giuseppe Esposito */ 5355c5a25aSEmanuele Giuseppe Esposito QemuMutex job_mutex; 5455c5a25aSEmanuele Giuseppe Esposito 55afe1e8a7SEmanuele Giuseppe Esposito /* Protected by job_mutex */ 56e7c1d78bSKevin Wolf static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs); 57e7c1d78bSKevin Wolf 58a50c2ab8SKevin Wolf /* Job State Transition Table */ 59a50c2ab8SKevin Wolf bool JobSTT[JOB_STATUS__MAX][JOB_STATUS__MAX] = { 60a50c2ab8SKevin Wolf /* U, C, R, P, Y, S, W, D, X, E, N */ 61a50c2ab8SKevin Wolf /* U: */ [JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 62a50c2ab8SKevin Wolf /* C: */ [JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1}, 63a50c2ab8SKevin Wolf /* R: */ [JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0}, 64a50c2ab8SKevin Wolf /* P: */ [JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, 65a50c2ab8SKevin Wolf /* Y: */ [JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0}, 66a50c2ab8SKevin Wolf /* S: */ [JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, 67a50c2ab8SKevin Wolf /* W: */ [JOB_STATUS_WAITING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0}, 68a50c2ab8SKevin Wolf /* D: */ [JOB_STATUS_PENDING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, 69a50c2ab8SKevin Wolf /* X: */ [JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, 70a50c2ab8SKevin Wolf /* E: */ [JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, 71a50c2ab8SKevin Wolf /* N: */ [JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 72a50c2ab8SKevin Wolf }; 73a50c2ab8SKevin Wolf 74a50c2ab8SKevin Wolf bool JobVerbTable[JOB_VERB__MAX][JOB_STATUS__MAX] = { 75a50c2ab8SKevin Wolf /* U, C, R, P, Y, S, W, D, X, E, N */ 76a50c2ab8SKevin Wolf [JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, 77a50c2ab8SKevin Wolf [JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, 78a50c2ab8SKevin Wolf [JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, 79a50c2ab8SKevin Wolf [JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, 8053ddb9c8SMax Reitz [JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0}, 81a50c2ab8SKevin Wolf [JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, 82a50c2ab8SKevin Wolf [JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, 83a50c2ab8SKevin Wolf }; 84a50c2ab8SKevin Wolf 857eaa8fb5SKevin Wolf /* Transactional group of jobs */ 867eaa8fb5SKevin Wolf struct JobTxn { 877eaa8fb5SKevin Wolf 887eaa8fb5SKevin Wolf /* Is this txn being cancelled? */ 897eaa8fb5SKevin Wolf bool aborting; 907eaa8fb5SKevin Wolf 917eaa8fb5SKevin Wolf /* List of jobs */ 927eaa8fb5SKevin Wolf QLIST_HEAD(, Job) jobs; 937eaa8fb5SKevin Wolf 947eaa8fb5SKevin Wolf /* Reference count */ 957eaa8fb5SKevin Wolf int refcnt; 967eaa8fb5SKevin Wolf }; 977eaa8fb5SKevin Wolf 9855c5a25aSEmanuele Giuseppe Esposito void job_lock(void) 9955c5a25aSEmanuele Giuseppe Esposito { 100da01ff7fSKevin Wolf qemu_mutex_lock(&job_mutex); 101da01ff7fSKevin Wolf } 102da01ff7fSKevin Wolf 1036f592e5aSEmanuele Giuseppe Esposito void job_unlock(void) 104da01ff7fSKevin Wolf { 105da01ff7fSKevin Wolf qemu_mutex_unlock(&job_mutex); 106da01ff7fSKevin Wolf } 107da01ff7fSKevin Wolf 108da01ff7fSKevin Wolf static void __attribute__((__constructor__)) job_init(void) 109da01ff7fSKevin Wolf { 110da01ff7fSKevin Wolf qemu_mutex_init(&job_mutex); 111da01ff7fSKevin Wolf } 112da01ff7fSKevin Wolf 1137eaa8fb5SKevin Wolf JobTxn *job_txn_new(void) 1147eaa8fb5SKevin Wolf { 1157eaa8fb5SKevin Wolf JobTxn *txn = g_new0(JobTxn, 1); 1167eaa8fb5SKevin Wolf QLIST_INIT(&txn->jobs); 1177eaa8fb5SKevin Wolf txn->refcnt = 1; 1187eaa8fb5SKevin Wolf return txn; 1197eaa8fb5SKevin Wolf } 1207eaa8fb5SKevin Wolf 121afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 122afe1e8a7SEmanuele Giuseppe Esposito static void job_txn_ref_locked(JobTxn *txn) 1237eaa8fb5SKevin Wolf { 1247eaa8fb5SKevin Wolf txn->refcnt++; 1257eaa8fb5SKevin Wolf } 1267eaa8fb5SKevin Wolf 127afe1e8a7SEmanuele Giuseppe Esposito void job_txn_unref_locked(JobTxn *txn) 1287eaa8fb5SKevin Wolf { 1297eaa8fb5SKevin Wolf if (txn && --txn->refcnt == 0) { 1307eaa8fb5SKevin Wolf g_free(txn); 1317eaa8fb5SKevin Wolf } 1327eaa8fb5SKevin Wolf } 1337eaa8fb5SKevin Wolf 134afe1e8a7SEmanuele Giuseppe Esposito void job_txn_unref(JobTxn *txn) 135afe1e8a7SEmanuele Giuseppe Esposito { 136afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 137afe1e8a7SEmanuele Giuseppe Esposito job_txn_unref_locked(txn); 138afe1e8a7SEmanuele Giuseppe Esposito } 139afe1e8a7SEmanuele Giuseppe Esposito 140544f4d52SEmanuele Giuseppe Esposito /** 141544f4d52SEmanuele Giuseppe Esposito * @txn: The transaction (may be NULL) 142544f4d52SEmanuele Giuseppe Esposito * @job: Job to add to the transaction 143544f4d52SEmanuele Giuseppe Esposito * 144544f4d52SEmanuele Giuseppe Esposito * Add @job to the transaction. The @job must not already be in a transaction. 145544f4d52SEmanuele Giuseppe Esposito * The caller must call either job_txn_unref() or job_completed() to release 146544f4d52SEmanuele Giuseppe Esposito * the reference that is automatically grabbed here. 147544f4d52SEmanuele Giuseppe Esposito * 148544f4d52SEmanuele Giuseppe Esposito * If @txn is NULL, the function does nothing. 149afe1e8a7SEmanuele Giuseppe Esposito * 150afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex held. 151544f4d52SEmanuele Giuseppe Esposito */ 152afe1e8a7SEmanuele Giuseppe Esposito static void job_txn_add_job_locked(JobTxn *txn, Job *job) 1537eaa8fb5SKevin Wolf { 1547eaa8fb5SKevin Wolf if (!txn) { 1557eaa8fb5SKevin Wolf return; 1567eaa8fb5SKevin Wolf } 1577eaa8fb5SKevin Wolf 1587eaa8fb5SKevin Wolf assert(!job->txn); 1597eaa8fb5SKevin Wolf job->txn = txn; 1607eaa8fb5SKevin Wolf 1617eaa8fb5SKevin Wolf QLIST_INSERT_HEAD(&txn->jobs, job, txn_list); 162afe1e8a7SEmanuele Giuseppe Esposito job_txn_ref_locked(txn); 1637eaa8fb5SKevin Wolf } 1647eaa8fb5SKevin Wolf 165afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 166afe1e8a7SEmanuele Giuseppe Esposito static void job_txn_del_job_locked(Job *job) 1677eaa8fb5SKevin Wolf { 1687eaa8fb5SKevin Wolf if (job->txn) { 1697eaa8fb5SKevin Wolf QLIST_REMOVE(job, txn_list); 170afe1e8a7SEmanuele Giuseppe Esposito job_txn_unref_locked(job->txn); 1717eaa8fb5SKevin Wolf job->txn = NULL; 1727eaa8fb5SKevin Wolf } 1737eaa8fb5SKevin Wolf } 1747eaa8fb5SKevin Wolf 175afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily. */ 176afe1e8a7SEmanuele Giuseppe Esposito static int job_txn_apply_locked(Job *job, int fn(Job *)) 1777eaa8fb5SKevin Wolf { 178b660a84bSStefan Reiter Job *other_job, *next; 179b660a84bSStefan Reiter JobTxn *txn = job->txn; 1807eaa8fb5SKevin Wolf int rc = 0; 1817eaa8fb5SKevin Wolf 182b660a84bSStefan Reiter /* 183b660a84bSStefan Reiter * Similar to job_completed_txn_abort, we take each job's lock before 184b660a84bSStefan Reiter * applying fn, but since we assume that outer_ctx is held by the caller, 185b660a84bSStefan Reiter * we need to release it here to avoid holding the lock twice - which would 186b660a84bSStefan Reiter * break AIO_WAIT_WHILE from within fn. 187b660a84bSStefan Reiter */ 188afe1e8a7SEmanuele Giuseppe Esposito job_ref_locked(job); 189b660a84bSStefan Reiter 190b660a84bSStefan Reiter QLIST_FOREACH_SAFE(other_job, &txn->jobs, txn_list, next) { 191b660a84bSStefan Reiter rc = fn(other_job); 1927eaa8fb5SKevin Wolf if (rc) { 1937eaa8fb5SKevin Wolf break; 1947eaa8fb5SKevin Wolf } 1957eaa8fb5SKevin Wolf } 196b660a84bSStefan Reiter 197afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 1987eaa8fb5SKevin Wolf return rc; 1997eaa8fb5SKevin Wolf } 2007eaa8fb5SKevin Wolf 201456273b0SKevin Wolf bool job_is_internal(Job *job) 2021dac83f1SKevin Wolf { 2031dac83f1SKevin Wolf return (job->id == NULL); 2041dac83f1SKevin Wolf } 2051dac83f1SKevin Wolf 206afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 207afe1e8a7SEmanuele Giuseppe Esposito static void job_state_transition_locked(Job *job, JobStatus s1) 208a50c2ab8SKevin Wolf { 209a50c2ab8SKevin Wolf JobStatus s0 = job->status; 210c2032289SLiam Merwick assert(s1 >= 0 && s1 < JOB_STATUS__MAX); 2114ad35181SKevin Wolf trace_job_state_transition(job, job->ret, 212a50c2ab8SKevin Wolf JobSTT[s0][s1] ? "allowed" : "disallowed", 213a50c2ab8SKevin Wolf JobStatus_str(s0), JobStatus_str(s1)); 214a50c2ab8SKevin Wolf assert(JobSTT[s0][s1]); 215a50c2ab8SKevin Wolf job->status = s1; 2161dac83f1SKevin Wolf 2171dac83f1SKevin Wolf if (!job_is_internal(job) && s1 != s0) { 2183ab72385SPeter Xu qapi_event_send_job_status_change(job->id, job->status); 2191dac83f1SKevin Wolf } 220a50c2ab8SKevin Wolf } 221a50c2ab8SKevin Wolf 222afe1e8a7SEmanuele Giuseppe Esposito int job_apply_verb_locked(Job *job, JobVerb verb, Error **errp) 223a50c2ab8SKevin Wolf { 224a50c2ab8SKevin Wolf JobStatus s0 = job->status; 225c2032289SLiam Merwick assert(verb >= 0 && verb < JOB_VERB__MAX); 226a50c2ab8SKevin Wolf trace_job_apply_verb(job, JobStatus_str(s0), JobVerb_str(verb), 227a50c2ab8SKevin Wolf JobVerbTable[verb][s0] ? "allowed" : "prohibited"); 228a50c2ab8SKevin Wolf if (JobVerbTable[verb][s0]) { 229a50c2ab8SKevin Wolf return 0; 230a50c2ab8SKevin Wolf } 231a50c2ab8SKevin Wolf error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'", 232a50c2ab8SKevin Wolf job->id, JobStatus_str(s0), JobVerb_str(verb)); 233a50c2ab8SKevin Wolf return -EPERM; 234a50c2ab8SKevin Wolf } 235a50c2ab8SKevin Wolf 236afe1e8a7SEmanuele Giuseppe Esposito int job_apply_verb(Job *job, JobVerb verb, Error **errp) 237afe1e8a7SEmanuele Giuseppe Esposito { 238afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 239afe1e8a7SEmanuele Giuseppe Esposito return job_apply_verb_locked(job, verb, errp); 240afe1e8a7SEmanuele Giuseppe Esposito } 241afe1e8a7SEmanuele Giuseppe Esposito 242252291eaSKevin Wolf JobType job_type(const Job *job) 243252291eaSKevin Wolf { 244252291eaSKevin Wolf return job->driver->job_type; 245252291eaSKevin Wolf } 246252291eaSKevin Wolf 247252291eaSKevin Wolf const char *job_type_str(const Job *job) 248252291eaSKevin Wolf { 249252291eaSKevin Wolf return JobType_str(job_type(job)); 250252291eaSKevin Wolf } 251252291eaSKevin Wolf 252afe1e8a7SEmanuele Giuseppe Esposito bool job_is_cancelled_locked(Job *job) 253daa7f2f9SKevin Wolf { 254a640fa0eSHanna Reitz /* force_cancel may be true only if cancelled is true, too */ 255a640fa0eSHanna Reitz assert(job->cancelled || !job->force_cancel); 256a640fa0eSHanna Reitz return job->force_cancel; 25708b83bffSHanna Reitz } 25808b83bffSHanna Reitz 259afe1e8a7SEmanuele Giuseppe Esposito bool job_is_cancelled(Job *job) 260afe1e8a7SEmanuele Giuseppe Esposito { 261afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 262afe1e8a7SEmanuele Giuseppe Esposito return job_is_cancelled_locked(job); 263afe1e8a7SEmanuele Giuseppe Esposito } 264afe1e8a7SEmanuele Giuseppe Esposito 265afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 266afe1e8a7SEmanuele Giuseppe Esposito static bool job_cancel_requested_locked(Job *job) 26708b83bffSHanna Reitz { 268daa7f2f9SKevin Wolf return job->cancelled; 269daa7f2f9SKevin Wolf } 270daa7f2f9SKevin Wolf 271afe1e8a7SEmanuele Giuseppe Esposito bool job_cancel_requested(Job *job) 272afe1e8a7SEmanuele Giuseppe Esposito { 273afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 274afe1e8a7SEmanuele Giuseppe Esposito return job_cancel_requested_locked(job); 275afe1e8a7SEmanuele Giuseppe Esposito } 276afe1e8a7SEmanuele Giuseppe Esposito 277afe1e8a7SEmanuele Giuseppe Esposito bool job_is_ready_locked(Job *job) 278df956ae2SKevin Wolf { 279df956ae2SKevin Wolf switch (job->status) { 280df956ae2SKevin Wolf case JOB_STATUS_UNDEFINED: 281df956ae2SKevin Wolf case JOB_STATUS_CREATED: 282df956ae2SKevin Wolf case JOB_STATUS_RUNNING: 283df956ae2SKevin Wolf case JOB_STATUS_PAUSED: 284df956ae2SKevin Wolf case JOB_STATUS_WAITING: 285df956ae2SKevin Wolf case JOB_STATUS_PENDING: 286df956ae2SKevin Wolf case JOB_STATUS_ABORTING: 287df956ae2SKevin Wolf case JOB_STATUS_CONCLUDED: 288df956ae2SKevin Wolf case JOB_STATUS_NULL: 289df956ae2SKevin Wolf return false; 290df956ae2SKevin Wolf case JOB_STATUS_READY: 291df956ae2SKevin Wolf case JOB_STATUS_STANDBY: 292df956ae2SKevin Wolf return true; 293df956ae2SKevin Wolf default: 294df956ae2SKevin Wolf g_assert_not_reached(); 295df956ae2SKevin Wolf } 296df956ae2SKevin Wolf return false; 297df956ae2SKevin Wolf } 298df956ae2SKevin Wolf 299afe1e8a7SEmanuele Giuseppe Esposito bool job_is_ready(Job *job) 300afe1e8a7SEmanuele Giuseppe Esposito { 301afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 302afe1e8a7SEmanuele Giuseppe Esposito return job_is_ready_locked(job); 303afe1e8a7SEmanuele Giuseppe Esposito } 304afe1e8a7SEmanuele Giuseppe Esposito 305afe1e8a7SEmanuele Giuseppe Esposito bool job_is_completed_locked(Job *job) 306afe1e8a7SEmanuele Giuseppe Esposito { 307afe1e8a7SEmanuele Giuseppe Esposito switch (job->status) { 308afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_UNDEFINED: 309afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_CREATED: 310afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_RUNNING: 311afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_PAUSED: 312afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_READY: 313afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_STANDBY: 314afe1e8a7SEmanuele Giuseppe Esposito return false; 315afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_WAITING: 316afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_PENDING: 317afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_ABORTING: 318afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_CONCLUDED: 319afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_NULL: 320afe1e8a7SEmanuele Giuseppe Esposito return true; 321afe1e8a7SEmanuele Giuseppe Esposito default: 322afe1e8a7SEmanuele Giuseppe Esposito g_assert_not_reached(); 323afe1e8a7SEmanuele Giuseppe Esposito } 324afe1e8a7SEmanuele Giuseppe Esposito return false; 325afe1e8a7SEmanuele Giuseppe Esposito } 326afe1e8a7SEmanuele Giuseppe Esposito 327dbe5e6c1SKevin Wolf bool job_is_completed(Job *job) 328dbe5e6c1SKevin Wolf { 329afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 330afe1e8a7SEmanuele Giuseppe Esposito return job_is_completed_locked(job); 331dbe5e6c1SKevin Wolf } 332dbe5e6c1SKevin Wolf 333afe1e8a7SEmanuele Giuseppe Esposito static bool job_started_locked(Job *job) 334da01ff7fSKevin Wolf { 335da01ff7fSKevin Wolf return job->co; 336da01ff7fSKevin Wolf } 337da01ff7fSKevin Wolf 338afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 339afe1e8a7SEmanuele Giuseppe Esposito static bool job_should_pause_locked(Job *job) 340da01ff7fSKevin Wolf { 341da01ff7fSKevin Wolf return job->pause_count > 0; 342da01ff7fSKevin Wolf } 343da01ff7fSKevin Wolf 344afe1e8a7SEmanuele Giuseppe Esposito Job *job_next_locked(Job *job) 345e7c1d78bSKevin Wolf { 346e7c1d78bSKevin Wolf if (!job) { 347e7c1d78bSKevin Wolf return QLIST_FIRST(&jobs); 348e7c1d78bSKevin Wolf } 349e7c1d78bSKevin Wolf return QLIST_NEXT(job, job_list); 350e7c1d78bSKevin Wolf } 351e7c1d78bSKevin Wolf 352afe1e8a7SEmanuele Giuseppe Esposito Job *job_next(Job *job) 353afe1e8a7SEmanuele Giuseppe Esposito { 354afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 355afe1e8a7SEmanuele Giuseppe Esposito return job_next_locked(job); 356afe1e8a7SEmanuele Giuseppe Esposito } 357afe1e8a7SEmanuele Giuseppe Esposito 358afe1e8a7SEmanuele Giuseppe Esposito Job *job_get_locked(const char *id) 359e7c1d78bSKevin Wolf { 360e7c1d78bSKevin Wolf Job *job; 361e7c1d78bSKevin Wolf 362e7c1d78bSKevin Wolf QLIST_FOREACH(job, &jobs, job_list) { 363e7c1d78bSKevin Wolf if (job->id && !strcmp(id, job->id)) { 364e7c1d78bSKevin Wolf return job; 365e7c1d78bSKevin Wolf } 366e7c1d78bSKevin Wolf } 367e7c1d78bSKevin Wolf 368e7c1d78bSKevin Wolf return NULL; 369e7c1d78bSKevin Wolf } 370e7c1d78bSKevin Wolf 371afe1e8a7SEmanuele Giuseppe Esposito Job *job_get(const char *id) 372afe1e8a7SEmanuele Giuseppe Esposito { 373afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 374afe1e8a7SEmanuele Giuseppe Esposito return job_get_locked(id); 375afe1e8a7SEmanuele Giuseppe Esposito } 376afe1e8a7SEmanuele Giuseppe Esposito 3773ed4f708SEmanuele Giuseppe Esposito void job_set_aio_context(Job *job, AioContext *ctx) 3783ed4f708SEmanuele Giuseppe Esposito { 3793ed4f708SEmanuele Giuseppe Esposito /* protect against read in job_finish_sync_locked and job_start */ 3803ed4f708SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 3813ed4f708SEmanuele Giuseppe Esposito /* protect against read in job_do_yield_locked */ 3823ed4f708SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 3833ed4f708SEmanuele Giuseppe Esposito /* ensure the job is quiescent while the AioContext is changed */ 3843ed4f708SEmanuele Giuseppe Esposito assert(job->paused || job_is_completed_locked(job)); 3853ed4f708SEmanuele Giuseppe Esposito job->aio_context = ctx; 3863ed4f708SEmanuele Giuseppe Esposito } 3873ed4f708SEmanuele Giuseppe Esposito 388afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex *not* held. */ 3895d43e86eSKevin Wolf static void job_sleep_timer_cb(void *opaque) 3905d43e86eSKevin Wolf { 3915d43e86eSKevin Wolf Job *job = opaque; 3925d43e86eSKevin Wolf 3935d43e86eSKevin Wolf job_enter(job); 3945d43e86eSKevin Wolf } 3955d43e86eSKevin Wolf 3967eaa8fb5SKevin Wolf void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn, 3977eaa8fb5SKevin Wolf AioContext *ctx, int flags, BlockCompletionFunc *cb, 3987eaa8fb5SKevin Wolf void *opaque, Error **errp) 39933e9e9bdSKevin Wolf { 40033e9e9bdSKevin Wolf Job *job; 40133e9e9bdSKevin Wolf 402afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 403afe1e8a7SEmanuele Giuseppe Esposito 40433e9e9bdSKevin Wolf if (job_id) { 405bb02b65cSKevin Wolf if (flags & JOB_INTERNAL) { 406bb02b65cSKevin Wolf error_setg(errp, "Cannot specify job ID for internal job"); 407bb02b65cSKevin Wolf return NULL; 408bb02b65cSKevin Wolf } 40933e9e9bdSKevin Wolf if (!id_wellformed(job_id)) { 41033e9e9bdSKevin Wolf error_setg(errp, "Invalid job ID '%s'", job_id); 41133e9e9bdSKevin Wolf return NULL; 41233e9e9bdSKevin Wolf } 413afe1e8a7SEmanuele Giuseppe Esposito if (job_get_locked(job_id)) { 414e7c1d78bSKevin Wolf error_setg(errp, "Job ID '%s' already in use", job_id); 415e7c1d78bSKevin Wolf return NULL; 416e7c1d78bSKevin Wolf } 417bb02b65cSKevin Wolf } else if (!(flags & JOB_INTERNAL)) { 418bb02b65cSKevin Wolf error_setg(errp, "An explicit job ID is required"); 419bb02b65cSKevin Wolf return NULL; 42033e9e9bdSKevin Wolf } 42133e9e9bdSKevin Wolf 42233e9e9bdSKevin Wolf job = g_malloc0(driver->instance_size); 42333e9e9bdSKevin Wolf job->driver = driver; 42433e9e9bdSKevin Wolf job->id = g_strdup(job_id); 42580fa2c75SKevin Wolf job->refcnt = 1; 42608be6fe2SKevin Wolf job->aio_context = ctx; 427da01ff7fSKevin Wolf job->busy = false; 428da01ff7fSKevin Wolf job->paused = true; 429da01ff7fSKevin Wolf job->pause_count = 1; 430bb02b65cSKevin Wolf job->auto_finalize = !(flags & JOB_MANUAL_FINALIZE); 431bb02b65cSKevin Wolf job->auto_dismiss = !(flags & JOB_MANUAL_DISMISS); 4324ad35181SKevin Wolf job->cb = cb; 4334ad35181SKevin Wolf job->opaque = opaque; 43433e9e9bdSKevin Wolf 435a7b4f8fcSEmanuele Giuseppe Esposito progress_init(&job->progress); 436a7b4f8fcSEmanuele Giuseppe Esposito 437139a9f02SKevin Wolf notifier_list_init(&job->on_finalize_cancelled); 438139a9f02SKevin Wolf notifier_list_init(&job->on_finalize_completed); 439139a9f02SKevin Wolf notifier_list_init(&job->on_pending); 4402e1795b5SKevin Wolf notifier_list_init(&job->on_ready); 441252f4091SEmanuele Giuseppe Esposito notifier_list_init(&job->on_idle); 442139a9f02SKevin Wolf 443afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_CREATED); 4445d43e86eSKevin Wolf aio_timer_init(qemu_get_aio_context(), &job->sleep_timer, 4455d43e86eSKevin Wolf QEMU_CLOCK_REALTIME, SCALE_NS, 4465d43e86eSKevin Wolf job_sleep_timer_cb, job); 447a50c2ab8SKevin Wolf 448e7c1d78bSKevin Wolf QLIST_INSERT_HEAD(&jobs, job, job_list); 449e7c1d78bSKevin Wolf 4507eaa8fb5SKevin Wolf /* Single jobs are modeled as single-job transactions for sake of 4517eaa8fb5SKevin Wolf * consolidating the job management logic */ 4527eaa8fb5SKevin Wolf if (!txn) { 4537eaa8fb5SKevin Wolf txn = job_txn_new(); 454afe1e8a7SEmanuele Giuseppe Esposito job_txn_add_job_locked(txn, job); 455afe1e8a7SEmanuele Giuseppe Esposito job_txn_unref_locked(txn); 4567eaa8fb5SKevin Wolf } else { 457afe1e8a7SEmanuele Giuseppe Esposito job_txn_add_job_locked(txn, job); 4587eaa8fb5SKevin Wolf } 4597eaa8fb5SKevin Wolf 46033e9e9bdSKevin Wolf return job; 46133e9e9bdSKevin Wolf } 462fd61a701SKevin Wolf 463afe1e8a7SEmanuele Giuseppe Esposito void job_ref_locked(Job *job) 464fd61a701SKevin Wolf { 46580fa2c75SKevin Wolf ++job->refcnt; 46680fa2c75SKevin Wolf } 46780fa2c75SKevin Wolf 468afe1e8a7SEmanuele Giuseppe Esposito void job_ref(Job *job) 469afe1e8a7SEmanuele Giuseppe Esposito { 470afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 471afe1e8a7SEmanuele Giuseppe Esposito job_ref_locked(job); 472afe1e8a7SEmanuele Giuseppe Esposito } 473afe1e8a7SEmanuele Giuseppe Esposito 474afe1e8a7SEmanuele Giuseppe Esposito void job_unref_locked(Job *job) 47580fa2c75SKevin Wolf { 476c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 477c70b8031SEmanuele Giuseppe Esposito 47880fa2c75SKevin Wolf if (--job->refcnt == 0) { 47980fa2c75SKevin Wolf assert(job->status == JOB_STATUS_NULL); 4805d43e86eSKevin Wolf assert(!timer_pending(&job->sleep_timer)); 4817eaa8fb5SKevin Wolf assert(!job->txn); 48280fa2c75SKevin Wolf 48380fa2c75SKevin Wolf if (job->driver->free) { 4846f592e5aSEmanuele Giuseppe Esposito AioContext *aio_context = job->aio_context; 485afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 4866f592e5aSEmanuele Giuseppe Esposito /* FIXME: aiocontext lock is required because cb calls blk_unref */ 4876f592e5aSEmanuele Giuseppe Esposito aio_context_acquire(aio_context); 48880fa2c75SKevin Wolf job->driver->free(job); 4896f592e5aSEmanuele Giuseppe Esposito aio_context_release(aio_context); 490afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 49180fa2c75SKevin Wolf } 49280fa2c75SKevin Wolf 493e7c1d78bSKevin Wolf QLIST_REMOVE(job, job_list); 494e7c1d78bSKevin Wolf 495a7b4f8fcSEmanuele Giuseppe Esposito progress_destroy(&job->progress); 4963d1f8b07SJohn Snow error_free(job->err); 497fd61a701SKevin Wolf g_free(job->id); 498fd61a701SKevin Wolf g_free(job); 499fd61a701SKevin Wolf } 50080fa2c75SKevin Wolf } 5011908a559SKevin Wolf 502afe1e8a7SEmanuele Giuseppe Esposito void job_unref(Job *job) 503afe1e8a7SEmanuele Giuseppe Esposito { 504afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 505afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 506afe1e8a7SEmanuele Giuseppe Esposito } 507afe1e8a7SEmanuele Giuseppe Esposito 50830a5c887SKevin Wolf void job_progress_update(Job *job, uint64_t done) 50930a5c887SKevin Wolf { 51001fe1ca9SVladimir Sementsov-Ogievskiy progress_work_done(&job->progress, done); 51130a5c887SKevin Wolf } 51230a5c887SKevin Wolf 51330a5c887SKevin Wolf void job_progress_set_remaining(Job *job, uint64_t remaining) 51430a5c887SKevin Wolf { 51501fe1ca9SVladimir Sementsov-Ogievskiy progress_set_remaining(&job->progress, remaining); 51630a5c887SKevin Wolf } 51730a5c887SKevin Wolf 51862f13600SMax Reitz void job_progress_increase_remaining(Job *job, uint64_t delta) 51962f13600SMax Reitz { 52001fe1ca9SVladimir Sementsov-Ogievskiy progress_increase_remaining(&job->progress, delta); 52162f13600SMax Reitz } 52262f13600SMax Reitz 523544f4d52SEmanuele Giuseppe Esposito /** 524544f4d52SEmanuele Giuseppe Esposito * To be called when a cancelled job is finalised. 525afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex held. 526544f4d52SEmanuele Giuseppe Esposito */ 527afe1e8a7SEmanuele Giuseppe Esposito static void job_event_cancelled_locked(Job *job) 528139a9f02SKevin Wolf { 529139a9f02SKevin Wolf notifier_list_notify(&job->on_finalize_cancelled, job); 530139a9f02SKevin Wolf } 531139a9f02SKevin Wolf 532544f4d52SEmanuele Giuseppe Esposito /** 533544f4d52SEmanuele Giuseppe Esposito * To be called when a successfully completed job is finalised. 534afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex held. 535544f4d52SEmanuele Giuseppe Esposito */ 536afe1e8a7SEmanuele Giuseppe Esposito static void job_event_completed_locked(Job *job) 537139a9f02SKevin Wolf { 538139a9f02SKevin Wolf notifier_list_notify(&job->on_finalize_completed, job); 539139a9f02SKevin Wolf } 540139a9f02SKevin Wolf 541afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 542afe1e8a7SEmanuele Giuseppe Esposito static void job_event_pending_locked(Job *job) 543139a9f02SKevin Wolf { 544139a9f02SKevin Wolf notifier_list_notify(&job->on_pending, job); 545139a9f02SKevin Wolf } 546139a9f02SKevin Wolf 547afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 548afe1e8a7SEmanuele Giuseppe Esposito static void job_event_ready_locked(Job *job) 5492e1795b5SKevin Wolf { 5502e1795b5SKevin Wolf notifier_list_notify(&job->on_ready, job); 5512e1795b5SKevin Wolf } 5522e1795b5SKevin Wolf 553afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 554afe1e8a7SEmanuele Giuseppe Esposito static void job_event_idle_locked(Job *job) 55534dc97b9SKevin Wolf { 55634dc97b9SKevin Wolf notifier_list_notify(&job->on_idle, job); 55734dc97b9SKevin Wolf } 55834dc97b9SKevin Wolf 559afe1e8a7SEmanuele Giuseppe Esposito void job_enter_cond_locked(Job *job, bool(*fn)(Job *job)) 560da01ff7fSKevin Wolf { 561afe1e8a7SEmanuele Giuseppe Esposito if (!job_started_locked(job)) { 562da01ff7fSKevin Wolf return; 563da01ff7fSKevin Wolf } 564da01ff7fSKevin Wolf if (job->deferred_to_main_loop) { 565da01ff7fSKevin Wolf return; 566da01ff7fSKevin Wolf } 567da01ff7fSKevin Wolf 568da01ff7fSKevin Wolf if (job->busy) { 569da01ff7fSKevin Wolf return; 570da01ff7fSKevin Wolf } 571da01ff7fSKevin Wolf 572da01ff7fSKevin Wolf if (fn && !fn(job)) { 573da01ff7fSKevin Wolf return; 574da01ff7fSKevin Wolf } 575da01ff7fSKevin Wolf 576da01ff7fSKevin Wolf assert(!job->deferred_to_main_loop); 577da01ff7fSKevin Wolf timer_del(&job->sleep_timer); 578da01ff7fSKevin Wolf job->busy = true; 579afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 580ef02dac2SPaolo Bonzini aio_co_wake(job->co); 581afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 582afe1e8a7SEmanuele Giuseppe Esposito } 583afe1e8a7SEmanuele Giuseppe Esposito 584afe1e8a7SEmanuele Giuseppe Esposito void job_enter_cond(Job *job, bool(*fn)(Job *job)) 585afe1e8a7SEmanuele Giuseppe Esposito { 586afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 587afe1e8a7SEmanuele Giuseppe Esposito job_enter_cond_locked(job, fn); 588da01ff7fSKevin Wolf } 589da01ff7fSKevin Wolf 5905d43e86eSKevin Wolf void job_enter(Job *job) 5915d43e86eSKevin Wolf { 592afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 593afe1e8a7SEmanuele Giuseppe Esposito job_enter_cond_locked(job, NULL); 5945d43e86eSKevin Wolf } 5955d43e86eSKevin Wolf 596da01ff7fSKevin Wolf /* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds. 5973d70ff53SKevin Wolf * Reentering the job coroutine with job_enter() before the timer has expired 5983d70ff53SKevin Wolf * is allowed and cancels the timer. 599da01ff7fSKevin Wolf * 6003d70ff53SKevin Wolf * If @ns is (uint64_t) -1, no timer is scheduled and job_enter() must be 601afe1e8a7SEmanuele Giuseppe Esposito * called explicitly. 602afe1e8a7SEmanuele Giuseppe Esposito * 603afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex held, but releases it temporarily. 604afe1e8a7SEmanuele Giuseppe Esposito */ 605afe1e8a7SEmanuele Giuseppe Esposito static void coroutine_fn job_do_yield_locked(Job *job, uint64_t ns) 606da01ff7fSKevin Wolf { 607ef02dac2SPaolo Bonzini AioContext *next_aio_context; 608ef02dac2SPaolo Bonzini 609da01ff7fSKevin Wolf if (ns != -1) { 610da01ff7fSKevin Wolf timer_mod(&job->sleep_timer, ns); 611da01ff7fSKevin Wolf } 612da01ff7fSKevin Wolf job->busy = false; 613afe1e8a7SEmanuele Giuseppe Esposito job_event_idle_locked(job); 614afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 615da01ff7fSKevin Wolf qemu_coroutine_yield(); 616afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 617da01ff7fSKevin Wolf 618ef02dac2SPaolo Bonzini next_aio_context = job->aio_context; 619ef02dac2SPaolo Bonzini /* 620ef02dac2SPaolo Bonzini * Coroutine has resumed, but in the meanwhile the job AioContext 621ef02dac2SPaolo Bonzini * might have changed via bdrv_try_set_aio_context(), so we need to move 622ef02dac2SPaolo Bonzini * the coroutine too in the new aiocontext. 623ef02dac2SPaolo Bonzini */ 624ef02dac2SPaolo Bonzini while (qemu_get_current_aio_context() != next_aio_context) { 625ef02dac2SPaolo Bonzini job_unlock(); 626ef02dac2SPaolo Bonzini aio_co_reschedule_self(next_aio_context); 627ef02dac2SPaolo Bonzini job_lock(); 628ef02dac2SPaolo Bonzini next_aio_context = job->aio_context; 629ef02dac2SPaolo Bonzini } 630ef02dac2SPaolo Bonzini 631ef02dac2SPaolo Bonzini /* Set by job_enter_cond_locked() before re-entering the coroutine. */ 632da01ff7fSKevin Wolf assert(job->busy); 633da01ff7fSKevin Wolf } 634da01ff7fSKevin Wolf 635afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily. */ 636afe1e8a7SEmanuele Giuseppe Esposito static void coroutine_fn job_pause_point_locked(Job *job) 637da01ff7fSKevin Wolf { 638afe1e8a7SEmanuele Giuseppe Esposito assert(job && job_started_locked(job)); 639da01ff7fSKevin Wolf 640afe1e8a7SEmanuele Giuseppe Esposito if (!job_should_pause_locked(job)) { 641da01ff7fSKevin Wolf return; 642da01ff7fSKevin Wolf } 643afe1e8a7SEmanuele Giuseppe Esposito if (job_is_cancelled_locked(job)) { 644da01ff7fSKevin Wolf return; 645da01ff7fSKevin Wolf } 646da01ff7fSKevin Wolf 647da01ff7fSKevin Wolf if (job->driver->pause) { 648afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 649da01ff7fSKevin Wolf job->driver->pause(job); 650afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 651da01ff7fSKevin Wolf } 652da01ff7fSKevin Wolf 653afe1e8a7SEmanuele Giuseppe Esposito if (job_should_pause_locked(job) && !job_is_cancelled_locked(job)) { 654da01ff7fSKevin Wolf JobStatus status = job->status; 655afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, status == JOB_STATUS_READY 656da01ff7fSKevin Wolf ? JOB_STATUS_STANDBY 657da01ff7fSKevin Wolf : JOB_STATUS_PAUSED); 658da01ff7fSKevin Wolf job->paused = true; 659afe1e8a7SEmanuele Giuseppe Esposito job_do_yield_locked(job, -1); 660da01ff7fSKevin Wolf job->paused = false; 661afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, status); 662da01ff7fSKevin Wolf } 663da01ff7fSKevin Wolf 664da01ff7fSKevin Wolf if (job->driver->resume) { 665afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 666da01ff7fSKevin Wolf job->driver->resume(job); 667afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 668da01ff7fSKevin Wolf } 669da01ff7fSKevin Wolf } 670da01ff7fSKevin Wolf 671afe1e8a7SEmanuele Giuseppe Esposito void coroutine_fn job_pause_point(Job *job) 672afe1e8a7SEmanuele Giuseppe Esposito { 673afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 674afe1e8a7SEmanuele Giuseppe Esposito job_pause_point_locked(job); 675afe1e8a7SEmanuele Giuseppe Esposito } 676afe1e8a7SEmanuele Giuseppe Esposito 677afe1e8a7SEmanuele Giuseppe Esposito static void coroutine_fn job_yield_locked(Job *job) 678afe1e8a7SEmanuele Giuseppe Esposito { 679afe1e8a7SEmanuele Giuseppe Esposito assert(job->busy); 680afe1e8a7SEmanuele Giuseppe Esposito 681afe1e8a7SEmanuele Giuseppe Esposito /* Check cancellation *before* setting busy = false, too! */ 682afe1e8a7SEmanuele Giuseppe Esposito if (job_is_cancelled_locked(job)) { 683afe1e8a7SEmanuele Giuseppe Esposito return; 684afe1e8a7SEmanuele Giuseppe Esposito } 685afe1e8a7SEmanuele Giuseppe Esposito 686afe1e8a7SEmanuele Giuseppe Esposito if (!job_should_pause_locked(job)) { 687afe1e8a7SEmanuele Giuseppe Esposito job_do_yield_locked(job, -1); 688afe1e8a7SEmanuele Giuseppe Esposito } 689afe1e8a7SEmanuele Giuseppe Esposito 690afe1e8a7SEmanuele Giuseppe Esposito job_pause_point_locked(job); 691afe1e8a7SEmanuele Giuseppe Esposito } 692afe1e8a7SEmanuele Giuseppe Esposito 69306753a07SPaolo Bonzini void coroutine_fn job_yield(Job *job) 694198c49ccSKevin Wolf { 695afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 696afe1e8a7SEmanuele Giuseppe Esposito job_yield_locked(job); 697198c49ccSKevin Wolf } 698198c49ccSKevin Wolf 6995d43e86eSKevin Wolf void coroutine_fn job_sleep_ns(Job *job, int64_t ns) 7005d43e86eSKevin Wolf { 701afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 7025d43e86eSKevin Wolf assert(job->busy); 7035d43e86eSKevin Wolf 7045d43e86eSKevin Wolf /* Check cancellation *before* setting busy = false, too! */ 705afe1e8a7SEmanuele Giuseppe Esposito if (job_is_cancelled_locked(job)) { 7065d43e86eSKevin Wolf return; 7075d43e86eSKevin Wolf } 7085d43e86eSKevin Wolf 709afe1e8a7SEmanuele Giuseppe Esposito if (!job_should_pause_locked(job)) { 710afe1e8a7SEmanuele Giuseppe Esposito job_do_yield_locked(job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns); 7115d43e86eSKevin Wolf } 7125d43e86eSKevin Wolf 713afe1e8a7SEmanuele Giuseppe Esposito job_pause_point_locked(job); 7145d43e86eSKevin Wolf } 7155d43e86eSKevin Wolf 716afe1e8a7SEmanuele Giuseppe Esposito /* Assumes the job_mutex is held */ 717afe1e8a7SEmanuele Giuseppe Esposito static bool job_timer_not_pending_locked(Job *job) 718b15de828SKevin Wolf { 719b15de828SKevin Wolf return !timer_pending(&job->sleep_timer); 720b15de828SKevin Wolf } 721b15de828SKevin Wolf 722afe1e8a7SEmanuele Giuseppe Esposito void job_pause_locked(Job *job) 723b15de828SKevin Wolf { 724b15de828SKevin Wolf job->pause_count++; 7253ee1483bSVladimir Sementsov-Ogievskiy if (!job->paused) { 726afe1e8a7SEmanuele Giuseppe Esposito job_enter_cond_locked(job, NULL); 7273ee1483bSVladimir Sementsov-Ogievskiy } 728b15de828SKevin Wolf } 729b15de828SKevin Wolf 730afe1e8a7SEmanuele Giuseppe Esposito void job_pause(Job *job) 731afe1e8a7SEmanuele Giuseppe Esposito { 732afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 733afe1e8a7SEmanuele Giuseppe Esposito job_pause_locked(job); 734afe1e8a7SEmanuele Giuseppe Esposito } 735afe1e8a7SEmanuele Giuseppe Esposito 736afe1e8a7SEmanuele Giuseppe Esposito void job_resume_locked(Job *job) 737b15de828SKevin Wolf { 738b15de828SKevin Wolf assert(job->pause_count > 0); 739b15de828SKevin Wolf job->pause_count--; 740b15de828SKevin Wolf if (job->pause_count) { 741b15de828SKevin Wolf return; 742b15de828SKevin Wolf } 743b15de828SKevin Wolf 744b15de828SKevin Wolf /* kick only if no timer is pending */ 745afe1e8a7SEmanuele Giuseppe Esposito job_enter_cond_locked(job, job_timer_not_pending_locked); 746b15de828SKevin Wolf } 747b15de828SKevin Wolf 748afe1e8a7SEmanuele Giuseppe Esposito void job_resume(Job *job) 749b15de828SKevin Wolf { 750afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 751afe1e8a7SEmanuele Giuseppe Esposito job_resume_locked(job); 752afe1e8a7SEmanuele Giuseppe Esposito } 753afe1e8a7SEmanuele Giuseppe Esposito 754afe1e8a7SEmanuele Giuseppe Esposito void job_user_pause_locked(Job *job, Error **errp) 755afe1e8a7SEmanuele Giuseppe Esposito { 756afe1e8a7SEmanuele Giuseppe Esposito if (job_apply_verb_locked(job, JOB_VERB_PAUSE, errp)) { 757b15de828SKevin Wolf return; 758b15de828SKevin Wolf } 759b15de828SKevin Wolf if (job->user_paused) { 760b15de828SKevin Wolf error_setg(errp, "Job is already paused"); 761b15de828SKevin Wolf return; 762b15de828SKevin Wolf } 763b15de828SKevin Wolf job->user_paused = true; 764afe1e8a7SEmanuele Giuseppe Esposito job_pause_locked(job); 765b15de828SKevin Wolf } 766b15de828SKevin Wolf 767afe1e8a7SEmanuele Giuseppe Esposito void job_user_pause(Job *job, Error **errp) 768afe1e8a7SEmanuele Giuseppe Esposito { 769afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 770afe1e8a7SEmanuele Giuseppe Esposito job_user_pause_locked(job, errp); 771afe1e8a7SEmanuele Giuseppe Esposito } 772afe1e8a7SEmanuele Giuseppe Esposito 773afe1e8a7SEmanuele Giuseppe Esposito bool job_user_paused_locked(Job *job) 774b15de828SKevin Wolf { 775b15de828SKevin Wolf return job->user_paused; 776b15de828SKevin Wolf } 777b15de828SKevin Wolf 778afe1e8a7SEmanuele Giuseppe Esposito bool job_user_paused(Job *job) 779afe1e8a7SEmanuele Giuseppe Esposito { 780afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 781afe1e8a7SEmanuele Giuseppe Esposito return job_user_paused_locked(job); 782afe1e8a7SEmanuele Giuseppe Esposito } 783afe1e8a7SEmanuele Giuseppe Esposito 784afe1e8a7SEmanuele Giuseppe Esposito void job_user_resume_locked(Job *job, Error **errp) 785b15de828SKevin Wolf { 786b15de828SKevin Wolf assert(job); 787c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 788b15de828SKevin Wolf if (!job->user_paused || job->pause_count <= 0) { 789b15de828SKevin Wolf error_setg(errp, "Can't resume a job that was not paused"); 790b15de828SKevin Wolf return; 791b15de828SKevin Wolf } 792afe1e8a7SEmanuele Giuseppe Esposito if (job_apply_verb_locked(job, JOB_VERB_RESUME, errp)) { 793b15de828SKevin Wolf return; 794b15de828SKevin Wolf } 795b15de828SKevin Wolf if (job->driver->user_resume) { 796afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 797b15de828SKevin Wolf job->driver->user_resume(job); 798afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 799b15de828SKevin Wolf } 800b15de828SKevin Wolf job->user_paused = false; 801afe1e8a7SEmanuele Giuseppe Esposito job_resume_locked(job); 802b15de828SKevin Wolf } 803b15de828SKevin Wolf 804afe1e8a7SEmanuele Giuseppe Esposito void job_user_resume(Job *job, Error **errp) 805afe1e8a7SEmanuele Giuseppe Esposito { 806afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 807afe1e8a7SEmanuele Giuseppe Esposito job_user_resume_locked(job, errp); 808afe1e8a7SEmanuele Giuseppe Esposito } 809afe1e8a7SEmanuele Giuseppe Esposito 810afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily. */ 811afe1e8a7SEmanuele Giuseppe Esposito static void job_do_dismiss_locked(Job *job) 8124ad35181SKevin Wolf { 8134ad35181SKevin Wolf assert(job); 8144ad35181SKevin Wolf job->busy = false; 8154ad35181SKevin Wolf job->paused = false; 8164ad35181SKevin Wolf job->deferred_to_main_loop = true; 8174ad35181SKevin Wolf 818afe1e8a7SEmanuele Giuseppe Esposito job_txn_del_job_locked(job); 8194ad35181SKevin Wolf 820afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_NULL); 821afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 8224ad35181SKevin Wolf } 8234ad35181SKevin Wolf 824afe1e8a7SEmanuele Giuseppe Esposito void job_dismiss_locked(Job **jobptr, Error **errp) 8255f9a6a08SKevin Wolf { 8265f9a6a08SKevin Wolf Job *job = *jobptr; 8275f9a6a08SKevin Wolf /* similarly to _complete, this is QMP-interface only. */ 8285f9a6a08SKevin Wolf assert(job->id); 829afe1e8a7SEmanuele Giuseppe Esposito if (job_apply_verb_locked(job, JOB_VERB_DISMISS, errp)) { 8305f9a6a08SKevin Wolf return; 8315f9a6a08SKevin Wolf } 8325f9a6a08SKevin Wolf 833afe1e8a7SEmanuele Giuseppe Esposito job_do_dismiss_locked(job); 8345f9a6a08SKevin Wolf *jobptr = NULL; 8355f9a6a08SKevin Wolf } 8365f9a6a08SKevin Wolf 837afe1e8a7SEmanuele Giuseppe Esposito void job_dismiss(Job **jobptr, Error **errp) 838afe1e8a7SEmanuele Giuseppe Esposito { 839afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 840afe1e8a7SEmanuele Giuseppe Esposito job_dismiss_locked(jobptr, errp); 841afe1e8a7SEmanuele Giuseppe Esposito } 842afe1e8a7SEmanuele Giuseppe Esposito 8434ad35181SKevin Wolf void job_early_fail(Job *job) 8444ad35181SKevin Wolf { 845afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 8464ad35181SKevin Wolf assert(job->status == JOB_STATUS_CREATED); 847afe1e8a7SEmanuele Giuseppe Esposito job_do_dismiss_locked(job); 8484ad35181SKevin Wolf } 8494ad35181SKevin Wolf 850afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 851afe1e8a7SEmanuele Giuseppe Esposito static void job_conclude_locked(Job *job) 8524ad35181SKevin Wolf { 853afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_CONCLUDED); 854afe1e8a7SEmanuele Giuseppe Esposito if (job->auto_dismiss || !job_started_locked(job)) { 855afe1e8a7SEmanuele Giuseppe Esposito job_do_dismiss_locked(job); 8564ad35181SKevin Wolf } 8574ad35181SKevin Wolf } 8584ad35181SKevin Wolf 859afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 860afe1e8a7SEmanuele Giuseppe Esposito static void job_update_rc_locked(Job *job) 8614ad35181SKevin Wolf { 862afe1e8a7SEmanuele Giuseppe Esposito if (!job->ret && job_is_cancelled_locked(job)) { 8634ad35181SKevin Wolf job->ret = -ECANCELED; 8644ad35181SKevin Wolf } 8654ad35181SKevin Wolf if (job->ret) { 8663d1f8b07SJohn Snow if (!job->err) { 8673d1f8b07SJohn Snow error_setg(&job->err, "%s", strerror(-job->ret)); 8681266c9b9SKevin Wolf } 869afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_ABORTING); 8704ad35181SKevin Wolf } 8714ad35181SKevin Wolf } 8724ad35181SKevin Wolf 8734ad35181SKevin Wolf static void job_commit(Job *job) 8744ad35181SKevin Wolf { 8754ad35181SKevin Wolf assert(!job->ret); 876c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 8774ad35181SKevin Wolf if (job->driver->commit) { 8784ad35181SKevin Wolf job->driver->commit(job); 8794ad35181SKevin Wolf } 8804ad35181SKevin Wolf } 8814ad35181SKevin Wolf 8824ad35181SKevin Wolf static void job_abort(Job *job) 8834ad35181SKevin Wolf { 8844ad35181SKevin Wolf assert(job->ret); 885c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 8864ad35181SKevin Wolf if (job->driver->abort) { 8874ad35181SKevin Wolf job->driver->abort(job); 8884ad35181SKevin Wolf } 8894ad35181SKevin Wolf } 8904ad35181SKevin Wolf 8914ad35181SKevin Wolf static void job_clean(Job *job) 8924ad35181SKevin Wolf { 893c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 8944ad35181SKevin Wolf if (job->driver->clean) { 8954ad35181SKevin Wolf job->driver->clean(job); 8964ad35181SKevin Wolf } 8974ad35181SKevin Wolf } 8984ad35181SKevin Wolf 8996f592e5aSEmanuele Giuseppe Esposito /* 9006f592e5aSEmanuele Giuseppe Esposito * Called with job_mutex held, but releases it temporarily. 9016f592e5aSEmanuele Giuseppe Esposito * Takes AioContext lock internally to invoke a job->driver callback. 9026f592e5aSEmanuele Giuseppe Esposito */ 903afe1e8a7SEmanuele Giuseppe Esposito static int job_finalize_single_locked(Job *job) 9044ad35181SKevin Wolf { 905afe1e8a7SEmanuele Giuseppe Esposito int job_ret; 9066f592e5aSEmanuele Giuseppe Esposito AioContext *ctx = job->aio_context; 907afe1e8a7SEmanuele Giuseppe Esposito 908afe1e8a7SEmanuele Giuseppe Esposito assert(job_is_completed_locked(job)); 9094ad35181SKevin Wolf 9104ad35181SKevin Wolf /* Ensure abort is called for late-transactional failures */ 911afe1e8a7SEmanuele Giuseppe Esposito job_update_rc_locked(job); 9124ad35181SKevin Wolf 913afe1e8a7SEmanuele Giuseppe Esposito job_ret = job->ret; 914afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 9156f592e5aSEmanuele Giuseppe Esposito aio_context_acquire(ctx); 916afe1e8a7SEmanuele Giuseppe Esposito 917afe1e8a7SEmanuele Giuseppe Esposito if (!job_ret) { 9184ad35181SKevin Wolf job_commit(job); 9194ad35181SKevin Wolf } else { 9204ad35181SKevin Wolf job_abort(job); 9214ad35181SKevin Wolf } 9224ad35181SKevin Wolf job_clean(job); 9234ad35181SKevin Wolf 9244ad35181SKevin Wolf if (job->cb) { 925afe1e8a7SEmanuele Giuseppe Esposito job->cb(job->opaque, job_ret); 9264ad35181SKevin Wolf } 9274ad35181SKevin Wolf 9286f592e5aSEmanuele Giuseppe Esposito aio_context_release(ctx); 9296f592e5aSEmanuele Giuseppe Esposito job_lock(); 9306f592e5aSEmanuele Giuseppe Esposito 9314ad35181SKevin Wolf /* Emit events only if we actually started */ 932afe1e8a7SEmanuele Giuseppe Esposito if (job_started_locked(job)) { 933afe1e8a7SEmanuele Giuseppe Esposito if (job_is_cancelled_locked(job)) { 934afe1e8a7SEmanuele Giuseppe Esposito job_event_cancelled_locked(job); 9354ad35181SKevin Wolf } else { 936afe1e8a7SEmanuele Giuseppe Esposito job_event_completed_locked(job); 9374ad35181SKevin Wolf } 9384ad35181SKevin Wolf } 9394ad35181SKevin Wolf 940afe1e8a7SEmanuele Giuseppe Esposito job_txn_del_job_locked(job); 941afe1e8a7SEmanuele Giuseppe Esposito job_conclude_locked(job); 9424ad35181SKevin Wolf return 0; 9434ad35181SKevin Wolf } 9444ad35181SKevin Wolf 9456f592e5aSEmanuele Giuseppe Esposito /* 9466f592e5aSEmanuele Giuseppe Esposito * Called with job_mutex held, but releases it temporarily. 9476f592e5aSEmanuele Giuseppe Esposito * Takes AioContext lock internally to invoke a job->driver callback. 9486f592e5aSEmanuele Giuseppe Esposito */ 949afe1e8a7SEmanuele Giuseppe Esposito static void job_cancel_async_locked(Job *job, bool force) 9507eaa8fb5SKevin Wolf { 9516f592e5aSEmanuele Giuseppe Esposito AioContext *ctx = job->aio_context; 952c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 9539820933bSVladimir Sementsov-Ogievskiy if (job->driver->cancel) { 954afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 9556f592e5aSEmanuele Giuseppe Esposito aio_context_acquire(ctx); 95673895f38SHanna Reitz force = job->driver->cancel(job, force); 9576f592e5aSEmanuele Giuseppe Esposito aio_context_release(ctx); 958afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 95973895f38SHanna Reitz } else { 96073895f38SHanna Reitz /* No .cancel() means the job will behave as if force-cancelled */ 96173895f38SHanna Reitz force = true; 9629820933bSVladimir Sementsov-Ogievskiy } 96373895f38SHanna Reitz 9647eaa8fb5SKevin Wolf if (job->user_paused) { 9657eaa8fb5SKevin Wolf /* Do not call job_enter here, the caller will handle it. */ 9667eaa8fb5SKevin Wolf if (job->driver->user_resume) { 967afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 9687eaa8fb5SKevin Wolf job->driver->user_resume(job); 969afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 9707eaa8fb5SKevin Wolf } 971e321c059SJeff Cody job->user_paused = false; 9727eaa8fb5SKevin Wolf assert(job->pause_count > 0); 9737eaa8fb5SKevin Wolf job->pause_count--; 9747eaa8fb5SKevin Wolf } 975401dd096SHanna Reitz 976401dd096SHanna Reitz /* 977401dd096SHanna Reitz * Ignore soft cancel requests after the job is already done 978401dd096SHanna Reitz * (We will still invoke job->driver->cancel() above, but if the 979401dd096SHanna Reitz * job driver supports soft cancelling and the job is done, that 980401dd096SHanna Reitz * should be a no-op, too. We still call it so it can override 981401dd096SHanna Reitz * @force.) 982401dd096SHanna Reitz */ 983401dd096SHanna Reitz if (force || !job->deferred_to_main_loop) { 9847eaa8fb5SKevin Wolf job->cancelled = true; 9857eaa8fb5SKevin Wolf /* To prevent 'force == false' overriding a previous 'force == true' */ 9867eaa8fb5SKevin Wolf job->force_cancel |= force; 9877eaa8fb5SKevin Wolf } 988401dd096SHanna Reitz } 9897eaa8fb5SKevin Wolf 9906f592e5aSEmanuele Giuseppe Esposito /* 9916f592e5aSEmanuele Giuseppe Esposito * Called with job_mutex held, but releases it temporarily. 9926f592e5aSEmanuele Giuseppe Esposito * Takes AioContext lock internally to invoke a job->driver callback. 9936f592e5aSEmanuele Giuseppe Esposito */ 994afe1e8a7SEmanuele Giuseppe Esposito static void job_completed_txn_abort_locked(Job *job) 9957eaa8fb5SKevin Wolf { 9967eaa8fb5SKevin Wolf JobTxn *txn = job->txn; 9977eaa8fb5SKevin Wolf Job *other_job; 9987eaa8fb5SKevin Wolf 9997eaa8fb5SKevin Wolf if (txn->aborting) { 10007eaa8fb5SKevin Wolf /* 10017eaa8fb5SKevin Wolf * We are cancelled by another job, which will handle everything. 10027eaa8fb5SKevin Wolf */ 10037eaa8fb5SKevin Wolf return; 10047eaa8fb5SKevin Wolf } 10057eaa8fb5SKevin Wolf txn->aborting = true; 1006afe1e8a7SEmanuele Giuseppe Esposito job_txn_ref_locked(txn); 10077eaa8fb5SKevin Wolf 1008afe1e8a7SEmanuele Giuseppe Esposito job_ref_locked(job); 10097eaa8fb5SKevin Wolf 10107eaa8fb5SKevin Wolf /* Other jobs are effectively cancelled by us, set the status for 10117eaa8fb5SKevin Wolf * them; this job, however, may or may not be cancelled, depending 10127eaa8fb5SKevin Wolf * on the caller, so leave it. */ 10137eaa8fb5SKevin Wolf QLIST_FOREACH(other_job, &txn->jobs, txn_list) { 10147eaa8fb5SKevin Wolf if (other_job != job) { 10151d4a43e9SHanna Reitz /* 10161d4a43e9SHanna Reitz * This is a transaction: If one job failed, no result will matter. 10171d4a43e9SHanna Reitz * Therefore, pass force=true to terminate all other jobs as quickly 10181d4a43e9SHanna Reitz * as possible. 10191d4a43e9SHanna Reitz */ 1020afe1e8a7SEmanuele Giuseppe Esposito job_cancel_async_locked(other_job, true); 10217eaa8fb5SKevin Wolf } 10227eaa8fb5SKevin Wolf } 10237eaa8fb5SKevin Wolf while (!QLIST_EMPTY(&txn->jobs)) { 10247eaa8fb5SKevin Wolf other_job = QLIST_FIRST(&txn->jobs); 1025afe1e8a7SEmanuele Giuseppe Esposito if (!job_is_completed_locked(other_job)) { 1026afe1e8a7SEmanuele Giuseppe Esposito assert(job_cancel_requested_locked(other_job)); 1027afe1e8a7SEmanuele Giuseppe Esposito job_finish_sync_locked(other_job, NULL, NULL); 10287eaa8fb5SKevin Wolf } 1029afe1e8a7SEmanuele Giuseppe Esposito job_finalize_single_locked(other_job); 10307eaa8fb5SKevin Wolf } 10317eaa8fb5SKevin Wolf 1032afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 1033afe1e8a7SEmanuele Giuseppe Esposito job_txn_unref_locked(txn); 10347eaa8fb5SKevin Wolf } 10357eaa8fb5SKevin Wolf 1036afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily */ 1037afe1e8a7SEmanuele Giuseppe Esposito static int job_prepare_locked(Job *job) 10387eaa8fb5SKevin Wolf { 1039afe1e8a7SEmanuele Giuseppe Esposito int ret; 10406f592e5aSEmanuele Giuseppe Esposito AioContext *ctx = job->aio_context; 1041afe1e8a7SEmanuele Giuseppe Esposito 1042c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 10436f592e5aSEmanuele Giuseppe Esposito 10447eaa8fb5SKevin Wolf if (job->ret == 0 && job->driver->prepare) { 1045afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 10466f592e5aSEmanuele Giuseppe Esposito aio_context_acquire(ctx); 1047afe1e8a7SEmanuele Giuseppe Esposito ret = job->driver->prepare(job); 10486f592e5aSEmanuele Giuseppe Esposito aio_context_release(ctx); 1049afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 1050afe1e8a7SEmanuele Giuseppe Esposito job->ret = ret; 1051afe1e8a7SEmanuele Giuseppe Esposito job_update_rc_locked(job); 10527eaa8fb5SKevin Wolf } 10536f592e5aSEmanuele Giuseppe Esposito 10547eaa8fb5SKevin Wolf return job->ret; 10557eaa8fb5SKevin Wolf } 10567eaa8fb5SKevin Wolf 1057afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held */ 1058afe1e8a7SEmanuele Giuseppe Esposito static int job_needs_finalize_locked(Job *job) 10597eaa8fb5SKevin Wolf { 10607eaa8fb5SKevin Wolf return !job->auto_finalize; 10617eaa8fb5SKevin Wolf } 10627eaa8fb5SKevin Wolf 1063afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held */ 1064afe1e8a7SEmanuele Giuseppe Esposito static void job_do_finalize_locked(Job *job) 10657eaa8fb5SKevin Wolf { 10667eaa8fb5SKevin Wolf int rc; 10677eaa8fb5SKevin Wolf assert(job && job->txn); 10687eaa8fb5SKevin Wolf 10697eaa8fb5SKevin Wolf /* prepare the transaction to complete */ 1070afe1e8a7SEmanuele Giuseppe Esposito rc = job_txn_apply_locked(job, job_prepare_locked); 10717eaa8fb5SKevin Wolf if (rc) { 1072afe1e8a7SEmanuele Giuseppe Esposito job_completed_txn_abort_locked(job); 10737eaa8fb5SKevin Wolf } else { 1074afe1e8a7SEmanuele Giuseppe Esposito job_txn_apply_locked(job, job_finalize_single_locked); 10757eaa8fb5SKevin Wolf } 10767eaa8fb5SKevin Wolf } 10777eaa8fb5SKevin Wolf 1078afe1e8a7SEmanuele Giuseppe Esposito void job_finalize_locked(Job *job, Error **errp) 1079afe1e8a7SEmanuele Giuseppe Esposito { 1080afe1e8a7SEmanuele Giuseppe Esposito assert(job && job->id); 1081afe1e8a7SEmanuele Giuseppe Esposito if (job_apply_verb_locked(job, JOB_VERB_FINALIZE, errp)) { 1082afe1e8a7SEmanuele Giuseppe Esposito return; 1083afe1e8a7SEmanuele Giuseppe Esposito } 1084afe1e8a7SEmanuele Giuseppe Esposito job_do_finalize_locked(job); 1085afe1e8a7SEmanuele Giuseppe Esposito } 1086afe1e8a7SEmanuele Giuseppe Esposito 10877eaa8fb5SKevin Wolf void job_finalize(Job *job, Error **errp) 10887eaa8fb5SKevin Wolf { 1089afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1090afe1e8a7SEmanuele Giuseppe Esposito job_finalize_locked(job, errp); 10917eaa8fb5SKevin Wolf } 10927eaa8fb5SKevin Wolf 1093afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 1094afe1e8a7SEmanuele Giuseppe Esposito static int job_transition_to_pending_locked(Job *job) 10957eaa8fb5SKevin Wolf { 1096afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_PENDING); 10977eaa8fb5SKevin Wolf if (!job->auto_finalize) { 1098afe1e8a7SEmanuele Giuseppe Esposito job_event_pending_locked(job); 10997eaa8fb5SKevin Wolf } 11007eaa8fb5SKevin Wolf return 0; 11017eaa8fb5SKevin Wolf } 11027eaa8fb5SKevin Wolf 11032e1795b5SKevin Wolf void job_transition_to_ready(Job *job) 11042e1795b5SKevin Wolf { 1105afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1106afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_READY); 1107afe1e8a7SEmanuele Giuseppe Esposito job_event_ready_locked(job); 11082e1795b5SKevin Wolf } 11092e1795b5SKevin Wolf 1110afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 1111afe1e8a7SEmanuele Giuseppe Esposito static void job_completed_txn_success_locked(Job *job) 11127eaa8fb5SKevin Wolf { 11137eaa8fb5SKevin Wolf JobTxn *txn = job->txn; 11147eaa8fb5SKevin Wolf Job *other_job; 11157eaa8fb5SKevin Wolf 1116afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_WAITING); 11177eaa8fb5SKevin Wolf 11187eaa8fb5SKevin Wolf /* 11197eaa8fb5SKevin Wolf * Successful completion, see if there are other running jobs in this 11207eaa8fb5SKevin Wolf * txn. 11217eaa8fb5SKevin Wolf */ 11227eaa8fb5SKevin Wolf QLIST_FOREACH(other_job, &txn->jobs, txn_list) { 1123afe1e8a7SEmanuele Giuseppe Esposito if (!job_is_completed_locked(other_job)) { 11247eaa8fb5SKevin Wolf return; 11257eaa8fb5SKevin Wolf } 11267eaa8fb5SKevin Wolf assert(other_job->ret == 0); 11277eaa8fb5SKevin Wolf } 11287eaa8fb5SKevin Wolf 1129afe1e8a7SEmanuele Giuseppe Esposito job_txn_apply_locked(job, job_transition_to_pending_locked); 11307eaa8fb5SKevin Wolf 11317eaa8fb5SKevin Wolf /* If no jobs need manual finalization, automatically do so */ 1132afe1e8a7SEmanuele Giuseppe Esposito if (job_txn_apply_locked(job, job_needs_finalize_locked) == 0) { 1133afe1e8a7SEmanuele Giuseppe Esposito job_do_finalize_locked(job); 11347eaa8fb5SKevin Wolf } 11357eaa8fb5SKevin Wolf } 11367eaa8fb5SKevin Wolf 1137afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 1138afe1e8a7SEmanuele Giuseppe Esposito static void job_completed_locked(Job *job) 11393d70ff53SKevin Wolf { 1140afe1e8a7SEmanuele Giuseppe Esposito assert(job && job->txn && !job_is_completed_locked(job)); 11411266c9b9SKevin Wolf 1142afe1e8a7SEmanuele Giuseppe Esposito job_update_rc_locked(job); 1143404ff28dSJohn Snow trace_job_completed(job, job->ret); 11443d70ff53SKevin Wolf if (job->ret) { 1145afe1e8a7SEmanuele Giuseppe Esposito job_completed_txn_abort_locked(job); 11463d70ff53SKevin Wolf } else { 1147afe1e8a7SEmanuele Giuseppe Esposito job_completed_txn_success_locked(job); 11483d70ff53SKevin Wolf } 11493d70ff53SKevin Wolf } 11503d70ff53SKevin Wolf 1151afe1e8a7SEmanuele Giuseppe Esposito /** 1152afe1e8a7SEmanuele Giuseppe Esposito * Useful only as a type shim for aio_bh_schedule_oneshot. 1153afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex *not* held. 1154afe1e8a7SEmanuele Giuseppe Esposito */ 1155ccbfb331SJohn Snow static void job_exit(void *opaque) 1156ccbfb331SJohn Snow { 1157ccbfb331SJohn Snow Job *job = (Job *)opaque; 1158afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1159afe1e8a7SEmanuele Giuseppe Esposito job_ref_locked(job); 1160b5a7a057SKevin Wolf 1161b5a7a057SKevin Wolf /* This is a lie, we're not quiescent, but still doing the completion 1162b5a7a057SKevin Wolf * callbacks. However, completion callbacks tend to involve operations that 1163b5a7a057SKevin Wolf * drain block nodes, and if .drained_poll still returned true, we would 1164b5a7a057SKevin Wolf * deadlock. */ 1165b5a7a057SKevin Wolf job->busy = false; 1166afe1e8a7SEmanuele Giuseppe Esposito job_event_idle_locked(job); 1167b5a7a057SKevin Wolf 1168afe1e8a7SEmanuele Giuseppe Esposito job_completed_locked(job); 1169afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 1170ccbfb331SJohn Snow } 1171ccbfb331SJohn Snow 1172ccbfb331SJohn Snow /** 1173ccbfb331SJohn Snow * All jobs must allow a pause point before entering their job proper. This 1174ccbfb331SJohn Snow * ensures that jobs can be paused prior to being started, then resumed later. 1175ccbfb331SJohn Snow */ 1176ccbfb331SJohn Snow static void coroutine_fn job_co_entry(void *opaque) 1177ccbfb331SJohn Snow { 1178ccbfb331SJohn Snow Job *job = opaque; 1179afe1e8a7SEmanuele Giuseppe Esposito int ret; 1180ccbfb331SJohn Snow 1181ccbfb331SJohn Snow assert(job && job->driver && job->driver->run); 1182afe1e8a7SEmanuele Giuseppe Esposito WITH_JOB_LOCK_GUARD() { 1183c70b8031SEmanuele Giuseppe Esposito assert(job->aio_context == qemu_get_current_aio_context()); 1184afe1e8a7SEmanuele Giuseppe Esposito job_pause_point_locked(job); 1185afe1e8a7SEmanuele Giuseppe Esposito } 1186afe1e8a7SEmanuele Giuseppe Esposito ret = job->driver->run(job, &job->err); 1187afe1e8a7SEmanuele Giuseppe Esposito WITH_JOB_LOCK_GUARD() { 1188afe1e8a7SEmanuele Giuseppe Esposito job->ret = ret; 1189ccbfb331SJohn Snow job->deferred_to_main_loop = true; 1190b5a7a057SKevin Wolf job->busy = true; 1191afe1e8a7SEmanuele Giuseppe Esposito } 1192ccbfb331SJohn Snow aio_bh_schedule_oneshot(qemu_get_aio_context(), job_exit, job); 1193ccbfb331SJohn Snow } 1194ccbfb331SJohn Snow 1195ccbfb331SJohn Snow void job_start(Job *job) 1196ccbfb331SJohn Snow { 1197afe1e8a7SEmanuele Giuseppe Esposito assert(qemu_in_main_thread()); 1198afe1e8a7SEmanuele Giuseppe Esposito 1199afe1e8a7SEmanuele Giuseppe Esposito WITH_JOB_LOCK_GUARD() { 1200afe1e8a7SEmanuele Giuseppe Esposito assert(job && !job_started_locked(job) && job->paused && 1201ccbfb331SJohn Snow job->driver && job->driver->run); 1202ccbfb331SJohn Snow job->co = qemu_coroutine_create(job_co_entry, job); 1203ccbfb331SJohn Snow job->pause_count--; 1204ccbfb331SJohn Snow job->busy = true; 1205ccbfb331SJohn Snow job->paused = false; 1206afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_RUNNING); 1207afe1e8a7SEmanuele Giuseppe Esposito } 1208ccbfb331SJohn Snow aio_co_enter(job->aio_context, job->co); 1209ccbfb331SJohn Snow } 1210ccbfb331SJohn Snow 1211afe1e8a7SEmanuele Giuseppe Esposito void job_cancel_locked(Job *job, bool force) 12123d70ff53SKevin Wolf { 12133d70ff53SKevin Wolf if (job->status == JOB_STATUS_CONCLUDED) { 1214afe1e8a7SEmanuele Giuseppe Esposito job_do_dismiss_locked(job); 12153d70ff53SKevin Wolf return; 12163d70ff53SKevin Wolf } 1217afe1e8a7SEmanuele Giuseppe Esposito job_cancel_async_locked(job, force); 1218afe1e8a7SEmanuele Giuseppe Esposito if (!job_started_locked(job)) { 1219afe1e8a7SEmanuele Giuseppe Esposito job_completed_locked(job); 12203d70ff53SKevin Wolf } else if (job->deferred_to_main_loop) { 1221401dd096SHanna Reitz /* 1222401dd096SHanna Reitz * job_cancel_async() ignores soft-cancel requests for jobs 1223401dd096SHanna Reitz * that are already done (i.e. deferred to the main loop). We 1224401dd096SHanna Reitz * have to check again whether the job is really cancelled. 122508b83bffSHanna Reitz * (job_cancel_requested() and job_is_cancelled() are equivalent 122608b83bffSHanna Reitz * here, because job_cancel_async() will make soft-cancel 122708b83bffSHanna Reitz * requests no-ops when deferred_to_main_loop is true. We 122808b83bffSHanna Reitz * choose to call job_is_cancelled() to show that we invoke 122908b83bffSHanna Reitz * job_completed_txn_abort() only for force-cancelled jobs.) 1230401dd096SHanna Reitz */ 1231afe1e8a7SEmanuele Giuseppe Esposito if (job_is_cancelled_locked(job)) { 1232afe1e8a7SEmanuele Giuseppe Esposito job_completed_txn_abort_locked(job); 1233401dd096SHanna Reitz } 12343d70ff53SKevin Wolf } else { 1235afe1e8a7SEmanuele Giuseppe Esposito job_enter_cond_locked(job, NULL); 12363d70ff53SKevin Wolf } 12373d70ff53SKevin Wolf } 12383d70ff53SKevin Wolf 1239afe1e8a7SEmanuele Giuseppe Esposito void job_cancel(Job *job, bool force) 1240afe1e8a7SEmanuele Giuseppe Esposito { 1241afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1242afe1e8a7SEmanuele Giuseppe Esposito job_cancel_locked(job, force); 1243afe1e8a7SEmanuele Giuseppe Esposito } 1244afe1e8a7SEmanuele Giuseppe Esposito 1245afe1e8a7SEmanuele Giuseppe Esposito void job_user_cancel_locked(Job *job, bool force, Error **errp) 1246afe1e8a7SEmanuele Giuseppe Esposito { 1247afe1e8a7SEmanuele Giuseppe Esposito if (job_apply_verb_locked(job, JOB_VERB_CANCEL, errp)) { 1248afe1e8a7SEmanuele Giuseppe Esposito return; 1249afe1e8a7SEmanuele Giuseppe Esposito } 1250afe1e8a7SEmanuele Giuseppe Esposito job_cancel_locked(job, force); 1251afe1e8a7SEmanuele Giuseppe Esposito } 1252afe1e8a7SEmanuele Giuseppe Esposito 12533d70ff53SKevin Wolf void job_user_cancel(Job *job, bool force, Error **errp) 12543d70ff53SKevin Wolf { 1255afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1256afe1e8a7SEmanuele Giuseppe Esposito job_user_cancel_locked(job, force, errp); 12573d70ff53SKevin Wolf } 12583d70ff53SKevin Wolf 12593d70ff53SKevin Wolf /* A wrapper around job_cancel() taking an Error ** parameter so it may be 12603d70ff53SKevin Wolf * used with job_finish_sync() without the need for (rather nasty) function 1261afe1e8a7SEmanuele Giuseppe Esposito * pointer casts there. 1262afe1e8a7SEmanuele Giuseppe Esposito * 1263afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex held. 1264afe1e8a7SEmanuele Giuseppe Esposito */ 1265afe1e8a7SEmanuele Giuseppe Esposito static void job_cancel_err_locked(Job *job, Error **errp) 12663d70ff53SKevin Wolf { 1267afe1e8a7SEmanuele Giuseppe Esposito job_cancel_locked(job, false); 12683d70ff53SKevin Wolf } 12693d70ff53SKevin Wolf 12704cfb3f05SHanna Reitz /** 12714cfb3f05SHanna Reitz * Same as job_cancel_err(), but force-cancel. 1272afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex held. 12734cfb3f05SHanna Reitz */ 1274afe1e8a7SEmanuele Giuseppe Esposito static void job_force_cancel_err_locked(Job *job, Error **errp) 12753d70ff53SKevin Wolf { 1276afe1e8a7SEmanuele Giuseppe Esposito job_cancel_locked(job, true); 1277afe1e8a7SEmanuele Giuseppe Esposito } 1278afe1e8a7SEmanuele Giuseppe Esposito 1279afe1e8a7SEmanuele Giuseppe Esposito int job_cancel_sync_locked(Job *job, bool force) 1280afe1e8a7SEmanuele Giuseppe Esposito { 1281afe1e8a7SEmanuele Giuseppe Esposito if (force) { 1282afe1e8a7SEmanuele Giuseppe Esposito return job_finish_sync_locked(job, &job_force_cancel_err_locked, NULL); 1283afe1e8a7SEmanuele Giuseppe Esposito } else { 1284afe1e8a7SEmanuele Giuseppe Esposito return job_finish_sync_locked(job, &job_cancel_err_locked, NULL); 1285afe1e8a7SEmanuele Giuseppe Esposito } 12864cfb3f05SHanna Reitz } 12874cfb3f05SHanna Reitz 12884cfb3f05SHanna Reitz int job_cancel_sync(Job *job, bool force) 12894cfb3f05SHanna Reitz { 1290afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1291afe1e8a7SEmanuele Giuseppe Esposito return job_cancel_sync_locked(job, force); 12924cfb3f05SHanna Reitz } 12933d70ff53SKevin Wolf 12943d70ff53SKevin Wolf void job_cancel_sync_all(void) 12953d70ff53SKevin Wolf { 12963d70ff53SKevin Wolf Job *job; 1297afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 12983d70ff53SKevin Wolf 1299afe1e8a7SEmanuele Giuseppe Esposito while ((job = job_next_locked(NULL))) { 1300afe1e8a7SEmanuele Giuseppe Esposito job_cancel_sync_locked(job, true); 13013d70ff53SKevin Wolf } 13023d70ff53SKevin Wolf } 13033d70ff53SKevin Wolf 1304afe1e8a7SEmanuele Giuseppe Esposito int job_complete_sync_locked(Job *job, Error **errp) 13053d70ff53SKevin Wolf { 1306afe1e8a7SEmanuele Giuseppe Esposito return job_finish_sync_locked(job, job_complete_locked, errp); 13073d70ff53SKevin Wolf } 13083d70ff53SKevin Wolf 1309afe1e8a7SEmanuele Giuseppe Esposito int job_complete_sync(Job *job, Error **errp) 1310afe1e8a7SEmanuele Giuseppe Esposito { 1311afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1312afe1e8a7SEmanuele Giuseppe Esposito return job_complete_sync_locked(job, errp); 1313afe1e8a7SEmanuele Giuseppe Esposito } 1314afe1e8a7SEmanuele Giuseppe Esposito 1315afe1e8a7SEmanuele Giuseppe Esposito void job_complete_locked(Job *job, Error **errp) 13163453d972SKevin Wolf { 13173453d972SKevin Wolf /* Should not be reachable via external interface for internal jobs */ 13183453d972SKevin Wolf assert(job->id); 1319c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 1320afe1e8a7SEmanuele Giuseppe Esposito if (job_apply_verb_locked(job, JOB_VERB_COMPLETE, errp)) { 13213453d972SKevin Wolf return; 13223453d972SKevin Wolf } 1323afe1e8a7SEmanuele Giuseppe Esposito if (job_cancel_requested_locked(job) || !job->driver->complete) { 13243453d972SKevin Wolf error_setg(errp, "The active block job '%s' cannot be completed", 13253453d972SKevin Wolf job->id); 13263453d972SKevin Wolf return; 13273453d972SKevin Wolf } 13283453d972SKevin Wolf 1329afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 13303453d972SKevin Wolf job->driver->complete(job, errp); 1331afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 13323453d972SKevin Wolf } 13333453d972SKevin Wolf 1334afe1e8a7SEmanuele Giuseppe Esposito void job_complete(Job *job, Error **errp) 1335afe1e8a7SEmanuele Giuseppe Esposito { 1336afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1337afe1e8a7SEmanuele Giuseppe Esposito job_complete_locked(job, errp); 1338afe1e8a7SEmanuele Giuseppe Esposito } 1339afe1e8a7SEmanuele Giuseppe Esposito 1340afe1e8a7SEmanuele Giuseppe Esposito int job_finish_sync_locked(Job *job, 1341afe1e8a7SEmanuele Giuseppe Esposito void (*finish)(Job *, Error **errp), 1342afe1e8a7SEmanuele Giuseppe Esposito Error **errp) 13436a74c075SKevin Wolf { 13446a74c075SKevin Wolf Error *local_err = NULL; 13456a74c075SKevin Wolf int ret; 13463ed4f708SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 13476a74c075SKevin Wolf 1348afe1e8a7SEmanuele Giuseppe Esposito job_ref_locked(job); 13496a74c075SKevin Wolf 13506a74c075SKevin Wolf if (finish) { 13516a74c075SKevin Wolf finish(job, &local_err); 13526a74c075SKevin Wolf } 13536a74c075SKevin Wolf if (local_err) { 13546a74c075SKevin Wolf error_propagate(errp, local_err); 1355afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 13566a74c075SKevin Wolf return -EBUSY; 13576a74c075SKevin Wolf } 1358de0fbe64SKevin Wolf 1359afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 13606f592e5aSEmanuele Giuseppe Esposito AIO_WAIT_WHILE_UNLOCKED(job->aio_context, 1361bb0c9409SVladimir Sementsov-Ogievskiy (job_enter(job), !job_is_completed(job))); 1362afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 1363de0fbe64SKevin Wolf 1364afe1e8a7SEmanuele Giuseppe Esposito ret = (job_is_cancelled_locked(job) && job->ret == 0) 1365afe1e8a7SEmanuele Giuseppe Esposito ? -ECANCELED : job->ret; 1366afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 13676a74c075SKevin Wolf return ret; 13686a74c075SKevin Wolf } 1369afe1e8a7SEmanuele Giuseppe Esposito 1370afe1e8a7SEmanuele Giuseppe Esposito int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp) 1371afe1e8a7SEmanuele Giuseppe Esposito { 1372afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1373afe1e8a7SEmanuele Giuseppe Esposito return job_finish_sync_locked(job, finish, errp); 1374afe1e8a7SEmanuele Giuseppe Esposito } 1375