1/*
2 * Copyright © 2016 Red Hat.
3 * Copyright © 2016 Bas Nieuwenhuizen
4 * based on amdgpu winsys.
5 * Copyright © 2011 Marek Olšák <maraeo@gmail.com>
6 * Copyright © 2015 Advanced Micro Devices, Inc.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice (including the next
16 * paragraph) shall be included in all copies or substantial portions of the
17 * Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
22 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25 * IN THE SOFTWARE.
26 */
27#include "radv_amdgpu_winsys.h"
28#include <assert.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include "drm-uapi/amdgpu_drm.h"
33#include "ac_surface.h"
34#include "radv_amdgpu_bo.h"
35#include "radv_amdgpu_cs.h"
36#include "radv_amdgpu_surface.h"
37#include "radv_amdgpu_winsys_public.h"
38#include "radv_debug.h"
39#include "xf86drm.h"
40
41static bool
42do_winsys_init(struct radv_amdgpu_winsys *ws, int fd)
43{
44   if (!ac_query_gpu_info(fd, ws->dev, &ws->info, &ws->amdinfo))
45      return false;
46
47   if (ws->info.drm_minor < 23) {
48      fprintf(stderr, "radv: DRM 3.23+ is required (Linux kernel 4.15+)\n");
49      return false;
50   }
51
52   ws->addrlib = ac_addrlib_create(&ws->info, &ws->info.max_alignment);
53   if (!ws->addrlib) {
54      fprintf(stderr, "amdgpu: Cannot create addrlib.\n");
55      return false;
56   }
57
58   ws->info.num_rings[RING_DMA] = MIN2(ws->info.num_rings[RING_DMA], MAX_RINGS_PER_TYPE);
59   ws->info.num_rings[RING_COMPUTE] = MIN2(ws->info.num_rings[RING_COMPUTE], MAX_RINGS_PER_TYPE);
60
61   ws->use_ib_bos = ws->info.chip_class >= GFX7;
62   return true;
63}
64
65static void
66radv_amdgpu_winsys_query_info(struct radeon_winsys *rws, struct radeon_info *info)
67{
68   *info = ((struct radv_amdgpu_winsys *)rws)->info;
69}
70
71static uint64_t
72radv_amdgpu_winsys_query_value(struct radeon_winsys *rws, enum radeon_value_id value)
73{
74   struct radv_amdgpu_winsys *ws = (struct radv_amdgpu_winsys *)rws;
75   struct amdgpu_heap_info heap;
76   uint64_t retval = 0;
77
78   switch (value) {
79   case RADEON_ALLOCATED_VRAM:
80      return ws->allocated_vram;
81   case RADEON_ALLOCATED_VRAM_VIS:
82      return ws->allocated_vram_vis;
83   case RADEON_ALLOCATED_GTT:
84      return ws->allocated_gtt;
85   case RADEON_TIMESTAMP:
86      amdgpu_query_info(ws->dev, AMDGPU_INFO_TIMESTAMP, 8, &retval);
87      return retval;
88   case RADEON_NUM_BYTES_MOVED:
89      amdgpu_query_info(ws->dev, AMDGPU_INFO_NUM_BYTES_MOVED, 8, &retval);
90      return retval;
91   case RADEON_NUM_EVICTIONS:
92      amdgpu_query_info(ws->dev, AMDGPU_INFO_NUM_EVICTIONS, 8, &retval);
93      return retval;
94   case RADEON_NUM_VRAM_CPU_PAGE_FAULTS:
95      amdgpu_query_info(ws->dev, AMDGPU_INFO_NUM_VRAM_CPU_PAGE_FAULTS, 8, &retval);
96      return retval;
97   case RADEON_VRAM_USAGE:
98      amdgpu_query_heap_info(ws->dev, AMDGPU_GEM_DOMAIN_VRAM, 0, &heap);
99      return heap.heap_usage;
100   case RADEON_VRAM_VIS_USAGE:
101      amdgpu_query_heap_info(ws->dev, AMDGPU_GEM_DOMAIN_VRAM, AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED,
102                             &heap);
103      return heap.heap_usage;
104   case RADEON_GTT_USAGE:
105      amdgpu_query_heap_info(ws->dev, AMDGPU_GEM_DOMAIN_GTT, 0, &heap);
106      return heap.heap_usage;
107   case RADEON_GPU_TEMPERATURE:
108      amdgpu_query_sensor_info(ws->dev, AMDGPU_INFO_SENSOR_GPU_TEMP, 4, &retval);
109      return retval;
110   case RADEON_CURRENT_SCLK:
111      amdgpu_query_sensor_info(ws->dev, AMDGPU_INFO_SENSOR_GFX_SCLK, 4, &retval);
112      return retval;
113   case RADEON_CURRENT_MCLK:
114      amdgpu_query_sensor_info(ws->dev, AMDGPU_INFO_SENSOR_GFX_MCLK, 4, &retval);
115      return retval;
116   default:
117      unreachable("invalid query value");
118   }
119
120   return 0;
121}
122
123static bool
124radv_amdgpu_winsys_read_registers(struct radeon_winsys *rws, unsigned reg_offset,
125                                  unsigned num_registers, uint32_t *out)
126{
127   struct radv_amdgpu_winsys *ws = (struct radv_amdgpu_winsys *)rws;
128
129   return amdgpu_read_mm_registers(ws->dev, reg_offset / 4, num_registers, 0xffffffff, 0, out) == 0;
130}
131
132static const char *
133radv_amdgpu_winsys_get_chip_name(struct radeon_winsys *rws)
134{
135   amdgpu_device_handle dev = ((struct radv_amdgpu_winsys *)rws)->dev;
136
137   return amdgpu_get_marketing_name(dev);
138}
139
140static simple_mtx_t winsys_creation_mutex = _SIMPLE_MTX_INITIALIZER_NP;
141static struct hash_table *winsyses = NULL;
142
143static void
144radv_amdgpu_winsys_destroy(struct radeon_winsys *rws)
145{
146   struct radv_amdgpu_winsys *ws = (struct radv_amdgpu_winsys *)rws;
147   bool destroy = false;
148
149   simple_mtx_lock(&winsys_creation_mutex);
150   if (!--ws->refcount) {
151      _mesa_hash_table_remove_key(winsyses, ws->dev);
152
153      /* Clean the hashtable up if empty, though there is no
154       * empty function. */
155      if (_mesa_hash_table_num_entries(winsyses) == 0) {
156         _mesa_hash_table_destroy(winsyses, NULL);
157         winsyses = NULL;
158      }
159
160      destroy = true;
161   }
162   simple_mtx_unlock(&winsys_creation_mutex);
163   if (!destroy)
164      return;
165
166   for (unsigned i = 0; i < ws->syncobj_count; ++i)
167      amdgpu_cs_destroy_syncobj(ws->dev, ws->syncobj[i]);
168   free(ws->syncobj);
169
170   u_rwlock_destroy(&ws->global_bo_list.lock);
171   free(ws->global_bo_list.bos);
172
173   if (ws->reserve_vmid)
174      amdgpu_vm_unreserve_vmid(ws->dev, 0);
175
176   pthread_mutex_destroy(&ws->syncobj_lock);
177   u_rwlock_destroy(&ws->log_bo_list_lock);
178   ac_addrlib_destroy(ws->addrlib);
179   amdgpu_device_deinitialize(ws->dev);
180   FREE(rws);
181}
182
183struct radeon_winsys *
184radv_amdgpu_winsys_create(int fd, uint64_t debug_flags, uint64_t perftest_flags, bool reserve_vmid)
185{
186   uint32_t drm_major, drm_minor, r;
187   amdgpu_device_handle dev;
188   struct radv_amdgpu_winsys *ws = NULL;
189
190   r = amdgpu_device_initialize(fd, &drm_major, &drm_minor, &dev);
191   if (r)
192      return NULL;
193
194   /* We have to keep this lock till insertion. */
195   simple_mtx_lock(&winsys_creation_mutex);
196   if (!winsyses)
197      winsyses = _mesa_pointer_hash_table_create(NULL);
198   if (!winsyses)
199      goto fail;
200
201   struct hash_entry *entry = _mesa_hash_table_search(winsyses, dev);
202   if (entry) {
203      ws = (struct radv_amdgpu_winsys *)entry->data;
204      ++ws->refcount;
205   }
206
207   if (ws) {
208      simple_mtx_unlock(&winsys_creation_mutex);
209      amdgpu_device_deinitialize(dev);
210
211      /* Check that options don't differ from the existing winsys. */
212      if (((debug_flags & RADV_DEBUG_ALL_BOS) && !ws->debug_all_bos) ||
213          ((debug_flags & RADV_DEBUG_HANG) && !ws->debug_log_bos) ||
214          ((debug_flags & RADV_DEBUG_NO_IBS) && ws->use_ib_bos) ||
215          (perftest_flags != ws->perftest)) {
216         fprintf(stderr, "amdgpu: Found options that differ from the existing winsys.\n");
217         return NULL;
218      }
219
220      /* RADV_DEBUG_ZERO_VRAM is the only option that is allowed to be set again. */
221      if (debug_flags & RADV_DEBUG_ZERO_VRAM)
222         ws->zero_all_vram_allocs = true;
223
224      return &ws->base;
225   }
226
227   ws = calloc(1, sizeof(struct radv_amdgpu_winsys));
228   if (!ws)
229      goto fail;
230
231   ws->refcount = 1;
232   ws->dev = dev;
233   ws->info.drm_major = drm_major;
234   ws->info.drm_minor = drm_minor;
235   if (!do_winsys_init(ws, fd))
236      goto winsys_fail;
237
238   ws->debug_all_bos = !!(debug_flags & RADV_DEBUG_ALL_BOS);
239   ws->debug_log_bos = debug_flags & RADV_DEBUG_HANG;
240   if (debug_flags & RADV_DEBUG_NO_IBS)
241      ws->use_ib_bos = false;
242
243   ws->reserve_vmid = reserve_vmid;
244   if (ws->reserve_vmid) {
245      r = amdgpu_vm_reserve_vmid(dev, 0);
246      if (r)
247         goto vmid_fail;
248   }
249
250   ws->perftest = perftest_flags;
251   ws->zero_all_vram_allocs = debug_flags & RADV_DEBUG_ZERO_VRAM;
252   u_rwlock_init(&ws->global_bo_list.lock);
253   list_inithead(&ws->log_bo_list);
254   u_rwlock_init(&ws->log_bo_list_lock);
255   pthread_mutex_init(&ws->syncobj_lock, NULL);
256   ws->base.query_info = radv_amdgpu_winsys_query_info;
257   ws->base.query_value = radv_amdgpu_winsys_query_value;
258   ws->base.read_registers = radv_amdgpu_winsys_read_registers;
259   ws->base.get_chip_name = radv_amdgpu_winsys_get_chip_name;
260   ws->base.destroy = radv_amdgpu_winsys_destroy;
261   radv_amdgpu_bo_init_functions(ws);
262   radv_amdgpu_cs_init_functions(ws);
263   radv_amdgpu_surface_init_functions(ws);
264
265   _mesa_hash_table_insert(winsyses, dev, ws);
266   simple_mtx_unlock(&winsys_creation_mutex);
267
268   return &ws->base;
269
270vmid_fail:
271   ac_addrlib_destroy(ws->addrlib);
272winsys_fail:
273   free(ws);
274fail:
275   if (winsyses && _mesa_hash_table_num_entries(winsyses) == 0) {
276      _mesa_hash_table_destroy(winsyses, NULL);
277      winsyses = NULL;
278   }
279   simple_mtx_unlock(&winsys_creation_mutex);
280   amdgpu_device_deinitialize(dev);
281   return NULL;
282}
283