drm/i915/gvt: vGPU schedule policy framework
This patch introduces a vGPU schedule policy framework, with a timer based schedule policy module for now Signed-off-by: Zhi Wang <zhi.a.wang@intel.com> Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com>
This commit is contained in:
parent
e473405783
commit
4b63960ebd
|
@ -1,7 +1,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
|
||||
execlist.o scheduler.o sched_policy.o
|
||||
|
||||
ccflags-y += -I$(src) -I$(src)/$(GVT_DIR) -Wall
|
||||
i915-y += $(addprefix $(GVT_DIR)/, $(GVT_SOURCE))
|
||||
|
|
|
@ -177,6 +177,7 @@ void intel_gvt_clean_device(struct drm_i915_private *dev_priv)
|
|||
return;
|
||||
|
||||
clean_service_thread(gvt);
|
||||
intel_gvt_clean_sched_policy(gvt);
|
||||
intel_gvt_clean_workload_scheduler(gvt);
|
||||
intel_gvt_clean_opregion(gvt);
|
||||
intel_gvt_clean_gtt(gvt);
|
||||
|
@ -244,14 +245,20 @@ int intel_gvt_init_device(struct drm_i915_private *dev_priv)
|
|||
if (ret)
|
||||
goto out_clean_opregion;
|
||||
|
||||
ret = init_service_thread(gvt);
|
||||
ret = intel_gvt_init_sched_policy(gvt);
|
||||
if (ret)
|
||||
goto out_clean_workload_scheduler;
|
||||
|
||||
ret = init_service_thread(gvt);
|
||||
if (ret)
|
||||
goto out_clean_sched_policy;
|
||||
|
||||
gvt_dbg_core("gvt device creation is done\n");
|
||||
gvt->initialized = true;
|
||||
return 0;
|
||||
|
||||
out_clean_sched_policy:
|
||||
intel_gvt_clean_sched_policy(gvt);
|
||||
out_clean_workload_scheduler:
|
||||
intel_gvt_clean_workload_scheduler(gvt);
|
||||
out_clean_opregion:
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "edid.h"
|
||||
#include "execlist.h"
|
||||
#include "scheduler.h"
|
||||
#include "sched_policy.h"
|
||||
|
||||
#define GVT_MAX_VGPU 8
|
||||
|
||||
|
@ -139,6 +140,7 @@ struct intel_vgpu {
|
|||
unsigned long handle; /* vGPU handle used by hypervisor MPT modules */
|
||||
bool active;
|
||||
bool resetting;
|
||||
void *sched_data;
|
||||
|
||||
struct intel_vgpu_fence fence;
|
||||
struct intel_vgpu_gm gm;
|
||||
|
|
|
@ -235,6 +235,7 @@ static int handle_device_reset(struct intel_vgpu *vgpu, unsigned int offset,
|
|||
|
||||
vgpu->resetting = true;
|
||||
|
||||
intel_vgpu_stop_schedule(vgpu);
|
||||
if (scheduler->current_vgpu == vgpu) {
|
||||
mutex_unlock(&vgpu->gvt->lock);
|
||||
intel_gvt_wait_vgpu_idle(vgpu);
|
||||
|
@ -1317,6 +1318,28 @@ static int elsp_mmio_write(struct intel_vgpu *vgpu, unsigned int offset,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ring_mode_mmio_write(struct intel_vgpu *vgpu, unsigned int offset,
|
||||
void *p_data, unsigned int bytes)
|
||||
{
|
||||
u32 data = *(u32 *)p_data;
|
||||
int ring_id = render_mmio_to_ring_id(vgpu->gvt, offset);
|
||||
bool enable_execlist;
|
||||
|
||||
write_vreg(vgpu, offset, p_data, bytes);
|
||||
if ((data & _MASKED_BIT_ENABLE(GFX_RUN_LIST_ENABLE))
|
||||
|| (data & _MASKED_BIT_DISABLE(GFX_RUN_LIST_ENABLE))) {
|
||||
enable_execlist = !!(data & GFX_RUN_LIST_ENABLE);
|
||||
|
||||
gvt_dbg_core("EXECLIST %s on ring %d\n",
|
||||
(enable_execlist ? "enabling" : "disabling"),
|
||||
ring_id);
|
||||
|
||||
if (enable_execlist)
|
||||
intel_vgpu_start_schedule(vgpu);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MMIO_F(reg, s, f, am, rm, d, r, w) do { \
|
||||
ret = new_mmio_info(gvt, INTEL_GVT_MMIO_OFFSET(reg), \
|
||||
f, s, am, rm, d, r, w); \
|
||||
|
@ -1398,7 +1421,7 @@ static int init_generic_mmio_info(struct intel_gvt *gvt)
|
|||
|
||||
/* RING MODE */
|
||||
#define RING_REG(base) (base + 0x29c)
|
||||
MMIO_RING_DFH(RING_REG, D_ALL, F_MODE_MASK, NULL, NULL);
|
||||
MMIO_RING_DFH(RING_REG, D_ALL, F_MODE_MASK, NULL, ring_mode_mmio_write);
|
||||
#undef RING_REG
|
||||
|
||||
MMIO_RING_DFH(RING_MI_MODE, D_ALL, F_MODE_MASK, NULL, NULL);
|
||||
|
@ -2213,7 +2236,7 @@ static int init_broadwell_mmio_info(struct intel_gvt *gvt)
|
|||
MMIO_D(RING_CTL(GEN8_BSD2_RING_BASE), D_BDW_PLUS);
|
||||
MMIO_D(RING_ACTHD(GEN8_BSD2_RING_BASE), D_BDW_PLUS);
|
||||
MMIO_D(RING_ACTHD_UDW(GEN8_BSD2_RING_BASE), D_BDW_PLUS);
|
||||
MMIO_DFH(0x1c29c, D_BDW_PLUS, F_MODE_MASK, NULL, NULL);
|
||||
MMIO_DFH(0x1c29c, D_BDW_PLUS, F_MODE_MASK, NULL, ring_mode_mmio_write);
|
||||
MMIO_DFH(RING_MI_MODE(GEN8_BSD2_RING_BASE), D_BDW_PLUS, F_MODE_MASK,
|
||||
NULL, NULL);
|
||||
MMIO_DFH(RING_INSTPM(GEN8_BSD2_RING_BASE), D_BDW_PLUS, F_MODE_MASK,
|
||||
|
|
|
@ -0,0 +1,291 @@
|
|||
/*
|
||||
* 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@intel.com>
|
||||
*
|
||||
* Contributors:
|
||||
* Min He <min.he@intel.com>
|
||||
* Bing Niu <bing.niu@intel.com>
|
||||
* Zhi Wang <zhi.a.wang@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "i915_drv.h"
|
||||
|
||||
static bool vgpu_has_pending_workload(struct intel_vgpu *vgpu)
|
||||
{
|
||||
struct intel_vgpu_execlist *execlist;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < I915_NUM_ENGINES; i++) {
|
||||
execlist = &vgpu->execlist[i];
|
||||
if (!list_empty(workload_q_head(vgpu, i)))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void try_to_schedule_next_vgpu(struct intel_gvt *gvt)
|
||||
{
|
||||
struct intel_gvt_workload_scheduler *scheduler = &gvt->scheduler;
|
||||
int i;
|
||||
|
||||
/* no target to schedule */
|
||||
if (!scheduler->next_vgpu)
|
||||
return;
|
||||
|
||||
gvt_dbg_sched("try to schedule next vgpu %d\n",
|
||||
scheduler->next_vgpu->id);
|
||||
|
||||
/*
|
||||
* after the flag is set, workload dispatch thread will
|
||||
* stop dispatching workload for current vgpu
|
||||
*/
|
||||
scheduler->need_reschedule = true;
|
||||
|
||||
/* still have uncompleted workload? */
|
||||
for (i = 0; i < I915_NUM_ENGINES; i++) {
|
||||
if (scheduler->current_workload[i]) {
|
||||
gvt_dbg_sched("still have running workload\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
gvt_dbg_sched("switch to next vgpu %d\n",
|
||||
scheduler->next_vgpu->id);
|
||||
|
||||
/* switch current vgpu */
|
||||
scheduler->current_vgpu = scheduler->next_vgpu;
|
||||
scheduler->next_vgpu = NULL;
|
||||
|
||||
scheduler->need_reschedule = false;
|
||||
|
||||
/* wake up workload dispatch thread */
|
||||
for (i = 0; i < I915_NUM_ENGINES; i++)
|
||||
wake_up(&scheduler->waitq[i]);
|
||||
}
|
||||
|
||||
struct tbs_vgpu_data {
|
||||
struct list_head list;
|
||||
struct intel_vgpu *vgpu;
|
||||
/* put some per-vgpu sched stats here */
|
||||
};
|
||||
|
||||
struct tbs_sched_data {
|
||||
struct intel_gvt *gvt;
|
||||
struct delayed_work work;
|
||||
unsigned long period;
|
||||
struct list_head runq_head;
|
||||
};
|
||||
|
||||
#define GVT_DEFAULT_TIME_SLICE (1 * HZ / 1000)
|
||||
|
||||
static void tbs_sched_func(struct work_struct *work)
|
||||
{
|
||||
struct tbs_sched_data *sched_data = container_of(work,
|
||||
struct tbs_sched_data, work.work);
|
||||
struct tbs_vgpu_data *vgpu_data;
|
||||
|
||||
struct intel_gvt *gvt = sched_data->gvt;
|
||||
struct intel_gvt_workload_scheduler *scheduler = &gvt->scheduler;
|
||||
|
||||
struct intel_vgpu *vgpu = NULL;
|
||||
struct list_head *pos, *head;
|
||||
|
||||
mutex_lock(&gvt->lock);
|
||||
|
||||
/* no vgpu or has already had a target */
|
||||
if (list_empty(&sched_data->runq_head) || scheduler->next_vgpu)
|
||||
goto out;
|
||||
|
||||
if (scheduler->current_vgpu) {
|
||||
vgpu_data = scheduler->current_vgpu->sched_data;
|
||||
head = &vgpu_data->list;
|
||||
} else {
|
||||
gvt_dbg_sched("no current vgpu search from q head\n");
|
||||
head = &sched_data->runq_head;
|
||||
}
|
||||
|
||||
/* search a vgpu with pending workload */
|
||||
list_for_each(pos, head) {
|
||||
if (pos == &sched_data->runq_head)
|
||||
continue;
|
||||
|
||||
vgpu_data = container_of(pos, struct tbs_vgpu_data, list);
|
||||
if (!vgpu_has_pending_workload(vgpu_data->vgpu))
|
||||
continue;
|
||||
|
||||
vgpu = vgpu_data->vgpu;
|
||||
break;
|
||||
}
|
||||
|
||||
if (vgpu) {
|
||||
scheduler->next_vgpu = vgpu;
|
||||
gvt_dbg_sched("pick next vgpu %d\n", vgpu->id);
|
||||
}
|
||||
out:
|
||||
if (scheduler->next_vgpu) {
|
||||
gvt_dbg_sched("try to schedule next vgpu %d\n",
|
||||
scheduler->next_vgpu->id);
|
||||
try_to_schedule_next_vgpu(gvt);
|
||||
}
|
||||
|
||||
/*
|
||||
* still have vgpu on runq
|
||||
* or last schedule haven't finished due to running workload
|
||||
*/
|
||||
if (!list_empty(&sched_data->runq_head) || scheduler->next_vgpu)
|
||||
schedule_delayed_work(&sched_data->work, sched_data->period);
|
||||
|
||||
mutex_unlock(&gvt->lock);
|
||||
}
|
||||
|
||||
static int tbs_sched_init(struct intel_gvt *gvt)
|
||||
{
|
||||
struct intel_gvt_workload_scheduler *scheduler =
|
||||
&gvt->scheduler;
|
||||
|
||||
struct tbs_sched_data *data;
|
||||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&data->runq_head);
|
||||
INIT_DELAYED_WORK(&data->work, tbs_sched_func);
|
||||
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 tbs_sched_data *data = scheduler->sched_data;
|
||||
|
||||
cancel_delayed_work(&data->work);
|
||||
kfree(data);
|
||||
scheduler->sched_data = NULL;
|
||||
}
|
||||
|
||||
static int tbs_sched_init_vgpu(struct intel_vgpu *vgpu)
|
||||
{
|
||||
struct tbs_vgpu_data *data;
|
||||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->vgpu = vgpu;
|
||||
INIT_LIST_HEAD(&data->list);
|
||||
|
||||
vgpu->sched_data = data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tbs_sched_clean_vgpu(struct intel_vgpu *vgpu)
|
||||
{
|
||||
kfree(vgpu->sched_data);
|
||||
vgpu->sched_data = NULL;
|
||||
}
|
||||
|
||||
static void tbs_sched_start_schedule(struct intel_vgpu *vgpu)
|
||||
{
|
||||
struct tbs_sched_data *sched_data = vgpu->gvt->scheduler.sched_data;
|
||||
struct tbs_vgpu_data *vgpu_data = vgpu->sched_data;
|
||||
|
||||
if (!list_empty(&vgpu_data->list))
|
||||
return;
|
||||
|
||||
list_add_tail(&vgpu_data->list, &sched_data->runq_head);
|
||||
schedule_delayed_work(&sched_data->work, sched_data->period);
|
||||
}
|
||||
|
||||
static void tbs_sched_stop_schedule(struct intel_vgpu *vgpu)
|
||||
{
|
||||
struct tbs_vgpu_data *vgpu_data = vgpu->sched_data;
|
||||
|
||||
list_del_init(&vgpu_data->list);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
gvt->scheduler.sched_ops = &tbs_schedule_ops;
|
||||
|
||||
return gvt->scheduler.sched_ops->init(gvt);
|
||||
}
|
||||
|
||||
void intel_gvt_clean_sched_policy(struct intel_gvt *gvt)
|
||||
{
|
||||
gvt->scheduler.sched_ops->clean(gvt);
|
||||
}
|
||||
|
||||
int intel_vgpu_init_sched_policy(struct intel_vgpu *vgpu)
|
||||
{
|
||||
return vgpu->gvt->scheduler.sched_ops->init_vgpu(vgpu);
|
||||
}
|
||||
|
||||
void intel_vgpu_clean_sched_policy(struct intel_vgpu *vgpu)
|
||||
{
|
||||
vgpu->gvt->scheduler.sched_ops->clean_vgpu(vgpu);
|
||||
}
|
||||
|
||||
void intel_vgpu_start_schedule(struct intel_vgpu *vgpu)
|
||||
{
|
||||
gvt_dbg_core("vgpu%d: start schedule\n", vgpu->id);
|
||||
|
||||
vgpu->gvt->scheduler.sched_ops->start_schedule(vgpu);
|
||||
}
|
||||
|
||||
void intel_vgpu_stop_schedule(struct intel_vgpu *vgpu)
|
||||
{
|
||||
struct intel_gvt_workload_scheduler *scheduler =
|
||||
&vgpu->gvt->scheduler;
|
||||
|
||||
gvt_dbg_core("vgpu%d: stop schedule\n", vgpu->id);
|
||||
|
||||
scheduler->sched_ops->stop_schedule(vgpu);
|
||||
|
||||
if (scheduler->next_vgpu == vgpu)
|
||||
scheduler->next_vgpu = NULL;
|
||||
|
||||
if (scheduler->current_vgpu == vgpu) {
|
||||
/* stop workload dispatching */
|
||||
scheduler->need_reschedule = true;
|
||||
scheduler->current_vgpu = NULL;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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@intel.com>
|
||||
*
|
||||
* Contributors:
|
||||
* Min He <min.he@intel.com>
|
||||
* Bing Niu <bing.niu@intel.com>
|
||||
* Zhi Wang <zhi.a.wang@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __GVT_SCHED_POLICY__
|
||||
#define __GVT_SCHED_POLICY__
|
||||
|
||||
struct intel_gvt_sched_policy_ops {
|
||||
int (*init)(struct intel_gvt *gvt);
|
||||
void (*clean)(struct intel_gvt *gvt);
|
||||
int (*init_vgpu)(struct intel_vgpu *vgpu);
|
||||
void (*clean_vgpu)(struct intel_vgpu *vgpu);
|
||||
void (*start_schedule)(struct intel_vgpu *vgpu);
|
||||
void (*stop_schedule)(struct intel_vgpu *vgpu);
|
||||
};
|
||||
|
||||
int intel_gvt_init_sched_policy(struct intel_gvt *gvt);
|
||||
|
||||
void intel_gvt_clean_sched_policy(struct intel_gvt *gvt);
|
||||
|
||||
int intel_vgpu_init_sched_policy(struct intel_vgpu *vgpu);
|
||||
|
||||
void intel_vgpu_clean_sched_policy(struct intel_vgpu *vgpu);
|
||||
|
||||
void intel_vgpu_start_schedule(struct intel_vgpu *vgpu);
|
||||
|
||||
void intel_vgpu_stop_schedule(struct intel_vgpu *vgpu);
|
||||
|
||||
#endif
|
|
@ -45,6 +45,9 @@ struct intel_gvt_workload_scheduler {
|
|||
wait_queue_head_t workload_complete_wq;
|
||||
struct task_struct *thread[I915_NUM_ENGINES];
|
||||
wait_queue_head_t waitq[I915_NUM_ENGINES];
|
||||
|
||||
void *sched_data;
|
||||
struct intel_gvt_sched_policy_ops *sched_ops;
|
||||
};
|
||||
|
||||
struct intel_vgpu_workload {
|
||||
|
|
|
@ -146,6 +146,14 @@ void intel_gvt_destroy_vgpu(struct intel_vgpu *vgpu)
|
|||
vgpu->active = false;
|
||||
idr_remove(&gvt->vgpu_idr, vgpu->id);
|
||||
|
||||
if (atomic_read(&vgpu->running_workload_num)) {
|
||||
mutex_unlock(&gvt->lock);
|
||||
intel_gvt_wait_vgpu_idle(vgpu);
|
||||
mutex_lock(&gvt->lock);
|
||||
}
|
||||
|
||||
intel_vgpu_stop_schedule(vgpu);
|
||||
intel_vgpu_clean_sched_policy(vgpu);
|
||||
intel_vgpu_clean_gvt_context(vgpu);
|
||||
intel_vgpu_clean_execlist(vgpu);
|
||||
intel_vgpu_clean_display(vgpu);
|
||||
|
@ -231,11 +239,17 @@ struct intel_vgpu *intel_gvt_create_vgpu(struct intel_gvt *gvt,
|
|||
if (ret)
|
||||
goto out_clean_execlist;
|
||||
|
||||
ret = intel_vgpu_init_sched_policy(vgpu);
|
||||
if (ret)
|
||||
goto out_clean_shadow_ctx;
|
||||
|
||||
vgpu->active = true;
|
||||
mutex_unlock(&gvt->lock);
|
||||
|
||||
return vgpu;
|
||||
|
||||
out_clean_shadow_ctx:
|
||||
intel_vgpu_clean_gvt_context(vgpu);
|
||||
out_clean_execlist:
|
||||
intel_vgpu_clean_execlist(vgpu);
|
||||
out_clean_display:
|
||||
|
|
Loading…
Reference in New Issue