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