]>
Commit | Line | Data |
---|---|---|
9ef8112a AG |
1 | /* |
2 | * Blockjob tests | |
3 | * | |
4 | * Copyright Igalia, S.L. 2016 | |
5 | * | |
6 | * Authors: | |
7 | * Alberto Garcia <[email protected]> | |
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 | ||
164 | static void cancel_job_completed(BlockJob *job, void *opaque) | |
165 | { | |
166 | CancelJob *s = opaque; | |
167 | s->completed = true; | |
168 | block_job_completed(job, 0); | |
169 | } | |
170 | ||
171 | static void cancel_job_complete(BlockJob *job, Error **errp) | |
172 | { | |
173 | CancelJob *s = container_of(job, CancelJob, common); | |
174 | s->should_complete = true; | |
175 | } | |
176 | ||
177 | static void coroutine_fn cancel_job_start(void *opaque) | |
178 | { | |
179 | CancelJob *s = opaque; | |
180 | ||
181 | while (!s->should_complete) { | |
daa7f2f9 | 182 | if (job_is_cancelled(&s->common.job)) { |
fb367e03 JS |
183 | goto defer; |
184 | } | |
185 | ||
186 | if (!s->common.ready && s->should_converge) { | |
187 | block_job_event_ready(&s->common); | |
188 | } | |
189 | ||
190 | block_job_sleep_ns(&s->common, 100000); | |
191 | } | |
192 | ||
193 | defer: | |
194 | block_job_defer_to_main_loop(&s->common, cancel_job_completed, s); | |
195 | } | |
196 | ||
197 | static const BlockJobDriver test_cancel_driver = { | |
33e9e9bd KW |
198 | .job_driver = { |
199 | .instance_size = sizeof(CancelJob), | |
80fa2c75 | 200 | .free = block_job_free, |
33e9e9bd | 201 | }, |
fb367e03 JS |
202 | .start = cancel_job_start, |
203 | .complete = cancel_job_complete, | |
204 | }; | |
205 | ||
206 | static CancelJob *create_common(BlockJob **pjob) | |
207 | { | |
208 | BlockBackend *blk; | |
209 | BlockJob *job; | |
210 | CancelJob *s; | |
211 | ||
212 | blk = create_blk(NULL); | |
213 | job = mk_job(blk, "Steve", &test_cancel_driver, true, | |
214 | BLOCK_JOB_MANUAL_FINALIZE | BLOCK_JOB_MANUAL_DISMISS); | |
80fa2c75 | 215 | job_ref(&job->job); |
a50c2ab8 | 216 | assert(job->job.status == JOB_STATUS_CREATED); |
fb367e03 JS |
217 | s = container_of(job, CancelJob, common); |
218 | s->blk = blk; | |
219 | ||
220 | *pjob = job; | |
221 | return s; | |
222 | } | |
223 | ||
224 | static void cancel_common(CancelJob *s) | |
225 | { | |
226 | BlockJob *job = &s->common; | |
227 | BlockBackend *blk = s->blk; | |
a50c2ab8 | 228 | JobStatus sts = job->job.status; |
fb367e03 JS |
229 | |
230 | block_job_cancel_sync(job); | |
a50c2ab8 | 231 | if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) { |
fb367e03 JS |
232 | BlockJob *dummy = job; |
233 | block_job_dismiss(&dummy, &error_abort); | |
234 | } | |
a50c2ab8 | 235 | assert(job->job.status == JOB_STATUS_NULL); |
80fa2c75 | 236 | job_unref(&job->job); |
fb367e03 JS |
237 | destroy_blk(blk); |
238 | } | |
239 | ||
240 | static void test_cancel_created(void) | |
241 | { | |
242 | BlockJob *job; | |
243 | CancelJob *s; | |
244 | ||
245 | s = create_common(&job); | |
246 | cancel_common(s); | |
247 | } | |
248 | ||
249 | static void test_cancel_running(void) | |
250 | { | |
251 | BlockJob *job; | |
252 | CancelJob *s; | |
253 | ||
254 | s = create_common(&job); | |
255 | ||
256 | block_job_start(job); | |
a50c2ab8 | 257 | assert(job->job.status == JOB_STATUS_RUNNING); |
fb367e03 JS |
258 | |
259 | cancel_common(s); | |
260 | } | |
261 | ||
262 | static void test_cancel_paused(void) | |
263 | { | |
264 | BlockJob *job; | |
265 | CancelJob *s; | |
266 | ||
267 | s = create_common(&job); | |
268 | ||
269 | block_job_start(job); | |
a50c2ab8 | 270 | assert(job->job.status == JOB_STATUS_RUNNING); |
fb367e03 JS |
271 | |
272 | block_job_user_pause(job, &error_abort); | |
273 | block_job_enter(job); | |
a50c2ab8 | 274 | assert(job->job.status == JOB_STATUS_PAUSED); |
fb367e03 JS |
275 | |
276 | cancel_common(s); | |
277 | } | |
278 | ||
279 | static void test_cancel_ready(void) | |
280 | { | |
281 | BlockJob *job; | |
282 | CancelJob *s; | |
283 | ||
284 | s = create_common(&job); | |
285 | ||
286 | block_job_start(job); | |
a50c2ab8 | 287 | assert(job->job.status == JOB_STATUS_RUNNING); |
fb367e03 JS |
288 | |
289 | s->should_converge = true; | |
290 | block_job_enter(job); | |
a50c2ab8 | 291 | assert(job->job.status == JOB_STATUS_READY); |
fb367e03 JS |
292 | |
293 | cancel_common(s); | |
294 | } | |
295 | ||
296 | static void test_cancel_standby(void) | |
297 | { | |
298 | BlockJob *job; | |
299 | CancelJob *s; | |
300 | ||
301 | s = create_common(&job); | |
302 | ||
303 | block_job_start(job); | |
a50c2ab8 | 304 | assert(job->job.status == JOB_STATUS_RUNNING); |
fb367e03 JS |
305 | |
306 | s->should_converge = true; | |
307 | block_job_enter(job); | |
a50c2ab8 | 308 | assert(job->job.status == JOB_STATUS_READY); |
fb367e03 JS |
309 | |
310 | block_job_user_pause(job, &error_abort); | |
311 | block_job_enter(job); | |
a50c2ab8 | 312 | assert(job->job.status == JOB_STATUS_STANDBY); |
fb367e03 JS |
313 | |
314 | cancel_common(s); | |
315 | } | |
316 | ||
317 | static void test_cancel_pending(void) | |
318 | { | |
319 | BlockJob *job; | |
320 | CancelJob *s; | |
321 | ||
322 | s = create_common(&job); | |
323 | ||
324 | block_job_start(job); | |
a50c2ab8 | 325 | assert(job->job.status == JOB_STATUS_RUNNING); |
fb367e03 JS |
326 | |
327 | s->should_converge = true; | |
328 | block_job_enter(job); | |
a50c2ab8 | 329 | assert(job->job.status == JOB_STATUS_READY); |
fb367e03 JS |
330 | |
331 | block_job_complete(job, &error_abort); | |
332 | block_job_enter(job); | |
333 | while (!s->completed) { | |
334 | aio_poll(qemu_get_aio_context(), true); | |
335 | } | |
a50c2ab8 | 336 | assert(job->job.status == JOB_STATUS_PENDING); |
fb367e03 JS |
337 | |
338 | cancel_common(s); | |
339 | } | |
340 | ||
341 | static void test_cancel_concluded(void) | |
342 | { | |
343 | BlockJob *job; | |
344 | CancelJob *s; | |
345 | ||
346 | s = create_common(&job); | |
347 | ||
348 | block_job_start(job); | |
a50c2ab8 | 349 | assert(job->job.status == JOB_STATUS_RUNNING); |
fb367e03 JS |
350 | |
351 | s->should_converge = true; | |
352 | block_job_enter(job); | |
a50c2ab8 | 353 | assert(job->job.status == JOB_STATUS_READY); |
fb367e03 JS |
354 | |
355 | block_job_complete(job, &error_abort); | |
356 | block_job_enter(job); | |
357 | while (!s->completed) { | |
358 | aio_poll(qemu_get_aio_context(), true); | |
359 | } | |
a50c2ab8 | 360 | assert(job->job.status == JOB_STATUS_PENDING); |
fb367e03 JS |
361 | |
362 | block_job_finalize(job, &error_abort); | |
a50c2ab8 | 363 | assert(job->job.status == JOB_STATUS_CONCLUDED); |
fb367e03 JS |
364 | |
365 | cancel_common(s); | |
366 | } | |
367 | ||
9ef8112a AG |
368 | int main(int argc, char **argv) |
369 | { | |
370 | qemu_init_main_loop(&error_abort); | |
d185cf0e | 371 | bdrv_init(); |
9ef8112a AG |
372 | |
373 | g_test_init(&argc, &argv, NULL); | |
374 | g_test_add_func("/blockjob/ids", test_job_ids); | |
fb367e03 JS |
375 | g_test_add_func("/blockjob/cancel/created", test_cancel_created); |
376 | g_test_add_func("/blockjob/cancel/running", test_cancel_running); | |
377 | g_test_add_func("/blockjob/cancel/paused", test_cancel_paused); | |
378 | g_test_add_func("/blockjob/cancel/ready", test_cancel_ready); | |
379 | g_test_add_func("/blockjob/cancel/standby", test_cancel_standby); | |
380 | g_test_add_func("/blockjob/cancel/pending", test_cancel_pending); | |
381 | g_test_add_func("/blockjob/cancel/concluded", test_cancel_concluded); | |
9ef8112a AG |
382 | return g_test_run(); |
383 | } |