1844 lines · 45.2 KB · C
RawDownload
/* * Copyright 2008 Jerome Glisse. * All Rights Reserved. * * 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 (including the next * paragraph) 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 * PRECISION INSIGHT AND/OR ITS SUPPLIERS 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. * * Authors: *    Jerome Glisse <glisse@freedesktop.org> */ #include <linux/file.h>#include <linux/pagemap.h>#include <linux/sync_file.h>#include <linux/dma-buf.h> #include <drm/amdgpu_drm.h>#include <drm/drm_syncobj.h>#include <drm/ttm/ttm_tt.h> #include "amdgpu_cs.h"#include "amdgpu.h"#include "amdgpu_trace.h"#include "amdgpu_gmc.h"#include "amdgpu_gem.h"#include "amdgpu_ras.h"#include "amdgpu_hmm.h" /* * Maximum IB length (dwords) for rings whose emit_ib packet format * documents a 20-bit size field. */#define AMDGPU_GFX_SDMA_IB_PACKET_SIZE_MAX_DW	0xFFFFF#define AMDGPU_MM_IB_PACKET_SIZE_MAX_DW	0x7FFFF0 static u32 amdgpu_cs_ib_packet_size_max_dw(enum amdgpu_ring_type type){	switch (type) {	case AMDGPU_RING_TYPE_GFX:	case AMDGPU_RING_TYPE_COMPUTE:	case AMDGPU_RING_TYPE_SDMA:	case AMDGPU_RING_TYPE_VPE:		return AMDGPU_GFX_SDMA_IB_PACKET_SIZE_MAX_DW;	default:		return AMDGPU_MM_IB_PACKET_SIZE_MAX_DW;	}} static int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p,				 struct amdgpu_device *adev,				 struct drm_file *filp,				 union drm_amdgpu_cs *cs){	struct amdgpu_fpriv *fpriv = filp->driver_priv; 	if (cs->in.num_chunks == 0)		return -EINVAL; 	memset(p, 0, sizeof(*p));	p->adev = adev;	p->filp = filp; 	p->ctx = amdgpu_ctx_get(fpriv, cs->in.ctx_id);	if (!p->ctx)		return -EINVAL; 	amdgpu_sync_create(&p->sync);	drm_exec_init(&p->exec, DRM_EXEC_INTERRUPTIBLE_WAIT |		      DRM_EXEC_IGNORE_DUPLICATES, 0);	return 0;} static int amdgpu_cs_job_idx(struct amdgpu_cs_parser *p,			     struct drm_amdgpu_cs_chunk_ib *chunk_ib){	struct drm_sched_entity *entity;	unsigned int i;	int r; 	r = amdgpu_ctx_get_entity(p->ctx, chunk_ib->ip_type,				  chunk_ib->ip_instance,				  chunk_ib->ring, &entity);	if (r)		return r; 	/* Check if we can add this IB to some existing job */	for (i = 0; i < p->gang_size; ++i)		if (p->entities[i] == entity)			return i; 	/* If not increase the gang size if possible */	if (i == AMDGPU_CS_GANG_SIZE)		return -EINVAL; 	p->entities[i] = entity;	p->gang_size = i + 1;	return i;} static int amdgpu_cs_p1_ib(struct amdgpu_cs_parser *p,			   struct drm_amdgpu_cs_chunk_ib *chunk_ib,			   unsigned int *num_ibs){	int r; 	r = amdgpu_cs_job_idx(p, chunk_ib);	if (r < 0)		return r; 	if (num_ibs[r] >= amdgpu_ring_max_ibs(chunk_ib->ip_type))		return -EINVAL; 	++(num_ibs[r]);	p->gang_leader_idx = r;	return 0;} static int amdgpu_cs_p1_user_fence(struct amdgpu_cs_parser *p,				   struct drm_amdgpu_cs_chunk_fence *data,				   uint32_t *offset){	struct drm_gem_object *gobj;	unsigned long size; 	gobj = drm_gem_object_lookup(p->filp, data->handle);	if (gobj == NULL)		return -EINVAL; 	p->uf_bo = amdgpu_bo_ref(gem_to_amdgpu_bo(gobj));	drm_gem_object_put(gobj); 	size = amdgpu_bo_size(p->uf_bo);	if (size != PAGE_SIZE || data->offset > (size - 8))		return -EINVAL; 	if (amdgpu_ttm_tt_get_usermm(p->uf_bo->tbo.ttm))		return -EINVAL; 	*offset = data->offset;	return 0;} static int amdgpu_cs_p1_bo_handles(struct amdgpu_cs_parser *p,				   struct drm_amdgpu_bo_list_in *data){	struct drm_amdgpu_bo_list_entry *info;	int r; 	r = amdgpu_bo_create_list_entry_array(data, &info);	if (r)		return r; 	r = amdgpu_bo_list_create(p->adev, p->filp, info, data->bo_number,				  &p->bo_list);	if (r)		goto error_free; 	kvfree(info);	return 0; error_free:	kvfree(info); 	return r;} /* Copy the data from userspace and go over it the first time */static int amdgpu_cs_pass1(struct amdgpu_cs_parser *p,			   union drm_amdgpu_cs *cs){	struct amdgpu_fpriv *fpriv = p->filp->driver_priv;	unsigned int num_ibs[AMDGPU_CS_GANG_SIZE] = { };	struct amdgpu_vm *vm = &fpriv->vm;	uint64_t *chunk_array;	uint32_t uf_offset = 0;	size_t size;	int ret;	int i; 	chunk_array = memdup_array_user(u64_to_user_ptr(cs->in.chunks),					cs->in.num_chunks,					sizeof(uint64_t));	if (IS_ERR(chunk_array))		return PTR_ERR(chunk_array); 	p->nchunks = cs->in.num_chunks;	p->chunks = kvmalloc_objs(struct amdgpu_cs_chunk, p->nchunks);	if (!p->chunks) {		ret = -ENOMEM;		goto free_chunk;	} 	for (i = 0; i < p->nchunks; i++) {		struct drm_amdgpu_cs_chunk __user *chunk_ptr = NULL;		struct drm_amdgpu_cs_chunk user_chunk; 		chunk_ptr = u64_to_user_ptr(chunk_array[i]);		if (copy_from_user(&user_chunk, chunk_ptr,				       sizeof(struct drm_amdgpu_cs_chunk))) {			ret = -EFAULT;			i--;			goto free_partial_kdata;		}		p->chunks[i].chunk_id = user_chunk.chunk_id;		p->chunks[i].length_dw = user_chunk.length_dw; 		size = p->chunks[i].length_dw; 		p->chunks[i].kdata = vmemdup_array_user(u64_to_user_ptr(user_chunk.chunk_data),							size,							sizeof(uint32_t));		if (IS_ERR(p->chunks[i].kdata)) {			ret = PTR_ERR(p->chunks[i].kdata);			i--;			goto free_partial_kdata;		}		size *= sizeof(uint32_t); 		/* Assume the worst on the following checks */		ret = -EINVAL;		switch (p->chunks[i].chunk_id) {		case AMDGPU_CHUNK_ID_IB:			if (size < sizeof(struct drm_amdgpu_cs_chunk_ib))				goto free_partial_kdata; 			ret = amdgpu_cs_p1_ib(p, p->chunks[i].kdata, num_ibs);			if (ret)				goto free_partial_kdata;			break; 		case AMDGPU_CHUNK_ID_FENCE:			if (size < sizeof(struct drm_amdgpu_cs_chunk_fence))				goto free_partial_kdata; 			/* Only a single user fence is allowed to simplify handling. */			if (p->uf_bo)				goto free_partial_kdata; 			ret = amdgpu_cs_p1_user_fence(p, p->chunks[i].kdata,						      &uf_offset);			if (ret)				goto free_partial_kdata;			break; 		case AMDGPU_CHUNK_ID_BO_HANDLES:			if (size < sizeof(struct drm_amdgpu_bo_list_in))				goto free_partial_kdata; 			/* Only a single BO list is allowed to simplify handling. */			if (p->bo_list)				goto free_partial_kdata; 			ret = amdgpu_cs_p1_bo_handles(p, p->chunks[i].kdata);			if (ret)				goto free_partial_kdata;			break; 		case AMDGPU_CHUNK_ID_CP_GFX_SHADOW:			if (size < sizeof(struct drm_amdgpu_cs_chunk_cp_gfx_shadow))				goto free_partial_kdata;			break; 		case AMDGPU_CHUNK_ID_DEPENDENCIES:		case AMDGPU_CHUNK_ID_SYNCOBJ_IN:		case AMDGPU_CHUNK_ID_SYNCOBJ_OUT:		case AMDGPU_CHUNK_ID_SCHEDULED_DEPENDENCIES:		case AMDGPU_CHUNK_ID_SYNCOBJ_TIMELINE_WAIT:		case AMDGPU_CHUNK_ID_SYNCOBJ_TIMELINE_SIGNAL:			break; 		default:			goto free_partial_kdata;		}	} 	if (!p->gang_size || (amdgpu_sriov_vf(p->adev) && p->gang_size > 1)) {		ret = -EINVAL;		goto free_all_kdata;	} 	for (i = 0; i < p->gang_size; ++i) {		ret = amdgpu_job_alloc(p->adev, vm, p->entities[i], vm,				       num_ibs[i], &p->jobs[i],				       p->filp->client_id);		if (ret)			goto free_all_kdata;		switch (p->adev->enforce_isolation[fpriv->xcp_id]) {		case AMDGPU_ENFORCE_ISOLATION_DISABLE:		default:			p->jobs[i]->enforce_isolation = false;			p->jobs[i]->run_cleaner_shader = false;			break;		case AMDGPU_ENFORCE_ISOLATION_ENABLE:			p->jobs[i]->enforce_isolation = true;			p->jobs[i]->run_cleaner_shader = true;			break;		case AMDGPU_ENFORCE_ISOLATION_ENABLE_LEGACY:			p->jobs[i]->enforce_isolation = true;			p->jobs[i]->run_cleaner_shader = false;			break;		case AMDGPU_ENFORCE_ISOLATION_NO_CLEANER_SHADER:			p->jobs[i]->enforce_isolation = true;			p->jobs[i]->run_cleaner_shader = false;			break;		}	}	p->gang_leader = p->jobs[p->gang_leader_idx]; 	if (p->ctx->generation != p->gang_leader->generation) {		ret = -ECANCELED;		goto free_all_kdata;	} 	if (p->uf_bo)		p->gang_leader->uf_addr = uf_offset;	kvfree(chunk_array); 	/* Use this opportunity to fill in task info for the vm */	amdgpu_vm_set_task_info(vm); 	return 0; free_all_kdata:	i = p->nchunks - 1;free_partial_kdata:	for (; i >= 0; i--)		kvfree(p->chunks[i].kdata);	kvfree(p->chunks);	p->chunks = NULL;	p->nchunks = 0;free_chunk:	kvfree(chunk_array); 	return ret;} static int amdgpu_cs_p2_ib(struct amdgpu_cs_parser *p,			   struct amdgpu_cs_chunk *chunk,			   unsigned int *ce_preempt,			   unsigned int *de_preempt){	struct drm_amdgpu_cs_chunk_ib *chunk_ib = chunk->kdata;	struct amdgpu_fpriv *fpriv = p->filp->driver_priv;	struct amdgpu_vm *vm = &fpriv->vm;	struct amdgpu_ring *ring;	struct amdgpu_job *job;	struct amdgpu_ib *ib;	int r; 	r = amdgpu_cs_job_idx(p, chunk_ib);	if (r < 0)		return r; 	job = p->jobs[r];	ring = amdgpu_job_ring(job); 	/* submissions to kernel queues are disabled */	if (ring->no_user_submission)		return -EINVAL; 	/* MM engine doesn't support user fences */	if (p->uf_bo && ring->funcs->no_user_fence)		return -EINVAL; 	if (!p->adev->debug_enable_ce_cs &&	    chunk_ib->flags & AMDGPU_IB_FLAG_CE) {		dev_err_ratelimited(p->adev->dev, "CE CS is blocked, use debug=0x400 to override\n");		return -EINVAL;	} 	if (chunk_ib->ip_type == AMDGPU_HW_IP_GFX &&	    chunk_ib->flags & AMDGPU_IB_FLAG_PREEMPT) {		if (chunk_ib->flags & AMDGPU_IB_FLAG_CE)			(*ce_preempt)++;		else			(*de_preempt)++; 		/* Each GFX command submit allows only 1 IB max		 * preemptible for CE & DE */		if (*ce_preempt > 1 || *de_preempt > 1)			return -EINVAL;	} 	if (chunk_ib->ib_bytes / 4 >	    amdgpu_cs_ib_packet_size_max_dw(ring->funcs->type))		return -EINVAL; 	ib = &job->ibs[job->num_ibs++]; 	if (chunk_ib->flags & AMDGPU_IB_FLAG_PREAMBLE)		job->preamble_status |= AMDGPU_PREAMBLE_IB_PRESENT; 	r =  amdgpu_ib_get(p->adev, vm, ring->funcs->parse_cs ?			   chunk_ib->ib_bytes : 0,			   AMDGPU_IB_POOL_DELAYED, ib);	if (r) {		drm_err(adev_to_drm(p->adev), "Failed to get ib !\n");		return r;	} 	ib->gpu_addr = chunk_ib->va_start;	ib->length_dw = chunk_ib->ib_bytes / 4;	ib->flags = chunk_ib->flags;	return 0;} static int amdgpu_cs_p2_dependencies(struct amdgpu_cs_parser *p,				     struct amdgpu_cs_chunk *chunk){	struct drm_amdgpu_cs_chunk_dep *deps = chunk->kdata;	struct amdgpu_fpriv *fpriv = p->filp->driver_priv;	unsigned int num_deps;	int i, r; 	num_deps = chunk->length_dw * 4 /		sizeof(struct drm_amdgpu_cs_chunk_dep); 	for (i = 0; i < num_deps; ++i) {		struct amdgpu_ctx *ctx;		struct drm_sched_entity *entity;		struct dma_fence *fence; 		ctx = amdgpu_ctx_get(fpriv, deps[i].ctx_id);		if (ctx == NULL)			return -EINVAL; 		r = amdgpu_ctx_get_entity(ctx, deps[i].ip_type,					  deps[i].ip_instance,					  deps[i].ring, &entity);		if (r) {			amdgpu_ctx_put(ctx);			return r;		} 		fence = amdgpu_ctx_get_fence(ctx, entity, deps[i].handle);		amdgpu_ctx_put(ctx); 		if (IS_ERR(fence))			return PTR_ERR(fence);		else if (!fence)			continue; 		if (chunk->chunk_id == AMDGPU_CHUNK_ID_SCHEDULED_DEPENDENCIES) {			struct drm_sched_fence *s_fence;			struct dma_fence *old = fence; 			s_fence = to_drm_sched_fence(fence);			fence = dma_fence_get(&s_fence->scheduled);			dma_fence_put(old);		} 		r = amdgpu_sync_fence(&p->sync, fence, GFP_KERNEL);		dma_fence_put(fence);		if (r)			return r;	}	return 0;} static int amdgpu_syncobj_lookup_and_add(struct amdgpu_cs_parser *p,					 uint32_t handle, u64 point,					 u64 flags){	struct dma_fence *fence;	int r; 	r = drm_syncobj_find_fence(p->filp, handle, point, flags, &fence);	if (r) {		drm_err(adev_to_drm(p->adev), "syncobj %u failed to find fence @ %llu (%d)!\n",			  handle, point, r);		return r;	} 	r = amdgpu_sync_fence(&p->sync, fence, GFP_KERNEL);	dma_fence_put(fence);	return r;} static int amdgpu_cs_p2_syncobj_in(struct amdgpu_cs_parser *p,				   struct amdgpu_cs_chunk *chunk){	struct drm_amdgpu_cs_chunk_sem *deps = chunk->kdata;	unsigned int num_deps;	int i, r;

Lines 1–500 of 1844.