radeon_dri3.c revision 0d2a5547
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		int ret = glamor_fd_from_pixmap(screen, pixmap, stride, size);
224
225		/* Any pending drawing operations need to be flushed to the
226		 * kernel driver before the client starts using the pixmap
227		 * storage for direct rendering.
228		 */
229		if (ret >= 0)
230			radeon_cs_flush_indirect(scrn);
231
232		return ret;
233	}
234#endif
235
236	bo = radeon_get_pixmap_bo(pixmap);
237	if (!bo) {
238		exaMoveInPixmap(pixmap);
239		bo = radeon_get_pixmap_bo(pixmap);
240		if (!bo)
241			return -1;
242	}
243
244	if (pixmap->devKind > UINT16_MAX)
245		return -1;
246
247	if (radeon_gem_prime_share_bo(bo->bo.radeon, &fd) < 0)
248		return -1;
249
250	*stride = pixmap->devKind;
251	*size = bo->bo.radeon->size;
252	return fd;
253}
254
255static dri3_screen_info_rec radeon_dri3_screen_info = {
256#if DRI3_SCREEN_INFO_VERSION >= 1 && XORG_VERSION_CURRENT <= XORG_VERSION_NUMERIC(1,18,99,1,0)
257	.version = 1,
258	.open_client = radeon_dri3_open_client,
259#else
260	.version = 0,
261	.open = radeon_dri3_open,
262#endif
263	.pixmap_from_fd = radeon_dri3_pixmap_from_fd,
264	.fd_from_pixmap = radeon_dri3_fd_from_pixmap
265};
266
267Bool
268radeon_dri3_screen_init(ScreenPtr screen)
269{
270	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
271	RADEONEntPtr pRADEONEnt = RADEONEntPriv(scrn);
272
273	pRADEONEnt->render_node = drmGetRenderDeviceNameFromFd(pRADEONEnt->fd);
274
275	if (!dri3_screen_init(screen, &radeon_dri3_screen_info)) {
276		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
277			   "dri3_screen_init failed\n");
278		return FALSE;
279	}
280
281	return TRUE;
282}
283
284#else /* !HAVE_DRI3_H */
285
286Bool
287radeon_dri3_screen_init(ScreenPtr screen)
288{
289	xf86DrvMsg(xf86ScreenToScrn(screen)->scrnIndex, X_INFO,
290		   "Can't initialize DRI3 because dri3.h not available at "
291		   "build time\n");
292
293	return FALSE;
294}
295
296#endif
297