Module: Mesa
Branch: main
Commit: d95ec56f8c6884e0ae975b683fe7249fab9e740d
URL:    
http://cgit.freedesktop.org/mesa/mesa/commit/?id=d95ec56f8c6884e0ae975b683fe7249fab9e740d

Author: Boris Brezillon <boris.brezil...@collabora.com>
Date:   Mon Jul  3 12:01:11 2023 +0200

panfrost: Abstract kernel driver operations

We have generic BO management and device management layers that
directly call kernel driver-specific ioctls. The introduction of
Panthor (the new kernel driver supporting CSF hardware) forces us to
abstract some low-level operations. This could be done directly in
pan_{bo,device,props}.{c,h}, but having the abstraction clearly defined
and separated from the rest of the code makes for a cleaner
implementation.

This is also a good way to get a low-level KMD abstraction that
we can use without pulling all sort of gallium-related details in,
which will be important for some refactoring we plan to do in panvk.

Signed-off-by: Boris Brezillon <boris.brezil...@collabora.com>
Reviewed-by: Erik Faye-Lund <erik.faye-l...@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/26357>

---

 src/panfrost/ci/gitlab-ci.yml            |   1 +
 src/panfrost/lib/kmod/meson.build        |  38 ++
 src/panfrost/lib/kmod/pan_kmod.c         | 218 +++++++++++
 src/panfrost/lib/kmod/pan_kmod.h         | 619 +++++++++++++++++++++++++++++++
 src/panfrost/lib/kmod/pan_kmod_backend.h | 117 ++++++
 src/panfrost/lib/meson.build             |   5 +-
 6 files changed, 996 insertions(+), 2 deletions(-)

diff --git a/src/panfrost/ci/gitlab-ci.yml b/src/panfrost/ci/gitlab-ci.yml
index 18be6b04b82..8267c92e17c 100644
--- a/src/panfrost/ci/gitlab-ci.yml
+++ b/src/panfrost/ci/gitlab-ci.yml
@@ -17,6 +17,7 @@
         - src/panfrost/ci/$PIGLIT_TRACES_FILE
         - src/panfrost/include/*
         - src/panfrost/lib/*
+        - src/panfrost/lib/kmod/*
         - src/panfrost/shared/*
         - src/panfrost/util/*
       when: on_success
diff --git a/src/panfrost/lib/kmod/meson.build 
b/src/panfrost/lib/kmod/meson.build
new file mode 100644
index 00000000000..8966d0a5c09
--- /dev/null
+++ b/src/panfrost/lib/kmod/meson.build
@@ -0,0 +1,38 @@
+# Copyright © 2023 Collabora
+
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+libpankmod_lib_files = files(
+  'pan_kmod.c',
+)
+
+libpankmod_lib = static_library(
+  'pankmod_lib',
+  [libpankmod_lib_files],
+  include_directories : [inc_include, inc_src, inc_panfrost],
+  c_args : [no_override_init_args],
+  gnu_symbol_visibility : 'hidden',
+  dependencies: [dep_libdrm, idep_mesautil],
+  build_by_default : false,
+)
+
+libpankmod_dep = declare_dependency(
+  include_directories: [inc_include, inc_src],
+  dependencies: [dep_libdrm],
+)
diff --git a/src/panfrost/lib/kmod/pan_kmod.c b/src/panfrost/lib/kmod/pan_kmod.c
new file mode 100644
index 00000000000..fe333f4c8a9
--- /dev/null
+++ b/src/panfrost/lib/kmod/pan_kmod.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright © 2023 Collabora, Ltd.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include <string.h>
+#include <xf86drm.h>
+
+#include "util/macros.h"
+#include "pan_kmod.h"
+
+static const struct {
+   const char *name;
+   const struct pan_kmod_ops *ops;
+} drivers[] = {
+};
+
+static void *
+default_zalloc(const struct pan_kmod_allocator *allocator, size_t size,
+               UNUSED bool transient)
+{
+   return rzalloc_size(allocator, size);
+}
+
+static void
+default_free(const struct pan_kmod_allocator *allocator, void *data)
+{
+   return ralloc_free(data);
+}
+
+static const struct pan_kmod_allocator *
+create_default_allocator(void)
+{
+   struct pan_kmod_allocator *allocator =
+      rzalloc(NULL, struct pan_kmod_allocator);
+
+   if (allocator) {
+      allocator->zalloc = default_zalloc;
+      allocator->free = default_free;
+   }
+
+   return allocator;
+}
+
+struct pan_kmod_dev *
+pan_kmod_dev_create(int fd, uint32_t flags,
+                    const struct pan_kmod_allocator *allocator)
+{
+   drmVersionPtr version = drmGetVersion(fd);
+   struct pan_kmod_dev *dev = NULL;
+
+   if (!version)
+      return NULL;
+
+   if (!allocator) {
+      allocator = create_default_allocator();
+      if (!allocator)
+         goto out_free_version;
+   }
+
+   for (unsigned i = 0; i < ARRAY_SIZE(drivers); i++) {
+      if (!strcmp(drivers[i].name, version->name)) {
+         const struct pan_kmod_ops *ops = drivers[i].ops;
+
+         dev = ops->dev_create(fd, flags, version, allocator);
+         if (dev)
+            goto out_free_version;
+
+         break;
+      }
+   }
+
+   if (allocator->zalloc == default_zalloc)
+      ralloc_free((void *)allocator);
+
+out_free_version:
+   drmFreeVersion(version);
+   return dev;
+}
+
+void
+pan_kmod_dev_destroy(struct pan_kmod_dev *dev)
+{
+   const struct pan_kmod_allocator *allocator = dev->allocator;
+
+   dev->ops->dev_destroy(dev);
+
+   if (allocator->zalloc == default_zalloc)
+      ralloc_free((void *)allocator);
+}
+
+struct pan_kmod_bo *
+pan_kmod_bo_alloc(struct pan_kmod_dev *dev, struct pan_kmod_vm *exclusive_vm,
+                  size_t size, uint32_t flags)
+{
+   struct pan_kmod_bo *bo;
+
+   bo = dev->ops->bo_alloc(dev, exclusive_vm, size, flags);
+   if (!bo)
+      return NULL;
+
+   /* We intentionally don't take the lock when filling the sparse array,
+    * because we just created the BO, and haven't exported it yet, so
+    * there's no risk of imports racing with our BO insertion.
+    */
+   struct pan_kmod_bo **slot =
+      util_sparse_array_get(&dev->handle_to_bo.array, bo->handle);
+
+   if (!slot) {
+      mesa_loge("failed to allocate slot in the handle_to_bo array");
+      bo->dev->ops->bo_free(bo);
+      return NULL;
+   }
+
+   assert(*slot == NULL);
+   *slot = bo;
+   return bo;
+}
+
+void
+pan_kmod_bo_put(struct pan_kmod_bo *bo)
+{
+   if (!bo)
+      return;
+
+   int32_t refcnt = p_atomic_dec_return(&bo->refcnt);
+
+   assert(refcnt >= 0);
+
+   if (refcnt)
+      return;
+
+   struct pan_kmod_dev *dev = bo->dev;
+
+   simple_mtx_lock(&dev->handle_to_bo.lock);
+
+   /* If some import took a ref on this BO while we were trying to acquire the
+    * lock, skip the destruction.
+    */
+   if (!p_atomic_read(&bo->refcnt)) {
+      struct pan_kmod_bo **slot = (struct pan_kmod_bo **)util_sparse_array_get(
+         &dev->handle_to_bo.array, bo->handle);
+
+      assert(slot);
+      *slot = NULL;
+      bo->dev->ops->bo_free(bo);
+   }
+
+   simple_mtx_unlock(&dev->handle_to_bo.lock);
+}
+
+static bool
+pan_kmod_bo_check_import_flags(struct pan_kmod_bo *bo, uint32_t flags)
+{
+   uint32_t mask = PAN_KMOD_BO_FLAG_EXECUTABLE |
+                   PAN_KMOD_BO_FLAG_ALLOC_ON_FAULT | PAN_KMOD_BO_FLAG_NO_MMAP |
+                   PAN_KMOD_BO_FLAG_GPU_UNCACHED;
+
+   /* If the BO exists, make sure the import flags match the original flags. */
+   return (bo->flags & mask) == (flags & mask);
+}
+
+struct pan_kmod_bo *
+pan_kmod_bo_import(struct pan_kmod_dev *dev, int fd, uint32_t flags)
+{
+   struct pan_kmod_bo *bo = NULL;
+   struct pan_kmod_bo **slot;
+
+   simple_mtx_lock(&dev->handle_to_bo.lock);
+
+   uint32_t handle;
+   int ret = drmPrimeFDToHandle(dev->fd, fd, &handle);
+   if (ret)
+      goto err_unlock;
+
+   slot = util_sparse_array_get(&dev->handle_to_bo.array, handle);
+   if (!slot)
+      goto err_close_handle;
+
+   if (*slot) {
+      if (!pan_kmod_bo_check_import_flags(*slot, flags)) {
+         mesa_loge("invalid import flags");
+         goto err_unlock;
+      }
+
+      bo = *slot;
+
+      p_atomic_inc(&bo->refcnt);
+   } else {
+      size_t size = lseek(fd, 0, SEEK_END);
+      if (size == 0 || size == (size_t)-1) {
+         mesa_loge("invalid dmabuf size");
+         goto err_close_handle;
+      }
+
+      bo = dev->ops->bo_import(dev, handle, size, flags);
+      if (!bo)
+         goto err_close_handle;
+
+      *slot = bo;
+   }
+
+   assert(p_atomic_read(&bo->refcnt) > 0);
+
+   simple_mtx_unlock(&dev->handle_to_bo.lock);
+
+   return bo;
+
+err_close_handle:
+   drmCloseBufferHandle(dev->fd, handle);
+
+err_unlock:
+   simple_mtx_unlock(&dev->handle_to_bo.lock);
+
+   return NULL;
+}
+
diff --git a/src/panfrost/lib/kmod/pan_kmod.h b/src/panfrost/lib/kmod/pan_kmod.h
new file mode 100644
index 00000000000..7b991c90cab
--- /dev/null
+++ b/src/panfrost/lib/kmod/pan_kmod.h
@@ -0,0 +1,619 @@
+/*
+ * Copyright © 2023 Collabora, Ltd.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * This file exposes some core KMD functionalities in a driver-agnostic way.
+ * The drivers are still assumed to be regular DRM drivers, such that some
+ * operations can be handled generically.
+ *
+ * Any operation that's too specific to be abstracted can either have a backend
+ * specific helper exposed through pan_kmod_<backend>.h, or no helper at all
+ * (in the latter case, users are expected to call the ioctl directly).
+ *
+ * If some operations are not natively supported by a KMD, the kmod backend
+ * should fail or emulate the functionality (if deemed necessary).
+ */
+
+#pragma once
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <xf86drm.h>
+
+#include "drm-uapi/drm.h"
+
+#include "util/log.h"
+#include "util/macros.h"
+#include "util/os_file.h"
+#include "util/os_mman.h"
+#include "util/ralloc.h"
+#include "util/simple_mtx.h"
+#include "util/sparse_array.h"
+#include "util/u_atomic.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct pan_kmod_dev;
+
+/* GPU VM creation flags. */
+enum pan_kmod_vm_flags {
+   /* Set if you want the VM to automatically assign virtual addresses when
+    * pan_kmod_vm_map(). If this flag is set, all pan_kmod_vm_map() calls
+    * must have va=PAN_KMOD_VM_MAP_AUTO_VA.
+    */
+   PAN_KMOD_VM_FLAG_AUTO_VA = BITFIELD_BIT(0),
+};
+
+/* Object representing a GPU VM. */
+struct pan_kmod_vm {
+   /* Combination of pan_kmod_vm_flags flags. */
+   uint32_t flags;
+
+   /* The VM handle returned by the KMD. If the KMD supports only one VM per
+    * context, this should be zero.
+    */
+   uint32_t handle;
+
+   /* Device this VM was created from. */
+   struct pan_kmod_dev *dev;
+};
+
+/* Buffer object flags. */
+enum pan_kmod_bo_flags {
+   /* Allow GPU execution on this buffer. */
+   PAN_KMOD_BO_FLAG_EXECUTABLE = BITFIELD_BIT(0),
+
+   /* Allocate memory when a GPU fault occurs instead of allocating
+    * up-front.
+    */
+   PAN_KMOD_BO_FLAG_ALLOC_ON_FAULT = BITFIELD_BIT(1),
+
+   /* If set, the buffer object will never be CPU-mapped in userspace. */
+   PAN_KMOD_BO_FLAG_NO_MMAP = BITFIELD_BIT(2),
+
+   /* Set when the buffer object has been exported. Users don't directly
+    * control this flag, it's set when pan_kmod_bo_export() is called.
+    */
+   PAN_KMOD_BO_FLAG_EXPORTED = BITFIELD_BIT(3),
+
+   /* Set when the buffer object has been impported. Users don't directly
+    * control this flag, it's set when pan_kmod_bo_import() is called.
+    */
+   PAN_KMOD_BO_FLAG_IMPORTED = BITFIELD_BIT(4),
+
+   /* If set, the buffer in mapped GPU-uncached when pan_kmod_vm_map()
+    * is called.
+    */
+   PAN_KMOD_BO_FLAG_GPU_UNCACHED = BITFIELD_BIT(5),
+};
+
+/* Buffer object. */
+struct pan_kmod_bo {
+   /* Atomic reference count. The only reason we need to refcnt BOs at this
+    * level is because of how DRM prime import works: the import logic
+    * returns the handle of an existing object if the object was previously
+    * imported or was created by the driver.
+    * In order to prevent call GEM_CLOSE on an object that's still supposed
+    * to be active, we need count the number of users left.
+    */
+   int32_t refcnt;
+
+   /* Size of the buffer object. */
+   size_t size;
+
+   /* Handle attached to the buffer object. */
+   uint32_t handle;
+
+   /* Combination of pan_kmod_bo_flags flags. */
+   uint32_t flags;
+
+   /* If non-NULL, the buffer object can only by mapped on this VM. Typical
+    * the case for all internal/non-shareable buffers. The backend can
+    * optimize things based on this information. Calling pan_kmod_bo_export()
+    * on such buffer objects is forbidden.
+    */
+   struct pan_kmod_vm *exclusive_vm;
+
+   /* The device this buffer object was created from. */
+   struct pan_kmod_dev *dev;
+
+   /* User private data. Use pan_kmod_bo_{set,get}_user_priv() to access it. */
+   void *user_priv;
+};
+
+/* List of GPU properties needed by the UMD. */
+struct pan_kmod_dev_props {
+   /* GPU product ID. */
+   uint32_t gpu_prod_id;
+
+   /* GPU revision. */
+   uint32_t gpu_revision;
+
+   /* Bitmask encoding the number of shader cores exposed by the GPU. */
+   uint64_t shader_present;
+
+   /* Tiler features bits. */
+   uint32_t tiler_features;
+
+   /* Memory related feature bits. */
+   uint32_t mem_features;
+
+   /* MMU feature bits. */
+   uint32_t mmu_features;
+#define MMU_FEATURES_VA_BITS(mmu_features) (mmu_features & 0xff)
+
+   /* Texture feature bits. */
+   uint32_t texture_features[4];
+
+   /* Maximum number of threads per core. */
+   uint32_t thread_tls_alloc;
+
+   /* AFBC feature bits. */
+   uint32_t afbc_features;
+};
+
+/* Memory allocator for kmod internal allocations. */
+struct pan_kmod_allocator {
+   /* Allocate and set to zero. */
+   void *(*zalloc)(const struct pan_kmod_allocator *allocator, size_t size,
+                   bool transient);
+
+   /* Free. */
+   void (*free)(const struct pan_kmod_allocator *allocator, void *data);
+
+   /* Private data allocator data. Can be NULL if unused. */
+   void *priv;
+};
+
+/* Synchronization type. */
+enum pan_kmod_sync_type {
+   PAN_KMOD_SYNC_TYPE_WAIT = 0,
+   PAN_KMOD_SYNC_TYPE_SIGNAL,
+};
+
+/* Synchronization operation. */
+struct pan_kmod_sync_op {
+   /* Type of operation. */
+   enum pan_kmod_sync_type type;
+
+   /* Syncobj handle. */
+   uint32_t handle;
+
+   /* Syncobj point. Zero for binary syncobjs. */
+   uint64_t point;
+};
+
+/* Special value passed to pan_kmod_vm_map() to signify the VM it should
+ * automatically allocate a VA. Only valid if the VM was created with
+ * PAN_KMOD_VM_FLAG_AUTO_VA.
+ */
+#define PAN_KMOD_VM_MAP_AUTO_VA ~0ull
+
+/* Special value return when the vm_map() operation failed. */
+#define PAN_KMOD_VM_MAP_FAILED ~0ull
+
+/* VM operations can be executed in different modes. */
+enum pan_kmod_vm_op_mode {
+   /* The map/unmap operation is executed immediately, which might cause
+    * GPU faults if the GPU was still accessing buffers when we unmap or
+    * remap.
+    */
+   PAN_KMOD_VM_OP_MODE_IMMEDIATE,
+
+   /* The map/unmap operation is executed asynchronously, and the user
+    * provides explicit wait/signal sync operations.
+    */
+   PAN_KMOD_VM_OP_MODE_ASYNC,
+
+   /* The map/unmap operation is executed when the next GPU/VM idle-point
+    * is reached. This guarantees fault-free unmap/remap operations when the
+    * kmod user doesn't want to deal with synchronizations explicitly.
+    */
+   PAN_KMOD_VM_OP_MODE_DEFER_TO_NEXT_IDLE_POINT,
+};
+
+/* VM operation type. */
+enum pan_kmod_vm_op_type {
+   /* Map a buffer object. */
+   PAN_KMOD_VM_OP_TYPE_MAP,
+
+   /* Unmap a VA range. */
+   PAN_KMOD_VM_OP_TYPE_UNMAP,
+
+   /* Do nothing. Used as a way to execute sync operations on a VM queue,
+    * without touching the VM.
+    */
+   PAN_KMOD_VM_OP_TYPE_SYNC_ONLY,
+};
+
+/* VM operation data. */
+struct pan_kmod_vm_op {
+   /* The type of operation being requested. */
+   enum pan_kmod_vm_op_type type;
+
+   /* VA range. */
+   struct {
+      /* Start of the VA range.
+       * Must be PAN_KMOD_VM_MAP_AUTO_VA if PAN_KMOD_VM_FLAG_AUTO_VA was set
+       * at VM creation time. In that case, the allocated VA is returned
+       * in this field.
+       */
+      uint64_t start;
+
+      /* Size of the VA range */
+      size_t size;
+   } va;
+
+   union {
+      /* Arguments specific to map operations. */
+      struct {
+         /* Buffer object to map. */
+         struct pan_kmod_bo *bo;
+
+         /* Offset in the buffer object. */
+         off_t bo_offset;
+      } map;
+   };
+
+   /* Synchronization operations attached to the VM operation. */
+   struct {
+      /* Number of synchronization operations. Must be zero if mode is
+       * PAN_KMOD_VM_OP_MODE_IMMEDIATE or PAN_KMOD_VM_OP_MODE_WAIT_IDLE.
+       */
+      uint32_t count;
+
+      /* Array of synchronization operation descriptors. NULL if count is 
zero. */
+      const struct pan_kmod_sync_op *array;
+   } syncs;
+};
+
+/* VM state. */
+enum pan_kmod_vm_state {
+   PAN_KMOD_VM_USABLE,
+   PAN_KMOD_VM_FAULTY,
+};
+
+/* Device flags. */
+enum pan_kmod_dev_flags {
+   /* Set when the fd passed to pan_kmod_create() is expected to be
+    * owned by the device, iff the device creation succeeded.
+    */
+   PAN_KMOD_DEV_FLAG_OWNS_FD = (1 << 0),
+};
+
+/* Encode a virtual address range. */
+struct pan_kmod_va_range {
+   /* Start of the VA range. */
+   uint64_t start;
+
+   /* Size of the VA range. */
+   uint64_t size;
+};
+
+/* KMD backend vtable.
+ *
+ * All methods described there are mandatory, unless explicitly flagged as
+ * optional.
+ */
+struct pan_kmod_ops {
+   /* Create a pan_kmod_dev object.
+    * Return NULL if the creation fails for any reason.
+    */
+   struct pan_kmod_dev *(*dev_create)(
+      int fd, uint32_t flags, const drmVersionPtr version,
+      const struct pan_kmod_allocator *allocator);
+
+   /* Destroy a pan_kmod_dev object. */
+   void (*dev_destroy)(struct pan_kmod_dev *dev);
+
+   /* Query device properties. */
+   void (*dev_query_props)(const struct pan_kmod_dev *dev,
+                           struct pan_kmod_dev_props *props);
+
+   /* Query the maxium user VA range.
+    * Users are free to use a subset of this range if they need less VA space.
+    * This method is optional, when not specified, kmod assumes the whole VA
+    * space (extracted from MMU_FEATURES.VA_BITS) is usable.
+    */
+   struct pan_kmod_va_range (*dev_query_user_va_range)(
+      const struct pan_kmod_dev *dev);
+
+   /* Allocate a buffer object.
+    * Return NULL if the creation fails for any reason.
+    */
+   struct pan_kmod_bo *(*bo_alloc)(struct pan_kmod_dev *dev,
+                                   struct pan_kmod_vm *exclusive_vm,
+                                   size_t size, uint32_t flags);
+
+   /* Free buffer object. */
+   void (*bo_free)(struct pan_kmod_bo *bo);
+
+   /* Import a buffer object.
+    * Return NULL if the import fails for any reason.
+    */
+   struct pan_kmod_bo *(*bo_import)(struct pan_kmod_dev *dev, uint32_t handle,
+                                    size_t size, uint32_t flags);
+
+   /* Post export operations.
+    * Return 0 on success, -1 otherwise.
+    * This method is optional.
+    */
+   int (*bo_export)(struct pan_kmod_bo *bo, int dmabuf_fd);
+
+   /* Get the file offset to use to mmap() a buffer object. */
+   off_t (*bo_get_mmap_offset)(struct pan_kmod_bo *bo);
+
+   /* Wait for a buffer object to be ready for read or read/write accesses. */
+   bool (*bo_wait)(struct pan_kmod_bo *bo, int64_t timeout_ns,
+                   bool for_read_only_access);
+
+   /* Make a buffer object evictable. This method is optional. */
+   void (*bo_make_evictable)(struct pan_kmod_bo *bo);
+
+   /* Make the buffer object unevictable. This method is optional. */
+   bool (*bo_make_unevictable)(struct pan_kmod_bo *bo);
+
+   /* Create a VM object. */
+   struct pan_kmod_vm *(*vm_create)(struct pan_kmod_dev *dev, uint32_t flags,
+                                    uint64_t va_start, uint64_t va_range);
+
+   /* Destroy a VM object. */
+   void (*vm_destroy)(struct pan_kmod_vm *vm);
+
+   /* Execute VM operations.
+    * Return 0 if the submission suceeds, -1 otherwise.
+    * For PAN_KMOD_VM_OP_MODE_IMMEDIATE submissions, the return value also
+    * reflects the successfulness of the VM operation, for other modes,
+    * if any of the VM operation fails, the VM might be flagged as unusable
+    * and users should create a new VM to recover.
+    */
+   int (*vm_bind)(struct pan_kmod_vm *vm, enum pan_kmod_vm_op_mode mode,
+                  struct pan_kmod_vm_op *ops, uint32_t op_count);
+
+   /* Query the VM state.
+    * This method is optional. When missing the VM is assumed to always be
+    * usable.
+    */
+   enum pan_kmod_vm_state (*vm_query_state)(struct pan_kmod_vm *vm);
+};
+
+/* KMD information. */
+struct pan_kmod_driver {
+   /* KMD version. */
+   struct {
+      uint32_t major;
+      uint32_t minor;
+   } version;
+};
+
+/* Device object. */
+struct pan_kmod_dev {
+   /* FD attached to the device. */
+   int fd;
+
+   /* Device flags. */
+   uint32_t flags;
+
+   /* KMD backing this device. */
+   struct pan_kmod_driver driver;
+
+   /* kmod backend ops assigned at device creation. */
+   const struct pan_kmod_ops *ops;
+
+   /* DRM prime import returns the handle of a pre-existing GEM if we are
+    * importing an object that was created by us or previously imported.
+    * We need to make sure we return the same pan_kmod_bo in that case,
+    * otherwise freeing one pan_kmod_bo will make all other BOs sharing
+    * the same handle invalid.
+    */
+   struct {
+      struct util_sparse_array array;
+      simple_mtx_t lock;
+   } handle_to_bo;
+
+   /* Allocator attached to the device. */
+   const struct pan_kmod_allocator *allocator;
+
+   /* User private data. Use pan_kmod_dev_{set,get}_user_priv() to access it. 
*/
+   void *user_priv;
+};
+
+struct pan_kmod_dev *
+pan_kmod_dev_create(int fd, uint32_t flags,
+                    const struct pan_kmod_allocator *allocator);
+
+void pan_kmod_dev_destroy(struct pan_kmod_dev *dev);
+
+static inline void
+pan_kmod_dev_query_props(const struct pan_kmod_dev *dev,
+                         struct pan_kmod_dev_props *props)
+{
+   dev->ops->dev_query_props(dev, props);
+}
+
+static inline struct pan_kmod_va_range
+pan_kmod_dev_query_user_va_range(const struct pan_kmod_dev *dev)
+{
+   if (dev->ops->dev_query_user_va_range)
+      return dev->ops->dev_query_user_va_range(dev);
+
+   struct pan_kmod_dev_props props;
+
+   pan_kmod_dev_query_props(dev, &props);
+   return (struct pan_kmod_va_range){
+      .start = 0,
+      .size = 1ull << MMU_FEATURES_VA_BITS(props.mmu_features),
+   };
+}
+
+static inline void
+pan_kmod_dev_set_user_priv(struct pan_kmod_dev *dev, void *data)
+{
+   dev->user_priv = data;
+}
+
+static inline void *
+pan_kmod_dev_get_user_priv(struct pan_kmod_dev *dev)
+{
+   return dev->user_priv;
+}
+
+struct pan_kmod_bo *pan_kmod_bo_alloc(struct pan_kmod_dev *dev,
+                                      struct pan_kmod_vm *exclusive_vm,
+                                      size_t size, uint32_t flags);
+
+static inline struct pan_kmod_bo *
+pan_kmod_bo_get(struct pan_kmod_bo *bo)
+{
+   if (!bo)
+      return NULL;
+
+   ASSERTED int32_t refcnt = p_atomic_inc_return(&bo->refcnt);
+
+   /* If refcnt was zero before our increment, we're in trouble. */
+   assert(refcnt > 1);
+
+   return bo;
+}
+
+void pan_kmod_bo_put(struct pan_kmod_bo *bo);
+
+static inline void *
+pan_kmod_bo_cmdxchg_user_priv(struct pan_kmod_bo *bo, void *old_data,
+                              void *new_data)
+{
+   return (void *)p_atomic_cmpxchg((uintptr_t *)&bo->user_priv,
+                                   (uintptr_t)old_data, (uintptr_t)new_data);
+}
+
+static inline void
+pan_kmod_bo_set_user_priv(struct pan_kmod_bo *bo, void *data)
+{
+   bo->user_priv = data;
+}
+
+static inline void *
+pan_kmod_bo_get_user_priv(const struct pan_kmod_bo *bo)
+{
+   return bo->user_priv;
+}
+
+struct pan_kmod_bo *pan_kmod_bo_import(struct pan_kmod_dev *dev, int fd,
+                                       uint32_t flags);
+
+static inline int
+pan_kmod_bo_export(struct pan_kmod_bo *bo)
+{
+   int fd;
+
+   if (drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC, &fd)) {
+      mesa_loge("drmPrimeHandleToFD() failed (err=%d)", errno);
+      return -1;
+   }
+
+   if (bo->dev->ops->bo_export && bo->dev->ops->bo_export(bo, fd)) {
+      close(fd);
+      return -1;
+   }
+
+   bo->flags |= PAN_KMOD_BO_FLAG_EXPORTED;
+   return fd;
+}
+
+static inline bool
+pan_kmod_bo_wait(struct pan_kmod_bo *bo, int64_t timeout_ns,
+                 bool for_read_only_access)
+{
+   return bo->dev->ops->bo_wait(bo, timeout_ns, for_read_only_access);
+}
+
+static inline void
+pan_kmod_bo_make_evictable(struct pan_kmod_bo *bo)
+{
+   if (bo->dev->ops->bo_make_evictable)
+      bo->dev->ops->bo_make_evictable(bo);
+}
+
+static inline bool
+pan_kmod_bo_make_unevictable(struct pan_kmod_bo *bo)
+{
+   if (bo->dev->ops->bo_make_unevictable)
+      return bo->dev->ops->bo_make_unevictable(bo);
+
+   return true;
+}
+
+static inline void *
+pan_kmod_bo_mmap(struct pan_kmod_bo *bo, off_t bo_offset, size_t size, int 
prot,
+                 int flags, void *host_addr)
+{
+   off_t mmap_offset;
+
+   if (bo_offset + size > bo->size)
+      return MAP_FAILED;
+
+   mmap_offset = bo->dev->ops->bo_get_mmap_offset(bo);
+   if (mmap_offset < 0)
+      return MAP_FAILED;
+
+   host_addr = os_mmap(host_addr, size, prot, flags, bo->dev->fd,
+                       mmap_offset + bo_offset);
+   if (host_addr == MAP_FAILED)
+      mesa_loge("mmap() failed (err=%d)", errno);
+
+   return host_addr;
+}
+
+static inline size_t
+pan_kmod_bo_size(struct pan_kmod_bo *bo)
+{
+   return bo->size;
+}
+
+static inline uint32_t
+pan_kmod_bo_handle(struct pan_kmod_bo *bo)
+{
+   return bo->handle;
+}
+
+static inline struct pan_kmod_vm *
+pan_kmod_vm_create(struct pan_kmod_dev *dev, uint32_t flags, uint64_t va_start,
+                   uint64_t va_range)
+{
+   return dev->ops->vm_create(dev, flags, va_start, va_range);
+}
+
+static inline void
+pan_kmod_vm_destroy(struct pan_kmod_vm *vm)
+{
+   vm->dev->ops->vm_destroy(vm);
+}
+
+static inline int
+pan_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)
+{
+   return vm->dev->ops->vm_bind(vm, mode, ops, op_count);
+}
+
+static inline enum pan_kmod_vm_state
+pan_kmod_vm_query_state(struct pan_kmod_vm *vm)
+{
+   if (vm->dev->ops->vm_query_state)
+      return vm->dev->ops->vm_query_state(vm);
+
+   return PAN_KMOD_VM_USABLE;
+}
+
+static inline uint32_t
+pan_kmod_vm_handle(struct pan_kmod_vm *vm)
+{
+   return vm->handle;
+}
+
+#if defined(__cplusplus)
+} // extern "C"
+#endif
diff --git a/src/panfrost/lib/kmod/pan_kmod_backend.h 
b/src/panfrost/lib/kmod/pan_kmod_backend.h
new file mode 100644
index 00000000000..f64e4a0c1c3
--- /dev/null
+++ b/src/panfrost/lib/kmod/pan_kmod_backend.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright © 2023 Collabora, Ltd.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#pragma once
+
+#include "util/log.h"
+
+#include "pan_kmod.h"
+
+static inline void
+pan_kmod_dev_init(struct pan_kmod_dev *dev, int fd, uint32_t flags,
+                  drmVersionPtr version, const struct pan_kmod_ops *ops,
+                  const struct pan_kmod_allocator *allocator)
+{
+   simple_mtx_init(&dev->handle_to_bo.lock, mtx_plain);
+   util_sparse_array_init(&dev->handle_to_bo.array,
+                          sizeof(struct pan_kmod_bo *), 512);
+   dev->driver.version.major = version->version_major;
+   dev->driver.version.minor = version->version_minor;
+   dev->fd = fd;
+   dev->flags = flags;
+   dev->ops = ops;
+   dev->allocator = allocator;
+}
+
+static inline void
+pan_kmod_dev_cleanup(struct pan_kmod_dev *dev)
+{
+   if (dev->flags & PAN_KMOD_DEV_FLAG_OWNS_FD)
+      close(dev->fd);
+
+   util_sparse_array_finish(&dev->handle_to_bo.array);
+   simple_mtx_destroy(&dev->handle_to_bo.lock);
+}
+
+static inline void *
+pan_kmod_alloc(const struct pan_kmod_allocator *allocator, size_t size)
+{
+   return allocator->zalloc(allocator, size, false);
+}
+
+static inline void *
+pan_kmod_alloc_transient(const struct pan_kmod_allocator *allocator,
+                         size_t size)
+{
+   return allocator->zalloc(allocator, size, true);
+}
+
+static inline void
+pan_kmod_free(const struct pan_kmod_allocator *allocator, void *data)
+{
+   return allocator->free(allocator, data);
+}
+
+static inline void *
+pan_kmod_dev_alloc(struct pan_kmod_dev *dev, size_t size)
+{
+   return pan_kmod_alloc(dev->allocator, size);
+}
+
+static inline void *
+pan_kmod_dev_alloc_transient(struct pan_kmod_dev *dev, size_t size)
+{
+   return pan_kmod_alloc_transient(dev->allocator, size);
+}
+
+static inline void
+pan_kmod_dev_free(const struct pan_kmod_dev *dev, void *data)
+{
+   return pan_kmod_free(dev->allocator, data);
+}
+
+static inline void
+pan_kmod_bo_init(struct pan_kmod_bo *bo, struct pan_kmod_dev *dev,
+                 struct pan_kmod_vm *exclusive_vm, size_t size, uint32_t flags,
+                 uint32_t handle)
+{
+   bo->dev = dev;
+   bo->exclusive_vm = exclusive_vm;
+   bo->size = size;
+   bo->flags = flags;
+   bo->handle = handle;
+   p_atomic_set(&bo->refcnt, 1);
+}
+
+static inline void
+pan_kmod_vm_init(struct pan_kmod_vm *vm, struct pan_kmod_dev *dev,
+                 uint32_t handle, uint32_t flags)
+{
+   vm->dev = dev;
+   vm->handle = handle;
+   vm->flags = flags;
+}
+
+static inline int
+pank_kmod_vm_op_check(struct pan_kmod_vm *vm, enum pan_kmod_vm_op_mode mode,
+                      struct pan_kmod_vm_op *op)
+{
+   /* We should only have sync operations on an async VM bind request. */
+   if (mode != PAN_KMOD_VM_OP_MODE_ASYNC && op->syncs.count) {
+      mesa_loge("only PAN_KMOD_VM_OP_MODE_ASYNC can be passed sync 
operations");
+      return -1;
+   }
+
+   /* Make sure the PAN_KMOD_VM_FLAG_AUTO_VA and VA passed to the op match. */
+   if (op->type == PAN_KMOD_VM_OP_TYPE_MAP &&
+       !!(vm->flags & PAN_KMOD_VM_FLAG_AUTO_VA) !=
+          (op->va.start == PAN_KMOD_VM_MAP_AUTO_VA)) {
+      mesa_loge("op->va.start and vm->flags don't match");
+      return -1;
+   }
+
+   return 0;
+}
diff --git a/src/panfrost/lib/meson.build b/src/panfrost/lib/meson.build
index e27483e1280..12b927a2973 100644
--- a/src/panfrost/lib/meson.build
+++ b/src/panfrost/lib/meson.build
@@ -20,6 +20,7 @@
 # SOFTWARE.
 
 subdir('genxml')
+subdir('kmod')
 
 pixel_format_versions = ['6', '7', '9']
 libpanfrost_pixel_format = []
@@ -92,13 +93,13 @@ libpanfrost_lib = static_library(
   gnu_symbol_visibility : 'hidden',
   dependencies: [dep_libdrm, idep_nir, idep_mesautil],
   build_by_default : false,
-  link_with: [libpanfrost_pixel_format, libpanfrost_per_arch],
+  link_with: [libpanfrost_pixel_format, libpanfrost_per_arch, libpankmod_lib],
 )
 
 libpanfrost_dep = declare_dependency(
   link_with: [libpanfrost_lib, libpanfrost_decode, libpanfrost_midgard, 
libpanfrost_bifrost, libpanfrost_pixel_format, libpanfrost_per_arch],
   include_directories: [inc_include, inc_src, inc_mapi, inc_mesa, inc_gallium, 
inc_gallium_aux, inc_panfrost_hw, inc_panfrost],
-  dependencies: [dep_libdrm, idep_nir, idep_pan_packers],
+  dependencies: [dep_libdrm, libpankmod_dep, idep_nir, idep_pan_packers],
 )
 
 if with_tests

Reply via email to