static void test_pair_jobs_fail_cancel_race(void) { BlockJob *job1; BlockJob *job2; BlockJobTxn *txn; int result1 = -EINPROGRESS; int result2 = -EINPROGRESS; txn = block_job_txn_new(); job1 = test_block_job_start(1, true, -ECANCELED, &result1, txn); job2 = test_block_job_start(2, false, 0, &result2, txn); block_job_start(job1); block_job_start(job2); block_job_cancel(job1, false); /* Now make job2 finish before the main loop kicks jobs. This simulates * the race between a pending kick and another job completing. */ block_job_enter(job2); block_job_enter(job2); while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) { aio_poll(qemu_get_aio_context(), true); } g_assert_cmpint(result1, ==, -ECANCELED); g_assert_cmpint(result2, ==, -ECANCELED); block_job_txn_unref(txn); }
static void test_pair_jobs(int expected1, int expected2) { BlockJob *job1; BlockJob *job2; BlockJobTxn *txn; int result1 = -EINPROGRESS; int result2 = -EINPROGRESS; txn = block_job_txn_new(); job1 = test_block_job_start(1, true, expected1, &result1, txn); job2 = test_block_job_start(2, true, expected2, &result2, txn); block_job_start(job1); block_job_start(job2); /* Release our reference now to trigger as many nice * use-after-free bugs as possible. */ block_job_txn_unref(txn); if (expected1 == -ECANCELED) { block_job_cancel(job1, false); } if (expected2 == -ECANCELED) { block_job_cancel(job2, false); } while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) { aio_poll(qemu_get_aio_context(), true); } /* Failure or cancellation of one job cancels the other job */ if (expected1 != 0) { expected2 = -ECANCELED; } else if (expected2 != 0) { expected1 = -ECANCELED; } g_assert_cmpint(result1, ==, expected1); g_assert_cmpint(result2, ==, expected2); }
static void test_pair_jobs(int expected1, int expected2) { BlockJob *job1; BlockJob *job2; BlockJobTxn *txn; int result1 = -EINPROGRESS; int result2 = -EINPROGRESS; txn = block_job_txn_new(); job1 = test_block_job_start(1, true, expected1, &result1); block_job_txn_add_job(txn, job1); job2 = test_block_job_start(2, true, expected2, &result2); block_job_txn_add_job(txn, job2); if (expected1 == -ECANCELED) { block_job_cancel(job1); } if (expected2 == -ECANCELED) { block_job_cancel(job2); } while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) { aio_poll(qemu_get_aio_context(), true); } /* Failure or cancellation of one job cancels the other job */ if (expected1 != 0) { expected2 = -ECANCELED; } else if (expected2 != 0) { expected1 = -ECANCELED; } g_assert_cmpint(result1, ==, expected1); g_assert_cmpint(result2, ==, expected2); block_job_txn_unref(txn); }
static void test_single_job(int expected) { BlockJob *job; BlockJobTxn *txn; int result = -EINPROGRESS; txn = block_job_txn_new(); job = test_block_job_start(1, true, expected, &result, txn); block_job_start(job); if (expected == -ECANCELED) { block_job_cancel(job, false); } while (result == -EINPROGRESS) { aio_poll(qemu_get_aio_context(), true); } g_assert_cmpint(result, ==, expected); block_job_txn_unref(txn); }
int block_job_cancel_sync(BlockJob *job) { struct BlockCancelData data; BlockDriverState *bs = job->bs; assert(bs->job == job); /* Set up our own callback to store the result and chain to * the original callback. */ data.job = job; data.cb = job->cb; data.opaque = job->opaque; data.ret = -EINPROGRESS; job->cb = block_job_cancel_cb; job->opaque = &data; block_job_cancel(job); while (data.ret == -EINPROGRESS) { qemu_aio_wait(); } return (data.cancelled && data.ret == 0) ? -ECANCELED : data.ret; }
/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be * used with block_job_finish_sync() without the need for (rather nasty) * function pointer casts there. */ static void block_job_cancel_err(BlockJob *job, Error **errp) { block_job_cancel(job); }