Skip to content

Commit bd22e44

Browse files
ChristianKoenigAMDalexdeucher
authored andcommitted
drm/amdgpu: rework how isolation is enforced v2
Limiting the number of available VMIDs to enforce isolation causes some issues with gang submit and applying certain HW workarounds which require multiple VMIDs to work correctly. So instead start to track all submissions to the relevant engines in a per partition data structure and use the dma_fences of the submissions to enforce isolation similar to what a VMID limit does. v2: use ~0l for jobs without isolation to distinct it from kernel submissions which uses NULL for the owner. Add some warning when we are OOM. Signed-off-by: Christian König <christian.koenig@amd.com> Acked-by: Srinivasan Shanmugam <srinivasan.shanmugam@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
1 parent 7f11c59 commit bd22e44

6 files changed

Lines changed: 155 additions & 35 deletions

File tree

drivers/gpu/drm/amd/amdgpu/amdgpu.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1194,9 +1194,15 @@ struct amdgpu_device {
11941194
bool debug_exp_resets;
11951195
bool debug_disable_gpu_ring_reset;
11961196

1197-
bool enforce_isolation[MAX_XCP];
1198-
/* Added this mutex for cleaner shader isolation between GFX and compute processes */
1197+
/* Protection for the following isolation structure */
11991198
struct mutex enforce_isolation_mutex;
1199+
bool enforce_isolation[MAX_XCP];
1200+
struct amdgpu_isolation {
1201+
void *owner;
1202+
struct dma_fence *spearhead;
1203+
struct amdgpu_sync active;
1204+
struct amdgpu_sync prev;
1205+
} isolation[MAX_XCP];
12001206

12011207
struct amdgpu_init_level *init_lvl;
12021208

@@ -1482,6 +1488,9 @@ void amdgpu_device_pcie_port_wreg(struct amdgpu_device *adev,
14821488
struct dma_fence *amdgpu_device_get_gang(struct amdgpu_device *adev);
14831489
struct dma_fence *amdgpu_device_switch_gang(struct amdgpu_device *adev,
14841490
struct dma_fence *gang);
1491+
struct dma_fence *amdgpu_device_enforce_isolation(struct amdgpu_device *adev,
1492+
struct amdgpu_ring *ring,
1493+
struct amdgpu_job *job);
14851494
bool amdgpu_device_has_display_hardware(struct amdgpu_device *adev);
14861495
ssize_t amdgpu_get_soft_full_reset_mask(struct amdgpu_ring *ring);
14871496
ssize_t amdgpu_show_reset_mask(char *buf, uint32_t supported_reset);

drivers/gpu/drm/amd/amdgpu/amdgpu_device.c

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4294,6 +4294,11 @@ int amdgpu_device_init(struct amdgpu_device *adev,
42944294
mutex_init(&adev->gfx.reset_sem_mutex);
42954295
/* Initialize the mutex for cleaner shader isolation between GFX and compute processes */
42964296
mutex_init(&adev->enforce_isolation_mutex);
4297+
for (i = 0; i < MAX_XCP; ++i) {
4298+
adev->isolation[i].spearhead = dma_fence_get_stub();
4299+
amdgpu_sync_create(&adev->isolation[i].active);
4300+
amdgpu_sync_create(&adev->isolation[i].prev);
4301+
}
42974302
mutex_init(&adev->gfx.kfd_sch_mutex);
42984303
mutex_init(&adev->gfx.workload_profile_mutex);
42994304
mutex_init(&adev->vcn.workload_profile_mutex);
@@ -4799,14 +4804,19 @@ void amdgpu_device_fini_hw(struct amdgpu_device *adev)
47994804

48004805
void amdgpu_device_fini_sw(struct amdgpu_device *adev)
48014806
{
4802-
int idx;
4807+
int i, idx;
48034808
bool px;
48044809

48054810
amdgpu_device_ip_fini(adev);
48064811
amdgpu_fence_driver_sw_fini(adev);
48074812
amdgpu_ucode_release(&adev->firmware.gpu_info_fw);
48084813
adev->accel_working = false;
48094814
dma_fence_put(rcu_dereference_protected(adev->gang_submit, true));
4815+
for (i = 0; i < MAX_XCP; ++i) {
4816+
dma_fence_put(adev->isolation[i].spearhead);
4817+
amdgpu_sync_free(&adev->isolation[i].active);
4818+
amdgpu_sync_free(&adev->isolation[i].prev);
4819+
}
48104820

48114821
amdgpu_reset_fini(adev);
48124822

@@ -6953,6 +6963,92 @@ struct dma_fence *amdgpu_device_switch_gang(struct amdgpu_device *adev,
69536963
return NULL;
69546964
}
69556965

6966+
/**
6967+
* amdgpu_device_enforce_isolation - enforce HW isolation
6968+
* @adev: the amdgpu device pointer
6969+
* @ring: the HW ring the job is supposed to run on
6970+
* @job: the job which is about to be pushed to the HW ring
6971+
*
6972+
* Makes sure that only one client at a time can use the GFX block.
6973+
* Returns: The dependency to wait on before the job can be pushed to the HW.
6974+
* The function is called multiple times until NULL is returned.
6975+
*/
6976+
struct dma_fence *amdgpu_device_enforce_isolation(struct amdgpu_device *adev,
6977+
struct amdgpu_ring *ring,
6978+
struct amdgpu_job *job)
6979+
{
6980+
struct amdgpu_isolation *isolation = &adev->isolation[ring->xcp_id];
6981+
struct drm_sched_fence *f = job->base.s_fence;
6982+
struct dma_fence *dep;
6983+
void *owner;
6984+
int r;
6985+
6986+
/*
6987+
* For now enforce isolation only for the GFX block since we only need
6988+
* the cleaner shader on those rings.
6989+
*/
6990+
if (ring->funcs->type != AMDGPU_RING_TYPE_GFX &&
6991+
ring->funcs->type != AMDGPU_RING_TYPE_COMPUTE)
6992+
return NULL;
6993+
6994+
/*
6995+
* All submissions where enforce isolation is false are handled as if
6996+
* they come from a single client. Use ~0l as the owner to distinct it
6997+
* from kernel submissions where the owner is NULL.
6998+
*/
6999+
owner = job->enforce_isolation ? f->owner : (void *)~0l;
7000+
7001+
mutex_lock(&adev->enforce_isolation_mutex);
7002+
7003+
/*
7004+
* The "spearhead" submission is the first one which changes the
7005+
* ownership to its client. We always need to wait for it to be
7006+
* pushed to the HW before proceeding with anything.
7007+
*/
7008+
if (&f->scheduled != isolation->spearhead &&
7009+
!dma_fence_is_signaled(isolation->spearhead)) {
7010+
dep = isolation->spearhead;
7011+
goto out_grab_ref;
7012+
}
7013+
7014+
if (isolation->owner != owner) {
7015+
7016+
/*
7017+
* Wait for any gang to be assembled before switching to a
7018+
* different owner or otherwise we could deadlock the
7019+
* submissions.
7020+
*/
7021+
if (!job->gang_submit) {
7022+
dep = amdgpu_device_get_gang(adev);
7023+
if (!dma_fence_is_signaled(dep))
7024+
goto out_return_dep;
7025+
dma_fence_put(dep);
7026+
}
7027+
7028+
dma_fence_put(isolation->spearhead);
7029+
isolation->spearhead = dma_fence_get(&f->scheduled);
7030+
amdgpu_sync_move(&isolation->active, &isolation->prev);
7031+
isolation->owner = owner;
7032+
}
7033+
7034+
/*
7035+
* Specifying the ring here helps to pipeline submissions even when
7036+
* isolation is enabled. If that is not desired for testing NULL can be
7037+
* used instead of the ring to enforce a CPU round trip while switching
7038+
* between clients.
7039+
*/
7040+
dep = amdgpu_sync_peek_fence(&isolation->prev, ring);
7041+
r = amdgpu_sync_fence(&isolation->active, &f->finished, GFP_NOWAIT);
7042+
if (r)
7043+
DRM_WARN("OOM tracking isolation\n");
7044+
7045+
out_grab_ref:
7046+
dma_fence_get(dep);
7047+
out_return_dep:
7048+
mutex_unlock(&adev->enforce_isolation_mutex);
7049+
return dep;
7050+
}
7051+
69567052
bool amdgpu_device_has_display_hardware(struct amdgpu_device *adev)
69577053
{
69587054
switch (adev->asic_type) {

drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -287,40 +287,27 @@ static int amdgpu_vmid_grab_reserved(struct amdgpu_vm *vm,
287287
(*id)->flushed_updates < updates ||
288288
!(*id)->last_flush ||
289289
((*id)->last_flush->context != fence_context &&
290-
!dma_fence_is_signaled((*id)->last_flush))) {
290+
!dma_fence_is_signaled((*id)->last_flush)))
291+
needs_flush = true;
292+
293+
if ((*id)->owner != vm->immediate.fence_context ||
294+
(!adev->vm_manager.concurrent_flush && needs_flush)) {
291295
struct dma_fence *tmp;
292296

293-
/* Wait for the gang to be assembled before using a
294-
* reserved VMID or otherwise the gang could deadlock.
297+
/* Don't use per engine and per process VMID at the
298+
* same time
295299
*/
296-
tmp = amdgpu_device_get_gang(adev);
297-
if (!dma_fence_is_signaled(tmp) && tmp != job->gang_submit) {
300+
if (adev->vm_manager.concurrent_flush)
301+
ring = NULL;
302+
303+
/* to prevent one context starved by another context */
304+
(*id)->pd_gpu_addr = 0;
305+
tmp = amdgpu_sync_peek_fence(&(*id)->active, ring);
306+
if (tmp) {
298307
*id = NULL;
299-
*fence = tmp;
308+
*fence = dma_fence_get(tmp);
300309
return 0;
301310
}
302-
dma_fence_put(tmp);
303-
304-
/* Make sure the id is owned by the gang before proceeding */
305-
if (!job->gang_submit ||
306-
(*id)->owner != vm->immediate.fence_context) {
307-
308-
/* Don't use per engine and per process VMID at the
309-
* same time
310-
*/
311-
if (adev->vm_manager.concurrent_flush)
312-
ring = NULL;
313-
314-
/* to prevent one context starved by another context */
315-
(*id)->pd_gpu_addr = 0;
316-
tmp = amdgpu_sync_peek_fence(&(*id)->active, ring);
317-
if (tmp) {
318-
*id = NULL;
319-
*fence = dma_fence_get(tmp);
320-
return 0;
321-
}
322-
}
323-
needs_flush = true;
324311
}
325312

326313
/* Good we can use this VMID. Remember this submission as

drivers/gpu/drm/amd/amdgpu/amdgpu_job.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -361,17 +361,24 @@ amdgpu_job_prepare_job(struct drm_sched_job *sched_job,
361361
{
362362
struct amdgpu_ring *ring = to_amdgpu_ring(s_entity->rq->sched);
363363
struct amdgpu_job *job = to_amdgpu_job(sched_job);
364-
struct dma_fence *fence = NULL;
364+
struct dma_fence *fence;
365365
int r;
366366

367367
r = drm_sched_entity_error(s_entity);
368368
if (r)
369369
goto error;
370370

371-
if (job->gang_submit)
371+
if (job->gang_submit) {
372372
fence = amdgpu_device_switch_gang(ring->adev, job->gang_submit);
373+
if (fence)
374+
return fence;
375+
}
376+
377+
fence = amdgpu_device_enforce_isolation(ring->adev, ring, job);
378+
if (fence)
379+
return fence;
373380

374-
if (!fence && job->vm && !job->vmid) {
381+
if (job->vm && !job->vmid) {
375382
r = amdgpu_vmid_grab(job->vm, ring, job, &fence);
376383
if (r) {
377384
dev_err(ring->adev->dev, "Error getting VM ID (%d)\n", r);
@@ -384,9 +391,10 @@ amdgpu_job_prepare_job(struct drm_sched_job *sched_job,
384391
*/
385392
if (!fence)
386393
job->vm = NULL;
394+
return fence;
387395
}
388396

389-
return fence;
397+
return NULL;
390398

391399
error:
392400
dma_fence_set_error(&job->base.s_fence->finished, r);

drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,25 @@ int amdgpu_sync_clone(struct amdgpu_sync *source, struct amdgpu_sync *clone)
405405
return 0;
406406
}
407407

408+
/**
409+
* amdgpu_sync_move - move all fences from src to dst
410+
*
411+
* @src: source of the fences, empty after function
412+
* @dst: destination for the fences
413+
*
414+
* Moves all fences from source to destination. All fences in destination are
415+
* freed and source is empty after the function call.
416+
*/
417+
void amdgpu_sync_move(struct amdgpu_sync *src, struct amdgpu_sync *dst)
418+
{
419+
unsigned int i;
420+
421+
amdgpu_sync_free(dst);
422+
423+
for (i = 0; i < HASH_SIZE(src->fences); ++i)
424+
hlist_move_list(&src->fences[i], &dst->fences[i]);
425+
}
426+
408427
/**
409428
* amdgpu_sync_push_to_job - push fences into job
410429
* @sync: sync object to get the fences from

drivers/gpu/drm/amd/amdgpu/amdgpu_sync.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ struct dma_fence *amdgpu_sync_peek_fence(struct amdgpu_sync *sync,
5757
struct amdgpu_ring *ring);
5858
struct dma_fence *amdgpu_sync_get_fence(struct amdgpu_sync *sync);
5959
int amdgpu_sync_clone(struct amdgpu_sync *source, struct amdgpu_sync *clone);
60+
void amdgpu_sync_move(struct amdgpu_sync *src, struct amdgpu_sync *dst);
6061
int amdgpu_sync_push_to_job(struct amdgpu_sync *sync, struct amdgpu_job *job);
6162
int amdgpu_sync_wait(struct amdgpu_sync *sync, bool intr);
6263
void amdgpu_sync_free(struct amdgpu_sync *sync);

0 commit comments

Comments
 (0)