Edit

IABSD.fr/xenocara/lib/libdrm/tests/amdgpu/amdgpu_stress.c

Branch :

  • Show log

    Commit

  • Author : jsg
    Date : 2022-04-29 10:11:42
    Hash : 17634269
    Message : Import libdrm 2.4.110

  • lib/libdrm/tests/amdgpu/amdgpu_stress.c
  • /*
     * Copyright 2021 Advanced Micro Devices, Inc.
     *
     * Permission is hereby granted, free of charge, to any person obtaining a
     * copy of this software and associated documentation files (the "Software"),
     * to deal in the Software without restriction, including without limitation
     * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     * and/or sell copies of the Software, and to permit persons to whom the
     * Software is furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
     * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     * OTHER DEALINGS IN THE SOFTWARE.
     *
    */
    
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <stdarg.h>
    #include <string.h>
    #include <errno.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    #include "drm.h"
    #include "xf86drmMode.h"
    #include "xf86drm.h"
    #include "amdgpu.h"
    #include "amdgpu_drm.h"
    #include "amdgpu_internal.h"
    
    #define MAX_CARDS_SUPPORTED	4
    #define NUM_BUFFER_OBJECTS	1024
    
    #define SDMA_PACKET(op, sub_op, e)      ((((e) & 0xFFFF) << 16) |  \
    					(((sub_op) & 0xFF) << 8) | \
    					(((op) & 0xFF) << 0))
    
    #define SDMA_OPCODE_COPY				  1
    #       define SDMA_COPY_SUB_OPCODE_LINEAR		0
    
    
    #define SDMA_PACKET_SI(op, b, t, s, cnt)	((((op) & 0xF) << 28) | \
    						(((b) & 0x1) << 26) |	\
    						(((t) & 0x1) << 23) |	\
    						(((s) & 0x1) << 22) |	\
    						(((cnt) & 0xFFFFF) << 0))
    #define SDMA_OPCODE_COPY_SI     3
    
    
    /** Help string for command line parameters */
    static const char usage[] =
    	"Usage: %s [-?h] [-b v|g|vg size] "
    	"[-c from to size count]\n"
    	"where:\n"
    	"	b - Allocate a BO in VRAM, GTT or VRAM|GTT of size bytes.\n"
    	"	    This flag can be used multiple times. The first bo will\n"
    	"	    have id `1`, then second id `2`, ...\n"
    	"       c - Copy size bytes from BO (bo_id1) to BO (bo_id2), count times\n"
    	"       h - Display this help\n"
    	"\n"
    	"Sizes can be postfixes with k, m or g for kilo, mega and gigabyte scaling\n";
    
    /** Specified options strings for getopt */
    static const char options[]   = "?hb:c:";
    
    /* Open AMD devices.
     * Returns the fd of the first device it could open.
     */
    static int amdgpu_open_device(void)
    {
    	drmDevicePtr devices[MAX_CARDS_SUPPORTED];
    	unsigned int i;
    	int drm_count;
    
    	drm_count = drmGetDevices2(0, devices, MAX_CARDS_SUPPORTED);
    	if (drm_count < 0) {
    		fprintf(stderr, "drmGetDevices2() returned an error %d\n",
    			drm_count);
    		return drm_count;
    	}
    
    	for (i = 0; i < drm_count; i++) {
    		drmVersionPtr version;
    		int fd;
    
    		/* If this is not PCI device, skip*/
    		if (devices[i]->bustype != DRM_BUS_PCI)
    			continue;
    
    		/* If this is not AMD GPU vender ID, skip*/
    		if (devices[i]->deviceinfo.pci->vendor_id != 0x1002)
    			continue;
    
    		if (!(devices[i]->available_nodes & 1 << DRM_NODE_RENDER))
    			continue;
    
    		fd = open(devices[i]->nodes[DRM_NODE_RENDER], O_RDWR | O_CLOEXEC);
    
    		/* This node is not available. */
    		if (fd < 0) continue;
    
    		version = drmGetVersion(fd);
    		if (!version) {
    			fprintf(stderr,
    				"Warning: Cannot get version for %s."
    				"Error is %s\n",
    				devices[i]->nodes[DRM_NODE_RENDER],
    				strerror(errno));
    			close(fd);
    			continue;
    		}
    
    		if (strcmp(version->name, "amdgpu")) {
    			/* This is not AMDGPU driver, skip.*/
    			drmFreeVersion(version);
    			close(fd);
    			continue;
    		}
    
    		drmFreeVersion(version);
    		drmFreeDevices(devices, drm_count);
    		return fd;
    	}
    
    	return -1;
    }
    
    amdgpu_device_handle device_handle;
    amdgpu_context_handle context_handle;
    
    amdgpu_bo_handle resources[NUM_BUFFER_OBJECTS];
    uint64_t virtual[NUM_BUFFER_OBJECTS];
    unsigned int num_buffers;
    uint32_t *pm4;
    
    int alloc_bo(uint32_t domain, uint64_t size)
    {
    	struct amdgpu_bo_alloc_request request = {};
    	amdgpu_bo_handle bo;
    	amdgpu_va_handle va;
    	uint64_t addr;
    	int r;
    
    	if (num_buffers >= NUM_BUFFER_OBJECTS)
    		return -ENOSPC;
    
    	request.alloc_size = size;
    	request.phys_alignment = 0;
    	request.preferred_heap = domain;
    	request.flags = 0;
    	r = amdgpu_bo_alloc(device_handle, &request, &bo);
    	if (r)
    		return r;
    
    	r = amdgpu_va_range_alloc(device_handle, amdgpu_gpu_va_range_general,
    				  size, 0, 0, &addr, &va, 0);
    	if (r)
    		return r;
    
    	r = amdgpu_bo_va_op_raw(device_handle, bo, 0, size, addr,
    				AMDGPU_VM_PAGE_READABLE | AMDGPU_VM_PAGE_WRITEABLE |
    				AMDGPU_VM_PAGE_EXECUTABLE, AMDGPU_VA_OP_MAP);
    	if (r)
    		return r;
    
    	resources[num_buffers] = bo;
    	virtual[num_buffers] = addr;
    	fprintf(stdout, "Allocated BO number %u at 0x%lx, domain 0x%x, size %lu\n",
    		num_buffers++, addr, domain, size);
    	return 0;
    }
    
    int submit_ib(uint32_t from, uint32_t to, uint64_t size, uint32_t count)
    {
    	struct amdgpu_cs_request ibs_request;
    	struct amdgpu_cs_fence fence_status;
    	struct amdgpu_cs_ib_info ib_info;
    	uint64_t copied = size, delta;
    	struct timespec start, stop;
    
    	uint64_t src = virtual[from];
    	uint64_t dst = virtual[to];
    	uint32_t expired;
    	int i, r;
    
    	i = 0;
    	while (size) {
    		uint64_t bytes = size < 0x40000 ? size : 0x40000;
    
    		if (device_handle->info.family_id == AMDGPU_FAMILY_SI) {
    			pm4[i++] = SDMA_PACKET_SI(SDMA_OPCODE_COPY_SI, 0, 0, 0,
    						  bytes);
    			pm4[i++] = 0xffffffff & dst;
    			pm4[i++] = 0xffffffff & src;
    			pm4[i++] = (0xffffffff00000000 & dst) >> 32;
    			pm4[i++] = (0xffffffff00000000 & src) >> 32;
    		} else {
    			pm4[i++] = SDMA_PACKET(SDMA_OPCODE_COPY,
    					       SDMA_COPY_SUB_OPCODE_LINEAR,
    					       0);
    			if ( device_handle->info.family_id >= AMDGPU_FAMILY_AI)
    				pm4[i++] = bytes - 1;
    			else
    				pm4[i++] = bytes;
    			pm4[i++] = 0;
    			pm4[i++] = 0xffffffff & src;
    			pm4[i++] = (0xffffffff00000000 & src) >> 32;
    			pm4[i++] = 0xffffffff & dst;
    			pm4[i++] = (0xffffffff00000000 & dst) >> 32;
    		}
    
    		size -= bytes;
    		src += bytes;
    		dst += bytes;
    	}
    
    	memset(&ib_info, 0, sizeof(ib_info));
    	ib_info.ib_mc_address = virtual[0];
    	ib_info.size = i;
    
    	memset(&ibs_request, 0, sizeof(ibs_request));
    	ibs_request.ip_type = AMDGPU_HW_IP_DMA;
    	ibs_request.ring = 0;
    	ibs_request.number_of_ibs = 1;
    	ibs_request.ibs = &ib_info;
    	ibs_request.fence_info.handle = NULL;
    
    	r = clock_gettime(CLOCK_MONOTONIC, &start);
    	if (r)
    		return errno;
    
    	r = amdgpu_bo_list_create(device_handle, num_buffers, resources, NULL,
    				  &ibs_request.resources);
    	if (r)
    		return r;
    
    	for (i = 0; i < count; ++i) {
    		r = amdgpu_cs_submit(context_handle, 0, &ibs_request, 1);
    		if (r)
    			return r;
    	}
    
    	r = amdgpu_bo_list_destroy(ibs_request.resources);
    	if (r)
    		return r;
    
    	memset(&fence_status, 0, sizeof(fence_status));
    	fence_status.ip_type = ibs_request.ip_type;
    	fence_status.ip_instance = 0;
    	fence_status.ring = ibs_request.ring;
    	fence_status.context = context_handle;
    	fence_status.fence = ibs_request.seq_no;
    	r = amdgpu_cs_query_fence_status(&fence_status,
    					 AMDGPU_TIMEOUT_INFINITE,
    					 0, &expired);
    	if (r)
    		return r;
    
    	r = clock_gettime(CLOCK_MONOTONIC, &stop);
    	if (r)
    		return errno;
    
    	delta = stop.tv_nsec + stop.tv_sec * 1000000000UL;
    	delta -= start.tv_nsec + start.tv_sec * 1000000000UL;
    
    	fprintf(stdout, "Submitted %u IBs to copy from %u(%lx) to %u(%lx) %lu bytes took %lu usec\n",
    		count, from, virtual[from], to, virtual[to], copied, delta / 1000);
    	return 0;
    }
    
    void next_arg(int argc, char **argv, const char *msg)
    {
    	optarg = argv[optind++];
    	if (optind > argc || optarg[0] == '-') {
    		fprintf(stderr, "%s\n", msg);
    		exit(EXIT_FAILURE);
    	}
    }
    
    uint64_t parse_size(void)
    {
    	uint64_t size;
    	char ext[2];
    
    	ext[0] = 0;
    	if (sscanf(optarg, "%li%1[kmgKMG]", &size, ext) < 1) {
    		fprintf(stderr, "Can't parse size arg: %s\n", optarg);
    		exit(EXIT_FAILURE);
    	}
    	switch (ext[0]) {
    	case 'k':
    	case 'K':
    		size *= 1024;
    		break;
    	case 'm':
    	case 'M':
    		size *= 1024 * 1024;
    		break;
    	case 'g':
    	case 'G':
    		size *= 1024 * 1024 * 1024;
    		break;
    	default:
    		break;
    	}
    	return size;
    }
    
    int main(int argc, char **argv)
    {
    	uint32_t major_version, minor_version;
    	uint32_t domain, from, to, count;
           	uint64_t size;
    	int fd, r, c;
    
    	fd = amdgpu_open_device();
           	if (fd < 0) {
    		perror("Cannot open AMDGPU device");
    		exit(EXIT_FAILURE);
    	}
    
    	r = amdgpu_device_initialize(fd, &major_version, &minor_version, &device_handle);
    	if (r) {
    		fprintf(stderr, "amdgpu_device_initialize returned %d\n", r);
    		exit(EXIT_FAILURE);
    	}
    
    	r = amdgpu_cs_ctx_create(device_handle, &context_handle);
    	if (r) {
    		fprintf(stderr, "amdgpu_cs_ctx_create returned %d\n", r);
    		exit(EXIT_FAILURE);
    	}
    
    	if (argc == 1) {
    		fprintf(stderr, usage, argv[0]);
    		exit(EXIT_FAILURE);
    	}
    
    	r = alloc_bo(AMDGPU_GEM_DOMAIN_GTT, 2ULL * 1024 * 1024);
    	if (r) {
    		fprintf(stderr, "Buffer allocation failed with %d\n", r);
    		exit(EXIT_FAILURE);
    	}
    
    	r = amdgpu_bo_cpu_map(resources[0], (void **)&pm4);
    	if (r) {
    		fprintf(stderr, "Buffer mapping failed with %d\n", r);
    		exit(EXIT_FAILURE);
    	}
    
    	opterr = 0;
    	while ((c = getopt(argc, argv, options)) != -1) {
    		switch (c) {
    		case 'b':
    			if (!strcmp(optarg, "v"))
    				domain = AMDGPU_GEM_DOMAIN_VRAM;
    			else if (!strcmp(optarg, "g"))
    				domain = AMDGPU_GEM_DOMAIN_GTT;
    			else if (!strcmp(optarg, "vg"))
    				domain = AMDGPU_GEM_DOMAIN_VRAM | AMDGPU_GEM_DOMAIN_GTT;
    			else {
    				fprintf(stderr, "Invalid domain: %s\n", optarg);
    				exit(EXIT_FAILURE);
    			}
    			next_arg(argc, argv, "Missing buffer size");
    			size = parse_size();
    			if (size < getpagesize()) {
    				fprintf(stderr, "Buffer size to small %lu\n", size);
    				exit(EXIT_FAILURE);
    			}
    			r = alloc_bo(domain, size);
    			if (r) {
    				fprintf(stderr, "Buffer allocation failed with %d\n", r);
    				exit(EXIT_FAILURE);
    			}
    			break;
    		case 'c':
    			if (sscanf(optarg, "%u", &from) != 1) {
    				fprintf(stderr, "Can't parse from buffer: %s\n", optarg);
    				exit(EXIT_FAILURE);
    			}
    			next_arg(argc, argv, "Missing to buffer");
    			if (sscanf(optarg, "%u", &to) != 1) {
    				fprintf(stderr, "Can't parse to buffer: %s\n", optarg);
    				exit(EXIT_FAILURE);
    			}
    			next_arg(argc, argv, "Missing size");
    			size = parse_size();
    			next_arg(argc, argv, "Missing count");
    			count = parse_size();
    			r = submit_ib(from, to, size, count);
    			if (r) {
    				fprintf(stderr, "IB submission failed with %d\n", r);
    				exit(EXIT_FAILURE);
    			}
    			break;
    		case '?':
    		case 'h':
    			fprintf(stderr, usage, argv[0]);
    			exit(EXIT_SUCCESS);
    		default:
    			fprintf(stderr, usage, argv[0]);
    			exit(EXIT_FAILURE);
    		}
    	}
    
    	return EXIT_SUCCESS;
    }