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}, 8361a3a5a7SFiona Ebner [JOB_VERB_CHANGE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, 84a50c2ab8SKevin Wolf }; 85a50c2ab8SKevin Wolf 867eaa8fb5SKevin Wolf /* Transactional group of jobs */ 877eaa8fb5SKevin Wolf struct JobTxn { 887eaa8fb5SKevin Wolf 897eaa8fb5SKevin Wolf /* Is this txn being cancelled? */ 907eaa8fb5SKevin Wolf bool aborting; 917eaa8fb5SKevin Wolf 927eaa8fb5SKevin Wolf /* List of jobs */ 937eaa8fb5SKevin Wolf QLIST_HEAD(, Job) jobs; 947eaa8fb5SKevin Wolf 957eaa8fb5SKevin Wolf /* Reference count */ 967eaa8fb5SKevin Wolf int refcnt; 977eaa8fb5SKevin Wolf }; 987eaa8fb5SKevin Wolf 9955c5a25aSEmanuele Giuseppe Esposito void job_lock(void) 10055c5a25aSEmanuele Giuseppe Esposito { 101da01ff7fSKevin Wolf qemu_mutex_lock(&job_mutex); 102da01ff7fSKevin Wolf } 103da01ff7fSKevin Wolf 1046f592e5aSEmanuele Giuseppe Esposito void job_unlock(void) 105da01ff7fSKevin Wolf { 106da01ff7fSKevin Wolf qemu_mutex_unlock(&job_mutex); 107da01ff7fSKevin Wolf } 108da01ff7fSKevin Wolf 109da01ff7fSKevin Wolf static void __attribute__((__constructor__)) job_init(void) 110da01ff7fSKevin Wolf { 111da01ff7fSKevin Wolf qemu_mutex_init(&job_mutex); 112da01ff7fSKevin Wolf } 113da01ff7fSKevin Wolf 1147eaa8fb5SKevin Wolf JobTxn *job_txn_new(void) 1157eaa8fb5SKevin Wolf { 1167eaa8fb5SKevin Wolf JobTxn *txn = g_new0(JobTxn, 1); 1177eaa8fb5SKevin Wolf QLIST_INIT(&txn->jobs); 1187eaa8fb5SKevin Wolf txn->refcnt = 1; 1197eaa8fb5SKevin Wolf return txn; 1207eaa8fb5SKevin Wolf } 1217eaa8fb5SKevin Wolf 122afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 123afe1e8a7SEmanuele Giuseppe Esposito static void job_txn_ref_locked(JobTxn *txn) 1247eaa8fb5SKevin Wolf { 1257eaa8fb5SKevin Wolf txn->refcnt++; 1267eaa8fb5SKevin Wolf } 1277eaa8fb5SKevin Wolf 128afe1e8a7SEmanuele Giuseppe Esposito void job_txn_unref_locked(JobTxn *txn) 1297eaa8fb5SKevin Wolf { 1307eaa8fb5SKevin Wolf if (txn && --txn->refcnt == 0) { 1317eaa8fb5SKevin Wolf g_free(txn); 1327eaa8fb5SKevin Wolf } 1337eaa8fb5SKevin Wolf } 1347eaa8fb5SKevin Wolf 135afe1e8a7SEmanuele Giuseppe Esposito void job_txn_unref(JobTxn *txn) 136afe1e8a7SEmanuele Giuseppe Esposito { 137afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 138afe1e8a7SEmanuele Giuseppe Esposito job_txn_unref_locked(txn); 139afe1e8a7SEmanuele Giuseppe Esposito } 140afe1e8a7SEmanuele Giuseppe Esposito 141544f4d52SEmanuele Giuseppe Esposito /** 142544f4d52SEmanuele Giuseppe Esposito * @txn: The transaction (may be NULL) 143544f4d52SEmanuele Giuseppe Esposito * @job: Job to add to the transaction 144544f4d52SEmanuele Giuseppe Esposito * 145544f4d52SEmanuele Giuseppe Esposito * Add @job to the transaction. The @job must not already be in a transaction. 146544f4d52SEmanuele Giuseppe Esposito * The caller must call either job_txn_unref() or job_completed() to release 147544f4d52SEmanuele Giuseppe Esposito * the reference that is automatically grabbed here. 148544f4d52SEmanuele Giuseppe Esposito * 149544f4d52SEmanuele Giuseppe Esposito * If @txn is NULL, the function does nothing. 150afe1e8a7SEmanuele Giuseppe Esposito * 151afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex held. 152544f4d52SEmanuele Giuseppe Esposito */ 153afe1e8a7SEmanuele Giuseppe Esposito static void job_txn_add_job_locked(JobTxn *txn, Job *job) 1547eaa8fb5SKevin Wolf { 1557eaa8fb5SKevin Wolf if (!txn) { 1567eaa8fb5SKevin Wolf return; 1577eaa8fb5SKevin Wolf } 1587eaa8fb5SKevin Wolf 1597eaa8fb5SKevin Wolf assert(!job->txn); 1607eaa8fb5SKevin Wolf job->txn = txn; 1617eaa8fb5SKevin Wolf 1627eaa8fb5SKevin Wolf QLIST_INSERT_HEAD(&txn->jobs, job, txn_list); 163afe1e8a7SEmanuele Giuseppe Esposito job_txn_ref_locked(txn); 1647eaa8fb5SKevin Wolf } 1657eaa8fb5SKevin Wolf 166afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 167afe1e8a7SEmanuele Giuseppe Esposito static void job_txn_del_job_locked(Job *job) 1687eaa8fb5SKevin Wolf { 1697eaa8fb5SKevin Wolf if (job->txn) { 1707eaa8fb5SKevin Wolf QLIST_REMOVE(job, txn_list); 171afe1e8a7SEmanuele Giuseppe Esposito job_txn_unref_locked(job->txn); 1727eaa8fb5SKevin Wolf job->txn = NULL; 1737eaa8fb5SKevin Wolf } 1747eaa8fb5SKevin Wolf } 1757eaa8fb5SKevin Wolf 176afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily. */ 177afe1e8a7SEmanuele Giuseppe Esposito static int job_txn_apply_locked(Job *job, int fn(Job *)) 1787eaa8fb5SKevin Wolf { 179b660a84bSStefan Reiter Job *other_job, *next; 180b660a84bSStefan Reiter JobTxn *txn = job->txn; 1817eaa8fb5SKevin Wolf int rc = 0; 1827eaa8fb5SKevin Wolf 183b660a84bSStefan Reiter /* 184b660a84bSStefan Reiter * Similar to job_completed_txn_abort, we take each job's lock before 185b660a84bSStefan Reiter * applying fn, but since we assume that outer_ctx is held by the caller, 186b660a84bSStefan Reiter * we need to release it here to avoid holding the lock twice - which would 187b660a84bSStefan Reiter * break AIO_WAIT_WHILE from within fn. 188b660a84bSStefan Reiter */ 189afe1e8a7SEmanuele Giuseppe Esposito job_ref_locked(job); 190b660a84bSStefan Reiter 191b660a84bSStefan Reiter QLIST_FOREACH_SAFE(other_job, &txn->jobs, txn_list, next) { 192b660a84bSStefan Reiter rc = fn(other_job); 1937eaa8fb5SKevin Wolf if (rc) { 1947eaa8fb5SKevin Wolf break; 1957eaa8fb5SKevin Wolf } 1967eaa8fb5SKevin Wolf } 197b660a84bSStefan Reiter 198afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 1997eaa8fb5SKevin Wolf return rc; 2007eaa8fb5SKevin Wolf } 2017eaa8fb5SKevin Wolf 202456273b0SKevin Wolf bool job_is_internal(Job *job) 2031dac83f1SKevin Wolf { 2041dac83f1SKevin Wolf return (job->id == NULL); 2051dac83f1SKevin Wolf } 2061dac83f1SKevin Wolf 207afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 208afe1e8a7SEmanuele Giuseppe Esposito static void job_state_transition_locked(Job *job, JobStatus s1) 209a50c2ab8SKevin Wolf { 210a50c2ab8SKevin Wolf JobStatus s0 = job->status; 211c2032289SLiam Merwick assert(s1 >= 0 && s1 < JOB_STATUS__MAX); 2124ad35181SKevin Wolf trace_job_state_transition(job, job->ret, 213a50c2ab8SKevin Wolf JobSTT[s0][s1] ? "allowed" : "disallowed", 214a50c2ab8SKevin Wolf JobStatus_str(s0), JobStatus_str(s1)); 215a50c2ab8SKevin Wolf assert(JobSTT[s0][s1]); 216a50c2ab8SKevin Wolf job->status = s1; 2171dac83f1SKevin Wolf 2181dac83f1SKevin Wolf if (!job_is_internal(job) && s1 != s0) { 2193ab72385SPeter Xu qapi_event_send_job_status_change(job->id, job->status); 2201dac83f1SKevin Wolf } 221a50c2ab8SKevin Wolf } 222a50c2ab8SKevin Wolf 223afe1e8a7SEmanuele Giuseppe Esposito int job_apply_verb_locked(Job *job, JobVerb verb, Error **errp) 224a50c2ab8SKevin Wolf { 225a50c2ab8SKevin Wolf JobStatus s0 = job->status; 226c2032289SLiam Merwick assert(verb >= 0 && verb < JOB_VERB__MAX); 227a50c2ab8SKevin Wolf trace_job_apply_verb(job, JobStatus_str(s0), JobVerb_str(verb), 228a50c2ab8SKevin Wolf JobVerbTable[verb][s0] ? "allowed" : "prohibited"); 229a50c2ab8SKevin Wolf if (JobVerbTable[verb][s0]) { 230a50c2ab8SKevin Wolf return 0; 231a50c2ab8SKevin Wolf } 232a50c2ab8SKevin Wolf error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'", 233a50c2ab8SKevin Wolf job->id, JobStatus_str(s0), JobVerb_str(verb)); 234a50c2ab8SKevin Wolf return -EPERM; 235a50c2ab8SKevin Wolf } 236a50c2ab8SKevin Wolf 237252291eaSKevin Wolf JobType job_type(const Job *job) 238252291eaSKevin Wolf { 239252291eaSKevin Wolf return job->driver->job_type; 240252291eaSKevin Wolf } 241252291eaSKevin Wolf 242252291eaSKevin Wolf const char *job_type_str(const Job *job) 243252291eaSKevin Wolf { 244252291eaSKevin Wolf return JobType_str(job_type(job)); 245252291eaSKevin Wolf } 246252291eaSKevin Wolf 247afe1e8a7SEmanuele Giuseppe Esposito bool job_is_cancelled_locked(Job *job) 248daa7f2f9SKevin Wolf { 249a640fa0eSHanna Reitz /* force_cancel may be true only if cancelled is true, too */ 250a640fa0eSHanna Reitz assert(job->cancelled || !job->force_cancel); 251a640fa0eSHanna Reitz return job->force_cancel; 25208b83bffSHanna Reitz } 25308b83bffSHanna Reitz 254afe1e8a7SEmanuele Giuseppe Esposito bool job_is_cancelled(Job *job) 255afe1e8a7SEmanuele Giuseppe Esposito { 256afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 257afe1e8a7SEmanuele Giuseppe Esposito return job_is_cancelled_locked(job); 258afe1e8a7SEmanuele Giuseppe Esposito } 259afe1e8a7SEmanuele Giuseppe Esposito 260afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 261afe1e8a7SEmanuele Giuseppe Esposito static bool job_cancel_requested_locked(Job *job) 26208b83bffSHanna Reitz { 263daa7f2f9SKevin Wolf return job->cancelled; 264daa7f2f9SKevin Wolf } 265daa7f2f9SKevin Wolf 266afe1e8a7SEmanuele Giuseppe Esposito bool job_cancel_requested(Job *job) 267afe1e8a7SEmanuele Giuseppe Esposito { 268afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 269afe1e8a7SEmanuele Giuseppe Esposito return job_cancel_requested_locked(job); 270afe1e8a7SEmanuele Giuseppe Esposito } 271afe1e8a7SEmanuele Giuseppe Esposito 272afe1e8a7SEmanuele Giuseppe Esposito bool job_is_ready_locked(Job *job) 273df956ae2SKevin Wolf { 274df956ae2SKevin Wolf switch (job->status) { 275df956ae2SKevin Wolf case JOB_STATUS_UNDEFINED: 276df956ae2SKevin Wolf case JOB_STATUS_CREATED: 277df956ae2SKevin Wolf case JOB_STATUS_RUNNING: 278df956ae2SKevin Wolf case JOB_STATUS_PAUSED: 279df956ae2SKevin Wolf case JOB_STATUS_WAITING: 280df956ae2SKevin Wolf case JOB_STATUS_PENDING: 281df956ae2SKevin Wolf case JOB_STATUS_ABORTING: 282df956ae2SKevin Wolf case JOB_STATUS_CONCLUDED: 283df956ae2SKevin Wolf case JOB_STATUS_NULL: 284df956ae2SKevin Wolf return false; 285df956ae2SKevin Wolf case JOB_STATUS_READY: 286df956ae2SKevin Wolf case JOB_STATUS_STANDBY: 287df956ae2SKevin Wolf return true; 288df956ae2SKevin Wolf default: 289df956ae2SKevin Wolf g_assert_not_reached(); 290df956ae2SKevin Wolf } 291df956ae2SKevin Wolf return false; 292df956ae2SKevin Wolf } 293df956ae2SKevin Wolf 294afe1e8a7SEmanuele Giuseppe Esposito bool job_is_ready(Job *job) 295afe1e8a7SEmanuele Giuseppe Esposito { 296afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 297afe1e8a7SEmanuele Giuseppe Esposito return job_is_ready_locked(job); 298afe1e8a7SEmanuele Giuseppe Esposito } 299afe1e8a7SEmanuele Giuseppe Esposito 300afe1e8a7SEmanuele Giuseppe Esposito bool job_is_completed_locked(Job *job) 301afe1e8a7SEmanuele Giuseppe Esposito { 302afe1e8a7SEmanuele Giuseppe Esposito switch (job->status) { 303afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_UNDEFINED: 304afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_CREATED: 305afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_RUNNING: 306afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_PAUSED: 307afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_READY: 308afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_STANDBY: 309afe1e8a7SEmanuele Giuseppe Esposito return false; 310afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_WAITING: 311afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_PENDING: 312afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_ABORTING: 313afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_CONCLUDED: 314afe1e8a7SEmanuele Giuseppe Esposito case JOB_STATUS_NULL: 315afe1e8a7SEmanuele Giuseppe Esposito return true; 316afe1e8a7SEmanuele Giuseppe Esposito default: 317afe1e8a7SEmanuele Giuseppe Esposito g_assert_not_reached(); 318afe1e8a7SEmanuele Giuseppe Esposito } 319afe1e8a7SEmanuele Giuseppe Esposito return false; 320afe1e8a7SEmanuele Giuseppe Esposito } 321afe1e8a7SEmanuele Giuseppe Esposito 3229bd4d3c2SEmanuele Giuseppe Esposito static bool job_is_completed(Job *job) 323dbe5e6c1SKevin Wolf { 324afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 325afe1e8a7SEmanuele Giuseppe Esposito return job_is_completed_locked(job); 326dbe5e6c1SKevin Wolf } 327dbe5e6c1SKevin Wolf 328afe1e8a7SEmanuele Giuseppe Esposito static bool job_started_locked(Job *job) 329da01ff7fSKevin Wolf { 330da01ff7fSKevin Wolf return job->co; 331da01ff7fSKevin Wolf } 332da01ff7fSKevin Wolf 333afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 334afe1e8a7SEmanuele Giuseppe Esposito static bool job_should_pause_locked(Job *job) 335da01ff7fSKevin Wolf { 336da01ff7fSKevin Wolf return job->pause_count > 0; 337da01ff7fSKevin Wolf } 338da01ff7fSKevin Wolf 339afe1e8a7SEmanuele Giuseppe Esposito Job *job_next_locked(Job *job) 340e7c1d78bSKevin Wolf { 341e7c1d78bSKevin Wolf if (!job) { 342e7c1d78bSKevin Wolf return QLIST_FIRST(&jobs); 343e7c1d78bSKevin Wolf } 344e7c1d78bSKevin Wolf return QLIST_NEXT(job, job_list); 345e7c1d78bSKevin Wolf } 346e7c1d78bSKevin Wolf 347afe1e8a7SEmanuele Giuseppe Esposito Job *job_next(Job *job) 348afe1e8a7SEmanuele Giuseppe Esposito { 349afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 350afe1e8a7SEmanuele Giuseppe Esposito return job_next_locked(job); 351afe1e8a7SEmanuele Giuseppe Esposito } 352afe1e8a7SEmanuele Giuseppe Esposito 353afe1e8a7SEmanuele Giuseppe Esposito Job *job_get_locked(const char *id) 354e7c1d78bSKevin Wolf { 355e7c1d78bSKevin Wolf Job *job; 356e7c1d78bSKevin Wolf 357e7c1d78bSKevin Wolf QLIST_FOREACH(job, &jobs, job_list) { 358e7c1d78bSKevin Wolf if (job->id && !strcmp(id, job->id)) { 359e7c1d78bSKevin Wolf return job; 360e7c1d78bSKevin Wolf } 361e7c1d78bSKevin Wolf } 362e7c1d78bSKevin Wolf 363e7c1d78bSKevin Wolf return NULL; 364e7c1d78bSKevin Wolf } 365e7c1d78bSKevin Wolf 3663ed4f708SEmanuele Giuseppe Esposito void job_set_aio_context(Job *job, AioContext *ctx) 3673ed4f708SEmanuele Giuseppe Esposito { 3683ed4f708SEmanuele Giuseppe Esposito /* protect against read in job_finish_sync_locked and job_start */ 3693ed4f708SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 3703ed4f708SEmanuele Giuseppe Esposito /* protect against read in job_do_yield_locked */ 3713ed4f708SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 3723ed4f708SEmanuele Giuseppe Esposito /* ensure the job is quiescent while the AioContext is changed */ 3733ed4f708SEmanuele Giuseppe Esposito assert(job->paused || job_is_completed_locked(job)); 3743ed4f708SEmanuele Giuseppe Esposito job->aio_context = ctx; 3753ed4f708SEmanuele Giuseppe Esposito } 3763ed4f708SEmanuele Giuseppe Esposito 377afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex *not* held. */ 3785d43e86eSKevin Wolf static void job_sleep_timer_cb(void *opaque) 3795d43e86eSKevin Wolf { 3805d43e86eSKevin Wolf Job *job = opaque; 3815d43e86eSKevin Wolf 3825d43e86eSKevin Wolf job_enter(job); 3835d43e86eSKevin Wolf } 3845d43e86eSKevin Wolf 3857eaa8fb5SKevin Wolf void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn, 3867eaa8fb5SKevin Wolf AioContext *ctx, int flags, BlockCompletionFunc *cb, 3877eaa8fb5SKevin Wolf void *opaque, Error **errp) 38833e9e9bdSKevin Wolf { 38933e9e9bdSKevin Wolf Job *job; 39033e9e9bdSKevin Wolf 391afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 392afe1e8a7SEmanuele Giuseppe Esposito 39333e9e9bdSKevin Wolf if (job_id) { 394bb02b65cSKevin Wolf if (flags & JOB_INTERNAL) { 395bb02b65cSKevin Wolf error_setg(errp, "Cannot specify job ID for internal job"); 396bb02b65cSKevin Wolf return NULL; 397bb02b65cSKevin Wolf } 39833e9e9bdSKevin Wolf if (!id_wellformed(job_id)) { 39933e9e9bdSKevin Wolf error_setg(errp, "Invalid job ID '%s'", job_id); 40033e9e9bdSKevin Wolf return NULL; 40133e9e9bdSKevin Wolf } 402afe1e8a7SEmanuele Giuseppe Esposito if (job_get_locked(job_id)) { 403e7c1d78bSKevin Wolf error_setg(errp, "Job ID '%s' already in use", job_id); 404e7c1d78bSKevin Wolf return NULL; 405e7c1d78bSKevin Wolf } 406bb02b65cSKevin Wolf } else if (!(flags & JOB_INTERNAL)) { 407bb02b65cSKevin Wolf error_setg(errp, "An explicit job ID is required"); 408bb02b65cSKevin Wolf return NULL; 40933e9e9bdSKevin Wolf } 41033e9e9bdSKevin Wolf 41133e9e9bdSKevin Wolf job = g_malloc0(driver->instance_size); 41233e9e9bdSKevin Wolf job->driver = driver; 41333e9e9bdSKevin Wolf job->id = g_strdup(job_id); 41480fa2c75SKevin Wolf job->refcnt = 1; 41508be6fe2SKevin Wolf job->aio_context = ctx; 416da01ff7fSKevin Wolf job->busy = false; 417da01ff7fSKevin Wolf job->paused = true; 418da01ff7fSKevin Wolf job->pause_count = 1; 419bb02b65cSKevin Wolf job->auto_finalize = !(flags & JOB_MANUAL_FINALIZE); 420bb02b65cSKevin Wolf job->auto_dismiss = !(flags & JOB_MANUAL_DISMISS); 4214ad35181SKevin Wolf job->cb = cb; 4224ad35181SKevin Wolf job->opaque = opaque; 42333e9e9bdSKevin Wolf 424a7b4f8fcSEmanuele Giuseppe Esposito progress_init(&job->progress); 425a7b4f8fcSEmanuele Giuseppe Esposito 426139a9f02SKevin Wolf notifier_list_init(&job->on_finalize_cancelled); 427139a9f02SKevin Wolf notifier_list_init(&job->on_finalize_completed); 428139a9f02SKevin Wolf notifier_list_init(&job->on_pending); 4292e1795b5SKevin Wolf notifier_list_init(&job->on_ready); 430252f4091SEmanuele Giuseppe Esposito notifier_list_init(&job->on_idle); 431139a9f02SKevin Wolf 432afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_CREATED); 4335d43e86eSKevin Wolf aio_timer_init(qemu_get_aio_context(), &job->sleep_timer, 4345d43e86eSKevin Wolf QEMU_CLOCK_REALTIME, SCALE_NS, 4355d43e86eSKevin Wolf job_sleep_timer_cb, job); 436a50c2ab8SKevin Wolf 437e7c1d78bSKevin Wolf QLIST_INSERT_HEAD(&jobs, job, job_list); 438e7c1d78bSKevin Wolf 4397eaa8fb5SKevin Wolf /* Single jobs are modeled as single-job transactions for sake of 4407eaa8fb5SKevin Wolf * consolidating the job management logic */ 4417eaa8fb5SKevin Wolf if (!txn) { 4427eaa8fb5SKevin Wolf txn = job_txn_new(); 443afe1e8a7SEmanuele Giuseppe Esposito job_txn_add_job_locked(txn, job); 444afe1e8a7SEmanuele Giuseppe Esposito job_txn_unref_locked(txn); 4457eaa8fb5SKevin Wolf } else { 446afe1e8a7SEmanuele Giuseppe Esposito job_txn_add_job_locked(txn, job); 4477eaa8fb5SKevin Wolf } 4487eaa8fb5SKevin Wolf 44933e9e9bdSKevin Wolf return job; 45033e9e9bdSKevin Wolf } 451fd61a701SKevin Wolf 452afe1e8a7SEmanuele Giuseppe Esposito void job_ref_locked(Job *job) 453fd61a701SKevin Wolf { 45480fa2c75SKevin Wolf ++job->refcnt; 45580fa2c75SKevin Wolf } 45680fa2c75SKevin Wolf 457afe1e8a7SEmanuele Giuseppe Esposito void job_unref_locked(Job *job) 45880fa2c75SKevin Wolf { 459c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 460c70b8031SEmanuele Giuseppe Esposito 46180fa2c75SKevin Wolf if (--job->refcnt == 0) { 46280fa2c75SKevin Wolf assert(job->status == JOB_STATUS_NULL); 4635d43e86eSKevin Wolf assert(!timer_pending(&job->sleep_timer)); 4647eaa8fb5SKevin Wolf assert(!job->txn); 46580fa2c75SKevin Wolf 46680fa2c75SKevin Wolf if (job->driver->free) { 4676f592e5aSEmanuele Giuseppe Esposito AioContext *aio_context = job->aio_context; 468afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 4696f592e5aSEmanuele Giuseppe Esposito /* FIXME: aiocontext lock is required because cb calls blk_unref */ 4706f592e5aSEmanuele Giuseppe Esposito aio_context_acquire(aio_context); 47180fa2c75SKevin Wolf job->driver->free(job); 4726f592e5aSEmanuele Giuseppe Esposito aio_context_release(aio_context); 473afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 47480fa2c75SKevin Wolf } 47580fa2c75SKevin Wolf 476e7c1d78bSKevin Wolf QLIST_REMOVE(job, job_list); 477e7c1d78bSKevin Wolf 478a7b4f8fcSEmanuele Giuseppe Esposito progress_destroy(&job->progress); 4793d1f8b07SJohn Snow error_free(job->err); 480fd61a701SKevin Wolf g_free(job->id); 481fd61a701SKevin Wolf g_free(job); 482fd61a701SKevin Wolf } 48380fa2c75SKevin Wolf } 4841908a559SKevin Wolf 48530a5c887SKevin Wolf void job_progress_update(Job *job, uint64_t done) 48630a5c887SKevin Wolf { 48701fe1ca9SVladimir Sementsov-Ogievskiy progress_work_done(&job->progress, done); 48830a5c887SKevin Wolf } 48930a5c887SKevin Wolf 49030a5c887SKevin Wolf void job_progress_set_remaining(Job *job, uint64_t remaining) 49130a5c887SKevin Wolf { 49201fe1ca9SVladimir Sementsov-Ogievskiy progress_set_remaining(&job->progress, remaining); 49330a5c887SKevin Wolf } 49430a5c887SKevin Wolf 49562f13600SMax Reitz void job_progress_increase_remaining(Job *job, uint64_t delta) 49662f13600SMax Reitz { 49701fe1ca9SVladimir Sementsov-Ogievskiy progress_increase_remaining(&job->progress, delta); 49862f13600SMax Reitz } 49962f13600SMax Reitz 500544f4d52SEmanuele Giuseppe Esposito /** 501544f4d52SEmanuele Giuseppe Esposito * To be called when a cancelled job is finalised. 502afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex held. 503544f4d52SEmanuele Giuseppe Esposito */ 504afe1e8a7SEmanuele Giuseppe Esposito static void job_event_cancelled_locked(Job *job) 505139a9f02SKevin Wolf { 506139a9f02SKevin Wolf notifier_list_notify(&job->on_finalize_cancelled, job); 507139a9f02SKevin Wolf } 508139a9f02SKevin Wolf 509544f4d52SEmanuele Giuseppe Esposito /** 510544f4d52SEmanuele Giuseppe Esposito * To be called when a successfully completed job is finalised. 511afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex held. 512544f4d52SEmanuele Giuseppe Esposito */ 513afe1e8a7SEmanuele Giuseppe Esposito static void job_event_completed_locked(Job *job) 514139a9f02SKevin Wolf { 515139a9f02SKevin Wolf notifier_list_notify(&job->on_finalize_completed, job); 516139a9f02SKevin Wolf } 517139a9f02SKevin Wolf 518afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 519afe1e8a7SEmanuele Giuseppe Esposito static void job_event_pending_locked(Job *job) 520139a9f02SKevin Wolf { 521139a9f02SKevin Wolf notifier_list_notify(&job->on_pending, job); 522139a9f02SKevin Wolf } 523139a9f02SKevin Wolf 524afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 525afe1e8a7SEmanuele Giuseppe Esposito static void job_event_ready_locked(Job *job) 5262e1795b5SKevin Wolf { 5272e1795b5SKevin Wolf notifier_list_notify(&job->on_ready, job); 5282e1795b5SKevin Wolf } 5292e1795b5SKevin Wolf 530afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 531afe1e8a7SEmanuele Giuseppe Esposito static void job_event_idle_locked(Job *job) 53234dc97b9SKevin Wolf { 53334dc97b9SKevin Wolf notifier_list_notify(&job->on_idle, job); 53434dc97b9SKevin Wolf } 53534dc97b9SKevin Wolf 536afe1e8a7SEmanuele Giuseppe Esposito void job_enter_cond_locked(Job *job, bool(*fn)(Job *job)) 537da01ff7fSKevin Wolf { 538afe1e8a7SEmanuele Giuseppe Esposito if (!job_started_locked(job)) { 539da01ff7fSKevin Wolf return; 540da01ff7fSKevin Wolf } 541da01ff7fSKevin Wolf if (job->deferred_to_main_loop) { 542da01ff7fSKevin Wolf return; 543da01ff7fSKevin Wolf } 544da01ff7fSKevin Wolf 545da01ff7fSKevin Wolf if (job->busy) { 546da01ff7fSKevin Wolf return; 547da01ff7fSKevin Wolf } 548da01ff7fSKevin Wolf 549da01ff7fSKevin Wolf if (fn && !fn(job)) { 550da01ff7fSKevin Wolf return; 551da01ff7fSKevin Wolf } 552da01ff7fSKevin Wolf 553da01ff7fSKevin Wolf assert(!job->deferred_to_main_loop); 554da01ff7fSKevin Wolf timer_del(&job->sleep_timer); 555da01ff7fSKevin Wolf job->busy = true; 556afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 557ef02dac2SPaolo Bonzini aio_co_wake(job->co); 558afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 559afe1e8a7SEmanuele Giuseppe Esposito } 560afe1e8a7SEmanuele Giuseppe Esposito 5615d43e86eSKevin Wolf void job_enter(Job *job) 5625d43e86eSKevin Wolf { 563afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 564afe1e8a7SEmanuele Giuseppe Esposito job_enter_cond_locked(job, NULL); 5655d43e86eSKevin Wolf } 5665d43e86eSKevin Wolf 567da01ff7fSKevin Wolf /* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds. 5683d70ff53SKevin Wolf * Reentering the job coroutine with job_enter() before the timer has expired 5693d70ff53SKevin Wolf * is allowed and cancels the timer. 570da01ff7fSKevin Wolf * 5713d70ff53SKevin Wolf * If @ns is (uint64_t) -1, no timer is scheduled and job_enter() must be 572afe1e8a7SEmanuele Giuseppe Esposito * called explicitly. 573afe1e8a7SEmanuele Giuseppe Esposito * 574afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex held, but releases it temporarily. 575afe1e8a7SEmanuele Giuseppe Esposito */ 576afe1e8a7SEmanuele Giuseppe Esposito static void coroutine_fn job_do_yield_locked(Job *job, uint64_t ns) 577da01ff7fSKevin Wolf { 578ef02dac2SPaolo Bonzini AioContext *next_aio_context; 579ef02dac2SPaolo Bonzini 580da01ff7fSKevin Wolf if (ns != -1) { 581da01ff7fSKevin Wolf timer_mod(&job->sleep_timer, ns); 582da01ff7fSKevin Wolf } 583da01ff7fSKevin Wolf job->busy = false; 584afe1e8a7SEmanuele Giuseppe Esposito job_event_idle_locked(job); 585afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 586da01ff7fSKevin Wolf qemu_coroutine_yield(); 587afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 588da01ff7fSKevin Wolf 589ef02dac2SPaolo Bonzini next_aio_context = job->aio_context; 590ef02dac2SPaolo Bonzini /* 591ef02dac2SPaolo Bonzini * Coroutine has resumed, but in the meanwhile the job AioContext 592142e6907SEmanuele Giuseppe Esposito * might have changed via bdrv_try_change_aio_context(), so we need to move 593ef02dac2SPaolo Bonzini * the coroutine too in the new aiocontext. 594ef02dac2SPaolo Bonzini */ 595ef02dac2SPaolo Bonzini while (qemu_get_current_aio_context() != next_aio_context) { 596ef02dac2SPaolo Bonzini job_unlock(); 597ef02dac2SPaolo Bonzini aio_co_reschedule_self(next_aio_context); 598ef02dac2SPaolo Bonzini job_lock(); 599ef02dac2SPaolo Bonzini next_aio_context = job->aio_context; 600ef02dac2SPaolo Bonzini } 601ef02dac2SPaolo Bonzini 602ef02dac2SPaolo Bonzini /* Set by job_enter_cond_locked() before re-entering the coroutine. */ 603da01ff7fSKevin Wolf assert(job->busy); 604da01ff7fSKevin Wolf } 605da01ff7fSKevin Wolf 606afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily. */ 607afe1e8a7SEmanuele Giuseppe Esposito static void coroutine_fn job_pause_point_locked(Job *job) 608da01ff7fSKevin Wolf { 609afe1e8a7SEmanuele Giuseppe Esposito assert(job && job_started_locked(job)); 610da01ff7fSKevin Wolf 611afe1e8a7SEmanuele Giuseppe Esposito if (!job_should_pause_locked(job)) { 612da01ff7fSKevin Wolf return; 613da01ff7fSKevin Wolf } 614afe1e8a7SEmanuele Giuseppe Esposito if (job_is_cancelled_locked(job)) { 615da01ff7fSKevin Wolf return; 616da01ff7fSKevin Wolf } 617da01ff7fSKevin Wolf 618da01ff7fSKevin Wolf if (job->driver->pause) { 619afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 620da01ff7fSKevin Wolf job->driver->pause(job); 621afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 622da01ff7fSKevin Wolf } 623da01ff7fSKevin Wolf 624afe1e8a7SEmanuele Giuseppe Esposito if (job_should_pause_locked(job) && !job_is_cancelled_locked(job)) { 625da01ff7fSKevin Wolf JobStatus status = job->status; 626afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, status == JOB_STATUS_READY 627da01ff7fSKevin Wolf ? JOB_STATUS_STANDBY 628da01ff7fSKevin Wolf : JOB_STATUS_PAUSED); 629da01ff7fSKevin Wolf job->paused = true; 630afe1e8a7SEmanuele Giuseppe Esposito job_do_yield_locked(job, -1); 631da01ff7fSKevin Wolf job->paused = false; 632afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, status); 633da01ff7fSKevin Wolf } 634da01ff7fSKevin Wolf 635da01ff7fSKevin Wolf if (job->driver->resume) { 636afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 637da01ff7fSKevin Wolf job->driver->resume(job); 638afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 639da01ff7fSKevin Wolf } 640da01ff7fSKevin Wolf } 641da01ff7fSKevin Wolf 642afe1e8a7SEmanuele Giuseppe Esposito void coroutine_fn job_pause_point(Job *job) 643afe1e8a7SEmanuele Giuseppe Esposito { 644afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 645afe1e8a7SEmanuele Giuseppe Esposito job_pause_point_locked(job); 646afe1e8a7SEmanuele Giuseppe Esposito } 647afe1e8a7SEmanuele Giuseppe Esposito 6489bd4d3c2SEmanuele Giuseppe Esposito void coroutine_fn job_yield(Job *job) 649afe1e8a7SEmanuele Giuseppe Esposito { 6509bd4d3c2SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 651afe1e8a7SEmanuele Giuseppe Esposito assert(job->busy); 652afe1e8a7SEmanuele Giuseppe Esposito 653afe1e8a7SEmanuele Giuseppe Esposito /* Check cancellation *before* setting busy = false, too! */ 654afe1e8a7SEmanuele Giuseppe Esposito if (job_is_cancelled_locked(job)) { 655afe1e8a7SEmanuele Giuseppe Esposito return; 656afe1e8a7SEmanuele Giuseppe Esposito } 657afe1e8a7SEmanuele Giuseppe Esposito 658afe1e8a7SEmanuele Giuseppe Esposito if (!job_should_pause_locked(job)) { 659afe1e8a7SEmanuele Giuseppe Esposito job_do_yield_locked(job, -1); 660afe1e8a7SEmanuele Giuseppe Esposito } 661afe1e8a7SEmanuele Giuseppe Esposito 662afe1e8a7SEmanuele Giuseppe Esposito job_pause_point_locked(job); 663afe1e8a7SEmanuele Giuseppe Esposito } 664afe1e8a7SEmanuele Giuseppe Esposito 6655d43e86eSKevin Wolf void coroutine_fn job_sleep_ns(Job *job, int64_t ns) 6665d43e86eSKevin Wolf { 667afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 6685d43e86eSKevin Wolf assert(job->busy); 6695d43e86eSKevin Wolf 6705d43e86eSKevin Wolf /* Check cancellation *before* setting busy = false, too! */ 671afe1e8a7SEmanuele Giuseppe Esposito if (job_is_cancelled_locked(job)) { 6725d43e86eSKevin Wolf return; 6735d43e86eSKevin Wolf } 6745d43e86eSKevin Wolf 675afe1e8a7SEmanuele Giuseppe Esposito if (!job_should_pause_locked(job)) { 676afe1e8a7SEmanuele Giuseppe Esposito job_do_yield_locked(job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns); 6775d43e86eSKevin Wolf } 6785d43e86eSKevin Wolf 679afe1e8a7SEmanuele Giuseppe Esposito job_pause_point_locked(job); 6805d43e86eSKevin Wolf } 6815d43e86eSKevin Wolf 682afe1e8a7SEmanuele Giuseppe Esposito /* Assumes the job_mutex is held */ 683afe1e8a7SEmanuele Giuseppe Esposito static bool job_timer_not_pending_locked(Job *job) 684b15de828SKevin Wolf { 685b15de828SKevin Wolf return !timer_pending(&job->sleep_timer); 686b15de828SKevin Wolf } 687b15de828SKevin Wolf 688afe1e8a7SEmanuele Giuseppe Esposito void job_pause_locked(Job *job) 689b15de828SKevin Wolf { 690b15de828SKevin Wolf job->pause_count++; 6913ee1483bSVladimir Sementsov-Ogievskiy if (!job->paused) { 692afe1e8a7SEmanuele Giuseppe Esposito job_enter_cond_locked(job, NULL); 6933ee1483bSVladimir Sementsov-Ogievskiy } 694b15de828SKevin Wolf } 695b15de828SKevin Wolf 696afe1e8a7SEmanuele Giuseppe Esposito void job_pause(Job *job) 697afe1e8a7SEmanuele Giuseppe Esposito { 698afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 699afe1e8a7SEmanuele Giuseppe Esposito job_pause_locked(job); 700afe1e8a7SEmanuele Giuseppe Esposito } 701afe1e8a7SEmanuele Giuseppe Esposito 702afe1e8a7SEmanuele Giuseppe Esposito void job_resume_locked(Job *job) 703b15de828SKevin Wolf { 704b15de828SKevin Wolf assert(job->pause_count > 0); 705b15de828SKevin Wolf job->pause_count--; 706b15de828SKevin Wolf if (job->pause_count) { 707b15de828SKevin Wolf return; 708b15de828SKevin Wolf } 709b15de828SKevin Wolf 710b15de828SKevin Wolf /* kick only if no timer is pending */ 711afe1e8a7SEmanuele Giuseppe Esposito job_enter_cond_locked(job, job_timer_not_pending_locked); 712b15de828SKevin Wolf } 713b15de828SKevin Wolf 714afe1e8a7SEmanuele Giuseppe Esposito void job_resume(Job *job) 715b15de828SKevin Wolf { 716afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 717afe1e8a7SEmanuele Giuseppe Esposito job_resume_locked(job); 718afe1e8a7SEmanuele Giuseppe Esposito } 719afe1e8a7SEmanuele Giuseppe Esposito 720afe1e8a7SEmanuele Giuseppe Esposito void job_user_pause_locked(Job *job, Error **errp) 721afe1e8a7SEmanuele Giuseppe Esposito { 722afe1e8a7SEmanuele Giuseppe Esposito if (job_apply_verb_locked(job, JOB_VERB_PAUSE, errp)) { 723b15de828SKevin Wolf return; 724b15de828SKevin Wolf } 725b15de828SKevin Wolf if (job->user_paused) { 726b15de828SKevin Wolf error_setg(errp, "Job is already paused"); 727b15de828SKevin Wolf return; 728b15de828SKevin Wolf } 729b15de828SKevin Wolf job->user_paused = true; 730afe1e8a7SEmanuele Giuseppe Esposito job_pause_locked(job); 731b15de828SKevin Wolf } 732b15de828SKevin Wolf 733afe1e8a7SEmanuele Giuseppe Esposito bool job_user_paused_locked(Job *job) 734b15de828SKevin Wolf { 735b15de828SKevin Wolf return job->user_paused; 736b15de828SKevin Wolf } 737b15de828SKevin Wolf 738afe1e8a7SEmanuele Giuseppe Esposito void job_user_resume_locked(Job *job, Error **errp) 739b15de828SKevin Wolf { 740b15de828SKevin Wolf assert(job); 741c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 742b15de828SKevin Wolf if (!job->user_paused || job->pause_count <= 0) { 743b15de828SKevin Wolf error_setg(errp, "Can't resume a job that was not paused"); 744b15de828SKevin Wolf return; 745b15de828SKevin Wolf } 746afe1e8a7SEmanuele Giuseppe Esposito if (job_apply_verb_locked(job, JOB_VERB_RESUME, errp)) { 747b15de828SKevin Wolf return; 748b15de828SKevin Wolf } 749b15de828SKevin Wolf if (job->driver->user_resume) { 750afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 751b15de828SKevin Wolf job->driver->user_resume(job); 752afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 753b15de828SKevin Wolf } 754b15de828SKevin Wolf job->user_paused = false; 755afe1e8a7SEmanuele Giuseppe Esposito job_resume_locked(job); 756b15de828SKevin Wolf } 757b15de828SKevin Wolf 758afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily. */ 759afe1e8a7SEmanuele Giuseppe Esposito static void job_do_dismiss_locked(Job *job) 7604ad35181SKevin Wolf { 7614ad35181SKevin Wolf assert(job); 7624ad35181SKevin Wolf job->busy = false; 7634ad35181SKevin Wolf job->paused = false; 7644ad35181SKevin Wolf job->deferred_to_main_loop = true; 7654ad35181SKevin Wolf 766afe1e8a7SEmanuele Giuseppe Esposito job_txn_del_job_locked(job); 7674ad35181SKevin Wolf 768afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_NULL); 769afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 7704ad35181SKevin Wolf } 7714ad35181SKevin Wolf 772afe1e8a7SEmanuele Giuseppe Esposito void job_dismiss_locked(Job **jobptr, Error **errp) 7735f9a6a08SKevin Wolf { 7745f9a6a08SKevin Wolf Job *job = *jobptr; 7755f9a6a08SKevin Wolf /* similarly to _complete, this is QMP-interface only. */ 7765f9a6a08SKevin Wolf assert(job->id); 777afe1e8a7SEmanuele Giuseppe Esposito if (job_apply_verb_locked(job, JOB_VERB_DISMISS, errp)) { 7785f9a6a08SKevin Wolf return; 7795f9a6a08SKevin Wolf } 7805f9a6a08SKevin Wolf 781afe1e8a7SEmanuele Giuseppe Esposito job_do_dismiss_locked(job); 7825f9a6a08SKevin Wolf *jobptr = NULL; 7835f9a6a08SKevin Wolf } 7845f9a6a08SKevin Wolf 7854ad35181SKevin Wolf void job_early_fail(Job *job) 7864ad35181SKevin Wolf { 787afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 7884ad35181SKevin Wolf assert(job->status == JOB_STATUS_CREATED); 789afe1e8a7SEmanuele Giuseppe Esposito job_do_dismiss_locked(job); 7904ad35181SKevin Wolf } 7914ad35181SKevin Wolf 792afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 793afe1e8a7SEmanuele Giuseppe Esposito static void job_conclude_locked(Job *job) 7944ad35181SKevin Wolf { 795afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_CONCLUDED); 796afe1e8a7SEmanuele Giuseppe Esposito if (job->auto_dismiss || !job_started_locked(job)) { 797afe1e8a7SEmanuele Giuseppe Esposito job_do_dismiss_locked(job); 7984ad35181SKevin Wolf } 7994ad35181SKevin Wolf } 8004ad35181SKevin Wolf 801afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 802afe1e8a7SEmanuele Giuseppe Esposito static void job_update_rc_locked(Job *job) 8034ad35181SKevin Wolf { 804afe1e8a7SEmanuele Giuseppe Esposito if (!job->ret && job_is_cancelled_locked(job)) { 8054ad35181SKevin Wolf job->ret = -ECANCELED; 8064ad35181SKevin Wolf } 8074ad35181SKevin Wolf if (job->ret) { 8083d1f8b07SJohn Snow if (!job->err) { 8093d1f8b07SJohn Snow error_setg(&job->err, "%s", strerror(-job->ret)); 8101266c9b9SKevin Wolf } 811afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_ABORTING); 8124ad35181SKevin Wolf } 8134ad35181SKevin Wolf } 8144ad35181SKevin Wolf 8154ad35181SKevin Wolf static void job_commit(Job *job) 8164ad35181SKevin Wolf { 8174ad35181SKevin Wolf assert(!job->ret); 818c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 8194ad35181SKevin Wolf if (job->driver->commit) { 8204ad35181SKevin Wolf job->driver->commit(job); 8214ad35181SKevin Wolf } 8224ad35181SKevin Wolf } 8234ad35181SKevin Wolf 8244ad35181SKevin Wolf static void job_abort(Job *job) 8254ad35181SKevin Wolf { 8264ad35181SKevin Wolf assert(job->ret); 827c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 8284ad35181SKevin Wolf if (job->driver->abort) { 8294ad35181SKevin Wolf job->driver->abort(job); 8304ad35181SKevin Wolf } 8314ad35181SKevin Wolf } 8324ad35181SKevin Wolf 8334ad35181SKevin Wolf static void job_clean(Job *job) 8344ad35181SKevin Wolf { 835c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 8364ad35181SKevin Wolf if (job->driver->clean) { 8374ad35181SKevin Wolf job->driver->clean(job); 8384ad35181SKevin Wolf } 8394ad35181SKevin Wolf } 8404ad35181SKevin Wolf 8416f592e5aSEmanuele Giuseppe Esposito /* 8426f592e5aSEmanuele Giuseppe Esposito * Called with job_mutex held, but releases it temporarily. 8436f592e5aSEmanuele Giuseppe Esposito * Takes AioContext lock internally to invoke a job->driver callback. 8446f592e5aSEmanuele Giuseppe Esposito */ 845afe1e8a7SEmanuele Giuseppe Esposito static int job_finalize_single_locked(Job *job) 8464ad35181SKevin Wolf { 847afe1e8a7SEmanuele Giuseppe Esposito int job_ret; 8486f592e5aSEmanuele Giuseppe Esposito AioContext *ctx = job->aio_context; 849afe1e8a7SEmanuele Giuseppe Esposito 850afe1e8a7SEmanuele Giuseppe Esposito assert(job_is_completed_locked(job)); 8514ad35181SKevin Wolf 8524ad35181SKevin Wolf /* Ensure abort is called for late-transactional failures */ 853afe1e8a7SEmanuele Giuseppe Esposito job_update_rc_locked(job); 8544ad35181SKevin Wolf 855afe1e8a7SEmanuele Giuseppe Esposito job_ret = job->ret; 856afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 8576f592e5aSEmanuele Giuseppe Esposito aio_context_acquire(ctx); 858afe1e8a7SEmanuele Giuseppe Esposito 859afe1e8a7SEmanuele Giuseppe Esposito if (!job_ret) { 8604ad35181SKevin Wolf job_commit(job); 8614ad35181SKevin Wolf } else { 8624ad35181SKevin Wolf job_abort(job); 8634ad35181SKevin Wolf } 8644ad35181SKevin Wolf job_clean(job); 8654ad35181SKevin Wolf 8664ad35181SKevin Wolf if (job->cb) { 867afe1e8a7SEmanuele Giuseppe Esposito job->cb(job->opaque, job_ret); 8684ad35181SKevin Wolf } 8694ad35181SKevin Wolf 8706f592e5aSEmanuele Giuseppe Esposito aio_context_release(ctx); 8716f592e5aSEmanuele Giuseppe Esposito job_lock(); 8726f592e5aSEmanuele Giuseppe Esposito 8734ad35181SKevin Wolf /* Emit events only if we actually started */ 874afe1e8a7SEmanuele Giuseppe Esposito if (job_started_locked(job)) { 875afe1e8a7SEmanuele Giuseppe Esposito if (job_is_cancelled_locked(job)) { 876afe1e8a7SEmanuele Giuseppe Esposito job_event_cancelled_locked(job); 8774ad35181SKevin Wolf } else { 878afe1e8a7SEmanuele Giuseppe Esposito job_event_completed_locked(job); 8794ad35181SKevin Wolf } 8804ad35181SKevin Wolf } 8814ad35181SKevin Wolf 882afe1e8a7SEmanuele Giuseppe Esposito job_txn_del_job_locked(job); 883afe1e8a7SEmanuele Giuseppe Esposito job_conclude_locked(job); 8844ad35181SKevin Wolf return 0; 8854ad35181SKevin Wolf } 8864ad35181SKevin Wolf 8876f592e5aSEmanuele Giuseppe Esposito /* 8886f592e5aSEmanuele Giuseppe Esposito * Called with job_mutex held, but releases it temporarily. 8896f592e5aSEmanuele Giuseppe Esposito * Takes AioContext lock internally to invoke a job->driver callback. 8906f592e5aSEmanuele Giuseppe Esposito */ 891afe1e8a7SEmanuele Giuseppe Esposito static void job_cancel_async_locked(Job *job, bool force) 8927eaa8fb5SKevin Wolf { 8936f592e5aSEmanuele Giuseppe Esposito AioContext *ctx = job->aio_context; 894c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 8959820933bSVladimir Sementsov-Ogievskiy if (job->driver->cancel) { 896afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 8976f592e5aSEmanuele Giuseppe Esposito aio_context_acquire(ctx); 89873895f38SHanna Reitz force = job->driver->cancel(job, force); 8996f592e5aSEmanuele Giuseppe Esposito aio_context_release(ctx); 900afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 90173895f38SHanna Reitz } else { 90273895f38SHanna Reitz /* No .cancel() means the job will behave as if force-cancelled */ 90373895f38SHanna Reitz force = true; 9049820933bSVladimir Sementsov-Ogievskiy } 90573895f38SHanna Reitz 9067eaa8fb5SKevin Wolf if (job->user_paused) { 9077eaa8fb5SKevin Wolf /* Do not call job_enter here, the caller will handle it. */ 9087eaa8fb5SKevin Wolf if (job->driver->user_resume) { 909afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 9107eaa8fb5SKevin Wolf job->driver->user_resume(job); 911afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 9127eaa8fb5SKevin Wolf } 913e321c059SJeff Cody job->user_paused = false; 9147eaa8fb5SKevin Wolf assert(job->pause_count > 0); 9157eaa8fb5SKevin Wolf job->pause_count--; 9167eaa8fb5SKevin Wolf } 917401dd096SHanna Reitz 918401dd096SHanna Reitz /* 919401dd096SHanna Reitz * Ignore soft cancel requests after the job is already done 920401dd096SHanna Reitz * (We will still invoke job->driver->cancel() above, but if the 921401dd096SHanna Reitz * job driver supports soft cancelling and the job is done, that 922401dd096SHanna Reitz * should be a no-op, too. We still call it so it can override 923401dd096SHanna Reitz * @force.) 924401dd096SHanna Reitz */ 925401dd096SHanna Reitz if (force || !job->deferred_to_main_loop) { 9267eaa8fb5SKevin Wolf job->cancelled = true; 9277eaa8fb5SKevin Wolf /* To prevent 'force == false' overriding a previous 'force == true' */ 9287eaa8fb5SKevin Wolf job->force_cancel |= force; 9297eaa8fb5SKevin Wolf } 930401dd096SHanna Reitz } 9317eaa8fb5SKevin Wolf 9326f592e5aSEmanuele Giuseppe Esposito /* 9336f592e5aSEmanuele Giuseppe Esposito * Called with job_mutex held, but releases it temporarily. 9346f592e5aSEmanuele Giuseppe Esposito * Takes AioContext lock internally to invoke a job->driver callback. 9356f592e5aSEmanuele Giuseppe Esposito */ 936afe1e8a7SEmanuele Giuseppe Esposito static void job_completed_txn_abort_locked(Job *job) 9377eaa8fb5SKevin Wolf { 9387eaa8fb5SKevin Wolf JobTxn *txn = job->txn; 9397eaa8fb5SKevin Wolf Job *other_job; 9407eaa8fb5SKevin Wolf 9417eaa8fb5SKevin Wolf if (txn->aborting) { 9427eaa8fb5SKevin Wolf /* 9437eaa8fb5SKevin Wolf * We are cancelled by another job, which will handle everything. 9447eaa8fb5SKevin Wolf */ 9457eaa8fb5SKevin Wolf return; 9467eaa8fb5SKevin Wolf } 9477eaa8fb5SKevin Wolf txn->aborting = true; 948afe1e8a7SEmanuele Giuseppe Esposito job_txn_ref_locked(txn); 9497eaa8fb5SKevin Wolf 950afe1e8a7SEmanuele Giuseppe Esposito job_ref_locked(job); 9517eaa8fb5SKevin Wolf 9527eaa8fb5SKevin Wolf /* Other jobs are effectively cancelled by us, set the status for 9537eaa8fb5SKevin Wolf * them; this job, however, may or may not be cancelled, depending 9547eaa8fb5SKevin Wolf * on the caller, so leave it. */ 9557eaa8fb5SKevin Wolf QLIST_FOREACH(other_job, &txn->jobs, txn_list) { 9567eaa8fb5SKevin Wolf if (other_job != job) { 9571d4a43e9SHanna Reitz /* 9581d4a43e9SHanna Reitz * This is a transaction: If one job failed, no result will matter. 9591d4a43e9SHanna Reitz * Therefore, pass force=true to terminate all other jobs as quickly 9601d4a43e9SHanna Reitz * as possible. 9611d4a43e9SHanna Reitz */ 962afe1e8a7SEmanuele Giuseppe Esposito job_cancel_async_locked(other_job, true); 9637eaa8fb5SKevin Wolf } 9647eaa8fb5SKevin Wolf } 9657eaa8fb5SKevin Wolf while (!QLIST_EMPTY(&txn->jobs)) { 9667eaa8fb5SKevin Wolf other_job = QLIST_FIRST(&txn->jobs); 967afe1e8a7SEmanuele Giuseppe Esposito if (!job_is_completed_locked(other_job)) { 968afe1e8a7SEmanuele Giuseppe Esposito assert(job_cancel_requested_locked(other_job)); 969afe1e8a7SEmanuele Giuseppe Esposito job_finish_sync_locked(other_job, NULL, NULL); 9707eaa8fb5SKevin Wolf } 971afe1e8a7SEmanuele Giuseppe Esposito job_finalize_single_locked(other_job); 9727eaa8fb5SKevin Wolf } 9737eaa8fb5SKevin Wolf 974afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 975afe1e8a7SEmanuele Giuseppe Esposito job_txn_unref_locked(txn); 9767eaa8fb5SKevin Wolf } 9777eaa8fb5SKevin Wolf 978afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held, but releases it temporarily */ 979afe1e8a7SEmanuele Giuseppe Esposito static int job_prepare_locked(Job *job) 9807eaa8fb5SKevin Wolf { 981afe1e8a7SEmanuele Giuseppe Esposito int ret; 9826f592e5aSEmanuele Giuseppe Esposito AioContext *ctx = job->aio_context; 983afe1e8a7SEmanuele Giuseppe Esposito 984c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 9856f592e5aSEmanuele Giuseppe Esposito 9867eaa8fb5SKevin Wolf if (job->ret == 0 && job->driver->prepare) { 987afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 9886f592e5aSEmanuele Giuseppe Esposito aio_context_acquire(ctx); 989afe1e8a7SEmanuele Giuseppe Esposito ret = job->driver->prepare(job); 9906f592e5aSEmanuele Giuseppe Esposito aio_context_release(ctx); 991afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 992afe1e8a7SEmanuele Giuseppe Esposito job->ret = ret; 993afe1e8a7SEmanuele Giuseppe Esposito job_update_rc_locked(job); 9947eaa8fb5SKevin Wolf } 9956f592e5aSEmanuele Giuseppe Esposito 9967eaa8fb5SKevin Wolf return job->ret; 9977eaa8fb5SKevin Wolf } 9987eaa8fb5SKevin Wolf 999afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held */ 1000afe1e8a7SEmanuele Giuseppe Esposito static int job_needs_finalize_locked(Job *job) 10017eaa8fb5SKevin Wolf { 10027eaa8fb5SKevin Wolf return !job->auto_finalize; 10037eaa8fb5SKevin Wolf } 10047eaa8fb5SKevin Wolf 1005afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held */ 1006afe1e8a7SEmanuele Giuseppe Esposito static void job_do_finalize_locked(Job *job) 10077eaa8fb5SKevin Wolf { 10087eaa8fb5SKevin Wolf int rc; 10097eaa8fb5SKevin Wolf assert(job && job->txn); 10107eaa8fb5SKevin Wolf 10117eaa8fb5SKevin Wolf /* prepare the transaction to complete */ 1012afe1e8a7SEmanuele Giuseppe Esposito rc = job_txn_apply_locked(job, job_prepare_locked); 10137eaa8fb5SKevin Wolf if (rc) { 1014afe1e8a7SEmanuele Giuseppe Esposito job_completed_txn_abort_locked(job); 10157eaa8fb5SKevin Wolf } else { 1016afe1e8a7SEmanuele Giuseppe Esposito job_txn_apply_locked(job, job_finalize_single_locked); 10177eaa8fb5SKevin Wolf } 10187eaa8fb5SKevin Wolf } 10197eaa8fb5SKevin Wolf 1020afe1e8a7SEmanuele Giuseppe Esposito void job_finalize_locked(Job *job, Error **errp) 1021afe1e8a7SEmanuele Giuseppe Esposito { 1022afe1e8a7SEmanuele Giuseppe Esposito assert(job && job->id); 1023afe1e8a7SEmanuele Giuseppe Esposito if (job_apply_verb_locked(job, JOB_VERB_FINALIZE, errp)) { 1024afe1e8a7SEmanuele Giuseppe Esposito return; 1025afe1e8a7SEmanuele Giuseppe Esposito } 1026afe1e8a7SEmanuele Giuseppe Esposito job_do_finalize_locked(job); 1027afe1e8a7SEmanuele Giuseppe Esposito } 1028afe1e8a7SEmanuele Giuseppe Esposito 1029afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 1030afe1e8a7SEmanuele Giuseppe Esposito static int job_transition_to_pending_locked(Job *job) 10317eaa8fb5SKevin Wolf { 1032afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_PENDING); 10337eaa8fb5SKevin Wolf if (!job->auto_finalize) { 1034afe1e8a7SEmanuele Giuseppe Esposito job_event_pending_locked(job); 10357eaa8fb5SKevin Wolf } 10367eaa8fb5SKevin Wolf return 0; 10377eaa8fb5SKevin Wolf } 10387eaa8fb5SKevin Wolf 10392e1795b5SKevin Wolf void job_transition_to_ready(Job *job) 10402e1795b5SKevin Wolf { 1041afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1042afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_READY); 1043afe1e8a7SEmanuele Giuseppe Esposito job_event_ready_locked(job); 10442e1795b5SKevin Wolf } 10452e1795b5SKevin Wolf 1046afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 1047afe1e8a7SEmanuele Giuseppe Esposito static void job_completed_txn_success_locked(Job *job) 10487eaa8fb5SKevin Wolf { 10497eaa8fb5SKevin Wolf JobTxn *txn = job->txn; 10507eaa8fb5SKevin Wolf Job *other_job; 10517eaa8fb5SKevin Wolf 1052afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_WAITING); 10537eaa8fb5SKevin Wolf 10547eaa8fb5SKevin Wolf /* 10557eaa8fb5SKevin Wolf * Successful completion, see if there are other running jobs in this 10567eaa8fb5SKevin Wolf * txn. 10577eaa8fb5SKevin Wolf */ 10587eaa8fb5SKevin Wolf QLIST_FOREACH(other_job, &txn->jobs, txn_list) { 1059afe1e8a7SEmanuele Giuseppe Esposito if (!job_is_completed_locked(other_job)) { 10607eaa8fb5SKevin Wolf return; 10617eaa8fb5SKevin Wolf } 10627eaa8fb5SKevin Wolf assert(other_job->ret == 0); 10637eaa8fb5SKevin Wolf } 10647eaa8fb5SKevin Wolf 1065afe1e8a7SEmanuele Giuseppe Esposito job_txn_apply_locked(job, job_transition_to_pending_locked); 10667eaa8fb5SKevin Wolf 10677eaa8fb5SKevin Wolf /* If no jobs need manual finalization, automatically do so */ 1068afe1e8a7SEmanuele Giuseppe Esposito if (job_txn_apply_locked(job, job_needs_finalize_locked) == 0) { 1069afe1e8a7SEmanuele Giuseppe Esposito job_do_finalize_locked(job); 10707eaa8fb5SKevin Wolf } 10717eaa8fb5SKevin Wolf } 10727eaa8fb5SKevin Wolf 1073afe1e8a7SEmanuele Giuseppe Esposito /* Called with job_mutex held. */ 1074afe1e8a7SEmanuele Giuseppe Esposito static void job_completed_locked(Job *job) 10753d70ff53SKevin Wolf { 1076afe1e8a7SEmanuele Giuseppe Esposito assert(job && job->txn && !job_is_completed_locked(job)); 10771266c9b9SKevin Wolf 1078afe1e8a7SEmanuele Giuseppe Esposito job_update_rc_locked(job); 1079404ff28dSJohn Snow trace_job_completed(job, job->ret); 10803d70ff53SKevin Wolf if (job->ret) { 1081afe1e8a7SEmanuele Giuseppe Esposito job_completed_txn_abort_locked(job); 10823d70ff53SKevin Wolf } else { 1083afe1e8a7SEmanuele Giuseppe Esposito job_completed_txn_success_locked(job); 10843d70ff53SKevin Wolf } 10853d70ff53SKevin Wolf } 10863d70ff53SKevin Wolf 1087afe1e8a7SEmanuele Giuseppe Esposito /** 1088afe1e8a7SEmanuele Giuseppe Esposito * Useful only as a type shim for aio_bh_schedule_oneshot. 1089afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex *not* held. 1090afe1e8a7SEmanuele Giuseppe Esposito */ 1091ccbfb331SJohn Snow static void job_exit(void *opaque) 1092ccbfb331SJohn Snow { 1093ccbfb331SJohn Snow Job *job = (Job *)opaque; 1094afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1095afe1e8a7SEmanuele Giuseppe Esposito job_ref_locked(job); 1096b5a7a057SKevin Wolf 1097b5a7a057SKevin Wolf /* This is a lie, we're not quiescent, but still doing the completion 1098b5a7a057SKevin Wolf * callbacks. However, completion callbacks tend to involve operations that 1099b5a7a057SKevin Wolf * drain block nodes, and if .drained_poll still returned true, we would 1100b5a7a057SKevin Wolf * deadlock. */ 1101b5a7a057SKevin Wolf job->busy = false; 1102afe1e8a7SEmanuele Giuseppe Esposito job_event_idle_locked(job); 1103b5a7a057SKevin Wolf 1104afe1e8a7SEmanuele Giuseppe Esposito job_completed_locked(job); 1105afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 1106ccbfb331SJohn Snow } 1107ccbfb331SJohn Snow 1108ccbfb331SJohn Snow /** 1109ccbfb331SJohn Snow * All jobs must allow a pause point before entering their job proper. This 1110ccbfb331SJohn Snow * ensures that jobs can be paused prior to being started, then resumed later. 1111ccbfb331SJohn Snow */ 1112ccbfb331SJohn Snow static void coroutine_fn job_co_entry(void *opaque) 1113ccbfb331SJohn Snow { 1114ccbfb331SJohn Snow Job *job = opaque; 1115afe1e8a7SEmanuele Giuseppe Esposito int ret; 1116ccbfb331SJohn Snow 1117ccbfb331SJohn Snow assert(job && job->driver && job->driver->run); 1118afe1e8a7SEmanuele Giuseppe Esposito WITH_JOB_LOCK_GUARD() { 1119c70b8031SEmanuele Giuseppe Esposito assert(job->aio_context == qemu_get_current_aio_context()); 1120afe1e8a7SEmanuele Giuseppe Esposito job_pause_point_locked(job); 1121afe1e8a7SEmanuele Giuseppe Esposito } 1122afe1e8a7SEmanuele Giuseppe Esposito ret = job->driver->run(job, &job->err); 1123afe1e8a7SEmanuele Giuseppe Esposito WITH_JOB_LOCK_GUARD() { 1124afe1e8a7SEmanuele Giuseppe Esposito job->ret = ret; 1125ccbfb331SJohn Snow job->deferred_to_main_loop = true; 1126b5a7a057SKevin Wolf job->busy = true; 1127afe1e8a7SEmanuele Giuseppe Esposito } 1128ccbfb331SJohn Snow aio_bh_schedule_oneshot(qemu_get_aio_context(), job_exit, job); 1129ccbfb331SJohn Snow } 1130ccbfb331SJohn Snow 1131ccbfb331SJohn Snow void job_start(Job *job) 1132ccbfb331SJohn Snow { 1133afe1e8a7SEmanuele Giuseppe Esposito assert(qemu_in_main_thread()); 1134afe1e8a7SEmanuele Giuseppe Esposito 1135afe1e8a7SEmanuele Giuseppe Esposito WITH_JOB_LOCK_GUARD() { 1136afe1e8a7SEmanuele Giuseppe Esposito assert(job && !job_started_locked(job) && job->paused && 1137ccbfb331SJohn Snow job->driver && job->driver->run); 1138ccbfb331SJohn Snow job->co = qemu_coroutine_create(job_co_entry, job); 1139ccbfb331SJohn Snow job->pause_count--; 1140ccbfb331SJohn Snow job->busy = true; 1141ccbfb331SJohn Snow job->paused = false; 1142afe1e8a7SEmanuele Giuseppe Esposito job_state_transition_locked(job, JOB_STATUS_RUNNING); 1143afe1e8a7SEmanuele Giuseppe Esposito } 1144ccbfb331SJohn Snow aio_co_enter(job->aio_context, job->co); 1145ccbfb331SJohn Snow } 1146ccbfb331SJohn Snow 1147afe1e8a7SEmanuele Giuseppe Esposito void job_cancel_locked(Job *job, bool force) 11483d70ff53SKevin Wolf { 11493d70ff53SKevin Wolf if (job->status == JOB_STATUS_CONCLUDED) { 1150afe1e8a7SEmanuele Giuseppe Esposito job_do_dismiss_locked(job); 11513d70ff53SKevin Wolf return; 11523d70ff53SKevin Wolf } 1153afe1e8a7SEmanuele Giuseppe Esposito job_cancel_async_locked(job, force); 1154afe1e8a7SEmanuele Giuseppe Esposito if (!job_started_locked(job)) { 1155afe1e8a7SEmanuele Giuseppe Esposito job_completed_locked(job); 11563d70ff53SKevin Wolf } else if (job->deferred_to_main_loop) { 1157401dd096SHanna Reitz /* 1158401dd096SHanna Reitz * job_cancel_async() ignores soft-cancel requests for jobs 1159401dd096SHanna Reitz * that are already done (i.e. deferred to the main loop). We 1160401dd096SHanna Reitz * have to check again whether the job is really cancelled. 116108b83bffSHanna Reitz * (job_cancel_requested() and job_is_cancelled() are equivalent 116208b83bffSHanna Reitz * here, because job_cancel_async() will make soft-cancel 116308b83bffSHanna Reitz * requests no-ops when deferred_to_main_loop is true. We 116408b83bffSHanna Reitz * choose to call job_is_cancelled() to show that we invoke 116508b83bffSHanna Reitz * job_completed_txn_abort() only for force-cancelled jobs.) 1166401dd096SHanna Reitz */ 1167afe1e8a7SEmanuele Giuseppe Esposito if (job_is_cancelled_locked(job)) { 1168afe1e8a7SEmanuele Giuseppe Esposito job_completed_txn_abort_locked(job); 1169401dd096SHanna Reitz } 11703d70ff53SKevin Wolf } else { 1171afe1e8a7SEmanuele Giuseppe Esposito job_enter_cond_locked(job, NULL); 11723d70ff53SKevin Wolf } 11733d70ff53SKevin Wolf } 11743d70ff53SKevin Wolf 1175afe1e8a7SEmanuele Giuseppe Esposito void job_user_cancel_locked(Job *job, bool force, Error **errp) 1176afe1e8a7SEmanuele Giuseppe Esposito { 1177afe1e8a7SEmanuele Giuseppe Esposito if (job_apply_verb_locked(job, JOB_VERB_CANCEL, errp)) { 1178afe1e8a7SEmanuele Giuseppe Esposito return; 1179afe1e8a7SEmanuele Giuseppe Esposito } 1180afe1e8a7SEmanuele Giuseppe Esposito job_cancel_locked(job, force); 1181afe1e8a7SEmanuele Giuseppe Esposito } 1182afe1e8a7SEmanuele Giuseppe Esposito 11839bd4d3c2SEmanuele Giuseppe Esposito /* A wrapper around job_cancel_locked() taking an Error ** parameter so it may 11849bd4d3c2SEmanuele Giuseppe Esposito * be used with job_finish_sync_locked() without the need for (rather nasty) 11859bd4d3c2SEmanuele Giuseppe Esposito * function pointer casts there. 1186afe1e8a7SEmanuele Giuseppe Esposito * 1187afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex held. 1188afe1e8a7SEmanuele Giuseppe Esposito */ 1189afe1e8a7SEmanuele Giuseppe Esposito static void job_cancel_err_locked(Job *job, Error **errp) 11903d70ff53SKevin Wolf { 1191afe1e8a7SEmanuele Giuseppe Esposito job_cancel_locked(job, false); 11923d70ff53SKevin Wolf } 11933d70ff53SKevin Wolf 11944cfb3f05SHanna Reitz /** 11954cfb3f05SHanna Reitz * Same as job_cancel_err(), but force-cancel. 1196afe1e8a7SEmanuele Giuseppe Esposito * Called with job_mutex held. 11974cfb3f05SHanna Reitz */ 1198afe1e8a7SEmanuele Giuseppe Esposito static void job_force_cancel_err_locked(Job *job, Error **errp) 11993d70ff53SKevin Wolf { 1200afe1e8a7SEmanuele Giuseppe Esposito job_cancel_locked(job, true); 1201afe1e8a7SEmanuele Giuseppe Esposito } 1202afe1e8a7SEmanuele Giuseppe Esposito 1203afe1e8a7SEmanuele Giuseppe Esposito int job_cancel_sync_locked(Job *job, bool force) 1204afe1e8a7SEmanuele Giuseppe Esposito { 1205afe1e8a7SEmanuele Giuseppe Esposito if (force) { 1206afe1e8a7SEmanuele Giuseppe Esposito return job_finish_sync_locked(job, &job_force_cancel_err_locked, NULL); 1207afe1e8a7SEmanuele Giuseppe Esposito } else { 1208afe1e8a7SEmanuele Giuseppe Esposito return job_finish_sync_locked(job, &job_cancel_err_locked, NULL); 1209afe1e8a7SEmanuele Giuseppe Esposito } 12104cfb3f05SHanna Reitz } 12114cfb3f05SHanna Reitz 12124cfb3f05SHanna Reitz int job_cancel_sync(Job *job, bool force) 12134cfb3f05SHanna Reitz { 1214afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 1215afe1e8a7SEmanuele Giuseppe Esposito return job_cancel_sync_locked(job, force); 12164cfb3f05SHanna Reitz } 12173d70ff53SKevin Wolf 12183d70ff53SKevin Wolf void job_cancel_sync_all(void) 12193d70ff53SKevin Wolf { 12203d70ff53SKevin Wolf Job *job; 1221afe1e8a7SEmanuele Giuseppe Esposito JOB_LOCK_GUARD(); 12223d70ff53SKevin Wolf 1223afe1e8a7SEmanuele Giuseppe Esposito while ((job = job_next_locked(NULL))) { 1224afe1e8a7SEmanuele Giuseppe Esposito job_cancel_sync_locked(job, true); 12253d70ff53SKevin Wolf } 12263d70ff53SKevin Wolf } 12273d70ff53SKevin Wolf 1228afe1e8a7SEmanuele Giuseppe Esposito int job_complete_sync_locked(Job *job, Error **errp) 12293d70ff53SKevin Wolf { 1230afe1e8a7SEmanuele Giuseppe Esposito return job_finish_sync_locked(job, job_complete_locked, errp); 12313d70ff53SKevin Wolf } 12323d70ff53SKevin Wolf 1233afe1e8a7SEmanuele Giuseppe Esposito void job_complete_locked(Job *job, Error **errp) 12343453d972SKevin Wolf { 12353453d972SKevin Wolf /* Should not be reachable via external interface for internal jobs */ 12363453d972SKevin Wolf assert(job->id); 1237c70b8031SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 1238afe1e8a7SEmanuele Giuseppe Esposito if (job_apply_verb_locked(job, JOB_VERB_COMPLETE, errp)) { 12393453d972SKevin Wolf return; 12403453d972SKevin Wolf } 1241afe1e8a7SEmanuele Giuseppe Esposito if (job_cancel_requested_locked(job) || !job->driver->complete) { 12423453d972SKevin Wolf error_setg(errp, "The active block job '%s' cannot be completed", 12433453d972SKevin Wolf job->id); 12443453d972SKevin Wolf return; 12453453d972SKevin Wolf } 12463453d972SKevin Wolf 1247afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 12483453d972SKevin Wolf job->driver->complete(job, errp); 1249afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 12503453d972SKevin Wolf } 12513453d972SKevin Wolf 1252afe1e8a7SEmanuele Giuseppe Esposito int job_finish_sync_locked(Job *job, 1253afe1e8a7SEmanuele Giuseppe Esposito void (*finish)(Job *, Error **errp), 1254afe1e8a7SEmanuele Giuseppe Esposito Error **errp) 12556a74c075SKevin Wolf { 12566a74c075SKevin Wolf Error *local_err = NULL; 12576a74c075SKevin Wolf int ret; 12583ed4f708SEmanuele Giuseppe Esposito GLOBAL_STATE_CODE(); 12596a74c075SKevin Wolf 1260afe1e8a7SEmanuele Giuseppe Esposito job_ref_locked(job); 12616a74c075SKevin Wolf 12626a74c075SKevin Wolf if (finish) { 12636a74c075SKevin Wolf finish(job, &local_err); 12646a74c075SKevin Wolf } 12656a74c075SKevin Wolf if (local_err) { 12666a74c075SKevin Wolf error_propagate(errp, local_err); 1267afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 12686a74c075SKevin Wolf return -EBUSY; 12696a74c075SKevin Wolf } 1270de0fbe64SKevin Wolf 1271afe1e8a7SEmanuele Giuseppe Esposito job_unlock(); 12726f592e5aSEmanuele Giuseppe Esposito AIO_WAIT_WHILE_UNLOCKED(job->aio_context, 1273bb0c9409SVladimir Sementsov-Ogievskiy (job_enter(job), !job_is_completed(job))); 1274afe1e8a7SEmanuele Giuseppe Esposito job_lock(); 1275de0fbe64SKevin Wolf 1276afe1e8a7SEmanuele Giuseppe Esposito ret = (job_is_cancelled_locked(job) && job->ret == 0) 1277afe1e8a7SEmanuele Giuseppe Esposito ? -ECANCELED : job->ret; 1278afe1e8a7SEmanuele Giuseppe Esposito job_unref_locked(job); 12796a74c075SKevin Wolf return ret; 12806a74c075SKevin Wolf } 1281