18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright 2008 Jerome Glisse.
38c2ecf20Sopenharmony_ci * All Rights Reserved.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
68c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
78c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation
88c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
98c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
108c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next
138c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
148c2ecf20Sopenharmony_ci * Software.
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
178c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
188c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
198c2ecf20Sopenharmony_ci * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
208c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
218c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
228c2ecf20Sopenharmony_ci * DEALINGS IN THE SOFTWARE.
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * Authors:
258c2ecf20Sopenharmony_ci *    Jerome Glisse <glisse@freedesktop.org>
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include <linux/list_sort.h>
298c2ecf20Sopenharmony_ci#include <linux/pci.h>
308c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#include <drm/drm_device.h>
338c2ecf20Sopenharmony_ci#include <drm/drm_file.h>
348c2ecf20Sopenharmony_ci#include <drm/radeon_drm.h>
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#include "radeon.h"
378c2ecf20Sopenharmony_ci#include "radeon_reg.h"
388c2ecf20Sopenharmony_ci#include "radeon_trace.h"
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define RADEON_CS_MAX_PRIORITY		32u
418c2ecf20Sopenharmony_ci#define RADEON_CS_NUM_BUCKETS		(RADEON_CS_MAX_PRIORITY + 1)
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/* This is based on the bucket sort with O(n) time complexity.
448c2ecf20Sopenharmony_ci * An item with priority "i" is added to bucket[i]. The lists are then
458c2ecf20Sopenharmony_ci * concatenated in descending order.
468c2ecf20Sopenharmony_ci */
478c2ecf20Sopenharmony_cistruct radeon_cs_buckets {
488c2ecf20Sopenharmony_ci	struct list_head bucket[RADEON_CS_NUM_BUCKETS];
498c2ecf20Sopenharmony_ci};
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic void radeon_cs_buckets_init(struct radeon_cs_buckets *b)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	unsigned i;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	for (i = 0; i < RADEON_CS_NUM_BUCKETS; i++)
568c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&b->bucket[i]);
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic void radeon_cs_buckets_add(struct radeon_cs_buckets *b,
608c2ecf20Sopenharmony_ci				  struct list_head *item, unsigned priority)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	/* Since buffers which appear sooner in the relocation list are
638c2ecf20Sopenharmony_ci	 * likely to be used more often than buffers which appear later
648c2ecf20Sopenharmony_ci	 * in the list, the sort mustn't change the ordering of buffers
658c2ecf20Sopenharmony_ci	 * with the same priority, i.e. it must be stable.
668c2ecf20Sopenharmony_ci	 */
678c2ecf20Sopenharmony_ci	list_add_tail(item, &b->bucket[min(priority, RADEON_CS_MAX_PRIORITY)]);
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic void radeon_cs_buckets_get_list(struct radeon_cs_buckets *b,
718c2ecf20Sopenharmony_ci				       struct list_head *out_list)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	unsigned i;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	/* Connect the sorted buckets in the output list. */
768c2ecf20Sopenharmony_ci	for (i = 0; i < RADEON_CS_NUM_BUCKETS; i++) {
778c2ecf20Sopenharmony_ci		list_splice(&b->bucket[i], out_list);
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	struct radeon_cs_chunk *chunk;
848c2ecf20Sopenharmony_ci	struct radeon_cs_buckets buckets;
858c2ecf20Sopenharmony_ci	unsigned i;
868c2ecf20Sopenharmony_ci	bool need_mmap_lock = false;
878c2ecf20Sopenharmony_ci	int r;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	if (p->chunk_relocs == NULL) {
908c2ecf20Sopenharmony_ci		return 0;
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci	chunk = p->chunk_relocs;
938c2ecf20Sopenharmony_ci	p->dma_reloc_idx = 0;
948c2ecf20Sopenharmony_ci	/* FIXME: we assume that each relocs use 4 dwords */
958c2ecf20Sopenharmony_ci	p->nrelocs = chunk->length_dw / 4;
968c2ecf20Sopenharmony_ci	p->relocs = kvmalloc_array(p->nrelocs, sizeof(struct radeon_bo_list),
978c2ecf20Sopenharmony_ci			GFP_KERNEL | __GFP_ZERO);
988c2ecf20Sopenharmony_ci	if (p->relocs == NULL) {
998c2ecf20Sopenharmony_ci		return -ENOMEM;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	radeon_cs_buckets_init(&buckets);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	for (i = 0; i < p->nrelocs; i++) {
1058c2ecf20Sopenharmony_ci		struct drm_radeon_cs_reloc *r;
1068c2ecf20Sopenharmony_ci		struct drm_gem_object *gobj;
1078c2ecf20Sopenharmony_ci		unsigned priority;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci		r = (struct drm_radeon_cs_reloc *)&chunk->kdata[i*4];
1108c2ecf20Sopenharmony_ci		gobj = drm_gem_object_lookup(p->filp, r->handle);
1118c2ecf20Sopenharmony_ci		if (gobj == NULL) {
1128c2ecf20Sopenharmony_ci			DRM_ERROR("gem object lookup failed 0x%x\n",
1138c2ecf20Sopenharmony_ci				  r->handle);
1148c2ecf20Sopenharmony_ci			return -ENOENT;
1158c2ecf20Sopenharmony_ci		}
1168c2ecf20Sopenharmony_ci		p->relocs[i].robj = gem_to_radeon_bo(gobj);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci		/* The userspace buffer priorities are from 0 to 15. A higher
1198c2ecf20Sopenharmony_ci		 * number means the buffer is more important.
1208c2ecf20Sopenharmony_ci		 * Also, the buffers used for write have a higher priority than
1218c2ecf20Sopenharmony_ci		 * the buffers used for read only, which doubles the range
1228c2ecf20Sopenharmony_ci		 * to 0 to 31. 32 is reserved for the kernel driver.
1238c2ecf20Sopenharmony_ci		 */
1248c2ecf20Sopenharmony_ci		priority = (r->flags & RADEON_RELOC_PRIO_MASK) * 2
1258c2ecf20Sopenharmony_ci			   + !!r->write_domain;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci		/* The first reloc of an UVD job is the msg and that must be in
1288c2ecf20Sopenharmony_ci		 * VRAM, the second reloc is the DPB and for WMV that must be in
1298c2ecf20Sopenharmony_ci		 * VRAM as well. Also put everything into VRAM on AGP cards and older
1308c2ecf20Sopenharmony_ci		 * IGP chips to avoid image corruptions
1318c2ecf20Sopenharmony_ci		 */
1328c2ecf20Sopenharmony_ci		if (p->ring == R600_RING_TYPE_UVD_INDEX &&
1338c2ecf20Sopenharmony_ci		    (i <= 0 || pci_find_capability(p->rdev->ddev->pdev,
1348c2ecf20Sopenharmony_ci						   PCI_CAP_ID_AGP) ||
1358c2ecf20Sopenharmony_ci		     p->rdev->family == CHIP_RS780 ||
1368c2ecf20Sopenharmony_ci		     p->rdev->family == CHIP_RS880)) {
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci			/* TODO: is this still needed for NI+ ? */
1398c2ecf20Sopenharmony_ci			p->relocs[i].preferred_domains =
1408c2ecf20Sopenharmony_ci				RADEON_GEM_DOMAIN_VRAM;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci			p->relocs[i].allowed_domains =
1438c2ecf20Sopenharmony_ci				RADEON_GEM_DOMAIN_VRAM;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci			/* prioritize this over any other relocation */
1468c2ecf20Sopenharmony_ci			priority = RADEON_CS_MAX_PRIORITY;
1478c2ecf20Sopenharmony_ci		} else {
1488c2ecf20Sopenharmony_ci			uint32_t domain = r->write_domain ?
1498c2ecf20Sopenharmony_ci				r->write_domain : r->read_domains;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci			if (domain & RADEON_GEM_DOMAIN_CPU) {
1528c2ecf20Sopenharmony_ci				DRM_ERROR("RADEON_GEM_DOMAIN_CPU is not valid "
1538c2ecf20Sopenharmony_ci					  "for command submission\n");
1548c2ecf20Sopenharmony_ci				return -EINVAL;
1558c2ecf20Sopenharmony_ci			}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci			p->relocs[i].preferred_domains = domain;
1588c2ecf20Sopenharmony_ci			if (domain == RADEON_GEM_DOMAIN_VRAM)
1598c2ecf20Sopenharmony_ci				domain |= RADEON_GEM_DOMAIN_GTT;
1608c2ecf20Sopenharmony_ci			p->relocs[i].allowed_domains = domain;
1618c2ecf20Sopenharmony_ci		}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci		if (radeon_ttm_tt_has_userptr(p->rdev, p->relocs[i].robj->tbo.ttm)) {
1648c2ecf20Sopenharmony_ci			uint32_t domain = p->relocs[i].preferred_domains;
1658c2ecf20Sopenharmony_ci			if (!(domain & RADEON_GEM_DOMAIN_GTT)) {
1668c2ecf20Sopenharmony_ci				DRM_ERROR("Only RADEON_GEM_DOMAIN_GTT is "
1678c2ecf20Sopenharmony_ci					  "allowed for userptr BOs\n");
1688c2ecf20Sopenharmony_ci				return -EINVAL;
1698c2ecf20Sopenharmony_ci			}
1708c2ecf20Sopenharmony_ci			need_mmap_lock = true;
1718c2ecf20Sopenharmony_ci			domain = RADEON_GEM_DOMAIN_GTT;
1728c2ecf20Sopenharmony_ci			p->relocs[i].preferred_domains = domain;
1738c2ecf20Sopenharmony_ci			p->relocs[i].allowed_domains = domain;
1748c2ecf20Sopenharmony_ci		}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci		/* Objects shared as dma-bufs cannot be moved to VRAM */
1778c2ecf20Sopenharmony_ci		if (p->relocs[i].robj->prime_shared_count) {
1788c2ecf20Sopenharmony_ci			p->relocs[i].allowed_domains &= ~RADEON_GEM_DOMAIN_VRAM;
1798c2ecf20Sopenharmony_ci			if (!p->relocs[i].allowed_domains) {
1808c2ecf20Sopenharmony_ci				DRM_ERROR("BO associated with dma-buf cannot "
1818c2ecf20Sopenharmony_ci					  "be moved to VRAM\n");
1828c2ecf20Sopenharmony_ci				return -EINVAL;
1838c2ecf20Sopenharmony_ci			}
1848c2ecf20Sopenharmony_ci		}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci		p->relocs[i].tv.bo = &p->relocs[i].robj->tbo;
1878c2ecf20Sopenharmony_ci		p->relocs[i].tv.num_shared = !r->write_domain;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci		radeon_cs_buckets_add(&buckets, &p->relocs[i].tv.head,
1908c2ecf20Sopenharmony_ci				      priority);
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	radeon_cs_buckets_get_list(&buckets, &p->validated);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	if (p->cs_flags & RADEON_CS_USE_VM)
1968c2ecf20Sopenharmony_ci		p->vm_bos = radeon_vm_get_bos(p->rdev, p->ib.vm,
1978c2ecf20Sopenharmony_ci					      &p->validated);
1988c2ecf20Sopenharmony_ci	if (need_mmap_lock)
1998c2ecf20Sopenharmony_ci		mmap_read_lock(current->mm);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	r = radeon_bo_list_validate(p->rdev, &p->ticket, &p->validated, p->ring);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	if (need_mmap_lock)
2048c2ecf20Sopenharmony_ci		mmap_read_unlock(current->mm);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	return r;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	p->priority = priority;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	switch (ring) {
2148c2ecf20Sopenharmony_ci	default:
2158c2ecf20Sopenharmony_ci		DRM_ERROR("unknown ring id: %d\n", ring);
2168c2ecf20Sopenharmony_ci		return -EINVAL;
2178c2ecf20Sopenharmony_ci	case RADEON_CS_RING_GFX:
2188c2ecf20Sopenharmony_ci		p->ring = RADEON_RING_TYPE_GFX_INDEX;
2198c2ecf20Sopenharmony_ci		break;
2208c2ecf20Sopenharmony_ci	case RADEON_CS_RING_COMPUTE:
2218c2ecf20Sopenharmony_ci		if (p->rdev->family >= CHIP_TAHITI) {
2228c2ecf20Sopenharmony_ci			if (p->priority > 0)
2238c2ecf20Sopenharmony_ci				p->ring = CAYMAN_RING_TYPE_CP1_INDEX;
2248c2ecf20Sopenharmony_ci			else
2258c2ecf20Sopenharmony_ci				p->ring = CAYMAN_RING_TYPE_CP2_INDEX;
2268c2ecf20Sopenharmony_ci		} else
2278c2ecf20Sopenharmony_ci			p->ring = RADEON_RING_TYPE_GFX_INDEX;
2288c2ecf20Sopenharmony_ci		break;
2298c2ecf20Sopenharmony_ci	case RADEON_CS_RING_DMA:
2308c2ecf20Sopenharmony_ci		if (p->rdev->family >= CHIP_CAYMAN) {
2318c2ecf20Sopenharmony_ci			if (p->priority > 0)
2328c2ecf20Sopenharmony_ci				p->ring = R600_RING_TYPE_DMA_INDEX;
2338c2ecf20Sopenharmony_ci			else
2348c2ecf20Sopenharmony_ci				p->ring = CAYMAN_RING_TYPE_DMA1_INDEX;
2358c2ecf20Sopenharmony_ci		} else if (p->rdev->family >= CHIP_RV770) {
2368c2ecf20Sopenharmony_ci			p->ring = R600_RING_TYPE_DMA_INDEX;
2378c2ecf20Sopenharmony_ci		} else {
2388c2ecf20Sopenharmony_ci			return -EINVAL;
2398c2ecf20Sopenharmony_ci		}
2408c2ecf20Sopenharmony_ci		break;
2418c2ecf20Sopenharmony_ci	case RADEON_CS_RING_UVD:
2428c2ecf20Sopenharmony_ci		p->ring = R600_RING_TYPE_UVD_INDEX;
2438c2ecf20Sopenharmony_ci		break;
2448c2ecf20Sopenharmony_ci	case RADEON_CS_RING_VCE:
2458c2ecf20Sopenharmony_ci		/* TODO: only use the low priority ring for now */
2468c2ecf20Sopenharmony_ci		p->ring = TN_RING_TYPE_VCE1_INDEX;
2478c2ecf20Sopenharmony_ci		break;
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci	return 0;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic int radeon_cs_sync_rings(struct radeon_cs_parser *p)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	struct radeon_bo_list *reloc;
2558c2ecf20Sopenharmony_ci	int r;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	list_for_each_entry(reloc, &p->validated, tv.head) {
2588c2ecf20Sopenharmony_ci		struct dma_resv *resv;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		resv = reloc->robj->tbo.base.resv;
2618c2ecf20Sopenharmony_ci		r = radeon_sync_resv(p->rdev, &p->ib.sync, resv,
2628c2ecf20Sopenharmony_ci				     reloc->tv.num_shared);
2638c2ecf20Sopenharmony_ci		if (r)
2648c2ecf20Sopenharmony_ci			return r;
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci	return 0;
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci/* XXX: note that this is called from the legacy UMS CS ioctl as well */
2708c2ecf20Sopenharmony_ciint radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	struct drm_radeon_cs *cs = data;
2738c2ecf20Sopenharmony_ci	uint64_t *chunk_array_ptr;
2748c2ecf20Sopenharmony_ci	u64 size;
2758c2ecf20Sopenharmony_ci	unsigned i;
2768c2ecf20Sopenharmony_ci	u32 ring = RADEON_CS_RING_GFX;
2778c2ecf20Sopenharmony_ci	s32 priority = 0;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&p->validated);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	if (!cs->num_chunks) {
2828c2ecf20Sopenharmony_ci		return 0;
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	/* get chunks */
2868c2ecf20Sopenharmony_ci	p->idx = 0;
2878c2ecf20Sopenharmony_ci	p->ib.sa_bo = NULL;
2888c2ecf20Sopenharmony_ci	p->const_ib.sa_bo = NULL;
2898c2ecf20Sopenharmony_ci	p->chunk_ib = NULL;
2908c2ecf20Sopenharmony_ci	p->chunk_relocs = NULL;
2918c2ecf20Sopenharmony_ci	p->chunk_flags = NULL;
2928c2ecf20Sopenharmony_ci	p->chunk_const_ib = NULL;
2938c2ecf20Sopenharmony_ci	p->chunks_array = kcalloc(cs->num_chunks, sizeof(uint64_t), GFP_KERNEL);
2948c2ecf20Sopenharmony_ci	if (p->chunks_array == NULL) {
2958c2ecf20Sopenharmony_ci		return -ENOMEM;
2968c2ecf20Sopenharmony_ci	}
2978c2ecf20Sopenharmony_ci	chunk_array_ptr = (uint64_t *)(unsigned long)(cs->chunks);
2988c2ecf20Sopenharmony_ci	if (copy_from_user(p->chunks_array, chunk_array_ptr,
2998c2ecf20Sopenharmony_ci			       sizeof(uint64_t)*cs->num_chunks)) {
3008c2ecf20Sopenharmony_ci		return -EFAULT;
3018c2ecf20Sopenharmony_ci	}
3028c2ecf20Sopenharmony_ci	p->cs_flags = 0;
3038c2ecf20Sopenharmony_ci	p->nchunks = cs->num_chunks;
3048c2ecf20Sopenharmony_ci	p->chunks = kcalloc(p->nchunks, sizeof(struct radeon_cs_chunk), GFP_KERNEL);
3058c2ecf20Sopenharmony_ci	if (p->chunks == NULL) {
3068c2ecf20Sopenharmony_ci		return -ENOMEM;
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci	for (i = 0; i < p->nchunks; i++) {
3098c2ecf20Sopenharmony_ci		struct drm_radeon_cs_chunk __user **chunk_ptr = NULL;
3108c2ecf20Sopenharmony_ci		struct drm_radeon_cs_chunk user_chunk;
3118c2ecf20Sopenharmony_ci		uint32_t __user *cdata;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci		chunk_ptr = (void __user*)(unsigned long)p->chunks_array[i];
3148c2ecf20Sopenharmony_ci		if (copy_from_user(&user_chunk, chunk_ptr,
3158c2ecf20Sopenharmony_ci				       sizeof(struct drm_radeon_cs_chunk))) {
3168c2ecf20Sopenharmony_ci			return -EFAULT;
3178c2ecf20Sopenharmony_ci		}
3188c2ecf20Sopenharmony_ci		p->chunks[i].length_dw = user_chunk.length_dw;
3198c2ecf20Sopenharmony_ci		if (user_chunk.chunk_id == RADEON_CHUNK_ID_RELOCS) {
3208c2ecf20Sopenharmony_ci			p->chunk_relocs = &p->chunks[i];
3218c2ecf20Sopenharmony_ci		}
3228c2ecf20Sopenharmony_ci		if (user_chunk.chunk_id == RADEON_CHUNK_ID_IB) {
3238c2ecf20Sopenharmony_ci			p->chunk_ib = &p->chunks[i];
3248c2ecf20Sopenharmony_ci			/* zero length IB isn't useful */
3258c2ecf20Sopenharmony_ci			if (p->chunks[i].length_dw == 0)
3268c2ecf20Sopenharmony_ci				return -EINVAL;
3278c2ecf20Sopenharmony_ci		}
3288c2ecf20Sopenharmony_ci		if (user_chunk.chunk_id == RADEON_CHUNK_ID_CONST_IB) {
3298c2ecf20Sopenharmony_ci			p->chunk_const_ib = &p->chunks[i];
3308c2ecf20Sopenharmony_ci			/* zero length CONST IB isn't useful */
3318c2ecf20Sopenharmony_ci			if (p->chunks[i].length_dw == 0)
3328c2ecf20Sopenharmony_ci				return -EINVAL;
3338c2ecf20Sopenharmony_ci		}
3348c2ecf20Sopenharmony_ci		if (user_chunk.chunk_id == RADEON_CHUNK_ID_FLAGS) {
3358c2ecf20Sopenharmony_ci			p->chunk_flags = &p->chunks[i];
3368c2ecf20Sopenharmony_ci			/* zero length flags aren't useful */
3378c2ecf20Sopenharmony_ci			if (p->chunks[i].length_dw == 0)
3388c2ecf20Sopenharmony_ci				return -EINVAL;
3398c2ecf20Sopenharmony_ci		}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci		size = p->chunks[i].length_dw;
3428c2ecf20Sopenharmony_ci		cdata = (void __user *)(unsigned long)user_chunk.chunk_data;
3438c2ecf20Sopenharmony_ci		p->chunks[i].user_ptr = cdata;
3448c2ecf20Sopenharmony_ci		if (user_chunk.chunk_id == RADEON_CHUNK_ID_CONST_IB)
3458c2ecf20Sopenharmony_ci			continue;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci		if (user_chunk.chunk_id == RADEON_CHUNK_ID_IB) {
3488c2ecf20Sopenharmony_ci			if (!p->rdev || !(p->rdev->flags & RADEON_IS_AGP))
3498c2ecf20Sopenharmony_ci				continue;
3508c2ecf20Sopenharmony_ci		}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci		p->chunks[i].kdata = kvmalloc_array(size, sizeof(uint32_t), GFP_KERNEL);
3538c2ecf20Sopenharmony_ci		size *= sizeof(uint32_t);
3548c2ecf20Sopenharmony_ci		if (p->chunks[i].kdata == NULL) {
3558c2ecf20Sopenharmony_ci			return -ENOMEM;
3568c2ecf20Sopenharmony_ci		}
3578c2ecf20Sopenharmony_ci		if (copy_from_user(p->chunks[i].kdata, cdata, size)) {
3588c2ecf20Sopenharmony_ci			return -EFAULT;
3598c2ecf20Sopenharmony_ci		}
3608c2ecf20Sopenharmony_ci		if (user_chunk.chunk_id == RADEON_CHUNK_ID_FLAGS) {
3618c2ecf20Sopenharmony_ci			p->cs_flags = p->chunks[i].kdata[0];
3628c2ecf20Sopenharmony_ci			if (p->chunks[i].length_dw > 1)
3638c2ecf20Sopenharmony_ci				ring = p->chunks[i].kdata[1];
3648c2ecf20Sopenharmony_ci			if (p->chunks[i].length_dw > 2)
3658c2ecf20Sopenharmony_ci				priority = (s32)p->chunks[i].kdata[2];
3668c2ecf20Sopenharmony_ci		}
3678c2ecf20Sopenharmony_ci	}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	/* these are KMS only */
3708c2ecf20Sopenharmony_ci	if (p->rdev) {
3718c2ecf20Sopenharmony_ci		if ((p->cs_flags & RADEON_CS_USE_VM) &&
3728c2ecf20Sopenharmony_ci		    !p->rdev->vm_manager.enabled) {
3738c2ecf20Sopenharmony_ci			DRM_ERROR("VM not active on asic!\n");
3748c2ecf20Sopenharmony_ci			return -EINVAL;
3758c2ecf20Sopenharmony_ci		}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci		if (radeon_cs_get_ring(p, ring, priority))
3788c2ecf20Sopenharmony_ci			return -EINVAL;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci		/* we only support VM on some SI+ rings */
3818c2ecf20Sopenharmony_ci		if ((p->cs_flags & RADEON_CS_USE_VM) == 0) {
3828c2ecf20Sopenharmony_ci			if (p->rdev->asic->ring[p->ring]->cs_parse == NULL) {
3838c2ecf20Sopenharmony_ci				DRM_ERROR("Ring %d requires VM!\n", p->ring);
3848c2ecf20Sopenharmony_ci				return -EINVAL;
3858c2ecf20Sopenharmony_ci			}
3868c2ecf20Sopenharmony_ci		} else {
3878c2ecf20Sopenharmony_ci			if (p->rdev->asic->ring[p->ring]->ib_parse == NULL) {
3888c2ecf20Sopenharmony_ci				DRM_ERROR("VM not supported on ring %d!\n",
3898c2ecf20Sopenharmony_ci					  p->ring);
3908c2ecf20Sopenharmony_ci				return -EINVAL;
3918c2ecf20Sopenharmony_ci			}
3928c2ecf20Sopenharmony_ci		}
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	return 0;
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cistatic int cmp_size_smaller_first(void *priv, const struct list_head *a,
3998c2ecf20Sopenharmony_ci				  const struct list_head *b)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	struct radeon_bo_list *la = list_entry(a, struct radeon_bo_list, tv.head);
4028c2ecf20Sopenharmony_ci	struct radeon_bo_list *lb = list_entry(b, struct radeon_bo_list, tv.head);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	/* Sort A before B if A is smaller. */
4058c2ecf20Sopenharmony_ci	return (int)la->robj->tbo.num_pages - (int)lb->robj->tbo.num_pages;
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci/**
4098c2ecf20Sopenharmony_ci * cs_parser_fini() - clean parser states
4108c2ecf20Sopenharmony_ci * @parser:	parser structure holding parsing context.
4118c2ecf20Sopenharmony_ci * @error:	error number
4128c2ecf20Sopenharmony_ci *
4138c2ecf20Sopenharmony_ci * If error is set than unvalidate buffer, otherwise just free memory
4148c2ecf20Sopenharmony_ci * used by parsing context.
4158c2ecf20Sopenharmony_ci **/
4168c2ecf20Sopenharmony_cistatic void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error, bool backoff)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	unsigned i;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	if (!error) {
4218c2ecf20Sopenharmony_ci		/* Sort the buffer list from the smallest to largest buffer,
4228c2ecf20Sopenharmony_ci		 * which affects the order of buffers in the LRU list.
4238c2ecf20Sopenharmony_ci		 * This assures that the smallest buffers are added first
4248c2ecf20Sopenharmony_ci		 * to the LRU list, so they are likely to be later evicted
4258c2ecf20Sopenharmony_ci		 * first, instead of large buffers whose eviction is more
4268c2ecf20Sopenharmony_ci		 * expensive.
4278c2ecf20Sopenharmony_ci		 *
4288c2ecf20Sopenharmony_ci		 * This slightly lowers the number of bytes moved by TTM
4298c2ecf20Sopenharmony_ci		 * per frame under memory pressure.
4308c2ecf20Sopenharmony_ci		 */
4318c2ecf20Sopenharmony_ci		list_sort(NULL, &parser->validated, cmp_size_smaller_first);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci		ttm_eu_fence_buffer_objects(&parser->ticket,
4348c2ecf20Sopenharmony_ci					    &parser->validated,
4358c2ecf20Sopenharmony_ci					    &parser->ib.fence->base);
4368c2ecf20Sopenharmony_ci	} else if (backoff) {
4378c2ecf20Sopenharmony_ci		ttm_eu_backoff_reservation(&parser->ticket,
4388c2ecf20Sopenharmony_ci					   &parser->validated);
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	if (parser->relocs != NULL) {
4428c2ecf20Sopenharmony_ci		for (i = 0; i < parser->nrelocs; i++) {
4438c2ecf20Sopenharmony_ci			struct radeon_bo *bo = parser->relocs[i].robj;
4448c2ecf20Sopenharmony_ci			if (bo == NULL)
4458c2ecf20Sopenharmony_ci				continue;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci			drm_gem_object_put(&bo->tbo.base);
4488c2ecf20Sopenharmony_ci		}
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci	kfree(parser->track);
4518c2ecf20Sopenharmony_ci	kvfree(parser->relocs);
4528c2ecf20Sopenharmony_ci	kvfree(parser->vm_bos);
4538c2ecf20Sopenharmony_ci	for (i = 0; i < parser->nchunks; i++)
4548c2ecf20Sopenharmony_ci		kvfree(parser->chunks[i].kdata);
4558c2ecf20Sopenharmony_ci	kfree(parser->chunks);
4568c2ecf20Sopenharmony_ci	kfree(parser->chunks_array);
4578c2ecf20Sopenharmony_ci	radeon_ib_free(parser->rdev, &parser->ib);
4588c2ecf20Sopenharmony_ci	radeon_ib_free(parser->rdev, &parser->const_ib);
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_cistatic int radeon_cs_ib_chunk(struct radeon_device *rdev,
4628c2ecf20Sopenharmony_ci			      struct radeon_cs_parser *parser)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	int r;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	if (parser->chunk_ib == NULL)
4678c2ecf20Sopenharmony_ci		return 0;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	if (parser->cs_flags & RADEON_CS_USE_VM)
4708c2ecf20Sopenharmony_ci		return 0;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	r = radeon_cs_parse(rdev, parser->ring, parser);
4738c2ecf20Sopenharmony_ci	if (r || parser->parser_error) {
4748c2ecf20Sopenharmony_ci		DRM_ERROR("Invalid command stream !\n");
4758c2ecf20Sopenharmony_ci		return r;
4768c2ecf20Sopenharmony_ci	}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	r = radeon_cs_sync_rings(parser);
4798c2ecf20Sopenharmony_ci	if (r) {
4808c2ecf20Sopenharmony_ci		if (r != -ERESTARTSYS)
4818c2ecf20Sopenharmony_ci			DRM_ERROR("Failed to sync rings: %i\n", r);
4828c2ecf20Sopenharmony_ci		return r;
4838c2ecf20Sopenharmony_ci	}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	if (parser->ring == R600_RING_TYPE_UVD_INDEX)
4868c2ecf20Sopenharmony_ci		radeon_uvd_note_usage(rdev);
4878c2ecf20Sopenharmony_ci	else if ((parser->ring == TN_RING_TYPE_VCE1_INDEX) ||
4888c2ecf20Sopenharmony_ci		 (parser->ring == TN_RING_TYPE_VCE2_INDEX))
4898c2ecf20Sopenharmony_ci		radeon_vce_note_usage(rdev);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	r = radeon_ib_schedule(rdev, &parser->ib, NULL, true);
4928c2ecf20Sopenharmony_ci	if (r) {
4938c2ecf20Sopenharmony_ci		DRM_ERROR("Failed to schedule IB !\n");
4948c2ecf20Sopenharmony_ci	}
4958c2ecf20Sopenharmony_ci	return r;
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic int radeon_bo_vm_update_pte(struct radeon_cs_parser *p,
4998c2ecf20Sopenharmony_ci				   struct radeon_vm *vm)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	struct radeon_device *rdev = p->rdev;
5028c2ecf20Sopenharmony_ci	struct radeon_bo_va *bo_va;
5038c2ecf20Sopenharmony_ci	int i, r;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	r = radeon_vm_update_page_directory(rdev, vm);
5068c2ecf20Sopenharmony_ci	if (r)
5078c2ecf20Sopenharmony_ci		return r;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	r = radeon_vm_clear_freed(rdev, vm);
5108c2ecf20Sopenharmony_ci	if (r)
5118c2ecf20Sopenharmony_ci		return r;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	if (vm->ib_bo_va == NULL) {
5148c2ecf20Sopenharmony_ci		DRM_ERROR("Tmp BO not in VM!\n");
5158c2ecf20Sopenharmony_ci		return -EINVAL;
5168c2ecf20Sopenharmony_ci	}
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	r = radeon_vm_bo_update(rdev, vm->ib_bo_va,
5198c2ecf20Sopenharmony_ci				&rdev->ring_tmp_bo.bo->tbo.mem);
5208c2ecf20Sopenharmony_ci	if (r)
5218c2ecf20Sopenharmony_ci		return r;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	for (i = 0; i < p->nrelocs; i++) {
5248c2ecf20Sopenharmony_ci		struct radeon_bo *bo;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci		bo = p->relocs[i].robj;
5278c2ecf20Sopenharmony_ci		bo_va = radeon_vm_bo_find(vm, bo);
5288c2ecf20Sopenharmony_ci		if (bo_va == NULL) {
5298c2ecf20Sopenharmony_ci			dev_err(rdev->dev, "bo %p not in vm %p\n", bo, vm);
5308c2ecf20Sopenharmony_ci			return -EINVAL;
5318c2ecf20Sopenharmony_ci		}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci		r = radeon_vm_bo_update(rdev, bo_va, &bo->tbo.mem);
5348c2ecf20Sopenharmony_ci		if (r)
5358c2ecf20Sopenharmony_ci			return r;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci		radeon_sync_fence(&p->ib.sync, bo_va->last_pt_update);
5388c2ecf20Sopenharmony_ci	}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	return radeon_vm_clear_invalids(rdev, vm);
5418c2ecf20Sopenharmony_ci}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_cistatic int radeon_cs_ib_vm_chunk(struct radeon_device *rdev,
5448c2ecf20Sopenharmony_ci				 struct radeon_cs_parser *parser)
5458c2ecf20Sopenharmony_ci{
5468c2ecf20Sopenharmony_ci	struct radeon_fpriv *fpriv = parser->filp->driver_priv;
5478c2ecf20Sopenharmony_ci	struct radeon_vm *vm = &fpriv->vm;
5488c2ecf20Sopenharmony_ci	int r;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	if (parser->chunk_ib == NULL)
5518c2ecf20Sopenharmony_ci		return 0;
5528c2ecf20Sopenharmony_ci	if ((parser->cs_flags & RADEON_CS_USE_VM) == 0)
5538c2ecf20Sopenharmony_ci		return 0;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	if (parser->const_ib.length_dw) {
5568c2ecf20Sopenharmony_ci		r = radeon_ring_ib_parse(rdev, parser->ring, &parser->const_ib);
5578c2ecf20Sopenharmony_ci		if (r) {
5588c2ecf20Sopenharmony_ci			return r;
5598c2ecf20Sopenharmony_ci		}
5608c2ecf20Sopenharmony_ci	}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	r = radeon_ring_ib_parse(rdev, parser->ring, &parser->ib);
5638c2ecf20Sopenharmony_ci	if (r) {
5648c2ecf20Sopenharmony_ci		return r;
5658c2ecf20Sopenharmony_ci	}
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	if (parser->ring == R600_RING_TYPE_UVD_INDEX)
5688c2ecf20Sopenharmony_ci		radeon_uvd_note_usage(rdev);
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	mutex_lock(&vm->mutex);
5718c2ecf20Sopenharmony_ci	r = radeon_bo_vm_update_pte(parser, vm);
5728c2ecf20Sopenharmony_ci	if (r) {
5738c2ecf20Sopenharmony_ci		goto out;
5748c2ecf20Sopenharmony_ci	}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	r = radeon_cs_sync_rings(parser);
5778c2ecf20Sopenharmony_ci	if (r) {
5788c2ecf20Sopenharmony_ci		if (r != -ERESTARTSYS)
5798c2ecf20Sopenharmony_ci			DRM_ERROR("Failed to sync rings: %i\n", r);
5808c2ecf20Sopenharmony_ci		goto out;
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	if ((rdev->family >= CHIP_TAHITI) &&
5848c2ecf20Sopenharmony_ci	    (parser->chunk_const_ib != NULL)) {
5858c2ecf20Sopenharmony_ci		r = radeon_ib_schedule(rdev, &parser->ib, &parser->const_ib, true);
5868c2ecf20Sopenharmony_ci	} else {
5878c2ecf20Sopenharmony_ci		r = radeon_ib_schedule(rdev, &parser->ib, NULL, true);
5888c2ecf20Sopenharmony_ci	}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ciout:
5918c2ecf20Sopenharmony_ci	mutex_unlock(&vm->mutex);
5928c2ecf20Sopenharmony_ci	return r;
5938c2ecf20Sopenharmony_ci}
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_cistatic int radeon_cs_handle_lockup(struct radeon_device *rdev, int r)
5968c2ecf20Sopenharmony_ci{
5978c2ecf20Sopenharmony_ci	if (r == -EDEADLK) {
5988c2ecf20Sopenharmony_ci		r = radeon_gpu_reset(rdev);
5998c2ecf20Sopenharmony_ci		if (!r)
6008c2ecf20Sopenharmony_ci			r = -EAGAIN;
6018c2ecf20Sopenharmony_ci	}
6028c2ecf20Sopenharmony_ci	return r;
6038c2ecf20Sopenharmony_ci}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_cistatic int radeon_cs_ib_fill(struct radeon_device *rdev, struct radeon_cs_parser *parser)
6068c2ecf20Sopenharmony_ci{
6078c2ecf20Sopenharmony_ci	struct radeon_cs_chunk *ib_chunk;
6088c2ecf20Sopenharmony_ci	struct radeon_vm *vm = NULL;
6098c2ecf20Sopenharmony_ci	int r;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	if (parser->chunk_ib == NULL)
6128c2ecf20Sopenharmony_ci		return 0;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	if (parser->cs_flags & RADEON_CS_USE_VM) {
6158c2ecf20Sopenharmony_ci		struct radeon_fpriv *fpriv = parser->filp->driver_priv;
6168c2ecf20Sopenharmony_ci		vm = &fpriv->vm;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci		if ((rdev->family >= CHIP_TAHITI) &&
6198c2ecf20Sopenharmony_ci		    (parser->chunk_const_ib != NULL)) {
6208c2ecf20Sopenharmony_ci			ib_chunk = parser->chunk_const_ib;
6218c2ecf20Sopenharmony_ci			if (ib_chunk->length_dw > RADEON_IB_VM_MAX_SIZE) {
6228c2ecf20Sopenharmony_ci				DRM_ERROR("cs IB CONST too big: %d\n", ib_chunk->length_dw);
6238c2ecf20Sopenharmony_ci				return -EINVAL;
6248c2ecf20Sopenharmony_ci			}
6258c2ecf20Sopenharmony_ci			r =  radeon_ib_get(rdev, parser->ring, &parser->const_ib,
6268c2ecf20Sopenharmony_ci					   vm, ib_chunk->length_dw * 4);
6278c2ecf20Sopenharmony_ci			if (r) {
6288c2ecf20Sopenharmony_ci				DRM_ERROR("Failed to get const ib !\n");
6298c2ecf20Sopenharmony_ci				return r;
6308c2ecf20Sopenharmony_ci			}
6318c2ecf20Sopenharmony_ci			parser->const_ib.is_const_ib = true;
6328c2ecf20Sopenharmony_ci			parser->const_ib.length_dw = ib_chunk->length_dw;
6338c2ecf20Sopenharmony_ci			if (copy_from_user(parser->const_ib.ptr,
6348c2ecf20Sopenharmony_ci					       ib_chunk->user_ptr,
6358c2ecf20Sopenharmony_ci					       ib_chunk->length_dw * 4))
6368c2ecf20Sopenharmony_ci				return -EFAULT;
6378c2ecf20Sopenharmony_ci		}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci		ib_chunk = parser->chunk_ib;
6408c2ecf20Sopenharmony_ci		if (ib_chunk->length_dw > RADEON_IB_VM_MAX_SIZE) {
6418c2ecf20Sopenharmony_ci			DRM_ERROR("cs IB too big: %d\n", ib_chunk->length_dw);
6428c2ecf20Sopenharmony_ci			return -EINVAL;
6438c2ecf20Sopenharmony_ci		}
6448c2ecf20Sopenharmony_ci	}
6458c2ecf20Sopenharmony_ci	ib_chunk = parser->chunk_ib;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	r =  radeon_ib_get(rdev, parser->ring, &parser->ib,
6488c2ecf20Sopenharmony_ci			   vm, ib_chunk->length_dw * 4);
6498c2ecf20Sopenharmony_ci	if (r) {
6508c2ecf20Sopenharmony_ci		DRM_ERROR("Failed to get ib !\n");
6518c2ecf20Sopenharmony_ci		return r;
6528c2ecf20Sopenharmony_ci	}
6538c2ecf20Sopenharmony_ci	parser->ib.length_dw = ib_chunk->length_dw;
6548c2ecf20Sopenharmony_ci	if (ib_chunk->kdata)
6558c2ecf20Sopenharmony_ci		memcpy(parser->ib.ptr, ib_chunk->kdata, ib_chunk->length_dw * 4);
6568c2ecf20Sopenharmony_ci	else if (copy_from_user(parser->ib.ptr, ib_chunk->user_ptr, ib_chunk->length_dw * 4))
6578c2ecf20Sopenharmony_ci		return -EFAULT;
6588c2ecf20Sopenharmony_ci	return 0;
6598c2ecf20Sopenharmony_ci}
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ciint radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
6628c2ecf20Sopenharmony_ci{
6638c2ecf20Sopenharmony_ci	struct radeon_device *rdev = dev->dev_private;
6648c2ecf20Sopenharmony_ci	struct radeon_cs_parser parser;
6658c2ecf20Sopenharmony_ci	int r;
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	down_read(&rdev->exclusive_lock);
6688c2ecf20Sopenharmony_ci	if (!rdev->accel_working) {
6698c2ecf20Sopenharmony_ci		up_read(&rdev->exclusive_lock);
6708c2ecf20Sopenharmony_ci		return -EBUSY;
6718c2ecf20Sopenharmony_ci	}
6728c2ecf20Sopenharmony_ci	if (rdev->in_reset) {
6738c2ecf20Sopenharmony_ci		up_read(&rdev->exclusive_lock);
6748c2ecf20Sopenharmony_ci		r = radeon_gpu_reset(rdev);
6758c2ecf20Sopenharmony_ci		if (!r)
6768c2ecf20Sopenharmony_ci			r = -EAGAIN;
6778c2ecf20Sopenharmony_ci		return r;
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci	/* initialize parser */
6808c2ecf20Sopenharmony_ci	memset(&parser, 0, sizeof(struct radeon_cs_parser));
6818c2ecf20Sopenharmony_ci	parser.filp = filp;
6828c2ecf20Sopenharmony_ci	parser.rdev = rdev;
6838c2ecf20Sopenharmony_ci	parser.dev = rdev->dev;
6848c2ecf20Sopenharmony_ci	parser.family = rdev->family;
6858c2ecf20Sopenharmony_ci	r = radeon_cs_parser_init(&parser, data);
6868c2ecf20Sopenharmony_ci	if (r) {
6878c2ecf20Sopenharmony_ci		DRM_ERROR("Failed to initialize parser !\n");
6888c2ecf20Sopenharmony_ci		radeon_cs_parser_fini(&parser, r, false);
6898c2ecf20Sopenharmony_ci		up_read(&rdev->exclusive_lock);
6908c2ecf20Sopenharmony_ci		r = radeon_cs_handle_lockup(rdev, r);
6918c2ecf20Sopenharmony_ci		return r;
6928c2ecf20Sopenharmony_ci	}
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	r = radeon_cs_ib_fill(rdev, &parser);
6958c2ecf20Sopenharmony_ci	if (!r) {
6968c2ecf20Sopenharmony_ci		r = radeon_cs_parser_relocs(&parser);
6978c2ecf20Sopenharmony_ci		if (r && r != -ERESTARTSYS)
6988c2ecf20Sopenharmony_ci			DRM_ERROR("Failed to parse relocation %d!\n", r);
6998c2ecf20Sopenharmony_ci	}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	if (r) {
7028c2ecf20Sopenharmony_ci		radeon_cs_parser_fini(&parser, r, false);
7038c2ecf20Sopenharmony_ci		up_read(&rdev->exclusive_lock);
7048c2ecf20Sopenharmony_ci		r = radeon_cs_handle_lockup(rdev, r);
7058c2ecf20Sopenharmony_ci		return r;
7068c2ecf20Sopenharmony_ci	}
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	trace_radeon_cs(&parser);
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	r = radeon_cs_ib_chunk(rdev, &parser);
7118c2ecf20Sopenharmony_ci	if (r) {
7128c2ecf20Sopenharmony_ci		goto out;
7138c2ecf20Sopenharmony_ci	}
7148c2ecf20Sopenharmony_ci	r = radeon_cs_ib_vm_chunk(rdev, &parser);
7158c2ecf20Sopenharmony_ci	if (r) {
7168c2ecf20Sopenharmony_ci		goto out;
7178c2ecf20Sopenharmony_ci	}
7188c2ecf20Sopenharmony_ciout:
7198c2ecf20Sopenharmony_ci	radeon_cs_parser_fini(&parser, r, true);
7208c2ecf20Sopenharmony_ci	up_read(&rdev->exclusive_lock);
7218c2ecf20Sopenharmony_ci	r = radeon_cs_handle_lockup(rdev, r);
7228c2ecf20Sopenharmony_ci	return r;
7238c2ecf20Sopenharmony_ci}
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci/**
7268c2ecf20Sopenharmony_ci * radeon_cs_packet_parse() - parse cp packet and point ib index to next packet
7278c2ecf20Sopenharmony_ci * @parser:	parser structure holding parsing context.
7288c2ecf20Sopenharmony_ci * @pkt:	where to store packet information
7298c2ecf20Sopenharmony_ci *
7308c2ecf20Sopenharmony_ci * Assume that chunk_ib_index is properly set. Will return -EINVAL
7318c2ecf20Sopenharmony_ci * if packet is bigger than remaining ib size. or if packets is unknown.
7328c2ecf20Sopenharmony_ci **/
7338c2ecf20Sopenharmony_ciint radeon_cs_packet_parse(struct radeon_cs_parser *p,
7348c2ecf20Sopenharmony_ci			   struct radeon_cs_packet *pkt,
7358c2ecf20Sopenharmony_ci			   unsigned idx)
7368c2ecf20Sopenharmony_ci{
7378c2ecf20Sopenharmony_ci	struct radeon_cs_chunk *ib_chunk = p->chunk_ib;
7388c2ecf20Sopenharmony_ci	struct radeon_device *rdev = p->rdev;
7398c2ecf20Sopenharmony_ci	uint32_t header;
7408c2ecf20Sopenharmony_ci	int ret = 0, i;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	if (idx >= ib_chunk->length_dw) {
7438c2ecf20Sopenharmony_ci		DRM_ERROR("Can not parse packet at %d after CS end %d !\n",
7448c2ecf20Sopenharmony_ci			  idx, ib_chunk->length_dw);
7458c2ecf20Sopenharmony_ci		return -EINVAL;
7468c2ecf20Sopenharmony_ci	}
7478c2ecf20Sopenharmony_ci	header = radeon_get_ib_value(p, idx);
7488c2ecf20Sopenharmony_ci	pkt->idx = idx;
7498c2ecf20Sopenharmony_ci	pkt->type = RADEON_CP_PACKET_GET_TYPE(header);
7508c2ecf20Sopenharmony_ci	pkt->count = RADEON_CP_PACKET_GET_COUNT(header);
7518c2ecf20Sopenharmony_ci	pkt->one_reg_wr = 0;
7528c2ecf20Sopenharmony_ci	switch (pkt->type) {
7538c2ecf20Sopenharmony_ci	case RADEON_PACKET_TYPE0:
7548c2ecf20Sopenharmony_ci		if (rdev->family < CHIP_R600) {
7558c2ecf20Sopenharmony_ci			pkt->reg = R100_CP_PACKET0_GET_REG(header);
7568c2ecf20Sopenharmony_ci			pkt->one_reg_wr =
7578c2ecf20Sopenharmony_ci				RADEON_CP_PACKET0_GET_ONE_REG_WR(header);
7588c2ecf20Sopenharmony_ci		} else
7598c2ecf20Sopenharmony_ci			pkt->reg = R600_CP_PACKET0_GET_REG(header);
7608c2ecf20Sopenharmony_ci		break;
7618c2ecf20Sopenharmony_ci	case RADEON_PACKET_TYPE3:
7628c2ecf20Sopenharmony_ci		pkt->opcode = RADEON_CP_PACKET3_GET_OPCODE(header);
7638c2ecf20Sopenharmony_ci		break;
7648c2ecf20Sopenharmony_ci	case RADEON_PACKET_TYPE2:
7658c2ecf20Sopenharmony_ci		pkt->count = -1;
7668c2ecf20Sopenharmony_ci		break;
7678c2ecf20Sopenharmony_ci	default:
7688c2ecf20Sopenharmony_ci		DRM_ERROR("Unknown packet type %d at %d !\n", pkt->type, idx);
7698c2ecf20Sopenharmony_ci		ret = -EINVAL;
7708c2ecf20Sopenharmony_ci		goto dump_ib;
7718c2ecf20Sopenharmony_ci	}
7728c2ecf20Sopenharmony_ci	if ((pkt->count + 1 + pkt->idx) >= ib_chunk->length_dw) {
7738c2ecf20Sopenharmony_ci		DRM_ERROR("Packet (%d:%d:%d) end after CS buffer (%d) !\n",
7748c2ecf20Sopenharmony_ci			  pkt->idx, pkt->type, pkt->count, ib_chunk->length_dw);
7758c2ecf20Sopenharmony_ci		ret = -EINVAL;
7768c2ecf20Sopenharmony_ci		goto dump_ib;
7778c2ecf20Sopenharmony_ci	}
7788c2ecf20Sopenharmony_ci	return 0;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_cidump_ib:
7818c2ecf20Sopenharmony_ci	for (i = 0; i < ib_chunk->length_dw; i++) {
7828c2ecf20Sopenharmony_ci		if (i == idx)
7838c2ecf20Sopenharmony_ci			printk("\t0x%08x <---\n", radeon_get_ib_value(p, i));
7848c2ecf20Sopenharmony_ci		else
7858c2ecf20Sopenharmony_ci			printk("\t0x%08x\n", radeon_get_ib_value(p, i));
7868c2ecf20Sopenharmony_ci	}
7878c2ecf20Sopenharmony_ci	return ret;
7888c2ecf20Sopenharmony_ci}
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci/**
7918c2ecf20Sopenharmony_ci * radeon_cs_packet_next_is_pkt3_nop() - test if the next packet is P3 NOP
7928c2ecf20Sopenharmony_ci * @p:		structure holding the parser context.
7938c2ecf20Sopenharmony_ci *
7948c2ecf20Sopenharmony_ci * Check if the next packet is NOP relocation packet3.
7958c2ecf20Sopenharmony_ci **/
7968c2ecf20Sopenharmony_cibool radeon_cs_packet_next_is_pkt3_nop(struct radeon_cs_parser *p)
7978c2ecf20Sopenharmony_ci{
7988c2ecf20Sopenharmony_ci	struct radeon_cs_packet p3reloc;
7998c2ecf20Sopenharmony_ci	int r;
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	r = radeon_cs_packet_parse(p, &p3reloc, p->idx);
8028c2ecf20Sopenharmony_ci	if (r)
8038c2ecf20Sopenharmony_ci		return false;
8048c2ecf20Sopenharmony_ci	if (p3reloc.type != RADEON_PACKET_TYPE3)
8058c2ecf20Sopenharmony_ci		return false;
8068c2ecf20Sopenharmony_ci	if (p3reloc.opcode != RADEON_PACKET3_NOP)
8078c2ecf20Sopenharmony_ci		return false;
8088c2ecf20Sopenharmony_ci	return true;
8098c2ecf20Sopenharmony_ci}
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci/**
8128c2ecf20Sopenharmony_ci * radeon_cs_dump_packet() - dump raw packet context
8138c2ecf20Sopenharmony_ci * @p:		structure holding the parser context.
8148c2ecf20Sopenharmony_ci * @pkt:	structure holding the packet.
8158c2ecf20Sopenharmony_ci *
8168c2ecf20Sopenharmony_ci * Used mostly for debugging and error reporting.
8178c2ecf20Sopenharmony_ci **/
8188c2ecf20Sopenharmony_civoid radeon_cs_dump_packet(struct radeon_cs_parser *p,
8198c2ecf20Sopenharmony_ci			   struct radeon_cs_packet *pkt)
8208c2ecf20Sopenharmony_ci{
8218c2ecf20Sopenharmony_ci	volatile uint32_t *ib;
8228c2ecf20Sopenharmony_ci	unsigned i;
8238c2ecf20Sopenharmony_ci	unsigned idx;
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	ib = p->ib.ptr;
8268c2ecf20Sopenharmony_ci	idx = pkt->idx;
8278c2ecf20Sopenharmony_ci	for (i = 0; i <= (pkt->count + 1); i++, idx++)
8288c2ecf20Sopenharmony_ci		DRM_INFO("ib[%d]=0x%08X\n", idx, ib[idx]);
8298c2ecf20Sopenharmony_ci}
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci/**
8328c2ecf20Sopenharmony_ci * radeon_cs_packet_next_reloc() - parse next (should be reloc) packet
8338c2ecf20Sopenharmony_ci * @parser:		parser structure holding parsing context.
8348c2ecf20Sopenharmony_ci * @data:		pointer to relocation data
8358c2ecf20Sopenharmony_ci * @offset_start:	starting offset
8368c2ecf20Sopenharmony_ci * @offset_mask:	offset mask (to align start offset on)
8378c2ecf20Sopenharmony_ci * @reloc:		reloc informations
8388c2ecf20Sopenharmony_ci *
8398c2ecf20Sopenharmony_ci * Check if next packet is relocation packet3, do bo validation and compute
8408c2ecf20Sopenharmony_ci * GPU offset using the provided start.
8418c2ecf20Sopenharmony_ci **/
8428c2ecf20Sopenharmony_ciint radeon_cs_packet_next_reloc(struct radeon_cs_parser *p,
8438c2ecf20Sopenharmony_ci				struct radeon_bo_list **cs_reloc,
8448c2ecf20Sopenharmony_ci				int nomm)
8458c2ecf20Sopenharmony_ci{
8468c2ecf20Sopenharmony_ci	struct radeon_cs_chunk *relocs_chunk;
8478c2ecf20Sopenharmony_ci	struct radeon_cs_packet p3reloc;
8488c2ecf20Sopenharmony_ci	unsigned idx;
8498c2ecf20Sopenharmony_ci	int r;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	if (p->chunk_relocs == NULL) {
8528c2ecf20Sopenharmony_ci		DRM_ERROR("No relocation chunk !\n");
8538c2ecf20Sopenharmony_ci		return -EINVAL;
8548c2ecf20Sopenharmony_ci	}
8558c2ecf20Sopenharmony_ci	*cs_reloc = NULL;
8568c2ecf20Sopenharmony_ci	relocs_chunk = p->chunk_relocs;
8578c2ecf20Sopenharmony_ci	r = radeon_cs_packet_parse(p, &p3reloc, p->idx);
8588c2ecf20Sopenharmony_ci	if (r)
8598c2ecf20Sopenharmony_ci		return r;
8608c2ecf20Sopenharmony_ci	p->idx += p3reloc.count + 2;
8618c2ecf20Sopenharmony_ci	if (p3reloc.type != RADEON_PACKET_TYPE3 ||
8628c2ecf20Sopenharmony_ci	    p3reloc.opcode != RADEON_PACKET3_NOP) {
8638c2ecf20Sopenharmony_ci		DRM_ERROR("No packet3 for relocation for packet at %d.\n",
8648c2ecf20Sopenharmony_ci			  p3reloc.idx);
8658c2ecf20Sopenharmony_ci		radeon_cs_dump_packet(p, &p3reloc);
8668c2ecf20Sopenharmony_ci		return -EINVAL;
8678c2ecf20Sopenharmony_ci	}
8688c2ecf20Sopenharmony_ci	idx = radeon_get_ib_value(p, p3reloc.idx + 1);
8698c2ecf20Sopenharmony_ci	if (idx >= relocs_chunk->length_dw) {
8708c2ecf20Sopenharmony_ci		DRM_ERROR("Relocs at %d after relocations chunk end %d !\n",
8718c2ecf20Sopenharmony_ci			  idx, relocs_chunk->length_dw);
8728c2ecf20Sopenharmony_ci		radeon_cs_dump_packet(p, &p3reloc);
8738c2ecf20Sopenharmony_ci		return -EINVAL;
8748c2ecf20Sopenharmony_ci	}
8758c2ecf20Sopenharmony_ci	/* FIXME: we assume reloc size is 4 dwords */
8768c2ecf20Sopenharmony_ci	if (nomm) {
8778c2ecf20Sopenharmony_ci		*cs_reloc = p->relocs;
8788c2ecf20Sopenharmony_ci		(*cs_reloc)->gpu_offset =
8798c2ecf20Sopenharmony_ci			(u64)relocs_chunk->kdata[idx + 3] << 32;
8808c2ecf20Sopenharmony_ci		(*cs_reloc)->gpu_offset |= relocs_chunk->kdata[idx + 0];
8818c2ecf20Sopenharmony_ci	} else
8828c2ecf20Sopenharmony_ci		*cs_reloc = &p->relocs[(idx / 4)];
8838c2ecf20Sopenharmony_ci	return 0;
8848c2ecf20Sopenharmony_ci}
885