Skip to content

Commit b01739f

Browse files
coxuintelzhenyw
authored andcommitted
drm/i915/gvt: Refactor GVT vblank emulator for vGPU virtual display
Current vblank emulator uses single hrtimer at 16ms period for all vGPUs, which introduces three major issues: - 16ms matches the refresh rate at 62.5Hz (instead of 60Hz) which doesn't follow standard timing. This leads to some frame drop or glitch issue during video playback. SW expects a vsync interval of 16.667ms or higher precision for an accurate 60Hz refresh rate. However current vblank emulator only works at 16ms. - Doesn't respect the fact that with current virtual EDID timing set, not all resolutions are running at 60Hz. For example, current virtual EDID also supports refresh rate at 56Hz, 59.97Hz, 60Hz, 75Hz, etc. - Current vblank emulator use single hrtimer for all vGPUs. Regardsless the possibility that different guests could run in different resolutions, all vsync interrupts are injected at 16ms interval with same hrtimer. Based on previous patch which decode guest expected refresh rate from vreg, the vblank emulator refactor patch makes following changes: - Change the vblank emulator hrtimer from gvt global to per-vGPU. By doing this, each vGPU display can operates at different refresh rates. Currently only one dislay is supported for each vGPU so per-vGPU hrtimer is enough. If multiple displays are supported per-vGPU in future, we can expand to per-PIPE further. - Change the fixed hrtimer period from 16ms to dynamic based on vreg. GVT is expected to emulate the HW as close as possible. So reflacting the accurate vsync interrupt interval is more correct than fixed 16ms. - Change the vblank timer period and start the timer on PIPECONF change. The initial period is updated to 16666667 based on 60Hz refresh rate. According to PRM, PIPECONF controls the timing generator of the connected display on this pipe, so it's safe to stop hrtimer on PIPECONF disabling, and re-start hrtimer at new period on enabling. Other changes including: - Move vblank_timer_fn from irq.c into display.c. - Clean per-vGPU vblank timer at clean_display instead of clean_irq. To run quick test, launch a web browser and goto URL: www.displayhz.com The actual refresh rate from guest can now always match guest settings. V2: Rebase to 5.11. Remove unused intel_gvt_clean_irq(). Simplify enable logic in update_vblank_emulation(). (zhenyu) Loop all vGPU by idr when check all vblank timer. (zhenyu) Signed-off-by: Colin Xu <[email protected]> Signed-off-by: Zhenyu Wang <[email protected]> Link: http://patchwork.freedesktop.org/patch/msgid/[email protected] Reviewed-by: Zhenyu Wang <[email protected]>
1 parent 6a4500c commit b01739f

File tree

8 files changed

+94
-112
lines changed

8 files changed

+94
-112
lines changed

drivers/gpu/drm/i915/gvt/display.c

Lines changed: 57 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -501,11 +501,27 @@ static void clean_virtual_dp_monitor(struct intel_vgpu *vgpu, int port_num)
501501
port->dpcd = NULL;
502502
}
503503

504+
static enum hrtimer_restart vblank_timer_fn(struct hrtimer *data)
505+
{
506+
struct intel_vgpu_vblank_timer *vblank_timer;
507+
struct intel_vgpu *vgpu;
508+
509+
vblank_timer = container_of(data, struct intel_vgpu_vblank_timer, timer);
510+
vgpu = container_of(vblank_timer, struct intel_vgpu, vblank_timer);
511+
512+
/* Set vblank emulation request per-vGPU bit */
513+
intel_gvt_request_service(vgpu->gvt,
514+
INTEL_GVT_REQUEST_EMULATE_VBLANK + vgpu->id);
515+
hrtimer_add_expires_ns(&vblank_timer->timer, vblank_timer->period);
516+
return HRTIMER_RESTART;
517+
}
518+
504519
static int setup_virtual_dp_monitor(struct intel_vgpu *vgpu, int port_num,
505520
int type, unsigned int resolution)
506521
{
507522
struct drm_i915_private *i915 = vgpu->gvt->gt->i915;
508523
struct intel_vgpu_port *port = intel_vgpu_port(vgpu, port_num);
524+
struct intel_vgpu_vblank_timer *vblank_timer = &vgpu->vblank_timer;
509525

510526
if (drm_WARN_ON(&i915->drm, resolution >= GVT_EDID_NUM))
511527
return -EINVAL;
@@ -532,47 +548,56 @@ static int setup_virtual_dp_monitor(struct intel_vgpu *vgpu, int port_num,
532548
port->vrefresh_k = GVT_DEFAULT_REFRESH_RATE * MSEC_PER_SEC;
533549
vgpu->display.port_num = port_num;
534550

551+
/* Init hrtimer based on default refresh rate */
552+
hrtimer_init(&vblank_timer->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
553+
vblank_timer->timer.function = vblank_timer_fn;
554+
vblank_timer->vrefresh_k = port->vrefresh_k;
555+
vblank_timer->period = DIV64_U64_ROUND_CLOSEST(NSEC_PER_SEC * MSEC_PER_SEC, vblank_timer->vrefresh_k);
556+
535557
emulate_monitor_status_change(vgpu);
536558

537559
return 0;
538560
}
539561

540562
/**
541-
* intel_gvt_check_vblank_emulation - check if vblank emulation timer should
542-
* be turned on/off when a virtual pipe is enabled/disabled.
543-
* @gvt: a GVT device
563+
* vgpu_update_vblank_emulation - Update per-vGPU vblank_timer
564+
* @vgpu: vGPU operated
565+
* @turnon: Turn ON/OFF vblank_timer
544566
*
545-
* This function is used to turn on/off vblank timer according to currently
546-
* enabled/disabled virtual pipes.
567+
* This function is used to turn on/off or update the per-vGPU vblank_timer
568+
* when PIPECONF is enabled or disabled. vblank_timer period is also updated
569+
* if guest changed the refresh rate.
547570
*
548571
*/
549-
void intel_gvt_check_vblank_emulation(struct intel_gvt *gvt)
572+
void vgpu_update_vblank_emulation(struct intel_vgpu *vgpu, bool turnon)
550573
{
551-
struct intel_gvt_irq *irq = &gvt->irq;
552-
struct intel_vgpu *vgpu;
553-
int pipe, id;
554-
int found = false;
555-
556-
mutex_lock(&gvt->lock);
557-
for_each_active_vgpu(gvt, vgpu, id) {
558-
for (pipe = 0; pipe < I915_MAX_PIPES; pipe++) {
559-
if (pipe_is_enabled(vgpu, pipe)) {
560-
found = true;
561-
break;
562-
}
574+
struct intel_vgpu_vblank_timer *vblank_timer = &vgpu->vblank_timer;
575+
struct intel_vgpu_port *port =
576+
intel_vgpu_port(vgpu, vgpu->display.port_num);
577+
578+
if (turnon) {
579+
/*
580+
* Skip the re-enable if already active and vrefresh unchanged.
581+
* Otherwise, stop timer if already active and restart with new
582+
* period.
583+
*/
584+
if (vblank_timer->vrefresh_k != port->vrefresh_k ||
585+
!hrtimer_active(&vblank_timer->timer)) {
586+
/* Stop timer before start with new period if active */
587+
if (hrtimer_active(&vblank_timer->timer))
588+
hrtimer_cancel(&vblank_timer->timer);
589+
590+
/* Make sure new refresh rate updated to timer period */
591+
vblank_timer->vrefresh_k = port->vrefresh_k;
592+
vblank_timer->period = DIV64_U64_ROUND_CLOSEST(NSEC_PER_SEC * MSEC_PER_SEC, vblank_timer->vrefresh_k);
593+
hrtimer_start(&vblank_timer->timer,
594+
ktime_add_ns(ktime_get(), vblank_timer->period),
595+
HRTIMER_MODE_ABS);
563596
}
564-
if (found)
565-
break;
597+
} else {
598+
/* Caller request to stop vblank */
599+
hrtimer_cancel(&vblank_timer->timer);
566600
}
567-
568-
/* all the pipes are disabled */
569-
if (!found)
570-
hrtimer_cancel(&irq->vblank_timer.timer);
571-
else
572-
hrtimer_start(&irq->vblank_timer.timer,
573-
ktime_add_ns(ktime_get(), irq->vblank_timer.period),
574-
HRTIMER_MODE_ABS);
575-
mutex_unlock(&gvt->lock);
576601
}
577602

578603
static void emulate_vblank_on_pipe(struct intel_vgpu *vgpu, int pipe)
@@ -604,7 +629,7 @@ static void emulate_vblank_on_pipe(struct intel_vgpu *vgpu, int pipe)
604629
}
605630
}
606631

607-
static void emulate_vblank(struct intel_vgpu *vgpu)
632+
void intel_vgpu_emulate_vblank(struct intel_vgpu *vgpu)
608633
{
609634
int pipe;
610635

@@ -614,24 +639,6 @@ static void emulate_vblank(struct intel_vgpu *vgpu)
614639
mutex_unlock(&vgpu->vgpu_lock);
615640
}
616641

617-
/**
618-
* intel_gvt_emulate_vblank - trigger vblank events for vGPUs on GVT device
619-
* @gvt: a GVT device
620-
*
621-
* This function is used to trigger vblank interrupts for vGPUs on GVT device
622-
*
623-
*/
624-
void intel_gvt_emulate_vblank(struct intel_gvt *gvt)
625-
{
626-
struct intel_vgpu *vgpu;
627-
int id;
628-
629-
mutex_lock(&gvt->lock);
630-
for_each_active_vgpu(gvt, vgpu, id)
631-
emulate_vblank(vgpu);
632-
mutex_unlock(&gvt->lock);
633-
}
634-
635642
/**
636643
* intel_vgpu_emulate_hotplug - trigger hotplug event for vGPU
637644
* @vgpu: a vGPU
@@ -722,6 +729,8 @@ void intel_vgpu_clean_display(struct intel_vgpu *vgpu)
722729
clean_virtual_dp_monitor(vgpu, PORT_D);
723730
else
724731
clean_virtual_dp_monitor(vgpu, PORT_B);
732+
733+
vgpu_update_vblank_emulation(vgpu, false);
725734
}
726735

727736
/**

drivers/gpu/drm/i915/gvt/display.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#define _GVT_DISPLAY_H_
3737

3838
#include <linux/types.h>
39+
#include <linux/hrtimer.h>
3940

4041
struct intel_gvt;
4142
struct intel_vgpu;
@@ -169,6 +170,12 @@ struct intel_vgpu_port {
169170
u32 vrefresh_k;
170171
};
171172

173+
struct intel_vgpu_vblank_timer {
174+
struct hrtimer timer;
175+
u32 vrefresh_k;
176+
u64 period;
177+
};
178+
172179
static inline char *vgpu_edid_str(enum intel_vgpu_edid id)
173180
{
174181
switch (id) {
@@ -205,8 +212,8 @@ static inline unsigned int vgpu_edid_yres(enum intel_vgpu_edid id)
205212
}
206213
}
207214

208-
void intel_gvt_emulate_vblank(struct intel_gvt *gvt);
209-
void intel_gvt_check_vblank_emulation(struct intel_gvt *gvt);
215+
void intel_vgpu_emulate_vblank(struct intel_vgpu *vgpu);
216+
void vgpu_update_vblank_emulation(struct intel_vgpu *vgpu, bool turnon);
210217

211218
int intel_vgpu_init_display(struct intel_vgpu *vgpu, u64 resolution);
212219
void intel_vgpu_reset_display(struct intel_vgpu *vgpu);

drivers/gpu/drm/i915/gvt/gvt.c

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,22 @@ static void init_device_info(struct intel_gvt *gvt)
203203
info->msi_cap_offset = pdev->msi_cap;
204204
}
205205

206+
static void intel_gvt_test_and_emulate_vblank(struct intel_gvt *gvt)
207+
{
208+
struct intel_vgpu *vgpu;
209+
int id;
210+
211+
mutex_lock(&gvt->lock);
212+
idr_for_each_entry((&(gvt)->vgpu_idr), (vgpu), (id)) {
213+
if (test_and_clear_bit(INTEL_GVT_REQUEST_EMULATE_VBLANK + id,
214+
(void *)&gvt->service_request)) {
215+
if (vgpu->active)
216+
intel_vgpu_emulate_vblank(vgpu);
217+
}
218+
}
219+
mutex_unlock(&gvt->lock);
220+
}
221+
206222
static int gvt_service_thread(void *data)
207223
{
208224
struct intel_gvt *gvt = (struct intel_gvt *)data;
@@ -220,9 +236,7 @@ static int gvt_service_thread(void *data)
220236
if (WARN_ONCE(ret, "service thread is waken up by signal.\n"))
221237
continue;
222238

223-
if (test_and_clear_bit(INTEL_GVT_REQUEST_EMULATE_VBLANK,
224-
(void *)&gvt->service_request))
225-
intel_gvt_emulate_vblank(gvt);
239+
intel_gvt_test_and_emulate_vblank(gvt);
226240

227241
if (test_bit(INTEL_GVT_REQUEST_SCHED,
228242
(void *)&gvt->service_request) ||
@@ -278,7 +292,6 @@ void intel_gvt_clean_device(struct drm_i915_private *i915)
278292
intel_gvt_clean_sched_policy(gvt);
279293
intel_gvt_clean_workload_scheduler(gvt);
280294
intel_gvt_clean_gtt(gvt);
281-
intel_gvt_clean_irq(gvt);
282295
intel_gvt_free_firmware(gvt);
283296
intel_gvt_clean_mmio_info(gvt);
284297
idr_destroy(&gvt->vgpu_idr);
@@ -337,7 +350,7 @@ int intel_gvt_init_device(struct drm_i915_private *i915)
337350

338351
ret = intel_gvt_init_gtt(gvt);
339352
if (ret)
340-
goto out_clean_irq;
353+
goto out_free_firmware;
341354

342355
ret = intel_gvt_init_workload_scheduler(gvt);
343356
if (ret)
@@ -392,8 +405,6 @@ int intel_gvt_init_device(struct drm_i915_private *i915)
392405
intel_gvt_clean_workload_scheduler(gvt);
393406
out_clean_gtt:
394407
intel_gvt_clean_gtt(gvt);
395-
out_clean_irq:
396-
intel_gvt_clean_irq(gvt);
397408
out_free_firmware:
398409
intel_gvt_free_firmware(gvt);
399410
out_clean_mmio_info:

drivers/gpu/drm/i915/gvt/gvt.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ struct intel_vgpu {
215215
struct list_head dmabuf_obj_list_head;
216216
struct mutex dmabuf_lock;
217217
struct idr object_idr;
218+
struct intel_vgpu_vblank_timer vblank_timer;
218219

219220
u32 scan_nonprivbb;
220221
};
@@ -344,13 +345,16 @@ static inline struct intel_gvt *to_gvt(struct drm_i915_private *i915)
344345
}
345346

346347
enum {
347-
INTEL_GVT_REQUEST_EMULATE_VBLANK = 0,
348-
349348
/* Scheduling trigger by timer */
350-
INTEL_GVT_REQUEST_SCHED = 1,
349+
INTEL_GVT_REQUEST_SCHED = 0,
351350

352351
/* Scheduling trigger by event */
353-
INTEL_GVT_REQUEST_EVENT_SCHED = 2,
352+
INTEL_GVT_REQUEST_EVENT_SCHED = 1,
353+
354+
/* per-vGPU vblank emulation request */
355+
INTEL_GVT_REQUEST_EMULATE_VBLANK = 2,
356+
INTEL_GVT_REQUEST_EMULATE_VBLANK_MAX = INTEL_GVT_REQUEST_EMULATE_VBLANK
357+
+ GVT_MAX_VGPU,
354358
};
355359

356360
static inline void intel_gvt_request_service(struct intel_gvt *gvt,

drivers/gpu/drm/i915/gvt/handlers.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -703,14 +703,11 @@ static int pipeconf_mmio_write(struct intel_vgpu *vgpu, unsigned int offset,
703703
if (data & PIPECONF_ENABLE) {
704704
vgpu_vreg(vgpu, offset) |= I965_PIPECONF_ACTIVE;
705705
vgpu_update_refresh_rate(vgpu);
706-
706+
vgpu_update_vblank_emulation(vgpu, true);
707707
} else {
708708
vgpu_vreg(vgpu, offset) &= ~I965_PIPECONF_ACTIVE;
709+
vgpu_update_vblank_emulation(vgpu, false);
709710
}
710-
/* vgpu_lock already hold by emulate mmio r/w */
711-
mutex_unlock(&vgpu->vgpu_lock);
712-
intel_gvt_check_vblank_emulation(vgpu->gvt);
713-
mutex_lock(&vgpu->vgpu_lock);
714711
return 0;
715712
}
716713

drivers/gpu/drm/i915/gvt/interrupt.c

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -647,38 +647,6 @@ static void init_events(
647647
}
648648
}
649649

650-
static enum hrtimer_restart vblank_timer_fn(struct hrtimer *data)
651-
{
652-
struct intel_gvt_vblank_timer *vblank_timer;
653-
struct intel_gvt_irq *irq;
654-
struct intel_gvt *gvt;
655-
656-
vblank_timer = container_of(data, struct intel_gvt_vblank_timer, timer);
657-
irq = container_of(vblank_timer, struct intel_gvt_irq, vblank_timer);
658-
gvt = container_of(irq, struct intel_gvt, irq);
659-
660-
intel_gvt_request_service(gvt, INTEL_GVT_REQUEST_EMULATE_VBLANK);
661-
hrtimer_add_expires_ns(&vblank_timer->timer, vblank_timer->period);
662-
return HRTIMER_RESTART;
663-
}
664-
665-
/**
666-
* intel_gvt_clean_irq - clean up GVT-g IRQ emulation subsystem
667-
* @gvt: a GVT device
668-
*
669-
* This function is called at driver unloading stage, to clean up GVT-g IRQ
670-
* emulation subsystem.
671-
*
672-
*/
673-
void intel_gvt_clean_irq(struct intel_gvt *gvt)
674-
{
675-
struct intel_gvt_irq *irq = &gvt->irq;
676-
677-
hrtimer_cancel(&irq->vblank_timer.timer);
678-
}
679-
680-
#define VBLANK_TIMER_PERIOD 16000000
681-
682650
/**
683651
* intel_gvt_init_irq - initialize GVT-g IRQ emulation subsystem
684652
* @gvt: a GVT device
@@ -692,7 +660,6 @@ void intel_gvt_clean_irq(struct intel_gvt *gvt)
692660
int intel_gvt_init_irq(struct intel_gvt *gvt)
693661
{
694662
struct intel_gvt_irq *irq = &gvt->irq;
695-
struct intel_gvt_vblank_timer *vblank_timer = &irq->vblank_timer;
696663

697664
gvt_dbg_core("init irq framework\n");
698665

@@ -707,9 +674,5 @@ int intel_gvt_init_irq(struct intel_gvt *gvt)
707674

708675
init_irq_map(irq);
709676

710-
hrtimer_init(&vblank_timer->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
711-
vblank_timer->timer.function = vblank_timer_fn;
712-
vblank_timer->period = VBLANK_TIMER_PERIOD;
713-
714677
return 0;
715678
}

drivers/gpu/drm/i915/gvt/interrupt.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -201,11 +201,6 @@ struct intel_gvt_irq_map {
201201
u32 down_irq_bitmask;
202202
};
203203

204-
struct intel_gvt_vblank_timer {
205-
struct hrtimer timer;
206-
u64 period;
207-
};
208-
209204
/* structure containing device specific IRQ state */
210205
struct intel_gvt_irq {
211206
struct intel_gvt_irq_ops *ops;
@@ -214,11 +209,9 @@ struct intel_gvt_irq {
214209
struct intel_gvt_event_info events[INTEL_GVT_EVENT_MAX];
215210
DECLARE_BITMAP(pending_events, INTEL_GVT_EVENT_MAX);
216211
struct intel_gvt_irq_map *irq_map;
217-
struct intel_gvt_vblank_timer vblank_timer;
218212
};
219213

220214
int intel_gvt_init_irq(struct intel_gvt *gvt);
221-
void intel_gvt_clean_irq(struct intel_gvt *gvt);
222215

223216
void intel_vgpu_trigger_virtual_event(struct intel_vgpu *vgpu,
224217
enum intel_gvt_event_type event);

drivers/gpu/drm/i915/gvt/vgpu.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,8 +300,6 @@ void intel_gvt_destroy_vgpu(struct intel_vgpu *vgpu)
300300
mutex_unlock(&vgpu->vgpu_lock);
301301

302302
mutex_lock(&gvt->lock);
303-
if (idr_is_empty(&gvt->vgpu_idr))
304-
intel_gvt_clean_irq(gvt);
305303
intel_gvt_update_vgpu_types(gvt);
306304
mutex_unlock(&gvt->lock);
307305

0 commit comments

Comments
 (0)