hv: add priority based scheduler

This patch adds a new priority based scheduler to support
vCPU scheduling based on their pre-configured priorities.
A vCPU can be running only if there is no higher priority
vCPU running on the same pCPU.

Tracked-On: #6571
Signed-off-by: Jie Deng <jie.deng@intel.com>
This commit is contained in:
Jie Deng
2021-09-09 16:21:30 +08:00
committed by wenlingz
parent dfe49ee972
commit 064fd7647f
12 changed files with 149 additions and 2 deletions

View File

@@ -244,6 +244,9 @@ endif
ifeq ($(CONFIG_SCHED_BVT),y)
HW_C_SRCS += common/sched_bvt.c
endif
ifeq ($(CONFIG_SCHED_PRIO),y)
HW_C_SRCS += common/sched_prio.c
endif
HW_C_SRCS += hw/pci.c
HW_C_SRCS += arch/x86/configs/vm_config.c
HW_C_SRCS += boot/acpi_base.c

View File

@@ -963,6 +963,7 @@ int32_t prepare_vcpu(struct acrn_vm *vm, uint16_t pcpu_id)
vcpu->thread_obj.host_sp = build_stack_frame(vcpu);
vcpu->thread_obj.switch_out = context_switch_out;
vcpu->thread_obj.switch_in = context_switch_in;
vcpu->thread_obj.priority = get_vm_config(vm->vm_id)->vm_prio;
init_thread_data(&vcpu->thread_obj);
for (i = 0; i < VCPU_EVENT_NUM; i++) {
init_event(&vcpu->events[i]);

View File

@@ -105,6 +105,7 @@ void run_idle_thread(void)
idle->thread_entry = default_idle;
idle->switch_out = NULL;
idle->switch_in = NULL;
idle->priority = PRIO_IDLE;
run_thread(idle);

View File

@@ -0,0 +1,98 @@
/*
* Copyright (C) 2021 Intel Corporation.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <list.h>
#include <asm/per_cpu.h>
#include <schedule.h>
struct sched_prio_data {
/* keep list as the first item */
struct list_head list;
};
static int sched_prio_init(struct sched_control *ctl)
{
struct sched_prio_control *prio_ctl = &per_cpu(sched_prio_ctl, ctl->pcpu_id);
ASSERT(ctl->pcpu_id == get_pcpu_id(), "Init scheduler on wrong CPU!");
ctl->priv = prio_ctl;
INIT_LIST_HEAD(&prio_ctl->prio_queue);
return 0;
}
static void sched_prio_init_data(struct thread_object *obj)
{
struct sched_prio_data *data;
data = (struct sched_prio_data *)obj->data;
INIT_LIST_HEAD(&data->list);
}
static struct thread_object *sched_prio_pick_next(struct sched_control *ctl)
{
struct sched_prio_control *prio_ctl = (struct sched_prio_control *)ctl->priv;
struct thread_object *next = NULL;
if (!list_empty(&prio_ctl->prio_queue)) {
next = get_first_item(&prio_ctl->prio_queue, struct thread_object, data);
} else {
next = &get_cpu_var(idle);
}
return next;
}
static void prio_queue_add(struct thread_object *obj)
{
struct sched_prio_control *prio_ctl =
(struct sched_prio_control *)obj->sched_ctl->priv;
struct sched_prio_data *data = (struct sched_prio_data *)obj->data;
struct thread_object *iter_obj;
struct list_head *pos;
if (list_empty(&prio_ctl->prio_queue)) {
list_add(&data->list, &prio_ctl->prio_queue);
} else {
list_for_each(pos, &prio_ctl->prio_queue) {
iter_obj = container_of(pos, struct thread_object, data);
if (iter_obj->priority < obj->priority) {
list_add_node(&data->list, pos->prev, pos);
break;
}
}
if (list_empty(&data->list)) {
list_add_tail(&data->list, &prio_ctl->prio_queue);
}
}
}
static void prio_queue_remove(struct thread_object *obj)
{
struct sched_prio_data *data = (struct sched_prio_data *)obj->data;
list_del_init(&data->list);
}
static void sched_prio_sleep(struct thread_object *obj)
{
prio_queue_remove(obj);
}
static void sched_prio_wake(struct thread_object *obj)
{
prio_queue_add(obj);
}
struct acrn_scheduler sched_prio = {
.name = "sched_prio",
.init = sched_prio_init,
.init_data = sched_prio_init_data,
.pick_next = sched_prio_pick_next,
.sleep = sched_prio_sleep,
.wake = sched_prio_wake,
};

View File

@@ -77,6 +77,9 @@ void init_sched(uint16_t pcpu_id)
#endif
#ifdef CONFIG_SCHED_BVT
ctl->scheduler = &sched_bvt;
#endif
#ifdef CONFIG_SCHED_PRIO
ctl->scheduler = &sched_prio;
#endif
if (ctl->scheduler->init != NULL) {
ctl->scheduler->init(ctl);

View File

@@ -41,6 +41,7 @@ struct per_cpu_region {
struct sched_noop_control sched_noop_ctl;
struct sched_iorr_control sched_iorr_ctl;
struct sched_bvt_control sched_bvt_ctl;
struct sched_prio_control sched_prio_ctl;
struct thread_object idle;
struct host_gdt gdt;
struct tss_64 tss;

View File

@@ -169,6 +169,7 @@ struct acrn_vm_config {
* GUEST_FLAG_LAPIC_PASSTHROUGH
* We could add more guest flags in future;
*/
uint32_t vm_prio; /* The priority for VM vCPU scheduling */
struct acrn_vm_mem_config memory; /* memory configuration of VM */
struct epc_section epc; /* EPC memory configuration of VM */
uint16_t pci_dev_num; /* indicate how many PCI devices in VM */

View File

@@ -28,6 +28,14 @@ enum sched_notify_mode {
SCHED_NOTIFY_IPI
};
/* Tools can configure a VM to use PRIO_LOW or PRIO_HIGH */
enum thread_priority {
PRIO_IDLE = 0,
PRIO_LOW,
PRIO_HIGH,
PRIO_MAX
};
struct thread_object;
typedef void (*thread_entry_t)(struct thread_object *obj);
typedef void (*switch_t)(struct thread_object *obj);
@@ -44,6 +52,8 @@ struct thread_object {
switch_t switch_out;
switch_t switch_in;
int priority;
uint8_t data[THREAD_DATA_SIZE];
};
@@ -97,6 +107,11 @@ struct sched_bvt_control {
struct hv_timer tick_timer;
};
extern struct acrn_scheduler sched_prio;
struct sched_prio_control {
struct list_head prio_queue;
};
bool is_idle_thread(const struct thread_object *obj);
uint16_t sched_get_pcpuid(const struct thread_object *obj);
struct thread_object *sched_get_current(uint16_t pcpu_id);