• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2023 Collabora, Ltd.
3  *
4  * SPDX-License-Identifier: MIT
5  */
6 
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <string.h>
10 #include <xf86drm.h>
11 
12 #include "util/hash_table.h"
13 #include "util/macros.h"
14 #include "util/simple_mtx.h"
15 
16 #include "drm-uapi/panfrost_drm.h"
17 
18 #include "pan_kmod_backend.h"
19 
20 #include "pan_props.h"
21 
22 const struct pan_kmod_ops panfrost_kmod_ops;
23 
24 struct panfrost_kmod_vm {
25    struct pan_kmod_vm base;
26 };
27 
28 struct panfrost_kmod_dev {
29    struct pan_kmod_dev base;
30    struct panfrost_kmod_vm *vm;
31 };
32 
33 struct panfrost_kmod_bo {
34    struct pan_kmod_bo base;
35 
36    /* This is actually the VA assigned to the BO at creation/import time.
37     * We don't control it, it's automatically assigned by the kernel driver.
38     */
39    uint64_t offset;
40 };
41 
42 static struct pan_kmod_dev *
panfrost_kmod_dev_create(int fd,uint32_t flags,drmVersionPtr version,const struct pan_kmod_allocator * allocator)43 panfrost_kmod_dev_create(int fd, uint32_t flags, drmVersionPtr version,
44                          const struct pan_kmod_allocator *allocator)
45 {
46    if (version->version_major < 1 ||
47        (version->version_major == 1 && version->version_minor < 1)) {
48       mesa_loge("kernel driver is too old (requires at least 1.1, found %d.%d)",
49                 version->version_major, version->version_minor);
50       return NULL;
51    }
52 
53    struct panfrost_kmod_dev *panfrost_dev =
54       pan_kmod_alloc(allocator, sizeof(*panfrost_dev));
55    if (!panfrost_dev) {
56       mesa_loge("failed to allocate a panfrost_kmod_dev object");
57       return NULL;
58    }
59 
60    pan_kmod_dev_init(&panfrost_dev->base, fd, flags, version,
61                      &panfrost_kmod_ops, allocator);
62    return &panfrost_dev->base;
63 }
64 
65 static void
panfrost_kmod_dev_destroy(struct pan_kmod_dev * dev)66 panfrost_kmod_dev_destroy(struct pan_kmod_dev *dev)
67 {
68    struct panfrost_kmod_dev *panfrost_dev =
69       container_of(dev, struct panfrost_kmod_dev, base);
70 
71    pan_kmod_dev_cleanup(dev);
72    pan_kmod_free(dev->allocator, panfrost_dev);
73 }
74 
75 /* Abstraction over the raw drm_panfrost_get_param ioctl for fetching
76  * information about devices.
77  */
78 static __u64
panfrost_query_raw(int fd,enum drm_panfrost_param param,bool required,unsigned default_value)79 panfrost_query_raw(int fd, enum drm_panfrost_param param, bool required,
80                    unsigned default_value)
81 {
82    struct drm_panfrost_get_param get_param = {};
83    ASSERTED int ret;
84 
85    get_param.param = param;
86    ret = drmIoctl(fd, DRM_IOCTL_PANFROST_GET_PARAM, &get_param);
87 
88    if (ret) {
89       assert(!required);
90       return default_value;
91    }
92 
93    return get_param.value;
94 }
95 
96 static void
panfrost_dev_query_thread_props(const struct pan_kmod_dev * dev,struct pan_kmod_dev_props * props)97 panfrost_dev_query_thread_props(const struct pan_kmod_dev *dev,
98                                 struct pan_kmod_dev_props *props)
99 {
100    int fd = dev->fd;
101 
102    props->max_threads_per_core =
103       panfrost_query_raw(fd, DRM_PANFROST_PARAM_MAX_THREADS, true, 0);
104    if (!props->max_threads_per_core) {
105       switch (pan_arch(props->gpu_prod_id)) {
106       case 4:
107       case 5:
108          props->max_threads_per_core = 256;
109          break;
110 
111       case 6:
112          /* Bifrost, first generation */
113          props->max_threads_per_core = 384;
114          break;
115 
116       case 7:
117          /* Bifrost, second generation (G31 is 512 but it doesn't matter) */
118          props->max_threads_per_core = 768;
119          break;
120 
121       case 9:
122          /* Valhall, first generation. */
123          props->max_threads_per_core = 512;
124          break;
125 
126       default:
127          assert(!"Unsupported arch");
128       }
129    }
130 
131    props->max_threads_per_wg = panfrost_query_raw(
132       fd, DRM_PANFROST_PARAM_THREAD_MAX_WORKGROUP_SZ, true, 0);
133    if (!props->max_threads_per_wg)
134       props->max_threads_per_wg = props->max_threads_per_core;
135 
136    uint32_t thread_features =
137       panfrost_query_raw(fd, DRM_PANFROST_PARAM_THREAD_FEATURES, true, 0);
138    props->max_tasks_per_core = MAX2(thread_features >> 24, 1);
139    props->num_registers_per_core = thread_features & 0xffff;
140    if (!props->num_registers_per_core) {
141       switch (pan_arch(props->gpu_prod_id)) {
142       case 4:
143       case 5:
144          /* Assume we can always schedule max_threads_per_core when using 4
145           * registers per-shader or less.
146           */
147          props->num_registers_per_core = props->max_threads_per_core * 4;
148          break;
149 
150       case 6:
151          /* Assume we can always schedule max_threads_per_core for shader
152           * using the full per-shader register file (64 regs).
153           */
154          props->num_registers_per_core = props->max_threads_per_core * 64;
155          break;
156 
157       case 7:
158       case 9:
159          /* Assume we can always schedule max_threads_per_core for shaders
160           * using half the per-shader register file (32 regs).
161           */
162          props->num_registers_per_core = props->max_threads_per_core * 32;
163          break;
164 
165       default:
166          assert(!"Unsupported arch");
167       }
168    }
169 
170    props->max_tls_instance_per_core =
171       panfrost_query_raw(fd, DRM_PANFROST_PARAM_THREAD_TLS_ALLOC, true, 0);
172    if (!props->max_tls_instance_per_core)
173       props->max_tls_instance_per_core = props->max_threads_per_core;
174 }
175 
176 static void
panfrost_dev_query_props(const struct pan_kmod_dev * dev,struct pan_kmod_dev_props * props)177 panfrost_dev_query_props(const struct pan_kmod_dev *dev,
178                          struct pan_kmod_dev_props *props)
179 {
180    int fd = dev->fd;
181 
182    memset(props, 0, sizeof(*props));
183    props->gpu_prod_id =
184       panfrost_query_raw(fd, DRM_PANFROST_PARAM_GPU_PROD_ID, true, 0);
185    props->gpu_revision =
186       panfrost_query_raw(fd, DRM_PANFROST_PARAM_GPU_REVISION, true, 0);
187    props->shader_present =
188       panfrost_query_raw(fd, DRM_PANFROST_PARAM_SHADER_PRESENT, true, 0);
189    props->tiler_features =
190       panfrost_query_raw(fd, DRM_PANFROST_PARAM_TILER_FEATURES, true, 0);
191    props->mem_features =
192       panfrost_query_raw(fd, DRM_PANFROST_PARAM_MEM_FEATURES, true, 0);
193    props->mmu_features =
194       panfrost_query_raw(fd, DRM_PANFROST_PARAM_MMU_FEATURES, true, 0);
195 
196    for (unsigned i = 0; i < ARRAY_SIZE(props->texture_features); i++) {
197       props->texture_features[i] = panfrost_query_raw(
198          fd, DRM_PANFROST_PARAM_TEXTURE_FEATURES0 + i, true, 0);
199    }
200 
201    props->afbc_features =
202       panfrost_query_raw(fd, DRM_PANFROST_PARAM_AFBC_FEATURES, true, 0);
203 
204    panfrost_dev_query_thread_props(dev, props);
205 
206    if (dev->driver.version.major > 1 || dev->driver.version.minor >= 3) {
207       props->gpu_can_query_timestamp = true;
208       props->timestamp_frequency = panfrost_query_raw(
209          fd, DRM_PANFROST_PARAM_SYSTEM_TIMESTAMP_FREQUENCY, true, 0);
210    }
211 
212    /* Panfrost currently doesn't support priorities, assumes default priority as
213     * medium */
214    props->allowed_group_priorities_mask = PAN_KMOD_GROUP_ALLOW_PRIORITY_MEDIUM;
215 }
216 
217 static uint32_t
to_panfrost_bo_flags(struct pan_kmod_dev * dev,uint32_t flags)218 to_panfrost_bo_flags(struct pan_kmod_dev *dev, uint32_t flags)
219 {
220    uint32_t panfrost_flags = 0;
221 
222    if (dev->driver.version.major > 1 || dev->driver.version.minor >= 1) {
223       /* The alloc-on-fault feature is only used for the tiler HEAP object,
224        * hence the name of the flag on panfrost.
225        */
226       if (flags & PAN_KMOD_BO_FLAG_ALLOC_ON_FAULT)
227          panfrost_flags |= PANFROST_BO_HEAP;
228 
229       if (!(flags & PAN_KMOD_BO_FLAG_EXECUTABLE))
230          panfrost_flags |= PANFROST_BO_NOEXEC;
231    }
232 
233    return panfrost_flags;
234 }
235 
236 static struct pan_kmod_bo *
panfrost_kmod_bo_alloc(struct pan_kmod_dev * dev,struct pan_kmod_vm * exclusive_vm,size_t size,uint32_t flags)237 panfrost_kmod_bo_alloc(struct pan_kmod_dev *dev,
238                        struct pan_kmod_vm *exclusive_vm, size_t size,
239                        uint32_t flags)
240 {
241    /* We can't map GPU uncached. */
242    if (flags & PAN_KMOD_BO_FLAG_GPU_UNCACHED)
243       return NULL;
244 
245    struct panfrost_kmod_bo *bo = pan_kmod_dev_alloc(dev, sizeof(*bo));
246    if (!bo)
247       return NULL;
248 
249    struct drm_panfrost_create_bo req = {
250       .size = size,
251       .flags = to_panfrost_bo_flags(dev, flags),
252    };
253 
254    int ret = drmIoctl(dev->fd, DRM_IOCTL_PANFROST_CREATE_BO, &req);
255    if (ret) {
256       mesa_loge("DRM_IOCTL_PANFROST_CREATE_BO failed (err=%d)", errno);
257       goto err_free_bo;
258    }
259 
260    pan_kmod_bo_init(&bo->base, dev, exclusive_vm, req.size, flags, req.handle);
261    bo->offset = req.offset;
262    return &bo->base;
263 
264 err_free_bo:
265    pan_kmod_dev_free(dev, bo);
266    return NULL;
267 }
268 
269 static void
panfrost_kmod_bo_free(struct pan_kmod_bo * bo)270 panfrost_kmod_bo_free(struct pan_kmod_bo *bo)
271 {
272    drmCloseBufferHandle(bo->dev->fd, bo->handle);
273    pan_kmod_dev_free(bo->dev, bo);
274 }
275 
276 static struct pan_kmod_bo *
panfrost_kmod_bo_import(struct pan_kmod_dev * dev,uint32_t handle,size_t size,uint32_t flags)277 panfrost_kmod_bo_import(struct pan_kmod_dev *dev, uint32_t handle, size_t size,
278                         uint32_t flags)
279 {
280    struct panfrost_kmod_bo *panfrost_bo =
281       pan_kmod_dev_alloc(dev, sizeof(*panfrost_bo));
282    if (!panfrost_bo) {
283       mesa_loge("failed to allocate a panfrost_kmod_bo object");
284       return NULL;
285    }
286 
287    struct drm_panfrost_get_bo_offset get_bo_offset = {.handle = handle, 0};
288    int ret =
289       drmIoctl(dev->fd, DRM_IOCTL_PANFROST_GET_BO_OFFSET, &get_bo_offset);
290    if (ret) {
291       mesa_loge("DRM_IOCTL_PANFROST_GET_BO_OFFSET failed (err=%d)", errno);
292       goto err_free_bo;
293    }
294 
295    panfrost_bo->offset = get_bo_offset.offset;
296 
297    pan_kmod_bo_init(&panfrost_bo->base, dev, NULL, size,
298                     flags | PAN_KMOD_BO_FLAG_IMPORTED, handle);
299    return &panfrost_bo->base;
300 
301 err_free_bo:
302    pan_kmod_dev_free(dev, panfrost_bo);
303    return NULL;
304 }
305 
306 static off_t
panfrost_kmod_bo_get_mmap_offset(struct pan_kmod_bo * bo)307 panfrost_kmod_bo_get_mmap_offset(struct pan_kmod_bo *bo)
308 {
309    struct drm_panfrost_mmap_bo mmap_bo = {.handle = bo->handle};
310    int ret = drmIoctl(bo->dev->fd, DRM_IOCTL_PANFROST_MMAP_BO, &mmap_bo);
311    if (ret) {
312       fprintf(stderr, "DRM_IOCTL_PANFROST_MMAP_BO failed: %m\n");
313       assert(0);
314    }
315 
316    return mmap_bo.offset;
317 }
318 
319 static bool
panfrost_kmod_bo_wait(struct pan_kmod_bo * bo,int64_t timeout_ns,bool for_read_only_access)320 panfrost_kmod_bo_wait(struct pan_kmod_bo *bo, int64_t timeout_ns,
321                       bool for_read_only_access)
322 {
323    struct drm_panfrost_wait_bo req = {
324       .handle = bo->handle,
325       .timeout_ns = timeout_ns,
326    };
327 
328    /* The ioctl returns >= 0 value when the BO we are waiting for is ready
329     * -1 otherwise.
330     */
331    if (drmIoctl(bo->dev->fd, DRM_IOCTL_PANFROST_WAIT_BO, &req) != -1)
332       return true;
333 
334    assert(errno == ETIMEDOUT || errno == EBUSY);
335    return false;
336 }
337 
338 static void
panfrost_kmod_bo_make_evictable(struct pan_kmod_bo * bo)339 panfrost_kmod_bo_make_evictable(struct pan_kmod_bo *bo)
340 {
341    struct drm_panfrost_madvise req = {
342       .handle = bo->handle,
343       .madv = PANFROST_MADV_DONTNEED,
344    };
345 
346    drmIoctl(bo->dev->fd, DRM_IOCTL_PANFROST_MADVISE, &req);
347 }
348 
349 static bool
panfrost_kmod_bo_make_unevictable(struct pan_kmod_bo * bo)350 panfrost_kmod_bo_make_unevictable(struct pan_kmod_bo *bo)
351 {
352    struct drm_panfrost_madvise req = {
353       .handle = bo->handle,
354       .madv = PANFROST_MADV_WILLNEED,
355    };
356 
357    if (drmIoctl(bo->dev->fd, DRM_IOCTL_PANFROST_MADVISE, &req) == 0 &&
358        req.retained == 0)
359       return false;
360 
361    return true;
362 }
363 
364 /* The VA range is restricted by the kernel driver. Lower 32MB are reserved, and
365  * the address space is limited to 32-bit.
366  */
367 #define PANFROST_KMOD_VA_START 0x2000000ull
368 #define PANFROST_KMOD_VA_END   (1ull << 32)
369 
370 static struct pan_kmod_va_range
panfrost_kmod_dev_query_user_va_range(const struct pan_kmod_dev * dev)371 panfrost_kmod_dev_query_user_va_range(const struct pan_kmod_dev *dev)
372 {
373    return (struct pan_kmod_va_range){
374       .start = PANFROST_KMOD_VA_START,
375       .size = PANFROST_KMOD_VA_END - PANFROST_KMOD_VA_START,
376    };
377 }
378 
379 static struct pan_kmod_vm *
panfrost_kmod_vm_create(struct pan_kmod_dev * dev,uint32_t flags,uint64_t va_start,uint64_t va_range)380 panfrost_kmod_vm_create(struct pan_kmod_dev *dev, uint32_t flags,
381                         uint64_t va_start, uint64_t va_range)
382 {
383    struct panfrost_kmod_dev *panfrost_dev =
384       container_of(dev, struct panfrost_kmod_dev, base);
385 
386    /* Only one VM per device. */
387    if (panfrost_dev->vm) {
388       mesa_loge("panfrost_kmod only supports one VM per device");
389       return NULL;
390    }
391 
392    /* Panfrost kernel driver doesn't support userspace VA management. */
393    if (!(flags & PAN_KMOD_VM_FLAG_AUTO_VA)) {
394       mesa_loge("panfrost_kmod only supports PAN_KMOD_VM_FLAG_AUTO_VA");
395       assert(0);
396       return NULL;
397    }
398 
399    struct panfrost_kmod_vm *vm = pan_kmod_dev_alloc(dev, sizeof(*vm));
400    if (!vm) {
401       mesa_loge("failed to allocate a panfrost_kmod_vm object");
402       return NULL;
403    }
404 
405    pan_kmod_vm_init(&vm->base, dev, 0, flags);
406    panfrost_dev->vm = vm;
407    return &vm->base;
408 }
409 
410 static void
panfrost_kmod_vm_destroy(struct pan_kmod_vm * vm)411 panfrost_kmod_vm_destroy(struct pan_kmod_vm *vm)
412 {
413    struct panfrost_kmod_dev *panfrost_dev =
414       container_of(vm->dev, struct panfrost_kmod_dev, base);
415 
416    panfrost_dev->vm = NULL;
417    pan_kmod_dev_free(vm->dev, vm);
418 }
419 
420 static int
panfrost_kmod_vm_bind(struct pan_kmod_vm * vm,enum pan_kmod_vm_op_mode mode,struct pan_kmod_vm_op * ops,uint32_t op_count)421 panfrost_kmod_vm_bind(struct pan_kmod_vm *vm, enum pan_kmod_vm_op_mode mode,
422                       struct pan_kmod_vm_op *ops, uint32_t op_count)
423 {
424    UNUSED struct panfrost_kmod_vm *panfrost_vm =
425       container_of(vm, struct panfrost_kmod_vm, base);
426 
427    /* We only support IMMEDIATE and WAIT_IDLE mode. Actually we always do
428     * WAIT_IDLE in practice, but it shouldn't matter.
429     */
430    if (mode != PAN_KMOD_VM_OP_MODE_IMMEDIATE &&
431        mode != PAN_KMOD_VM_OP_MODE_DEFER_TO_NEXT_IDLE_POINT) {
432       mesa_loge("panfrost_kmod doesn't support mode=%d", mode);
433       assert(0);
434       return -1;
435    }
436 
437    for (uint32_t i = 0; i < op_count; i++) {
438 
439       if (ops[i].type == PAN_KMOD_VM_OP_TYPE_MAP) {
440          struct panfrost_kmod_bo *panfrost_bo =
441             container_of(ops[i].map.bo, struct panfrost_kmod_bo, base);
442 
443          /* Panfrost kernel driver doesn't support userspace VA management. */
444          if (ops[i].va.start != PAN_KMOD_VM_MAP_AUTO_VA) {
445             mesa_loge("panfrost_kmod can only do auto-VA allocation");
446             assert(0);
447             return -1;
448          }
449 
450          /* Panfrost kernel driver only support full BO mapping. */
451          if (ops[i].map.bo_offset != 0 ||
452              ops[i].va.size != ops[i].map.bo->size) {
453             mesa_loge("panfrost_kmod doesn't support partial BO mapping");
454             assert(0);
455             return -1;
456          }
457 
458          ops[i].va.start = panfrost_bo->offset;
459       } else if (ops[i].type == PAN_KMOD_VM_OP_TYPE_UNMAP) {
460          /* Do nothing, unmapping is done at BO destruction time. */
461       } else {
462          /* We reject PAN_KMOD_VM_OP_TYPE_SYNC_ONLY as this implies
463           * supporting PAN_KMOD_VM_OP_MODE_ASYNC, which we don't support.
464           */
465          mesa_loge("panfrost_kmod doesn't support op=%d", ops[i].type);
466          assert(0);
467          return -1;
468       }
469    }
470 
471    return 0;
472 }
473 
474 static uint64_t
panfrost_kmod_query_timestamp(const struct pan_kmod_dev * dev)475 panfrost_kmod_query_timestamp(const struct pan_kmod_dev *dev)
476 {
477    return panfrost_query_raw(dev->fd, DRM_PANFROST_PARAM_SYSTEM_TIMESTAMP,
478                              false, 0);
479 }
480 
481 const struct pan_kmod_ops panfrost_kmod_ops = {
482    .dev_create = panfrost_kmod_dev_create,
483    .dev_destroy = panfrost_kmod_dev_destroy,
484    .dev_query_props = panfrost_dev_query_props,
485    .dev_query_user_va_range = panfrost_kmod_dev_query_user_va_range,
486    .bo_alloc = panfrost_kmod_bo_alloc,
487    .bo_free = panfrost_kmod_bo_free,
488    .bo_import = panfrost_kmod_bo_import,
489    .bo_get_mmap_offset = panfrost_kmod_bo_get_mmap_offset,
490    .bo_wait = panfrost_kmod_bo_wait,
491    .bo_make_evictable = panfrost_kmod_bo_make_evictable,
492    .bo_make_unevictable = panfrost_kmod_bo_make_unevictable,
493    .vm_create = panfrost_kmod_vm_create,
494    .vm_destroy = panfrost_kmod_vm_destroy,
495    .vm_bind = panfrost_kmod_vm_bind,
496    .query_timestamp = panfrost_kmod_query_timestamp,
497 };
498