static void test_co_queue(void) { Coroutine *c1; Coroutine *c2; c1 = qemu_coroutine_create(c1_fn); c2 = qemu_coroutine_create(c2_fn); qemu_coroutine_enter(c1, c2); memset(c1, 0xff, sizeof(Coroutine)); qemu_coroutine_enter(c2, NULL); }
int simple_bus_fdt_init(char *bus_node_path, FDTMachineInfo *fdti, void *unused) { int i; int num_children = qemu_devtree_get_num_children(fdti->fdt, bus_node_path, 1); char **children = qemu_devtree_get_children(fdti->fdt, bus_node_path, 1); int initialRoutinesPending = fdti->routinesPending; DB_PRINT("num child devices: %d\n", num_children); for (i = 0; i < num_children; i++) { struct FDTInitNodeArgs *init_args = g_malloc0(sizeof(*init_args)); init_args->node_path = children[i]; init_args->fdti = fdti; fdti->routinesPending++; qemu_coroutine_enter(qemu_coroutine_create(fdt_init_node), init_args); } if (fdti->routinesPending != initialRoutinesPending) { bdrv_drain_all(); } g_free(children); return 0; }
static int do_co_write_zeroes(int64_t offset, int count, int *total) { Coroutine *co; CoWriteZeroes data = { .offset = offset, .count = count, .total = total, .done = false, }; co = qemu_coroutine_create(co_write_zeroes_entry); qemu_coroutine_enter(co, &data); while (!data.done) { qemu_aio_wait(); } if (data.ret < 0) { return data.ret; } else { return 1; } } static int do_write_compressed(char *buf, int64_t offset, int count, int *total) { int ret; ret = bdrv_write_compressed(bs, offset >> 9, (uint8_t *)buf, count >> 9); if (ret < 0) { return ret; } *total = count; return 1; }
static void test_self(void) { Coroutine *coroutine; coroutine = qemu_coroutine_create(verify_self); qemu_coroutine_enter(coroutine, coroutine); }
static void test_lifecycle(void) { Coroutine *coroutine; bool done = false; /* Create, enter, and return from coroutine */ coroutine = qemu_coroutine_create(set_and_exit); qemu_coroutine_enter(coroutine, &done); g_assert(done); /* expect done to be true (first time) */ /* Repeat to check that no state affects this test */ done = false; coroutine = qemu_coroutine_create(set_and_exit); qemu_coroutine_enter(coroutine, &done); g_assert(done); /* expect done to be true (second time) */ }
static void test_nesting(void) { Coroutine *root; NestData nd = { .n_enter = 0, .n_return = 0, .max = 128, }; root = qemu_coroutine_create(nest); qemu_coroutine_enter(root, &nd); /* Must enter and return from max nesting level */ g_assert_cmpint(nd.n_enter, ==, nd.max); g_assert_cmpint(nd.n_return, ==, nd.max); } /* * Check that yield/enter transfer control correctly */ static void coroutine_fn yield_5_times(void *opaque) { bool *done = opaque; int i; for (i = 0; i < 5; i++) { qemu_coroutine_yield(); } *done = true; }
static int do_co_write_zeroes(int64_t offset, int count, int *total) { Coroutine *co; CoWriteZeroes data = { .offset = offset, .count = count, .total = total, .done = false, }; co = qemu_coroutine_create(co_write_zeroes_entry); qemu_coroutine_enter(co, &data); while (!data.done) { qemu_aio_wait(); } if (data.ret < 0) { return data.ret; } else { return 1; } } static int do_load_vmstate(char *buf, int64_t offset, int count, int *total) { *total = bdrv_load_vmstate(bs, (uint8_t *)buf, offset, count); if (*total < 0) { return *total; } return 1; }
static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target, const char *replaces, int64_t speed, int64_t granularity, int64_t buf_size, BlockdevOnError on_source_error, BlockdevOnError on_target_error, BlockDriverCompletionFunc *cb, void *opaque, Error **errp, const BlockJobDriver *driver, bool is_none_mode, BlockDriverState *base) { MirrorBlockJob *s; if (granularity == 0) { /* Choose the default granularity based on the target file's cluster * size, clamped between 4k and 64k. */ BlockDriverInfo bdi; if (bdrv_get_info(target, &bdi) >= 0 && bdi.cluster_size != 0) { granularity = MAX(4096, bdi.cluster_size); granularity = MIN(65536, granularity); } else { granularity = 65536; } } assert ((granularity & (granularity - 1)) == 0); if ((on_source_error == BLOCKDEV_ON_ERROR_STOP || on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) && !bdrv_iostatus_is_enabled(bs)) { error_set(errp, QERR_INVALID_PARAMETER, "on-source-error"); return; } s = block_job_create(driver, bs, speed, cb, opaque, errp); if (!s) { return; } s->replaces = g_strdup(replaces); s->on_source_error = on_source_error; s->on_target_error = on_target_error; s->target = target; s->is_none_mode = is_none_mode; s->base = base; s->granularity = granularity; s->buf_size = MAX(buf_size, granularity); s->dirty_bitmap = bdrv_create_dirty_bitmap(bs, granularity, errp); if (!s->dirty_bitmap) { return; } bdrv_set_enable_write_cache(s->target, true); bdrv_set_on_error(s->target, on_target_error, on_target_error); bdrv_iostatus_enable(s->target); s->common.co = qemu_coroutine_create(mirror_run); trace_mirror_start(bs, s, s->common.co, opaque); qemu_coroutine_enter(s->common.co, s); }
/* Create a block job that completes with a given return code after a given * number of event loop iterations. The return code is stored in the given * result pointer. * * The event loop iterations can either be handled automatically with a 0 delay * timer, or they can be stepped manually by entering the coroutine. */ static BlockJob *test_block_job_start(unsigned int iterations, bool use_timer, int rc, int *result) { BlockDriverState *bs; TestBlockJob *s; TestBlockJobCBData *data; static unsigned counter; char job_id[24]; data = g_new0(TestBlockJobCBData, 1); bs = bdrv_new(); snprintf(job_id, sizeof(job_id), "job%u", counter++); s = block_job_create(job_id, &test_block_job_driver, bs, 0, BLOCK_JOB_DEFAULT, test_block_job_cb, data, &error_abort); s->iterations = iterations; s->use_timer = use_timer; s->rc = rc; s->result = result; s->common.co = qemu_coroutine_create(test_block_job_run, s); data->job = s; data->result = result; qemu_coroutine_enter(s->common.co); return &s->common; }
static void test_entered(void) { Coroutine *coroutine; coroutine = qemu_coroutine_create(verify_entered_step_1, NULL); g_assert(!qemu_coroutine_entered(coroutine)); qemu_coroutine_enter(coroutine); }
static void test_in_coroutine(void) { Coroutine *coroutine; g_assert(!qemu_in_coroutine()); coroutine = qemu_coroutine_create(verify_in_coroutine); qemu_coroutine_enter(coroutine, NULL); }
void process_incoming_migration(QEMUFile *f) { Coroutine *co = qemu_coroutine_create(process_incoming_migration_co); int fd = qemu_get_fd(f); assert(fd != -1); socket_set_nonblock(fd); qemu_coroutine_enter(co, f); }
static void test_no_dangling_access(void) { Coroutine *c1; Coroutine *c2; Coroutine tmp; c2 = qemu_coroutine_create(c2_fn, NULL); c1 = qemu_coroutine_create(c1_fn, c2); qemu_coroutine_enter(c1); /* c1 shouldn't be used any more now; make sure we segfault if it is */ tmp = *c1; memset(c1, 0xff, sizeof(Coroutine)); qemu_coroutine_enter(c2); /* Must restore the coroutine now to avoid corrupted pool */ *c1 = tmp; }
static void nbd_read(void *opaque) { NBDClient *client = opaque; if (client->recv_coroutine) { qemu_coroutine_enter(client->recv_coroutine, NULL); } else { qemu_coroutine_enter(qemu_coroutine_create(nbd_trip), client); } }
static void do_test_co_mutex(CoroutineEntry *entry, void *opaque) { Coroutine *c1 = qemu_coroutine_create(entry, opaque); Coroutine *c2 = qemu_coroutine_create(entry, opaque); done = 0; qemu_coroutine_enter(c1); g_assert(locked); qemu_coroutine_enter(c2); /* Unlock queues c2. It is then started automatically when c1 yields or * terminates. */ qemu_coroutine_enter(c1); g_assert_cmpint(done, ==, 1); g_assert(locked); qemu_coroutine_enter(c2); g_assert_cmpint(done, ==, 2); g_assert(!locked); }
static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target, const char *replaces, int64_t speed, uint32_t granularity, int64_t buf_size, BlockdevOnError on_source_error, BlockdevOnError on_target_error, BlockCompletionFunc *cb, void *opaque, Error **errp, const BlockJobDriver *driver, bool is_none_mode, BlockDriverState *base) { MirrorBlockJob *s; if (granularity == 0) { granularity = bdrv_get_default_bitmap_granularity(target); } assert ((granularity & (granularity - 1)) == 0); if ((on_source_error == BLOCKDEV_ON_ERROR_STOP || on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) && !bdrv_iostatus_is_enabled(bs)) { error_setg(errp, QERR_INVALID_PARAMETER, "on-source-error"); return; } s = block_job_create(driver, bs, speed, cb, opaque, errp); if (!s) { return; } s->replaces = g_strdup(replaces); s->on_source_error = on_source_error; s->on_target_error = on_target_error; s->target = target; s->is_none_mode = is_none_mode; s->base = base; s->granularity = granularity; s->buf_size = MAX(buf_size, granularity); s->dirty_bitmap = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp); if (!s->dirty_bitmap) { return; } bdrv_set_enable_write_cache(s->target, true); bdrv_set_on_error(s->target, on_target_error, on_target_error); bdrv_iostatus_enable(s->target); s->common.co = qemu_coroutine_create(mirror_run); trace_mirror_start(bs, s, s->common.co, opaque); qemu_coroutine_enter(s->common.co, s); }
static void coroutine_fn verify_entered_step_1(void *opaque) { Coroutine *self = qemu_coroutine_self(); Coroutine *coroutine; g_assert(qemu_coroutine_entered(self)); coroutine = qemu_coroutine_create(verify_entered_step_2, self); g_assert(!qemu_coroutine_entered(coroutine)); qemu_coroutine_enter(coroutine); g_assert(!qemu_coroutine_entered(coroutine)); qemu_coroutine_enter(coroutine); }
static void test_yield(void) { Coroutine *coroutine; bool done = false; int i = -1; /* one extra time to return from coroutine */ coroutine = qemu_coroutine_create(yield_5_times); while (!done) { qemu_coroutine_enter(coroutine, &done); i++; } g_assert_cmpint(i, ==, 5); /* coroutine must yield 5 times */ }
action_t *get_action(handler_func_t func) { action_t *action = (action_t *) malloc(sizeof(action_t)); action->func = func; action->arg = NULL; action->coro = qemu_coroutine_create(func); action->fd = -1; action->flags = 0; action->ready = 1; action->next = NULL; return action; }
static void coroutine_fn nest(void *opaque) { NestData *nd = opaque; nd->n_enter++; if (nd->n_enter < nd->max) { Coroutine *child; child = qemu_coroutine_create(nest); qemu_coroutine_enter(child, nd); } nd->n_return++; }
static void perf_lifecycle(void) { Coroutine *coroutine; unsigned int i, max; double duration; max = 1000000; g_test_timer_start(); for (i = 0; i < max; i++) { coroutine = qemu_coroutine_create(empty_coroutine); qemu_coroutine_enter(coroutine, NULL); } duration = g_test_timer_elapsed(); g_test_message("Lifecycle %u iterations: %f s\n", max, duration); }
static void perf_yield(void) { unsigned int i, maxcycles; double duration; maxcycles = 100000000; i = maxcycles; Coroutine *coroutine = qemu_coroutine_create(yield_loop); g_test_timer_start(); while (i > 0) { qemu_coroutine_enter(coroutine, &i); } duration = g_test_timer_elapsed(); g_test_message("Yield %u iterations: %f s\n", maxcycles, duration); }
static int simple_bus_fdt_init(char *node_path, FDTMachineInfo *fdti) { int i; int num_children = qemu_devtree_get_num_children(fdti->fdt, node_path, 1); char **children = qemu_devtree_get_children(fdti->fdt, node_path, 1); DB_PRINT_NP(num_children ? 0 : 1, "num child devices: %d\n", num_children); for (i = 0; i < num_children; i++) { struct FDTInitNodeArgs *init_args = g_malloc0(sizeof(*init_args)); init_args->node_path = children[i]; init_args->fdti = fdti; qemu_coroutine_enter(qemu_coroutine_create(fdt_init_node), init_args); } g_free(children); return 0; }
/* Create a block job that completes with a given return code after a given * number of event loop iterations. The return code is stored in the given * result pointer. * * The event loop iterations can either be handled automatically with a 0 delay * timer, or they can be stepped manually by entering the coroutine. */ static BlockJob *test_block_job_start(unsigned int iterations, bool use_timer, int rc, int *result) { BlockDriverState *bs; TestBlockJob *s; TestBlockJobCBData *data; data = g_new0(TestBlockJobCBData, 1); bs = bdrv_new(); s = block_job_create(&test_block_job_driver, bs, 0, test_block_job_cb, data, &error_abort); s->iterations = iterations; s->use_timer = use_timer; s->rc = rc; s->result = result; s->common.co = qemu_coroutine_create(test_block_job_run); data->job = s; data->result = result; qemu_coroutine_enter(s->common.co, s); return &s->common; }
static void perf_nesting(void) { unsigned int i, maxcycles, maxnesting; double duration; maxcycles = 10000; maxnesting = 1000; Coroutine *root; g_test_timer_start(); for (i = 0; i < maxcycles; i++) { NestData nd = { .n_enter = 0, .n_return = 0, .max = maxnesting, }; root = qemu_coroutine_create(nest); qemu_coroutine_enter(root, &nd); } duration = g_test_timer_elapsed(); g_test_message("Nesting %u iterations of %u depth each: %f s\n", maxcycles, maxnesting, duration); } /* * Yield benchmark */ static void coroutine_fn yield_loop(void *opaque) { unsigned int *counter = opaque; while ((*counter) > 0) { (*counter)--; qemu_coroutine_yield(); } }
void commit_start(BlockDriverState *bs, BlockDriverState *base, BlockDriverState *top, int64_t speed, BlockdevOnError on_error, BlockDriverCompletionFunc *cb, void *opaque, Error **errp) { CommitBlockJob *s; BlockReopenQueue *reopen_queue = NULL; int orig_overlay_flags; int orig_base_flags; BlockDriverState *overlay_bs; Error *local_err = NULL; if ((on_error == BLOCKDEV_ON_ERROR_STOP || on_error == BLOCKDEV_ON_ERROR_ENOSPC) && !bdrv_iostatus_is_enabled(bs)) { error_set(errp, QERR_INVALID_PARAMETER_COMBINATION); return; } /* Once we support top == active layer, remove this check */ if (top == bs) { error_setg(errp, "Top image as the active layer is currently unsupported"); return; } if (top == base) { error_setg(errp, "Invalid files for merge: top and base are the same"); return; } overlay_bs = bdrv_find_overlay(bs, top); if (overlay_bs == NULL) { error_setg(errp, "Could not find overlay image for %s:", top->filename); return; } orig_base_flags = bdrv_get_flags(base); orig_overlay_flags = bdrv_get_flags(overlay_bs); /* convert base & overlay_bs to r/w, if necessary */ if (!(orig_base_flags & BDRV_O_RDWR)) { reopen_queue = bdrv_reopen_queue(reopen_queue, base, orig_base_flags | BDRV_O_RDWR); } if (!(orig_overlay_flags & BDRV_O_RDWR)) { reopen_queue = bdrv_reopen_queue(reopen_queue, overlay_bs, orig_overlay_flags | BDRV_O_RDWR); } if (reopen_queue) { bdrv_reopen_multiple(reopen_queue, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; } } s = block_job_create(&commit_job_type, bs, speed, cb, opaque, errp); if (!s) { return; } s->base = base; s->top = top; s->active = bs; s->base_flags = orig_base_flags; s->orig_overlay_flags = orig_overlay_flags; s->on_error = on_error; s->common.co = qemu_coroutine_create(commit_run); trace_commit_start(bs, base, top, s, s->common.co, opaque); qemu_coroutine_enter(s->common.co, s); }
void commit_start(BlockDriverState *bs, BlockDriverState *base, BlockDriverState *top, int64_t speed, BlockErrorAction on_error, BlockDriverCompletionFunc *cb, void *opaque, Error **errp) { CommitBlockJob *s; BlockReopenQueue *reopen_queue = NULL; int orig_overlay_flags; int orig_base_flags; BlockDriverState *overlay_bs; Error *local_err = NULL; if ((on_error == BLOCK_ERR_STOP_ANY || on_error == BLOCK_ERR_STOP_ENOSPC) && !bdrv_iostatus_is_enabled(bs)) { error_set(errp, QERR_INVALID_PARAMETER_COMBINATION); return; } /* Once we support top == active layer, remove this check */ if (top == bs) { error_set(errp, QERR_TOP_IS_ACTIVE); return; } if (top == base) { error_set(errp, QERR_TOP_AND_BASE_IDENTICAL); return; } overlay_bs = bdrv_find_overlay(bs, top); if (overlay_bs == NULL) { error_set(errp, QERR_TOP_NOT_FOUND, top->filename); return; } orig_base_flags = bdrv_get_flags(base); orig_overlay_flags = bdrv_get_flags(overlay_bs); /* convert base & overlay_bs to r/w, if necessary */ if (!(orig_base_flags & BDRV_O_RDWR)) { reopen_queue = bdrv_reopen_queue(reopen_queue, base, orig_base_flags | BDRV_O_RDWR); } if (!(orig_overlay_flags & BDRV_O_RDWR)) { reopen_queue = bdrv_reopen_queue(reopen_queue, overlay_bs, orig_overlay_flags | BDRV_O_RDWR); } if (reopen_queue) { bdrv_reopen_multiple(reopen_queue, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; } } s = block_job_create(&commit_job_type, bs, speed, cb, opaque); if (!s) { error_set(errp, QERR_DEVICE_IN_USE, bs->device_name); return; } s->base = base; s->top = top; s->active = bs; s->base_flags = orig_base_flags; s->orig_overlay_flags = orig_overlay_flags; s->on_error = on_error; s->common.co = qemu_coroutine_create(commit_run); trace_commit_start(bs, base, top, s, s->common.co, opaque); qemu_coroutine_enter(s->common.co, s); }