[PATCH v4 6/9] drm/i915/gvt: introduce the vGPU LRU list

Tian, Kevin kevin.tian at intel.com
Wed Mar 15 03:16:16 UTC 2017


> From: Ping Gao
> Sent: Wednesday, March 8, 2017 2:25 PM
> 
> vGPU LRU list introduced to record the schedule status of all the vGPUs, the

'is introduced'

> longest unsched vGPU get the highest priority to schedule, it's mainly used to

unscheduled

> avoid guest driver trigger TDR because of vGPU service is non-available for a

avoid guest driver timeout when its vGPU is not scheduled for a long time

> long time. Besides that it keep the round-robin scheduling fairness when

keep->keeps

> scheduler need choose a new head beacause of vGPU stop/kill.
> 
> Signed-off-by: Ping Gao <ping.a.gao at intel.com>
> ---
>  drivers/gpu/drm/i915/gvt/sched_policy.c | 77
> ++++++++++++++++++++++++++++++---
>  1 file changed, 72 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/gvt/sched_policy.c
> b/drivers/gpu/drm/i915/gvt/sched_policy.c
> index e8a9db1..e0311c8 100644
> --- a/drivers/gpu/drm/i915/gvt/sched_policy.c
> +++ b/drivers/gpu/drm/i915/gvt/sched_policy.c
> @@ -49,6 +49,7 @@ static bool vgpu_has_pending_workload(struct
> intel_vgpu *vgpu)
> 
>  struct vgpu_sched_data {
>  	struct list_head list;
> +	struct list_head lru_list;
>  	struct intel_vgpu *vgpu;
> 
>  	/* per-vgpu sched stats */
> @@ -66,6 +67,7 @@ struct gvt_sched_data {
>  	struct hrtimer timer;
>  	unsigned long period;
>  	struct list_head runq_head;
> +	struct list_head lru_vgpu_head;
>  };
> 
>  static void try_to_schedule_next_vgpu(struct intel_gvt *gvt) @@ -148,6
> +150,37 @@ static struct intel_vgpu *get_vgpu_has_workload(struct
> list_head *head,
>  	return vgpu;
>  }
> 
> +/* 1.5 second */
> +#define VGPU_TDR_THRES_MS  ((cycles_t)tsc_khz * 1500) #define
> +is_idle_vgpu(vgpu) ((vgpu)->id == 0)

break lines

> +
> +static struct intel_vgpu *find_longest_unsched_vgpu(struct list_head
> +*lru_vgpu_head) {
> +	struct list_head *pos;
> +	struct vgpu_sched_data *vgpu_data;
> +	struct intel_vgpu *vgpu = NULL;
> +
> +	/* Find out the first active vGPU which has
> +	 * pending workload in the lru list, the longest
> +	 * unsched vGPU is in the head.

head->candidate?

> +	 */
> +	list_for_each(pos, lru_vgpu_head) {
> +		vgpu_data = container_of(pos, struct vgpu_sched_data,
> lru_list);
> +		if (!is_idle_vgpu(vgpu_data->vgpu) &&
> +				vgpu_has_pending_workload(vgpu_data-
> >vgpu)) {
> +			vgpu = vgpu_data->vgpu;
> +			break;
> +		}
> +	}
> +
> +	/* Return the vGPU if it's pending time exceed the threshold */
> +	if (vgpu && (get_cycles() - vgpu->last_ctx_submit_time >
> +			VGPU_TDR_THRES_MS))
> +		return vgpu;
> +
> +	return NULL;
> +}
> +
>  static struct list_head *get_sched_head(struct gvt_sched_data *sched_data)
> {
>  	struct intel_gvt *gvt = sched_data->gvt; @@ -159,8 +192,17 @@
> static struct list_head *get_sched_head(struct gvt_sched_data *sched_data)
>  		cur_vgpu_data = scheduler->current_vgpu->sched_data;
>  		head = &cur_vgpu_data->list;
>  	} else {
> +		struct vgpu_sched_data *lru_vgpu_data;
> +
>  		gvt_dbg_sched("no current vgpu search from q head\n");
> -		head = &sched_data->runq_head;
> +
> +		/* Choose new head according to lru list to avoid
> +		 * unfairness when current_vgpu set to NULL because
> +		 * of the vGPU stop/kill.
> +		 */
> +		lru_vgpu_data = container_of(sched_data-
> >lru_vgpu_head.next,
> +					struct vgpu_sched_data, lru_list);
> +		head = lru_vgpu_data->list.prev;
>  	}
> 
>  	return head;
> @@ -168,8 +210,9 @@ static struct list_head *get_sched_head(struct
> gvt_sched_data *sched_data)
> 
>  static struct intel_vgpu *pickup_next_vgpu(struct gvt_sched_data
> *sched_data)  {
> -	struct intel_vgpu *next_vgpu = NULL;
> -	struct list_head *head = NULL;
> +	struct vgpu_sched_data *next_vgpu_data;
> +	struct intel_vgpu *next_vgpu;
> +	struct list_head *head;
> 
>  	/* The scheduler is follow round-robin style, sched
>  	 * head means where start to choose next vGPU, it's @@ -179,8
> +222,22 @@ static struct intel_vgpu *pickup_next_vgpu(struct
> gvt_sched_data *sched_data)
>  	 */
>  	head = get_sched_head(sched_data);
> 
> -	/* Choose the vGPU which has pending workload. */
> -	next_vgpu = get_vgpu_has_workload(head, sched_data);
> +	/* Find out the vGPU which have not been scheduled near a max
> +	 * threshold, then execute it immediately to avoid guest TDR.
> +	 */
> +	next_vgpu = find_longest_unsched_vgpu(&sched_data-
> >lru_vgpu_head);
> +	if (next_vgpu) {
> +		next_vgpu_data = next_vgpu->sched_data;
> +
> +		/* Move the longest unsched vGPU after
> +		 * current vGPU to keep fairness round-robin.
> +		 */
> +		list_del_init(&next_vgpu_data->list);
> +		list_add(&next_vgpu_data->list, head);
> +	} else {
> +		/* Choose the vGPU which has pending workload */
> +		next_vgpu = get_vgpu_has_workload(head, sched_data);
> +	}
> 
>  	return next_vgpu;
>  }
> @@ -191,6 +248,7 @@ static void tbs_sched_func(struct gvt_sched_data
> *sched_data)  {
>  	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 */ @@ -203,6 +261,11
> @@ static void tbs_sched_func(struct gvt_sched_data *sched_data)
>  	if (vgpu) {
>  		scheduler->next_vgpu = vgpu;
>  		gvt_dbg_sched("pick next vgpu %d\n", vgpu->id);
> +
> +		/* Move the last used vGPU to the tail of lru_list */
> +		vgpu_data = vgpu->sched_data;
> +		list_del_init(&vgpu_data->lru_list);
> +		list_add_tail(&vgpu_data->lru_list, &sched_data-
> >lru_vgpu_head);
>  	}
>  out:
>  	if (scheduler->next_vgpu) {
> @@ -246,6 +309,7 @@ static int tbs_sched_init(struct intel_gvt *gvt)
>  		return -ENOMEM;
> 
>  	INIT_LIST_HEAD(&data->runq_head);
> +	INIT_LIST_HEAD(&data->lru_vgpu_head);
>  	hrtimer_init(&data->timer, CLOCK_MONOTONIC,
> HRTIMER_MODE_ABS);
>  	data->timer.function = tbs_timer_fn;
>  	data->period = GVT_DEFAULT_TIME_SLICE; @@ -278,6 +342,7 @@
> static int tbs_sched_init_vgpu(struct intel_vgpu *vgpu)
> 
>  	data->vgpu = vgpu;
>  	INIT_LIST_HEAD(&data->list);
> +	INIT_LIST_HEAD(&data->lru_list);
> 
>  	vgpu->sched_data = data;
>  	return 0;
> @@ -298,6 +363,7 @@ static void tbs_sched_start_schedule(struct
> intel_vgpu *vgpu)
>  		return;
> 
>  	list_add_tail(&vgpu_data->list, &sched_data->runq_head);
> +	list_add_tail(&vgpu_data->lru_list, &sched_data->lru_vgpu_head);
> 
>  	atomic_inc(&vgpu->gvt->num_vgpu_sched);
>  	if (atomic_read(&vgpu->gvt->num_vgpu_sched) ==
> HAS_ACTIVE_VGPU_SCHED) @@ -314,6 +380,7 @@ static void
> tbs_sched_stop_schedule(struct intel_vgpu *vgpu)
>  		return;
> 
>  	list_del_init(&vgpu_data->list);
> +	list_del_init(&vgpu_data->lru_list);
> 
>  	atomic_dec(&vgpu->gvt->num_vgpu_sched);
>  	if (atomic_read(&vgpu->gvt->num_vgpu_sched) ==
> ONLY_IDLE_VGPU_SCHED)
> --
> 2.7.4
> 
> _______________________________________________
> intel-gvt-dev mailing list
> intel-gvt-dev at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/intel-gvt-dev


More information about the intel-gvt-dev mailing list