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