amdgpu_dri3.c revision 77d6d1ec
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 "amdgpu_drv.h" 30 31#ifdef HAVE_DRI3_H 32 33#include "amdgpu_glamor.h" 34#include "amdgpu_pixmap.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 AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn); 48 AMDGPUInfoPtr info = AMDGPUPTR(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(pAMDGPUEnt->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 AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn); 94 int fd; 95 96 fd = open(pAMDGPUEnt->render_node, O_RDWR | O_CLOEXEC); 97 if (fd < 0) 98 return BadAlloc; 99 100 *out = fd; 101 return Success; 102} 103 104static int 105amdgpu_dri3_open(ScreenPtr screen, RRProviderPtr provider, int *out) 106{ 107 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 108 AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn); 109 int ret = BadAlloc; 110 111 if (pAMDGPUEnt->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 123amdgpu_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 amdgpu_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 amdgpu_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 (AMDGPUPTR(xf86ScreenToScrn(screen))->use_glamor) { 165 pixmap = glamor_pixmap_from_fd(screen, fd, width, height, 166 stride, depth, bpp); 167 if (pixmap) { 168 struct amdgpu_pixmap *priv = calloc(1, sizeof(*priv)); 169 170 if (priv) { 171 amdgpu_set_pixmap_private(pixmap, priv); 172 pixmap->usage_hint |= AMDGPU_CREATE_PIXMAP_DRI2; 173 return pixmap; 174 } 175 176 screen->DestroyPixmap(pixmap); 177 return NULL; 178 } 179 } 180#endif 181 182 if (depth < 8) 183 return NULL; 184 185 switch (bpp) { 186 case 8: 187 case 16: 188 case 32: 189 break; 190 default: 191 return NULL; 192 } 193 194 pixmap = screen->CreatePixmap(screen, 0, 0, depth, 195 AMDGPU_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 amdgpu_dri3_fd_from_pixmap(ScreenPtr screen, 212 PixmapPtr pixmap, 213 CARD16 *stride, 214 CARD32 *size) 215{ 216 struct amdgpu_buffer *bo; 217 struct amdgpu_bo_info bo_info; 218 uint32_t fd; 219#ifdef USE_GLAMOR 220 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 221 AMDGPUInfoPtr info = AMDGPUPTR(scrn); 222 223 if (info->use_glamor) { 224 int ret = glamor_fd_from_pixmap(screen, pixmap, stride, size); 225 226 /* Any pending drawing operations need to be flushed to the 227 * kernel driver before the client starts using the pixmap 228 * storage for direct rendering. 229 */ 230 if (ret >= 0) 231 amdgpu_glamor_flush(scrn); 232 233 return ret; 234 } 235#endif 236 237 bo = amdgpu_get_pixmap_bo(pixmap); 238 if (!bo) 239 return -1; 240 241 if (pixmap->devKind > UINT16_MAX) 242 return -1; 243 244 if (amdgpu_bo_query_info(bo->bo.amdgpu, &bo_info) != 0) 245 return -1; 246 247 if (amdgpu_bo_export(bo->bo.amdgpu, amdgpu_bo_handle_type_dma_buf_fd, 248 &fd) != 0) 249 return -1; 250 251 *stride = pixmap->devKind; 252 *size = bo_info.alloc_size; 253 return fd; 254} 255 256static dri3_screen_info_rec amdgpu_dri3_screen_info = { 257#if DRI3_SCREEN_INFO_VERSION >= 1 && XORG_VERSION_CURRENT <= XORG_VERSION_NUMERIC(1,18,99,1,0) 258 .version = 1, 259 .open_client = amdgpu_dri3_open_client, 260#else 261 .version = 0, 262 .open = amdgpu_dri3_open, 263#endif 264 .pixmap_from_fd = amdgpu_dri3_pixmap_from_fd, 265 .fd_from_pixmap = amdgpu_dri3_fd_from_pixmap 266}; 267 268Bool 269amdgpu_dri3_screen_init(ScreenPtr screen) 270{ 271 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 272 AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn); 273 274 pAMDGPUEnt->render_node = drmGetRenderDeviceNameFromFd(pAMDGPUEnt->fd); 275 276 if (!dri3_screen_init(screen, &amdgpu_dri3_screen_info)) { 277 xf86DrvMsg(scrn->scrnIndex, X_WARNING, 278 "dri3_screen_init failed\n"); 279 return FALSE; 280 } 281 282 return TRUE; 283} 284 285#else /* !HAVE_DRI3_H */ 286 287Bool 288amdgpu_dri3_screen_init(ScreenPtr screen) 289{ 290 xf86DrvMsg(xf86ScreenToScrn(screen)->scrnIndex, X_INFO, 291 "Can't initialize DRI3 because dri3.h not available at " 292 "build time\n"); 293 294 return FALSE; 295} 296 297#endif 298