xref: /qemu/tests/unit/test-blockjob-txn.c (revision 191e7af3)
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