acrn-hypervisor/hypervisor/common/sched_bvt.c
Conghui Chen a38f2cc918 hv: sched_bvt: add wakeup and sleep handler
In the wakeup handler, the vcpu_thread object will be inserted into the
runqueue, and in the sleep handler, it will be removed from the queue.
vcpu_thread object is ordered by EVT (effective virtual time).

Tracked-On: #4410

Signed-off-by: Conghui Chen <conghui.chen@intel.com>
Signed-off-by: Shuo A Liu <shuo.a.liu@intel.com>
Acked-by: Eddie Dong <eddie.dong@intel.com>
2020-02-25 09:11:32 +08:00

199 lines
4.8 KiB
C

/*
* Copyright (C) 2020 Intel Corporation. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <list.h>
#include <per_cpu.h>
#include <schedule.h>
#define BVT_MCU_MS 1U
/* context switch allowance */
#define BVT_CSA_MCU 5U
struct sched_bvt_data {
/* keep list as the first item */
struct list_head list;
/* minimum charging unit in cycles */
uint64_t mcu;
/* a thread receives a share of cpu in proportion to its weight */
uint16_t weight;
/* virtual time advance variable, proportional to 1 / weight */
uint64_t vt_ratio;
/* the count down number of mcu until reschedule should take place */
uint64_t run_countdown;
/* actual virtual time in units of mcu */
int64_t avt;
/* effective virtual time in units of mcu */
int64_t evt;
uint64_t start_tsc;
};
/*
* @pre obj != NULL
* @pre obj->data != NULL
*/
static bool is_inqueue(struct thread_object *obj)
{
struct sched_bvt_data *data = (struct sched_bvt_data *)obj->data;
return !list_empty(&data->list);
}
/*
* @pre obj != NULL
* @pre obj->data != NULL
* @pre obj->sched_ctl != NULL
* @pre obj->sched_ctl->priv != NULL
*/
static void runqueue_add(struct thread_object *obj)
{
struct sched_bvt_control *bvt_ctl =
(struct sched_bvt_control *)obj->sched_ctl->priv;
struct sched_bvt_data *data = (struct sched_bvt_data *)obj->data;
struct list_head *pos;
struct thread_object *iter_obj;
struct sched_bvt_data *iter_data;
/*
* the earliest evt has highest priority,
* the runqueue is ordered by priority.
*/
if (list_empty(&bvt_ctl->runqueue)) {
list_add(&data->list, &bvt_ctl->runqueue);
} else {
list_for_each(pos, &bvt_ctl->runqueue) {
iter_obj = list_entry(pos, struct thread_object, data);
iter_data = (struct sched_bvt_data *)iter_obj->data;
if (iter_data->evt > data->evt) {
list_add_node(&data->list, pos->prev, pos);
break;
}
}
if (!is_inqueue(obj)) {
list_add_tail(&data->list, &bvt_ctl->runqueue);
}
}
}
/*
* @pre obj != NULL
* @pre obj->data != NULL
*/
static void runqueue_remove(struct thread_object *obj)
{
struct sched_bvt_data *data = (struct sched_bvt_data *)obj->data;
list_del_init(&data->list);
}
/*
* @brief Get the SVT (scheduler virtual time) which indicates the
* minimum AVT of any runnable threads.
* @pre obj != NULL
* @pre obj->data != NULL
* @pre obj->sched_ctl != NULL
* @pre obj->sched_ctl->priv != NULL
*/
static int64_t get_svt(struct thread_object *obj)
{
struct sched_bvt_control *bvt_ctl = (struct sched_bvt_control *)obj->sched_ctl->priv;
struct sched_bvt_data *obj_data;
struct thread_object *tmp_obj;
int64_t svt = 0;
if (!list_empty(&bvt_ctl->runqueue)) {
tmp_obj = get_first_item(&bvt_ctl->runqueue, struct thread_object, data);
obj_data = (struct sched_bvt_data *)tmp_obj->data;
svt = obj_data->avt;
}
return svt;
}
static void sched_tick_handler(__unused void *param)
{
}
/*
*@pre: ctl->pcpu_id == get_pcpu_id()
*/
static int sched_bvt_init(struct sched_control *ctl)
{
struct sched_bvt_control *bvt_ctl = &per_cpu(sched_bvt_ctl, ctl->pcpu_id);
uint64_t tick_period = BVT_MCU_MS * CYCLES_PER_MS;
int ret = 0;
ASSERT(ctl->pcpu_id == get_pcpu_id(), "Init scheduler on wrong CPU!");
ctl->priv = bvt_ctl;
INIT_LIST_HEAD(&bvt_ctl->runqueue);
/* The tick_timer is periodically */
initialize_timer(&bvt_ctl->tick_timer, sched_tick_handler, ctl,
rdtsc() + tick_period, TICK_MODE_PERIODIC, tick_period);
if (add_timer(&bvt_ctl->tick_timer) < 0) {
pr_err("Failed to add schedule tick timer!");
ret = -1;
}
return ret;
}
static void sched_bvt_deinit(struct sched_control *ctl)
{
struct sched_bvt_control *bvt_ctl = (struct sched_bvt_control *)ctl->priv;
del_timer(&bvt_ctl->tick_timer);
}
static void sched_bvt_init_data(struct thread_object *obj)
{
struct sched_bvt_data *data;
data = (struct sched_bvt_data *)obj->data;
INIT_LIST_HEAD(&data->list);
data->mcu = BVT_MCU_MS * CYCLES_PER_MS;
/* TODO: virtual time advance ratio should be proportional to weight. */
data->vt_ratio = 1U;
data->run_countdown = BVT_CSA_MCU;
}
static struct thread_object *sched_bvt_pick_next(__unused struct sched_control *ctl)
{
return NULL;
}
static void sched_bvt_sleep(struct thread_object *obj)
{
runqueue_remove(obj);
}
static void sched_bvt_wake(struct thread_object *obj)
{
struct sched_bvt_data *data;
int64_t svt, threshold;
data = (struct sched_bvt_data *)obj->data;
svt = get_svt(obj);
threshold = svt - BVT_CSA_MCU;
/* adjusting AVT for a thread after a long sleep */
data->avt = (data->avt > threshold) ? data->avt : svt;
/* TODO: evt = avt - (warp ? warpback : 0U) */
data->evt = data->avt;
/* add to runqueue in order */
runqueue_add(obj);
}
struct acrn_scheduler sched_bvt = {
.name = "sched_bvt",
.init = sched_bvt_init,
.init_data = sched_bvt_init_data,
.pick_next = sched_bvt_pick_next,
.sleep = sched_bvt_sleep,
.wake = sched_bvt_wake,
.deinit = sched_bvt_deinit,
};