gpu_scheduler.c 13.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
/*
 * Copyright 2015 Advanced Micro Devices, Inc.
 *
 * 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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
 *
 *
 */
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <drm/drmP.h>
#include "gpu_scheduler.h"

30 31 32
#define CREATE_TRACE_POINTS
#include "gpu_sched_trace.h"

33
static bool amd_sched_entity_is_ready(struct amd_sched_entity *entity);
34 35
static void amd_sched_wakeup(struct amd_gpu_scheduler *sched);

36 37 38
struct kmem_cache *sched_fence_slab;
atomic_t sched_fence_slab_ref = ATOMIC_INIT(0);

39
/* Initialize a given run queue struct */
40
static void amd_sched_rq_init(struct amd_sched_rq *rq)
41
{
42
	spin_lock_init(&rq->lock);
43 44
	INIT_LIST_HEAD(&rq->entities);
	rq->current_entity = NULL;
45 46
}

47 48
static void amd_sched_rq_add_entity(struct amd_sched_rq *rq,
				    struct amd_sched_entity *entity)
49
{
50 51
	if (!list_empty(&entity->list))
		return;
52
	spin_lock(&rq->lock);
53
	list_add_tail(&entity->list, &rq->entities);
54
	spin_unlock(&rq->lock);
55 56
}

57 58
static void amd_sched_rq_remove_entity(struct amd_sched_rq *rq,
				       struct amd_sched_entity *entity)
59
{
60 61
	if (list_empty(&entity->list))
		return;
62
	spin_lock(&rq->lock);
63 64 65
	list_del_init(&entity->list);
	if (rq->current_entity == entity)
		rq->current_entity = NULL;
66
	spin_unlock(&rq->lock);
67 68 69
}

/**
70 71 72 73 74
 * Select an entity which could provide a job to run
 *
 * @rq		The run queue to check.
 *
 * Try to find a ready entity, returns NULL if none found.
75
 */
76 77
static struct amd_sched_entity *
amd_sched_rq_select_entity(struct amd_sched_rq *rq)
78
{
79
	struct amd_sched_entity *entity;
80

81 82 83
	spin_lock(&rq->lock);

	entity = rq->current_entity;
84 85
	if (entity) {
		list_for_each_entry_continue(entity, &rq->entities, list) {
86
			if (amd_sched_entity_is_ready(entity)) {
87
				rq->current_entity = entity;
88
				spin_unlock(&rq->lock);
89
				return entity;
90
			}
91 92 93
		}
	}

94
	list_for_each_entry(entity, &rq->entities, list) {
95

96
		if (amd_sched_entity_is_ready(entity)) {
97
			rq->current_entity = entity;
98
			spin_unlock(&rq->lock);
99
			return entity;
100
		}
101

102 103 104
		if (entity == rq->current_entity)
			break;
	}
105

106 107
	spin_unlock(&rq->lock);

108
	return NULL;
109 110 111 112 113 114
}

/**
 * Init a context entity used by scheduler when submit to HW ring.
 *
 * @sched	The pointer to the scheduler
115
 * @entity	The pointer to a valid amd_sched_entity
116
 * @rq		The run queue this entity belongs
117
 * @kernel	If this is an entity for the kernel
118
 * @jobs	The max number of jobs in the job queue
119 120 121
 *
 * return 0 if succeed. negative error code on failure
*/
122
int amd_sched_entity_init(struct amd_gpu_scheduler *sched,
123
			  struct amd_sched_entity *entity,
124
			  struct amd_sched_rq *rq,
125
			  uint32_t jobs)
126
{
127 128
	int r;

129 130 131
	if (!(sched && entity && rq))
		return -EINVAL;

132
	memset(entity, 0, sizeof(struct amd_sched_entity));
133 134 135
	INIT_LIST_HEAD(&entity->list);
	entity->rq = rq;
	entity->sched = sched;
136 137

	spin_lock_init(&entity->queue_lock);
138 139 140 141
	r = kfifo_alloc(&entity->job_queue, jobs * sizeof(void *), GFP_KERNEL);
	if (r)
		return r;

142
	atomic_set(&entity->fence_seq, 0);
143
	entity->fence_context = fence_context_alloc(1);
144 145 146 147 148 149 150 151 152 153 154 155

	return 0;
}

/**
 * Query if entity is initialized
 *
 * @sched       Pointer to scheduler instance
 * @entity	The pointer to a valid scheduler entity
 *
 * return true if entity is initialized, false otherwise
*/
156 157
static bool amd_sched_entity_is_initialized(struct amd_gpu_scheduler *sched,
					    struct amd_sched_entity *entity)
158
{
159 160
	return entity->sched == sched &&
		entity->rq != NULL;
161 162
}

163 164 165 166 167 168 169 170
/**
 * Check if entity is idle
 *
 * @entity	The pointer to a valid scheduler entity
 *
 * Return true if entity don't has any unscheduled jobs.
 */
static bool amd_sched_entity_is_idle(struct amd_sched_entity *entity)
171
{
172 173
	rmb();
	if (kfifo_is_empty(&entity->job_queue))
174 175 176 177 178
		return true;

	return false;
}

179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
/**
 * Check if entity is ready
 *
 * @entity	The pointer to a valid scheduler entity
 *
 * Return true if entity could provide a job.
 */
static bool amd_sched_entity_is_ready(struct amd_sched_entity *entity)
{
	if (kfifo_is_empty(&entity->job_queue))
		return false;

	if (ACCESS_ONCE(entity->dependency))
		return false;

	return true;
}

197 198 199 200 201 202
/**
 * Destroy a context entity
 *
 * @sched       Pointer to scheduler instance
 * @entity	The pointer to a valid scheduler entity
 *
203
 * Cleanup and free the allocated resources.
204
 */
205 206
void amd_sched_entity_fini(struct amd_gpu_scheduler *sched,
			   struct amd_sched_entity *entity)
207
{
208
	struct amd_sched_rq *rq = entity->rq;
209

210
	if (!amd_sched_entity_is_initialized(sched, entity))
211
		return;
212

213 214 215 216
	/**
	 * The client will not queue more IBs during this fini, consume existing
	 * queued IBs
	*/
217
	wait_event(sched->job_scheduled, amd_sched_entity_is_idle(entity));
218

219
	amd_sched_rq_remove_entity(rq, entity);
220 221 222
	kfifo_free(&entity->job_queue);
}

223 224 225 226 227 228
static void amd_sched_entity_wakeup(struct fence *f, struct fence_cb *cb)
{
	struct amd_sched_entity *entity =
		container_of(cb, struct amd_sched_entity, cb);
	entity->dependency = NULL;
	fence_put(f);
229
	amd_sched_wakeup(entity->sched);
230 231
}

232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
static bool amd_sched_entity_add_dependency_cb(struct amd_sched_entity *entity)
{
	struct amd_gpu_scheduler *sched = entity->sched;
	struct fence * fence = entity->dependency;
	struct amd_sched_fence *s_fence;

	if (fence->context == entity->fence_context) {
		/* We can ignore fences from ourself */
		fence_put(entity->dependency);
		return false;
	}

	s_fence = to_amd_sched_fence(fence);
	if (s_fence && s_fence->sched == sched) {
		/* Fence is from the same scheduler */
		if (test_bit(AMD_SCHED_FENCE_SCHEDULED_BIT, &fence->flags)) {
			/* Ignore it when it is already scheduled */
			fence_put(entity->dependency);
			return false;
		}

		/* Wait for fence to be scheduled */
		entity->cb.func = amd_sched_entity_wakeup;
		list_add_tail(&entity->cb.node, &s_fence->scheduled_cb);
		return true;
	}

	if (!fence_add_callback(entity->dependency, &entity->cb,
				amd_sched_entity_wakeup))
		return true;

	fence_put(entity->dependency);
	return false;
}

267 268 269
static struct amd_sched_job *
amd_sched_entity_pop_job(struct amd_sched_entity *entity)
{
270
	struct amd_gpu_scheduler *sched = entity->sched;
271
	struct amd_sched_job *sched_job;
272

273
	if (!kfifo_out_peek(&entity->job_queue, &sched_job, sizeof(sched_job)))
274 275
		return NULL;

276 277
	while ((entity->dependency = sched->ops->dependency(sched_job)))
		if (amd_sched_entity_add_dependency_cb(entity))
278 279
			return NULL;

280
	return sched_job;
281 282
}

283
/**
284
 * Helper to submit a job to the job queue
285
 *
286
 * @sched_job		The pointer to job required to submit
287 288 289
 *
 * Returns true if we could submit the job.
 */
290
static bool amd_sched_entity_in(struct amd_sched_job *sched_job)
291
{
292
	struct amd_gpu_scheduler *sched = sched_job->sched;
293
	struct amd_sched_entity *entity = sched_job->s_entity;
294 295 296
	bool added, first = false;

	spin_lock(&entity->queue_lock);
297 298
	added = kfifo_in(&entity->job_queue, &sched_job,
			sizeof(sched_job)) == sizeof(sched_job);
299

300
	if (added && kfifo_len(&entity->job_queue) == sizeof(sched_job))
301 302 303 304 305
		first = true;

	spin_unlock(&entity->queue_lock);

	/* first job wakes up scheduler */
306 307 308
	if (first) {
		/* Add the entity to the run queue */
		amd_sched_rq_add_entity(entity->rq, entity);
309
		amd_sched_wakeup(sched);
310
	}
311 312 313 314 315 316
	return added;
}

/**
 * Submit a job to the job queue
 *
317
 * @sched_job		The pointer to job required to submit
318 319 320
 *
 * Returns 0 for success, negative error code otherwise.
 */
321
void amd_sched_entity_push_job(struct amd_sched_job *sched_job)
322 323 324
{
	struct amd_sched_entity *entity = sched_job->s_entity;

325
	trace_amd_sched_job(sched_job);
326
	wait_event(entity->sched->job_scheduled,
327
		   amd_sched_entity_in(sched_job));
328 329
}

330 331 332 333 334 335 336 337 338
/**
 * Return ture if we can push more jobs to the hw.
 */
static bool amd_sched_ready(struct amd_gpu_scheduler *sched)
{
	return atomic_read(&sched->hw_rq_count) <
		sched->hw_submission_limit;
}

339 340 341 342 343 344
/**
 * Wake up the scheduler when it is ready
 */
static void amd_sched_wakeup(struct amd_gpu_scheduler *sched)
{
	if (amd_sched_ready(sched))
345
		wake_up_interruptible(&sched->wake_up_worker);
346 347
}

348
/**
349
 * Select next entity to process
350
*/
351 352
static struct amd_sched_entity *
amd_sched_select_entity(struct amd_gpu_scheduler *sched)
353
{
354
	struct amd_sched_entity *entity;
355
	int i;
356 357 358 359 360

	if (!amd_sched_ready(sched))
		return NULL;

	/* Kernel run queue has higher priority than normal run queue*/
361 362 363 364 365
	for (i = 0; i < AMD_SCHED_MAX_PRIORITY; i++) {
		entity = amd_sched_rq_select_entity(&sched->sched_rq[i]);
		if (entity)
			break;
	}
366

367
	return entity;
368 369
}

370 371
static void amd_sched_process_job(struct fence *f, struct fence_cb *cb)
{
372 373
	struct amd_sched_fence *s_fence =
		container_of(cb, struct amd_sched_fence, cb);
374
	struct amd_gpu_scheduler *sched = s_fence->sched;
375
	unsigned long flags;
376

377
	atomic_dec(&sched->hw_rq_count);
378
	amd_sched_fence_signal(s_fence);
379
	if (sched->timeout != MAX_SCHEDULE_TIMEOUT) {
380
		cancel_delayed_work(&s_fence->dwork);
381 382 383 384
		spin_lock_irqsave(&sched->fence_list_lock, flags);
		list_del_init(&s_fence->list);
		spin_unlock_irqrestore(&sched->fence_list_lock, flags);
	}
385
	trace_amd_sched_process_job(s_fence);
386
	fence_put(&s_fence->base);
387
	wake_up_interruptible(&sched->wake_up_worker);
388 389
}

390 391 392 393 394 395 396 397 398 399 400
static void amd_sched_fence_work_func(struct work_struct *work)
{
	struct amd_sched_fence *s_fence =
		container_of(work, struct amd_sched_fence, dwork.work);
	struct amd_gpu_scheduler *sched = s_fence->sched;
	struct amd_sched_fence *entity, *tmp;
	unsigned long flags;

	DRM_ERROR("[%s] scheduler is timeout!\n", sched->name);

	/* Clean all pending fences */
401
	spin_lock_irqsave(&sched->fence_list_lock, flags);
402 403
	list_for_each_entry_safe(entity, tmp, &sched->fence_list, list) {
		DRM_ERROR("  fence no %d\n", entity->base.seqno);
404
		cancel_delayed_work(&entity->dwork);
405 406 407
		list_del_init(&entity->list);
		fence_put(&entity->base);
	}
408
	spin_unlock_irqrestore(&sched->fence_list_lock, flags);
409 410
}

411 412 413 414
static int amd_sched_main(void *param)
{
	struct sched_param sparam = {.sched_priority = 1};
	struct amd_gpu_scheduler *sched = (struct amd_gpu_scheduler *)param;
415
	int r, count;
416

417 418
	spin_lock_init(&sched->fence_list_lock);
	INIT_LIST_HEAD(&sched->fence_list);
419 420 421
	sched_setscheduler(current, SCHED_FIFO, &sparam);

	while (!kthread_should_stop()) {
422
		struct amd_sched_entity *entity;
423
		struct amd_sched_fence *s_fence;
424
		struct amd_sched_job *sched_job;
425
		struct fence *fence;
426
		unsigned long flags;
427

428
		wait_event_interruptible(sched->wake_up_worker,
429 430
			(entity = amd_sched_select_entity(sched)) ||
			kthread_should_stop());
431

432 433 434 435
		if (!entity)
			continue;

		sched_job = amd_sched_entity_pop_job(entity);
436
		if (!sched_job)
437 438
			continue;

439
		s_fence = sched_job->s_fence;
440 441 442 443 444 445 446 447 448

		if (sched->timeout != MAX_SCHEDULE_TIMEOUT) {
			INIT_DELAYED_WORK(&s_fence->dwork, amd_sched_fence_work_func);
			schedule_delayed_work(&s_fence->dwork, sched->timeout);
			spin_lock_irqsave(&sched->fence_list_lock, flags);
			list_add_tail(&s_fence->list, &sched->fence_list);
			spin_unlock_irqrestore(&sched->fence_list_lock, flags);
		}

449
		atomic_inc(&sched->hw_rq_count);
450
		fence = sched->ops->run_job(sched_job);
451
		amd_sched_fence_scheduled(s_fence);
452
		if (fence) {
453
			r = fence_add_callback(fence, &s_fence->cb,
454 455
					       amd_sched_process_job);
			if (r == -ENOENT)
456
				amd_sched_process_job(fence, &s_fence->cb);
457 458 459
			else if (r)
				DRM_ERROR("fence add callback failed (%d)\n", r);
			fence_put(fence);
460 461
		} else {
			DRM_ERROR("Failed to run job!\n");
462
			amd_sched_process_job(NULL, &s_fence->cb);
463
		}
464

465 466 467
		count = kfifo_out(&entity->job_queue, &sched_job,
				sizeof(sched_job));
		WARN_ON(count != sizeof(sched_job));
468
		wake_up(&sched->job_scheduled);
469 470 471 472 473
	}
	return 0;
}

/**
474
 * Init a gpu scheduler instance
475
 *
476
 * @sched		The pointer to the scheduler
477 478
 * @ops			The backend operations for this scheduler.
 * @hw_submissions	Number of hw submissions to do.
479
 * @name		Name used for debugging
480
 *
481
 * Return 0 on success, otherwise error code.
482
*/
483 484
int amd_sched_init(struct amd_gpu_scheduler *sched,
		   struct amd_sched_backend_ops *ops,
485
		   unsigned hw_submission, long timeout, const char *name)
486
{
487
	int i;
488
	sched->ops = ops;
489
	sched->hw_submission_limit = hw_submission;
490
	sched->name = name;
491
	sched->timeout = timeout;
492 493
	for (i = 0; i < AMD_SCHED_MAX_PRIORITY; i++)
		amd_sched_rq_init(&sched->sched_rq[i]);
494

495 496
	init_waitqueue_head(&sched->wake_up_worker);
	init_waitqueue_head(&sched->job_scheduled);
497
	atomic_set(&sched->hw_rq_count, 0);
498 499 500 501 502 503 504
	if (atomic_inc_return(&sched_fence_slab_ref) == 1) {
		sched_fence_slab = kmem_cache_create(
			"amd_sched_fence", sizeof(struct amd_sched_fence), 0,
			SLAB_HWCACHE_ALIGN, NULL);
		if (!sched_fence_slab)
			return -ENOMEM;
	}
505

506
	/* Each scheduler will run on a seperate kernel thread */
507
	sched->thread = kthread_run(amd_sched_main, sched, sched->name);
508
	if (IS_ERR(sched->thread)) {
509 510
		DRM_ERROR("Failed to create scheduler for %s.\n", name);
		return PTR_ERR(sched->thread);
511 512
	}

513
	return 0;
514 515 516 517 518 519 520
}

/**
 * Destroy a gpu scheduler
 *
 * @sched	The pointer to the scheduler
 */
521
void amd_sched_fini(struct amd_gpu_scheduler *sched)
522
{
523 524
	if (sched->thread)
		kthread_stop(sched->thread);
525 526
	if (atomic_dec_and_test(&sched_fence_slab_ref))
		kmem_cache_destroy(sched_fence_slab);
527
}