2 * Blockjob transactions tests
4 * Copyright Red Hat, Inc. 2015
7 * Stefan Hajnoczi <stefanha@redhat.com>
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.
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"
22 unsigned int iterations
;
28 static int coroutine_fn
test_block_job_run(Job
*job
, Error
**errp
)
30 TestBlockJob
*s
= container_of(job
, TestBlockJob
, common
.job
);
32 while (s
->iterations
--) {
39 if (job_is_cancelled(job
)) {
52 static void test_block_job_cb(void *opaque
, int ret
)
54 TestBlockJobCBData
*data
= opaque
;
55 if (!ret
&& job_is_cancelled(&data
->job
->common
.job
)) {
62 static const BlockJobDriver test_block_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
,
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
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.
78 static BlockJob
*test_block_job_start(unsigned int iterations
,
80 int rc
, int *result
, JobTxn
*txn
)
84 TestBlockJobCBData
*data
;
85 static unsigned counter
;
88 data
= g_new0(TestBlockJobCBData
, 1);
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
);
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
;
105 data
->result
= result
;
109 static void test_single_job(int expected
)
113 int result
= -EINPROGRESS
;
116 job
= test_block_job_start(1, true, expected
, &result
, txn
);
117 job_start(&job
->job
);
119 WITH_JOB_LOCK_GUARD() {
120 if (expected
== -ECANCELED
) {
121 job_cancel_locked(&job
->job
, false);
125 while (result
== -EINPROGRESS
) {
126 aio_poll(qemu_get_aio_context(), true);
128 g_assert_cmpint(result
, ==, expected
);
133 static void test_single_job_success(void)
138 static void test_single_job_failure(void)
140 test_single_job(-EIO
);
143 static void test_single_job_cancel(void)
145 test_single_job(-ECANCELED
);
148 static void test_pair_jobs(int expected1
, int expected2
)
153 int result1
= -EINPROGRESS
;
154 int result2
= -EINPROGRESS
;
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
);
162 /* Release our reference now to trigger as many nice
163 * use-after-free bugs as possible.
165 WITH_JOB_LOCK_GUARD() {
166 job_txn_unref_locked(txn
);
168 if (expected1
== -ECANCELED
) {
169 job_cancel_locked(&job1
->job
, false);
171 if (expected2
== -ECANCELED
) {
172 job_cancel_locked(&job2
->job
, false);
176 while (result1
== -EINPROGRESS
|| result2
== -EINPROGRESS
) {
177 aio_poll(qemu_get_aio_context(), true);
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
;
187 g_assert_cmpint(result1
, ==, expected1
);
188 g_assert_cmpint(result2
, ==, expected2
);
191 static void test_pair_jobs_success(void)
193 test_pair_jobs(0, 0);
196 static void test_pair_jobs_failure(void)
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
202 test_pair_jobs(-EIO
, 0);
203 test_pair_jobs(0, -EIO
);
206 static void test_pair_jobs_cancel(void)
208 test_pair_jobs(-ECANCELED
, 0);
209 test_pair_jobs(0, -ECANCELED
);
212 static void test_pair_jobs_fail_cancel_race(void)
217 int result1
= -EINPROGRESS
;
218 int result2
= -EINPROGRESS
;
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
);
226 WITH_JOB_LOCK_GUARD() {
227 job_cancel_locked(&job1
->job
, false);
230 /* Now make job2 finish before the main loop kicks jobs. This simulates
231 * the race between a pending kick and another job completing.
233 job_enter(&job2
->job
);
234 job_enter(&job2
->job
);
236 while (result1
== -EINPROGRESS
|| result2
== -EINPROGRESS
) {
237 aio_poll(qemu_get_aio_context(), true);
240 g_assert_cmpint(result1
, ==, -ECANCELED
);
241 g_assert_cmpint(result2
, ==, -ECANCELED
);
246 int main(int argc
, char **argv
)
248 qemu_init_main_loop(&error_abort
);
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
);