• 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/libsync.h"
14 #include "util/macros.h"
15 #include "util/os_time.h"
16 #include "util/simple_mtx.h"
17 #include "util/u_debug.h"
18 #include "util/vma.h"
19 
20 #include "drm-uapi/dma-buf.h"
21 #include "drm-uapi/panthor_drm.h"
22 
23 #include "pan_kmod_backend.h"
24 
25 const struct pan_kmod_ops panthor_kmod_ops;
26 
27 /* Objects used to track VAs returned through async unmaps. */
28 struct panthor_kmod_va_collect {
29    struct list_head node;
30 
31    /* VM sync point at which the VA range should be released. */
32    uint64_t sync_point;
33 
34    /* Start of the VA range to release. */
35    uint64_t va;
36 
37    /* Size of the VA range to release. */
38    size_t size;
39 };
40 
41 struct panthor_kmod_vm {
42    struct pan_kmod_vm base;
43 
44    /* Fields used for auto-VA management. Since the kernel doesn't do it for
45     * us, we need to deal with the VA allocation ourselves.
46     */
47    struct {
48       /* Lock protecting VA allocation/freeing. */
49       simple_mtx_t lock;
50 
51       /* VA heap used to automatically assign a VA. */
52       struct util_vma_heap heap;
53 
54       /* VA ranges to garbage collect. */
55       struct list_head gc_list;
56    } auto_va;
57 
58    /* Fields used for VM activity tracking (TRACK_ACTIVITY flag). */
59    struct {
60       /* VM sync handle. */
61       uint32_t handle;
62 
63       /* Current VM sync point. Incremented every time a GPU job or VM
64        * operation is issued.
65        */
66       uint64_t point;
67 
68       /* Lock protecting insertion of sync points to the timeline syncobj. */
69       simple_mtx_t lock;
70    } sync;
71 };
72 
73 struct panthor_kmod_dev {
74    struct pan_kmod_dev base;
75 
76    /* Userspace mapping of the LATEST_FLUSH_ID register page. */
77    uint32_t *flush_id;
78 
79    /* Cached device properties. Filled at device creation time. */
80    struct {
81       struct drm_panthor_gpu_info gpu;
82       struct drm_panthor_csif_info csif;
83       struct drm_panthor_timestamp_info timestamp;
84       struct drm_panthor_group_priorities_info group_priorities;
85    } props;
86 };
87 
88 struct panthor_kmod_bo {
89    struct pan_kmod_bo base;
90    struct {
91       /* BO sync handle. Will point to the VM BO if the object is not shared. */
92       uint32_t handle;
93 
94       /* BO read sync point. Zero when the object is shared. */
95       uint64_t read_point;
96 
97       /* BO write sync point. Zero when the object is shared. */
98       uint64_t write_point;
99    } sync;
100 };
101 
102 static struct pan_kmod_dev *
panthor_kmod_dev_create(int fd,uint32_t flags,drmVersionPtr version,const struct pan_kmod_allocator * allocator)103 panthor_kmod_dev_create(int fd, uint32_t flags, drmVersionPtr version,
104                         const struct pan_kmod_allocator *allocator)
105 {
106    struct panthor_kmod_dev *panthor_dev =
107       pan_kmod_alloc(allocator, sizeof(*panthor_dev));
108    if (!panthor_dev) {
109       mesa_loge("failed to allocate a panthor_kmod_dev object");
110       return NULL;
111    }
112 
113    /* Cache GPU and CSIF information. */
114    struct drm_panthor_dev_query query = {
115       .type = DRM_PANTHOR_DEV_QUERY_GPU_INFO,
116       .size = sizeof(panthor_dev->props.gpu),
117       .pointer = (uint64_t)(uintptr_t)&panthor_dev->props.gpu,
118    };
119 
120    int ret = drmIoctl(fd, DRM_IOCTL_PANTHOR_DEV_QUERY, &query);
121    if (ret) {
122       mesa_loge("DRM_IOCTL_PANTHOR_DEV_QUERY failed (err=%d)", errno);
123       goto err_free_dev;
124    }
125 
126    query = (struct drm_panthor_dev_query){
127       .type = DRM_PANTHOR_DEV_QUERY_CSIF_INFO,
128       .size = sizeof(panthor_dev->props.csif),
129       .pointer = (uint64_t)(uintptr_t)&panthor_dev->props.csif,
130    };
131 
132    ret = drmIoctl(fd, DRM_IOCTL_PANTHOR_DEV_QUERY, &query);
133    if (ret) {
134       mesa_loge("DRM_IOCTL_PANTHOR_DEV_QUERY failed (err=%d)", errno);
135       goto err_free_dev;
136    }
137 
138    if (version->version_major > 1 || version->version_minor >= 1) {
139       query = (struct drm_panthor_dev_query){
140          .type = DRM_PANTHOR_DEV_QUERY_TIMESTAMP_INFO,
141          .size = sizeof(panthor_dev->props.timestamp),
142          .pointer = (uint64_t)(uintptr_t)&panthor_dev->props.timestamp,
143       };
144 
145       ret = drmIoctl(fd, DRM_IOCTL_PANTHOR_DEV_QUERY, &query);
146       if (ret) {
147          mesa_loge("DRM_IOCTL_PANTHOR_DEV_QUERY failed (err=%d)", errno);
148          goto err_free_dev;
149       }
150    }
151 
152    /* Map the LATEST_FLUSH_ID register at device creation time. */
153    panthor_dev->flush_id = os_mmap(0, getpagesize(), PROT_READ, MAP_SHARED, fd,
154                                    DRM_PANTHOR_USER_FLUSH_ID_MMIO_OFFSET);
155    if (panthor_dev->flush_id == MAP_FAILED) {
156       mesa_loge("failed to mmap the LATEST_FLUSH_ID register (err=%d)", errno);
157       goto err_free_dev;
158    }
159 
160    if (version->version_major > 1 || version->version_minor >= 2) {
161       query = (struct drm_panthor_dev_query){
162          .type = DRM_PANTHOR_DEV_QUERY_GROUP_PRIORITIES_INFO,
163          .size = sizeof(panthor_dev->props.group_priorities),
164          .pointer = (uint64_t)(uintptr_t)&panthor_dev->props.group_priorities,
165       };
166 
167       ret = drmIoctl(fd, DRM_IOCTL_PANTHOR_DEV_QUERY, &query);
168       if (ret) {
169          mesa_loge("DRM_IOCTL_PANTHOR_DEV_QUERY failed (err=%d)", errno);
170          goto err_free_dev;
171       }
172    } else {
173       /* If the query isn't available, Panthor always allow LOW and MEDIUM
174        * priority */
175       panthor_dev->props.group_priorities.allowed_mask |=
176          BITFIELD_BIT(PANTHOR_GROUP_PRIORITY_MEDIUM);
177       panthor_dev->props.group_priorities.allowed_mask |=
178          BITFIELD_BIT(PANTHOR_GROUP_PRIORITY_LOW);
179    }
180 
181    assert(!ret);
182    pan_kmod_dev_init(&panthor_dev->base, fd, flags, version, &panthor_kmod_ops,
183                      allocator);
184    return &panthor_dev->base;
185 
186 err_free_dev:
187    pan_kmod_free(allocator, panthor_dev);
188    return NULL;
189 }
190 
191 static void
panthor_kmod_dev_destroy(struct pan_kmod_dev * dev)192 panthor_kmod_dev_destroy(struct pan_kmod_dev *dev)
193 {
194    struct panthor_kmod_dev *panthor_dev =
195       container_of(dev, struct panthor_kmod_dev, base);
196 
197    os_munmap(panthor_dev->flush_id, getpagesize());
198    pan_kmod_dev_cleanup(dev);
199    pan_kmod_free(dev->allocator, panthor_dev);
200 }
201 
202 static uint32_t
to_kmod_group_allow_priority_flags(uint32_t panthor_flags)203 to_kmod_group_allow_priority_flags(uint32_t panthor_flags)
204 {
205    uint32_t kmod_flags = 0;
206 
207    if (panthor_flags & BITFIELD_BIT(PANTHOR_GROUP_PRIORITY_REALTIME))
208       kmod_flags |= PAN_KMOD_GROUP_ALLOW_PRIORITY_REALTIME;
209 
210    if (panthor_flags & BITFIELD_BIT(PANTHOR_GROUP_PRIORITY_HIGH))
211       kmod_flags |= PAN_KMOD_GROUP_ALLOW_PRIORITY_HIGH;
212 
213    if (panthor_flags & BITFIELD_BIT(PANTHOR_GROUP_PRIORITY_MEDIUM))
214       kmod_flags |= PAN_KMOD_GROUP_ALLOW_PRIORITY_MEDIUM;
215 
216    if (panthor_flags & BITFIELD_BIT(PANTHOR_GROUP_PRIORITY_LOW))
217       kmod_flags |= PAN_KMOD_GROUP_ALLOW_PRIORITY_LOW;
218 
219    return kmod_flags;
220 }
221 
222 static void
panthor_dev_query_thread_props(const struct panthor_kmod_dev * panthor_dev,struct pan_kmod_dev_props * props)223 panthor_dev_query_thread_props(const struct panthor_kmod_dev *panthor_dev,
224                                struct pan_kmod_dev_props *props)
225 {
226    props->max_threads_per_wg = panthor_dev->props.gpu.thread_max_workgroup_size;
227    props->max_threads_per_core = panthor_dev->props.gpu.max_threads;
228    props->max_tasks_per_core = panthor_dev->props.gpu.thread_features >> 24;
229    props->num_registers_per_core =
230       panthor_dev->props.gpu.thread_features & 0x3fffff;
231 
232    /* We assume that all thread properties are populated. If we ever have a GPU
233     * that have one of the THREAD_xxx register that's zero, we can always add a
234     * quirk here.
235     */
236    assert(props->max_threads_per_wg && props->max_threads_per_core &&
237           props->max_tasks_per_core && props->num_registers_per_core);
238 
239    /* There is no THREAD_TLS_ALLOC register on v10+, and the maximum number
240     * of TLS instance per core is assumed to be the maximum number of threads
241     * per core.
242     */
243    props->max_tls_instance_per_core = props->max_threads_per_core;
244 }
245 
246 static void
panthor_dev_query_props(const struct pan_kmod_dev * dev,struct pan_kmod_dev_props * props)247 panthor_dev_query_props(const struct pan_kmod_dev *dev,
248                         struct pan_kmod_dev_props *props)
249 {
250    struct panthor_kmod_dev *panthor_dev =
251       container_of(dev, struct panthor_kmod_dev, base);
252 
253    *props = (struct pan_kmod_dev_props){
254       .gpu_prod_id = panthor_dev->props.gpu.gpu_id >> 16,
255       .gpu_revision = panthor_dev->props.gpu.gpu_id & 0xffff,
256       .gpu_variant = panthor_dev->props.gpu.core_features & 0xff,
257       .shader_present = panthor_dev->props.gpu.shader_present,
258       .tiler_features = panthor_dev->props.gpu.tiler_features,
259       .mem_features = panthor_dev->props.gpu.mem_features,
260       .mmu_features = panthor_dev->props.gpu.mmu_features,
261 
262       /* This register does not exist because AFBC is no longer optional. */
263       .afbc_features = 0,
264 
265       /* Access to timstamp from the GPU is always supported on Panthor. */
266       .gpu_can_query_timestamp = true,
267 
268       .timestamp_frequency = panthor_dev->props.timestamp.timestamp_frequency,
269 
270       .allowed_group_priorities_mask = to_kmod_group_allow_priority_flags(
271          panthor_dev->props.group_priorities.allowed_mask),
272    };
273 
274    static_assert(sizeof(props->texture_features) ==
275                     sizeof(panthor_dev->props.gpu.texture_features),
276                  "Mismatch in texture_features array size");
277 
278    memcpy(props->texture_features, panthor_dev->props.gpu.texture_features,
279           sizeof(props->texture_features));
280 
281    panthor_dev_query_thread_props(panthor_dev, props);
282 }
283 
284 static struct pan_kmod_va_range
panthor_kmod_dev_query_user_va_range(const struct pan_kmod_dev * dev)285 panthor_kmod_dev_query_user_va_range(const struct pan_kmod_dev *dev)
286 {
287    struct panthor_kmod_dev *panthor_dev =
288       container_of(dev, struct panthor_kmod_dev, base);
289    uint8_t va_bits = MMU_FEATURES_VA_BITS(panthor_dev->props.gpu.mmu_features);
290 
291    /* If we have less than 32-bit VA space it starts to be tricky, so let's
292     * assume we always have at least that.
293     */
294    assert(va_bits >= 32);
295 
296    return (struct pan_kmod_va_range){
297       .start = 0,
298 
299       /* 3G/1G user/kernel VA split for 32-bit VA space. Otherwise, we reserve
300        * half of the VA space for kernel objects.
301        */
302       .size =
303          va_bits == 32 ? (1ull << (va_bits - 2)) * 3 : 1ull << (va_bits - 1),
304    };
305 }
306 
307 static uint32_t
to_panthor_bo_flags(uint32_t flags)308 to_panthor_bo_flags(uint32_t flags)
309 {
310    uint32_t panthor_flags = 0;
311 
312    if (flags & PAN_KMOD_BO_FLAG_NO_MMAP)
313       panthor_flags |= DRM_PANTHOR_BO_NO_MMAP;
314 
315    return panthor_flags;
316 }
317 
318 static struct pan_kmod_bo *
panthor_kmod_bo_alloc(struct pan_kmod_dev * dev,struct pan_kmod_vm * exclusive_vm,size_t size,uint32_t flags)319 panthor_kmod_bo_alloc(struct pan_kmod_dev *dev,
320                       struct pan_kmod_vm *exclusive_vm, size_t size,
321                       uint32_t flags)
322 {
323    /* We don't support allocating on-fault. */
324    if (flags & PAN_KMOD_BO_FLAG_ALLOC_ON_FAULT) {
325       mesa_loge("panthor_kmod doesn't support PAN_KMOD_BO_FLAG_ALLOC_ON_FAULT");
326       return NULL;
327    }
328 
329    struct panthor_kmod_vm *panthor_vm =
330       exclusive_vm ? container_of(exclusive_vm, struct panthor_kmod_vm, base)
331                    : NULL;
332    struct panthor_kmod_bo *bo = pan_kmod_dev_alloc(dev, sizeof(*bo));
333    if (!bo) {
334       mesa_loge("failed to allocate a panthor_kmod_bo object");
335       return NULL;
336    }
337 
338    struct drm_panthor_bo_create req = {
339       .size = size,
340       .flags = to_panthor_bo_flags(flags),
341       .exclusive_vm_id = panthor_vm ? panthor_vm->base.handle : 0,
342    };
343 
344    int ret = drmIoctl(dev->fd, DRM_IOCTL_PANTHOR_BO_CREATE, &req);
345    if (ret) {
346       mesa_loge("DRM_IOCTL_PANTHOR_BO_CREATE failed (err=%d)", errno);
347       goto err_free_bo;
348    }
349 
350    if (!exclusive_vm) {
351       /* For buffers we know will be shared, create our own syncobj. */
352       int ret = drmSyncobjCreate(dev->fd, DRM_SYNCOBJ_CREATE_SIGNALED,
353                                  &bo->sync.handle);
354       if (ret) {
355          mesa_loge("drmSyncobjCreate() failed (err=%d)", errno);
356          goto err_destroy_bo;
357       }
358    } else {
359       /* If the buffer is private to the VM, we just use the VM syncobj. */
360       bo->sync.handle = panthor_vm->sync.handle;
361    }
362 
363    bo->sync.read_point = bo->sync.write_point = 0;
364 
365    pan_kmod_bo_init(&bo->base, dev, exclusive_vm, req.size, flags, req.handle);
366    return &bo->base;
367 
368 err_destroy_bo:
369    drmCloseBufferHandle(dev->fd, bo->base.handle);
370 err_free_bo:
371    pan_kmod_dev_free(dev, bo);
372    return NULL;
373 }
374 
375 static void
panthor_kmod_bo_free(struct pan_kmod_bo * bo)376 panthor_kmod_bo_free(struct pan_kmod_bo *bo)
377 {
378    struct panthor_kmod_bo *panthor_bo =
379       container_of(bo, struct panthor_kmod_bo, base);
380 
381    if (!bo->exclusive_vm)
382       drmSyncobjDestroy(bo->dev->fd, panthor_bo->sync.handle);
383 
384    drmCloseBufferHandle(bo->dev->fd, bo->handle);
385    pan_kmod_dev_free(bo->dev, bo);
386 }
387 
388 static struct pan_kmod_bo *
panthor_kmod_bo_import(struct pan_kmod_dev * dev,uint32_t handle,size_t size,uint32_t flags)389 panthor_kmod_bo_import(struct pan_kmod_dev *dev, uint32_t handle, size_t size,
390                        uint32_t flags)
391 {
392    struct panthor_kmod_bo *panthor_bo =
393       pan_kmod_dev_alloc(dev, sizeof(*panthor_bo));
394    if (!panthor_bo) {
395       mesa_loge("failed to allocate a panthor_kmod_bo object");
396       return NULL;
397    }
398 
399    /* Create a unsignalled syncobj on import. Will serve as a
400     * temporary container for the exported dmabuf sync file.
401     */
402    int ret = drmSyncobjCreate(dev->fd, 0, &panthor_bo->sync.handle);
403    if (ret) {
404       mesa_loge("drmSyncobjCreate() failed (err=%d)", errno);
405       goto err_free_bo;
406    }
407 
408    pan_kmod_bo_init(&panthor_bo->base, dev, NULL, size,
409                     flags | PAN_KMOD_BO_FLAG_IMPORTED, handle);
410    return &panthor_bo->base;
411 
412 err_free_bo:
413    pan_kmod_dev_free(dev, panthor_bo);
414    return NULL;
415 }
416 
417 static int
panthor_kmod_bo_export(struct pan_kmod_bo * bo,int dmabuf_fd)418 panthor_kmod_bo_export(struct pan_kmod_bo *bo, int dmabuf_fd)
419 {
420    struct panthor_kmod_bo *panthor_bo =
421       container_of(bo, struct panthor_kmod_bo, base);
422 
423    bool shared =
424       bo->flags & (PAN_KMOD_BO_FLAG_EXPORTED | PAN_KMOD_BO_FLAG_IMPORTED);
425 
426    /* If the BO wasn't already shared, we migrate our internal sync points to
427     * the dmabuf itself, so implicit sync can work correctly after this point.
428     */
429    if (!shared) {
430       if (panthor_bo->sync.read_point || panthor_bo->sync.write_point) {
431          struct dma_buf_import_sync_file isync = {
432             .flags = DMA_BUF_SYNC_RW,
433          };
434          int ret = drmSyncobjExportSyncFile(bo->dev->fd,
435                                             panthor_bo->sync.handle, &isync.fd);
436          if (ret) {
437             mesa_loge("drmSyncobjExportSyncFile() failed (err=%d)", errno);
438             return -1;
439          }
440 
441          ret = drmIoctl(dmabuf_fd, DMA_BUF_IOCTL_IMPORT_SYNC_FILE, &isync);
442          close(isync.fd);
443          if (ret) {
444             mesa_loge("DMA_BUF_IOCTL_IMPORT_SYNC_FILE failed (err=%d)", errno);
445             return -1;
446          }
447       }
448 
449       /* Make sure we reset the syncobj on export. We will use it as a
450        * temporary binary syncobj to import sync_file FD from now on.
451        */
452       int ret = drmSyncobjReset(bo->dev->fd, &panthor_bo->sync.handle, 1);
453       if (ret) {
454          mesa_loge("drmSyncobjReset() failed (err=%d)", errno);
455          return -1;
456       }
457 
458       panthor_bo->sync.read_point = 0;
459       panthor_bo->sync.write_point = 0;
460    }
461 
462    bo->flags |= PAN_KMOD_BO_FLAG_EXPORTED;
463    return 0;
464 }
465 
466 static off_t
panthor_kmod_bo_get_mmap_offset(struct pan_kmod_bo * bo)467 panthor_kmod_bo_get_mmap_offset(struct pan_kmod_bo *bo)
468 {
469    struct drm_panthor_bo_mmap_offset req = {.handle = bo->handle};
470    int ret = drmIoctl(bo->dev->fd, DRM_IOCTL_PANTHOR_BO_MMAP_OFFSET, &req);
471 
472    if (ret) {
473       mesa_loge("DRM_IOCTL_PANTHOR_BO_MMAP_OFFSET failed (err=%d)", errno);
474       return -1;
475    }
476 
477    return req.offset;
478 }
479 
480 static bool
panthor_kmod_bo_wait(struct pan_kmod_bo * bo,int64_t timeout_ns,bool for_read_only_access)481 panthor_kmod_bo_wait(struct pan_kmod_bo *bo, int64_t timeout_ns,
482                      bool for_read_only_access)
483 {
484    struct panthor_kmod_bo *panthor_bo =
485       container_of(bo, struct panthor_kmod_bo, base);
486    bool shared =
487       bo->flags & (PAN_KMOD_BO_FLAG_EXPORTED | PAN_KMOD_BO_FLAG_IMPORTED);
488 
489    if (shared) {
490       /* If the object is shared, we have to do this export sync-file dance
491        * to reconcile with the implicit sync model. This implies exporting
492        * our GEM object as a dma-buf and closing it right after the
493        * EXPORT_SYNC_FILE, unfortunately.
494        */
495       int dmabuf_fd;
496       int ret =
497          drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC, &dmabuf_fd);
498 
499       if (ret) {
500          mesa_loge("drmPrimeHandleToFD() failed (err=%d)", errno);
501          return false;
502       }
503 
504       struct dma_buf_export_sync_file esync = {
505          .flags = for_read_only_access ? DMA_BUF_SYNC_READ : DMA_BUF_SYNC_RW,
506       };
507 
508       ret = drmIoctl(dmabuf_fd, DMA_BUF_IOCTL_EXPORT_SYNC_FILE, &esync);
509       close(dmabuf_fd);
510 
511       if (ret) {
512          mesa_loge("DMA_BUF_IOCTL_EXPORT_SYNC_FILE failed (err=%d)", errno);
513          return false;
514       }
515 
516       ret = sync_wait(esync.fd, timeout_ns / 1000000);
517       close(esync.fd);
518       return ret == 0;
519    } else {
520       /* Waiting on non-shared object is much simpler. We just pick the
521        * right sync point based on for_read_only_access and call
522        * drmSyncobjTimelineWait().
523        */
524       uint64_t sync_point =
525          for_read_only_access
526             ? panthor_bo->sync.write_point
527             : MAX2(panthor_bo->sync.write_point, panthor_bo->sync.read_point);
528 
529       if (!sync_point)
530          return true;
531 
532       int64_t abs_timeout_ns = timeout_ns < INT64_MAX - os_time_get_nano()
533                                   ? timeout_ns + os_time_get_nano()
534                                   : INT64_MAX;
535       int ret = drmSyncobjTimelineWait(bo->dev->fd, &panthor_bo->sync.handle,
536                                        &sync_point, 1, abs_timeout_ns,
537                                        DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL, NULL);
538       if (ret >= 0)
539          return true;
540 
541       if (ret != -ETIME)
542          mesa_loge("DMA_BUF_IOCTL_EXPORT_SYNC_FILE failed (err=%d)", ret);
543 
544       return false;
545    }
546 }
547 
548 /* Attach a sync to a buffer object. */
549 int
panthor_kmod_bo_attach_sync_point(struct pan_kmod_bo * bo,uint32_t sync_handle,uint64_t sync_point,bool written)550 panthor_kmod_bo_attach_sync_point(struct pan_kmod_bo *bo, uint32_t sync_handle,
551                                   uint64_t sync_point, bool written)
552 {
553    struct panthor_kmod_bo *panthor_bo =
554       container_of(bo, struct panthor_kmod_bo, base);
555    struct panthor_kmod_vm *panthor_vm =
556       bo->exclusive_vm
557          ? container_of(bo->exclusive_vm, struct panthor_kmod_vm, base)
558          : NULL;
559    bool shared =
560       bo->flags & (PAN_KMOD_BO_FLAG_EXPORTED | PAN_KMOD_BO_FLAG_IMPORTED);
561 
562    if (shared) {
563       /* Reconciling explicit/implicit sync again: we need to import the
564        * new sync point in the dma-buf, so other parties can rely on
565        * implicit deps.
566        */
567       struct dma_buf_import_sync_file isync = {
568          .flags = written ? DMA_BUF_SYNC_RW : DMA_BUF_SYNC_READ,
569       };
570       int dmabuf_fd;
571       int ret = drmSyncobjExportSyncFile(bo->dev->fd, sync_handle, &isync.fd);
572       if (ret) {
573          mesa_loge("drmSyncobjExportSyncFile() failed (err=%d)", errno);
574          return -1;
575       }
576 
577       ret =
578          drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC, &dmabuf_fd);
579       if (ret) {
580          mesa_loge("drmPrimeHandleToFD() failed (err=%d)", errno);
581          close(isync.fd);
582          return -1;
583       }
584 
585       ret = drmIoctl(dmabuf_fd, DMA_BUF_IOCTL_IMPORT_SYNC_FILE, &isync);
586       close(dmabuf_fd);
587       close(isync.fd);
588       if (ret) {
589          mesa_loge("DMA_BUF_IOCTL_IMPORT_SYNC_FILE failed (err=%d)", errno);
590          return -1;
591       }
592    } else if (panthor_vm) {
593       /* Private BOs should be passed the VM syncobj. */
594       assert(sync_handle == panthor_vm->sync.handle);
595 
596       panthor_bo->sync.read_point =
597          MAX2(sync_point, panthor_bo->sync.read_point);
598       if (written) {
599          panthor_bo->sync.write_point =
600             MAX2(sync_point, panthor_bo->sync.write_point);
601       }
602    } else {
603       /* For non-private BOs that are not shared yet, we add a new sync point
604        * to our timeline syncobj, and push the sync there.
605        */
606       uint32_t new_sync_point =
607          MAX2(panthor_bo->sync.write_point, panthor_bo->sync.read_point) + 1;
608 
609       int ret = drmSyncobjTransfer(bo->dev->fd, panthor_bo->sync.handle,
610                                    new_sync_point, sync_handle, sync_point, 0);
611       if (ret) {
612          mesa_loge("drmSyncobjTransfer() failed (err=%d)", errno);
613          return -1;
614       }
615 
616       panthor_bo->sync.read_point = new_sync_point;
617       if (written)
618          panthor_bo->sync.write_point = new_sync_point;
619    }
620 
621    return 0;
622 }
623 
624 /* Get the sync point for a read or write operation on a buffer object. */
625 int
panthor_kmod_bo_get_sync_point(struct pan_kmod_bo * bo,uint32_t * sync_handle,uint64_t * sync_point,bool for_read_only_access)626 panthor_kmod_bo_get_sync_point(struct pan_kmod_bo *bo, uint32_t *sync_handle,
627                                uint64_t *sync_point, bool for_read_only_access)
628 {
629    struct panthor_kmod_bo *panthor_bo =
630       container_of(bo, struct panthor_kmod_bo, base);
631    bool shared =
632       bo->flags & (PAN_KMOD_BO_FLAG_EXPORTED | PAN_KMOD_BO_FLAG_IMPORTED);
633 
634    if (shared) {
635       /* Explicit/implicit sync reconciliation point. We need to export
636        * a sync-file from the dmabuf and make it a syncobj.
637        */
638       int dmabuf_fd;
639       int ret =
640          drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC, &dmabuf_fd);
641       if (ret) {
642          mesa_loge("drmPrimeHandleToFD() failed (err=%d)\n", errno);
643          return -1;
644       }
645 
646       struct dma_buf_export_sync_file esync = {
647          .flags = for_read_only_access ? DMA_BUF_SYNC_READ : DMA_BUF_SYNC_RW,
648       };
649 
650       ret = drmIoctl(dmabuf_fd, DMA_BUF_IOCTL_EXPORT_SYNC_FILE, &esync);
651       close(dmabuf_fd);
652       if (ret) {
653          mesa_loge("DMA_BUF_IOCTL_EXPORT_SYNC_FILE failed (err=%d)", errno);
654          return -1;
655       }
656 
657       /* We store the resulting sync in our BO syncobj, which will be assigned
658        * a new sync next time we enter this function.
659        */
660       ret = drmSyncobjImportSyncFile(bo->dev->fd, panthor_bo->sync.handle,
661                                      esync.fd);
662       close(esync.fd);
663       if (ret) {
664          mesa_loge("drmSyncobjImportSyncFile() failed (err=%d)", errno);
665          return -1;
666       }
667 
668       /* The syncobj is a binary syncobj in that case. */
669       *sync_handle = panthor_bo->sync.handle;
670       *sync_point = 0;
671    } else {
672       /* Fortunately, the non-shared path is much simpler, we just return
673        * the read/write sync point depending on the access type. The syncobj
674        * is a timeline syncobj in that case.
675        */
676       *sync_handle = panthor_bo->sync.handle;
677       *sync_point = for_read_only_access ? panthor_bo->sync.write_point
678                                          : MAX2(panthor_bo->sync.read_point,
679                                                 panthor_bo->sync.write_point);
680    }
681    return 0;
682 }
683 
684 static struct pan_kmod_vm *
panthor_kmod_vm_create(struct pan_kmod_dev * dev,uint32_t flags,uint64_t user_va_start,uint64_t user_va_range)685 panthor_kmod_vm_create(struct pan_kmod_dev *dev, uint32_t flags,
686                        uint64_t user_va_start, uint64_t user_va_range)
687 {
688    struct pan_kmod_dev_props props;
689 
690    panthor_dev_query_props(dev, &props);
691 
692    struct panthor_kmod_vm *panthor_vm =
693       pan_kmod_dev_alloc(dev, sizeof(*panthor_vm));
694    if (!panthor_vm) {
695       mesa_loge("failed to allocate a panthor_kmod_vm object");
696       return NULL;
697    }
698 
699    if (flags & PAN_KMOD_VM_FLAG_AUTO_VA) {
700       simple_mtx_init(&panthor_vm->auto_va.lock, mtx_plain);
701       list_inithead(&panthor_vm->auto_va.gc_list);
702       util_vma_heap_init(&panthor_vm->auto_va.heap, user_va_start,
703                          user_va_range);
704    }
705 
706    if (flags & PAN_KMOD_VM_FLAG_TRACK_ACTIVITY) {
707       simple_mtx_init(&panthor_vm->sync.lock, mtx_plain);
708       panthor_vm->sync.point = 0;
709       if (drmSyncobjCreate(dev->fd, DRM_SYNCOBJ_CREATE_SIGNALED,
710                            &panthor_vm->sync.handle)) {
711          mesa_loge("drmSyncobjCreate() failed (err=%d)", errno);
712          goto err_free_vm;
713       }
714    }
715 
716    struct drm_panthor_vm_create req = {
717       .user_va_range = user_va_start + user_va_range,
718    };
719 
720    if (drmIoctl(dev->fd, DRM_IOCTL_PANTHOR_VM_CREATE, &req)) {
721       mesa_loge("DRM_IOCTL_PANTHOR_VM_CREATE failed (err=%d)", errno);
722       goto err_destroy_sync;
723    }
724 
725    pan_kmod_vm_init(&panthor_vm->base, dev, req.id, flags);
726    return &panthor_vm->base;
727 
728 err_destroy_sync:
729    if (flags & PAN_KMOD_VM_FLAG_TRACK_ACTIVITY) {
730       drmSyncobjDestroy(dev->fd, panthor_vm->sync.handle);
731       simple_mtx_destroy(&panthor_vm->sync.lock);
732    }
733 
734 err_free_vm:
735    if (flags & PAN_KMOD_VM_FLAG_AUTO_VA) {
736       util_vma_heap_finish(&panthor_vm->auto_va.heap);
737       simple_mtx_destroy(&panthor_vm->auto_va.lock);
738    }
739 
740    pan_kmod_dev_free(dev, panthor_vm);
741    return NULL;
742 }
743 
744 static void
panthor_kmod_vm_collect_freed_vas(struct panthor_kmod_vm * vm)745 panthor_kmod_vm_collect_freed_vas(struct panthor_kmod_vm *vm)
746 {
747    if (!(vm->base.flags & PAN_KMOD_VM_FLAG_AUTO_VA))
748       return;
749 
750    bool done = false;
751 
752    simple_mtx_assert_locked(&vm->auto_va.lock);
753    list_for_each_entry_safe_rev(struct panthor_kmod_va_collect, req,
754                                 &vm->auto_va.gc_list, node)
755    {
756       /* Unmaps are queued in order of execution */
757       if (!done) {
758          int ret = drmSyncobjTimelineWait(
759             vm->base.dev->fd, &vm->sync.handle, &req->sync_point, 1, 0,
760             DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL, NULL);
761          if (ret >= 0)
762             done = true;
763          else
764             continue;
765       }
766 
767       list_del(&req->node);
768       util_vma_heap_free(&vm->auto_va.heap, req->va, req->size);
769       pan_kmod_dev_free(vm->base.dev, req);
770    }
771 }
772 
773 static void
panthor_kmod_vm_destroy(struct pan_kmod_vm * vm)774 panthor_kmod_vm_destroy(struct pan_kmod_vm *vm)
775 {
776    struct panthor_kmod_vm *panthor_vm =
777       container_of(vm, struct panthor_kmod_vm, base);
778    struct drm_panthor_vm_destroy req = {.id = vm->handle};
779    int ret = drmIoctl(vm->dev->fd, DRM_IOCTL_PANTHOR_VM_DESTROY, &req);
780    if (ret)
781       mesa_loge("DRM_IOCTL_PANTHOR_VM_DESTROY failed (err=%d)", errno);
782 
783    assert(!ret);
784 
785    if (panthor_vm->base.flags & PAN_KMOD_VM_FLAG_TRACK_ACTIVITY) {
786       drmSyncobjDestroy(vm->dev->fd, panthor_vm->sync.handle);
787       simple_mtx_destroy(&panthor_vm->sync.lock);
788    }
789 
790    if (panthor_vm->base.flags & PAN_KMOD_VM_FLAG_AUTO_VA) {
791       simple_mtx_lock(&panthor_vm->auto_va.lock);
792       list_for_each_entry_safe(struct panthor_kmod_va_collect, req,
793                                &panthor_vm->auto_va.gc_list, node) {
794          list_del(&req->node);
795          util_vma_heap_free(&panthor_vm->auto_va.heap, req->va, req->size);
796          pan_kmod_dev_free(vm->dev, req);
797       }
798       util_vma_heap_finish(&panthor_vm->auto_va.heap);
799       simple_mtx_unlock(&panthor_vm->auto_va.lock);
800       simple_mtx_destroy(&panthor_vm->auto_va.lock);
801    }
802 
803    pan_kmod_dev_free(vm->dev, panthor_vm);
804 }
805 
806 static uint64_t
panthor_kmod_vm_alloc_va(struct panthor_kmod_vm * panthor_vm,size_t size)807 panthor_kmod_vm_alloc_va(struct panthor_kmod_vm *panthor_vm, size_t size)
808 {
809    uint64_t va;
810 
811    assert(panthor_vm->base.flags & PAN_KMOD_VM_FLAG_AUTO_VA);
812 
813    simple_mtx_lock(&panthor_vm->auto_va.lock);
814    panthor_kmod_vm_collect_freed_vas(panthor_vm);
815    va = util_vma_heap_alloc(&panthor_vm->auto_va.heap, size,
816                             size > 0x200000 ? 0x200000 : 0x1000);
817    simple_mtx_unlock(&panthor_vm->auto_va.lock);
818 
819    return va;
820 }
821 
822 static void
panthor_kmod_vm_free_va(struct panthor_kmod_vm * panthor_vm,uint64_t va,size_t size)823 panthor_kmod_vm_free_va(struct panthor_kmod_vm *panthor_vm, uint64_t va,
824                         size_t size)
825 {
826    assert(panthor_vm->base.flags & PAN_KMOD_VM_FLAG_AUTO_VA);
827 
828    simple_mtx_lock(&panthor_vm->auto_va.lock);
829    util_vma_heap_free(&panthor_vm->auto_va.heap, va, size);
830    simple_mtx_unlock(&panthor_vm->auto_va.lock);
831 }
832 
833 static int
panthor_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)834 panthor_kmod_vm_bind(struct pan_kmod_vm *vm, enum pan_kmod_vm_op_mode mode,
835                      struct pan_kmod_vm_op *ops, uint32_t op_count)
836 {
837    struct panthor_kmod_vm *panthor_vm =
838       container_of(vm, struct panthor_kmod_vm, base);
839    struct drm_panthor_vm_bind_op bind_ops_storage[16];
840    struct drm_panthor_vm_bind_op *bind_ops = NULL;
841    struct drm_panthor_sync_op sync_ops_storage[16];
842    struct drm_panthor_sync_op *sync_ops = NULL;
843    uint32_t syncop_cnt = 0, syncop_ptr = 0;
844    bool async = mode == PAN_KMOD_VM_OP_MODE_ASYNC ||
845                 mode == PAN_KMOD_VM_OP_MODE_DEFER_TO_NEXT_IDLE_POINT;
846    bool auto_va = vm->flags & PAN_KMOD_VM_FLAG_AUTO_VA;
847    bool track_activity = vm->flags & PAN_KMOD_VM_FLAG_TRACK_ACTIVITY;
848    struct panthor_kmod_va_collect *cur_va_collect = NULL;
849    struct list_head va_collect_list;
850    uint32_t va_collect_cnt = 0;
851    int ret = -1;
852 
853    /* For any asynchronous VM bind, we assume the user is managing the VM
854     * address space, so we don't have to collect VMAs in that case.
855     */
856    if (mode == PAN_KMOD_VM_OP_MODE_ASYNC && auto_va) {
857       mesa_loge(
858          "auto-VA allocation is incompatible with PAN_KMOD_VM_OP_MODE_ASYNC");
859       return -1;
860    }
861 
862    if (mode == PAN_KMOD_VM_OP_MODE_DEFER_TO_NEXT_IDLE_POINT &&
863        !track_activity) {
864       mesa_loge(
865          "PAN_KMOD_VM_OP_MODE_DEFER_TO_NEXT_IDLE_POINT requires PAN_KMOD_VM_FLAG_TRACK_ACTIVITY");
866       return -1;
867    }
868 
869    if (op_count == 0)
870       return 0;
871 
872    /* If this is an async operation and VM activity tracking is enabled, we
873     * reserve one syncop per VM operation for the signaling of our VM timeline
874     * slot.
875     */
876    if (async && track_activity)
877       syncop_cnt += op_count;
878 
879    /* With PAN_KMOD_VM_OP_MODE_DEFER_TO_NEXT_IDLE_POINT, we need to push our
880     * wait VM syncobj in all of the submissions, hence the extra syncop per
881     * operation.
882     */
883    if (mode == PAN_KMOD_VM_OP_MODE_DEFER_TO_NEXT_IDLE_POINT)
884       syncop_cnt += op_count;
885 
886    for (uint32_t i = 0; i < op_count; i++) {
887       if (pan_kmod_vm_op_check(vm, mode, &ops[i]))
888          return -1;
889 
890       /* If auto-VA is used, for any asynchronous unmap operation, we need
891        * to register a VA collection node and add it to the GC list.
892        */
893       if (auto_va && async && ops[i].type == PAN_KMOD_VM_OP_TYPE_UNMAP &&
894           ops[i].va.size)
895          va_collect_cnt++;
896 
897       syncop_cnt += ops[i].syncs.count;
898    }
899 
900    /* Pre-allocate the VA collection nodes. */
901    list_inithead(&va_collect_list);
902    for (uint32_t i = 0; i < va_collect_cnt; i++) {
903       struct panthor_kmod_va_collect *va_collect =
904          pan_kmod_dev_alloc(vm->dev, sizeof(*va_collect));
905       if (!va_collect) {
906          mesa_loge("panthor_kmod_va_collect allocation failed");
907          goto out_free_va_collect;
908       }
909 
910       if (!i)
911          cur_va_collect = va_collect;
912 
913       list_addtail(&va_collect->node, &va_collect_list);
914    }
915 
916    if (syncop_cnt && syncop_cnt > ARRAY_SIZE(sync_ops_storage)) {
917       sync_ops =
918          pan_kmod_dev_alloc_transient(vm->dev, sizeof(*sync_ops) * syncop_cnt);
919       if (!sync_ops) {
920          mesa_loge("drm_panthor_sync_op[%d] array allocation failed",
921                    syncop_cnt);
922          goto out_free_va_collect;
923       }
924    } else if (syncop_cnt) {
925       sync_ops = sync_ops_storage;
926       memset(sync_ops, 0, sizeof(*sync_ops) * syncop_cnt);
927    }
928 
929    if (op_count > ARRAY_SIZE(bind_ops_storage)) {
930       bind_ops =
931          pan_kmod_dev_alloc_transient(vm->dev, sizeof(*bind_ops) * op_count);
932       if (!bind_ops) {
933          mesa_loge("drm_panthor_vm_bind_op[%d] array allocation failed",
934                    op_count);
935          goto out_free_sync_ops;
936       }
937    } else {
938       bind_ops = bind_ops_storage;
939       memset(bind_ops, 0, sizeof(*bind_ops) * op_count);
940    }
941 
942    struct drm_panthor_vm_bind req = {
943       .vm_id = vm->handle,
944       .flags =
945          mode != PAN_KMOD_VM_OP_MODE_IMMEDIATE ? DRM_PANTHOR_VM_BIND_ASYNC : 0,
946       .ops = DRM_PANTHOR_OBJ_ARRAY(op_count, bind_ops),
947    };
948 
949    uint64_t vm_orig_sync_point = 0, vm_new_sync_point = 0;
950 
951    if (track_activity)
952       vm_orig_sync_point = vm_new_sync_point = panthor_kmod_vm_sync_lock(vm);
953 
954    for (uint32_t i = 0; i < op_count; i++) {
955       uint32_t op_sync_cnt = ops[i].syncs.count;
956       uint64_t signal_vm_point = 0;
957 
958       if (async && track_activity) {
959          signal_vm_point = ++vm_new_sync_point;
960          op_sync_cnt++;
961          sync_ops[syncop_ptr++] = (struct drm_panthor_sync_op){
962             .flags = DRM_PANTHOR_SYNC_OP_SIGNAL |
963                      DRM_PANTHOR_SYNC_OP_HANDLE_TYPE_TIMELINE_SYNCOBJ,
964             .handle = panthor_vm->sync.handle,
965             .timeline_value = signal_vm_point,
966          };
967       }
968 
969       if (mode == PAN_KMOD_VM_OP_MODE_DEFER_TO_NEXT_IDLE_POINT) {
970          op_sync_cnt++;
971          sync_ops[syncop_ptr++] = (struct drm_panthor_sync_op){
972             .flags = DRM_PANTHOR_SYNC_OP_WAIT |
973                      DRM_PANTHOR_SYNC_OP_HANDLE_TYPE_TIMELINE_SYNCOBJ,
974             .handle = panthor_vm->sync.handle,
975             .timeline_value = vm_orig_sync_point,
976          };
977 
978          if (auto_va && ops[i].type == PAN_KMOD_VM_OP_TYPE_UNMAP &&
979              ops[i].va.size) {
980             struct panthor_kmod_va_collect *va_collect = cur_va_collect;
981 
982             assert(&va_collect->node != &va_collect_list);
983             assert(signal_vm_point);
984             va_collect->sync_point = signal_vm_point;
985             va_collect->va = ops[i].va.start;
986             va_collect->size = ops[i].va.size;
987 
988             cur_va_collect = list_entry(cur_va_collect->node.next,
989                                         struct panthor_kmod_va_collect, node);
990          }
991       }
992 
993       for (uint32_t j = 0; j < ops[i].syncs.count; j++) {
994          sync_ops[syncop_ptr++] = (struct drm_panthor_sync_op){
995             .flags = (ops[i].syncs.array[j].type == PAN_KMOD_SYNC_TYPE_WAIT
996                          ? DRM_PANTHOR_SYNC_OP_WAIT
997                          : DRM_PANTHOR_SYNC_OP_SIGNAL) |
998                      DRM_PANTHOR_SYNC_OP_HANDLE_TYPE_TIMELINE_SYNCOBJ,
999             .handle = ops[i].syncs.array[j].handle,
1000             .timeline_value = ops[i].syncs.array[j].point,
1001          };
1002       }
1003       op_sync_cnt += ops[i].syncs.count;
1004 
1005       bind_ops[i].syncs = (struct drm_panthor_obj_array)DRM_PANTHOR_OBJ_ARRAY(
1006          op_sync_cnt, op_sync_cnt ? &sync_ops[syncop_ptr - op_sync_cnt] : NULL);
1007 
1008       if (ops[i].type == PAN_KMOD_VM_OP_TYPE_MAP) {
1009          bind_ops[i].flags = DRM_PANTHOR_VM_BIND_OP_TYPE_MAP;
1010          bind_ops[i].size = ops[i].va.size;
1011          bind_ops[i].bo_handle = ops[i].map.bo->handle;
1012          bind_ops[i].bo_offset = ops[i].map.bo_offset;
1013 
1014          if (ops[i].va.start == PAN_KMOD_VM_MAP_AUTO_VA) {
1015             bind_ops[i].va =
1016                panthor_kmod_vm_alloc_va(panthor_vm, bind_ops[i].size);
1017             if (!bind_ops[i].va) {
1018                mesa_loge("VA allocation failed");
1019                ret = -1;
1020                goto out_update_vas;
1021             }
1022          } else {
1023             bind_ops[i].va = ops[i].va.start;
1024          }
1025 
1026          if (ops[i].map.bo->flags & PAN_KMOD_BO_FLAG_EXECUTABLE)
1027             bind_ops[i].flags |= DRM_PANTHOR_VM_BIND_OP_MAP_READONLY;
1028          else
1029             bind_ops[i].flags |= DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC;
1030 
1031          if (ops[i].map.bo->flags & PAN_KMOD_BO_FLAG_GPU_UNCACHED)
1032             bind_ops[i].flags |= DRM_PANTHOR_VM_BIND_OP_MAP_UNCACHED;
1033 
1034       } else if (ops[i].type == PAN_KMOD_VM_OP_TYPE_UNMAP) {
1035          bind_ops[i].flags = DRM_PANTHOR_VM_BIND_OP_TYPE_UNMAP;
1036          bind_ops[i].va = ops[i].va.start;
1037          bind_ops[i].size = ops[i].va.size;
1038       } else {
1039          assert(ops[i].type == PAN_KMOD_VM_OP_TYPE_SYNC_ONLY);
1040          bind_ops[i].flags = DRM_PANTHOR_VM_BIND_OP_TYPE_SYNC_ONLY;
1041       }
1042    }
1043 
1044    ret = drmIoctl(vm->dev->fd, DRM_IOCTL_PANTHOR_VM_BIND, &req);
1045    if (ret)
1046       mesa_loge("DRM_IOCTL_PANTHOR_VM_BIND failed (err=%d)", errno);
1047 
1048    if (!ret && va_collect_cnt) {
1049       assert(&cur_va_collect->node == &va_collect_list);
1050       simple_mtx_lock(&panthor_vm->auto_va.lock);
1051       list_splicetail(&va_collect_list, &panthor_vm->auto_va.gc_list);
1052       list_inithead(&va_collect_list);
1053       simple_mtx_unlock(&panthor_vm->auto_va.lock);
1054    }
1055 
1056 out_update_vas:
1057    if (track_activity) {
1058       panthor_kmod_vm_sync_unlock(vm,
1059                                   ret ? vm_orig_sync_point : vm_new_sync_point);
1060    }
1061 
1062    for (uint32_t i = 0; i < op_count; i++) {
1063       if (ops[i].type == PAN_KMOD_VM_OP_TYPE_MAP &&
1064           ops[i].va.start == PAN_KMOD_VM_MAP_AUTO_VA) {
1065          if (!ret) {
1066             ops[i].va.start = bind_ops[i].va;
1067          } else if (bind_ops[i].va != 0) {
1068             panthor_kmod_vm_free_va(panthor_vm, bind_ops[i].va,
1069                                     bind_ops[i].size);
1070          }
1071       }
1072 
1073       if (ops[i].type == PAN_KMOD_VM_OP_TYPE_UNMAP && auto_va && !async &&
1074           !ret) {
1075          panthor_kmod_vm_free_va(panthor_vm, bind_ops[i].va, bind_ops[i].size);
1076       }
1077    }
1078 
1079    if (bind_ops != bind_ops_storage)
1080       pan_kmod_dev_free(vm->dev, bind_ops);
1081 
1082 out_free_sync_ops:
1083    if (sync_ops != sync_ops_storage)
1084       pan_kmod_dev_free(vm->dev, sync_ops);
1085 
1086 out_free_va_collect:
1087    list_for_each_entry_safe(struct panthor_kmod_va_collect, va_collect,
1088                             &va_collect_list, node) {
1089       list_del(&va_collect->node);
1090       pan_kmod_dev_free(vm->dev, va_collect);
1091    }
1092 
1093    return ret;
1094 }
1095 
1096 static enum pan_kmod_vm_state
panthor_kmod_vm_query_state(struct pan_kmod_vm * vm)1097 panthor_kmod_vm_query_state(struct pan_kmod_vm *vm)
1098 {
1099    struct drm_panthor_vm_get_state query = {.vm_id = vm->handle};
1100    int ret = drmIoctl(vm->dev->fd, DRM_IOCTL_PANTHOR_VM_GET_STATE, &query);
1101 
1102    if (ret || query.state == DRM_PANTHOR_VM_STATE_UNUSABLE)
1103       return PAN_KMOD_VM_FAULTY;
1104 
1105    return PAN_KMOD_VM_USABLE;
1106 }
1107 
1108 uint32_t
panthor_kmod_vm_sync_handle(struct pan_kmod_vm * vm)1109 panthor_kmod_vm_sync_handle(struct pan_kmod_vm *vm)
1110 {
1111    struct panthor_kmod_vm *panthor_vm =
1112       container_of(vm, struct panthor_kmod_vm, base);
1113 
1114    assert(vm->flags & PAN_KMOD_VM_FLAG_TRACK_ACTIVITY);
1115    return panthor_vm->sync.handle;
1116 }
1117 
1118 uint64_t
panthor_kmod_vm_sync_lock(struct pan_kmod_vm * vm)1119 panthor_kmod_vm_sync_lock(struct pan_kmod_vm *vm)
1120 {
1121    struct panthor_kmod_vm *panthor_vm =
1122       container_of(vm, struct panthor_kmod_vm, base);
1123 
1124    assert(vm->flags & PAN_KMOD_VM_FLAG_TRACK_ACTIVITY);
1125 
1126    simple_mtx_lock(&panthor_vm->sync.lock);
1127    return panthor_vm->sync.point;
1128 }
1129 
1130 void
panthor_kmod_vm_sync_unlock(struct pan_kmod_vm * vm,uint64_t new_sync_point)1131 panthor_kmod_vm_sync_unlock(struct pan_kmod_vm *vm, uint64_t new_sync_point)
1132 {
1133    struct panthor_kmod_vm *panthor_vm =
1134       container_of(vm, struct panthor_kmod_vm, base);
1135 
1136    assert(vm->flags & PAN_KMOD_VM_FLAG_TRACK_ACTIVITY);
1137    assert(new_sync_point >= panthor_vm->sync.point);
1138 
1139    /* Check that the new syncpoint has a fence attached to it. */
1140    assert(new_sync_point == panthor_vm->sync.point ||
1141           drmSyncobjTimelineWait(
1142              vm->dev->fd, &panthor_vm->sync.handle, &new_sync_point, 1, 0,
1143              DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE, NULL) >= 0);
1144 
1145    panthor_vm->sync.point = new_sync_point;
1146    simple_mtx_unlock(&panthor_vm->sync.lock);
1147 }
1148 
1149 uint32_t
panthor_kmod_get_flush_id(const struct pan_kmod_dev * dev)1150 panthor_kmod_get_flush_id(const struct pan_kmod_dev *dev)
1151 {
1152    struct panthor_kmod_dev *panthor_dev =
1153       container_of(dev, struct panthor_kmod_dev, base);
1154 
1155    return *(panthor_dev->flush_id);
1156 }
1157 
1158 const struct drm_panthor_csif_info *
panthor_kmod_get_csif_props(const struct pan_kmod_dev * dev)1159 panthor_kmod_get_csif_props(const struct pan_kmod_dev *dev)
1160 {
1161    struct panthor_kmod_dev *panthor_dev =
1162       container_of(dev, struct panthor_kmod_dev, base);
1163 
1164    return &panthor_dev->props.csif;
1165 }
1166 
1167 static uint64_t
panthor_kmod_query_timestamp(const struct pan_kmod_dev * dev)1168 panthor_kmod_query_timestamp(const struct pan_kmod_dev *dev)
1169 {
1170    if (dev->driver.version.major <= 1 && dev->driver.version.minor < 1)
1171       return 0;
1172 
1173    struct drm_panthor_timestamp_info timestamp_info;
1174 
1175    struct drm_panthor_dev_query query = (struct drm_panthor_dev_query){
1176       .type = DRM_PANTHOR_DEV_QUERY_TIMESTAMP_INFO,
1177       .size = sizeof(timestamp_info),
1178       .pointer = (uint64_t)(uintptr_t)&timestamp_info,
1179    };
1180 
1181    int ret = drmIoctl(dev->fd, DRM_IOCTL_PANTHOR_DEV_QUERY, &query);
1182    if (ret) {
1183       mesa_loge("DRM_IOCTL_PANTHOR_DEV_QUERY failed (err=%d)", errno);
1184       return 0;
1185    }
1186 
1187    return timestamp_info.current_timestamp;
1188 }
1189 
1190 const struct pan_kmod_ops panthor_kmod_ops = {
1191    .dev_create = panthor_kmod_dev_create,
1192    .dev_destroy = panthor_kmod_dev_destroy,
1193    .dev_query_props = panthor_dev_query_props,
1194    .dev_query_user_va_range = panthor_kmod_dev_query_user_va_range,
1195    .bo_alloc = panthor_kmod_bo_alloc,
1196    .bo_free = panthor_kmod_bo_free,
1197    .bo_import = panthor_kmod_bo_import,
1198    .bo_export = panthor_kmod_bo_export,
1199    .bo_get_mmap_offset = panthor_kmod_bo_get_mmap_offset,
1200    .bo_wait = panthor_kmod_bo_wait,
1201    .vm_create = panthor_kmod_vm_create,
1202    .vm_destroy = panthor_kmod_vm_destroy,
1203    .vm_bind = panthor_kmod_vm_bind,
1204    .vm_query_state = panthor_kmod_vm_query_state,
1205    .query_timestamp = panthor_kmod_query_timestamp,
1206 };
1207