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
test_block_job_run(Job * job,Error ** errp)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
test_block_job_cb(void * opaque,int ret)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 */
test_block_job_start(unsigned int iterations,bool use_timer,int rc,int * result,JobTxn * txn)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);
997ac68e29SVladimir 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
test_single_job(int expected)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
119191e7af3SEmanuele Giuseppe Esposito WITH_JOB_LOCK_GUARD() {
120da668aa1SThomas Huth if (expected == -ECANCELED) {
121191e7af3SEmanuele Giuseppe Esposito job_cancel_locked(&job->job, false);
122191e7af3SEmanuele Giuseppe Esposito }
123da668aa1SThomas Huth }
124da668aa1SThomas Huth
125da668aa1SThomas Huth while (result == -EINPROGRESS) {
126da668aa1SThomas Huth aio_poll(qemu_get_aio_context(), true);
127da668aa1SThomas Huth }
128da668aa1SThomas Huth g_assert_cmpint(result, ==, expected);
129da668aa1SThomas Huth
130da668aa1SThomas Huth job_txn_unref(txn);
131da668aa1SThomas Huth }
132da668aa1SThomas Huth
test_single_job_success(void)133da668aa1SThomas Huth static void test_single_job_success(void)
134da668aa1SThomas Huth {
135da668aa1SThomas Huth test_single_job(0);
136da668aa1SThomas Huth }
137da668aa1SThomas Huth
test_single_job_failure(void)138da668aa1SThomas Huth static void test_single_job_failure(void)
139da668aa1SThomas Huth {
140da668aa1SThomas Huth test_single_job(-EIO);
141da668aa1SThomas Huth }
142da668aa1SThomas Huth
test_single_job_cancel(void)143da668aa1SThomas Huth static void test_single_job_cancel(void)
144da668aa1SThomas Huth {
145da668aa1SThomas Huth test_single_job(-ECANCELED);
146da668aa1SThomas Huth }
147da668aa1SThomas Huth
test_pair_jobs(int expected1,int expected2)148da668aa1SThomas Huth static void test_pair_jobs(int expected1, int expected2)
149da668aa1SThomas Huth {
150da668aa1SThomas Huth BlockJob *job1;
151da668aa1SThomas Huth BlockJob *job2;
152da668aa1SThomas Huth JobTxn *txn;
153da668aa1SThomas Huth int result1 = -EINPROGRESS;
154da668aa1SThomas Huth int result2 = -EINPROGRESS;
155da668aa1SThomas Huth
156da668aa1SThomas Huth txn = job_txn_new();
157da668aa1SThomas Huth job1 = test_block_job_start(1, true, expected1, &result1, txn);
158da668aa1SThomas Huth job2 = test_block_job_start(2, true, expected2, &result2, txn);
159da668aa1SThomas Huth job_start(&job1->job);
160da668aa1SThomas Huth job_start(&job2->job);
161da668aa1SThomas Huth
162da668aa1SThomas Huth /* Release our reference now to trigger as many nice
163da668aa1SThomas Huth * use-after-free bugs as possible.
164da668aa1SThomas Huth */
165191e7af3SEmanuele Giuseppe Esposito WITH_JOB_LOCK_GUARD() {
166191e7af3SEmanuele Giuseppe Esposito job_txn_unref_locked(txn);
167da668aa1SThomas Huth
168da668aa1SThomas Huth if (expected1 == -ECANCELED) {
169191e7af3SEmanuele Giuseppe Esposito job_cancel_locked(&job1->job, false);
170da668aa1SThomas Huth }
171da668aa1SThomas Huth if (expected2 == -ECANCELED) {
172191e7af3SEmanuele Giuseppe Esposito job_cancel_locked(&job2->job, false);
173191e7af3SEmanuele Giuseppe Esposito }
174da668aa1SThomas Huth }
175da668aa1SThomas Huth
176da668aa1SThomas Huth while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) {
177da668aa1SThomas Huth aio_poll(qemu_get_aio_context(), true);
178da668aa1SThomas Huth }
179da668aa1SThomas Huth
180da668aa1SThomas Huth /* Failure or cancellation of one job cancels the other job */
181da668aa1SThomas Huth if (expected1 != 0) {
182da668aa1SThomas Huth expected2 = -ECANCELED;
183da668aa1SThomas Huth } else if (expected2 != 0) {
184da668aa1SThomas Huth expected1 = -ECANCELED;
185da668aa1SThomas Huth }
186da668aa1SThomas Huth
187da668aa1SThomas Huth g_assert_cmpint(result1, ==, expected1);
188da668aa1SThomas Huth g_assert_cmpint(result2, ==, expected2);
189da668aa1SThomas Huth }
190da668aa1SThomas Huth
test_pair_jobs_success(void)191da668aa1SThomas Huth static void test_pair_jobs_success(void)
192da668aa1SThomas Huth {
193da668aa1SThomas Huth test_pair_jobs(0, 0);
194da668aa1SThomas Huth }
195da668aa1SThomas Huth
test_pair_jobs_failure(void)196da668aa1SThomas Huth static void test_pair_jobs_failure(void)
197da668aa1SThomas Huth {
198da668aa1SThomas Huth /* Test both orderings. The two jobs run for a different number of
199da668aa1SThomas Huth * iterations so the code path is different depending on which job fails
200da668aa1SThomas Huth * first.
201da668aa1SThomas Huth */
202da668aa1SThomas Huth test_pair_jobs(-EIO, 0);
203da668aa1SThomas Huth test_pair_jobs(0, -EIO);
204da668aa1SThomas Huth }
205da668aa1SThomas Huth
test_pair_jobs_cancel(void)206da668aa1SThomas Huth static void test_pair_jobs_cancel(void)
207da668aa1SThomas Huth {
208da668aa1SThomas Huth test_pair_jobs(-ECANCELED, 0);
209da668aa1SThomas Huth test_pair_jobs(0, -ECANCELED);
210da668aa1SThomas Huth }
211da668aa1SThomas Huth
test_pair_jobs_fail_cancel_race(void)212da668aa1SThomas Huth static void test_pair_jobs_fail_cancel_race(void)
213da668aa1SThomas Huth {
214da668aa1SThomas Huth BlockJob *job1;
215da668aa1SThomas Huth BlockJob *job2;
216da668aa1SThomas Huth JobTxn *txn;
217da668aa1SThomas Huth int result1 = -EINPROGRESS;
218da668aa1SThomas Huth int result2 = -EINPROGRESS;
219da668aa1SThomas Huth
220da668aa1SThomas Huth txn = job_txn_new();
221da668aa1SThomas Huth job1 = test_block_job_start(1, true, -ECANCELED, &result1, txn);
222da668aa1SThomas Huth job2 = test_block_job_start(2, false, 0, &result2, txn);
223da668aa1SThomas Huth job_start(&job1->job);
224da668aa1SThomas Huth job_start(&job2->job);
225da668aa1SThomas Huth
226191e7af3SEmanuele Giuseppe Esposito WITH_JOB_LOCK_GUARD() {
227191e7af3SEmanuele Giuseppe Esposito job_cancel_locked(&job1->job, false);
228191e7af3SEmanuele Giuseppe Esposito }
229da668aa1SThomas Huth
230da668aa1SThomas Huth /* Now make job2 finish before the main loop kicks jobs. This simulates
231da668aa1SThomas Huth * the race between a pending kick and another job completing.
232da668aa1SThomas Huth */
233da668aa1SThomas Huth job_enter(&job2->job);
234da668aa1SThomas Huth job_enter(&job2->job);
235da668aa1SThomas Huth
236da668aa1SThomas Huth while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) {
237da668aa1SThomas Huth aio_poll(qemu_get_aio_context(), true);
238da668aa1SThomas Huth }
239da668aa1SThomas Huth
240da668aa1SThomas Huth g_assert_cmpint(result1, ==, -ECANCELED);
241da668aa1SThomas Huth g_assert_cmpint(result2, ==, -ECANCELED);
242da668aa1SThomas Huth
243da668aa1SThomas Huth job_txn_unref(txn);
244da668aa1SThomas Huth }
245da668aa1SThomas Huth
main(int argc,char ** argv)246da668aa1SThomas Huth int main(int argc, char **argv)
247da668aa1SThomas Huth {
248da668aa1SThomas Huth qemu_init_main_loop(&error_abort);
249da668aa1SThomas Huth bdrv_init();
250da668aa1SThomas Huth
251da668aa1SThomas Huth g_test_init(&argc, &argv, NULL);
252da668aa1SThomas Huth g_test_add_func("/single/success", test_single_job_success);
253da668aa1SThomas Huth g_test_add_func("/single/failure", test_single_job_failure);
254da668aa1SThomas Huth g_test_add_func("/single/cancel", test_single_job_cancel);
255da668aa1SThomas Huth g_test_add_func("/pair/success", test_pair_jobs_success);
256da668aa1SThomas Huth g_test_add_func("/pair/failure", test_pair_jobs_failure);
257da668aa1SThomas Huth g_test_add_func("/pair/cancel", test_pair_jobs_cancel);
258da668aa1SThomas Huth g_test_add_func("/pair/fail-cancel-race", test_pair_jobs_fail_cancel_race);
259da668aa1SThomas Huth return g_test_run();
260da668aa1SThomas Huth }
261