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