All of lore.kernel.org
 help / color / mirror / Atom feed
From: John Snow <jsnow@redhat.com>
To: Fam Zheng <famz@redhat.com>, qemu-devel@nongnu.org
Cc: Kevin Wolf <kwolf@redhat.com>, Jeff Cody <jcody@redhat.com>,
	vsementsov@parallels.com, stefanha@redhat.com,
	Max Reitz <mreitz@redhat.com>
Subject: Re: [Qemu-devel] [PATCH v7 14/14] tests: add BlockJobTxn unit test
Date: Tue, 22 Sep 2015 19:33:22 -0400	[thread overview]
Message-ID: <5601E542.60105@redhat.com> (raw)
In-Reply-To: <1442889976-8733-15-git-send-email-famz@redhat.com>



On 09/21/2015 10:46 PM, Fam Zheng wrote:
> From: Stefan Hajnoczi <stefanha@redhat.com>
> 
> The BlockJobTxn unit test verifies that both single jobs and pairs of
> jobs behave as a transaction group.  Either all jobs complete
> successfully or the group is cancelled.
> 
> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
> Signed-off-by: Fam Zheng <famz@redhat.com>
> Reviewed-by: Max Reitz <mreitz@redhat.com>
> ---
>  tests/Makefile            |   3 +
>  tests/test-blockjob-txn.c | 244 ++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 247 insertions(+)
>  create mode 100644 tests/test-blockjob-txn.c
> 
> diff --git a/tests/Makefile b/tests/Makefile
> index 4063639..e887a61 100644
> --- a/tests/Makefile
> +++ b/tests/Makefile
> @@ -47,6 +47,8 @@ check-unit-y += tests/test-thread-pool$(EXESUF)
>  gcov-files-test-thread-pool-y = thread-pool.c
>  gcov-files-test-hbitmap-y = util/hbitmap.c
>  check-unit-y += tests/test-hbitmap$(EXESUF)
> +gcov-files-test-hbitmap-y = blockjob.c
> +check-unit-y += tests/test-blockjob-txn$(EXESUF)
>  check-unit-y += tests/test-x86-cpuid$(EXESUF)
>  # all code tested by test-x86-cpuid is inside topology.h
>  gcov-files-test-x86-cpuid-y =
> @@ -307,6 +309,7 @@ tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
>  tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y)
>  tests/test-rfifolock$(EXESUF): tests/test-rfifolock.o $(test-util-obj-y)
>  tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y)
> +tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y)
>  tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y)
>  tests/test-iov$(EXESUF): tests/test-iov.o $(test-util-obj-y)
>  tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o $(test-util-obj-y)
> diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
> new file mode 100644
> index 0000000..000f70e
> --- /dev/null
> +++ b/tests/test-blockjob-txn.c
> @@ -0,0 +1,244 @@
> +/*
> + * Blockjob transactions tests
> + *
> + * Copyright Red Hat, Inc. 2015
> + *
> + * Authors:
> + *  Stefan Hajnoczi    <stefanha@redhat.com>
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + */
> +
> +#include <glib.h>
> +#include "qapi/error.h"
> +#include "qemu/main-loop.h"
> +#include "block/blockjob.h"
> +
> +typedef struct {
> +    BlockJob common;
> +    unsigned int iterations;
> +    bool use_timer;
> +    int rc;
> +    int *result;
> +} TestBlockJob;
> +
> +static const BlockJobDriver test_block_job_driver = {
> +    .instance_size = sizeof(TestBlockJob),
> +};
> +
> +static void test_block_job_complete(BlockJob *job, void *opaque)
> +{
> +    BlockDriverState *bs = job->bs;
> +    int rc = (intptr_t)opaque;
> +
> +    if (block_job_is_cancelled(job)) {
> +        rc = -ECANCELED;
> +    }
> +
> +    block_job_completed(job, rc);
> +    bdrv_unref(bs);
> +}
> +
> +static void coroutine_fn test_block_job_run(void *opaque)
> +{
> +    TestBlockJob *s = opaque;
> +    BlockJob *job = &s->common;
> +
> +    while (s->iterations--) {
> +        if (s->use_timer) {
> +            block_job_sleep_ns(job, QEMU_CLOCK_REALTIME, 0);
> +        } else {
> +            block_job_yield(job);
> +        }
> +
> +        if (block_job_is_cancelled(job)) {
> +            break;
> +        }
> +    }
> +
> +    block_job_defer_to_main_loop(job, test_block_job_complete,
> +                                 (void *)(intptr_t)s->rc);
> +}
> +
> +typedef struct {
> +    TestBlockJob *job;
> +    int *result;
> +} TestBlockJobCBData;
> +
> +static void test_block_job_cb(void *opaque, int ret)
> +{
> +    TestBlockJobCBData *data = opaque;
> +    if (!ret && block_job_is_cancelled(&data->job->common)) {
> +        ret = -ECANCELED;
> +    }
> +    *data->result = ret;
> +    g_free(data);
> +}
> +
> +/* Create a block job that completes with a given return code after a given
> + * number of event loop iterations.  The return code is stored in the given
> + * result pointer.
> + *
> + * The event loop iterations can either be handled automatically with a 0 delay
> + * timer, or they can be stepped manually by entering the coroutine.
> + */
> +static BlockJob *test_block_job_start(unsigned int iterations,
> +                                      bool use_timer,
> +                                      int rc, int *result)
> +{
> +    BlockDriverState *bs;
> +    TestBlockJob *s;
> +    TestBlockJobCBData *data;
> +
> +    data = g_new0(TestBlockJobCBData, 1);
> +    bs = bdrv_new();
> +    s = block_job_create(&test_block_job_driver, bs, 0, test_block_job_cb,
> +                         data, &error_abort);
> +    s->iterations = iterations;
> +    s->use_timer = use_timer;
> +    s->rc = rc;
> +    s->result = result;
> +    s->common.co = qemu_coroutine_create(test_block_job_run);
> +    data->job = s;
> +    data->result = result;
> +    qemu_coroutine_enter(s->common.co, s);
> +    return &s->common;
> +}
> +
> +static void test_single_job(int expected)
> +{
> +    BlockJob *job;
> +    BlockJobTxn *txn;
> +    int result = -EINPROGRESS;
> +
> +    txn = block_job_txn_new();
> +    job = test_block_job_start(1, true, expected, &result);
> +    block_job_txn_add_job(txn, job);
> +
> +    if (expected == -ECANCELED) {
> +        block_job_cancel(job);
> +    }
> +
> +    while (result == -EINPROGRESS) {
> +        aio_poll(qemu_get_aio_context(), true);
> +    }
> +    g_assert_cmpint(result, ==, expected);
> +}
> +
> +static void test_single_job_success(void)
> +{
> +    test_single_job(0);
> +}
> +
> +static void test_single_job_failure(void)
> +{
> +    test_single_job(-EIO);
> +}
> +
> +static void test_single_job_cancel(void)
> +{
> +    test_single_job(-ECANCELED);
> +}
> +
> +static void test_pair_jobs(int expected1, int expected2)
> +{
> +    BlockJob *job1;
> +    BlockJob *job2;
> +    BlockJobTxn *txn;
> +    int result1 = -EINPROGRESS;
> +    int result2 = -EINPROGRESS;
> +
> +    txn = block_job_txn_new();
> +    job1 = test_block_job_start(1, true, expected1, &result1);
> +    block_job_txn_add_job(txn, job1);
> +    job2 = test_block_job_start(2, true, expected2, &result2);
> +    block_job_txn_add_job(txn, job2);
> +
> +    if (expected1 == -ECANCELED) {
> +        block_job_cancel(job1);
> +    }
> +    if (expected2 == -ECANCELED) {
> +        block_job_cancel(job2);
> +    }
> +
> +    while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) {
> +        aio_poll(qemu_get_aio_context(), true);
> +    }
> +
> +    /* Failure or cancellation of one job cancels the other job */
> +    if (expected1 != 0) {
> +        expected2 = -ECANCELED;
> +    } else if (expected2 != 0) {
> +        expected1 = -ECANCELED;
> +    }
> +
> +    g_assert_cmpint(result1, ==, expected1);
> +    g_assert_cmpint(result2, ==, expected2);
> +}
> +
> +static void test_pair_jobs_success(void)
> +{
> +    test_pair_jobs(0, 0);
> +}
> +
> +static void test_pair_jobs_failure(void)
> +{
> +    /* Test both orderings.  The two jobs run for a different number of
> +     * iterations so the code path is different depending on which job fails
> +     * first.
> +     */
> +    test_pair_jobs(-EIO, 0);
> +    test_pair_jobs(0, -EIO);
> +}
> +
> +static void test_pair_jobs_cancel(void)
> +{
> +    test_pair_jobs(-ECANCELED, 0);
> +    test_pair_jobs(0, -ECANCELED);
> +}
> +
> +static void test_pair_jobs_fail_cancel_race(void)
> +{
> +    BlockJob *job1;
> +    BlockJob *job2;
> +    BlockJobTxn *txn;
> +    int result1 = -EINPROGRESS;
> +    int result2 = -EINPROGRESS;
> +
> +    txn = block_job_txn_new();
> +    job1 = test_block_job_start(1, true, -ECANCELED, &result1);
> +    block_job_txn_add_job(txn, job1);
> +    job2 = test_block_job_start(2, false, 0, &result2);
> +    block_job_txn_add_job(txn, job2);
> +
> +    block_job_cancel(job1);
> +
> +    /* Now make job2 finish before the main loop kicks jobs.  This simulates
> +     * the race between a pending kick and another job completing.
> +     */
> +    block_job_enter(job2);
> +    block_job_enter(job2);
> +
> +    while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) {
> +        aio_poll(qemu_get_aio_context(), true);
> +    }
> +
> +    g_assert_cmpint(result1, ==, -ECANCELED);
> +    g_assert_cmpint(result2, ==, -ECANCELED);
> +}
> +
> +int main(int argc, char **argv)
> +{
> +    qemu_init_main_loop(&error_abort);
> +
> +    g_test_init(&argc, &argv, NULL);
> +    g_test_add_func("/single/success", test_single_job_success);
> +    g_test_add_func("/single/failure", test_single_job_failure);
> +    g_test_add_func("/single/cancel", test_single_job_cancel);
> +    g_test_add_func("/pair/success", test_pair_jobs_success);
> +    g_test_add_func("/pair/failure", test_pair_jobs_failure);
> +    g_test_add_func("/pair/cancel", test_pair_jobs_cancel);
> +    g_test_add_func("/pair/fail-cancel-race", test_pair_jobs_fail_cancel_race);
> +    return g_test_run();
> +}
> 

Reviewed-by: John Snow <jsnow@redhat.com>

  reply	other threads:[~2015-09-22 23:33 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-09-22  2:46 [Qemu-devel] [PATCH v7 00/14] block: incremental backup transactions using BlockJobTxn Fam Zheng
2015-09-22  2:46 ` [Qemu-devel] [PATCH v7 01/14] qapi: Add transaction support to block-dirty-bitmap operations Fam Zheng
2015-09-22  2:46 ` [Qemu-devel] [PATCH v7 02/14] iotests: add transactional incremental backup test Fam Zheng
2015-09-22  2:46 ` [Qemu-devel] [PATCH v7 03/14] block: rename BlkTransactionState and BdrvActionOps Fam Zheng
2015-09-22  2:46 ` [Qemu-devel] [PATCH v7 04/14] backup: Extract dirty bitmap handling as a separate function Fam Zheng
2015-09-22  2:46 ` [Qemu-devel] [PATCH v7 05/14] blockjob: Introduce reference count Fam Zheng
2015-09-22  2:46 ` [Qemu-devel] [PATCH v7 06/14] blockjob: Add .commit and .abort block job actions Fam Zheng
2015-09-22  2:46 ` [Qemu-devel] [PATCH v7 07/14] blockjob: Add "completed" and "ret" in BlockJob Fam Zheng
2015-09-22  2:46 ` [Qemu-devel] [PATCH v7 08/14] blockjob: Simplify block_job_finish_sync Fam Zheng
2015-09-22  2:46 ` [Qemu-devel] [PATCH v7 09/14] block: Add block job transactions Fam Zheng
2015-09-22 19:09   ` John Snow
2015-09-24  8:29     ` Fam Zheng
2015-09-22  2:46 ` [Qemu-devel] [PATCH v7 10/14] blockdev: make BlockJobTxn available to qmp 'transaction' Fam Zheng
2015-09-22 19:13   ` John Snow
2015-09-22  2:46 ` [Qemu-devel] [PATCH v7 11/14] block/backup: support block job transactions Fam Zheng
2015-09-22 16:03   ` Eric Blake
2015-09-22 21:08   ` John Snow
2015-09-22 22:34     ` Eric Blake
2015-09-22 23:27       ` John Snow
2015-09-23 11:09         ` Markus Armbruster
2015-09-23 16:14           ` John Snow
2015-09-24  6:34             ` Markus Armbruster
2015-09-25 16:05               ` John Snow
2015-09-22  2:46 ` [Qemu-devel] [PATCH v7 12/14] iotests: 124 - transactional failure test Fam Zheng
2015-09-22  2:46 ` [Qemu-devel] [PATCH v7 13/14] qmp-commands.hx: Update the supported 'transaction' operations Fam Zheng
2015-09-30 18:56   ` John Snow
2015-10-01 10:57     ` Kashyap Chamarthy
2015-09-22  2:46 ` [Qemu-devel] [PATCH v7 14/14] tests: add BlockJobTxn unit test Fam Zheng
2015-09-22 23:33   ` John Snow [this message]
2015-09-22 20:48 ` [Qemu-devel] [PATCH v7 00/14] block: incremental backup transactions using BlockJobTxn 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=5601E542.60105@redhat.com \
    --to=jsnow@redhat.com \
    --cc=famz@redhat.com \
    --cc=jcody@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=mreitz@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=stefanha@redhat.com \
    --cc=vsementsov@parallels.com \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.