diff options
author | John Snow <jsnow@redhat.com> | 2018-03-10 03:27:40 -0500 |
---|---|---|
committer | Kevin Wolf <kwolf@redhat.com> | 2018-03-19 12:01:24 +0100 |
commit | 2da4617a5457a6542b0ff702caa983e78d6fe5d6 (patch) | |
tree | 08b6983b1b702735b6b9eb320620627ff4d4237c /blockjob.c | |
parent | efe4d4b7b2f59bb8cdaa8651b832b8153ec24ce9 (diff) |
blockjobs: add prepare callback
Some jobs upon finalization may need to perform some work that can
still fail. If these jobs are part of a transaction, it's important
that these callbacks fail the entire transaction.
We allow for a new callback in addition to commit/abort/clean that
allows us the opportunity to have fairly late-breaking failures
in the transactional process.
The expected flow is:
- All jobs in a transaction converge to the PENDING state,
added in a forthcoming commit.
- Upon being finalized, either automatically or explicitly
by the user, jobs prepare to complete.
- If any job fails preparation, all jobs call .abort.
- Otherwise, they succeed and call .commit.
Signed-off-by: John Snow <jsnow@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Diffstat (limited to 'blockjob.c')
-rw-r--r-- | blockjob.c | 30 |
1 files changed, 27 insertions, 3 deletions
diff --git a/blockjob.c b/blockjob.c index 7e03824751..3c1580e1ef 100644 --- a/blockjob.c +++ b/blockjob.c @@ -415,6 +415,14 @@ static void block_job_update_rc(BlockJob *job) } } +static int block_job_prepare(BlockJob *job) +{ + if (job->ret == 0 && job->driver->prepare) { + job->ret = job->driver->prepare(job); + } + return job->ret; +} + static void block_job_commit(BlockJob *job) { assert(!job->ret); @@ -438,7 +446,7 @@ static void block_job_clean(BlockJob *job) } } -static void block_job_completed_single(BlockJob *job) +static int block_job_completed_single(BlockJob *job) { assert(job->completed); @@ -472,6 +480,7 @@ static void block_job_completed_single(BlockJob *job) QLIST_REMOVE(job, txn_list); block_job_txn_unref(job->txn); block_job_conclude(job); + return 0; } static void block_job_cancel_async(BlockJob *job) @@ -487,17 +496,22 @@ static void block_job_cancel_async(BlockJob *job) job->cancelled = true; } -static void block_job_txn_apply(BlockJobTxn *txn, void fn(BlockJob *)) +static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *)) { AioContext *ctx; BlockJob *job, *next; + int rc = 0; QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) { ctx = blk_get_aio_context(job->blk); aio_context_acquire(ctx); - fn(job); + rc = fn(job); aio_context_release(ctx); + if (rc) { + break; + } } + return rc; } static int block_job_finish_sync(BlockJob *job, @@ -580,6 +594,8 @@ static void block_job_completed_txn_success(BlockJob *job) { BlockJobTxn *txn = job->txn; BlockJob *other_job; + int rc = 0; + /* * Successful completion, see if there are other running jobs in this * txn. @@ -590,6 +606,14 @@ static void block_job_completed_txn_success(BlockJob *job) } assert(other_job->ret == 0); } + + /* Jobs may require some prep-work to complete without failure */ + rc = block_job_txn_apply(txn, block_job_prepare); + if (rc) { + block_job_completed_txn_abort(job); + return; + } + /* We are the last completed job, commit the transaction. */ block_job_txn_apply(txn, block_job_completed_single); } |