18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright(c) 2011-2016 Intel Corporation. All rights reserved.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation
78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next
128c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
138c2ecf20Sopenharmony_ci * Software.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
168c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
178c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
188c2ecf20Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
198c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
208c2ecf20Sopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
218c2ecf20Sopenharmony_ci * SOFTWARE.
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * Authors:
248c2ecf20Sopenharmony_ci *    Anhua Xu
258c2ecf20Sopenharmony_ci *    Kevin Tian <kevin.tian@intel.com>
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci * Contributors:
288c2ecf20Sopenharmony_ci *    Min He <min.he@intel.com>
298c2ecf20Sopenharmony_ci *    Bing Niu <bing.niu@intel.com>
308c2ecf20Sopenharmony_ci *    Zhi Wang <zhi.a.wang@intel.com>
318c2ecf20Sopenharmony_ci *
328c2ecf20Sopenharmony_ci */
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include "i915_drv.h"
358c2ecf20Sopenharmony_ci#include "gvt.h"
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic bool vgpu_has_pending_workload(struct intel_vgpu *vgpu)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	enum intel_engine_id i;
408c2ecf20Sopenharmony_ci	struct intel_engine_cs *engine;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	for_each_engine(engine, vgpu->gvt->gt, i) {
438c2ecf20Sopenharmony_ci		if (!list_empty(workload_q_head(vgpu, engine)))
448c2ecf20Sopenharmony_ci			return true;
458c2ecf20Sopenharmony_ci	}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	return false;
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/* We give 2 seconds higher prio for vGPU during start */
518c2ecf20Sopenharmony_ci#define GVT_SCHED_VGPU_PRI_TIME  2
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistruct vgpu_sched_data {
548c2ecf20Sopenharmony_ci	struct list_head lru_list;
558c2ecf20Sopenharmony_ci	struct intel_vgpu *vgpu;
568c2ecf20Sopenharmony_ci	bool active;
578c2ecf20Sopenharmony_ci	bool pri_sched;
588c2ecf20Sopenharmony_ci	ktime_t pri_time;
598c2ecf20Sopenharmony_ci	ktime_t sched_in_time;
608c2ecf20Sopenharmony_ci	ktime_t sched_time;
618c2ecf20Sopenharmony_ci	ktime_t left_ts;
628c2ecf20Sopenharmony_ci	ktime_t allocated_ts;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	struct vgpu_sched_ctl sched_ctl;
658c2ecf20Sopenharmony_ci};
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistruct gvt_sched_data {
688c2ecf20Sopenharmony_ci	struct intel_gvt *gvt;
698c2ecf20Sopenharmony_ci	struct hrtimer timer;
708c2ecf20Sopenharmony_ci	unsigned long period;
718c2ecf20Sopenharmony_ci	struct list_head lru_runq_head;
728c2ecf20Sopenharmony_ci	ktime_t expire_time;
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic void vgpu_update_timeslice(struct intel_vgpu *vgpu, ktime_t cur_time)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	ktime_t delta_ts;
788c2ecf20Sopenharmony_ci	struct vgpu_sched_data *vgpu_data;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	if (!vgpu || vgpu == vgpu->gvt->idle_vgpu)
818c2ecf20Sopenharmony_ci		return;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	vgpu_data = vgpu->sched_data;
848c2ecf20Sopenharmony_ci	delta_ts = ktime_sub(cur_time, vgpu_data->sched_in_time);
858c2ecf20Sopenharmony_ci	vgpu_data->sched_time = ktime_add(vgpu_data->sched_time, delta_ts);
868c2ecf20Sopenharmony_ci	vgpu_data->left_ts = ktime_sub(vgpu_data->left_ts, delta_ts);
878c2ecf20Sopenharmony_ci	vgpu_data->sched_in_time = cur_time;
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci#define GVT_TS_BALANCE_PERIOD_MS 100
918c2ecf20Sopenharmony_ci#define GVT_TS_BALANCE_STAGE_NUM 10
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic void gvt_balance_timeslice(struct gvt_sched_data *sched_data)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	struct vgpu_sched_data *vgpu_data;
968c2ecf20Sopenharmony_ci	struct list_head *pos;
978c2ecf20Sopenharmony_ci	static u64 stage_check;
988c2ecf20Sopenharmony_ci	int stage = stage_check++ % GVT_TS_BALANCE_STAGE_NUM;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	/* The timeslice accumulation reset at stage 0, which is
1018c2ecf20Sopenharmony_ci	 * allocated again without adding previous debt.
1028c2ecf20Sopenharmony_ci	 */
1038c2ecf20Sopenharmony_ci	if (stage == 0) {
1048c2ecf20Sopenharmony_ci		int total_weight = 0;
1058c2ecf20Sopenharmony_ci		ktime_t fair_timeslice;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci		list_for_each(pos, &sched_data->lru_runq_head) {
1088c2ecf20Sopenharmony_ci			vgpu_data = container_of(pos, struct vgpu_sched_data, lru_list);
1098c2ecf20Sopenharmony_ci			total_weight += vgpu_data->sched_ctl.weight;
1108c2ecf20Sopenharmony_ci		}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci		list_for_each(pos, &sched_data->lru_runq_head) {
1138c2ecf20Sopenharmony_ci			vgpu_data = container_of(pos, struct vgpu_sched_data, lru_list);
1148c2ecf20Sopenharmony_ci			fair_timeslice = ktime_divns(ms_to_ktime(GVT_TS_BALANCE_PERIOD_MS),
1158c2ecf20Sopenharmony_ci						     total_weight) * vgpu_data->sched_ctl.weight;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci			vgpu_data->allocated_ts = fair_timeslice;
1188c2ecf20Sopenharmony_ci			vgpu_data->left_ts = vgpu_data->allocated_ts;
1198c2ecf20Sopenharmony_ci		}
1208c2ecf20Sopenharmony_ci	} else {
1218c2ecf20Sopenharmony_ci		list_for_each(pos, &sched_data->lru_runq_head) {
1228c2ecf20Sopenharmony_ci			vgpu_data = container_of(pos, struct vgpu_sched_data, lru_list);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci			/* timeslice for next 100ms should add the left/debt
1258c2ecf20Sopenharmony_ci			 * slice of previous stages.
1268c2ecf20Sopenharmony_ci			 */
1278c2ecf20Sopenharmony_ci			vgpu_data->left_ts += vgpu_data->allocated_ts;
1288c2ecf20Sopenharmony_ci		}
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic void try_to_schedule_next_vgpu(struct intel_gvt *gvt)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	struct intel_gvt_workload_scheduler *scheduler = &gvt->scheduler;
1358c2ecf20Sopenharmony_ci	enum intel_engine_id i;
1368c2ecf20Sopenharmony_ci	struct intel_engine_cs *engine;
1378c2ecf20Sopenharmony_ci	struct vgpu_sched_data *vgpu_data;
1388c2ecf20Sopenharmony_ci	ktime_t cur_time;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	/* no need to schedule if next_vgpu is the same with current_vgpu,
1418c2ecf20Sopenharmony_ci	 * let scheduler chose next_vgpu again by setting it to NULL.
1428c2ecf20Sopenharmony_ci	 */
1438c2ecf20Sopenharmony_ci	if (scheduler->next_vgpu == scheduler->current_vgpu) {
1448c2ecf20Sopenharmony_ci		scheduler->next_vgpu = NULL;
1458c2ecf20Sopenharmony_ci		return;
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	/*
1498c2ecf20Sopenharmony_ci	 * after the flag is set, workload dispatch thread will
1508c2ecf20Sopenharmony_ci	 * stop dispatching workload for current vgpu
1518c2ecf20Sopenharmony_ci	 */
1528c2ecf20Sopenharmony_ci	scheduler->need_reschedule = true;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	/* still have uncompleted workload? */
1558c2ecf20Sopenharmony_ci	for_each_engine(engine, gvt->gt, i) {
1568c2ecf20Sopenharmony_ci		if (scheduler->current_workload[engine->id])
1578c2ecf20Sopenharmony_ci			return;
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	cur_time = ktime_get();
1618c2ecf20Sopenharmony_ci	vgpu_update_timeslice(scheduler->current_vgpu, cur_time);
1628c2ecf20Sopenharmony_ci	vgpu_data = scheduler->next_vgpu->sched_data;
1638c2ecf20Sopenharmony_ci	vgpu_data->sched_in_time = cur_time;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	/* switch current vgpu */
1668c2ecf20Sopenharmony_ci	scheduler->current_vgpu = scheduler->next_vgpu;
1678c2ecf20Sopenharmony_ci	scheduler->next_vgpu = NULL;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	scheduler->need_reschedule = false;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	/* wake up workload dispatch thread */
1728c2ecf20Sopenharmony_ci	for_each_engine(engine, gvt->gt, i)
1738c2ecf20Sopenharmony_ci		wake_up(&scheduler->waitq[engine->id]);
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic struct intel_vgpu *find_busy_vgpu(struct gvt_sched_data *sched_data)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	struct vgpu_sched_data *vgpu_data;
1798c2ecf20Sopenharmony_ci	struct intel_vgpu *vgpu = NULL;
1808c2ecf20Sopenharmony_ci	struct list_head *head = &sched_data->lru_runq_head;
1818c2ecf20Sopenharmony_ci	struct list_head *pos;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	/* search a vgpu with pending workload */
1848c2ecf20Sopenharmony_ci	list_for_each(pos, head) {
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci		vgpu_data = container_of(pos, struct vgpu_sched_data, lru_list);
1878c2ecf20Sopenharmony_ci		if (!vgpu_has_pending_workload(vgpu_data->vgpu))
1888c2ecf20Sopenharmony_ci			continue;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci		if (vgpu_data->pri_sched) {
1918c2ecf20Sopenharmony_ci			if (ktime_before(ktime_get(), vgpu_data->pri_time)) {
1928c2ecf20Sopenharmony_ci				vgpu = vgpu_data->vgpu;
1938c2ecf20Sopenharmony_ci				break;
1948c2ecf20Sopenharmony_ci			} else
1958c2ecf20Sopenharmony_ci				vgpu_data->pri_sched = false;
1968c2ecf20Sopenharmony_ci		}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci		/* Return the vGPU only if it has time slice left */
1998c2ecf20Sopenharmony_ci		if (vgpu_data->left_ts > 0) {
2008c2ecf20Sopenharmony_ci			vgpu = vgpu_data->vgpu;
2018c2ecf20Sopenharmony_ci			break;
2028c2ecf20Sopenharmony_ci		}
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	return vgpu;
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci/* in nanosecond */
2098c2ecf20Sopenharmony_ci#define GVT_DEFAULT_TIME_SLICE 1000000
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic void tbs_sched_func(struct gvt_sched_data *sched_data)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	struct intel_gvt *gvt = sched_data->gvt;
2148c2ecf20Sopenharmony_ci	struct intel_gvt_workload_scheduler *scheduler = &gvt->scheduler;
2158c2ecf20Sopenharmony_ci	struct vgpu_sched_data *vgpu_data;
2168c2ecf20Sopenharmony_ci	struct intel_vgpu *vgpu = NULL;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	/* no active vgpu or has already had a target */
2198c2ecf20Sopenharmony_ci	if (list_empty(&sched_data->lru_runq_head) || scheduler->next_vgpu)
2208c2ecf20Sopenharmony_ci		goto out;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	vgpu = find_busy_vgpu(sched_data);
2238c2ecf20Sopenharmony_ci	if (vgpu) {
2248c2ecf20Sopenharmony_ci		scheduler->next_vgpu = vgpu;
2258c2ecf20Sopenharmony_ci		vgpu_data = vgpu->sched_data;
2268c2ecf20Sopenharmony_ci		if (!vgpu_data->pri_sched) {
2278c2ecf20Sopenharmony_ci			/* Move the last used vGPU to the tail of lru_list */
2288c2ecf20Sopenharmony_ci			list_del_init(&vgpu_data->lru_list);
2298c2ecf20Sopenharmony_ci			list_add_tail(&vgpu_data->lru_list,
2308c2ecf20Sopenharmony_ci				      &sched_data->lru_runq_head);
2318c2ecf20Sopenharmony_ci		}
2328c2ecf20Sopenharmony_ci	} else {
2338c2ecf20Sopenharmony_ci		scheduler->next_vgpu = gvt->idle_vgpu;
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ciout:
2368c2ecf20Sopenharmony_ci	if (scheduler->next_vgpu)
2378c2ecf20Sopenharmony_ci		try_to_schedule_next_vgpu(gvt);
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_civoid intel_gvt_schedule(struct intel_gvt *gvt)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	struct gvt_sched_data *sched_data = gvt->scheduler.sched_data;
2438c2ecf20Sopenharmony_ci	ktime_t cur_time;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	mutex_lock(&gvt->sched_lock);
2468c2ecf20Sopenharmony_ci	cur_time = ktime_get();
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	if (test_and_clear_bit(INTEL_GVT_REQUEST_SCHED,
2498c2ecf20Sopenharmony_ci				(void *)&gvt->service_request)) {
2508c2ecf20Sopenharmony_ci		if (cur_time >= sched_data->expire_time) {
2518c2ecf20Sopenharmony_ci			gvt_balance_timeslice(sched_data);
2528c2ecf20Sopenharmony_ci			sched_data->expire_time = ktime_add_ms(
2538c2ecf20Sopenharmony_ci				cur_time, GVT_TS_BALANCE_PERIOD_MS);
2548c2ecf20Sopenharmony_ci		}
2558c2ecf20Sopenharmony_ci	}
2568c2ecf20Sopenharmony_ci	clear_bit(INTEL_GVT_REQUEST_EVENT_SCHED, (void *)&gvt->service_request);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	vgpu_update_timeslice(gvt->scheduler.current_vgpu, cur_time);
2598c2ecf20Sopenharmony_ci	tbs_sched_func(sched_data);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	mutex_unlock(&gvt->sched_lock);
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic enum hrtimer_restart tbs_timer_fn(struct hrtimer *timer_data)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	struct gvt_sched_data *data;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	data = container_of(timer_data, struct gvt_sched_data, timer);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	intel_gvt_request_service(data->gvt, INTEL_GVT_REQUEST_SCHED);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	hrtimer_add_expires_ns(&data->timer, data->period);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	return HRTIMER_RESTART;
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic int tbs_sched_init(struct intel_gvt *gvt)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	struct intel_gvt_workload_scheduler *scheduler =
2808c2ecf20Sopenharmony_ci		&gvt->scheduler;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	struct gvt_sched_data *data;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	data = kzalloc(sizeof(*data), GFP_KERNEL);
2858c2ecf20Sopenharmony_ci	if (!data)
2868c2ecf20Sopenharmony_ci		return -ENOMEM;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&data->lru_runq_head);
2898c2ecf20Sopenharmony_ci	hrtimer_init(&data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
2908c2ecf20Sopenharmony_ci	data->timer.function = tbs_timer_fn;
2918c2ecf20Sopenharmony_ci	data->period = GVT_DEFAULT_TIME_SLICE;
2928c2ecf20Sopenharmony_ci	data->gvt = gvt;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	scheduler->sched_data = data;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	return 0;
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic void tbs_sched_clean(struct intel_gvt *gvt)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	struct intel_gvt_workload_scheduler *scheduler =
3028c2ecf20Sopenharmony_ci		&gvt->scheduler;
3038c2ecf20Sopenharmony_ci	struct gvt_sched_data *data = scheduler->sched_data;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	hrtimer_cancel(&data->timer);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	kfree(data);
3088c2ecf20Sopenharmony_ci	scheduler->sched_data = NULL;
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic int tbs_sched_init_vgpu(struct intel_vgpu *vgpu)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	struct vgpu_sched_data *data;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	data = kzalloc(sizeof(*data), GFP_KERNEL);
3168c2ecf20Sopenharmony_ci	if (!data)
3178c2ecf20Sopenharmony_ci		return -ENOMEM;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	data->sched_ctl.weight = vgpu->sched_ctl.weight;
3208c2ecf20Sopenharmony_ci	data->vgpu = vgpu;
3218c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&data->lru_list);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	vgpu->sched_data = data;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	return 0;
3268c2ecf20Sopenharmony_ci}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic void tbs_sched_clean_vgpu(struct intel_vgpu *vgpu)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	struct intel_gvt *gvt = vgpu->gvt;
3318c2ecf20Sopenharmony_ci	struct gvt_sched_data *sched_data = gvt->scheduler.sched_data;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	kfree(vgpu->sched_data);
3348c2ecf20Sopenharmony_ci	vgpu->sched_data = NULL;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/* this vgpu id has been removed */
3378c2ecf20Sopenharmony_ci	if (idr_is_empty(&gvt->vgpu_idr))
3388c2ecf20Sopenharmony_ci		hrtimer_cancel(&sched_data->timer);
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cistatic void tbs_sched_start_schedule(struct intel_vgpu *vgpu)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	struct gvt_sched_data *sched_data = vgpu->gvt->scheduler.sched_data;
3448c2ecf20Sopenharmony_ci	struct vgpu_sched_data *vgpu_data = vgpu->sched_data;
3458c2ecf20Sopenharmony_ci	ktime_t now;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (!list_empty(&vgpu_data->lru_list))
3488c2ecf20Sopenharmony_ci		return;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	now = ktime_get();
3518c2ecf20Sopenharmony_ci	vgpu_data->pri_time = ktime_add(now,
3528c2ecf20Sopenharmony_ci					ktime_set(GVT_SCHED_VGPU_PRI_TIME, 0));
3538c2ecf20Sopenharmony_ci	vgpu_data->pri_sched = true;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	list_add(&vgpu_data->lru_list, &sched_data->lru_runq_head);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	if (!hrtimer_active(&sched_data->timer))
3588c2ecf20Sopenharmony_ci		hrtimer_start(&sched_data->timer, ktime_add_ns(ktime_get(),
3598c2ecf20Sopenharmony_ci			sched_data->period), HRTIMER_MODE_ABS);
3608c2ecf20Sopenharmony_ci	vgpu_data->active = true;
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cistatic void tbs_sched_stop_schedule(struct intel_vgpu *vgpu)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	struct vgpu_sched_data *vgpu_data = vgpu->sched_data;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	list_del_init(&vgpu_data->lru_list);
3688c2ecf20Sopenharmony_ci	vgpu_data->active = false;
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic struct intel_gvt_sched_policy_ops tbs_schedule_ops = {
3728c2ecf20Sopenharmony_ci	.init = tbs_sched_init,
3738c2ecf20Sopenharmony_ci	.clean = tbs_sched_clean,
3748c2ecf20Sopenharmony_ci	.init_vgpu = tbs_sched_init_vgpu,
3758c2ecf20Sopenharmony_ci	.clean_vgpu = tbs_sched_clean_vgpu,
3768c2ecf20Sopenharmony_ci	.start_schedule = tbs_sched_start_schedule,
3778c2ecf20Sopenharmony_ci	.stop_schedule = tbs_sched_stop_schedule,
3788c2ecf20Sopenharmony_ci};
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ciint intel_gvt_init_sched_policy(struct intel_gvt *gvt)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	int ret;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	mutex_lock(&gvt->sched_lock);
3858c2ecf20Sopenharmony_ci	gvt->scheduler.sched_ops = &tbs_schedule_ops;
3868c2ecf20Sopenharmony_ci	ret = gvt->scheduler.sched_ops->init(gvt);
3878c2ecf20Sopenharmony_ci	mutex_unlock(&gvt->sched_lock);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	return ret;
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_civoid intel_gvt_clean_sched_policy(struct intel_gvt *gvt)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	mutex_lock(&gvt->sched_lock);
3958c2ecf20Sopenharmony_ci	gvt->scheduler.sched_ops->clean(gvt);
3968c2ecf20Sopenharmony_ci	mutex_unlock(&gvt->sched_lock);
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci/* for per-vgpu scheduler policy, there are 2 per-vgpu data:
4008c2ecf20Sopenharmony_ci * sched_data, and sched_ctl. We see these 2 data as part of
4018c2ecf20Sopenharmony_ci * the global scheduler which are proteced by gvt->sched_lock.
4028c2ecf20Sopenharmony_ci * Caller should make their decision if the vgpu_lock should
4038c2ecf20Sopenharmony_ci * be hold outside.
4048c2ecf20Sopenharmony_ci */
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ciint intel_vgpu_init_sched_policy(struct intel_vgpu *vgpu)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	int ret;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	mutex_lock(&vgpu->gvt->sched_lock);
4118c2ecf20Sopenharmony_ci	ret = vgpu->gvt->scheduler.sched_ops->init_vgpu(vgpu);
4128c2ecf20Sopenharmony_ci	mutex_unlock(&vgpu->gvt->sched_lock);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	return ret;
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_civoid intel_vgpu_clean_sched_policy(struct intel_vgpu *vgpu)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	mutex_lock(&vgpu->gvt->sched_lock);
4208c2ecf20Sopenharmony_ci	vgpu->gvt->scheduler.sched_ops->clean_vgpu(vgpu);
4218c2ecf20Sopenharmony_ci	mutex_unlock(&vgpu->gvt->sched_lock);
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_civoid intel_vgpu_start_schedule(struct intel_vgpu *vgpu)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	struct vgpu_sched_data *vgpu_data = vgpu->sched_data;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	mutex_lock(&vgpu->gvt->sched_lock);
4298c2ecf20Sopenharmony_ci	if (!vgpu_data->active) {
4308c2ecf20Sopenharmony_ci		gvt_dbg_core("vgpu%d: start schedule\n", vgpu->id);
4318c2ecf20Sopenharmony_ci		vgpu->gvt->scheduler.sched_ops->start_schedule(vgpu);
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci	mutex_unlock(&vgpu->gvt->sched_lock);
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_civoid intel_gvt_kick_schedule(struct intel_gvt *gvt)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	mutex_lock(&gvt->sched_lock);
4398c2ecf20Sopenharmony_ci	intel_gvt_request_service(gvt, INTEL_GVT_REQUEST_EVENT_SCHED);
4408c2ecf20Sopenharmony_ci	mutex_unlock(&gvt->sched_lock);
4418c2ecf20Sopenharmony_ci}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_civoid intel_vgpu_stop_schedule(struct intel_vgpu *vgpu)
4448c2ecf20Sopenharmony_ci{
4458c2ecf20Sopenharmony_ci	struct intel_gvt_workload_scheduler *scheduler =
4468c2ecf20Sopenharmony_ci		&vgpu->gvt->scheduler;
4478c2ecf20Sopenharmony_ci	struct vgpu_sched_data *vgpu_data = vgpu->sched_data;
4488c2ecf20Sopenharmony_ci	struct drm_i915_private *dev_priv = vgpu->gvt->gt->i915;
4498c2ecf20Sopenharmony_ci	struct intel_engine_cs *engine;
4508c2ecf20Sopenharmony_ci	enum intel_engine_id id;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	if (!vgpu_data->active)
4538c2ecf20Sopenharmony_ci		return;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	gvt_dbg_core("vgpu%d: stop schedule\n", vgpu->id);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	mutex_lock(&vgpu->gvt->sched_lock);
4588c2ecf20Sopenharmony_ci	scheduler->sched_ops->stop_schedule(vgpu);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	if (scheduler->next_vgpu == vgpu)
4618c2ecf20Sopenharmony_ci		scheduler->next_vgpu = NULL;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	if (scheduler->current_vgpu == vgpu) {
4648c2ecf20Sopenharmony_ci		/* stop workload dispatching */
4658c2ecf20Sopenharmony_ci		scheduler->need_reschedule = true;
4668c2ecf20Sopenharmony_ci		scheduler->current_vgpu = NULL;
4678c2ecf20Sopenharmony_ci	}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	intel_runtime_pm_get(&dev_priv->runtime_pm);
4708c2ecf20Sopenharmony_ci	spin_lock_bh(&scheduler->mmio_context_lock);
4718c2ecf20Sopenharmony_ci	for_each_engine(engine, vgpu->gvt->gt, id) {
4728c2ecf20Sopenharmony_ci		if (scheduler->engine_owner[engine->id] == vgpu) {
4738c2ecf20Sopenharmony_ci			intel_gvt_switch_mmio(vgpu, NULL, engine);
4748c2ecf20Sopenharmony_ci			scheduler->engine_owner[engine->id] = NULL;
4758c2ecf20Sopenharmony_ci		}
4768c2ecf20Sopenharmony_ci	}
4778c2ecf20Sopenharmony_ci	spin_unlock_bh(&scheduler->mmio_context_lock);
4788c2ecf20Sopenharmony_ci	intel_runtime_pm_put_unchecked(&dev_priv->runtime_pm);
4798c2ecf20Sopenharmony_ci	mutex_unlock(&vgpu->gvt->sched_lock);
4808c2ecf20Sopenharmony_ci}
481