1da668aa1SThomas Huth /* 2da668aa1SThomas Huth * Blockjob transactions tests 3da668aa1SThomas Huth * 4da668aa1SThomas Huth * Copyright Red Hat, Inc. 2015 5da668aa1SThomas Huth * 6da668aa1SThomas Huth * Authors: 7da668aa1SThomas Huth * Stefan Hajnoczi <stefanha@redhat.com> 8da668aa1SThomas Huth * 9da668aa1SThomas Huth * This work is licensed under the terms of the GNU LGPL, version 2 or later. 10da668aa1SThomas Huth * See the COPYING.LIB file in the top-level directory. 11da668aa1SThomas Huth */ 12da668aa1SThomas Huth 13da668aa1SThomas Huth #include "qemu/osdep.h" 14da668aa1SThomas Huth #include "qapi/error.h" 15da668aa1SThomas Huth #include "qemu/main-loop.h" 16da668aa1SThomas Huth #include "block/blockjob_int.h" 17da668aa1SThomas Huth #include "sysemu/block-backend.h" 18da668aa1SThomas Huth #include "qapi/qmp/qdict.h" 19da668aa1SThomas Huth 20da668aa1SThomas Huth typedef struct { 21da668aa1SThomas Huth BlockJob common; 22da668aa1SThomas Huth unsigned int iterations; 23da668aa1SThomas Huth bool use_timer; 24da668aa1SThomas Huth int rc; 25da668aa1SThomas Huth int *result; 26da668aa1SThomas Huth } TestBlockJob; 27da668aa1SThomas Huth 28da668aa1SThomas Huth static int coroutine_fn test_block_job_run(Job *job, Error **errp) 29da668aa1SThomas Huth { 30da668aa1SThomas Huth TestBlockJob *s = container_of(job, TestBlockJob, common.job); 31da668aa1SThomas Huth 32da668aa1SThomas Huth while (s->iterations--) { 33da668aa1SThomas Huth if (s->use_timer) { 34da668aa1SThomas Huth job_sleep_ns(job, 0); 35da668aa1SThomas Huth } else { 36da668aa1SThomas Huth job_yield(job); 37da668aa1SThomas Huth } 38da668aa1SThomas Huth 39da668aa1SThomas Huth if (job_is_cancelled(job)) { 40da668aa1SThomas Huth break; 41da668aa1SThomas Huth } 42da668aa1SThomas Huth } 43da668aa1SThomas Huth 44da668aa1SThomas Huth return s->rc; 45da668aa1SThomas Huth } 46da668aa1SThomas Huth 47da668aa1SThomas Huth typedef struct { 48da668aa1SThomas Huth TestBlockJob *job; 49da668aa1SThomas Huth int *result; 50da668aa1SThomas Huth } TestBlockJobCBData; 51da668aa1SThomas Huth 52da668aa1SThomas Huth static void test_block_job_cb(void *opaque, int ret) 53da668aa1SThomas Huth { 54da668aa1SThomas Huth TestBlockJobCBData *data = opaque; 55da668aa1SThomas Huth if (!ret && job_is_cancelled(&data->job->common.job)) { 56da668aa1SThomas Huth ret = -ECANCELED; 57da668aa1SThomas Huth } 58da668aa1SThomas Huth *data->result = ret; 59da668aa1SThomas Huth g_free(data); 60da668aa1SThomas Huth } 61da668aa1SThomas Huth 62da668aa1SThomas Huth static const BlockJobDriver test_block_job_driver = { 63da668aa1SThomas Huth .job_driver = { 64da668aa1SThomas Huth .instance_size = sizeof(TestBlockJob), 65da668aa1SThomas Huth .free = block_job_free, 66da668aa1SThomas Huth .user_resume = block_job_user_resume, 67da668aa1SThomas Huth .run = test_block_job_run, 68da668aa1SThomas Huth }, 69da668aa1SThomas Huth }; 70da668aa1SThomas Huth 71da668aa1SThomas Huth /* Create a block job that completes with a given return code after a given 72da668aa1SThomas Huth * number of event loop iterations. The return code is stored in the given 73da668aa1SThomas Huth * result pointer. 74da668aa1SThomas Huth * 75da668aa1SThomas Huth * The event loop iterations can either be handled automatically with a 0 delay 76da668aa1SThomas Huth * timer, or they can be stepped manually by entering the coroutine. 77da668aa1SThomas Huth */ 78da668aa1SThomas Huth static BlockJob *test_block_job_start(unsigned int iterations, 79da668aa1SThomas Huth bool use_timer, 80da668aa1SThomas Huth int rc, int *result, JobTxn *txn) 81da668aa1SThomas Huth { 82da668aa1SThomas Huth BlockDriverState *bs; 83da668aa1SThomas Huth TestBlockJob *s; 84da668aa1SThomas Huth TestBlockJobCBData *data; 85da668aa1SThomas Huth static unsigned counter; 86da668aa1SThomas Huth char job_id[24]; 87da668aa1SThomas Huth 88da668aa1SThomas Huth data = g_new0(TestBlockJobCBData, 1); 89da668aa1SThomas Huth 90da668aa1SThomas Huth QDict *opt = qdict_new(); 91da668aa1SThomas Huth qdict_put_str(opt, "file.read-zeroes", "on"); 92da668aa1SThomas Huth bs = bdrv_open("null-co://", NULL, opt, 0, &error_abort); 93da668aa1SThomas Huth g_assert_nonnull(bs); 94da668aa1SThomas Huth 95da668aa1SThomas Huth snprintf(job_id, sizeof(job_id), "job%u", counter++); 96da668aa1SThomas Huth s = block_job_create(job_id, &test_block_job_driver, txn, bs, 97da668aa1SThomas Huth 0, BLK_PERM_ALL, 0, JOB_DEFAULT, 98da668aa1SThomas Huth test_block_job_cb, data, &error_abort); 99*7ac68e29SVladimir Sementsov-Ogievskiy bdrv_unref(bs); /* referenced by job now */ 100da668aa1SThomas Huth s->iterations = iterations; 101da668aa1SThomas Huth s->use_timer = use_timer; 102da668aa1SThomas Huth s->rc = rc; 103da668aa1SThomas Huth s->result = result; 104da668aa1SThomas Huth data->job = s; 105da668aa1SThomas Huth data->result = result; 106da668aa1SThomas Huth return &s->common; 107da668aa1SThomas Huth } 108da668aa1SThomas Huth 109da668aa1SThomas Huth static void test_single_job(int expected) 110da668aa1SThomas Huth { 111da668aa1SThomas Huth BlockJob *job; 112da668aa1SThomas Huth JobTxn *txn; 113da668aa1SThomas Huth int result = -EINPROGRESS; 114da668aa1SThomas Huth 115da668aa1SThomas Huth txn = job_txn_new(); 116da668aa1SThomas Huth job = test_block_job_start(1, true, expected, &result, txn); 117da668aa1SThomas Huth job_start(&job->job); 118da668aa1SThomas Huth 119da668aa1SThomas Huth if (expected == -ECANCELED) { 120da668aa1SThomas Huth job_cancel(&job->job, false); 121da668aa1SThomas Huth } 122da668aa1SThomas Huth 123da668aa1SThomas Huth while (result == -EINPROGRESS) { 124da668aa1SThomas Huth aio_poll(qemu_get_aio_context(), true); 125da668aa1SThomas Huth } 126da668aa1SThomas Huth g_assert_cmpint(result, ==, expected); 127da668aa1SThomas Huth 128da668aa1SThomas Huth job_txn_unref(txn); 129da668aa1SThomas Huth } 130da668aa1SThomas Huth 131da668aa1SThomas Huth static void test_single_job_success(void) 132da668aa1SThomas Huth { 133da668aa1SThomas Huth test_single_job(0); 134da668aa1SThomas Huth } 135da668aa1SThomas Huth 136da668aa1SThomas Huth static void test_single_job_failure(void) 137da668aa1SThomas Huth { 138da668aa1SThomas Huth test_single_job(-EIO); 139da668aa1SThomas Huth } 140da668aa1SThomas Huth 141da668aa1SThomas Huth static void test_single_job_cancel(void) 142da668aa1SThomas Huth { 143da668aa1SThomas Huth test_single_job(-ECANCELED); 144da668aa1SThomas Huth } 145da668aa1SThomas Huth 146da668aa1SThomas Huth static void test_pair_jobs(int expected1, int expected2) 147da668aa1SThomas Huth { 148da668aa1SThomas Huth BlockJob *job1; 149da668aa1SThomas Huth BlockJob *job2; 150da668aa1SThomas Huth JobTxn *txn; 151da668aa1SThomas Huth int result1 = -EINPROGRESS; 152da668aa1SThomas Huth int result2 = -EINPROGRESS; 153da668aa1SThomas Huth 154da668aa1SThomas Huth txn = job_txn_new(); 155da668aa1SThomas Huth job1 = test_block_job_start(1, true, expected1, &result1, txn); 156da668aa1SThomas Huth job2 = test_block_job_start(2, true, expected2, &result2, txn); 157da668aa1SThomas Huth job_start(&job1->job); 158da668aa1SThomas Huth job_start(&job2->job); 159da668aa1SThomas Huth 160da668aa1SThomas Huth /* Release our reference now to trigger as many nice 161da668aa1SThomas Huth * use-after-free bugs as possible. 162da668aa1SThomas Huth */ 163da668aa1SThomas Huth job_txn_unref(txn); 164da668aa1SThomas Huth 165da668aa1SThomas Huth if (expected1 == -ECANCELED) { 166da668aa1SThomas Huth job_cancel(&job1->job, false); 167da668aa1SThomas Huth } 168da668aa1SThomas Huth if (expected2 == -ECANCELED) { 169da668aa1SThomas Huth job_cancel(&job2->job, false); 170da668aa1SThomas Huth } 171da668aa1SThomas Huth 172da668aa1SThomas Huth while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) { 173da668aa1SThomas Huth aio_poll(qemu_get_aio_context(), true); 174da668aa1SThomas Huth } 175da668aa1SThomas Huth 176da668aa1SThomas Huth /* Failure or cancellation of one job cancels the other job */ 177da668aa1SThomas Huth if (expected1 != 0) { 178da668aa1SThomas Huth expected2 = -ECANCELED; 179da668aa1SThomas Huth } else if (expected2 != 0) { 180da668aa1SThomas Huth expected1 = -ECANCELED; 181da668aa1SThomas Huth } 182da668aa1SThomas Huth 183da668aa1SThomas Huth g_assert_cmpint(result1, ==, expected1); 184da668aa1SThomas Huth g_assert_cmpint(result2, ==, expected2); 185da668aa1SThomas Huth } 186da668aa1SThomas Huth 187da668aa1SThomas Huth static void test_pair_jobs_success(void) 188da668aa1SThomas Huth { 189da668aa1SThomas Huth test_pair_jobs(0, 0); 190da668aa1SThomas Huth } 191da668aa1SThomas Huth 192da668aa1SThomas Huth static void test_pair_jobs_failure(void) 193da668aa1SThomas Huth { 194da668aa1SThomas Huth /* Test both orderings. The two jobs run for a different number of 195da668aa1SThomas Huth * iterations so the code path is different depending on which job fails 196da668aa1SThomas Huth * first. 197da668aa1SThomas Huth */ 198da668aa1SThomas Huth test_pair_jobs(-EIO, 0); 199da668aa1SThomas Huth test_pair_jobs(0, -EIO); 200da668aa1SThomas Huth } 201da668aa1SThomas Huth 202da668aa1SThomas Huth static void test_pair_jobs_cancel(void) 203da668aa1SThomas Huth { 204da668aa1SThomas Huth test_pair_jobs(-ECANCELED, 0); 205da668aa1SThomas Huth test_pair_jobs(0, -ECANCELED); 206da668aa1SThomas Huth } 207da668aa1SThomas Huth 208da668aa1SThomas Huth static void test_pair_jobs_fail_cancel_race(void) 209da668aa1SThomas Huth { 210da668aa1SThomas Huth BlockJob *job1; 211da668aa1SThomas Huth BlockJob *job2; 212da668aa1SThomas Huth JobTxn *txn; 213da668aa1SThomas Huth int result1 = -EINPROGRESS; 214da668aa1SThomas Huth int result2 = -EINPROGRESS; 215da668aa1SThomas Huth 216da668aa1SThomas Huth txn = job_txn_new(); 217da668aa1SThomas Huth job1 = test_block_job_start(1, true, -ECANCELED, &result1, txn); 218da668aa1SThomas Huth job2 = test_block_job_start(2, false, 0, &result2, txn); 219da668aa1SThomas Huth job_start(&job1->job); 220da668aa1SThomas Huth job_start(&job2->job); 221da668aa1SThomas Huth 222da668aa1SThomas Huth job_cancel(&job1->job, false); 223da668aa1SThomas Huth 224da668aa1SThomas Huth /* Now make job2 finish before the main loop kicks jobs. This simulates 225da668aa1SThomas Huth * the race between a pending kick and another job completing. 226da668aa1SThomas Huth */ 227da668aa1SThomas Huth job_enter(&job2->job); 228da668aa1SThomas Huth job_enter(&job2->job); 229da668aa1SThomas Huth 230da668aa1SThomas Huth while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) { 231da668aa1SThomas Huth aio_poll(qemu_get_aio_context(), true); 232da668aa1SThomas Huth } 233da668aa1SThomas Huth 234da668aa1SThomas Huth g_assert_cmpint(result1, ==, -ECANCELED); 235da668aa1SThomas Huth g_assert_cmpint(result2, ==, -ECANCELED); 236da668aa1SThomas Huth 237da668aa1SThomas Huth job_txn_unref(txn); 238da668aa1SThomas Huth } 239da668aa1SThomas Huth 240da668aa1SThomas Huth int main(int argc, char **argv) 241da668aa1SThomas Huth { 242da668aa1SThomas Huth qemu_init_main_loop(&error_abort); 243da668aa1SThomas Huth bdrv_init(); 244da668aa1SThomas Huth 245da668aa1SThomas Huth g_test_init(&argc, &argv, NULL); 246da668aa1SThomas Huth g_test_add_func("/single/success", test_single_job_success); 247da668aa1SThomas Huth g_test_add_func("/single/failure", test_single_job_failure); 248da668aa1SThomas Huth g_test_add_func("/single/cancel", test_single_job_cancel); 249da668aa1SThomas Huth g_test_add_func("/pair/success", test_pair_jobs_success); 250da668aa1SThomas Huth g_test_add_func("/pair/failure", test_pair_jobs_failure); 251da668aa1SThomas Huth g_test_add_func("/pair/cancel", test_pair_jobs_cancel); 252da668aa1SThomas Huth g_test_add_func("/pair/fail-cancel-race", test_pair_jobs_fail_cancel_race); 253da668aa1SThomas Huth return g_test_run(); 254da668aa1SThomas Huth } 255