1 /* 2 * Copyright 2014 Advanced Micro Devices, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 */ 23 24 /** 25 * \file amdgpu_device.c 26 * 27 * Implementation of functions for AMD GPU device 28 * 29 */ 30 31 #ifdef HAVE_CONFIG_H 32 #include "config.h" 33 #endif 34 35 #include <sys/stat.h> 36 #include <errno.h> 37 #include <string.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <unistd.h> 41 42 #include "xf86drm.h" 43 #include "amdgpu_drm.h" 44 #include "amdgpu_internal.h" 45 #include "util_hash_table.h" 46 #include "util_math.h" 47 48 #define PTR_TO_UINT(x) ((unsigned)((intptr_t)(x))) 49 #define UINT_TO_PTR(x) ((void *)((intptr_t)(x))) 50 51 static pthread_mutex_t fd_mutex = PTHREAD_MUTEX_INITIALIZER; 52 static struct util_hash_table *fd_tab; 53 54 static unsigned handle_hash(void *key) 55 { 56 return PTR_TO_UINT(key); 57 } 58 59 static int handle_compare(void *key1, void *key2) 60 { 61 return PTR_TO_UINT(key1) != PTR_TO_UINT(key2); 62 } 63 64 static unsigned fd_hash(void *key) 65 { 66 int fd = PTR_TO_UINT(key); 67 char *name = drmGetPrimaryDeviceNameFromFd(fd); 68 unsigned result = 0; 69 char *c; 70 71 if (name == NULL) 72 return 0; 73 74 for (c = name; *c; ++c) 75 result += *c; 76 77 free(name); 78 79 return result; 80 } 81 82 static int fd_compare(void *key1, void *key2) 83 { 84 int fd1 = PTR_TO_UINT(key1); 85 int fd2 = PTR_TO_UINT(key2); 86 char *name1 = drmGetPrimaryDeviceNameFromFd(fd1); 87 char *name2 = drmGetPrimaryDeviceNameFromFd(fd2); 88 int result; 89 90 if (name1 == NULL || name2 == NULL) { 91 free(name1); 92 free(name2); 93 return 0; 94 } 95 96 result = strcmp(name1, name2); 97 free(name1); 98 free(name2); 99 100 return result; 101 } 102 103 /** 104 * Get the authenticated form fd, 105 * 106 * \param fd - \c [in] File descriptor for AMD GPU device 107 * \param auth - \c [out] Pointer to output the fd is authenticated or not 108 * A render node fd, output auth = 0 109 * A legacy fd, get the authenticated for compatibility root 110 * 111 * \return 0 on success\n 112 * >0 - AMD specific error code\n 113 * <0 - Negative POSIX Error code 114 */ 115 static int amdgpu_get_auth(int fd, int *auth) 116 { 117 int r = 0; 118 drm_client_t client = {}; 119 120 if (drmGetNodeTypeFromFd(fd) == DRM_NODE_RENDER) 121 *auth = 0; 122 else { 123 client.idx = 0; 124 r = drmIoctl(fd, DRM_IOCTL_GET_CLIENT, &client); 125 if (!r) 126 *auth = client.auth; 127 } 128 return r; 129 } 130 131 static void amdgpu_device_free_internal(amdgpu_device_handle dev) 132 { 133 amdgpu_vamgr_deinit(dev->vamgr); 134 free(dev->vamgr); 135 amdgpu_vamgr_deinit(dev->vamgr_32); 136 free(dev->vamgr_32); 137 util_hash_table_destroy(dev->bo_flink_names); 138 util_hash_table_destroy(dev->bo_handles); 139 pthread_mutex_destroy(&dev->bo_table_mutex); 140 util_hash_table_remove(fd_tab, UINT_TO_PTR(dev->fd)); 141 close(dev->fd); 142 if ((dev->flink_fd >= 0) && (dev->fd != dev->flink_fd)) 143 close(dev->flink_fd); 144 free(dev); 145 } 146 147 /** 148 * Assignment between two amdgpu_device pointers with reference counting. 149 * 150 * Usage: 151 * struct amdgpu_device *dst = ... , *src = ...; 152 * 153 * dst = src; 154 * // No reference counting. Only use this when you need to move 155 * // a reference from one pointer to another. 156 * 157 * amdgpu_device_reference(&dst, src); 158 * // Reference counters are updated. dst is decremented and src is 159 * // incremented. dst is freed if its reference counter is 0. 160 */ 161 static void amdgpu_device_reference(struct amdgpu_device **dst, 162 struct amdgpu_device *src) 163 { 164 if (update_references(&(*dst)->refcount, &src->refcount)) 165 amdgpu_device_free_internal(*dst); 166 *dst = src; 167 } 168 169 int amdgpu_device_initialize(int fd, 170 uint32_t *major_version, 171 uint32_t *minor_version, 172 amdgpu_device_handle *device_handle) 173 { 174 struct amdgpu_device *dev; 175 drmVersionPtr version; 176 int r; 177 int flag_auth = 0; 178 int flag_authexist=0; 179 uint32_t accel_working = 0; 180 uint64_t start, max; 181 182 *device_handle = NULL; 183 184 pthread_mutex_lock(&fd_mutex); 185 if (!fd_tab) 186 fd_tab = util_hash_table_create(fd_hash, fd_compare); 187 r = amdgpu_get_auth(fd, &flag_auth); 188 if (r) { 189 pthread_mutex_unlock(&fd_mutex); 190 return r; 191 } 192 dev = util_hash_table_get(fd_tab, UINT_TO_PTR(fd)); 193 if (dev) { 194 r = amdgpu_get_auth(dev->fd, &flag_authexist); 195 if (r) { 196 pthread_mutex_unlock(&fd_mutex); 197 return r; 198 } 199 if ((flag_auth) && (!flag_authexist)) { 200 dev->flink_fd = dup(fd); 201 } 202 *major_version = dev->major_version; 203 *minor_version = dev->minor_version; 204 amdgpu_device_reference(device_handle, dev); 205 pthread_mutex_unlock(&fd_mutex); 206 return 0; 207 } 208 209 dev = calloc(1, sizeof(struct amdgpu_device)); 210 if (!dev) { 211 pthread_mutex_unlock(&fd_mutex); 212 return -ENOMEM; 213 } 214 215 dev->fd = -1; 216 dev->flink_fd = -1; 217 218 atomic_set(&dev->refcount, 1); 219 220 version = drmGetVersion(fd); 221 if (version->version_major != 3) { 222 fprintf(stderr, "%s: DRM version is %d.%d.%d but this driver is " 223 "only compatible with 3.x.x.\n", 224 __func__, 225 version->version_major, 226 version->version_minor, 227 version->version_patchlevel); 228 drmFreeVersion(version); 229 r = -EBADF; 230 goto cleanup; 231 } 232 233 dev->fd = dup(fd); 234 dev->flink_fd = dev->fd; 235 dev->major_version = version->version_major; 236 dev->minor_version = version->version_minor; 237 drmFreeVersion(version); 238 239 dev->bo_flink_names = util_hash_table_create(handle_hash, 240 handle_compare); 241 dev->bo_handles = util_hash_table_create(handle_hash, handle_compare); 242 pthread_mutex_init(&dev->bo_table_mutex, NULL); 243 244 /* Check if acceleration is working. */ 245 r = amdgpu_query_info(dev, AMDGPU_INFO_ACCEL_WORKING, 4, &accel_working); 246 if (r) 247 goto cleanup; 248 if (!accel_working) { 249 r = -EBADF; 250 goto cleanup; 251 } 252 253 r = amdgpu_query_gpu_info_init(dev); 254 if (r) 255 goto cleanup; 256 257 dev->vamgr = calloc(1, sizeof(struct amdgpu_bo_va_mgr)); 258 if (dev->vamgr == NULL) 259 goto cleanup; 260 261 amdgpu_vamgr_init(dev->vamgr, dev->dev_info.virtual_address_offset, 262 dev->dev_info.virtual_address_max, 263 dev->dev_info.virtual_address_alignment); 264 265 max = MIN2(dev->dev_info.virtual_address_max, 0xffffffff); 266 start = amdgpu_vamgr_find_va(dev->vamgr, 267 max - dev->dev_info.virtual_address_offset, 268 dev->dev_info.virtual_address_alignment, 0); 269 if (start > 0xffffffff) 270 goto free_va; /* shouldn't get here */ 271 272 dev->vamgr_32 = calloc(1, sizeof(struct amdgpu_bo_va_mgr)); 273 if (dev->vamgr_32 == NULL) 274 goto free_va; 275 amdgpu_vamgr_init(dev->vamgr_32, start, max, 276 dev->dev_info.virtual_address_alignment); 277 278 *major_version = dev->major_version; 279 *minor_version = dev->minor_version; 280 *device_handle = dev; 281 util_hash_table_set(fd_tab, UINT_TO_PTR(dev->fd), dev); 282 pthread_mutex_unlock(&fd_mutex); 283 284 return 0; 285 286 free_va: 287 r = -ENOMEM; 288 amdgpu_vamgr_free_va(dev->vamgr, start, 289 max - dev->dev_info.virtual_address_offset); 290 amdgpu_vamgr_deinit(dev->vamgr); 291 free(dev->vamgr); 292 293 cleanup: 294 if (dev->fd >= 0) 295 close(dev->fd); 296 free(dev); 297 pthread_mutex_unlock(&fd_mutex); 298 return r; 299 } 300 301 int amdgpu_device_deinitialize(amdgpu_device_handle dev) 302 { 303 amdgpu_device_reference(&dev, NULL); 304 return 0; 305 } 306