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