qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Kevin Wolf <kwolf@redhat.com>
To: John Snow <jsnow@redhat.com>
Cc: qemu-block@nongnu.org, pkrempa@redhat.com, jtc@redhat.com,
	qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] [RFC v4 18/21] blockjobs: add block-job-finalize
Date: Wed, 28 Feb 2018 19:15:53 +0100	[thread overview]
Message-ID: <20180228181553.GL4855@localhost.localdomain> (raw)
In-Reply-To: <20180223235142.21501-19-jsnow@redhat.com>

Am 24.02.2018 um 00:51 hat John Snow geschrieben:
> Instead of automatically transitioning from PENDING to CONCLUDED, gate
> the .prepare() and .commit() phases behind an explicit acknowledgement
> provided by the QMP monitor if manual completion mode has been requested.
> 
> This allows us to perform graph changes in prepare and/or commit so that
> graph changes do not occur autonomously without knowledge of the
> controlling management layer.
> 
> Transactions that have reached the "PENDING" state together can all be
> moved to invoke their finalization methods by issuing block_job_finalize
> to any one job in the transaction.
> 
> Jobs in a transaction with mixed job->manual settings will remain stuck
> in the "WAITING" state until block_job_finalize is authored on the job(s)
> that have reached the "PENDING" state.

Why? Removing this inconsistency would make the code slightly simpler.

Is it because you want to avoid that the user picks an automatic job for
completing the mixed transaction?

> These jobs are not allowed to progress because other jobs in the
> transaction may still fail during their preparation phase during
> finalization, so these jobs must remain in the WAITING phase until
> success is guaranteed. These jobs will then automatically dismiss
> themselves, but jobs that had the manual property set will remain
> at CONCLUDED as normal.
> 
> Signed-off-by: John Snow <jsnow@redhat.com>
> ---
>  block/trace-events       |  1 +
>  blockdev.c               | 14 ++++++++++
>  blockjob.c               | 69 +++++++++++++++++++++++++++++++++++++-----------
>  include/block/blockjob.h | 17 ++++++++++++
>  qapi/block-core.json     | 23 +++++++++++++++-
>  5 files changed, 108 insertions(+), 16 deletions(-)
> 
> diff --git a/block/trace-events b/block/trace-events
> index 5e531e0310..a81b66ff36 100644
> --- a/block/trace-events
> +++ b/block/trace-events
> @@ -51,6 +51,7 @@ qmp_block_job_cancel(void *job) "job %p"
>  qmp_block_job_pause(void *job) "job %p"
>  qmp_block_job_resume(void *job) "job %p"
>  qmp_block_job_complete(void *job) "job %p"
> +qmp_block_job_finalize(void *job) "job %p"
>  qmp_block_job_dismiss(void *job) "job %p"
>  qmp_block_stream(void *bs, void *job) "bs %p job %p"
>  
> diff --git a/blockdev.c b/blockdev.c
> index 3180130782..05fd421cdc 100644
> --- a/blockdev.c
> +++ b/blockdev.c
> @@ -3852,6 +3852,20 @@ void qmp_block_job_complete(const char *device, Error **errp)
>      aio_context_release(aio_context);
>  }
>  
> +void qmp_block_job_finalize(const char *id, Error **errp)
> +{
> +    AioContext *aio_context;
> +    BlockJob *job = find_block_job(id, &aio_context, errp);
> +
> +    if (!job) {
> +        return;
> +    }
> +
> +    trace_qmp_block_job_finalize(job);
> +    block_job_finalize(job, errp);
> +    aio_context_release(aio_context);
> +}
> +
>  void qmp_block_job_dismiss(const char *id, Error **errp)
>  {
>      AioContext *aio_context;
> diff --git a/blockjob.c b/blockjob.c
> index 23b4b99fd4..f9e8a64261 100644
> --- a/blockjob.c
> +++ b/blockjob.c
> @@ -65,14 +65,15 @@ bool BlockJobVerbTable[BLOCK_JOB_VERB__MAX][BLOCK_JOB_STATUS__MAX] = {
>      [BLOCK_JOB_VERB_RESUME]               = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
>      [BLOCK_JOB_VERB_SET_SPEED]            = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
>      [BLOCK_JOB_VERB_COMPLETE]             = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
> +    [BLOCK_JOB_VERB_FINALIZE]             = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
>      [BLOCK_JOB_VERB_DISMISS]              = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
>  };
>  
> -static void block_job_state_transition(BlockJob *job, BlockJobStatus s1)
> +static bool block_job_state_transition(BlockJob *job, BlockJobStatus s1)
>  {
>      BlockJobStatus s0 = job->status;
>      if (s0 == s1) {
> -        return;
> +        return false;
>      }
>      assert(s1 >= 0 && s1 <= BLOCK_JOB_STATUS__MAX);
>      trace_block_job_state_transition(job, job->ret, BlockJobSTT[s0][s1] ?
> @@ -83,6 +84,7 @@ static void block_job_state_transition(BlockJob *job, BlockJobStatus s1)
>                                                        s1));
>      assert(BlockJobSTT[s0][s1]);
>      job->status = s1;
> +    return true;
>  }
>  
>  static int block_job_apply_verb(BlockJob *job, BlockJobVerb bv, Error **errp)
> @@ -432,7 +434,7 @@ static void block_job_clean(BlockJob *job)
>      }
>  }
>  
> -static int block_job_completed_single(BlockJob *job)
> +static int block_job_finalize_single(BlockJob *job)
>  {
>      assert(job->completed);
>  
> @@ -581,18 +583,44 @@ static void block_job_completed_txn_abort(BlockJob *job)
>              assert(other_job->cancelled);
>              block_job_finish_sync(other_job, NULL, NULL);
>          }
> -        block_job_completed_single(other_job);
> +        block_job_finalize_single(other_job);
>          aio_context_release(ctx);
>      }
>  
>      block_job_txn_unref(txn);
>  }
>  
> +static int block_job_is_manual(BlockJob *job)
> +{
> +    return job->manual;
> +}
> +
> +static void block_job_do_finalize(BlockJob *job)
> +{
> +    int rc;
> +    assert(job && job->txn);
> +
> +    /* For jobs set !job->manual, transition to pending synchronously now */
> +    block_job_txn_apply(job->txn, block_job_event_pending, false);
> +
> +    /* prepare the transaction to complete */
> +    rc = block_job_txn_apply(job->txn, block_job_prepare, true);
> +    if (rc) {
> +        block_job_completed_txn_abort(job);
> +    } else {
> +        block_job_txn_apply(job->txn, block_job_finalize_single, true);
> +    }
> +}
> +
> +static int block_job_pending_conditional(BlockJob *job)
> +{
> +    return job->manual ? block_job_event_pending(job) : 0;
> +}

As I said above, I'd get rid of this function altogether, but if not,
can we have something like this:

    if (job->manual) {
        ret = block_job_event_pending()
        assert(ret == 0);
    }
    return 0;

Because if block_job_event_pending() ever started returning errors, the
abort-on-error semantics of block_job_txn_apply() would break this
function.

>  static void block_job_completed_txn_success(BlockJob *job)
>  {
>      BlockJobTxn *txn = job->txn;
>      BlockJob *other_job;
> -    int rc = 0;
>  
>      block_job_state_transition(job, BLOCK_JOB_STATUS_WAITING);
>  
> @@ -606,16 +634,15 @@ static void block_job_completed_txn_success(BlockJob *job)
>          }
>      }
>  
> -    /* Jobs may require some prep-work to complete without failure */
> -    rc = block_job_txn_apply(txn, block_job_prepare, true);
> -    if (rc) {
> -        block_job_completed_txn_abort(job);
> -        return;
> -    }
> +    /* For jobs with (job->manual), transition to the PENDING state.
> +     * jobs with !job->manual are left WAITING (on their pending comrades). */
> +    block_job_txn_apply(txn, block_job_pending_conditional, false);
>  
> -    /* We are the last completed job, commit the transaction. */
> -    block_job_txn_apply(txn, block_job_event_pending, false);
> -    block_job_txn_apply(txn, block_job_completed_single, true);
> +    /* Transactions with any manual jobs must await finalization.
> +     * do_finalize will handle lingering WAITING->PENDING transitions. */
> +    if (!block_job_txn_apply(txn, block_job_is_manual, false)) {
> +        block_job_do_finalize(job);
> +    }
>  }
>  
>  /* Assumes the block_job_mutex is held */
> @@ -667,6 +694,15 @@ void block_job_complete(BlockJob *job, Error **errp)
>      job->driver->complete(job, errp);
>  }
>  
> +void block_job_finalize(BlockJob *job, Error **errp)
> +{
> +    assert(job && job->id && job->txn);
> +    if (block_job_apply_verb(job, BLOCK_JOB_VERB_FINALIZE, errp)) {
> +        return;
> +    }
> +    block_job_do_finalize(job);
> +}
> +
>  void block_job_dismiss(BlockJob **jobptr, Error **errp)
>  {
>      BlockJob *job = *jobptr;
> @@ -826,7 +862,10 @@ static void block_job_event_completed(BlockJob *job, const char *msg)
>  
>  static int block_job_event_pending(BlockJob *job)
>  {
> -    block_job_state_transition(job, BLOCK_JOB_STATUS_PENDING);
> +    /* If we're already pending, don't re-announce */
> +    if (!block_job_state_transition(job, BLOCK_JOB_STATUS_PENDING)) {
> +        return 0;
> +    }

Without the exception for manual jobs, you wouldn't get double
transitions and could avoid adding a return value to
block_job_state_transition(), where we were considering dropping the
s0 == s1 case anyway.

Kevin

  parent reply	other threads:[~2018-02-28 18:16 UTC|newest]

Thread overview: 98+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-02-23 23:51 [Qemu-devel] [RFC v4 00/21] blockjobs: add explicit job management John Snow
2018-02-23 23:51 ` [Qemu-devel] [RFC v4 01/21] blockjobs: fix set-speed kick John Snow
2018-02-27 16:32   ` Eric Blake
2018-02-27 17:18   ` Kevin Wolf
2018-02-23 23:51 ` [Qemu-devel] [RFC v4 02/21] blockjobs: model single jobs as transactions John Snow
2018-02-27 16:36   ` Eric Blake
2018-02-27 17:18   ` Kevin Wolf
2018-02-23 23:51 ` [Qemu-devel] [RFC v4 03/21] blockjobs: add manual property John Snow
2018-02-27 16:39   ` Eric Blake
2018-02-27 17:18   ` Kevin Wolf
2018-02-23 23:51 ` [Qemu-devel] [RFC v4 04/21] blockjobs: add status enum John Snow
2018-02-27 16:44   ` Eric Blake
2018-02-27 17:19   ` Kevin Wolf
2018-02-23 23:51 ` [Qemu-devel] [RFC v4 05/21] blockjobs: add state transition table John Snow
2018-02-27 16:27   ` Kevin Wolf
2018-02-27 16:45     ` John Snow
2018-02-27 17:08       ` Kevin Wolf
2018-02-27 18:58         ` John Snow
2018-02-27 18:58   ` Eric Blake
2018-02-27 19:06     ` John Snow
2018-02-27 19:22     ` John Snow
2018-02-23 23:51 ` [Qemu-devel] [RFC v4 06/21] iotests: add pause_wait John Snow
2018-02-27 17:19   ` Kevin Wolf
2018-02-27 19:01   ` Eric Blake
2018-02-23 23:51 ` [Qemu-devel] [RFC v4 07/21] blockjobs: add block_job_verb permission table John Snow
2018-02-27 17:19   ` Kevin Wolf
2018-02-27 19:25   ` Eric Blake
2018-02-27 19:38     ` John Snow
2018-02-23 23:51 ` [Qemu-devel] [RFC v4 08/21] blockjobs: add ABORTING state John Snow
2018-02-27 19:34   ` Eric Blake
2018-02-28 14:54   ` Kevin Wolf
2018-02-28 19:26     ` John Snow
2018-02-23 23:51 ` [Qemu-devel] [RFC v4 09/21] blockjobs: add CONCLUDED state John Snow
2018-02-27 19:38   ` Eric Blake
2018-02-27 19:44     ` John Snow
2018-02-28 15:37   ` Kevin Wolf
2018-02-28 19:29     ` John Snow
2018-02-23 23:51 ` [Qemu-devel] [RFC v4 10/21] blockjobs: add NULL state John Snow
2018-02-27 19:41   ` Eric Blake
2018-02-28 15:42   ` Kevin Wolf
2018-02-28 20:04     ` John Snow
2018-02-23 23:51 ` [Qemu-devel] [RFC v4 11/21] blockjobs: add block_job_dismiss John Snow
2018-02-27 19:44   ` Eric Blake
2018-02-28 15:53   ` Kevin Wolf
2018-02-28 20:35     ` John Snow
2018-02-23 23:51 ` [Qemu-devel] [RFC v4 12/21] blockjobs: ensure abort is called for cancelled jobs John Snow
2018-02-27 19:49   ` Eric Blake
2018-02-27 20:43     ` John Snow
2018-02-28 16:05   ` Kevin Wolf
2018-02-23 23:51 ` [Qemu-devel] [RFC v4 13/21] blockjobs: add commit, abort, clean helpers John Snow
2018-02-27 19:50   ` Eric Blake
2018-02-28 16:07   ` Kevin Wolf
2018-02-23 23:51 ` [Qemu-devel] [RFC v4 14/21] blockjobs: add block_job_txn_apply function John Snow
2018-02-27 19:52   ` Eric Blake
2018-02-28 16:32   ` Kevin Wolf
2018-02-23 23:51 ` [Qemu-devel] [RFC v4 15/21] blockjobs: add prepare callback John Snow
2018-02-27 19:56   ` Eric Blake
2018-02-27 20:45     ` John Snow
2018-02-28 17:04   ` Kevin Wolf
2018-03-07  3:19     ` John Snow
2018-02-23 23:51 ` [Qemu-devel] [RFC v4 16/21] blockjobs: add waiting status John Snow
2018-02-27 20:00   ` Eric Blake
2018-02-27 20:50     ` John Snow
2018-02-28 17:46       ` Kevin Wolf
2018-02-23 23:51 ` [Qemu-devel] [RFC v4 17/21] blockjobs: add PENDING status and event John Snow
2018-02-27 20:05   ` Eric Blake
2018-02-27 20:54     ` John Snow
2018-02-28 17:55   ` Kevin Wolf
2018-02-23 23:51 ` [Qemu-devel] [RFC v4 18/21] blockjobs: add block-job-finalize John Snow
2018-02-27 20:13   ` Eric Blake
2018-02-28 18:15   ` Kevin Wolf [this message]
2018-02-28 19:14     ` John Snow
2018-03-01 10:01       ` Kevin Wolf
2018-03-01 19:24         ` John Snow
2018-02-23 23:51 ` [Qemu-devel] [RFC v4 19/21] blockjobs: Expose manual property John Snow
2018-02-27 20:16   ` Eric Blake
2018-02-27 20:42     ` John Snow
2018-02-27 21:57     ` John Snow
2018-02-27 22:27       ` Eric Blake
2018-02-28 18:23       ` Kevin Wolf
2018-02-28 19:19         ` John Snow
2018-02-28 18:25   ` Kevin Wolf
2018-02-28 19:20     ` John Snow
2018-02-28 19:27       ` [Qemu-devel] [Qemu-block] " Eric Blake
2018-02-23 23:51 ` [Qemu-devel] [RFC v4 20/21] iotests: test manual job dismissal John Snow
2018-02-27 20:21   ` Eric Blake
2018-02-27 20:41     ` John Snow
2018-02-23 23:51 ` [Qemu-devel] [RFC v4 21/21] blockjobs: add manual_mgmt option to transactions John Snow
2018-02-27 20:24   ` Eric Blake
2018-02-28 18:29     ` Kevin Wolf
2018-02-28 19:24       ` John Snow
2018-03-01 10:10         ` Kevin Wolf
2018-02-24  0:30 ` [Qemu-devel] [RFC v4 00/21] blockjobs: add explicit job management no-reply
2018-02-24 14:31 ` no-reply
2018-02-27 21:01   ` John Snow
2018-02-28 18:32     ` Kevin Wolf
2018-02-25 23:25 ` no-reply
2018-02-27 20:58   ` John Snow

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180228181553.GL4855@localhost.localdomain \
    --to=kwolf@redhat.com \
    --cc=jsnow@redhat.com \
    --cc=jtc@redhat.com \
    --cc=pkrempa@redhat.com \
    --cc=qemu-block@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).