1e6188e58Smrg/*
2e6188e58Smrg * Copyright © 2015 Canonical Ltd. (Maarten Lankhorst)
3e6188e58Smrg *
4e6188e58Smrg * Permission is hereby granted, free of charge, to any person obtaining a
5e6188e58Smrg * copy of this software and associated documentation files (the "Software"),
6e6188e58Smrg * to deal in the Software without restriction, including without limitation
7e6188e58Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8e6188e58Smrg * and/or sell copies of the Software, and to permit persons to whom the
9e6188e58Smrg * Software is furnished to do so, subject to the following conditions:
10e6188e58Smrg *
11e6188e58Smrg * The above copyright notice and this permission notice shall be included in
12e6188e58Smrg * all copies or substantial portions of the Software.
13e6188e58Smrg *
14e6188e58Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15e6188e58Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16e6188e58Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17e6188e58Smrg * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18e6188e58Smrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19e6188e58Smrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20e6188e58Smrg * OTHER DEALINGS IN THE SOFTWARE.
21e6188e58Smrg */
22e6188e58Smrg
23e6188e58Smrg#include <sys/ioctl.h>
24e6188e58Smrg#include <dlfcn.h>
25e6188e58Smrg#include <fcntl.h>
26e6188e58Smrg#include <stdio.h>
27e6188e58Smrg#include <unistd.h>
28e6188e58Smrg#include <errno.h>
29e6188e58Smrg#include <pthread.h>
30e6188e58Smrg
31e6188e58Smrg#include "xf86drm.h"
32e6188e58Smrg#include "nouveau.h"
33e6188e58Smrg
340ed5401bSmrgstatic __typeof__(ioctl) *old_ioctl;
35e6188e58Smrgstatic int failed;
36e6188e58Smrg
37e6188e58Smrgstatic int import_fd;
38e6188e58Smrg
399bd392adSmrg#if defined(__GLIBC__) || defined(__FreeBSD__)
40e6188e58Smrgint ioctl(int fd, unsigned long request, ...)
419bd392adSmrg#else
429bd392adSmrgint ioctl(int fd, int request, ...)
439bd392adSmrg#endif
44e6188e58Smrg{
45e6188e58Smrg	va_list va;
46e6188e58Smrg	int ret;
47e6188e58Smrg	void *arg;
48e6188e58Smrg
49e6188e58Smrg	va_start(va, request);
50e6188e58Smrg	arg = va_arg(va, void *);
51e6188e58Smrg	ret = old_ioctl(fd, request, arg);
52e6188e58Smrg	va_end(va);
53e6188e58Smrg
54e6188e58Smrg	if (ret < 0 && request == DRM_IOCTL_GEM_CLOSE && errno == EINVAL)
55e6188e58Smrg		failed = 1;
56e6188e58Smrg
57e6188e58Smrg	return ret;
58e6188e58Smrg}
59e6188e58Smrg
60e6188e58Smrgstatic void *
61e6188e58Smrgopenclose(void *dev)
62e6188e58Smrg{
63e6188e58Smrg	struct nouveau_device *nvdev = dev;
64e6188e58Smrg	struct nouveau_bo *bo = NULL;
65e6188e58Smrg	int i;
66e6188e58Smrg
67e6188e58Smrg	for (i = 0; i < 100000; ++i) {
68e6188e58Smrg		if (!nouveau_bo_prime_handle_ref(nvdev, import_fd, &bo))
69e6188e58Smrg			nouveau_bo_ref(NULL, &bo);
70e6188e58Smrg	}
71e6188e58Smrg	return NULL;
72e6188e58Smrg}
73e6188e58Smrg
74e6188e58Smrgint main(int argc, char *argv[])
75e6188e58Smrg{
76e6188e58Smrg	drmVersionPtr version;
77e6188e58Smrg	const char *device = NULL;
78e6188e58Smrg	int err, fd, fd2;
79e6188e58Smrg	struct nouveau_device *nvdev, *nvdev2;
80e6188e58Smrg	struct nouveau_bo *bo;
81e6188e58Smrg	pthread_t t1, t2;
82e6188e58Smrg
83e6188e58Smrg	old_ioctl = dlsym(RTLD_NEXT, "ioctl");
84e6188e58Smrg
85e6188e58Smrg	if (argc < 2) {
86e6188e58Smrg		fd = drmOpenWithType("nouveau", NULL, DRM_NODE_RENDER);
87e6188e58Smrg		if (fd >= 0)
88e6188e58Smrg			fd2 = drmOpenWithType("nouveau", NULL, DRM_NODE_RENDER);
89e6188e58Smrg	} else {
90e6188e58Smrg		device = argv[1];
91e6188e58Smrg
92e6188e58Smrg		fd = open(device, O_RDWR);
93e6188e58Smrg		if (fd >= 0)
94e6188e58Smrg			fd2 = open(device, O_RDWR);
95e6188e58Smrg		else
96e6188e58Smrg			fd2 = fd = -errno;
97e6188e58Smrg	}
98e6188e58Smrg
99e6188e58Smrg	if (fd < 0) {
100e6188e58Smrg		fprintf(stderr, "Opening nouveau render node failed with %i\n", fd);
101e6188e58Smrg		return device ? -fd : 77;
102e6188e58Smrg	}
103e6188e58Smrg
104e6188e58Smrg	if (fd2 < 0) {
105e6188e58Smrg		fprintf(stderr, "Opening second nouveau render node failed with %i\n", -errno);
106e6188e58Smrg		return errno;
107e6188e58Smrg	}
108e6188e58Smrg
109e6188e58Smrg	version = drmGetVersion(fd);
110e6188e58Smrg	if (version) {
111e6188e58Smrg		printf("Version: %d.%d.%d\n", version->version_major,
112e6188e58Smrg		       version->version_minor, version->version_patchlevel);
113e6188e58Smrg		printf("  Name: %s\n", version->name);
114e6188e58Smrg		printf("  Date: %s\n", version->date);
115e6188e58Smrg		printf("  Description: %s\n", version->desc);
116e6188e58Smrg
117e6188e58Smrg		drmFreeVersion(version);
118e6188e58Smrg	}
119e6188e58Smrg
120e6188e58Smrg	err = nouveau_device_wrap(fd, 0, &nvdev);
121e6188e58Smrg	if (!err)
122e6188e58Smrg		err = nouveau_device_wrap(fd2, 0, &nvdev2);
123e6188e58Smrg	if (err < 0)
124e6188e58Smrg		return 1;
125e6188e58Smrg
126e6188e58Smrg	err = nouveau_bo_new(nvdev2, NOUVEAU_BO_GART, 0, 4096, NULL, &bo);
127e6188e58Smrg	if (!err)
128e6188e58Smrg		err = nouveau_bo_set_prime(bo, &import_fd);
129e6188e58Smrg
130e6188e58Smrg	if (!err) {
131e6188e58Smrg		pthread_create(&t1, NULL, openclose, nvdev);
132e6188e58Smrg		pthread_create(&t2, NULL, openclose, nvdev);
133e6188e58Smrg	}
134e6188e58Smrg
135e6188e58Smrg	pthread_join(t1, NULL);
136e6188e58Smrg	pthread_join(t2, NULL);
137e6188e58Smrg
138e6188e58Smrg	close(import_fd);
139e6188e58Smrg	nouveau_bo_ref(NULL, &bo);
140e6188e58Smrg
141e6188e58Smrg	nouveau_device_del(&nvdev2);
142e6188e58Smrg	nouveau_device_del(&nvdev);
143e6188e58Smrg	if (device) {
144e6188e58Smrg		close(fd2);
145e6188e58Smrg		close(fd);
146e6188e58Smrg	} else {
147e6188e58Smrg		drmClose(fd2);
148e6188e58Smrg		drmClose(fd);
149e6188e58Smrg	}
150e6188e58Smrg
151e6188e58Smrg	if (failed)
152e6188e58Smrg		fprintf(stderr, "DRM_IOCTL_GEM_CLOSE failed with EINVAL,\n"
153e6188e58Smrg				"race in opening/closing bo is likely.\n");
154e6188e58Smrg
155e6188e58Smrg	return failed;
156e6188e58Smrg}
157