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