388
cancel_common(s);
389
}
390
391
+typedef struct PauseCountJob {
392
+ BlockJob common;
393
+ int n;
394
+ bool should_complete;
395
+} PauseCountJob;
396
+
397
+static void pause_count_job_complete(Job *job, Error **errp)
398
+{
399
+ PauseCountJob *s = container_of(job, PauseCountJob, common.job);
400
+ s->should_complete = true;
401
+}
402
+
403
+static int coroutine_fn pause_count_job_run(Job *job, Error **errp)
404
+{
405
+ PauseCountJob *s = container_of(job, PauseCountJob, common.job);
406
+
407
+ while (!s->should_complete) {
408
+ if (job_is_cancelled(&s->common.job)) {
409
+ return 0;
410
+ }
411
+ s->n++;
412
+ /*
413
+ * Yields; while a pause is pending the yield is skipped and the job
414
+ * parks in job_pause_point() instead.
415
+ */
416
+ job_sleep_ns(&s->common.job, 10 * 1000 * 1000);
417
+ }
418
+
419
+ return 0;
420
+}
421
+
422
+static const BlockJobDriver pause_count_job_driver = {
423
+ .job_driver = {
424
+ .instance_size = sizeof(PauseCountJob),
425
+ .free = block_job_free,
426
+ .user_resume = block_job_user_resume,
427
+ .run = pause_count_job_run,
428
+ .complete = pause_count_job_complete,
429
+ },
430
+};
431
+
432
+/*
433
+ * A job that has reached its pause point must stay paused while a pause is
434
+ * still pending (pause_count > 0). An overlapping drain re-enters the job (one
435
+ * drain's job_resume() wakes it while the next drain's job_pause() is already
436
+ * counted); the job must not run or clear job->paused, otherwise
437
+ * job_set_aio_context() can observe paused == false and abort.
438
+ */
439
+static void test_pause_keeps_paused(void)
440
+{
441
+ BlockBackend *blk;
442
+ BlockJob *bjob;
443
+ PauseCountJob *s;
444
+ Job *job;
445
+ int n0;
446
+
447
+ blk = create_blk(NULL);
448
+ bjob = mk_job(blk, "job0", &pause_count_job_driver, true,
449
+ JOB_MANUAL_FINALIZE | JOB_MANUAL_DISMISS);
450
+ s = container_of(bjob, PauseCountJob, common);
451
+ job = &bjob->job;
452
+ WITH_JOB_LOCK_GUARD() {
453
+ job_ref_locked(job);
454
+ }
455
+
456
+ job_start(job);
457
+
458
+ /* Pause the running job; it parks in job_pause_point() with paused set. */
459
+ WITH_JOB_LOCK_GUARD() {
460
+ job_pause_locked(job);
461
+ g_assert_true(job->paused);
462
+ g_assert_cmpint(job->status, ==, JOB_STATUS_PAUSED);
463
+ }
464
+ n0 = s->n;
465
+
466
+ /*
467
+ * Spurious wake while the pause is still pending. The job must stay parked:
468
+ * the bug clears job->paused, runs an iteration (s->n advances) and
469
+ * re-pauses, exposing a paused == false window.
470
+ */
471
+ job_enter(job);
472
+ WITH_JOB_LOCK_GUARD() {
473
+ g_assert_true(job->paused);
474
+ }
475
+ g_assert_cmpint(s->n, ==, n0);
476
+
477
+ /* Resume and tear down. */
478
+ WITH_JOB_LOCK_GUARD() {
479
+ job_resume_locked(job);
480
+ }
481
+ job_cancel_sync(job, true);
482
+ WITH_JOB_LOCK_GUARD() {
483
+ Job *dummy = job;
484
+ job_dismiss_locked(&dummy, &error_abort);
485
+ job_unref_locked(job);
486
+ }
487
+ destroy_blk(blk);
488
+}
489
+
490
int main(int argc, char **argv)
491
{
492
qemu_init_main_loop(&error_abort);
501
g_test_add_func("/blockjob/cancel/standby", test_cancel_standby);
502
g_test_add_func("/blockjob/cancel/pending", test_cancel_pending);
503
g_test_add_func("/blockjob/cancel/concluded", test_cancel_concluded);
504
+ g_test_add_func("/blockjob/pause/keep_paused", test_pause_keeps_paused);
505
return g_test_run();
506
}