[RFC PATCH] drm/i915/gvt: split sched_policy for adding more policies
hang.yuan at linux.intel.com
hang.yuan at linux.intel.com
Tue Dec 17 10:32:43 UTC 2019
From: Hang Yuan <hang.yuan at linux.intel.com>
Leave common policy codes in sched_policy.c and move time based
scheduling to new file sched_policy_weight.c. Add module parameter
"gvt_scheduler_policy" to choose one policy.
Signed-off-by: Hang Yuan <hang.yuan at linux.intel.com>
---
drivers/gpu/drm/i915/gvt/Makefile | 2 +-
drivers/gpu/drm/i915/gvt/gvt.c | 2 +-
drivers/gpu/drm/i915/gvt/gvt.h | 3 +-
drivers/gpu/drm/i915/gvt/sched_policy.c | 361 +------------------------
drivers/gpu/drm/i915/gvt/sched_policy.h | 1 +
drivers/gpu/drm/i915/gvt/sched_policy_weight.c | 351 ++++++++++++++++++++++++
drivers/gpu/drm/i915/gvt/sched_policy_weight.h | 32 +++
drivers/gpu/drm/i915/gvt/vgpu.c | 4 +-
drivers/gpu/drm/i915/i915_params.c | 2 +
drivers/gpu/drm/i915/i915_params.h | 3 +-
10 files changed, 403 insertions(+), 358 deletions(-)
create mode 100644 drivers/gpu/drm/i915/gvt/sched_policy_weight.c
create mode 100644 drivers/gpu/drm/i915/gvt/sched_policy_weight.h
diff --git a/drivers/gpu/drm/i915/gvt/Makefile b/drivers/gpu/drm/i915/gvt/Makefile
index ea8324a..f180aae 100644
--- a/drivers/gpu/drm/i915/gvt/Makefile
+++ b/drivers/gpu/drm/i915/gvt/Makefile
@@ -3,7 +3,7 @@ GVT_DIR := gvt
GVT_SOURCE := gvt.o aperture_gm.o handlers.o vgpu.o trace_points.o firmware.o \
interrupt.o gtt.o cfg_space.o opregion.o mmio.o display.o edid.o \
execlist.o scheduler.o sched_policy.o mmio_context.o cmd_parser.o debugfs.o \
- fb_decoder.o dmabuf.o page_track.o
+ fb_decoder.o dmabuf.o page_track.o sched_policy_weight.o
ccflags-y += -I $(srctree)/$(src) -I $(srctree)/$(src)/$(GVT_DIR)/
i915-y += $(addprefix $(GVT_DIR)/, $(GVT_SOURCE))
diff --git a/drivers/gpu/drm/i915/gvt/gvt.c b/drivers/gpu/drm/i915/gvt/gvt.c
index cb5fa30..0aeb692 100644
--- a/drivers/gpu/drm/i915/gvt/gvt.c
+++ b/drivers/gpu/drm/i915/gvt/gvt.c
@@ -228,7 +228,7 @@ static int gvt_service_thread(void *data)
(void *)&gvt->service_request) ||
test_bit(INTEL_GVT_REQUEST_EVENT_SCHED,
(void *)&gvt->service_request)) {
- intel_gvt_schedule(gvt);
+ gvt->scheduler.sched_ops->schedule(gvt);
}
}
diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h
index 0081b05..eb66901 100644
--- a/drivers/gpu/drm/i915/gvt/gvt.h
+++ b/drivers/gpu/drm/i915/gvt/gvt.h
@@ -44,6 +44,7 @@
#include "execlist.h"
#include "scheduler.h"
#include "sched_policy.h"
+#include "sched_policy_weight.h"
#include "mmio_context.h"
#include "cmd_parser.h"
#include "fb_decoder.h"
@@ -131,7 +132,7 @@ struct intel_vgpu_display {
};
struct vgpu_sched_ctl {
- int weight;
+ int value;
};
enum {
diff --git a/drivers/gpu/drm/i915/gvt/sched_policy.c b/drivers/gpu/drm/i915/gvt/sched_policy.c
index 2369d4a..530931a 100644
--- a/drivers/gpu/drm/i915/gvt/sched_policy.c
+++ b/drivers/gpu/drm/i915/gvt/sched_policy.c
@@ -34,355 +34,18 @@
#include "i915_drv.h"
#include "gvt.h"
-static bool vgpu_has_pending_workload(struct intel_vgpu *vgpu)
-{
- enum intel_engine_id i;
- struct intel_engine_cs *engine;
-
- for_each_engine(engine, vgpu->gvt->dev_priv, i) {
- if (!list_empty(workload_q_head(vgpu, i)))
- return true;
- }
-
- return false;
-}
-
-/* We give 2 seconds higher prio for vGPU during start */
-#define GVT_SCHED_VGPU_PRI_TIME 2
-
-struct vgpu_sched_data {
- struct list_head lru_list;
- struct intel_vgpu *vgpu;
- bool active;
- bool pri_sched;
- ktime_t pri_time;
- ktime_t sched_in_time;
- ktime_t sched_time;
- ktime_t left_ts;
- ktime_t allocated_ts;
-
- struct vgpu_sched_ctl sched_ctl;
-};
-
-struct gvt_sched_data {
- struct intel_gvt *gvt;
- struct hrtimer timer;
- unsigned long period;
- struct list_head lru_runq_head;
- ktime_t expire_time;
-};
-
-static void vgpu_update_timeslice(struct intel_vgpu *vgpu, ktime_t cur_time)
-{
- ktime_t delta_ts;
- struct vgpu_sched_data *vgpu_data;
-
- if (!vgpu || vgpu == vgpu->gvt->idle_vgpu)
- return;
-
- vgpu_data = vgpu->sched_data;
- delta_ts = ktime_sub(cur_time, vgpu_data->sched_in_time);
- vgpu_data->sched_time = ktime_add(vgpu_data->sched_time, delta_ts);
- vgpu_data->left_ts = ktime_sub(vgpu_data->left_ts, delta_ts);
- vgpu_data->sched_in_time = cur_time;
-}
-
-#define GVT_TS_BALANCE_PERIOD_MS 100
-#define GVT_TS_BALANCE_STAGE_NUM 10
-
-static void gvt_balance_timeslice(struct gvt_sched_data *sched_data)
-{
- struct vgpu_sched_data *vgpu_data;
- struct list_head *pos;
- static u64 stage_check;
- int stage = stage_check++ % GVT_TS_BALANCE_STAGE_NUM;
-
- /* The timeslice accumulation reset at stage 0, which is
- * allocated again without adding previous debt.
- */
- if (stage == 0) {
- int total_weight = 0;
- ktime_t fair_timeslice;
-
- list_for_each(pos, &sched_data->lru_runq_head) {
- vgpu_data = container_of(pos, struct vgpu_sched_data, lru_list);
- total_weight += vgpu_data->sched_ctl.weight;
- }
-
- list_for_each(pos, &sched_data->lru_runq_head) {
- vgpu_data = container_of(pos, struct vgpu_sched_data, lru_list);
- fair_timeslice = ktime_divns(ms_to_ktime(GVT_TS_BALANCE_PERIOD_MS),
- total_weight) * vgpu_data->sched_ctl.weight;
-
- vgpu_data->allocated_ts = fair_timeslice;
- vgpu_data->left_ts = vgpu_data->allocated_ts;
- }
- } else {
- list_for_each(pos, &sched_data->lru_runq_head) {
- vgpu_data = container_of(pos, struct vgpu_sched_data, lru_list);
-
- /* timeslice for next 100ms should add the left/debt
- * slice of previous stages.
- */
- vgpu_data->left_ts += vgpu_data->allocated_ts;
- }
- }
-}
-
-static void try_to_schedule_next_vgpu(struct intel_gvt *gvt)
-{
- struct intel_gvt_workload_scheduler *scheduler = &gvt->scheduler;
- enum intel_engine_id i;
- struct intel_engine_cs *engine;
- struct vgpu_sched_data *vgpu_data;
- ktime_t cur_time;
-
- /* no need to schedule if next_vgpu is the same with current_vgpu,
- * let scheduler chose next_vgpu again by setting it to NULL.
- */
- if (scheduler->next_vgpu == scheduler->current_vgpu) {
- scheduler->next_vgpu = NULL;
- return;
- }
-
- /*
- * after the flag is set, workload dispatch thread will
- * stop dispatching workload for current vgpu
- */
- scheduler->need_reschedule = true;
-
- /* still have uncompleted workload? */
- for_each_engine(engine, gvt->dev_priv, i) {
- if (scheduler->current_workload[i])
- return;
- }
-
- cur_time = ktime_get();
- vgpu_update_timeslice(scheduler->current_vgpu, cur_time);
- vgpu_data = scheduler->next_vgpu->sched_data;
- vgpu_data->sched_in_time = cur_time;
-
- /* switch current vgpu */
- scheduler->current_vgpu = scheduler->next_vgpu;
- scheduler->next_vgpu = NULL;
-
- scheduler->need_reschedule = false;
-
- /* wake up workload dispatch thread */
- for_each_engine(engine, gvt->dev_priv, i)
- wake_up(&scheduler->waitq[i]);
-}
-
-static struct intel_vgpu *find_busy_vgpu(struct gvt_sched_data *sched_data)
-{
- struct vgpu_sched_data *vgpu_data;
- struct intel_vgpu *vgpu = NULL;
- struct list_head *head = &sched_data->lru_runq_head;
- struct list_head *pos;
-
- /* search a vgpu with pending workload */
- list_for_each(pos, head) {
-
- vgpu_data = container_of(pos, struct vgpu_sched_data, lru_list);
- if (!vgpu_has_pending_workload(vgpu_data->vgpu))
- continue;
-
- if (vgpu_data->pri_sched) {
- if (ktime_before(ktime_get(), vgpu_data->pri_time)) {
- vgpu = vgpu_data->vgpu;
- break;
- } else
- vgpu_data->pri_sched = false;
- }
-
- /* Return the vGPU only if it has time slice left */
- if (vgpu_data->left_ts > 0) {
- vgpu = vgpu_data->vgpu;
- break;
- }
- }
-
- return vgpu;
-}
-
-/* in nanosecond */
-#define GVT_DEFAULT_TIME_SLICE 1000000
-
-static void tbs_sched_func(struct gvt_sched_data *sched_data)
+int intel_gvt_init_sched_policy(struct intel_gvt *gvt)
{
- struct intel_gvt *gvt = sched_data->gvt;
- struct intel_gvt_workload_scheduler *scheduler = &gvt->scheduler;
- struct vgpu_sched_data *vgpu_data;
- struct intel_vgpu *vgpu = NULL;
-
- /* no active vgpu or has already had a target */
- if (list_empty(&sched_data->lru_runq_head) || scheduler->next_vgpu)
- goto out;
+ int ret;
- vgpu = find_busy_vgpu(sched_data);
- if (vgpu) {
- scheduler->next_vgpu = vgpu;
- vgpu_data = vgpu->sched_data;
- if (!vgpu_data->pri_sched) {
- /* Move the last used vGPU to the tail of lru_list */
- list_del_init(&vgpu_data->lru_list);
- list_add_tail(&vgpu_data->lru_list,
- &sched_data->lru_runq_head);
- }
+ if (!strcmp(i915_modparams.gvt_scheduler_policy, "weight")) {
+ gvt->scheduler.sched_ops = &tbs_schedule_ops;
} else {
- scheduler->next_vgpu = gvt->idle_vgpu;
+ gvt_err("unknown gvt scheduler policy %s\n",
+ i915_modparams.gvt_scheduler_policy);
}
-out:
- if (scheduler->next_vgpu)
- try_to_schedule_next_vgpu(gvt);
-}
-
-void intel_gvt_schedule(struct intel_gvt *gvt)
-{
- struct gvt_sched_data *sched_data = gvt->scheduler.sched_data;
- ktime_t cur_time;
mutex_lock(&gvt->sched_lock);
- cur_time = ktime_get();
-
- if (test_and_clear_bit(INTEL_GVT_REQUEST_SCHED,
- (void *)&gvt->service_request)) {
- if (cur_time >= sched_data->expire_time) {
- gvt_balance_timeslice(sched_data);
- sched_data->expire_time = ktime_add_ms(
- cur_time, GVT_TS_BALANCE_PERIOD_MS);
- }
- }
- clear_bit(INTEL_GVT_REQUEST_EVENT_SCHED, (void *)&gvt->service_request);
-
- vgpu_update_timeslice(gvt->scheduler.current_vgpu, cur_time);
- tbs_sched_func(sched_data);
-
- mutex_unlock(&gvt->sched_lock);
-}
-
-static enum hrtimer_restart tbs_timer_fn(struct hrtimer *timer_data)
-{
- struct gvt_sched_data *data;
-
- data = container_of(timer_data, struct gvt_sched_data, timer);
-
- intel_gvt_request_service(data->gvt, INTEL_GVT_REQUEST_SCHED);
-
- hrtimer_add_expires_ns(&data->timer, data->period);
-
- return HRTIMER_RESTART;
-}
-
-static int tbs_sched_init(struct intel_gvt *gvt)
-{
- struct intel_gvt_workload_scheduler *scheduler =
- &gvt->scheduler;
-
- struct gvt_sched_data *data;
-
- data = kzalloc(sizeof(*data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- INIT_LIST_HEAD(&data->lru_runq_head);
- hrtimer_init(&data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
- data->timer.function = tbs_timer_fn;
- data->period = GVT_DEFAULT_TIME_SLICE;
- data->gvt = gvt;
-
- scheduler->sched_data = data;
-
- return 0;
-}
-
-static void tbs_sched_clean(struct intel_gvt *gvt)
-{
- struct intel_gvt_workload_scheduler *scheduler =
- &gvt->scheduler;
- struct gvt_sched_data *data = scheduler->sched_data;
-
- hrtimer_cancel(&data->timer);
-
- kfree(data);
- scheduler->sched_data = NULL;
-}
-
-static int tbs_sched_init_vgpu(struct intel_vgpu *vgpu)
-{
- struct vgpu_sched_data *data;
-
- data = kzalloc(sizeof(*data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- data->sched_ctl.weight = vgpu->sched_ctl.weight;
- data->vgpu = vgpu;
- INIT_LIST_HEAD(&data->lru_list);
-
- vgpu->sched_data = data;
-
- return 0;
-}
-
-static void tbs_sched_clean_vgpu(struct intel_vgpu *vgpu)
-{
- struct intel_gvt *gvt = vgpu->gvt;
- struct gvt_sched_data *sched_data = gvt->scheduler.sched_data;
-
- kfree(vgpu->sched_data);
- vgpu->sched_data = NULL;
-
- /* this vgpu id has been removed */
- if (idr_is_empty(&gvt->vgpu_idr))
- hrtimer_cancel(&sched_data->timer);
-}
-
-static void tbs_sched_start_schedule(struct intel_vgpu *vgpu)
-{
- struct gvt_sched_data *sched_data = vgpu->gvt->scheduler.sched_data;
- struct vgpu_sched_data *vgpu_data = vgpu->sched_data;
- ktime_t now;
-
- if (!list_empty(&vgpu_data->lru_list))
- return;
-
- now = ktime_get();
- vgpu_data->pri_time = ktime_add(now,
- ktime_set(GVT_SCHED_VGPU_PRI_TIME, 0));
- vgpu_data->pri_sched = true;
-
- list_add(&vgpu_data->lru_list, &sched_data->lru_runq_head);
-
- if (!hrtimer_active(&sched_data->timer))
- hrtimer_start(&sched_data->timer, ktime_add_ns(ktime_get(),
- sched_data->period), HRTIMER_MODE_ABS);
- vgpu_data->active = true;
-}
-
-static void tbs_sched_stop_schedule(struct intel_vgpu *vgpu)
-{
- struct vgpu_sched_data *vgpu_data = vgpu->sched_data;
-
- list_del_init(&vgpu_data->lru_list);
- vgpu_data->active = false;
-}
-
-static struct intel_gvt_sched_policy_ops tbs_schedule_ops = {
- .init = tbs_sched_init,
- .clean = tbs_sched_clean,
- .init_vgpu = tbs_sched_init_vgpu,
- .clean_vgpu = tbs_sched_clean_vgpu,
- .start_schedule = tbs_sched_start_schedule,
- .stop_schedule = tbs_sched_stop_schedule,
-};
-
-int intel_gvt_init_sched_policy(struct intel_gvt *gvt)
-{
- int ret;
-
- mutex_lock(&gvt->sched_lock);
- gvt->scheduler.sched_ops = &tbs_schedule_ops;
ret = gvt->scheduler.sched_ops->init(gvt);
mutex_unlock(&gvt->sched_lock);
@@ -423,13 +86,9 @@ void intel_vgpu_clean_sched_policy(struct intel_vgpu *vgpu)
void intel_vgpu_start_schedule(struct intel_vgpu *vgpu)
{
- struct vgpu_sched_data *vgpu_data = vgpu->sched_data;
-
mutex_lock(&vgpu->gvt->sched_lock);
- if (!vgpu_data->active) {
- gvt_dbg_core("vgpu%d: start schedule\n", vgpu->id);
- vgpu->gvt->scheduler.sched_ops->start_schedule(vgpu);
- }
+ gvt_dbg_core("vgpu%d: start schedule\n", vgpu->id);
+ vgpu->gvt->scheduler.sched_ops->start_schedule(vgpu);
mutex_unlock(&vgpu->gvt->sched_lock);
}
@@ -445,12 +104,8 @@ void intel_vgpu_stop_schedule(struct intel_vgpu *vgpu)
struct intel_gvt_workload_scheduler *scheduler =
&vgpu->gvt->scheduler;
int ring_id;
- struct vgpu_sched_data *vgpu_data = vgpu->sched_data;
struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv;
- if (!vgpu_data->active)
- return;
-
gvt_dbg_core("vgpu%d: stop schedule\n", vgpu->id);
mutex_lock(&vgpu->gvt->sched_lock);
diff --git a/drivers/gpu/drm/i915/gvt/sched_policy.h b/drivers/gpu/drm/i915/gvt/sched_policy.h
index 3dacdad..a4953c2 100644
--- a/drivers/gpu/drm/i915/gvt/sched_policy.h
+++ b/drivers/gpu/drm/i915/gvt/sched_policy.h
@@ -44,6 +44,7 @@ struct intel_gvt_sched_policy_ops {
void (*clean_vgpu)(struct intel_vgpu *vgpu);
void (*start_schedule)(struct intel_vgpu *vgpu);
void (*stop_schedule)(struct intel_vgpu *vgpu);
+ void (*schedule)(struct intel_gvt *gvt);
};
void intel_gvt_schedule(struct intel_gvt *gvt);
diff --git a/drivers/gpu/drm/i915/gvt/sched_policy_weight.c b/drivers/gpu/drm/i915/gvt/sched_policy_weight.c
new file mode 100644
index 0000000..8aa15f0
--- /dev/null
+++ b/drivers/gpu/drm/i915/gvt/sched_policy_weight.c
@@ -0,0 +1,351 @@
+/*
+ * Copyright(c) 2011-2016 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Anhua Xu
+ * Kevin Tian <kevin.tian at intel.com>
+ *
+ * Contributors:
+ * Min He <min.he at intel.com>
+ * Bing Niu <bing.niu at intel.com>
+ * Zhi Wang <zhi.a.wang at intel.com>
+ *
+ */
+
+#include "i915_drv.h"
+#include "gvt.h"
+
+static bool vgpu_has_pending_workload(struct intel_vgpu *vgpu)
+{
+ enum intel_engine_id i;
+ struct intel_engine_cs *engine;
+
+ for_each_engine(engine, vgpu->gvt->dev_priv, i) {
+ if (!list_empty(workload_q_head(vgpu, i)))
+ return true;
+ }
+
+ return false;
+}
+
+static void vgpu_update_timeslice(struct intel_vgpu *vgpu, ktime_t cur_time)
+{
+ ktime_t delta_ts;
+ struct vgpu_tbs_sched_data *vgpu_data;
+
+ if (!vgpu || vgpu == vgpu->gvt->idle_vgpu)
+ return;
+
+ vgpu_data = vgpu->sched_data;
+ delta_ts = ktime_sub(cur_time, vgpu_data->sched_in_time);
+ vgpu_data->sched_time = ktime_add(vgpu_data->sched_time, delta_ts);
+ vgpu_data->left_ts = ktime_sub(vgpu_data->left_ts, delta_ts);
+ vgpu_data->sched_in_time = cur_time;
+}
+
+static void gvt_balance_timeslice(struct gvt_tbs_sched_data *sched_data)
+{
+ struct vgpu_tbs_sched_data *vgpu_data;
+ struct list_head *pos;
+ static u64 stage_check;
+ int stage = stage_check++ % GVT_TS_BALANCE_STAGE_NUM;
+
+ /* The timeslice accumulation reset at stage 0, which is
+ * allocated again without adding previous debt.
+ */
+ if (stage == 0) {
+ int total_weight = 0;
+ ktime_t fair_timeslice;
+
+ list_for_each(pos, &sched_data->lru_runq_head) {
+ vgpu_data = container_of(pos, struct vgpu_tbs_sched_data, lru_list);
+ total_weight += vgpu_data->weight;
+ }
+
+ list_for_each(pos, &sched_data->lru_runq_head) {
+ vgpu_data = container_of(pos, struct vgpu_tbs_sched_data, lru_list);
+ fair_timeslice = ktime_divns(ms_to_ktime(GVT_TS_BALANCE_PERIOD_MS),
+ total_weight) * vgpu_data->weight;
+
+ vgpu_data->allocated_ts = fair_timeslice;
+ vgpu_data->left_ts = vgpu_data->allocated_ts;
+ }
+ } else {
+ list_for_each(pos, &sched_data->lru_runq_head) {
+ vgpu_data = container_of(pos, struct vgpu_tbs_sched_data, lru_list);
+
+ /* timeslice for next 100ms should add the left/debt
+ * slice of previous stages.
+ */
+ vgpu_data->left_ts += vgpu_data->allocated_ts;
+ }
+ }
+}
+
+static void try_to_schedule_next_vgpu(struct intel_gvt *gvt)
+{
+ struct intel_gvt_workload_scheduler *scheduler = &gvt->scheduler;
+ enum intel_engine_id i;
+ struct intel_engine_cs *engine;
+ struct vgpu_tbs_sched_data *vgpu_data;
+ ktime_t cur_time;
+
+ /* no need to schedule if next_vgpu is the same with current_vgpu,
+ * let scheduler chose next_vgpu again by setting it to NULL.
+ */
+ if (scheduler->next_vgpu == scheduler->current_vgpu) {
+ scheduler->next_vgpu = NULL;
+ return;
+ }
+
+ /*
+ * after the flag is set, workload dispatch thread will
+ * stop dispatching workload for current vgpu
+ */
+ scheduler->need_reschedule = true;
+
+ /* still have uncompleted workload? */
+ for_each_engine(engine, gvt->dev_priv, i) {
+ if (scheduler->current_workload[i])
+ return;
+ }
+
+ cur_time = ktime_get();
+ vgpu_update_timeslice(scheduler->current_vgpu, cur_time);
+ vgpu_data = scheduler->next_vgpu->sched_data;
+ vgpu_data->sched_in_time = cur_time;
+
+ /* switch current vgpu */
+ scheduler->current_vgpu = scheduler->next_vgpu;
+ scheduler->next_vgpu = NULL;
+
+ scheduler->need_reschedule = false;
+
+ /* wake up workload dispatch thread */
+ for_each_engine(engine, gvt->dev_priv, i)
+ wake_up(&scheduler->waitq[i]);
+}
+
+static struct intel_vgpu *find_busy_vgpu(struct gvt_tbs_sched_data *sched_data)
+{
+ struct vgpu_tbs_sched_data *vgpu_data;
+ struct intel_vgpu *vgpu = NULL;
+ struct list_head *head = &sched_data->lru_runq_head;
+ struct list_head *pos;
+
+ /* search a vgpu with pending workload */
+ list_for_each(pos, head) {
+
+ vgpu_data = container_of(pos, struct vgpu_tbs_sched_data, lru_list);
+ if (!vgpu_has_pending_workload(vgpu_data->vgpu))
+ continue;
+
+ if (vgpu_data->pri_sched) {
+ if (ktime_before(ktime_get(), vgpu_data->pri_time)) {
+ vgpu = vgpu_data->vgpu;
+ break;
+ } else
+ vgpu_data->pri_sched = false;
+ }
+
+ /* Return the vGPU only if it has time slice left */
+ if (vgpu_data->left_ts > 0) {
+ vgpu = vgpu_data->vgpu;
+ break;
+ }
+ }
+
+ return vgpu;
+}
+
+static void tbs_sched_func(struct gvt_tbs_sched_data *sched_data)
+{
+ struct intel_gvt *gvt = sched_data->gvt;
+ struct intel_gvt_workload_scheduler *scheduler = &gvt->scheduler;
+ struct vgpu_tbs_sched_data *vgpu_data;
+ struct intel_vgpu *vgpu = NULL;
+
+ /* no active vgpu or has already had a target */
+ if (list_empty(&sched_data->lru_runq_head) || scheduler->next_vgpu)
+ goto out;
+
+ vgpu = find_busy_vgpu(sched_data);
+ if (vgpu) {
+ scheduler->next_vgpu = vgpu;
+ vgpu_data = vgpu->sched_data;
+ if (!vgpu_data->pri_sched) {
+ /* Move the last used vGPU to the tail of lru_list */
+ list_del_init(&vgpu_data->lru_list);
+ list_add_tail(&vgpu_data->lru_list,
+ &sched_data->lru_runq_head);
+ }
+ } else {
+ scheduler->next_vgpu = gvt->idle_vgpu;
+ }
+out:
+ if (scheduler->next_vgpu)
+ try_to_schedule_next_vgpu(gvt);
+}
+
+static void tbs_sched_do_schedule(struct intel_gvt *gvt)
+{
+ struct gvt_tbs_sched_data *sched_data = gvt->scheduler.sched_data;
+ ktime_t cur_time;
+
+ mutex_lock(&gvt->sched_lock);
+ cur_time = ktime_get();
+
+ if (test_and_clear_bit(INTEL_GVT_REQUEST_SCHED,
+ (void *)&gvt->service_request)) {
+ if (cur_time >= sched_data->expire_time) {
+ gvt_balance_timeslice(sched_data);
+ sched_data->expire_time = ktime_add_ms(
+ cur_time, GVT_TS_BALANCE_PERIOD_MS);
+ }
+ }
+ clear_bit(INTEL_GVT_REQUEST_EVENT_SCHED, (void *)&gvt->service_request);
+
+ vgpu_update_timeslice(gvt->scheduler.current_vgpu, cur_time);
+ tbs_sched_func(sched_data);
+
+ mutex_unlock(&gvt->sched_lock);
+}
+
+static enum hrtimer_restart tbs_timer_fn(struct hrtimer *timer_data)
+{
+ struct gvt_tbs_sched_data *data;
+
+ data = container_of(timer_data, struct gvt_tbs_sched_data, timer);
+
+ intel_gvt_request_service(data->gvt, INTEL_GVT_REQUEST_SCHED);
+
+ hrtimer_add_expires_ns(&data->timer, data->period);
+
+ return HRTIMER_RESTART;
+}
+
+static int tbs_sched_init(struct intel_gvt *gvt)
+{
+ struct intel_gvt_workload_scheduler *scheduler =
+ &gvt->scheduler;
+
+ struct gvt_tbs_sched_data *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&data->lru_runq_head);
+ hrtimer_init(&data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+ data->timer.function = tbs_timer_fn;
+ data->period = GVT_DEFAULT_TIME_SLICE;
+ data->gvt = gvt;
+
+ scheduler->sched_data = data;
+
+ return 0;
+}
+
+static void tbs_sched_clean(struct intel_gvt *gvt)
+{
+ struct intel_gvt_workload_scheduler *scheduler =
+ &gvt->scheduler;
+ struct gvt_tbs_sched_data *data = scheduler->sched_data;
+
+ hrtimer_cancel(&data->timer);
+
+ kfree(data);
+ scheduler->sched_data = NULL;
+}
+
+static int tbs_sched_init_vgpu(struct intel_vgpu *vgpu)
+{
+ struct vgpu_tbs_sched_data *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->weight = vgpu->sched_ctl.value;
+ data->vgpu = vgpu;
+ INIT_LIST_HEAD(&data->lru_list);
+
+ vgpu->sched_data = data;
+
+ return 0;
+}
+
+static void tbs_sched_clean_vgpu(struct intel_vgpu *vgpu)
+{
+ struct intel_gvt *gvt = vgpu->gvt;
+ struct gvt_tbs_sched_data *sched_data = gvt->scheduler.sched_data;
+
+ kfree(vgpu->sched_data);
+ vgpu->sched_data = NULL;
+
+ /* this vgpu id has been removed */
+ if (idr_is_empty(&gvt->vgpu_idr))
+ hrtimer_cancel(&sched_data->timer);
+}
+
+static void tbs_sched_start_schedule(struct intel_vgpu *vgpu)
+{
+ struct gvt_tbs_sched_data *sched_data = vgpu->gvt->scheduler.sched_data;
+ struct vgpu_tbs_sched_data *vgpu_data = vgpu->sched_data;
+ ktime_t now;
+
+ if (!list_empty(&vgpu_data->lru_list) || vgpu_data->active)
+ return;
+
+ now = ktime_get();
+ vgpu_data->pri_time = ktime_add(now,
+ ktime_set(GVT_SCHED_VGPU_PRI_TIME, 0));
+ vgpu_data->pri_sched = true;
+
+ list_add(&vgpu_data->lru_list, &sched_data->lru_runq_head);
+
+ if (!hrtimer_active(&sched_data->timer))
+ hrtimer_start(&sched_data->timer, ktime_add_ns(ktime_get(),
+ sched_data->period), HRTIMER_MODE_ABS);
+ vgpu_data->active = true;
+}
+
+static void tbs_sched_stop_schedule(struct intel_vgpu *vgpu)
+{
+ struct vgpu_tbs_sched_data *vgpu_data = vgpu->sched_data;
+
+ if (!vgpu_data->active)
+ return;
+
+ list_del_init(&vgpu_data->lru_list);
+ vgpu_data->active = false;
+}
+
+struct intel_gvt_sched_policy_ops tbs_schedule_ops = {
+ .init = tbs_sched_init,
+ .clean = tbs_sched_clean,
+ .init_vgpu = tbs_sched_init_vgpu,
+ .clean_vgpu = tbs_sched_clean_vgpu,
+ .start_schedule = tbs_sched_start_schedule,
+ .stop_schedule = tbs_sched_stop_schedule,
+ .schedule = tbs_sched_do_schedule,
+};
diff --git a/drivers/gpu/drm/i915/gvt/sched_policy_weight.h b/drivers/gpu/drm/i915/gvt/sched_policy_weight.h
new file mode 100644
index 0000000..b32a71f
--- /dev/null
+++ b/drivers/gpu/drm/i915/gvt/sched_policy_weight.h
@@ -0,0 +1,32 @@
+/* We give 2 seconds higher prio for vGPU during start */
+#define GVT_SCHED_VGPU_PRI_TIME 2
+
+#define GVT_TS_BALANCE_PERIOD_MS 100
+#define GVT_TS_BALANCE_STAGE_NUM 10
+
+/* in nanosecond */
+#define GVT_DEFAULT_TIME_SLICE 1000000
+
+struct vgpu_tbs_sched_data {
+ struct list_head lru_list;
+ struct intel_vgpu *vgpu;
+ bool active;
+ bool pri_sched;
+ ktime_t pri_time;
+ ktime_t sched_in_time;
+ ktime_t sched_time;
+ ktime_t left_ts;
+ ktime_t allocated_ts;
+
+ int weight;
+};
+
+struct gvt_tbs_sched_data {
+ struct intel_gvt *gvt;
+ struct hrtimer timer;
+ unsigned long period;
+ struct list_head lru_runq_head;
+ ktime_t expire_time;
+};
+
+extern struct intel_gvt_sched_policy_ops tbs_schedule_ops;
diff --git a/drivers/gpu/drm/i915/gvt/vgpu.c b/drivers/gpu/drm/i915/gvt/vgpu.c
index 85bd9bf..b1b2deb 100644
--- a/drivers/gpu/drm/i915/gvt/vgpu.c
+++ b/drivers/gpu/drm/i915/gvt/vgpu.c
@@ -378,7 +378,9 @@ static struct intel_vgpu *__intel_gvt_create_vgpu(struct intel_gvt *gvt,
vgpu->id = ret;
vgpu->handle = param->handle;
vgpu->gvt = gvt;
- vgpu->sched_ctl.weight = param->weight;
+ if (!strcmp(i915_modparams.gvt_scheduler_policy, "weight")) {
+ vgpu->sched_ctl.value = param->weight;
+ }
mutex_init(&vgpu->vgpu_lock);
mutex_init(&vgpu->dmabuf_lock);
INIT_LIST_HEAD(&vgpu->dmabuf_obj_list_head);
diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c
index 1dd1f36..bb90bf8 100644
--- a/drivers/gpu/drm/i915/i915_params.c
+++ b/drivers/gpu/drm/i915/i915_params.c
@@ -177,6 +177,8 @@ i915_param_named(enable_dpcd_backlight, int, 0600,
#if IS_ENABLED(CONFIG_DRM_I915_GVT)
i915_param_named(enable_gvt, bool, 0400,
"Enable support for Intel GVT-g graphics virtualization host support(default:false)");
+i915_param_named(gvt_scheduler_policy, charp, 0400,
+ "Select GVT-g scheduler policy, weight (default: weight)");
#endif
#if IS_ENABLED(CONFIG_DRM_I915_UNSTABLE_FAKE_LMEM)
diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h
index 31b88f2..8c63c49 100644
--- a/drivers/gpu/drm/i915/i915_params.h
+++ b/drivers/gpu/drm/i915/i915_params.h
@@ -78,7 +78,8 @@ struct drm_printer;
param(bool, verbose_state_checks, true) \
param(bool, nuclear_pageflip, false) \
param(bool, enable_dp_mst, true) \
- param(bool, enable_gvt, false)
+ param(bool, enable_gvt, false) \
+ param(char *, gvt_scheduler_policy, "weight")
#define MEMBER(T, member, ...) T member;
struct i915_params {
--
2.7.4
More information about the intel-gvt-dev
mailing list