#include #include #include #include #include "uctx.h" __thread struct tf_fiber *tf_current_fiber; __thread struct tf_vcpu *tf_current_vcpu; __thread struct tf_vmach *tf_current_vmach; extern struct tf_poll_hooks tf_epoll_hooks; struct tf_vmachine { struct tf_vmach vmach; struct tf_uctx startup_uctx; void *machine_init_fiber; }; static void tf_vcpu_main(void *fiber_data) { struct tf_vcpu *vcpu = fiber_data; struct tf_vmach *vmach = vcpu->machine; struct tf_fiber *self = container_of(fiber_data, struct tf_fiber, data); struct tf_fiber *f; tf_current_vmach = vmach; tf_current_vcpu = vcpu; while (vmach->num_user_fibers != 0) { if (tf_list_empty(&vmach->run_q)) { /* sleep */ continue; } f = tf_list_entry(tf_list_pop(&vmach->run_q), struct tf_fiber, queue_node); f->return_context = self->context; tf_current_fiber = f; tf_uctx_transfer(self->context, f->context); tf_list_splice_tail(&f->wakeup_q, &vmach->run_q); } } static void tf_vmach_main(void *fiber_data) { struct tf_fiber *self = container_of(fiber_data, struct tf_fiber, data); struct tf_vcpu *vcpu = fiber_data; struct tf_vmach *vmach = vcpu->machine; tf_current_vmach = vmach; tf_current_vcpu = vcpu; tf_current_fiber = self; /* Initialize IO subsystem */ vmach->poll_ops = &tf_epoll_hooks; vmach->poll_fiber = vmach->poll_ops->create(); vmach->timeout_fiber = tf_timeout_fiber_create(); vmach->startup_fiber.timeout.manager = vmach->timeout_fiber; /* Run the initial fiber */ tf_fiber_wakeup(&vmach->startup_fiber); /* Use main thread as a regular vcpu */ vmach->num_user_fibers = 1; tf_list_splice_tail(&self->wakeup_q, &vmach->run_q); tf_vcpu_main(vcpu); /* Kill all stuff */ /* Return to main fiber */ vmach->startup_fiber.return_context = NULL; tf_current_fiber = NULL; tf_current_vcpu = NULL; tf_current_vmach = NULL; tf_uctx_transfer(self->context, vmach->startup_fiber.context); } void tf_vmach_start(void) { struct tf_vmachine *vmach; struct tf_vcpu *vcpu; TF_BUG_ON(tf_current_vcpu != NULL); /* Create a self-fiber so we can surrender control to vcpu */ vmach = calloc(1, sizeof(struct tf_vmachine)); vmach->vmach.startup_fiber = (struct tf_fiber) { .ref_count = 1, .queue_node = TF_LIST_INITIALIZER(vmach->vmach.startup_fiber.queue_node), .wakeup_q = TF_LIST_HEAD_INITIALIZER(vmach->vmach.startup_fiber.wakeup_q), .context = tf_uctx_create_self(&vmach->startup_uctx), }; tf_list_init_head(&vmach->vmach.run_q); vcpu = tf_fiber_create(tf_vmach_main, sizeof(struct tf_vcpu)); vmach->machine_init_fiber = vcpu; vcpu->machine = &vmach->vmach; /* Create manager fiber to initialize vcpu */ tf_uctx_transfer(vmach->vmach.startup_fiber.context, container_of((void *) vcpu, struct tf_fiber, data)->context); } void tf_vmach_stop(void) { struct tf_fiber *self = tf_vmach_get_current_fiber(); struct tf_vmach *vmach = tf_vmach_get_current(); TF_BUG_ON(self != &vmach->startup_fiber); /* Wait for the vmachine to stop */ tf_vmach_get_current()->num_user_fibers--; while (self->return_context != NULL) tf_fiber_schedule(); /* And clean up */ tf_uctx_destroy(vmach->startup_fiber.context); free(vmach); }