]>
Commit | Line | Data |
---|---|---|
9ef8112a AG |
1 | /* |
2 | * Blockjob tests | |
3 | * | |
4 | * Copyright Igalia, S.L. 2016 | |
5 | * | |
6 | * Authors: | |
7 | * Alberto Garcia <berto@igalia.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" | |
c87621ea | 16 | #include "block/blockjob_int.h" |
9ef8112a AG |
17 | #include "sysemu/block-backend.h" |
18 | ||
19 | static const BlockJobDriver test_block_job_driver = { | |
33e9e9bd KW |
20 | .job_driver = { |
21 | .instance_size = sizeof(BlockJob), | |
80fa2c75 | 22 | .free = block_job_free, |
33e9e9bd | 23 | }, |
9ef8112a AG |
24 | }; |
25 | ||
26 | static void block_job_cb(void *opaque, int ret) | |
27 | { | |
28 | } | |
29 | ||
fb367e03 JS |
30 | static BlockJob *mk_job(BlockBackend *blk, const char *id, |
31 | const BlockJobDriver *drv, bool should_succeed, | |
32 | int flags) | |
9ef8112a AG |
33 | { |
34 | BlockJob *job; | |
35 | Error *errp = NULL; | |
36 | ||
fb367e03 JS |
37 | job = block_job_create(id, drv, NULL, blk_bs(blk), |
38 | 0, BLK_PERM_ALL, 0, flags, block_job_cb, | |
c6cc12bf | 39 | NULL, &errp); |
9ef8112a AG |
40 | if (should_succeed) { |
41 | g_assert_null(errp); | |
42 | g_assert_nonnull(job); | |
43 | if (id) { | |
33e9e9bd | 44 | g_assert_cmpstr(job->job.id, ==, id); |
9ef8112a | 45 | } else { |
33e9e9bd | 46 | g_assert_cmpstr(job->job.id, ==, blk_name(blk)); |
9ef8112a AG |
47 | } |
48 | } else { | |
49 | g_assert_nonnull(errp); | |
50 | g_assert_null(job); | |
51 | error_free(errp); | |
52 | } | |
53 | ||
54 | return job; | |
55 | } | |
56 | ||
fb367e03 JS |
57 | static BlockJob *do_test_id(BlockBackend *blk, const char *id, |
58 | bool should_succeed) | |
59 | { | |
60 | return mk_job(blk, id, &test_block_job_driver, | |
61 | should_succeed, BLOCK_JOB_DEFAULT); | |
62 | } | |
63 | ||
9ef8112a AG |
64 | /* This creates a BlockBackend (optionally with a name) with a |
65 | * BlockDriverState inserted. */ | |
66 | static BlockBackend *create_blk(const char *name) | |
67 | { | |
2807c0cd | 68 | /* No I/O is performed on this device */ |
6d0eb64d | 69 | BlockBackend *blk = blk_new(0, BLK_PERM_ALL); |
d185cf0e KW |
70 | BlockDriverState *bs; |
71 | ||
72 | bs = bdrv_open("null-co://", NULL, NULL, 0, &error_abort); | |
73 | g_assert_nonnull(bs); | |
9ef8112a | 74 | |
d7086422 | 75 | blk_insert_bs(blk, bs, &error_abort); |
9ef8112a AG |
76 | bdrv_unref(bs); |
77 | ||
78 | if (name) { | |
79 | Error *errp = NULL; | |
80 | monitor_add_blk(blk, name, &errp); | |
81 | g_assert_null(errp); | |
82 | } | |
83 | ||
84 | return blk; | |
85 | } | |
86 | ||
87 | /* This destroys the backend */ | |
88 | static void destroy_blk(BlockBackend *blk) | |
89 | { | |
90 | if (blk_name(blk)[0] != '\0') { | |
91 | monitor_remove_blk(blk); | |
92 | } | |
93 | ||
94 | blk_remove_bs(blk); | |
95 | blk_unref(blk); | |
96 | } | |
97 | ||
98 | static void test_job_ids(void) | |
99 | { | |
100 | BlockBackend *blk[3]; | |
101 | BlockJob *job[3]; | |
102 | ||
103 | blk[0] = create_blk(NULL); | |
104 | blk[1] = create_blk("drive1"); | |
105 | blk[2] = create_blk("drive2"); | |
106 | ||
107 | /* No job ID provided and the block backend has no name */ | |
108 | job[0] = do_test_id(blk[0], NULL, false); | |
109 | ||
110 | /* These are all invalid job IDs */ | |
111 | job[0] = do_test_id(blk[0], "0id", false); | |
112 | job[0] = do_test_id(blk[0], "", false); | |
113 | job[0] = do_test_id(blk[0], " ", false); | |
114 | job[0] = do_test_id(blk[0], "123", false); | |
115 | job[0] = do_test_id(blk[0], "_id", false); | |
116 | job[0] = do_test_id(blk[0], "-id", false); | |
117 | job[0] = do_test_id(blk[0], ".id", false); | |
118 | job[0] = do_test_id(blk[0], "#id", false); | |
119 | ||
120 | /* This one is valid */ | |
121 | job[0] = do_test_id(blk[0], "id0", true); | |
122 | ||
123 | /* We cannot have two jobs in the same BDS */ | |
124 | do_test_id(blk[0], "id1", false); | |
125 | ||
126 | /* Duplicate job IDs are not allowed */ | |
127 | job[1] = do_test_id(blk[1], "id0", false); | |
128 | ||
129 | /* But once job[0] finishes we can reuse its ID */ | |
05b0d8e3 | 130 | block_job_early_fail(job[0]); |
9ef8112a AG |
131 | job[1] = do_test_id(blk[1], "id0", true); |
132 | ||
133 | /* No job ID specified, defaults to the backend name ('drive1') */ | |
05b0d8e3 | 134 | block_job_early_fail(job[1]); |
9ef8112a AG |
135 | job[1] = do_test_id(blk[1], NULL, true); |
136 | ||
137 | /* Duplicate job ID */ | |
138 | job[2] = do_test_id(blk[2], "drive1", false); | |
139 | ||
140 | /* The ID of job[2] would default to 'drive2' but it is already in use */ | |
141 | job[0] = do_test_id(blk[0], "drive2", true); | |
142 | job[2] = do_test_id(blk[2], NULL, false); | |
143 | ||
144 | /* This one is valid */ | |
145 | job[2] = do_test_id(blk[2], "id_2", true); | |
146 | ||
05b0d8e3 PB |
147 | block_job_early_fail(job[0]); |
148 | block_job_early_fail(job[1]); | |
149 | block_job_early_fail(job[2]); | |
9ef8112a AG |
150 | |
151 | destroy_blk(blk[0]); | |
152 | destroy_blk(blk[1]); | |
153 | destroy_blk(blk[2]); | |
154 | } | |
155 | ||
fb367e03 JS |
156 | typedef struct CancelJob { |
157 | BlockJob common; | |
158 | BlockBackend *blk; | |
159 | bool should_converge; | |
160 | bool should_complete; | |
161 | bool completed; | |
162 | } CancelJob; | |
163 | ||
1908a559 | 164 | static void cancel_job_completed(Job *job, void *opaque) |
fb367e03 | 165 | { |
1908a559 | 166 | BlockJob *bjob = container_of(job, BlockJob, job); |
fb367e03 JS |
167 | CancelJob *s = opaque; |
168 | s->completed = true; | |
1908a559 | 169 | block_job_completed(bjob, 0); |
fb367e03 JS |
170 | } |
171 | ||
172 | static void cancel_job_complete(BlockJob *job, Error **errp) | |
173 | { | |
174 | CancelJob *s = container_of(job, CancelJob, common); | |
175 | s->should_complete = true; | |
176 | } | |
177 | ||
178 | static void coroutine_fn cancel_job_start(void *opaque) | |
179 | { | |
180 | CancelJob *s = opaque; | |
181 | ||
182 | while (!s->should_complete) { | |
daa7f2f9 | 183 | if (job_is_cancelled(&s->common.job)) { |
fb367e03 JS |
184 | goto defer; |
185 | } | |
186 | ||
187 | if (!s->common.ready && s->should_converge) { | |
188 | block_job_event_ready(&s->common); | |
189 | } | |
190 | ||
5d43e86e | 191 | job_sleep_ns(&s->common.job, 100000); |
fb367e03 JS |
192 | } |
193 | ||
194 | defer: | |
1908a559 | 195 | job_defer_to_main_loop(&s->common.job, cancel_job_completed, s); |
fb367e03 JS |
196 | } |
197 | ||
198 | static const BlockJobDriver test_cancel_driver = { | |
33e9e9bd KW |
199 | .job_driver = { |
200 | .instance_size = sizeof(CancelJob), | |
80fa2c75 | 201 | .free = block_job_free, |
da01ff7f | 202 | .start = cancel_job_start, |
33e9e9bd | 203 | }, |
fb367e03 JS |
204 | .complete = cancel_job_complete, |
205 | }; | |
206 | ||
207 | static CancelJob *create_common(BlockJob **pjob) | |
208 | { | |
209 | BlockBackend *blk; | |
210 | BlockJob *job; | |
211 | CancelJob *s; | |
212 | ||
213 | blk = create_blk(NULL); | |
214 | job = mk_job(blk, "Steve", &test_cancel_driver, true, | |
215 | BLOCK_JOB_MANUAL_FINALIZE | BLOCK_JOB_MANUAL_DISMISS); | |
80fa2c75 | 216 | job_ref(&job->job); |
a50c2ab8 | 217 | assert(job->job.status == JOB_STATUS_CREATED); |
fb367e03 JS |
218 | s = container_of(job, CancelJob, common); |
219 | s->blk = blk; | |
220 | ||
221 | *pjob = job; | |
222 | return s; | |
223 | } | |
224 | ||
225 | static void cancel_common(CancelJob *s) | |
226 | { | |
227 | BlockJob *job = &s->common; | |
228 | BlockBackend *blk = s->blk; | |
a50c2ab8 | 229 | JobStatus sts = job->job.status; |
fb367e03 JS |
230 | |
231 | block_job_cancel_sync(job); | |
a50c2ab8 | 232 | if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) { |
fb367e03 JS |
233 | BlockJob *dummy = job; |
234 | block_job_dismiss(&dummy, &error_abort); | |
235 | } | |
a50c2ab8 | 236 | assert(job->job.status == JOB_STATUS_NULL); |
80fa2c75 | 237 | job_unref(&job->job); |
fb367e03 JS |
238 | destroy_blk(blk); |
239 | } | |
240 | ||
241 | static void test_cancel_created(void) | |
242 | { | |
243 | BlockJob *job; | |
244 | CancelJob *s; | |
245 | ||
246 | s = create_common(&job); | |
247 | cancel_common(s); | |
248 | } | |
249 | ||
250 | static void test_cancel_running(void) | |
251 | { | |
252 | BlockJob *job; | |
253 | CancelJob *s; | |
254 | ||
255 | s = create_common(&job); | |
256 | ||
da01ff7f | 257 | job_start(&job->job); |
a50c2ab8 | 258 | assert(job->job.status == JOB_STATUS_RUNNING); |
fb367e03 JS |
259 | |
260 | cancel_common(s); | |
261 | } | |
262 | ||
263 | static void test_cancel_paused(void) | |
264 | { | |
265 | BlockJob *job; | |
266 | CancelJob *s; | |
267 | ||
268 | s = create_common(&job); | |
269 | ||
da01ff7f | 270 | job_start(&job->job); |
a50c2ab8 | 271 | assert(job->job.status == JOB_STATUS_RUNNING); |
fb367e03 JS |
272 | |
273 | block_job_user_pause(job, &error_abort); | |
274 | block_job_enter(job); | |
a50c2ab8 | 275 | assert(job->job.status == JOB_STATUS_PAUSED); |
fb367e03 JS |
276 | |
277 | cancel_common(s); | |
278 | } | |
279 | ||
280 | static void test_cancel_ready(void) | |
281 | { | |
282 | BlockJob *job; | |
283 | CancelJob *s; | |
284 | ||
285 | s = create_common(&job); | |
286 | ||
da01ff7f | 287 | job_start(&job->job); |
a50c2ab8 | 288 | assert(job->job.status == JOB_STATUS_RUNNING); |
fb367e03 JS |
289 | |
290 | s->should_converge = true; | |
291 | block_job_enter(job); | |
a50c2ab8 | 292 | assert(job->job.status == JOB_STATUS_READY); |
fb367e03 JS |
293 | |
294 | cancel_common(s); | |
295 | } | |
296 | ||
297 | static void test_cancel_standby(void) | |
298 | { | |
299 | BlockJob *job; | |
300 | CancelJob *s; | |
301 | ||
302 | s = create_common(&job); | |
303 | ||
da01ff7f | 304 | job_start(&job->job); |
a50c2ab8 | 305 | assert(job->job.status == JOB_STATUS_RUNNING); |
fb367e03 JS |
306 | |
307 | s->should_converge = true; | |
308 | block_job_enter(job); | |
a50c2ab8 | 309 | assert(job->job.status == JOB_STATUS_READY); |
fb367e03 JS |
310 | |
311 | block_job_user_pause(job, &error_abort); | |
312 | block_job_enter(job); | |
a50c2ab8 | 313 | assert(job->job.status == JOB_STATUS_STANDBY); |
fb367e03 JS |
314 | |
315 | cancel_common(s); | |
316 | } | |
317 | ||
318 | static void test_cancel_pending(void) | |
319 | { | |
320 | BlockJob *job; | |
321 | CancelJob *s; | |
322 | ||
323 | s = create_common(&job); | |
324 | ||
da01ff7f | 325 | job_start(&job->job); |
a50c2ab8 | 326 | assert(job->job.status == JOB_STATUS_RUNNING); |
fb367e03 JS |
327 | |
328 | s->should_converge = true; | |
329 | block_job_enter(job); | |
a50c2ab8 | 330 | assert(job->job.status == JOB_STATUS_READY); |
fb367e03 JS |
331 | |
332 | block_job_complete(job, &error_abort); | |
333 | block_job_enter(job); | |
334 | while (!s->completed) { | |
335 | aio_poll(qemu_get_aio_context(), true); | |
336 | } | |
a50c2ab8 | 337 | assert(job->job.status == JOB_STATUS_PENDING); |
fb367e03 JS |
338 | |
339 | cancel_common(s); | |
340 | } | |
341 | ||
342 | static void test_cancel_concluded(void) | |
343 | { | |
344 | BlockJob *job; | |
345 | CancelJob *s; | |
346 | ||
347 | s = create_common(&job); | |
348 | ||
da01ff7f | 349 | job_start(&job->job); |
a50c2ab8 | 350 | assert(job->job.status == JOB_STATUS_RUNNING); |
fb367e03 JS |
351 | |
352 | s->should_converge = true; | |
353 | block_job_enter(job); | |
a50c2ab8 | 354 | assert(job->job.status == JOB_STATUS_READY); |
fb367e03 JS |
355 | |
356 | block_job_complete(job, &error_abort); | |
357 | block_job_enter(job); | |
358 | while (!s->completed) { | |
359 | aio_poll(qemu_get_aio_context(), true); | |
360 | } | |
a50c2ab8 | 361 | assert(job->job.status == JOB_STATUS_PENDING); |
fb367e03 JS |
362 | |
363 | block_job_finalize(job, &error_abort); | |
a50c2ab8 | 364 | assert(job->job.status == JOB_STATUS_CONCLUDED); |
fb367e03 JS |
365 | |
366 | cancel_common(s); | |
367 | } | |
368 | ||
9ef8112a AG |
369 | int main(int argc, char **argv) |
370 | { | |
371 | qemu_init_main_loop(&error_abort); | |
d185cf0e | 372 | bdrv_init(); |
9ef8112a AG |
373 | |
374 | g_test_init(&argc, &argv, NULL); | |
375 | g_test_add_func("/blockjob/ids", test_job_ids); | |
fb367e03 JS |
376 | g_test_add_func("/blockjob/cancel/created", test_cancel_created); |
377 | g_test_add_func("/blockjob/cancel/running", test_cancel_running); | |
378 | g_test_add_func("/blockjob/cancel/paused", test_cancel_paused); | |
379 | g_test_add_func("/blockjob/cancel/ready", test_cancel_ready); | |
380 | g_test_add_func("/blockjob/cancel/standby", test_cancel_standby); | |
381 | g_test_add_func("/blockjob/cancel/pending", test_cancel_pending); | |
382 | g_test_add_func("/blockjob/cancel/concluded", test_cancel_concluded); | |
9ef8112a AG |
383 | return g_test_run(); |
384 | } |