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)×tamp_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