mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: "Christian König" <christian.koenig@amd.com>
To: Tomeu Vizoso <tomeu.vizoso@gmail.com>, Nishanth Menon <nm@ti.com>,
	"Andrew F. Davis" <afd@ti.com>, Randolph Sapp <rs@ti.com>,
	Jonathan Humphreys <j-humphreys@ti.com>,
	Andrei Aldea <a-aldea@ti.com>,
	Chirag Shilwant <c-shilwant@ti.com>,
	Vignesh Raghavendra <vigneshr@ti.com>,
	Beleswar Padhi <b-padhi@ti.com>, Tero Kristo <kristo@kernel.org>,
	Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>,
	Oded Gabbay <ogabbay@kernel.org>,
	Jonathan Corbet <corbet@lwn.net>,
	Sumit Semwal <sumit.semwal@linaro.org>,
	Robert Nelson <robertcnelson@gmail.com>,
	David Airlie <airlied@gmail.com>, Simona Vetter <simona@ffwll.ch>,
	Maarten Lankhorst <maarten.lankhorst@linux.intel.com>,
	Maxime Ripard <mripard@kernel.org>,
	Thomas Zimmermann <tzimmermann@suse.de>,
	Jani Nikula <jani.nikula@linux.intel.com>,
	Tvrtko Ursulin <tursulin@ursulin.net>,
	Markus Elfring <Markus.Elfring@web.de>,
	Bjorn Andersson <andersson@kernel.org>,
	Mathieu Poirier <mathieu.poirier@linaro.org>,
	Suman Anna <s-anna@ti.com>, Kees Cook <kees@kernel.org>,
	"Gustavo A. R. Silva" <gustavoars@kernel.org>,
	Shuah Khan <skhan@linuxfoundation.org>,
	Randy Dunlap <rdunlap@infradead.org>
Cc: linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org,
	linux-doc@vger.kernel.org, linux-media@vger.kernel.org,
	linaro-mm-sig@lists.linaro.org,
	Tomeu Vizoso <tomeu@tomeuvizoso.net>,
	linux-remoteproc@vger.kernel.org,
	linux-hardening@vger.kernel.org
Subject: Re: [PATCH v3 07/16] accel/thames: Add IOCTLs for BO creation and mapping
Date: Mon, 26 Oct 2026 11:07:02 +0100	[thread overview]
Message-ID: <93a8d4f5-fcbb-4fb1-a601-9503c215dfa6@amd.com> (raw)
In-Reply-To: <20261023-thames-v3-7-3ab40aa17157@tomeuvizoso.net>

On 10/23/26 08:39, Tomeu Vizoso wrote:
> Creation returns a handle and maps nothing. The client maps a buffer
> with DRM_IOCTL_THAMES_BO_BIND at a DSP address of its choice, as a
> Vulkan driver would; the kernel enters the mapping in the page table
> of every core, because we cannot know upfront which core will run a
> given job.
> 
> We use the SHMEM helpers for non-contiguous memory. Contiguous memory
> comes from the driver's own allocator, which aligns a buffer of 2 MiB or
> more to 2 MiB so it can be mapped as blocks; only the GEM DMA object
> and its mmap and vmap helpers are used for it.

How exactly do you do that? IIRC the SHMEM helpers can only do 2MiB allocations on a best effort basis.

In other words you don't get a guarantee that this always works and when memory is fragmented the 4k fallback must work as well.

Regards,
Christian.

> 
> Contiguous memory is needed for buffers that are going to be read from
> or written into by the DRU, as it doesn't resolve addresses via the MMU.
> 
> Binding at an address the client chooses lets a program be compiled,
> with its addresses fixed, on a machine without the hardware, and is what
> a Vulkan driver needs.
> 
> A buffer is bound anywhere from 4 GiB up to the user region at 1 TiB; a
> buffer shared through a dma-buf may be bound in several Contexts, so
> the bindings are kept under one device-wide lock. The IOCTLs take their
> arrays as versioned objects whose stride is the size userspace was
> built against, so a structure can grow under a minor bump.
> 
> Assisted-by: LLM
> Signed-off-by: Tomeu Vizoso <tomeu@tomeuvizoso.net>
> 
> ---
> 
> v3:
> - Added read-only, executable and contiguous flags.
> - Added BO_BIND IOCTL.
> - Copy the IOCTLs' arrays with vmemdup_array_user(), through one helper
>   for versioned arrays (Tvrtko Ursulin).
> 
> v2:
> - Add thames_accel.h UAPI header (Robert Nelson).
> ---
>  drivers/accel/thames/Kconfig         |   2 +
>  drivers/accel/thames/thames_device.h |   6 +
>  drivers/accel/thames/thames_drv.c    |  47 +++++
>  drivers/accel/thames/thames_drv.h    |   5 +
>  drivers/accel/thames/thames_gem.c    | 398 +++++++++++++++++++++++++++++++++++
>  drivers/accel/thames/thames_gem.h    |  21 ++
>  include/uapi/drm/thames_accel.h      | 183 +++++++++++++++-
>  7 files changed, 661 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/accel/thames/Kconfig b/drivers/accel/thames/Kconfig
> index 9362b9274075..47a2986fcabd 100644
> --- a/drivers/accel/thames/Kconfig
> +++ b/drivers/accel/thames/Kconfig
> @@ -6,6 +6,8 @@ config DRM_ACCEL_THAMES
>         depends on TI_K3_DSP_REMOTEPROC
>         depends on REMOTEPROC
>         depends on MAILBOX
> +       select DRM_GEM_DMA_HELPER
> +       select DRM_GEM_SHMEM_HELPER
>         select IOMMU_IO_PGTABLE_LPAE
>         help
>           Choose this option if you have a Texas Instruments J722S and want to
> diff --git a/drivers/accel/thames/thames_device.h b/drivers/accel/thames/thames_device.h
> index e89e0dfd89b8..a1a54d974359 100644
> --- a/drivers/accel/thames/thames_device.h
> +++ b/drivers/accel/thames/thames_device.h
> @@ -58,6 +58,12 @@ struct thames_device {
>         struct list_head contexts;
>         struct mutex contexts_lock;
> 
> +       /*
> +        * One lock for the device: a dma-buf shared buffer is bound from
> +        * several files.
> +        */
> +       struct mutex bindings_lock;
> +
>  };
> 
>  #define to_thames_device(drm_dev) \
> diff --git a/drivers/accel/thames/thames_drv.c b/drivers/accel/thames/thames_drv.c
> index cb0287430d25..a17e7a90f7c1 100644
> --- a/drivers/accel/thames/thames_drv.c
> +++ b/drivers/accel/thames/thames_drv.c
> @@ -93,6 +93,41 @@ static void thames_postclose(struct drm_device *dev, struct drm_file *file)
>         thames_file_priv_put(thames_priv);
>  }
> 
> +void *thames_get_uobj_array(const struct drm_thames_obj_array *in, u32 min_stride, u32 obj_size)
> +{
> +       void __user *in_ptr = u64_to_user_ptr(in->array);
> +       void *out, *out_ptr;
> +       int ret;
> +       u32 i;
> +
> +       if (!in->count)
> +               return NULL;
> +
> +       if (in->stride < min_stride)
> +               return ERR_PTR(-EINVAL);
> +
> +       if (in->stride == obj_size)
> +               return vmemdup_array_user(in_ptr, in->count, obj_size);
> +
> +       out = kvmalloc_array(in->count, obj_size, GFP_KERNEL);
> +       if (!out)
> +               return ERR_PTR(-ENOMEM);
> +
> +       out_ptr = out;
> +       for (i = 0; i < in->count; i++) {
> +               ret = copy_struct_from_user(out_ptr, obj_size, in_ptr, in->stride);
> +               if (ret) {
> +                       kvfree(out);
> +                       return ERR_PTR(ret);
> +               }
> +
> +               out_ptr += obj_size;
> +               in_ptr += in->stride;
> +       }
> +
> +       return out;
> +}
> +
>  #define THAMES_DRIVER_MAJOR 1
>  #define THAMES_DRIVER_MINOR 0
> 
> @@ -121,6 +156,8 @@ static int thames_query_caps(struct drm_device *dev, struct drm_thames_dev_query
>                 .l2_aux.dsp_va = thames_window_va(tdev, THAMES_WINDOW_L2AUX),
>                 .l2_aux.size = reported->window_sizes[THAMES_WINDOW_L2AUX],
>                 .dru_realtime_dsp_va = thames_window_va(tdev, THAMES_WINDOW_DRU),
> +               .dsp_va_range.dsp_va = THAMES_BIND_VA_START,
> +               .dsp_va_range.size = THAMES_BIND_VA_END - THAMES_BIND_VA_START,
>                 .user_region.dsp_va = THAMES_USER_REGION_BASE,
>                 .user_region.size = THAMES_USER_REGION_SIZE,
>                 .hardware_id = reported->hardware_id,
> @@ -165,11 +202,17 @@ static int thames_ioctl_##func##_entered(struct drm_device *dev, void *data,      \
>         return ret;                                                             \
>  }
> 
> +THAMES_IOCTL_ENTERED(bo_create)
> +THAMES_IOCTL_ENTERED(bo_mmap_offset)
>  THAMES_IOCTL_ENTERED(dev_query)
> +THAMES_IOCTL_ENTERED(bo_bind)
> 
>  static const struct drm_ioctl_desc thames_drm_driver_ioctls[] = {
>  #define THAMES_IOCTL(n, func) DRM_IOCTL_DEF_DRV(THAMES_##n, thames_ioctl_##func, 0)
> +       THAMES_IOCTL(BO_CREATE, bo_create_entered),
> +       THAMES_IOCTL(BO_MMAP_OFFSET, bo_mmap_offset_entered),
>         THAMES_IOCTL(DEV_QUERY, dev_query_entered),
> +       THAMES_IOCTL(BO_BIND, bo_bind_entered),
>  };
> 
>  DEFINE_DRM_ACCEL_FOPS(thames_accel_driver_fops);
> @@ -234,6 +277,10 @@ static struct thames_device *thames_device_init(struct platform_device *pdev,
>         if (err)
>                 return ERR_PTR(err);
> 
> +       err = drmm_mutex_init(&tdev->ddev, &tdev->bindings_lock);
> +       if (err)
> +               return ERR_PTR(err);
> +
>         INIT_LIST_HEAD(&tdev->contexts);
> 
>         tdev->asid_next = 1;
> diff --git a/drivers/accel/thames/thames_drv.h b/drivers/accel/thames/thames_drv.h
> index f4536078e02f..7f9031b63780 100644
> --- a/drivers/accel/thames/thames_drv.h
> +++ b/drivers/accel/thames/thames_drv.h
> @@ -15,6 +15,7 @@
>  #include "thames_device.h"
> 
>  struct drm_gem_dma_object;
> +struct drm_thames_obj_array;
> 
>  struct thames_file_priv {
>         struct thames_device *tdev;
> @@ -37,6 +38,8 @@ struct thames_file_priv {
>         struct drm_mm_node user_region;
>         struct drm_mm_node window_nodes[THAMES_NUM_WINDOWS];
> 
> +       struct list_head bindings;
> +
>         u32 context_id;
> 
>         u64 context_serial;
> @@ -47,4 +50,6 @@ struct thames_file_priv {
> 
>  void thames_file_priv_put(struct thames_file_priv *priv);
> 
> +void *thames_get_uobj_array(const struct drm_thames_obj_array *in, u32 min_stride, u32 obj_size);
> +
>  #endif
> diff --git a/drivers/accel/thames/thames_gem.c b/drivers/accel/thames/thames_gem.c
> index b98e0fe203b0..c9fd08c7aaf4 100644
> --- a/drivers/accel/thames/thames_gem.c
> +++ b/drivers/accel/thames/thames_gem.c
> @@ -14,6 +14,7 @@
>  #include <drm/drm_device.h>
>  #include <drm/drm_drv.h>
>  #include <drm/drm_gem_dma_helper.h>
> +#include <drm/drm_gem_shmem_helper.h>
>  #include <drm/drm_print.h>
>  #include <drm/drm_timeout.h>
>  #include <drm/thames_accel.h>
> @@ -23,6 +24,30 @@
>  #include "thames_drv.h"
>  #include "thames_gem.h"
> 
> +/*
> + * References neither the buffer nor the Context: whichever goes first removes
> + * it.
> + */
> +struct thames_binding {
> +       struct thames_gem_object *bo;
> +       struct thames_file_priv *priv;
> +       struct drm_mm_node mm;
> +       struct list_head bo_link;
> +       struct list_head context_link;
> +};
> +
> +static int thames_bo_flags_to_prot(u32 flags)
> +{
> +       int prot = IOMMU_READ | IOMMU_CACHE;
> +
> +       if (!(flags & DRM_THAMES_BO_EXECUTABLE))
> +               prot |= IOMMU_NOEXEC;
> +       if (!(flags & (DRM_THAMES_BO_READ_ONLY | DRM_THAMES_BO_EXECUTABLE)))
> +               prot |= IOMMU_WRITE;
> +
> +       return prot;
> +}
> +
>  static size_t thames_pgsize(u64 addr, size_t size, size_t *count)
>  {
>         size_t blk_offset = -addr % SZ_2M;
> @@ -95,6 +120,72 @@ static int thames_context_map(struct thames_file_priv *priv, u64 dsp_va, int pro
>         return 0;
>  }
> 
> +static void thames_context_unmap(struct thames_file_priv *priv, u64 dsp_va, u64 size)
> +{
> +       unsigned int c;
> +
> +       for (c = 0; c < priv->tdev->num_cores; c++)
> +               thames_pgtable_unmap(priv->pgtbl_ops[c], dsp_va, size);
> +}
> +
> +struct thames_gem_object *thames_context_find_bo(struct thames_file_priv *priv, u64 start,
> +                                                u64 end, u64 *offset)
> +{
> +       struct thames_binding *binding;
> +       struct thames_gem_object *bo;
> +       struct drm_mm_node *node;
> +       unsigned int i;
> +
> +       guard(mutex)(&priv->mm_lock);
> +
> +       node = __drm_mm_interval_first(&priv->mm, start, start);
> +       if (node == &priv->mm.head_node || node == &priv->first_page ||
> +           node == &priv->user_region)
> +               return NULL;
> +
> +       for (i = 0; i < THAMES_NUM_WINDOWS; i++) {
> +               if (node == &priv->window_nodes[i])
> +                       return NULL;
> +       }
> +
> +       binding = container_of(node, struct thames_binding, mm);
> +       bo = binding->bo;
> +       if (end > node->start + bo->base.base.size)
> +               return NULL;
> +
> +       if (!kref_get_unless_zero(&bo->base.base.refcount))
> +               return NULL;
> +
> +       *offset = start - node->start;
> +
> +       return bo;
> +}
> +
> +static struct thames_binding *thames_bo_find_binding(struct thames_gem_object *bo,
> +                                                    struct thames_file_priv *priv)
> +{
> +       struct thames_binding *binding;
> +
> +       lockdep_assert_held(&priv->tdev->bindings_lock);
> +
> +       list_for_each_entry(binding, &bo->bindings, bo_link) {
> +               if (binding->priv == priv)
> +                       return binding;
> +       }
> +
> +       return NULL;
> +}
> +
> +u64 thames_bo_dsp_va(struct thames_gem_object *bo, struct thames_file_priv *priv)
> +{
> +       struct thames_binding *binding;
> +
> +       guard(mutex)(&priv->tdev->bindings_lock);
> +       binding = thames_bo_find_binding(bo, priv);
> +
> +       return binding ? binding->mm.start : 0;
> +}
> +
>  /* Cyclic, so a freed ASID is not handed out again at once. */
>  static int thames_asid_alloc(struct thames_device *tdev)
>  {
> @@ -215,6 +306,7 @@ static int thames_context_map_core_regions(struct thames_file_priv *priv)
>  }
> 
>  static const struct drm_gem_object_funcs thames_gem_funcs;
> +static const struct drm_gem_object_funcs thames_gem_shmem_funcs;
> 
>  /*
>   * The allocator zeroes through the cache: clean it before the DSP can reach the
> @@ -234,6 +326,7 @@ static struct drm_gem_dma_object *thames_gem_create(struct drm_device *ddev, siz
>         if (!bo)
>                 return ERR_PTR(-ENOMEM);
> 
> +       INIT_LIST_HEAD(&bo->bindings);
>         obj = &bo->base.base;
>         obj->funcs = &thames_gem_funcs;
>         bo->base.map_noncoherent = true;
> @@ -271,6 +364,38 @@ static struct drm_gem_dma_object *thames_gem_create(struct drm_device *ddev, siz
>         return ERR_PTR(ret);
>  }
> 
> +/*
> + * The DSP reaches these pages only through the page tables, never by a DRU
> + * transfer.
> + */
> +static struct thames_gem_object *thames_gem_create_shmem(struct drm_device *ddev, size_t size)
> +{
> +       struct thames_gem_object *bo;
> +       struct sg_table *sgt;
> +       int ret;
> +
> +       bo = kzalloc_obj(*bo, GFP_KERNEL);
> +       if (!bo)
> +               return ERR_PTR(-ENOMEM);
> +
> +       INIT_LIST_HEAD(&bo->bindings);
> +       bo->shmem.base.funcs = &thames_gem_shmem_funcs;
> +
> +       ret = drm_gem_shmem_init(ddev, &bo->shmem, PAGE_ALIGN(size));
> +       if (ret) {
> +               kfree(bo);
> +               return ERR_PTR(ret);
> +       }
> +
> +       sgt = drm_gem_shmem_get_pages_sgt(&bo->shmem);
> +       if (IS_ERR(sgt)) {
> +               drm_gem_object_put(&bo->shmem.base);
> +               return ERR_CAST(sgt);
> +       }
> +
> +       return bo;
> +}
> +
>  static struct drm_gem_dma_object *thames_context_alloc_user_pages(struct thames_file_priv *priv,
>                                                                   u64 dsp_va, size_t size)
>  {
> @@ -459,6 +584,7 @@ int thames_context_create(struct thames_file_priv *priv)
> 
>         priv->context_id = ret;
>         priv->context_serial = atomic64_inc_return(&tdev->last_context_serial);
> +       INIT_LIST_HEAD(&priv->bindings);
> 
>         ret = thames_context_va_init(priv);
>         if (ret)
> @@ -500,14 +626,37 @@ int thames_context_create(struct thames_file_priv *priv)
>         return ret;
>  }
> 
> +static void thames_binding_remove(struct thames_binding *binding)
> +{
> +       struct thames_file_priv *priv = binding->priv;
> +
> +       lockdep_assert_held(&priv->tdev->bindings_lock);
> +
> +       thames_context_unmap(priv, binding->mm.start, binding->mm.size);
> +       thames_context_bump_generation(priv);
> +
> +       scoped_guard(mutex, &priv->mm_lock)
> +               drm_mm_remove_node(&binding->mm);
> +
> +       list_del(&binding->bo_link);
> +       list_del(&binding->context_link);
> +       kfree(binding);
> +}
> +
>  void thames_context_destroy(struct thames_file_priv *priv)
>  {
>         struct thames_device *tdev = priv->tdev;
> +       struct thames_binding *binding, *tmp;
> 
>         /*
>          * The Context goes after its last pushed Job completed, so no Job runs
>          * on mappings removed here.
>          */
> +       scoped_guard(mutex, &tdev->bindings_lock) {
> +               list_for_each_entry_safe(binding, tmp, &priv->bindings, context_link)
> +                       thames_binding_remove(binding);
> +       }
> +
>         scoped_guard(mutex, &tdev->contexts_lock)
>                 list_del(&priv->context_node);
> 
> @@ -522,10 +671,124 @@ void thames_context_destroy(struct thames_file_priv *priv)
>                 priv->context_id, priv->asid);
>  }
> 
> +static int thames_context_map_sgt(struct thames_file_priv *priv, u64 dsp_va, int prot,
> +                                 struct sg_table *sgt)
> +{
> +       struct scatterlist *sg;
> +       u64 offset = 0;
> +       unsigned int i;
> +       int ret;
> +
> +       for_each_sgtable_dma_sg(sgt, sg, i) {
> +               ret = thames_context_map(priv, dsp_va + offset, prot, sg_dma_address(sg),
> +                                        sg_dma_len(sg));
> +               if (ret) {
> +                       thames_context_unmap(priv, dsp_va, offset);
> +                       return ret;
> +               }
> +
> +               offset += sg_dma_len(sg);
> +       }
> +
> +       return 0;
> +}
> +
> +static int thames_bo_bind(struct thames_file_priv *priv, struct thames_gem_object *bo,
> +                         u64 dsp_va)
> +{
> +       size_t size = bo->base.base.size;
> +       int prot = thames_bo_flags_to_prot(bo->flags);
> +       struct thames_binding *binding;
> +       int ret;
> +
> +       if (!IS_ALIGNED(dsp_va, PAGE_SIZE))
> +               return -EINVAL;
> +
> +       if (dsp_va < THAMES_BIND_VA_START || dsp_va > THAMES_BIND_VA_END ||
> +           size > THAMES_BIND_VA_END - dsp_va)
> +               return -EINVAL;
> +
> +       binding = kzalloc_obj(*binding);
> +       if (!binding)
> +               return -ENOMEM;
> +
> +       binding->bo = bo;
> +       binding->priv = priv;
> +       binding->mm.start = dsp_va;
> +       binding->mm.size = size;
> +
> +       ret = mutex_lock_interruptible(&priv->tdev->bindings_lock);
> +       if (ret)
> +               goto err_free;
> +
> +       if (thames_bo_find_binding(bo, priv)) {
> +               ret = -EBUSY;
> +               goto err_unlock;
> +       }
> +
> +       scoped_guard(mutex, &priv->mm_lock)
> +               ret = drm_mm_reserve_node(&priv->mm, &binding->mm);
> +       if (ret)
> +               goto err_unlock;
> +
> +       if (bo->flags & DRM_THAMES_BO_CONTIGUOUS)
> +               ret = thames_context_map(priv, dsp_va, prot, bo->base.dma_addr, size);
> +       else
> +               ret = thames_context_map_sgt(priv, dsp_va, prot, bo->shmem.sgt);
> +       if (ret) {
> +               scoped_guard(mutex, &priv->mm_lock)
> +                       drm_mm_remove_node(&binding->mm);
> +               goto err_unlock;
> +       }
> +
> +       list_add_tail(&binding->bo_link, &bo->bindings);
> +       list_add_tail(&binding->context_link, &priv->bindings);
> +       mutex_unlock(&priv->tdev->bindings_lock);
> +
> +       return 0;
> +
> +err_unlock:
> +       mutex_unlock(&priv->tdev->bindings_lock);
> +err_free:
> +       kfree(binding);
> +       return ret;
> +}
> +
> +static int thames_bo_unbind(struct thames_file_priv *priv, struct thames_gem_object *bo)
> +{
> +       struct thames_binding *binding;
> +       int ret;
> +
> +       ret = mutex_lock_interruptible(&priv->tdev->bindings_lock);
> +       if (ret)
> +               return ret;
> +
> +       binding = thames_bo_find_binding(bo, priv);
> +       if (binding)
> +               thames_binding_remove(binding);
> +       else
> +               ret = -EINVAL;
> +
> +       mutex_unlock(&priv->tdev->bindings_lock);
> +       return ret;
> +}
> +
> +static void thames_bo_unmap_from_device(struct thames_gem_object *bo)
> +{
> +       struct thames_device *tdev = to_thames_device(bo->base.base.dev);
> +       struct thames_binding *binding, *tmp;
> +
> +       guard(mutex)(&tdev->bindings_lock);
> +       list_for_each_entry_safe(binding, tmp, &bo->bindings, bo_link)
> +               thames_binding_remove(binding);
> +}
> +
>  static void thames_gem_bo_free(struct drm_gem_object *obj)
>  {
>         struct thames_gem_object *bo = to_thames_bo(obj);
> 
> +       thames_bo_unmap_from_device(bo);
> +
>         if (bo->alloc_vaddr)
>                 dma_free_noncoherent(drm_dev_dma_dev(obj->dev), bo->alloc_size, bo->alloc_vaddr,
>                                      bo->alloc_dma, DMA_TO_DEVICE);
> @@ -535,6 +798,16 @@ static void thames_gem_bo_free(struct drm_gem_object *obj)
>         kfree(bo);
>  }
> 
> +static void thames_gem_shmem_bo_free(struct drm_gem_object *obj)
> +{
> +       struct thames_gem_object *bo = to_thames_bo(obj);
> +
> +       thames_bo_unmap_from_device(bo);
> +
> +       drm_gem_free_mmap_offset(obj);
> +       drm_gem_shmem_free(&bo->shmem);
> +}
> +
>  static const struct drm_gem_object_funcs thames_gem_funcs = {
>         .free = thames_gem_bo_free,
>         .print_info = drm_gem_dma_object_print_info,
> @@ -544,3 +817,128 @@ static const struct drm_gem_object_funcs thames_gem_funcs = {
>         .vm_ops = &drm_gem_dma_vm_ops,
>  };
> 
> +static const struct drm_gem_object_funcs thames_gem_shmem_funcs = {
> +       .free = thames_gem_shmem_bo_free,
> +       .print_info = drm_gem_shmem_object_print_info,
> +       .pin = drm_gem_shmem_object_pin,
> +       .unpin = drm_gem_shmem_object_unpin,
> +       .get_sg_table = drm_gem_shmem_object_get_sg_table,
> +       .vmap = drm_gem_shmem_object_vmap,
> +       .vunmap = drm_gem_shmem_object_vunmap,
> +       .mmap = drm_gem_shmem_object_mmap,
> +       .vm_ops = &drm_gem_shmem_vm_ops,
> +};
> +
> +int thames_ioctl_bo_create(struct drm_device *ddev, void *data, struct drm_file *file)
> +{
> +       struct drm_thames_bo_create *args = data;
> +       struct thames_gem_object *bo;
> +       struct drm_gem_dma_object *mem;
> +       int ret;
> +
> +       if (args->handle)
> +               return -EINVAL;
> +
> +       if (!args->size)
> +               return -EINVAL;
> +
> +       if (args->flags & ~(DRM_THAMES_BO_READ_ONLY | DRM_THAMES_BO_EXECUTABLE |
> +                           DRM_THAMES_BO_CONTIGUOUS))
> +               return -EINVAL;
> +
> +       if (args->flags & DRM_THAMES_BO_CONTIGUOUS) {
> +               mem = thames_gem_create(ddev, args->size);
> +               if (IS_ERR(mem))
> +                       return PTR_ERR(mem);
> +               bo = to_thames_bo(&mem->base);
> +       } else {
> +               bo = thames_gem_create_shmem(ddev, args->size);
> +               if (IS_ERR(bo))
> +                       return PTR_ERR(bo);
> +       }
> +
> +       bo->flags = args->flags;
> +
> +       ret = drm_gem_handle_create(file, &bo->base.base, &args->handle);
> +       drm_gem_object_put(&bo->base.base);
> +       if (ret) {
> +               dev_err(ddev->dev, "Failed to create gem handle: %d", ret);
> +               return ret;
> +       }
> +
> +       args->size = bo->base.base.size;
> +
> +       return 0;
> +}
> +
> +static int thames_bind_exec_op(struct drm_file *file, const struct drm_thames_bind_op *op)
> +{
> +       struct thames_file_priv *priv = file->driver_priv;
> +       struct drm_gem_object *obj;
> +       int ret;
> +
> +       if (op->flags & ~DRM_THAMES_BIND_OP_UNBIND)
> +               return -EINVAL;
> +
> +       if ((op->flags & DRM_THAMES_BIND_OP_UNBIND) && op->dsp_va)
> +               return -EINVAL;
> +
> +       obj = drm_gem_object_lookup(file, op->handle);
> +       if (!obj)
> +               return -ENOENT;
> +
> +       if (op->flags & DRM_THAMES_BIND_OP_UNBIND)
> +               ret = thames_bo_unbind(priv, to_thames_bo(obj));
> +       else
> +               ret = thames_bo_bind(priv, to_thames_bo(obj), op->dsp_va);
> +
> +       drm_gem_object_put(obj);
> +
> +       return ret;
> +}
> +
> +int thames_ioctl_bo_bind(struct drm_device *ddev, void *data, struct drm_file *file)
> +{
> +       struct drm_thames_bo_bind *args = data;
> +       struct drm_thames_bind_op *ops;
> +       int ret = 0;
> +       u32 i;
> +
> +       if (args->flags || args->pad)
> +               return -EINVAL;
> +
> +       ops = thames_get_uobj_array(&args->ops, sizeof(*ops), sizeof(*ops));
> +       if (IS_ERR(ops))
> +               return PTR_ERR(ops);
> +
> +       for (i = 0; i < args->ops.count; i++) {
> +               ret = thames_bind_exec_op(file, &ops[i]);
> +               if (ret) {
> +                       args->ops.count = i;
> +                       break;
> +               }
> +       }
> +
> +       kvfree(ops);
> +
> +       return ret;
> +}
> +
> +int thames_ioctl_bo_mmap_offset(struct drm_device *ddev, void *data, struct drm_file *file)
> +{
> +       struct drm_thames_bo_mmap_offset *args = data;
> +       struct drm_gem_object *obj;
> +
> +       if (args->pad)
> +               return -EINVAL;
> +
> +       obj = drm_gem_object_lookup(file, args->handle);
> +       if (!obj)
> +               return -ENOENT;
> +
> +       args->offset = drm_vma_node_offset_addr(&obj->vma_node);
> +       drm_gem_object_put(obj);
> +
> +       return 0;
> +}
> +
> diff --git a/drivers/accel/thames/thames_gem.h b/drivers/accel/thames/thames_gem.h
> index 09ac1b50d21a..c9d0e170dad3 100644
> --- a/drivers/accel/thames/thames_gem.h
> +++ b/drivers/accel/thames/thames_gem.h
> @@ -12,6 +12,13 @@
> 
>  #include "thames_abi.h"
> 
> +/*
> + * The windows sit below 4 GiB because Kernels load their addresses with 32-bit
> + * moves.
> + */
> +#define THAMES_BIND_VA_START   SZ_4G
> +#define THAMES_BIND_VA_END     THAMES_USER_REGION_BASE
> +
>  struct thames_core;
>  struct thames_device;
>  struct thames_file_priv;
> @@ -34,9 +41,18 @@ struct thames_gem_object {
>         dma_addr_t alloc_dma;
>         size_t alloc_size;
> 
> +       struct list_head bindings;
> +
> +       u32 flags;
>  };
> 
> 
> +int thames_ioctl_bo_create(struct drm_device *ddev, void *data, struct drm_file *file);
> +
> +int thames_ioctl_bo_mmap_offset(struct drm_device *ddev, void *data, struct drm_file *file);
> +
> +int thames_ioctl_bo_bind(struct drm_device *ddev, void *data, struct drm_file *file);
> +
>  int thames_context_create(struct thames_file_priv *priv);
> 
>  void thames_context_destroy(struct thames_file_priv *priv);
> @@ -44,6 +60,11 @@ void thames_context_rewrite_entries(struct thames_core *core);
> 
>  void thames_context_va_fini(struct thames_file_priv *priv);
> 
> +struct thames_gem_object *thames_context_find_bo(struct thames_file_priv *priv, u64 start,
> +                                                u64 end, u64 *offset);
> +
> +u64 thames_bo_dsp_va(struct thames_gem_object *bo, struct thames_file_priv *priv);
> +
>  static inline struct thames_gem_object *to_thames_bo(struct drm_gem_object *obj)
>  {
>         return container_of(obj, struct thames_gem_object, base.base);
> diff --git a/include/uapi/drm/thames_accel.h b/include/uapi/drm/thames_accel.h
> index af427a29b585..ea556b48f1d7 100644
> --- a/include/uapi/drm/thames_accel.h
> +++ b/include/uapi/drm/thames_accel.h
> @@ -45,15 +45,116 @@ extern "C" {
>   * definitions instead.
>   */
>  enum drm_thames_ioctl_id {
> +       /** @DRM_THAMES_BO_CREATE: Create a buffer object. */
> +       DRM_THAMES_BO_CREATE,
> +
> +       /**
> +        * @DRM_THAMES_BO_MMAP_OFFSET: Get the file offset to pass to
> +        * mmap to map a GEM object.
> +        */
> +       DRM_THAMES_BO_MMAP_OFFSET,
> +
>         /** @DRM_THAMES_DEV_QUERY: Query the device's capabilities. */
>         DRM_THAMES_DEV_QUERY,
> 
> +       /**
> +        * @DRM_THAMES_BO_BIND: Bind buffers into the file's Context at the
> +        * addresses the client chooses, or unbind them.
> +        */
> +       DRM_THAMES_BO_BIND,
>  };
> 
>  /**
>   * DOC: IOCTL arguments
>   */
> 
> +/**
> + * enum drm_thames_bo_flags - Buffer object flags, passed at creation time.
> + *
> + * A buffer with neither permission flag is user read-write and execute-never;
> + * DRM_THAMES_BO_CONTIGUOUS chooses the backing, not the permissions.
> + */
> +enum drm_thames_bo_flags {
> +       /**
> +        * @DRM_THAMES_BO_READ_ONLY: Mapped user read-only in the Context.  For
> +        * the params, the coefficients and the constants, so that a Kernel
> +        * cannot alter the inputs of a Job's next run.
> +        */
> +       DRM_THAMES_BO_READ_ONLY = 1 << 0,
> +
> +       /**
> +        * @DRM_THAMES_BO_EXECUTABLE: Mapped user read-execute and not
> +        * writable.  Required of a Job's program.
> +        */
> +       DRM_THAMES_BO_EXECUTABLE = 1 << 1,
> +
> +       /**
> +        * @DRM_THAMES_BO_CONTIGUOUS: Backed by one physically contiguous
> +        * allocation, so that a transfer of a Job can name it.  For the buffers
> +        * a DRU transfer reads or writes; any other buffer is backed page by
> +        * page.
> +        */
> +       DRM_THAMES_BO_CONTIGUOUS = 1 << 2,
> +};
> +
> +/**
> + * struct drm_thames_bo_create - Arguments passed to DRM_IOCTL_THAMES_BO_CREATE.
> + *
> + * Returns a handle and no address; the buffer is bound in no Context until
> + * DRM_IOCTL_THAMES_BO_BIND.
> + */
> +struct drm_thames_bo_create {
> +       /**
> +        * @size: Requested size for the object
> +        *
> +        * The (page-aligned) allocated size for the object will be returned.
> +        */
> +       __u64 size;
> +
> +       /**
> +        * @handle: MBZ on input; returns the object's handle, which is nonzero.
> +        */
> +       __u32 handle;
> +
> +       /** @flags: Combination of &enum drm_thames_bo_flags. */
> +       __u32 flags;
> +};
> +
> +/**
> + * struct drm_thames_bo_mmap_offset - Arguments passed to DRM_IOCTL_THAMES_BO_MMAP_OFFSET.
> + */
> +struct drm_thames_bo_mmap_offset {
> +       /** @handle: Handle of the object we want an mmap offset for. */
> +       __u32 handle;
> +
> +       /** @pad: MBZ. */
> +       __u32 pad;
> +
> +       /** @offset: The fake offset to use for subsequent mmap calls. */
> +       __u64 offset;
> +};
> +
> +/**
> + * struct drm_thames_obj_array - An array of versioned objects passed by pointer.
> + *
> + * The stride is the size of the object userspace was built against, so a
> + * structure can grow at its end under a minor bump.
> + * The kernel reads each element with copy_struct_from_user(): a stride smaller
> + * than its own size has the rest read as zero, down to the size the structure
> + * had when it entered the uAPI and no smaller; a larger stride is accepted only
> + * if the bytes the kernel does not know are zero.
> + */
> +struct drm_thames_obj_array {
> +       /** @stride: Size of one element in bytes. */
> +       __u32 stride;
> +
> +       /** @count: Number of elements. */
> +       __u32 count;
> +
> +       /** @array: User pointer to the first element. */
> +       __u64 array;
> +};
> +
>  /**
>   * struct drm_thames_range - A range of the Context's address space.
>   */
> @@ -65,6 +166,73 @@ struct drm_thames_range {
>         __u64 size;
>  };
> 
> +/**
> + * DOC: Bind operation flags
> + *
> + * - %DRM_THAMES_BIND_OP_UNBIND: Unbind the buffer from the Context instead
> + *   of binding it; &drm_thames_bind_op.dsp_va must be 0.
> + */
> +#define DRM_THAMES_BIND_OP_UNBIND      (1 << 0)
> +
> +/**
> + * struct drm_thames_bind_op - One operation of DRM_IOCTL_THAMES_BO_BIND
> + *
> + * A bind maps the whole buffer at @dsp_va in every page table of the file's
> + * Context, with the permissions of its creation flags; an unbind removes the
> + * mapping.  A buffer is bound at most once in a Context, at one address, over
> + * no range another buffer is bound in.  A buffer shared through a dma-buf may
> + * be bound in each importing file's Context, each at its own address.  The
> + * kernel maps 2 MiB blocks where both the address and the buffer's pages are
> + * 2 MiB aligned, so a client aligns a buffer of 2 MiB or more to 2 MiB.
> + *
> + * Unbinding a buffer a submitted Job still reaches through the MMU is the
> + * client's error: the mapping goes at once, and the Job faults in the client's
> + * own Context as on any other fault.  A Job's transfers keep the buffers they
> + * reach, whether bound or not, until the Job is destroyed.  Freeing a bound
> + * buffer unbinds it.
> + */
> +struct drm_thames_bind_op {
> +       /** @handle: The buffer. */
> +       __u32 handle;
> +
> +       /**
> +        * @flags: 0 to bind the buffer at @dsp_va,
> +        * %DRM_THAMES_BIND_OP_UNBIND to unbind it.
> +        */
> +       __u32 flags;
> +
> +       /**
> +        * @dsp_va: Where the buffer is bound in the DSP's address space,
> +        * page-aligned, the buffer wholly inside
> +        * &drm_thames_caps.dsp_va_range; 0 for an unbind.
> +        *
> +        * The address the core's MMU translates through the Context's page
> +        * table, the same on every core.  It is the number userspace embeds
> +        * in params and Kernel code.  It is neither the bus address the page
> +        * table entries hold nor the CPU address mmap returns.
> +        */
> +       __u64 dsp_va;
> +};
> +
> +/**
> + * struct drm_thames_bo_bind - Arguments passed to DRM_IOCTL_THAMES_BO_BIND
> + *
> + * The operations are applied in order and each is done when the call
> + * returns.  The call stops at the first operation that fails and returns its
> + * error; the operations before it hold, and @ops.count is set to their
> + * number.
> + */
> +struct drm_thames_bo_bind {
> +       /** @flags: MBZ. */
> +       __u32 flags;
> +
> +       /** @pad: MBZ. */
> +       __u32 pad;
> +
> +       /** @ops: Array of &struct drm_thames_bind_op, applied in order. */
> +       struct drm_thames_obj_array ops;
> +};
> +
>  /**
>   * enum drm_thames_dev_query_type - Query type
>   *
> @@ -126,9 +294,16 @@ struct drm_thames_caps {
>          */
>         __u64 dru_realtime_dsp_va;
> 
> +       /**
> +        * @dsp_va_range: The span of the Context's address space the client
> +        * binds its buffers in with DRM_IOCTL_THAMES_BO_BIND.  The windows
> +        * above sit below it and @user_region above it.
> +        */
> +       struct drm_thames_range dsp_va_range;
> +
>         /**
>          * @user_region: Where the kernel driver maps the Context's user stack
> -        * and save page.
> +        * and save page, outside @dsp_va_range.
>          */
>         struct drm_thames_range user_region;
> 
> @@ -189,8 +364,14 @@ struct drm_thames_dev_query {
>                            struct drm_thames_ ## __type)
> 
>  enum {
> +       DRM_IOCTL_THAMES_BO_CREATE =
> +               DRM_IOCTL_THAMES(WR, BO_CREATE, bo_create),
> +       DRM_IOCTL_THAMES_BO_MMAP_OFFSET =
> +               DRM_IOCTL_THAMES(WR, BO_MMAP_OFFSET, bo_mmap_offset),
>         DRM_IOCTL_THAMES_DEV_QUERY =
>                 DRM_IOCTL_THAMES(WR, DEV_QUERY, dev_query),
> +       DRM_IOCTL_THAMES_BO_BIND =
> +               DRM_IOCTL_THAMES(WR, BO_BIND, bo_bind),
>  };
> 
>  #if defined(__cplusplus)
> 
> --
> 2.54.0
> 


  reply	other threads:[~2026-10-26 10:09 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-23  6:39 [PATCH v3 00/16] New DRM accel driver for Texas Instruments' C7x DSPs Tomeu Vizoso
2026-10-23  6:39 ` [PATCH v3 01/16] dt-bindings: remoteproc: k3-dsp: Add the L2 auxiliary and DRU regions Tomeu Vizoso
2026-10-23  6:39 ` [PATCH v3 02/16] dt-bindings: remoteproc: k3-dsp: Allow a second sub-mailbox Tomeu Vizoso
2026-10-26  7:49   ` Krzysztof Kozlowski
2026-10-23  6:39 ` [PATCH v3 03/16] dt-bindings: remoteproc: k3-dsp: Allow referencing the timers' power domains Tomeu Vizoso
2026-10-26  7:51   ` Krzysztof Kozlowski
2026-10-23  6:39 ` [PATCH v3 04/16] accel/thames: Add header with IPC definitions Tomeu Vizoso
2026-10-23  6:39 ` [PATCH v3 05/16] accel/thames: Add driver for the C7x DSPs in TI SoCs Tomeu Vizoso
2026-10-23  6:39 ` [PATCH v3 06/16] accel/thames: Create a context per client on device open Tomeu Vizoso
2026-10-23  6:39 ` [PATCH v3 07/16] accel/thames: Add IOCTLs for BO creation and mapping Tomeu Vizoso
2026-10-26 10:07   ` Christian König [this message]
2026-10-23  6:39 ` [PATCH v3 08/16] accel/thames: Add support for submitting DMA operations Tomeu Vizoso
2026-10-23  6:39 ` [PATCH v3 09/16] accel/thames: Add IOCTLs for job creation and submission Tomeu Vizoso
2026-10-26 10:10   ` Christian König
2026-10-23  6:39 ` [PATCH v3 10/16] accel/thames: Recover from faults and timeouts Tomeu Vizoso
2026-10-23  6:39 ` [PATCH v3 11/16] accel/thames: Add IOCTL for memory synchronization Tomeu Vizoso
2026-10-23  6:39 ` [PATCH v3 12/16] accel/thames: Install memory mappings populated and dirty Tomeu Vizoso
2026-10-23  6:39 ` [PATCH v3 13/16] Documentation: accel/thames: Add initial docs Tomeu Vizoso
2026-10-23  6:39 ` [PATCH v3 14/16] arm64: dts: ti: k3-j722s: Add the AUX L2 SRAM and DRU regions Tomeu Vizoso
2026-10-23  6:39 ` [PATCH v3 15/16] arm64: dts: ti: k3-j722s: Add second submailbox to the C7x cores Tomeu Vizoso
2026-10-23  6:39 ` [PATCH v3 16/16] arm64: dts: ti: k3-j722s: Reference the power domains of the C7x timers Tomeu Vizoso

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=93a8d4f5-fcbb-4fb1-a601-9503c215dfa6@amd.com \
    --to=christian.koenig@amd.com \
    --cc=Markus.Elfring@web.de \
    --cc=a-aldea@ti.com \
    --cc=afd@ti.com \
    --cc=airlied@gmail.com \
    --cc=andersson@kernel.org \
    --cc=b-padhi@ti.com \
    --cc=c-shilwant@ti.com \
    --cc=conor+dt@kernel.org \
    --cc=corbet@lwn.net \
    --cc=devicetree@vger.kernel.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=gustavoars@kernel.org \
    --cc=j-humphreys@ti.com \
    --cc=jani.nikula@linux.intel.com \
    --cc=kees@kernel.org \
    --cc=kristo@kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=linaro-mm-sig@lists.linaro.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-hardening@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=linux-remoteproc@vger.kernel.org \
    --cc=maarten.lankhorst@linux.intel.com \
    --cc=mathieu.poirier@linaro.org \
    --cc=mripard@kernel.org \
    --cc=nm@ti.com \
    --cc=ogabbay@kernel.org \
    --cc=rdunlap@infradead.org \
    --cc=robertcnelson@gmail.com \
    --cc=robh@kernel.org \
    --cc=rs@ti.com \
    --cc=s-anna@ti.com \
    --cc=simona@ffwll.ch \
    --cc=skhan@linuxfoundation.org \
    --cc=sumit.semwal@linaro.org \
    --cc=tomeu.vizoso@gmail.com \
    --cc=tomeu@tomeuvizoso.net \
    --cc=tursulin@ursulin.net \
    --cc=tzimmermann@suse.de \
    --cc=vigneshr@ti.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®