radeon_dri3.c revision 446f62d6
1/*
2 * Copyright © 2013-2014 Intel Corporation
3 * Copyright © 2015 Advanced Micro Devices, Inc.
4 *
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that copyright
8 * notice and this permission notice appear in supporting documentation, and
9 * that the name of the copyright holders not be used in advertising or
10 * publicity pertaining to distribution of the software without specific,
11 * written prior permission.  The copyright holders make no representations
12 * about the suitability of this software for any purpose.  It is provided "as
13 * is" without express or implied warranty.
14 *
15 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
21 * OF THIS SOFTWARE.
22 */
23
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif
28
29#include "radeon.h"
30
31#ifdef HAVE_DRI3_H
32
33#include "radeon_bo_gem.h"
34#include "radeon_glamor.h"
35#include "dri3.h"
36
37#include <sys/types.h>
38#include <sys/stat.h>
39#include <fcntl.h>
40#include <gbm.h>
41#include <errno.h>
42#include <libgen.h>
43
44static int open_master_node(ScreenPtr screen, int *out)
45{
46	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
47	RADEONEntPtr pRADEONEnt = RADEONEntPriv(scrn);
48	RADEONInfoPtr info = RADEONPTR(scrn);
49	drm_magic_t magic;
50	int fd;
51
52	fd = open(info->dri2.device_name, O_RDWR | O_CLOEXEC);
53	if (fd < 0)
54		return BadAlloc;
55
56	/* Before FD passing in the X protocol with DRI3 (and increased
57	 * security of rendering with per-process address spaces on the
58	 * GPU), the kernel had to come up with a way to have the server
59	 * decide which clients got to access the GPU, which was done by
60	 * each client getting a unique (magic) number from the kernel,
61	 * passing it to the server, and the server then telling the
62	 * kernel which clients were authenticated for using the device.
63	 *
64	 * Now that we have FD passing, the server can just set up the
65	 * authentication on its own and hand the prepared FD off to the
66	 * client.
67	 */
68	if (drmGetMagic(fd, &magic) < 0) {
69		if (errno == EACCES) {
70			/* Assume that we're on a render node, and the fd is
71			 * already as authenticated as it should be.
72			 */
73			*out = fd;
74			return Success;
75		} else {
76			close(fd);
77			return BadMatch;
78		}
79	}
80
81	if (drmAuthMagic(pRADEONEnt->fd, magic) < 0) {
82		close(fd);
83		return BadMatch;
84	}
85
86	*out = fd;
87	return Success;
88}
89
90static int open_render_node(ScreenPtr screen, int *out)
91{
92	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
93	RADEONEntPtr pRADEONEnt = RADEONEntPriv(scrn);
94	int fd;
95
96	fd = open(pRADEONEnt->render_node, O_RDWR | O_CLOEXEC);
97	if (fd < 0)
98		return BadAlloc;
99
100	*out = fd;
101	return Success;
102}
103
104static int
105radeon_dri3_open(ScreenPtr screen, RRProviderPtr provider, int *out)
106{
107	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
108	RADEONEntPtr pRADEONEnt = RADEONEntPriv(scrn);
109	int ret = BadAlloc;
110
111	if (pRADEONEnt->render_node)
112		ret = open_render_node(screen, out);
113
114	if (ret != Success)
115		ret = open_master_node(screen, out);
116
117	return ret;
118}
119
120#if DRI3_SCREEN_INFO_VERSION >= 1 && XORG_VERSION_CURRENT <= XORG_VERSION_NUMERIC(1,18,99,1,0)
121
122static int
123radeon_dri3_open_client(ClientPtr client, ScreenPtr screen,
124			RRProviderPtr provider, int *out)
125{
126	const char *cmdname = GetClientCmdName(client);
127	Bool is_ssh = FALSE;
128
129	/* If the executable name is "ssh", assume that this client connection
130	 * is forwarded from another host via SSH
131	 */
132	if (cmdname) {
133		char *cmd = strdup(cmdname);
134
135		/* Cut off any colon and whatever comes after it, see
136		 * https://lists.freedesktop.org/archives/xorg-devel/2015-December/048164.html
137		 */
138		cmd = strtok(cmd, ":");
139
140		is_ssh = strcmp(basename(cmd), "ssh") == 0;
141		free(cmd);
142	}
143
144	if (!is_ssh)
145		return radeon_dri3_open(screen, provider, out);
146
147	return BadAccess;
148}
149
150#endif /* DRI3_SCREEN_INFO_VERSION >= 1 && XORG_VERSION_CURRENT <= XORG_VERSION_NUMERIC(1,18,99,1,0) */
151
152static PixmapPtr radeon_dri3_pixmap_from_fd(ScreenPtr screen,
153					    int fd,
154					    CARD16 width,
155					    CARD16 height,
156					    CARD16 stride,
157					    CARD8 depth,
158					    CARD8 bpp)
159{
160	PixmapPtr pixmap;
161
162#ifdef USE_GLAMOR
163	/* Avoid generating a GEM flink name if possible */
164	if (RADEONPTR(xf86ScreenToScrn(screen))->use_glamor) {
165		pixmap = glamor_pixmap_from_fd(screen, fd, width, height,
166					       stride, depth, bpp);
167		if (pixmap) {
168			struct radeon_pixmap *priv =
169				calloc(1, sizeof(struct radeon_pixmap));
170
171			if (priv) {
172				radeon_set_pixmap_private(pixmap, priv);
173				pixmap->usage_hint |= RADEON_CREATE_PIXMAP_DRI2;
174				return pixmap;
175			}
176
177			screen->DestroyPixmap(pixmap);
178			return NULL;
179		}
180	}
181#endif
182
183	if (depth < 8)
184		return NULL;
185
186	switch (bpp) {
187	case 8:
188	case 16:
189	case 32:
190		break;
191	default:
192		return NULL;
193	}
194
195	pixmap = screen->CreatePixmap(screen, 0, 0, depth, RADEON_CREATE_PIXMAP_DRI2);
196	if (!pixmap)
197		return NULL;
198
199	if (!screen->ModifyPixmapHeader(pixmap, width, height, 0, bpp, stride,
200					NULL))
201		goto free_pixmap;
202
203	if (screen->SetSharedPixmapBacking(pixmap, (void*)(intptr_t)fd))
204		return pixmap;
205
206free_pixmap:
207	fbDestroyPixmap(pixmap);
208	return NULL;
209}
210
211static int radeon_dri3_fd_from_pixmap(ScreenPtr screen,
212				      PixmapPtr pixmap,
213				      CARD16 *stride,
214				      CARD32 *size)
215{
216	struct radeon_buffer *bo;
217	int fd;
218#ifdef USE_GLAMOR
219	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
220	RADEONInfoPtr info = RADEONPTR(scrn);
221
222	if (info->use_glamor) {
223		Bool need_flush = TRUE;
224		int ret = -1;
225#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,19,99,904,0)
226		struct gbm_bo *gbm_bo = glamor_gbm_bo_from_pixmap(screen, pixmap);
227
228		if (gbm_bo) {
229			ret = gbm_bo_get_fd(gbm_bo);
230			gbm_bo_destroy(gbm_bo);
231
232			if (ret >= 0)
233				need_flush = FALSE;
234		}
235#endif
236
237		if (ret < 0)
238			ret = glamor_fd_from_pixmap(screen, pixmap, stride, size);
239
240		/* glamor might have needed to reallocate the pixmap storage and
241		 * copy the pixmap contents to the new storage. The copy
242		 * operation needs to be flushed to the kernel driver before the
243		 * client starts using the pixmap storage for direct rendering.
244		 */
245		if (ret >= 0 && need_flush)
246			radeon_cs_flush_indirect(scrn);
247
248		return ret;
249	}
250#endif
251
252	bo = radeon_get_pixmap_bo(pixmap);
253	if (!bo) {
254		exaMoveInPixmap(pixmap);
255		bo = radeon_get_pixmap_bo(pixmap);
256		if (!bo)
257			return -1;
258	}
259
260	if (pixmap->devKind > UINT16_MAX)
261		return -1;
262
263	if (radeon_gem_prime_share_bo(bo->bo.radeon, &fd) < 0)
264		return -1;
265
266	*stride = pixmap->devKind;
267	*size = bo->bo.radeon->size;
268	return fd;
269}
270
271static dri3_screen_info_rec radeon_dri3_screen_info = {
272#if DRI3_SCREEN_INFO_VERSION >= 1 && XORG_VERSION_CURRENT <= XORG_VERSION_NUMERIC(1,18,99,1,0)
273	.version = 1,
274	.open_client = radeon_dri3_open_client,
275#else
276	.version = 0,
277	.open = radeon_dri3_open,
278#endif
279	.pixmap_from_fd = radeon_dri3_pixmap_from_fd,
280	.fd_from_pixmap = radeon_dri3_fd_from_pixmap
281};
282
283Bool
284radeon_dri3_screen_init(ScreenPtr screen)
285{
286	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
287	RADEONEntPtr pRADEONEnt = RADEONEntPriv(scrn);
288
289	pRADEONEnt->render_node = drmGetRenderDeviceNameFromFd(pRADEONEnt->fd);
290
291	if (!dri3_screen_init(screen, &radeon_dri3_screen_info)) {
292		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
293			   "dri3_screen_init failed\n");
294		return FALSE;
295	}
296
297	return TRUE;
298}
299
300#else /* !HAVE_DRI3_H */
301
302Bool
303radeon_dri3_screen_init(ScreenPtr screen)
304{
305	xf86DrvMsg(xf86ScreenToScrn(screen)->scrnIndex, X_INFO,
306		   "Can't initialize DRI3 because dri3.h not available at "
307		   "build time\n");
308
309	return FALSE;
310}
311
312#endif
313