149ef06a4Smrg/*
249ef06a4Smrg * Copyright 2021 Advanced Micro Devices, Inc.
349ef06a4Smrg *
449ef06a4Smrg * Permission is hereby granted, free of charge, to any person obtaining a
549ef06a4Smrg * copy of this software and associated documentation files (the "Software"),
649ef06a4Smrg * to deal in the Software without restriction, including without limitation
749ef06a4Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
849ef06a4Smrg * and/or sell copies of the Software, and to permit persons to whom the
949ef06a4Smrg * Software is furnished to do so, subject to the following conditions:
1049ef06a4Smrg *
1149ef06a4Smrg * The above copyright notice and this permission notice shall be included in
1249ef06a4Smrg * all copies or substantial portions of the Software.
1349ef06a4Smrg *
1449ef06a4Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1549ef06a4Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1649ef06a4Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1749ef06a4Smrg * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
1849ef06a4Smrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
1949ef06a4Smrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2049ef06a4Smrg * OTHER DEALINGS IN THE SOFTWARE.
2149ef06a4Smrg *
2249ef06a4Smrg*/
2349ef06a4Smrg
2449ef06a4Smrg#include <stdio.h>
2549ef06a4Smrg#include <sys/types.h>
2649ef06a4Smrg#include <sys/stat.h>
2749ef06a4Smrg#include <fcntl.h>
2849ef06a4Smrg#include <stdarg.h>
2949ef06a4Smrg#include <string.h>
3049ef06a4Smrg#include <errno.h>
3149ef06a4Smrg#include <unistd.h>
3249ef06a4Smrg#include <stdlib.h>
33bbff01ceSmrg#include <inttypes.h>
3449ef06a4Smrg
3549ef06a4Smrg#include "drm.h"
3649ef06a4Smrg#include "xf86drmMode.h"
3749ef06a4Smrg#include "xf86drm.h"
3849ef06a4Smrg#include "amdgpu.h"
3949ef06a4Smrg#include "amdgpu_drm.h"
4049ef06a4Smrg#include "amdgpu_internal.h"
4149ef06a4Smrg
4249ef06a4Smrg#define MAX_CARDS_SUPPORTED	4
4349ef06a4Smrg#define NUM_BUFFER_OBJECTS	1024
4449ef06a4Smrg
4549ef06a4Smrg#define SDMA_PACKET(op, sub_op, e)      ((((e) & 0xFFFF) << 16) |  \
4649ef06a4Smrg					(((sub_op) & 0xFF) << 8) | \
4749ef06a4Smrg					(((op) & 0xFF) << 0))
4849ef06a4Smrg
4949ef06a4Smrg#define SDMA_OPCODE_COPY				  1
5049ef06a4Smrg#       define SDMA_COPY_SUB_OPCODE_LINEAR		0
5149ef06a4Smrg
5249ef06a4Smrg
5349ef06a4Smrg#define SDMA_PACKET_SI(op, b, t, s, cnt)	((((op) & 0xF) << 28) | \
5449ef06a4Smrg						(((b) & 0x1) << 26) |	\
5549ef06a4Smrg						(((t) & 0x1) << 23) |	\
5649ef06a4Smrg						(((s) & 0x1) << 22) |	\
5749ef06a4Smrg						(((cnt) & 0xFFFFF) << 0))
5849ef06a4Smrg#define SDMA_OPCODE_COPY_SI     3
5949ef06a4Smrg
6049ef06a4Smrg
6149ef06a4Smrg/** Help string for command line parameters */
6249ef06a4Smrgstatic const char usage[] =
6349ef06a4Smrg	"Usage: %s [-?h] [-b v|g|vg size] "
6449ef06a4Smrg	"[-c from to size count]\n"
6549ef06a4Smrg	"where:\n"
6649ef06a4Smrg	"	b - Allocate a BO in VRAM, GTT or VRAM|GTT of size bytes.\n"
6749ef06a4Smrg	"	    This flag can be used multiple times. The first bo will\n"
6849ef06a4Smrg	"	    have id `1`, then second id `2`, ...\n"
6949ef06a4Smrg	"       c - Copy size bytes from BO (bo_id1) to BO (bo_id2), count times\n"
7049ef06a4Smrg	"       h - Display this help\n"
7149ef06a4Smrg	"\n"
7249ef06a4Smrg	"Sizes can be postfixes with k, m or g for kilo, mega and gigabyte scaling\n";
7349ef06a4Smrg
7449ef06a4Smrg/** Specified options strings for getopt */
7549ef06a4Smrgstatic const char options[]   = "?hb:c:";
7649ef06a4Smrg
7749ef06a4Smrg/* Open AMD devices.
7849ef06a4Smrg * Returns the fd of the first device it could open.
7949ef06a4Smrg */
8049ef06a4Smrgstatic int amdgpu_open_device(void)
8149ef06a4Smrg{
8249ef06a4Smrg	drmDevicePtr devices[MAX_CARDS_SUPPORTED];
8349ef06a4Smrg	unsigned int i;
8449ef06a4Smrg	int drm_count;
8549ef06a4Smrg
8649ef06a4Smrg	drm_count = drmGetDevices2(0, devices, MAX_CARDS_SUPPORTED);
8749ef06a4Smrg	if (drm_count < 0) {
8849ef06a4Smrg		fprintf(stderr, "drmGetDevices2() returned an error %d\n",
8949ef06a4Smrg			drm_count);
9049ef06a4Smrg		return drm_count;
9149ef06a4Smrg	}
9249ef06a4Smrg
9349ef06a4Smrg	for (i = 0; i < drm_count; i++) {
9449ef06a4Smrg		drmVersionPtr version;
9549ef06a4Smrg		int fd;
9649ef06a4Smrg
9749ef06a4Smrg		/* If this is not PCI device, skip*/
9849ef06a4Smrg		if (devices[i]->bustype != DRM_BUS_PCI)
9949ef06a4Smrg			continue;
10049ef06a4Smrg
10149ef06a4Smrg		/* If this is not AMD GPU vender ID, skip*/
10249ef06a4Smrg		if (devices[i]->deviceinfo.pci->vendor_id != 0x1002)
10349ef06a4Smrg			continue;
10449ef06a4Smrg
10549ef06a4Smrg		if (!(devices[i]->available_nodes & 1 << DRM_NODE_RENDER))
10649ef06a4Smrg			continue;
10749ef06a4Smrg
10849ef06a4Smrg		fd = open(devices[i]->nodes[DRM_NODE_RENDER], O_RDWR | O_CLOEXEC);
10949ef06a4Smrg
11049ef06a4Smrg		/* This node is not available. */
11149ef06a4Smrg		if (fd < 0) continue;
11249ef06a4Smrg
11349ef06a4Smrg		version = drmGetVersion(fd);
11449ef06a4Smrg		if (!version) {
11549ef06a4Smrg			fprintf(stderr,
11649ef06a4Smrg				"Warning: Cannot get version for %s."
11749ef06a4Smrg				"Error is %s\n",
11849ef06a4Smrg				devices[i]->nodes[DRM_NODE_RENDER],
11949ef06a4Smrg				strerror(errno));
12049ef06a4Smrg			close(fd);
12149ef06a4Smrg			continue;
12249ef06a4Smrg		}
12349ef06a4Smrg
12449ef06a4Smrg		if (strcmp(version->name, "amdgpu")) {
12549ef06a4Smrg			/* This is not AMDGPU driver, skip.*/
12649ef06a4Smrg			drmFreeVersion(version);
12749ef06a4Smrg			close(fd);
12849ef06a4Smrg			continue;
12949ef06a4Smrg		}
13049ef06a4Smrg
13149ef06a4Smrg		drmFreeVersion(version);
13249ef06a4Smrg		drmFreeDevices(devices, drm_count);
13349ef06a4Smrg		return fd;
13449ef06a4Smrg	}
13549ef06a4Smrg
13649ef06a4Smrg	return -1;
13749ef06a4Smrg}
13849ef06a4Smrg
13949ef06a4Smrgamdgpu_device_handle device_handle;
14049ef06a4Smrgamdgpu_context_handle context_handle;
14149ef06a4Smrg
14249ef06a4Smrgamdgpu_bo_handle resources[NUM_BUFFER_OBJECTS];
14349ef06a4Smrguint64_t virtual[NUM_BUFFER_OBJECTS];
14449ef06a4Smrgunsigned int num_buffers;
14549ef06a4Smrguint32_t *pm4;
14649ef06a4Smrg
14749ef06a4Smrgint alloc_bo(uint32_t domain, uint64_t size)
14849ef06a4Smrg{
14949ef06a4Smrg	struct amdgpu_bo_alloc_request request = {};
15049ef06a4Smrg	amdgpu_bo_handle bo;
15149ef06a4Smrg	amdgpu_va_handle va;
15249ef06a4Smrg	uint64_t addr;
15349ef06a4Smrg	int r;
15449ef06a4Smrg
15549ef06a4Smrg	if (num_buffers >= NUM_BUFFER_OBJECTS)
15649ef06a4Smrg		return -ENOSPC;
15749ef06a4Smrg
15849ef06a4Smrg	request.alloc_size = size;
15949ef06a4Smrg	request.phys_alignment = 0;
16049ef06a4Smrg	request.preferred_heap = domain;
16149ef06a4Smrg	request.flags = 0;
16249ef06a4Smrg	r = amdgpu_bo_alloc(device_handle, &request, &bo);
16349ef06a4Smrg	if (r)
16449ef06a4Smrg		return r;
16549ef06a4Smrg
16649ef06a4Smrg	r = amdgpu_va_range_alloc(device_handle, amdgpu_gpu_va_range_general,
16749ef06a4Smrg				  size, 0, 0, &addr, &va, 0);
16849ef06a4Smrg	if (r)
16949ef06a4Smrg		return r;
17049ef06a4Smrg
17149ef06a4Smrg	r = amdgpu_bo_va_op_raw(device_handle, bo, 0, size, addr,
17249ef06a4Smrg				AMDGPU_VM_PAGE_READABLE | AMDGPU_VM_PAGE_WRITEABLE |
17349ef06a4Smrg				AMDGPU_VM_PAGE_EXECUTABLE, AMDGPU_VA_OP_MAP);
17449ef06a4Smrg	if (r)
17549ef06a4Smrg		return r;
17649ef06a4Smrg
17749ef06a4Smrg	resources[num_buffers] = bo;
17849ef06a4Smrg	virtual[num_buffers] = addr;
179bbff01ceSmrg	fprintf(stdout, "Allocated BO number %u at 0x%" PRIx64 ", domain 0x%x, size %" PRIu64 "\n",
18049ef06a4Smrg		num_buffers++, addr, domain, size);
18149ef06a4Smrg	return 0;
18249ef06a4Smrg}
18349ef06a4Smrg
18449ef06a4Smrgint submit_ib(uint32_t from, uint32_t to, uint64_t size, uint32_t count)
18549ef06a4Smrg{
18649ef06a4Smrg	struct amdgpu_cs_request ibs_request;
18749ef06a4Smrg	struct amdgpu_cs_fence fence_status;
18849ef06a4Smrg	struct amdgpu_cs_ib_info ib_info;
18949ef06a4Smrg	uint64_t copied = size, delta;
19049ef06a4Smrg	struct timespec start, stop;
19149ef06a4Smrg
19249ef06a4Smrg	uint64_t src = virtual[from];
19349ef06a4Smrg	uint64_t dst = virtual[to];
19449ef06a4Smrg	uint32_t expired;
19549ef06a4Smrg	int i, r;
19649ef06a4Smrg
19749ef06a4Smrg	i = 0;
19849ef06a4Smrg	while (size) {
19949ef06a4Smrg		uint64_t bytes = size < 0x40000 ? size : 0x40000;
20049ef06a4Smrg
20149ef06a4Smrg		if (device_handle->info.family_id == AMDGPU_FAMILY_SI) {
20249ef06a4Smrg			pm4[i++] = SDMA_PACKET_SI(SDMA_OPCODE_COPY_SI, 0, 0, 0,
20349ef06a4Smrg						  bytes);
20449ef06a4Smrg			pm4[i++] = 0xffffffff & dst;
20549ef06a4Smrg			pm4[i++] = 0xffffffff & src;
20649ef06a4Smrg			pm4[i++] = (0xffffffff00000000 & dst) >> 32;
20749ef06a4Smrg			pm4[i++] = (0xffffffff00000000 & src) >> 32;
20849ef06a4Smrg		} else {
20949ef06a4Smrg			pm4[i++] = SDMA_PACKET(SDMA_OPCODE_COPY,
21049ef06a4Smrg					       SDMA_COPY_SUB_OPCODE_LINEAR,
21149ef06a4Smrg					       0);
21249ef06a4Smrg			if ( device_handle->info.family_id >= AMDGPU_FAMILY_AI)
21349ef06a4Smrg				pm4[i++] = bytes - 1;
21449ef06a4Smrg			else
21549ef06a4Smrg				pm4[i++] = bytes;
21649ef06a4Smrg			pm4[i++] = 0;
21749ef06a4Smrg			pm4[i++] = 0xffffffff & src;
21849ef06a4Smrg			pm4[i++] = (0xffffffff00000000 & src) >> 32;
21949ef06a4Smrg			pm4[i++] = 0xffffffff & dst;
22049ef06a4Smrg			pm4[i++] = (0xffffffff00000000 & dst) >> 32;
22149ef06a4Smrg		}
22249ef06a4Smrg
22349ef06a4Smrg		size -= bytes;
22449ef06a4Smrg		src += bytes;
22549ef06a4Smrg		dst += bytes;
22649ef06a4Smrg	}
22749ef06a4Smrg
22849ef06a4Smrg	memset(&ib_info, 0, sizeof(ib_info));
22949ef06a4Smrg	ib_info.ib_mc_address = virtual[0];
23049ef06a4Smrg	ib_info.size = i;
23149ef06a4Smrg
23249ef06a4Smrg	memset(&ibs_request, 0, sizeof(ibs_request));
23349ef06a4Smrg	ibs_request.ip_type = AMDGPU_HW_IP_DMA;
23449ef06a4Smrg	ibs_request.ring = 0;
23549ef06a4Smrg	ibs_request.number_of_ibs = 1;
23649ef06a4Smrg	ibs_request.ibs = &ib_info;
23749ef06a4Smrg	ibs_request.fence_info.handle = NULL;
23849ef06a4Smrg
23949ef06a4Smrg	r = clock_gettime(CLOCK_MONOTONIC, &start);
24049ef06a4Smrg	if (r)
24149ef06a4Smrg		return errno;
24249ef06a4Smrg
24349ef06a4Smrg	r = amdgpu_bo_list_create(device_handle, num_buffers, resources, NULL,
24449ef06a4Smrg				  &ibs_request.resources);
24549ef06a4Smrg	if (r)
24649ef06a4Smrg		return r;
24749ef06a4Smrg
24849ef06a4Smrg	for (i = 0; i < count; ++i) {
24949ef06a4Smrg		r = amdgpu_cs_submit(context_handle, 0, &ibs_request, 1);
25049ef06a4Smrg		if (r)
25149ef06a4Smrg			return r;
25249ef06a4Smrg	}
25349ef06a4Smrg
25449ef06a4Smrg	r = amdgpu_bo_list_destroy(ibs_request.resources);
25549ef06a4Smrg	if (r)
25649ef06a4Smrg		return r;
25749ef06a4Smrg
25849ef06a4Smrg	memset(&fence_status, 0, sizeof(fence_status));
25949ef06a4Smrg	fence_status.ip_type = ibs_request.ip_type;
26049ef06a4Smrg	fence_status.ip_instance = 0;
26149ef06a4Smrg	fence_status.ring = ibs_request.ring;
26249ef06a4Smrg	fence_status.context = context_handle;
26349ef06a4Smrg	fence_status.fence = ibs_request.seq_no;
26449ef06a4Smrg	r = amdgpu_cs_query_fence_status(&fence_status,
26549ef06a4Smrg					 AMDGPU_TIMEOUT_INFINITE,
26649ef06a4Smrg					 0, &expired);
26749ef06a4Smrg	if (r)
26849ef06a4Smrg		return r;
26949ef06a4Smrg
27049ef06a4Smrg	r = clock_gettime(CLOCK_MONOTONIC, &stop);
27149ef06a4Smrg	if (r)
27249ef06a4Smrg		return errno;
27349ef06a4Smrg
27449ef06a4Smrg	delta = stop.tv_nsec + stop.tv_sec * 1000000000UL;
27549ef06a4Smrg	delta -= start.tv_nsec + start.tv_sec * 1000000000UL;
27649ef06a4Smrg
277bbff01ceSmrg	fprintf(stdout, "Submitted %u IBs to copy from %u(%" PRIx64 ") to %u(%" PRIx64 ") %" PRIu64 " bytes took %" PRIu64 " usec\n",
27849ef06a4Smrg		count, from, virtual[from], to, virtual[to], copied, delta / 1000);
27949ef06a4Smrg	return 0;
28049ef06a4Smrg}
28149ef06a4Smrg
28249ef06a4Smrgvoid next_arg(int argc, char **argv, const char *msg)
28349ef06a4Smrg{
28449ef06a4Smrg	optarg = argv[optind++];
28549ef06a4Smrg	if (optind > argc || optarg[0] == '-') {
28649ef06a4Smrg		fprintf(stderr, "%s\n", msg);
28749ef06a4Smrg		exit(EXIT_FAILURE);
28849ef06a4Smrg	}
28949ef06a4Smrg}
29049ef06a4Smrg
29149ef06a4Smrguint64_t parse_size(void)
29249ef06a4Smrg{
29349ef06a4Smrg	uint64_t size;
29449ef06a4Smrg	char ext[2];
29549ef06a4Smrg
29649ef06a4Smrg	ext[0] = 0;
297bbff01ceSmrg	if (sscanf(optarg, "%" PRIi64 "%1[kmgKMG]", &size, ext) < 1) {
29849ef06a4Smrg		fprintf(stderr, "Can't parse size arg: %s\n", optarg);
29949ef06a4Smrg		exit(EXIT_FAILURE);
30049ef06a4Smrg	}
30149ef06a4Smrg	switch (ext[0]) {
30249ef06a4Smrg	case 'k':
30349ef06a4Smrg	case 'K':
30449ef06a4Smrg		size *= 1024;
30549ef06a4Smrg		break;
30649ef06a4Smrg	case 'm':
30749ef06a4Smrg	case 'M':
30849ef06a4Smrg		size *= 1024 * 1024;
30949ef06a4Smrg		break;
31049ef06a4Smrg	case 'g':
31149ef06a4Smrg	case 'G':
31249ef06a4Smrg		size *= 1024 * 1024 * 1024;
31349ef06a4Smrg		break;
31449ef06a4Smrg	default:
31549ef06a4Smrg		break;
31649ef06a4Smrg	}
31749ef06a4Smrg	return size;
31849ef06a4Smrg}
31949ef06a4Smrg
32049ef06a4Smrgint main(int argc, char **argv)
32149ef06a4Smrg{
32249ef06a4Smrg	uint32_t major_version, minor_version;
32349ef06a4Smrg	uint32_t domain, from, to, count;
32449ef06a4Smrg       	uint64_t size;
32549ef06a4Smrg	int fd, r, c;
32649ef06a4Smrg
32749ef06a4Smrg	fd = amdgpu_open_device();
32849ef06a4Smrg       	if (fd < 0) {
32949ef06a4Smrg		perror("Cannot open AMDGPU device");
33049ef06a4Smrg		exit(EXIT_FAILURE);
33149ef06a4Smrg	}
33249ef06a4Smrg
33349ef06a4Smrg	r = amdgpu_device_initialize(fd, &major_version, &minor_version, &device_handle);
33449ef06a4Smrg	if (r) {
33549ef06a4Smrg		fprintf(stderr, "amdgpu_device_initialize returned %d\n", r);
33649ef06a4Smrg		exit(EXIT_FAILURE);
33749ef06a4Smrg	}
33849ef06a4Smrg
33949ef06a4Smrg	r = amdgpu_cs_ctx_create(device_handle, &context_handle);
34049ef06a4Smrg	if (r) {
34149ef06a4Smrg		fprintf(stderr, "amdgpu_cs_ctx_create returned %d\n", r);
34249ef06a4Smrg		exit(EXIT_FAILURE);
34349ef06a4Smrg	}
34449ef06a4Smrg
34549ef06a4Smrg	if (argc == 1) {
34649ef06a4Smrg		fprintf(stderr, usage, argv[0]);
34749ef06a4Smrg		exit(EXIT_FAILURE);
34849ef06a4Smrg	}
34949ef06a4Smrg
35049ef06a4Smrg	r = alloc_bo(AMDGPU_GEM_DOMAIN_GTT, 2ULL * 1024 * 1024);
35149ef06a4Smrg	if (r) {
35249ef06a4Smrg		fprintf(stderr, "Buffer allocation failed with %d\n", r);
35349ef06a4Smrg		exit(EXIT_FAILURE);
35449ef06a4Smrg	}
35549ef06a4Smrg
35649ef06a4Smrg	r = amdgpu_bo_cpu_map(resources[0], (void **)&pm4);
35749ef06a4Smrg	if (r) {
35849ef06a4Smrg		fprintf(stderr, "Buffer mapping failed with %d\n", r);
35949ef06a4Smrg		exit(EXIT_FAILURE);
36049ef06a4Smrg	}
36149ef06a4Smrg
36249ef06a4Smrg	opterr = 0;
36349ef06a4Smrg	while ((c = getopt(argc, argv, options)) != -1) {
36449ef06a4Smrg		switch (c) {
36549ef06a4Smrg		case 'b':
36649ef06a4Smrg			if (!strcmp(optarg, "v"))
36749ef06a4Smrg				domain = AMDGPU_GEM_DOMAIN_VRAM;
36849ef06a4Smrg			else if (!strcmp(optarg, "g"))
36949ef06a4Smrg				domain = AMDGPU_GEM_DOMAIN_GTT;
37049ef06a4Smrg			else if (!strcmp(optarg, "vg"))
37149ef06a4Smrg				domain = AMDGPU_GEM_DOMAIN_VRAM | AMDGPU_GEM_DOMAIN_GTT;
37249ef06a4Smrg			else {
37349ef06a4Smrg				fprintf(stderr, "Invalid domain: %s\n", optarg);
37449ef06a4Smrg				exit(EXIT_FAILURE);
37549ef06a4Smrg			}
37649ef06a4Smrg			next_arg(argc, argv, "Missing buffer size");
37749ef06a4Smrg			size = parse_size();
37849ef06a4Smrg			if (size < getpagesize()) {
379bbff01ceSmrg				fprintf(stderr, "Buffer size to small %" PRIu64 "\n", size);
38049ef06a4Smrg				exit(EXIT_FAILURE);
38149ef06a4Smrg			}
38249ef06a4Smrg			r = alloc_bo(domain, size);
38349ef06a4Smrg			if (r) {
38449ef06a4Smrg				fprintf(stderr, "Buffer allocation failed with %d\n", r);
38549ef06a4Smrg				exit(EXIT_FAILURE);
38649ef06a4Smrg			}
38749ef06a4Smrg			break;
38849ef06a4Smrg		case 'c':
38949ef06a4Smrg			if (sscanf(optarg, "%u", &from) != 1) {
39049ef06a4Smrg				fprintf(stderr, "Can't parse from buffer: %s\n", optarg);
39149ef06a4Smrg				exit(EXIT_FAILURE);
39249ef06a4Smrg			}
39349ef06a4Smrg			next_arg(argc, argv, "Missing to buffer");
39449ef06a4Smrg			if (sscanf(optarg, "%u", &to) != 1) {
39549ef06a4Smrg				fprintf(stderr, "Can't parse to buffer: %s\n", optarg);
39649ef06a4Smrg				exit(EXIT_FAILURE);
39749ef06a4Smrg			}
39849ef06a4Smrg			next_arg(argc, argv, "Missing size");
39949ef06a4Smrg			size = parse_size();
40049ef06a4Smrg			next_arg(argc, argv, "Missing count");
40149ef06a4Smrg			count = parse_size();
40249ef06a4Smrg			r = submit_ib(from, to, size, count);
40349ef06a4Smrg			if (r) {
40449ef06a4Smrg				fprintf(stderr, "IB submission failed with %d\n", r);
40549ef06a4Smrg				exit(EXIT_FAILURE);
40649ef06a4Smrg			}
40749ef06a4Smrg			break;
40849ef06a4Smrg		case '?':
40949ef06a4Smrg		case 'h':
41049ef06a4Smrg			fprintf(stderr, usage, argv[0]);
41149ef06a4Smrg			exit(EXIT_SUCCESS);
41249ef06a4Smrg		default:
41349ef06a4Smrg			fprintf(stderr, usage, argv[0]);
41449ef06a4Smrg			exit(EXIT_FAILURE);
41549ef06a4Smrg		}
41649ef06a4Smrg	}
41749ef06a4Smrg
41849ef06a4Smrg	return EXIT_SUCCESS;
41949ef06a4Smrg}
420