blob: f8495917067486e1ca0c124c187a9351e09c49a4 [file] [log] [blame]
Zhi Wang4b639602016-05-01 17:09:58 -04001/*
2 * Copyright(c) 2011-2016 Intel Corporation. All rights reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *
23 * Authors:
24 * Anhua Xu
25 * Kevin Tian <kevin.tian@intel.com>
26 *
27 * Contributors:
28 * Min He <min.he@intel.com>
29 * Bing Niu <bing.niu@intel.com>
30 * Zhi Wang <zhi.a.wang@intel.com>
31 *
32 */
33
34#include "i915_drv.h"
Zhenyu Wangfeddf6e2016-10-20 17:15:03 +080035#include "gvt.h"
Zhi Wang4b639602016-05-01 17:09:58 -040036
37static bool vgpu_has_pending_workload(struct intel_vgpu *vgpu)
38{
Zhenyu Wang0fac21e2016-10-20 13:30:33 +080039 enum intel_engine_id i;
40 struct intel_engine_cs *engine;
Zhi Wang4b639602016-05-01 17:09:58 -040041
Zhenyu Wang0fac21e2016-10-20 13:30:33 +080042 for_each_engine(engine, vgpu->gvt->dev_priv, i) {
Zhi Wang4b639602016-05-01 17:09:58 -040043 if (!list_empty(workload_q_head(vgpu, i)))
44 return true;
45 }
46
47 return false;
48}
49
Ping Gaof6504cc2017-03-30 00:36:35 +080050struct vgpu_sched_data {
Ping Gao32356922017-03-30 00:36:36 +080051 struct list_head lru_list;
Ping Gaof6504cc2017-03-30 00:36:35 +080052 struct intel_vgpu *vgpu;
53
54 ktime_t sched_in_time;
55 ktime_t sched_out_time;
56 ktime_t sched_time;
57 ktime_t left_ts;
58 ktime_t allocated_ts;
59
60 struct vgpu_sched_ctl sched_ctl;
61};
62
63struct gvt_sched_data {
64 struct intel_gvt *gvt;
65 struct hrtimer timer;
66 unsigned long period;
Ping Gao32356922017-03-30 00:36:36 +080067 struct list_head lru_runq_head;
Ping Gaof6504cc2017-03-30 00:36:35 +080068};
69
Ping Gao39d467c2017-03-30 10:03:24 +080070static void vgpu_update_timeslice(struct intel_vgpu *pre_vgpu)
71{
72 ktime_t delta_ts;
73 struct vgpu_sched_data *vgpu_data = pre_vgpu->sched_data;
74
75 delta_ts = vgpu_data->sched_out_time - vgpu_data->sched_in_time;
76
77 vgpu_data->sched_time += delta_ts;
78 vgpu_data->left_ts -= delta_ts;
79}
80
81#define GVT_TS_BALANCE_PERIOD_MS 100
82#define GVT_TS_BALANCE_STAGE_NUM 10
83
84static void gvt_balance_timeslice(struct gvt_sched_data *sched_data)
85{
86 struct vgpu_sched_data *vgpu_data;
87 struct list_head *pos;
88 static uint64_t stage_check;
89 int stage = stage_check++ % GVT_TS_BALANCE_STAGE_NUM;
90
91 /* The timeslice accumulation reset at stage 0, which is
92 * allocated again without adding previous debt.
93 */
94 if (stage == 0) {
95 int total_weight = 0;
96 ktime_t fair_timeslice;
97
98 list_for_each(pos, &sched_data->lru_runq_head) {
99 vgpu_data = container_of(pos, struct vgpu_sched_data, lru_list);
100 total_weight += vgpu_data->sched_ctl.weight;
101 }
102
103 list_for_each(pos, &sched_data->lru_runq_head) {
104 vgpu_data = container_of(pos, struct vgpu_sched_data, lru_list);
105 fair_timeslice = ms_to_ktime(GVT_TS_BALANCE_PERIOD_MS) *
106 vgpu_data->sched_ctl.weight /
107 total_weight;
108
109 vgpu_data->allocated_ts = fair_timeslice;
110 vgpu_data->left_ts = vgpu_data->allocated_ts;
111 }
112 } else {
113 list_for_each(pos, &sched_data->lru_runq_head) {
114 vgpu_data = container_of(pos, struct vgpu_sched_data, lru_list);
115
116 /* timeslice for next 100ms should add the left/debt
117 * slice of previous stages.
118 */
119 vgpu_data->left_ts += vgpu_data->allocated_ts;
120 }
121 }
122}
123
Zhi Wang4b639602016-05-01 17:09:58 -0400124static void try_to_schedule_next_vgpu(struct intel_gvt *gvt)
125{
126 struct intel_gvt_workload_scheduler *scheduler = &gvt->scheduler;
Zhenyu Wang0fac21e2016-10-20 13:30:33 +0800127 enum intel_engine_id i;
128 struct intel_engine_cs *engine;
Ping Gaof6504cc2017-03-30 00:36:35 +0800129 struct vgpu_sched_data *vgpu_data;
130 ktime_t cur_time;
Zhi Wang4b639602016-05-01 17:09:58 -0400131
132 /* no target to schedule */
133 if (!scheduler->next_vgpu)
134 return;
135
136 gvt_dbg_sched("try to schedule next vgpu %d\n",
137 scheduler->next_vgpu->id);
138
139 /*
140 * after the flag is set, workload dispatch thread will
141 * stop dispatching workload for current vgpu
142 */
143 scheduler->need_reschedule = true;
144
145 /* still have uncompleted workload? */
Zhenyu Wang0fac21e2016-10-20 13:30:33 +0800146 for_each_engine(engine, gvt->dev_priv, i) {
Zhi Wang4b639602016-05-01 17:09:58 -0400147 if (scheduler->current_workload[i]) {
148 gvt_dbg_sched("still have running workload\n");
149 return;
150 }
151 }
152
153 gvt_dbg_sched("switch to next vgpu %d\n",
154 scheduler->next_vgpu->id);
155
Ping Gaof6504cc2017-03-30 00:36:35 +0800156 cur_time = ktime_get();
157 if (scheduler->current_vgpu) {
158 vgpu_data = scheduler->current_vgpu->sched_data;
159 vgpu_data->sched_out_time = cur_time;
Ping Gao39d467c2017-03-30 10:03:24 +0800160 vgpu_update_timeslice(scheduler->current_vgpu);
Ping Gaof6504cc2017-03-30 00:36:35 +0800161 }
162 vgpu_data = scheduler->next_vgpu->sched_data;
163 vgpu_data->sched_in_time = cur_time;
164
Zhi Wang4b639602016-05-01 17:09:58 -0400165 /* switch current vgpu */
166 scheduler->current_vgpu = scheduler->next_vgpu;
167 scheduler->next_vgpu = NULL;
168
169 scheduler->need_reschedule = false;
170
171 /* wake up workload dispatch thread */
Zhenyu Wang0fac21e2016-10-20 13:30:33 +0800172 for_each_engine(engine, gvt->dev_priv, i)
Zhi Wang4b639602016-05-01 17:09:58 -0400173 wake_up(&scheduler->waitq[i]);
174}
175
Ping Gao32356922017-03-30 00:36:36 +0800176static struct intel_vgpu *find_busy_vgpu(struct gvt_sched_data *sched_data)
Zhi Wang4b639602016-05-01 17:09:58 -0400177{
Ping Gaof6504cc2017-03-30 00:36:35 +0800178 struct vgpu_sched_data *vgpu_data;
Zhi Wang4b639602016-05-01 17:09:58 -0400179 struct intel_vgpu *vgpu = NULL;
Ping Gao32356922017-03-30 00:36:36 +0800180 struct list_head *head = &sched_data->lru_runq_head;
181 struct list_head *pos;
Zhi Wang4b639602016-05-01 17:09:58 -0400182
183 /* search a vgpu with pending workload */
184 list_for_each(pos, head) {
Zhi Wang4b639602016-05-01 17:09:58 -0400185
Ping Gao32356922017-03-30 00:36:36 +0800186 vgpu_data = container_of(pos, struct vgpu_sched_data, lru_list);
Zhi Wang4b639602016-05-01 17:09:58 -0400187 if (!vgpu_has_pending_workload(vgpu_data->vgpu))
188 continue;
189
Ping Gaob35f34d2017-03-30 00:36:40 +0800190 /* Return the vGPU only if it has time slice left */
191 if (vgpu_data->left_ts > 0) {
192 vgpu = vgpu_data->vgpu;
193 break;
194 }
Zhi Wang4b639602016-05-01 17:09:58 -0400195 }
196
Ping Gao32356922017-03-30 00:36:36 +0800197 return vgpu;
198}
199
200/* in nanosecond */
201#define GVT_DEFAULT_TIME_SLICE 1000000
202
203static void tbs_sched_func(struct gvt_sched_data *sched_data)
204{
205 struct intel_gvt *gvt = sched_data->gvt;
206 struct intel_gvt_workload_scheduler *scheduler = &gvt->scheduler;
207 struct vgpu_sched_data *vgpu_data;
208 struct intel_vgpu *vgpu = NULL;
Ping Gao39d467c2017-03-30 10:03:24 +0800209 static uint64_t timer_check;
210
211 if (!(timer_check++ % GVT_TS_BALANCE_PERIOD_MS))
212 gvt_balance_timeslice(sched_data);
Ping Gao32356922017-03-30 00:36:36 +0800213
214 /* no active vgpu or has already had a target */
215 if (list_empty(&sched_data->lru_runq_head) || scheduler->next_vgpu)
216 goto out;
217
218 vgpu = find_busy_vgpu(sched_data);
Zhi Wang4b639602016-05-01 17:09:58 -0400219 if (vgpu) {
220 scheduler->next_vgpu = vgpu;
Ping Gao32356922017-03-30 00:36:36 +0800221
222 /* Move the last used vGPU to the tail of lru_list */
223 vgpu_data = vgpu->sched_data;
224 list_del_init(&vgpu_data->lru_list);
225 list_add_tail(&vgpu_data->lru_list,
226 &sched_data->lru_runq_head);
227
Zhi Wang4b639602016-05-01 17:09:58 -0400228 gvt_dbg_sched("pick next vgpu %d\n", vgpu->id);
Ping Gaob35f34d2017-03-30 00:36:40 +0800229 } else {
230 scheduler->next_vgpu = gvt->idle_vgpu;
Zhi Wang4b639602016-05-01 17:09:58 -0400231 }
232out:
233 if (scheduler->next_vgpu) {
234 gvt_dbg_sched("try to schedule next vgpu %d\n",
235 scheduler->next_vgpu->id);
236 try_to_schedule_next_vgpu(gvt);
237 }
Ping Gao91d01012017-03-30 00:36:34 +0800238}
Zhi Wang4b639602016-05-01 17:09:58 -0400239
Ping Gao91d01012017-03-30 00:36:34 +0800240void intel_gvt_schedule(struct intel_gvt *gvt)
241{
Ping Gaof6504cc2017-03-30 00:36:35 +0800242 struct gvt_sched_data *sched_data = gvt->scheduler.sched_data;
Zhi Wang4b639602016-05-01 17:09:58 -0400243
Ping Gao91d01012017-03-30 00:36:34 +0800244 mutex_lock(&gvt->lock);
245 tbs_sched_func(sched_data);
Zhi Wang4b639602016-05-01 17:09:58 -0400246 mutex_unlock(&gvt->lock);
247}
248
Ping Gao91d01012017-03-30 00:36:34 +0800249static enum hrtimer_restart tbs_timer_fn(struct hrtimer *timer_data)
250{
Ping Gaof6504cc2017-03-30 00:36:35 +0800251 struct gvt_sched_data *data;
Ping Gao91d01012017-03-30 00:36:34 +0800252
Ping Gaof6504cc2017-03-30 00:36:35 +0800253 data = container_of(timer_data, struct gvt_sched_data, timer);
Ping Gao91d01012017-03-30 00:36:34 +0800254
255 intel_gvt_request_service(data->gvt, INTEL_GVT_REQUEST_SCHED);
256
257 hrtimer_add_expires_ns(&data->timer, data->period);
258
259 return HRTIMER_RESTART;
260}
261
Zhi Wang4b639602016-05-01 17:09:58 -0400262static int tbs_sched_init(struct intel_gvt *gvt)
263{
264 struct intel_gvt_workload_scheduler *scheduler =
265 &gvt->scheduler;
266
Ping Gaof6504cc2017-03-30 00:36:35 +0800267 struct gvt_sched_data *data;
Zhi Wang4b639602016-05-01 17:09:58 -0400268
269 data = kzalloc(sizeof(*data), GFP_KERNEL);
270 if (!data)
271 return -ENOMEM;
272
Ping Gao32356922017-03-30 00:36:36 +0800273 INIT_LIST_HEAD(&data->lru_runq_head);
Ping Gao91d01012017-03-30 00:36:34 +0800274 hrtimer_init(&data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
275 data->timer.function = tbs_timer_fn;
Zhi Wang4b639602016-05-01 17:09:58 -0400276 data->period = GVT_DEFAULT_TIME_SLICE;
277 data->gvt = gvt;
278
279 scheduler->sched_data = data;
Ping Gao91d01012017-03-30 00:36:34 +0800280
Zhi Wang4b639602016-05-01 17:09:58 -0400281 return 0;
282}
283
284static void tbs_sched_clean(struct intel_gvt *gvt)
285{
286 struct intel_gvt_workload_scheduler *scheduler =
287 &gvt->scheduler;
Ping Gaof6504cc2017-03-30 00:36:35 +0800288 struct gvt_sched_data *data = scheduler->sched_data;
Zhi Wang4b639602016-05-01 17:09:58 -0400289
Ping Gao91d01012017-03-30 00:36:34 +0800290 hrtimer_cancel(&data->timer);
291
Zhi Wang4b639602016-05-01 17:09:58 -0400292 kfree(data);
293 scheduler->sched_data = NULL;
294}
295
296static int tbs_sched_init_vgpu(struct intel_vgpu *vgpu)
297{
Ping Gaof6504cc2017-03-30 00:36:35 +0800298 struct vgpu_sched_data *data;
Zhi Wang4b639602016-05-01 17:09:58 -0400299
300 data = kzalloc(sizeof(*data), GFP_KERNEL);
301 if (!data)
302 return -ENOMEM;
303
Ping Gaobc90d092017-03-30 00:36:37 +0800304 data->sched_ctl.weight = vgpu->sched_ctl.weight;
Zhi Wang4b639602016-05-01 17:09:58 -0400305 data->vgpu = vgpu;
Ping Gao32356922017-03-30 00:36:36 +0800306 INIT_LIST_HEAD(&data->lru_list);
Zhi Wang4b639602016-05-01 17:09:58 -0400307
308 vgpu->sched_data = data;
Ping Gao91d01012017-03-30 00:36:34 +0800309
Zhi Wang4b639602016-05-01 17:09:58 -0400310 return 0;
311}
312
313static void tbs_sched_clean_vgpu(struct intel_vgpu *vgpu)
314{
315 kfree(vgpu->sched_data);
316 vgpu->sched_data = NULL;
317}
318
319static void tbs_sched_start_schedule(struct intel_vgpu *vgpu)
320{
Ping Gaof6504cc2017-03-30 00:36:35 +0800321 struct gvt_sched_data *sched_data = vgpu->gvt->scheduler.sched_data;
322 struct vgpu_sched_data *vgpu_data = vgpu->sched_data;
Zhi Wang4b639602016-05-01 17:09:58 -0400323
Ping Gao32356922017-03-30 00:36:36 +0800324 if (!list_empty(&vgpu_data->lru_list))
Zhi Wang4b639602016-05-01 17:09:58 -0400325 return;
326
Ping Gao32356922017-03-30 00:36:36 +0800327 list_add_tail(&vgpu_data->lru_list, &sched_data->lru_runq_head);
Ping Gao91d01012017-03-30 00:36:34 +0800328
329 if (!hrtimer_active(&sched_data->timer))
330 hrtimer_start(&sched_data->timer, ktime_add_ns(ktime_get(),
331 sched_data->period), HRTIMER_MODE_ABS);
Zhi Wang4b639602016-05-01 17:09:58 -0400332}
333
334static void tbs_sched_stop_schedule(struct intel_vgpu *vgpu)
335{
Ping Gaof6504cc2017-03-30 00:36:35 +0800336 struct vgpu_sched_data *vgpu_data = vgpu->sched_data;
Zhi Wang4b639602016-05-01 17:09:58 -0400337
Ping Gao32356922017-03-30 00:36:36 +0800338 list_del_init(&vgpu_data->lru_list);
Zhi Wang4b639602016-05-01 17:09:58 -0400339}
340
Du, Changbin999ccb42016-10-20 14:08:47 +0800341static struct intel_gvt_sched_policy_ops tbs_schedule_ops = {
Zhi Wang4b639602016-05-01 17:09:58 -0400342 .init = tbs_sched_init,
343 .clean = tbs_sched_clean,
344 .init_vgpu = tbs_sched_init_vgpu,
345 .clean_vgpu = tbs_sched_clean_vgpu,
346 .start_schedule = tbs_sched_start_schedule,
347 .stop_schedule = tbs_sched_stop_schedule,
348};
349
350int intel_gvt_init_sched_policy(struct intel_gvt *gvt)
351{
352 gvt->scheduler.sched_ops = &tbs_schedule_ops;
353
354 return gvt->scheduler.sched_ops->init(gvt);
355}
356
357void intel_gvt_clean_sched_policy(struct intel_gvt *gvt)
358{
359 gvt->scheduler.sched_ops->clean(gvt);
360}
361
362int intel_vgpu_init_sched_policy(struct intel_vgpu *vgpu)
363{
364 return vgpu->gvt->scheduler.sched_ops->init_vgpu(vgpu);
365}
366
367void intel_vgpu_clean_sched_policy(struct intel_vgpu *vgpu)
368{
369 vgpu->gvt->scheduler.sched_ops->clean_vgpu(vgpu);
370}
371
372void intel_vgpu_start_schedule(struct intel_vgpu *vgpu)
373{
374 gvt_dbg_core("vgpu%d: start schedule\n", vgpu->id);
375
376 vgpu->gvt->scheduler.sched_ops->start_schedule(vgpu);
377}
378
379void intel_vgpu_stop_schedule(struct intel_vgpu *vgpu)
380{
381 struct intel_gvt_workload_scheduler *scheduler =
382 &vgpu->gvt->scheduler;
383
384 gvt_dbg_core("vgpu%d: stop schedule\n", vgpu->id);
385
386 scheduler->sched_ops->stop_schedule(vgpu);
387
388 if (scheduler->next_vgpu == vgpu)
389 scheduler->next_vgpu = NULL;
390
391 if (scheduler->current_vgpu == vgpu) {
392 /* stop workload dispatching */
393 scheduler->need_reschedule = true;
394 scheduler->current_vgpu = NULL;
395 }
396}