362 lines
7 KiB
C
362 lines
7 KiB
C
#include <errno.h>
|
|
#include <pthread.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include "mylock.h"
|
|
#include "netio.h"
|
|
#include "utils.h"
|
|
#include "vtaskmgr.h"
|
|
|
|
static int bind_vtask(vtask_t*, vcpu_t*);
|
|
|
|
vtask_t* vtask_alloc()
|
|
{
|
|
vtask_t* task;
|
|
|
|
task = calloc(1, sizeof(*task));
|
|
return task;
|
|
}
|
|
|
|
int vtask_init(vtask_t* task,
|
|
vtask_func_t entry,
|
|
endpoint_t* ep)
|
|
{
|
|
int rc;
|
|
|
|
task->entry = entry;
|
|
task->vcpu = -1;
|
|
task->ctx.ep = endpoint_ref(ep);
|
|
|
|
rc = read_packet(ep->fd, &(task->ctx.pkt));
|
|
if (rc < 0) {
|
|
free_packet(&(task->ctx.pkt));
|
|
endpoint_deref(task->ctx.ep);
|
|
task->ctx.ep = NULL;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
void vtask_fini(vtask_t* task)
|
|
{
|
|
if (task == NULL) {
|
|
return;
|
|
}
|
|
|
|
ioctx_deref(&(task->ctx), 0);
|
|
|
|
return;
|
|
}
|
|
|
|
void vcpu_idle(vcpu_t* vcpu)
|
|
{
|
|
vtaskmgr_t* vtaskmgr = (vtaskmgr_t*)vcpu->owner;
|
|
|
|
vcpu->task = NULL;
|
|
cbuf_put(&(vtaskmgr->wbuf),
|
|
(void*)&(vcpu->cpuid));
|
|
cbuf_get(&(vcpu->wbuf),
|
|
(void*)&(vcpu->task));
|
|
return;
|
|
}
|
|
|
|
void run_task(vtask_t* task)
|
|
{
|
|
vtaskmgr_t *vtaskmgr;
|
|
|
|
if (task->entry != NULL) {
|
|
(task->entry)(task);
|
|
}
|
|
|
|
endpoint_unblock_tasks(task->ctx.ep);
|
|
vtaskmgr = (vtaskmgr_t *)task->owner;
|
|
if (vtaskmgr != NULL) {
|
|
/* wake up dispatcher */
|
|
vtaskmgr_wakeup(vtaskmgr);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void* vcpu_entry(void* arg)
|
|
{
|
|
vcpu_t* my = (vcpu_t*)arg;
|
|
|
|
while (1) {
|
|
if (my->task != NULL) {
|
|
vtask_t* t = my->task;
|
|
|
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
|
|
|
|
my->task = NULL;
|
|
run_task(t);
|
|
vtask_fini(t);
|
|
free(t);
|
|
my->state = VCPU_IDLE;
|
|
|
|
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
|
|
} else {
|
|
vcpu_idle(my);
|
|
}
|
|
pthread_testcancel();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int vcpu_init(vcpu_t* vcpu,
|
|
int cpuid,
|
|
void* owner)
|
|
{
|
|
vcpu->state = VCPU_IDLE;
|
|
vcpu->vcpu = 0;
|
|
vcpu->cpuid = cpuid;
|
|
vcpu->owner = owner;
|
|
vcpu->task = NULL;
|
|
cbuf_init(&(vcpu->wbuf), 1, sizeof(void*));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int vcpu_start(vcpu_t* vcpu)
|
|
{
|
|
int rc;
|
|
|
|
rc = pthread_create(&(vcpu->vcpu), NULL,
|
|
&vcpu_entry, vcpu);
|
|
if (rc != 0) {
|
|
vcpu->state = VCPU_ERR;
|
|
return -errno;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
void vcpu_fini(vcpu_t* vcpu)
|
|
{
|
|
int rc;
|
|
void* res;
|
|
|
|
if (vcpu->vcpu) {
|
|
/* wakeup vcpu first */
|
|
bind_vtask(NULL, vcpu);
|
|
rc = pthread_cancel(vcpu->vcpu);
|
|
if (rc != 0) {
|
|
cbuf_fini(&(vcpu->wbuf));
|
|
return;
|
|
}
|
|
|
|
rc = pthread_join(vcpu->vcpu, &res);
|
|
if (rc != 0) {
|
|
cbuf_fini(&(vcpu->wbuf));
|
|
return;
|
|
}
|
|
}
|
|
|
|
cbuf_fini(&(vcpu->wbuf));
|
|
|
|
return;
|
|
}
|
|
|
|
static void taskq_item_killer(data_item_t* it)
|
|
{
|
|
if (it != NULL) {
|
|
vtask_t* task = ITPTR_TO_VTASK(it);
|
|
vtask_fini(task);
|
|
free(task);
|
|
}
|
|
}
|
|
|
|
int vtaskmgr_init(vtaskmgr_t* vtaskmgr,
|
|
int nvcpus)
|
|
{
|
|
int i;
|
|
|
|
if ((vtaskmgr == NULL) || (nvcpus <= 0)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
vtaskmgr->dispatcher = 0;
|
|
vtaskmgr->n_vcpus = nvcpus;
|
|
vtaskmgr->vcpus = (vcpu_t*)calloc(nvcpus,
|
|
sizeof(vcpu_t));
|
|
if (vtaskmgr->vcpus == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
for (i = 0; i < nvcpus; i++) {
|
|
vcpu_init(&(vtaskmgr->vcpus[i]),
|
|
i, vtaskmgr);
|
|
}
|
|
|
|
cbuf_init(&(vtaskmgr->wbuf),
|
|
nvcpus, sizeof(int));
|
|
cbuf_init(&(vtaskmgr->wtbuf),
|
|
1, sizeof(int));
|
|
|
|
dataq_init(&(vtaskmgr->taskq),
|
|
taskq_item_killer);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static vcpu_t* pick_idle_vcpu(vtaskmgr_t* vtaskmgr)
|
|
{
|
|
int i;
|
|
|
|
cbuf_get(&(vtaskmgr->wbuf), &i);
|
|
if ((i < 0) || (i >= vtaskmgr->n_vcpus)) {
|
|
return NULL;
|
|
}
|
|
|
|
TRACE("%s pick idle cpu #%d",
|
|
__func__, i);
|
|
|
|
return &(vtaskmgr->vcpus[i]);
|
|
}
|
|
|
|
static int vtask_eligible(data_item_t* data)
|
|
{
|
|
vtask_t* task = ITPTR_TO_VTASK(data);
|
|
int eligible = 0;
|
|
|
|
if (task->ctx.ep == NULL) {
|
|
WARN("no endpoint for task 0x%p", task);
|
|
return 0;
|
|
}
|
|
|
|
eligible = !(endpoint_is_task_blocked(task->ctx.ep));
|
|
TRACE("%s vtask %p is %s",
|
|
__func__, task,
|
|
eligible?"eligible":"not eligible");
|
|
|
|
return eligible;
|
|
}
|
|
|
|
static vtask_t* pick_eligible_task(vtaskmgr_t* vtaskmgr)
|
|
{
|
|
data_item_t* data;
|
|
vtask_t* task = NULL;
|
|
|
|
data = dataq_deq(&(vtaskmgr->taskq), vtask_eligible);
|
|
if (data == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
task = ITPTR_TO_VTASK(data);
|
|
|
|
return task;
|
|
}
|
|
|
|
static int bind_vtask(vtask_t* task,
|
|
vcpu_t* vcpu)
|
|
{
|
|
TRACE("binding task %p to cpu#%d\n",
|
|
task, vcpu->cpuid);
|
|
|
|
if (task != NULL) {
|
|
endpoint_block_tasks(task->ctx.ep);
|
|
|
|
task->vcpu = vcpu->cpuid;
|
|
}
|
|
|
|
cbuf_put(&(vcpu->wbuf), (void*)&task);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void* vdispatcher_entry(void* arg)
|
|
{
|
|
vtaskmgr_t* vtaskmgr = (vtaskmgr_t*)arg;
|
|
vcpu_t* vcpu = NULL;
|
|
vtask_t* task = NULL;
|
|
|
|
while (1) {
|
|
pthread_testcancel();
|
|
if (vcpu == NULL) {
|
|
vcpu = pick_idle_vcpu(vtaskmgr);
|
|
if (vcpu == NULL) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
task = pick_eligible_task(vtaskmgr);
|
|
if (task != NULL) {
|
|
bind_vtask(task, vcpu);
|
|
vcpu = NULL;
|
|
task = NULL;
|
|
}
|
|
else {
|
|
vtaskmgr_wait(vtaskmgr);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int vtaskmgr_start(vtaskmgr_t* vtaskmgr)
|
|
{
|
|
int i, rc;
|
|
|
|
rc = pthread_create(&(vtaskmgr->dispatcher), NULL,
|
|
&vdispatcher_entry, vtaskmgr);
|
|
if (rc != 0) {
|
|
return -errno;
|
|
}
|
|
|
|
for (i = 0; i < vtaskmgr->n_vcpus; i++) {
|
|
vcpu_start(&(vtaskmgr->vcpus[i]));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void vtaskmgr_wait(vtaskmgr_t *vtaskmgr) {
|
|
int dummy = 0;
|
|
|
|
cbuf_get(&(vtaskmgr->wtbuf), &dummy);
|
|
}
|
|
|
|
void vtaskmgr_wakeup(vtaskmgr_t *vtaskmgr) {
|
|
int dummy = 0;
|
|
|
|
cbuf_tryput(&(vtaskmgr->wtbuf), &dummy);
|
|
}
|
|
|
|
void vtaskmgr_fini(vtaskmgr_t* vtaskmgr)
|
|
{
|
|
int i;
|
|
void* res;
|
|
|
|
if (vtaskmgr->dispatcher != 0) {
|
|
pthread_cancel(vtaskmgr->dispatcher);
|
|
pthread_join(vtaskmgr->dispatcher, &res);
|
|
}
|
|
|
|
if (vtaskmgr->vcpus != NULL) {
|
|
for (i = 0; i < vtaskmgr->n_vcpus; i++) {
|
|
vcpu_fini(&(vtaskmgr->vcpus[i]));
|
|
}
|
|
free(vtaskmgr->vcpus);
|
|
}
|
|
|
|
dataq_fini(&(vtaskmgr->taskq));
|
|
cbuf_fini(&(vtaskmgr->wtbuf));
|
|
cbuf_fini(&(vtaskmgr->wbuf));
|
|
|
|
return;
|
|
}
|
|
|
|
int vtask_enq(vtaskmgr_t* vtaskmgr,
|
|
vtask_t* task)
|
|
{
|
|
int rc = 0;
|
|
ioctx_ref(&(task->ctx));
|
|
|
|
rc = dataq_enq(&(vtaskmgr->taskq),
|
|
&(task->ctx.link));
|
|
if (rc >= 0) {
|
|
/* wake up task dispatcher */
|
|
task->owner = vtaskmgr;
|
|
vtaskmgr_wakeup(vtaskmgr);
|
|
}
|
|
|
|
return rc;
|
|
}
|