1620f774fSChristian König /*
2620f774fSChristian König  * Copyright 2017 Advanced Micro Devices, Inc.
3620f774fSChristian König  *
4620f774fSChristian König  * Permission is hereby granted, free of charge, to any person obtaining a
5620f774fSChristian König  * copy of this software and associated documentation files (the "Software"),
6620f774fSChristian König  * to deal in the Software without restriction, including without limitation
7620f774fSChristian König  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8620f774fSChristian König  * and/or sell copies of the Software, and to permit persons to whom the
9620f774fSChristian König  * Software is furnished to do so, subject to the following conditions:
10620f774fSChristian König  *
11620f774fSChristian König  * The above copyright notice and this permission notice shall be included in
12620f774fSChristian König  * all copies or substantial portions of the Software.
13620f774fSChristian König  *
14620f774fSChristian König  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15620f774fSChristian König  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16620f774fSChristian König  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17620f774fSChristian König  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18620f774fSChristian König  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19620f774fSChristian König  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20620f774fSChristian König  * OTHER DEALINGS IN THE SOFTWARE.
21620f774fSChristian König  *
22620f774fSChristian König  */
23620f774fSChristian König #include "amdgpu_ids.h"
24620f774fSChristian König 
25620f774fSChristian König #include <linux/idr.h>
26620f774fSChristian König #include <linux/dma-fence-array.h>
27fdf2f6c5SSam Ravnborg 
28620f774fSChristian König 
29620f774fSChristian König #include "amdgpu.h"
30620f774fSChristian König #include "amdgpu_trace.h"
31620f774fSChristian König 
32620f774fSChristian König /*
33620f774fSChristian König  * PASID manager
34620f774fSChristian König  *
35620f774fSChristian König  * PASIDs are global address space identifiers that can be shared
36620f774fSChristian König  * between the GPU, an IOMMU and the driver. VMs on different devices
37620f774fSChristian König  * may use the same PASID if they share the same address
38620f774fSChristian König  * space. Therefore PASIDs are allocated using a global IDA. VMs are
39620f774fSChristian König  * looked up from the PASID per amdgpu_device.
40620f774fSChristian König  */
41620f774fSChristian König static DEFINE_IDA(amdgpu_pasid_ida);
42620f774fSChristian König 
434b5f7550SChristian König /* Helper to free pasid from a fence callback */
444b5f7550SChristian König struct amdgpu_pasid_cb {
454b5f7550SChristian König 	struct dma_fence_cb cb;
46c7b6bac9SFenghua Yu 	u32 pasid;
474b5f7550SChristian König };
484b5f7550SChristian König 
49620f774fSChristian König /**
50620f774fSChristian König  * amdgpu_pasid_alloc - Allocate a PASID
51620f774fSChristian König  * @bits: Maximum width of the PASID in bits, must be at least 1
52620f774fSChristian König  *
53620f774fSChristian König  * Allocates a PASID of the given width while keeping smaller PASIDs
54620f774fSChristian König  * available if possible.
55620f774fSChristian König  *
56620f774fSChristian König  * Returns a positive integer on success. Returns %-EINVAL if bits==0.
57620f774fSChristian König  * Returns %-ENOSPC if no PASID was available. Returns %-ENOMEM on
58620f774fSChristian König  * memory allocation failure.
59620f774fSChristian König  */
amdgpu_pasid_alloc(unsigned int bits)60620f774fSChristian König int amdgpu_pasid_alloc(unsigned int bits)
61620f774fSChristian König {
62620f774fSChristian König 	int pasid = -EINVAL;
63620f774fSChristian König 
64620f774fSChristian König 	for (bits = min(bits, 31U); bits > 0; bits--) {
658a1f7fddSChristophe JAILLET 		pasid = ida_alloc_range(&amdgpu_pasid_ida, 1U << (bits - 1),
668a1f7fddSChristophe JAILLET 					(1U << bits) - 1, GFP_KERNEL);
67620f774fSChristian König 		if (pasid != -ENOSPC)
68620f774fSChristian König 			break;
69620f774fSChristian König 	}
70620f774fSChristian König 
71c35ff188SChristian König 	if (pasid >= 0)
72c35ff188SChristian König 		trace_amdgpu_pasid_allocated(pasid);
73c35ff188SChristian König 
74620f774fSChristian König 	return pasid;
75620f774fSChristian König }
76620f774fSChristian König 
77620f774fSChristian König /**
78620f774fSChristian König  * amdgpu_pasid_free - Free a PASID
79620f774fSChristian König  * @pasid: PASID to free
80620f774fSChristian König  */
amdgpu_pasid_free(u32 pasid)81c7b6bac9SFenghua Yu void amdgpu_pasid_free(u32 pasid)
82620f774fSChristian König {
83c35ff188SChristian König 	trace_amdgpu_pasid_freed(pasid);
848a1f7fddSChristophe JAILLET 	ida_free(&amdgpu_pasid_ida, pasid);
85620f774fSChristian König }
86620f774fSChristian König 
amdgpu_pasid_free_cb(struct dma_fence * fence,struct dma_fence_cb * _cb)874b5f7550SChristian König static void amdgpu_pasid_free_cb(struct dma_fence *fence,
884b5f7550SChristian König 				 struct dma_fence_cb *_cb)
894b5f7550SChristian König {
904b5f7550SChristian König 	struct amdgpu_pasid_cb *cb =
914b5f7550SChristian König 		container_of(_cb, struct amdgpu_pasid_cb, cb);
924b5f7550SChristian König 
934b5f7550SChristian König 	amdgpu_pasid_free(cb->pasid);
944b5f7550SChristian König 	dma_fence_put(fence);
954b5f7550SChristian König 	kfree(cb);
964b5f7550SChristian König }
974b5f7550SChristian König 
984b5f7550SChristian König /**
994b5f7550SChristian König  * amdgpu_pasid_free_delayed - free pasid when fences signal
1004b5f7550SChristian König  *
1014b5f7550SChristian König  * @resv: reservation object with the fences to wait for
1024b5f7550SChristian König  * @pasid: pasid to free
1034b5f7550SChristian König  *
1044b5f7550SChristian König  * Free the pasid only after all the fences in resv are signaled.
1054b5f7550SChristian König  */
amdgpu_pasid_free_delayed(struct dma_resv * resv,u32 pasid)10652791eeeSChristian König void amdgpu_pasid_free_delayed(struct dma_resv *resv,
107c7b6bac9SFenghua Yu 			       u32 pasid)
1084b5f7550SChristian König {
1094b5f7550SChristian König 	struct amdgpu_pasid_cb *cb;
110ba5f33ccSChristian König 	struct dma_fence *fence;
1114b5f7550SChristian König 	int r;
1124b5f7550SChristian König 
1130cc848a7SChristian König 	r = dma_resv_get_singleton(resv, DMA_RESV_USAGE_BOOKKEEP, &fence);
1144b5f7550SChristian König 	if (r)
1154b5f7550SChristian König 		goto fallback;
1164b5f7550SChristian König 
117ba5f33ccSChristian König 	if (!fence) {
1184b5f7550SChristian König 		amdgpu_pasid_free(pasid);
1194b5f7550SChristian König 		return;
1204b5f7550SChristian König 	}
1214b5f7550SChristian König 
1224b5f7550SChristian König 	cb = kmalloc(sizeof(*cb), GFP_KERNEL);
1234b5f7550SChristian König 	if (!cb) {
1244b5f7550SChristian König 		/* Last resort when we are OOM */
1254b5f7550SChristian König 		dma_fence_wait(fence, false);
1264b5f7550SChristian König 		dma_fence_put(fence);
1274b5f7550SChristian König 		amdgpu_pasid_free(pasid);
1284b5f7550SChristian König 	} else {
1294b5f7550SChristian König 		cb->pasid = pasid;
1304b5f7550SChristian König 		if (dma_fence_add_callback(fence, &cb->cb,
1314b5f7550SChristian König 					   amdgpu_pasid_free_cb))
1324b5f7550SChristian König 			amdgpu_pasid_free_cb(fence, &cb->cb);
1334b5f7550SChristian König 	}
1344b5f7550SChristian König 
1354b5f7550SChristian König 	return;
1364b5f7550SChristian König 
1374b5f7550SChristian König fallback:
1384b5f7550SChristian König 	/* Not enough memory for the delayed delete, as last resort
1394b5f7550SChristian König 	 * block for all the fences to complete.
1404b5f7550SChristian König 	 */
1410cc848a7SChristian König 	dma_resv_wait_timeout(resv, DMA_RESV_USAGE_BOOKKEEP,
1427bc80a54SChristian König 			      false, MAX_SCHEDULE_TIMEOUT);
1434b5f7550SChristian König 	amdgpu_pasid_free(pasid);
1444b5f7550SChristian König }
1454b5f7550SChristian König 
146620f774fSChristian König /*
147620f774fSChristian König  * VMID manager
148620f774fSChristian König  *
149620f774fSChristian König  * VMIDs are a per VMHUB identifier for page tables handling.
150620f774fSChristian König  */
151620f774fSChristian König 
152620f774fSChristian König /**
153620f774fSChristian König  * amdgpu_vmid_had_gpu_reset - check if reset occured since last use
154620f774fSChristian König  *
155620f774fSChristian König  * @adev: amdgpu_device pointer
156620f774fSChristian König  * @id: VMID structure
157620f774fSChristian König  *
158620f774fSChristian König  * Check if GPU reset occured since last use of the VMID.
159620f774fSChristian König  */
amdgpu_vmid_had_gpu_reset(struct amdgpu_device * adev,struct amdgpu_vmid * id)160620f774fSChristian König bool amdgpu_vmid_had_gpu_reset(struct amdgpu_device *adev,
161620f774fSChristian König 			       struct amdgpu_vmid *id)
162620f774fSChristian König {
163620f774fSChristian König 	return id->current_gpu_reset_count !=
164620f774fSChristian König 		atomic_read(&adev->gpu_reset_counter);
165620f774fSChristian König }
166620f774fSChristian König 
16756b0989eSChristian König /* Check if we need to switch to another set of resources */
amdgpu_vmid_gds_switch_needed(struct amdgpu_vmid * id,struct amdgpu_job * job)16856b0989eSChristian König static bool amdgpu_vmid_gds_switch_needed(struct amdgpu_vmid *id,
16956b0989eSChristian König 					  struct amdgpu_job *job)
17056b0989eSChristian König {
17156b0989eSChristian König 	return id->gds_base != job->gds_base ||
17256b0989eSChristian König 		id->gds_size != job->gds_size ||
17356b0989eSChristian König 		id->gws_base != job->gws_base ||
17456b0989eSChristian König 		id->gws_size != job->gws_size ||
17556b0989eSChristian König 		id->oa_base != job->oa_base ||
17656b0989eSChristian König 		id->oa_size != job->oa_size;
17756b0989eSChristian König }
17856b0989eSChristian König 
17956b0989eSChristian König /* Check if the id is compatible with the job */
amdgpu_vmid_compatible(struct amdgpu_vmid * id,struct amdgpu_job * job)18056b0989eSChristian König static bool amdgpu_vmid_compatible(struct amdgpu_vmid *id,
18156b0989eSChristian König 				   struct amdgpu_job *job)
18256b0989eSChristian König {
18356b0989eSChristian König 	return  id->pd_gpu_addr == job->vm_pd_addr &&
18456b0989eSChristian König 		!amdgpu_vmid_gds_switch_needed(id, job);
18556b0989eSChristian König }
18656b0989eSChristian König 
1873a80e92bSChristian König /**
188c4cd63f9SLee Jones  * amdgpu_vmid_grab_idle - grab idle VMID
1893a80e92bSChristian König  *
1903a80e92bSChristian König  * @ring: ring we want to submit job to
1913a80e92bSChristian König  * @idle: resulting idle VMID
192940ca22bSChristian König  * @fence: fence to wait for if no id could be grabbed
1933a80e92bSChristian König  *
1943a80e92bSChristian König  * Try to find an idle VMID, if none is idle add a fence to wait to the sync
1953a80e92bSChristian König  * object. Returns -ENOMEM when we are out of memory.
1963a80e92bSChristian König  */
amdgpu_vmid_grab_idle(struct amdgpu_ring * ring,struct amdgpu_vmid ** idle,struct dma_fence ** fence)1974d5dc626SYifan Zhang static int amdgpu_vmid_grab_idle(struct amdgpu_ring *ring,
198940ca22bSChristian König 				 struct amdgpu_vmid **idle,
199940ca22bSChristian König 				 struct dma_fence **fence)
2003a80e92bSChristian König {
2013a80e92bSChristian König 	struct amdgpu_device *adev = ring->adev;
2020530553bSLe Ma 	unsigned vmhub = ring->vm_hub;
2033a80e92bSChristian König 	struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub];
2043a80e92bSChristian König 	struct dma_fence **fences;
2053a80e92bSChristian König 	unsigned i;
2063a80e92bSChristian König 
207940ca22bSChristian König 	if (!dma_fence_is_signaled(ring->vmid_wait)) {
208940ca22bSChristian König 		*fence = dma_fence_get(ring->vmid_wait);
209940ca22bSChristian König 		return 0;
210940ca22bSChristian König 	}
2113af81440SChristian König 
21216590745SChristian König 	fences = kmalloc_array(id_mgr->num_ids, sizeof(void *), GFP_NOWAIT);
2133a80e92bSChristian König 	if (!fences)
2143a80e92bSChristian König 		return -ENOMEM;
2153a80e92bSChristian König 
2163a80e92bSChristian König 	/* Check if we have an idle VMID */
2173a80e92bSChristian König 	i = 0;
2183a80e92bSChristian König 	list_for_each_entry((*idle), &id_mgr->ids_lru, list) {
21920a5f5a9SChristian König 		/* Don't use per engine and per process VMID at the same time */
22020a5f5a9SChristian König 		struct amdgpu_ring *r = adev->vm_manager.concurrent_flush ?
22120a5f5a9SChristian König 			NULL : ring;
22220a5f5a9SChristian König 
22320a5f5a9SChristian König 		fences[i] = amdgpu_sync_peek_fence(&(*idle)->active, r);
2243a80e92bSChristian König 		if (!fences[i])
2253a80e92bSChristian König 			break;
2263a80e92bSChristian König 		++i;
2273a80e92bSChristian König 	}
2283a80e92bSChristian König 
2293a80e92bSChristian König 	/* If we can't find a idle VMID to use, wait till one becomes available */
2303a80e92bSChristian König 	if (&(*idle)->list == &id_mgr->ids_lru) {
2313a80e92bSChristian König 		u64 fence_context = adev->vm_manager.fence_context + ring->idx;
2323a80e92bSChristian König 		unsigned seqno = ++adev->vm_manager.seqno[ring->idx];
2333a80e92bSChristian König 		struct dma_fence_array *array;
2343a80e92bSChristian König 		unsigned j;
2353a80e92bSChristian König 
2363a80e92bSChristian König 		*idle = NULL;
2373a80e92bSChristian König 		for (j = 0; j < i; ++j)
2383a80e92bSChristian König 			dma_fence_get(fences[j]);
2393a80e92bSChristian König 
2403a80e92bSChristian König 		array = dma_fence_array_create(i, fences, fence_context,
2413a80e92bSChristian König 					       seqno, true);
2423a80e92bSChristian König 		if (!array) {
2433a80e92bSChristian König 			for (j = 0; j < i; ++j)
2443a80e92bSChristian König 				dma_fence_put(fences[j]);
2453a80e92bSChristian König 			kfree(fences);
2463a80e92bSChristian König 			return -ENOMEM;
2473a80e92bSChristian König 		}
2483a80e92bSChristian König 
249940ca22bSChristian König 		*fence = dma_fence_get(&array->base);
2503af81440SChristian König 		dma_fence_put(ring->vmid_wait);
2513af81440SChristian König 		ring->vmid_wait = &array->base;
252940ca22bSChristian König 		return 0;
2533a80e92bSChristian König 	}
2543a80e92bSChristian König 	kfree(fences);
2553a80e92bSChristian König 
2563a80e92bSChristian König 	return 0;
2573a80e92bSChristian König }
2583a80e92bSChristian König 
259cb5372acSChristian König /**
260c4cd63f9SLee Jones  * amdgpu_vmid_grab_reserved - try to assign reserved VMID
261cb5372acSChristian König  *
262cb5372acSChristian König  * @vm: vm to allocate id for
263cb5372acSChristian König  * @ring: ring we want to submit job to
264cb5372acSChristian König  * @job: job who wants to use the VMID
2658ebc6df6SLee Jones  * @id: resulting VMID
266940ca22bSChristian König  * @fence: fence to wait for if no id could be grabbed
267cb5372acSChristian König  *
268cb5372acSChristian König  * Try to assign a reserved VMID.
269cb5372acSChristian König  */
amdgpu_vmid_grab_reserved(struct amdgpu_vm * vm,struct amdgpu_ring * ring,struct amdgpu_job * job,struct amdgpu_vmid ** id,struct dma_fence ** fence)270cb5372acSChristian König static int amdgpu_vmid_grab_reserved(struct amdgpu_vm *vm,
271620f774fSChristian König 				     struct amdgpu_ring *ring,
27258592a09SChristian König 				     struct amdgpu_job *job,
273940ca22bSChristian König 				     struct amdgpu_vmid **id,
274940ca22bSChristian König 				     struct dma_fence **fence)
275620f774fSChristian König {
276620f774fSChristian König 	struct amdgpu_device *adev = ring->adev;
2770530553bSLe Ma 	unsigned vmhub = ring->vm_hub;
278e44a0fe6SChristian König 	struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub];
279620f774fSChristian König 	uint64_t fence_context = adev->fence_context + ring->idx;
280620f774fSChristian König 	bool needs_flush = vm->use_cpu_for_update;
2815255e146SChristian König 	uint64_t updates = amdgpu_vm_tlb_seq(vm);
282e997b827SChristian König 	int r;
283620f774fSChristian König 
284e44a0fe6SChristian König 	*id = id_mgr->reserved;
285eaad0c3aSChristian König 	if ((*id)->owner != vm->immediate.fence_context ||
28656b0989eSChristian König 	    !amdgpu_vmid_compatible(*id, job) ||
287e997b827SChristian König 	    (*id)->flushed_updates < updates ||
288e997b827SChristian König 	    !(*id)->last_flush ||
28958592a09SChristian König 	    ((*id)->last_flush->context != fence_context &&
290bd22e44aSChristian König 	     !dma_fence_is_signaled((*id)->last_flush)))
291bd22e44aSChristian König 		needs_flush = true;
292bd22e44aSChristian König 
293bd22e44aSChristian König 	if ((*id)->owner != vm->immediate.fence_context ||
294bd22e44aSChristian König 	    (!adev->vm_manager.concurrent_flush && needs_flush)) {
295cb5372acSChristian König 		struct dma_fence *tmp;
296cb5372acSChristian König 
297a6328c9cSChristian König 		/* Don't use per engine and per process VMID at the
298a6328c9cSChristian König 		 * same time
299a6328c9cSChristian König 		 */
30020a5f5a9SChristian König 		if (adev->vm_manager.concurrent_flush)
30120a5f5a9SChristian König 			ring = NULL;
30220a5f5a9SChristian König 
303620f774fSChristian König 		/* to prevent one context starved by another context */
30458592a09SChristian König 		(*id)->pd_gpu_addr = 0;
30558592a09SChristian König 		tmp = amdgpu_sync_peek_fence(&(*id)->active, ring);
306620f774fSChristian König 		if (tmp) {
30758592a09SChristian König 			*id = NULL;
308940ca22bSChristian König 			*fence = dma_fence_get(tmp);
309940ca22bSChristian König 			return 0;
310620f774fSChristian König 		}
311a6328c9cSChristian König 	}
312620f774fSChristian König 
313620f774fSChristian König 	/* Good we can use this VMID. Remember this submission as
314620f774fSChristian König 	* user of the VMID.
315620f774fSChristian König 	*/
31616590745SChristian König 	r = amdgpu_sync_fence(&(*id)->active, &job->base.s_fence->finished,
31716590745SChristian König 			      GFP_NOWAIT);
318620f774fSChristian König 	if (r)
319cb5372acSChristian König 		return r;
320620f774fSChristian König 
321620f774fSChristian König 	job->vm_needs_flush = needs_flush;
3225f3c40e9SChristian König 	job->spm_update_needed = true;
323cb5372acSChristian König 	return 0;
324620f774fSChristian König }
325620f774fSChristian König 
326620f774fSChristian König /**
327c4cd63f9SLee Jones  * amdgpu_vmid_grab_used - try to reuse a VMID
32825ddf75bSChristian König  *
32925ddf75bSChristian König  * @vm: vm to allocate id for
33025ddf75bSChristian König  * @ring: ring we want to submit job to
33125ddf75bSChristian König  * @job: job who wants to use the VMID
33225ddf75bSChristian König  * @id: resulting VMID
33325ddf75bSChristian König  *
33425ddf75bSChristian König  * Try to reuse a VMID for this submission.
33525ddf75bSChristian König  */
amdgpu_vmid_grab_used(struct amdgpu_vm * vm,struct amdgpu_ring * ring,struct amdgpu_job * job,struct amdgpu_vmid ** id)33625ddf75bSChristian König static int amdgpu_vmid_grab_used(struct amdgpu_vm *vm,
33725ddf75bSChristian König 				 struct amdgpu_ring *ring,
33825ddf75bSChristian König 				 struct amdgpu_job *job,
339fc282e9eSTvrtko Ursulin 				 struct amdgpu_vmid **id)
34025ddf75bSChristian König {
34125ddf75bSChristian König 	struct amdgpu_device *adev = ring->adev;
3420530553bSLe Ma 	unsigned vmhub = ring->vm_hub;
34325ddf75bSChristian König 	struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub];
34425ddf75bSChristian König 	uint64_t fence_context = adev->fence_context + ring->idx;
3455255e146SChristian König 	uint64_t updates = amdgpu_vm_tlb_seq(vm);
34625ddf75bSChristian König 	int r;
34725ddf75bSChristian König 
34825ddf75bSChristian König 	job->vm_needs_flush = vm->use_cpu_for_update;
34925ddf75bSChristian König 
35025ddf75bSChristian König 	/* Check if we can use a VMID already assigned to this VM */
35125ddf75bSChristian König 	list_for_each_entry_reverse((*id), &id_mgr->ids_lru, list) {
35225ddf75bSChristian König 		bool needs_flush = vm->use_cpu_for_update;
35325ddf75bSChristian König 
35425ddf75bSChristian König 		/* Check all the prerequisites to using this VMID */
355eaad0c3aSChristian König 		if ((*id)->owner != vm->immediate.fence_context)
35625ddf75bSChristian König 			continue;
35725ddf75bSChristian König 
35856b0989eSChristian König 		if (!amdgpu_vmid_compatible(*id, job))
35925ddf75bSChristian König 			continue;
36025ddf75bSChristian König 
36125ddf75bSChristian König 		if (!(*id)->last_flush ||
36225ddf75bSChristian König 		    ((*id)->last_flush->context != fence_context &&
36325ddf75bSChristian König 		     !dma_fence_is_signaled((*id)->last_flush)))
36425ddf75bSChristian König 			needs_flush = true;
36525ddf75bSChristian König 
366e997b827SChristian König 		if ((*id)->flushed_updates < updates)
36725ddf75bSChristian König 			needs_flush = true;
36825ddf75bSChristian König 
36920a5f5a9SChristian König 		if (needs_flush && !adev->vm_manager.concurrent_flush)
37025ddf75bSChristian König 			continue;
37125ddf75bSChristian König 
37225ddf75bSChristian König 		/* Good, we can use this VMID. Remember this submission as
37325ddf75bSChristian König 		 * user of the VMID.
37425ddf75bSChristian König 		 */
375c5093cddSChristian König 		r = amdgpu_sync_fence(&(*id)->active,
37616590745SChristian König 				      &job->base.s_fence->finished,
37716590745SChristian König 				      GFP_NOWAIT);
37825ddf75bSChristian König 		if (r)
37925ddf75bSChristian König 			return r;
38025ddf75bSChristian König 
38125ddf75bSChristian König 		job->vm_needs_flush |= needs_flush;
38225ddf75bSChristian König 		return 0;
38325ddf75bSChristian König 	}
38425ddf75bSChristian König 
38525ddf75bSChristian König 	*id = NULL;
38625ddf75bSChristian König 	return 0;
38725ddf75bSChristian König }
38825ddf75bSChristian König 
38925ddf75bSChristian König /**
390c4cd63f9SLee Jones  * amdgpu_vmid_grab - allocate the next free VMID
391620f774fSChristian König  *
392620f774fSChristian König  * @vm: vm to allocate id for
393620f774fSChristian König  * @ring: ring we want to submit job to
394cb5372acSChristian König  * @job: job who wants to use the VMID
395940ca22bSChristian König  * @fence: fence to wait for if no id could be grabbed
396620f774fSChristian König  *
397620f774fSChristian König  * Allocate an id for the vm, adding fences to the sync obj as necessary.
398620f774fSChristian König  */
amdgpu_vmid_grab(struct amdgpu_vm * vm,struct amdgpu_ring * ring,struct amdgpu_job * job,struct dma_fence ** fence)399620f774fSChristian König int amdgpu_vmid_grab(struct amdgpu_vm *vm, struct amdgpu_ring *ring,
400940ca22bSChristian König 		     struct amdgpu_job *job, struct dma_fence **fence)
401620f774fSChristian König {
402620f774fSChristian König 	struct amdgpu_device *adev = ring->adev;
4030530553bSLe Ma 	unsigned vmhub = ring->vm_hub;
404620f774fSChristian König 	struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub];
405ed024578SHarry Wentland 	struct amdgpu_vmid *idle = NULL;
406ed024578SHarry Wentland 	struct amdgpu_vmid *id = NULL;
407620f774fSChristian König 	int r = 0;
408620f774fSChristian König 
409620f774fSChristian König 	mutex_lock(&id_mgr->lock);
4104d5dc626SYifan Zhang 	r = amdgpu_vmid_grab_idle(ring, &idle, fence);
4113a80e92bSChristian König 	if (r || !idle)
412620f774fSChristian König 		goto error;
413620f774fSChristian König 
414*db1e58ecSChristian König 	if (amdgpu_vmid_uses_reserved(vm, vmhub)) {
415940ca22bSChristian König 		r = amdgpu_vmid_grab_reserved(vm, ring, job, &id, fence);
41658592a09SChristian König 		if (r || !id)
41758592a09SChristian König 			goto error;
41858592a09SChristian König 	} else {
419fc282e9eSTvrtko Ursulin 		r = amdgpu_vmid_grab_used(vm, ring, job, &id);
420620f774fSChristian König 		if (r)
421620f774fSChristian König 			goto error;
422620f774fSChristian König 
42325ddf75bSChristian König 		if (!id) {
424620f774fSChristian König 			/* Still no ID to use? Then use the idle one found earlier */
425620f774fSChristian König 			id = idle;
426620f774fSChristian König 
427620f774fSChristian König 			/* Remember this submission as user of the VMID */
428c5093cddSChristian König 			r = amdgpu_sync_fence(&id->active,
42916590745SChristian König 					      &job->base.s_fence->finished,
43016590745SChristian König 					      GFP_NOWAIT);
431620f774fSChristian König 			if (r)
432620f774fSChristian König 				goto error;
433620f774fSChristian König 
434620f774fSChristian König 			job->vm_needs_flush = true;
43525ddf75bSChristian König 		}
43625ddf75bSChristian König 
43758592a09SChristian König 		list_move_tail(&id->list, &id_mgr->ids_lru);
43858592a09SChristian König 	}
43958592a09SChristian König 
44056b0989eSChristian König 	job->gds_switch_needed = amdgpu_vmid_gds_switch_needed(id, job);
44125ddf75bSChristian König 	if (job->vm_needs_flush) {
44256b0989eSChristian König 		id->flushed_updates = amdgpu_vm_tlb_seq(vm);
443620f774fSChristian König 		dma_fence_put(id->last_flush);
444620f774fSChristian König 		id->last_flush = NULL;
44525ddf75bSChristian König 	}
446c4f46f22SChristian König 	job->vmid = id - id_mgr->ids;
4475a4633c4SChristian König 	job->pasid = vm->pasid;
44856b0989eSChristian König 
44956b0989eSChristian König 	id->gds_base = job->gds_base;
45056b0989eSChristian König 	id->gds_size = job->gds_size;
45156b0989eSChristian König 	id->gws_base = job->gws_base;
45256b0989eSChristian König 	id->gws_size = job->gws_size;
45356b0989eSChristian König 	id->oa_base = job->oa_base;
45456b0989eSChristian König 	id->oa_size = job->oa_size;
45556b0989eSChristian König 	id->pd_gpu_addr = job->vm_pd_addr;
45656b0989eSChristian König 	id->owner = vm->immediate.fence_context;
45756b0989eSChristian König 
458620f774fSChristian König 	trace_amdgpu_vm_grab_id(vm, ring, job);
459620f774fSChristian König 
460620f774fSChristian König error:
461620f774fSChristian König 	mutex_unlock(&id_mgr->lock);
462620f774fSChristian König 	return r;
463620f774fSChristian König }
464620f774fSChristian König 
465320debcaSChristian König /*
466320debcaSChristian König  * amdgpu_vmid_uses_reserved - check if a VM will use a reserved VMID
467320debcaSChristian König  * @vm: the VM to check
468320debcaSChristian König  * @vmhub: the VMHUB which will be used
469320debcaSChristian König  *
470320debcaSChristian König  * Returns: True if the VM will use a reserved VMID.
471320debcaSChristian König  */
amdgpu_vmid_uses_reserved(struct amdgpu_vm * vm,unsigned int vmhub)472*db1e58ecSChristian König bool amdgpu_vmid_uses_reserved(struct amdgpu_vm *vm, unsigned int vmhub)
473320debcaSChristian König {
474*db1e58ecSChristian König 	return vm->reserved_vmid[vmhub];
475320debcaSChristian König }
476320debcaSChristian König 
amdgpu_vmid_alloc_reserved(struct amdgpu_device * adev,unsigned vmhub)477620f774fSChristian König int amdgpu_vmid_alloc_reserved(struct amdgpu_device *adev,
478620f774fSChristian König 			       unsigned vmhub)
479620f774fSChristian König {
480e44a0fe6SChristian König 	struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub];
481620f774fSChristian König 
482620f774fSChristian König 	mutex_lock(&id_mgr->lock);
483620f774fSChristian König 
484e44a0fe6SChristian König 	++id_mgr->reserved_use_count;
485e44a0fe6SChristian König 	if (!id_mgr->reserved) {
486e44a0fe6SChristian König 		struct amdgpu_vmid *id;
487e44a0fe6SChristian König 
488e44a0fe6SChristian König 		id = list_first_entry(&id_mgr->ids_lru, struct amdgpu_vmid,
489e44a0fe6SChristian König 				      list);
490e44a0fe6SChristian König 		/* Remove from normal round robin handling */
491e44a0fe6SChristian König 		list_del_init(&id->list);
492e44a0fe6SChristian König 		id_mgr->reserved = id;
493e44a0fe6SChristian König 	}
494e44a0fe6SChristian König 
495620f774fSChristian König 	mutex_unlock(&id_mgr->lock);
496e44a0fe6SChristian König 	return 0;
497620f774fSChristian König }
498620f774fSChristian König 
amdgpu_vmid_free_reserved(struct amdgpu_device * adev,unsigned vmhub)499620f774fSChristian König void amdgpu_vmid_free_reserved(struct amdgpu_device *adev,
500620f774fSChristian König 			       unsigned vmhub)
501620f774fSChristian König {
502620f774fSChristian König 	struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub];
503620f774fSChristian König 
504620f774fSChristian König 	mutex_lock(&id_mgr->lock);
50580e709eeSChong Li 	if (!--id_mgr->reserved_use_count) {
506e44a0fe6SChristian König 		/* give the reserved ID back to normal round robin */
507e44a0fe6SChristian König 		list_add(&id_mgr->reserved->list, &id_mgr->ids_lru);
5084463b1eeSChristian König 		id_mgr->reserved = NULL;
509620f774fSChristian König 	}
51080e709eeSChong Li 
511620f774fSChristian König 	mutex_unlock(&id_mgr->lock);
512620f774fSChristian König }
513620f774fSChristian König 
514620f774fSChristian König /**
515620f774fSChristian König  * amdgpu_vmid_reset - reset VMID to zero
516620f774fSChristian König  *
517620f774fSChristian König  * @adev: amdgpu device structure
5188ebc6df6SLee Jones  * @vmhub: vmhub type
519c4f46f22SChristian König  * @vmid: vmid number to use
520620f774fSChristian König  *
521620f774fSChristian König  * Reset saved GDW, GWS and OA to force switch on next flush.
522620f774fSChristian König  */
amdgpu_vmid_reset(struct amdgpu_device * adev,unsigned vmhub,unsigned vmid)523620f774fSChristian König void amdgpu_vmid_reset(struct amdgpu_device *adev, unsigned vmhub,
524620f774fSChristian König 		       unsigned vmid)
525620f774fSChristian König {
526620f774fSChristian König 	struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub];
527620f774fSChristian König 	struct amdgpu_vmid *id = &id_mgr->ids[vmid];
528620f774fSChristian König 
52910237448SChristian König 	mutex_lock(&id_mgr->lock);
53010237448SChristian König 	id->owner = 0;
531620f774fSChristian König 	id->gds_base = 0;
532620f774fSChristian König 	id->gds_size = 0;
533620f774fSChristian König 	id->gws_base = 0;
534620f774fSChristian König 	id->gws_size = 0;
535620f774fSChristian König 	id->oa_base = 0;
536620f774fSChristian König 	id->oa_size = 0;
53710237448SChristian König 	mutex_unlock(&id_mgr->lock);
538620f774fSChristian König }
539620f774fSChristian König 
540620f774fSChristian König /**
541620f774fSChristian König  * amdgpu_vmid_reset_all - reset VMID to zero
542620f774fSChristian König  *
543620f774fSChristian König  * @adev: amdgpu device structure
544620f774fSChristian König  *
545620f774fSChristian König  * Reset VMID to force flush on next use
546620f774fSChristian König  */
amdgpu_vmid_reset_all(struct amdgpu_device * adev)547620f774fSChristian König void amdgpu_vmid_reset_all(struct amdgpu_device *adev)
548620f774fSChristian König {
549620f774fSChristian König 	unsigned i, j;
550620f774fSChristian König 
551620f774fSChristian König 	for (i = 0; i < AMDGPU_MAX_VMHUBS; ++i) {
552620f774fSChristian König 		struct amdgpu_vmid_mgr *id_mgr =
553620f774fSChristian König 			&adev->vm_manager.id_mgr[i];
554620f774fSChristian König 
555620f774fSChristian König 		for (j = 1; j < id_mgr->num_ids; ++j)
556620f774fSChristian König 			amdgpu_vmid_reset(adev, i, j);
557620f774fSChristian König 	}
558620f774fSChristian König }
559620f774fSChristian König 
560620f774fSChristian König /**
561620f774fSChristian König  * amdgpu_vmid_mgr_init - init the VMID manager
562620f774fSChristian König  *
563620f774fSChristian König  * @adev: amdgpu_device pointer
564620f774fSChristian König  *
565620f774fSChristian König  * Initialize the VM manager structures
566620f774fSChristian König  */
amdgpu_vmid_mgr_init(struct amdgpu_device * adev)567620f774fSChristian König void amdgpu_vmid_mgr_init(struct amdgpu_device *adev)
568620f774fSChristian König {
569620f774fSChristian König 	unsigned i, j;
570620f774fSChristian König 
571620f774fSChristian König 	for (i = 0; i < AMDGPU_MAX_VMHUBS; ++i) {
572620f774fSChristian König 		struct amdgpu_vmid_mgr *id_mgr =
573620f774fSChristian König 			&adev->vm_manager.id_mgr[i];
574620f774fSChristian König 
575620f774fSChristian König 		mutex_init(&id_mgr->lock);
576620f774fSChristian König 		INIT_LIST_HEAD(&id_mgr->ids_lru);
577e44a0fe6SChristian König 		id_mgr->reserved_use_count = 0;
578620f774fSChristian König 
57940111ec2SFelix Kuehling 		/* manage only VMIDs not used by KFD */
58040111ec2SFelix Kuehling 		id_mgr->num_ids = adev->vm_manager.first_kfd_vmid;
58140111ec2SFelix Kuehling 
582620f774fSChristian König 		/* skip over VMID 0, since it is the system VM */
583620f774fSChristian König 		for (j = 1; j < id_mgr->num_ids; ++j) {
584620f774fSChristian König 			amdgpu_vmid_reset(adev, i, j);
5853df27645SRex Zhu 			amdgpu_sync_create(&id_mgr->ids[j].active);
586620f774fSChristian König 			list_add_tail(&id_mgr->ids[j].list, &id_mgr->ids_lru);
587620f774fSChristian König 		}
588620f774fSChristian König 	}
58980e709eeSChong Li 	/* alloc a default reserved vmid to enforce isolation */
59096595204SSrinivasan Shanmugam 	for (i = 0; i < (adev->xcp_mgr ? adev->xcp_mgr->num_xcps : 1); i++) {
59196595204SSrinivasan Shanmugam 		if (adev->enforce_isolation[i])
59296595204SSrinivasan Shanmugam 			amdgpu_vmid_alloc_reserved(adev, AMDGPU_GFXHUB(i));
59396595204SSrinivasan Shanmugam 	}
594620f774fSChristian König }
595620f774fSChristian König 
596620f774fSChristian König /**
597620f774fSChristian König  * amdgpu_vmid_mgr_fini - cleanup VM manager
598620f774fSChristian König  *
599620f774fSChristian König  * @adev: amdgpu_device pointer
600620f774fSChristian König  *
601620f774fSChristian König  * Cleanup the VM manager and free resources.
602620f774fSChristian König  */
amdgpu_vmid_mgr_fini(struct amdgpu_device * adev)603620f774fSChristian König void amdgpu_vmid_mgr_fini(struct amdgpu_device *adev)
604620f774fSChristian König {
605620f774fSChristian König 	unsigned i, j;
606620f774fSChristian König 
607620f774fSChristian König 	for (i = 0; i < AMDGPU_MAX_VMHUBS; ++i) {
608620f774fSChristian König 		struct amdgpu_vmid_mgr *id_mgr =
609620f774fSChristian König 			&adev->vm_manager.id_mgr[i];
610620f774fSChristian König 
611620f774fSChristian König 		mutex_destroy(&id_mgr->lock);
612620f774fSChristian König 		for (j = 0; j < AMDGPU_NUM_VMID; ++j) {
613620f774fSChristian König 			struct amdgpu_vmid *id = &id_mgr->ids[j];
614620f774fSChristian König 
615620f774fSChristian König 			amdgpu_sync_free(&id->active);
616620f774fSChristian König 			dma_fence_put(id->last_flush);
617b3cd285fSChristian König 			dma_fence_put(id->pasid_mapping);
618620f774fSChristian König 		}
619620f774fSChristian König 	}
620620f774fSChristian König }
621