Re: [PATCH v2 09/15] accel/qda: Add DMA-backed GEM objects and memory manager integration

Rob Clark <[email protected]>
Newsgroups org.kernel.vger.linux-doc,dev.linux.lists.iommu,dev.linux.lists.llvm,org.freedesktop.lists.dri-devel,org.kernel.vger.linux-arm-msm,org.kernel.vger.linux-kernel,org.kernel.vger.linux-media
Message-ID <CACSVV00MubttbkVJgMommPMorf67yqn7D1kfwpxVUZ0t-6au8Q@mail.gmail.com>
On Mon, Aug 24, 2026 at 7:31 AM Christian König
<[email protected]> wrote:
>
> On 8/17/26 06:47, Ekansh Gupta wrote:
> > Introduce DMA-coherent buffer management for the QDA driver, wiring
> > together the GEM subsystem, the IOMMU memory manager, and a DMA
> > allocation backend.
> >
> > qda_gem.c / qda_gem.h
> >   Implements the GEM object lifecycle for QDA buffers. Each buffer is
> >   represented by a qda_gem_obj which embeds a drm_gem_object and
> >   carries the kernel virtual address, DMA address, and a pointer to
> >   the IOMMU device that performed the allocation. The .free callback
> >   delegates to the memory manager, and the .mmap callback uses
> >   dma_mmap_coherent() via the DMA backend.
> >
> > qda_memory_dma.c / qda_memory_dma.h
> >   DMA coherent allocation backend. qda_dma_alloc() calls
> >   dma_alloc_coherent() on the CB device and encodes the stream ID
> >   (SID) in the upper 32 bits of the returned DMA address, following
> >   the Qualcomm FastRPC convention for IOMMU address space tagging.
> >   qda_dma_free() strips the SID prefix before calling
> >   dma_free_coherent().
> >
> > qda_memory_manager.c
> >   Adds process-to-device assignment: each DRM file (process) is
> >   assigned one IOMMU context bank device for the lifetime of the
> >   session. qda_memory_manager_assign_device() first checks whether
> >   the process already has a device (reusing it with a refcount
> >   increment), then falls back to claiming an unassigned device.
> >   qda_memory_manager_alloc() and qda_memory_manager_free() delegate
> >   to the DMA backend after resolving the correct CB device for the
> >   calling process.
>
> Oh, stuff like that is usually a pretty big NO-GO now.
>
> AMD has made the same mistake with KFD and it resulted in a massive chaos.
>
> The DRM file is the driver context your process uses and even if there are multiple DRM files for the same PID you should absolute *NOT* share anything between them.
>
> The general rule of thumb is to not attach anything to the process using the DRM file descriptor.

A couple other things to keep in mind:

1. drm device fd can be passed across processes
2. virtgpu/virglrenderer drm native ctx is a single process that opens
the drm device file N times on behalf of N guest userspace processes.

All that to say, Christian is right here.. drm_file is your context,
pgtables should be attached to the drm_file (potentially via
drm_gpuvm.. which you perhaps want to use).

BR,
-R

> Regards,
> Christian.
>
> >
> > qda_drv.c / qda_drv.h
> >   qda_file_priv gains an assigned_iommu_dev pointer and a pid field.
> >   The .postclose callback decrements the IOMMU device refcount and
> >   clears the process assignment when the last reference is dropped.
> >
> > Assisted-by: Claude:claude-sonnet-5
> > Signed-off-by: Ekansh Gupta <[email protected]>
> > ---
> > Changes in v2:
> > - Adapt to the dynamically-sized device array introduced in patch 07
> >   (kcalloc'd from DT node count, replaces fixed QDA_IOMMU_DEVICES_MAX)
> > - Protect register/unregister with the process_assignment_lock mutex so
> >   the device-assignment and device-registration paths are serialised
> > - No functional changes requested by reviewers on this patch
> > ---
> >  drivers/accel/qda/Makefile             |   2 +
> >  drivers/accel/qda/qda_drv.c            |   4 +
> >  drivers/accel/qda/qda_drv.h            |   4 +
> >  drivers/accel/qda/qda_gem.c            | 134 ++++++++++++++++++
> >  drivers/accel/qda/qda_gem.h            |  52 +++++++
> >  drivers/accel/qda/qda_memory_dma.c     |  82 +++++++++++
> >  drivers/accel/qda/qda_memory_dma.h     |  17 +++
> >  drivers/accel/qda/qda_memory_manager.c | 239 ++++++++++++++++++++++++++++++++-
> >  drivers/accel/qda/qda_memory_manager.h |  30 +++++
> >  9 files changed, 559 insertions(+), 5 deletions(-)
> >
> > diff --git a/drivers/accel/qda/Makefile b/drivers/accel/qda/Makefile
> > index b658dad35fee..a46ddceecfc5 100644
> > --- a/drivers/accel/qda/Makefile
> > +++ b/drivers/accel/qda/Makefile
> > @@ -8,7 +8,9 @@ obj-$(CONFIG_DRM_ACCEL_QDA)   := qda.o
> >  qda-y := \
> >       qda_cb.o \
> >       qda_drv.o \
> > +     qda_gem.o \
> >       qda_ioctl.o \
> > +     qda_memory_dma.o \
> >       qda_memory_manager.o \
> >       qda_rpmsg.o
> >
> > diff --git a/drivers/accel/qda/qda_drv.c b/drivers/accel/qda/qda_drv.c
> > index e1fd8bfa12d7..2b14dab95507 100644
> > --- a/drivers/accel/qda/qda_drv.c
> > +++ b/drivers/accel/qda/qda_drv.c
> > @@ -21,6 +21,7 @@ static int qda_open(struct drm_device *dev, struct drm_file *file)
> >       if (!qda_file_priv)
> >               return -ENOMEM;
> >
> > +     qda_file_priv->pid = current->pid;
> >       qda_file_priv->qda_dev = qda_dev_from_drm(dev);
> >       file->driver_priv = qda_file_priv;
> >
> > @@ -31,6 +32,9 @@ static void qda_postclose(struct drm_device *dev, struct drm_file *file)
> >  {
> >       struct qda_file_priv *qda_file_priv = file->driver_priv;
> >
> > +     if (qda_file_priv->assigned_iommu_dev)
> > +             qda_memory_manager_release_device(qda_file_priv->assigned_iommu_dev);
> > +
> >       kfree(qda_file_priv);
> >       file->driver_priv = NULL;
> >  }
> > diff --git a/drivers/accel/qda/qda_drv.h b/drivers/accel/qda/qda_drv.h
> > index 690a833d732b..2fe58f3efec0 100644
> > --- a/drivers/accel/qda/qda_drv.h
> > +++ b/drivers/accel/qda/qda_drv.h
> > @@ -24,6 +24,10 @@
> >  struct qda_file_priv {
> >       /** @qda_dev: Back-pointer to device structure */
> >       struct qda_dev *qda_dev;
> > +     /** @assigned_iommu_dev: IOMMU device assigned to this process */
> > +     struct qda_iommu_device *assigned_iommu_dev;
> > +     /** @pid: Process ID for tracking */
> > +     pid_t pid;
> >  };
> >
> >  /**
> > diff --git a/drivers/accel/qda/qda_gem.c b/drivers/accel/qda/qda_gem.c
> > new file mode 100644
> > index 000000000000..66e78013a726
> > --- /dev/null
> > +++ b/drivers/accel/qda/qda_gem.c
> > @@ -0,0 +1,134 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> > +#include <drm/drm_gem.h>
> > +#include <drm/drm_prime.h>
> > +#include <drm/drm_print.h>
> > +#include <linux/slab.h>
> > +#include <linux/dma-mapping.h>
> > +#include "qda_drv.h"
> > +#include "qda_gem.h"
> > +#include "qda_memory_manager.h"
> > +#include "qda_memory_dma.h"
> > +
> > +/**
> > + * qda_gem_free_object() - Free a GEM object and its associated resources
> > + * @gem_obj: DRM GEM object to free
> > + */
> > +void qda_gem_free_object(struct drm_gem_object *gem_obj)
> > +{
> > +     struct qda_gem_obj *qda_gem_obj = to_qda_gem_obj(gem_obj);
> > +     struct qda_dev *qdev = qda_dev_from_drm(gem_obj->dev);
> > +
> > +     if (qda_gem_obj->virt && qdev->iommu_mgr)
> > +             qda_memory_manager_free(qdev->iommu_mgr, qda_gem_obj);
> > +
> > +     drm_gem_object_release(gem_obj);
> > +     kfree(qda_gem_obj);
> > +}
> > +
> > +/**
> > + * qda_gem_mmap_obj() - Map a GEM object into userspace
> > + * @drm_obj: DRM GEM object to map
> > + * @vma: Virtual memory area to map into
> > + *
> > + * Return: 0 on success, negative error code on failure
> > + */
> > +int qda_gem_mmap_obj(struct drm_gem_object *drm_obj, struct vm_area_struct *vma)
> > +{
> > +     struct qda_gem_obj *qda_gem_obj = to_qda_gem_obj(drm_obj);
> > +     int ret;
> > +
> > +     /* The fake offset is only used to find the object, not to index it */
> > +     vma->vm_pgoff = 0;
> > +
> > +     ret = qda_dma_mmap(qda_gem_obj, vma);
> > +     if (ret)
> > +             return ret;
> > +
> > +     vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct drm_gem_object_funcs qda_gem_object_funcs = {
> > +     .free = qda_gem_free_object,
> > +     .mmap = qda_gem_mmap_obj,
> > +};
> > +
> > +/**
> > + * qda_gem_alloc_object() - Allocate a new QDA GEM object
> > + * @drm_dev: DRM device
> > + * @aligned_size: Size of the object in bytes (must be page-aligned)
> > + *
> > + * Return: Pointer to the new GEM object, or ERR_PTR on failure
> > + */
> > +struct qda_gem_obj *qda_gem_alloc_object(struct drm_device *drm_dev, size_t aligned_size)
> > +{
> > +     struct qda_gem_obj *qda_gem_obj;
> > +     int ret;
> > +
> > +     qda_gem_obj = kzalloc_obj(*qda_gem_obj);
> > +     if (!qda_gem_obj)
> > +             return ERR_PTR(-ENOMEM);
> > +
> > +     ret = drm_gem_object_init(drm_dev, &qda_gem_obj->base, aligned_size);
> > +     if (ret) {
> > +             kfree(qda_gem_obj);
> > +             return ERR_PTR(ret);
> > +     }
> > +
> > +     qda_gem_obj->base.funcs = &qda_gem_object_funcs;
> > +     qda_gem_obj->size = aligned_size;
> > +
> > +     return qda_gem_obj;
> > +}
> > +
> > +void qda_gem_cleanup_object(struct qda_gem_obj *qda_gem_obj)
> > +{
> > +     drm_gem_object_release(&qda_gem_obj->base);
> > +     kfree(qda_gem_obj);
> > +}
> > +
> > +int qda_gem_create_handle(struct drm_file *file_priv, struct drm_gem_object *gem_obj, u32 *handle)
> > +{
> > +     int ret;
> > +
> > +     ret = drm_gem_handle_create(file_priv, gem_obj, handle);
> > +     drm_gem_object_put(gem_obj);
> > +
> > +     return ret;
> > +}
> > +
> > +/**
> > + * qda_gem_create_object() - Allocate and initialize a GEM object with DMA backing
> > + * @drm_dev: DRM device
> > + * @iommu_mgr: Memory manager to use for DMA allocation
> > + * @size: Requested size in bytes
> > + * @file_priv: DRM file private data for process association
> > + *
> > + * Return: Pointer to the base DRM GEM object on success, ERR_PTR on failure
> > + */
> > +struct drm_gem_object *qda_gem_create_object(struct drm_device *drm_dev,
> > +                                          struct qda_memory_manager *iommu_mgr, size_t size,
> > +                                          struct drm_file *file_priv)
> > +{
> > +     struct qda_gem_obj *qda_gem_obj;
> > +     size_t aligned_size;
> > +     int ret;
> > +
> > +     aligned_size = PAGE_ALIGN(size);
> > +     if (!aligned_size)
> > +             return ERR_PTR(-EINVAL);
> > +
> > +     qda_gem_obj = qda_gem_alloc_object(drm_dev, aligned_size);
> > +     if (IS_ERR(qda_gem_obj))
> > +             return ERR_CAST(qda_gem_obj);
> > +
> > +     ret = qda_memory_manager_alloc(iommu_mgr, qda_gem_obj, file_priv);
> > +     if (ret) {
> > +             qda_gem_cleanup_object(qda_gem_obj);
> > +             return ERR_PTR(ret);
> > +     }
> > +
> > +     return &qda_gem_obj->base;
> > +}
> > diff --git a/drivers/accel/qda/qda_gem.h b/drivers/accel/qda/qda_gem.h
> > new file mode 100644
> > index 000000000000..afd7c9b49549
> > --- /dev/null
> > +++ b/drivers/accel/qda/qda_gem.h
> > @@ -0,0 +1,52 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> > + */
> > +#ifndef __QDA_GEM_H__
> > +#define __QDA_GEM_H__
> > +
> > +#include <linux/dma-mapping.h>
> > +#include <drm/drm_device.h>
> > +#include <drm/drm_gem.h>
> > +#include "qda_memory_manager.h"
> > +
> > +/**
> > + * struct qda_gem_obj - QDA GEM buffer object
> > + *
> > + * Represents a GEM buffer object that can be allocated by the driver
> > + * or imported from another driver via DMA-BUF.
> > + */
> > +struct qda_gem_obj {
> > +     /** @base: DRM GEM object base — must be first member */
> > +     struct drm_gem_object base;
> > +     /** @iommu_dev: IOMMU context bank device that performed the allocation */
> > +     struct qda_iommu_device *iommu_dev;
> > +     /** @virt: Kernel virtual address of the allocated DMA memory */
> > +     void *virt;
> > +     /** @dma_addr: DMA address (with SID encoded in upper 32 bits) */
> > +     dma_addr_t dma_addr;
> > +     /** @size: Size of the buffer in bytes */
> > +     size_t size;
> > +};
> > +
> > +/**
> > + * to_qda_gem_obj - Cast a drm_gem_object pointer to qda_gem_obj
> > + * @gem_obj: Pointer to the embedded drm_gem_object
> > + */
> > +#define to_qda_gem_obj(gem_obj) container_of(gem_obj, struct qda_gem_obj, base)
> > +
> > +/* GEM object lifecycle */
> > +struct drm_gem_object *qda_gem_create_object(struct drm_device *drm_dev,
> > +                                          struct qda_memory_manager *iommu_mgr,
> > +                                          size_t size, struct drm_file *file_priv);
> > +void qda_gem_free_object(struct drm_gem_object *gem_obj);
> > +int qda_gem_mmap_obj(struct drm_gem_object *gem_obj, struct vm_area_struct *vma);
> > +
> > +/* Internal helpers (also used by PRIME import) */
> > +struct qda_gem_obj *qda_gem_alloc_object(struct drm_device *drm_dev, size_t aligned_size);
> > +void qda_gem_cleanup_object(struct qda_gem_obj *qda_gem_obj);
> > +
> > +/* Utility functions */
> > +int qda_gem_create_handle(struct drm_file *file_priv, struct drm_gem_object *gem_obj, u32 *handle);
> > +
> > +#endif /* __QDA_GEM_H__ */
> > diff --git a/drivers/accel/qda/qda_memory_dma.c b/drivers/accel/qda/qda_memory_dma.c
> > new file mode 100644
> > index 000000000000..7072ed8edf0b
> > --- /dev/null
> > +++ b/drivers/accel/qda/qda_memory_dma.c
> > @@ -0,0 +1,82 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> > +#include <linux/slab.h>
> > +#include <linux/dma-mapping.h>
> > +#include "qda_drv.h"
> > +#include "qda_gem.h"
> > +#include "qda_memory_dma.h"
> > +
> > +/*
> > + * The DSP identifies the IOMMU context bank from the upper bits of the address,
> > + * so the stream ID is folded into every address handed to it. The DMA API only
> > + * ever sees the plain 32-bit address, which is why the context bank devices are
> > + * created with a 32-bit DMA mask.
> > + */
> > +static dma_addr_t get_actual_dma_addr(struct qda_gem_obj *gem_obj)
> > +{
> > +     return gem_obj->dma_addr - ((u64)gem_obj->iommu_dev->sid << 32);
> > +}
> > +
> > +static void setup_gem_object(struct qda_gem_obj *gem_obj, void *virt,
> > +                          dma_addr_t dma_addr, struct qda_iommu_device *iommu_dev)
> > +{
> > +     gem_obj->virt = virt;
> > +     gem_obj->dma_addr = dma_addr;
> > +     gem_obj->iommu_dev = iommu_dev;
> > +}
> > +
> > +static void cleanup_gem_object_fields(struct qda_gem_obj *gem_obj)
> > +{
> > +     gem_obj->virt = NULL;
> > +     gem_obj->dma_addr = 0;
> > +     gem_obj->iommu_dev = NULL;
> > +}
> > +
> > +/**
> > + * qda_dma_alloc() - Allocate DMA coherent memory for a GEM object
> > + * @iommu_dev: Pointer to the QDA IOMMU device structure
> > + * @gem_obj: Pointer to GEM object to allocate memory for
> > + * @size: Size of memory to allocate in bytes
> > + *
> > + * Return: 0 on success, negative error code on failure
> > + */
> > +int qda_dma_alloc(struct qda_iommu_device *iommu_dev,
> > +               struct qda_gem_obj *gem_obj, size_t size)
> > +{
> > +     dma_addr_t dma_addr;
> > +     void *virt;
> > +
> > +     virt = dma_alloc_coherent(iommu_dev->dev, size, &dma_addr, GFP_KERNEL);
> > +     if (!virt)
> > +             return -ENOMEM;
> > +
> > +     dma_addr += ((u64)iommu_dev->sid << 32);
> > +     setup_gem_object(gem_obj, virt, dma_addr, iommu_dev);
> > +
> > +     return 0;
> > +}
> > +
> > +/**
> > + * qda_dma_free() - Free DMA coherent memory for a GEM object
> > + * @gem_obj: Pointer to GEM object to free memory for
> > + */
> > +void qda_dma_free(struct qda_gem_obj *gem_obj)
> > +{
> > +     dma_free_coherent(gem_obj->iommu_dev->dev, gem_obj->size,
> > +                       gem_obj->virt, get_actual_dma_addr(gem_obj));
> > +
> > +     cleanup_gem_object_fields(gem_obj);
> > +}
> > +
> > +/**
> > + * qda_dma_mmap() - Map DMA memory into userspace
> > + * @gem_obj: Pointer to GEM object containing DMA memory
> > + * @vma: Virtual memory area to map into
> > + *
> > + * Return: 0 on success, negative error code on failure
> > + */
> > +int qda_dma_mmap(struct qda_gem_obj *gem_obj, struct vm_area_struct *vma)
> > +{
> > +     return dma_mmap_coherent(gem_obj->iommu_dev->dev, vma, gem_obj->virt,
> > +                              get_actual_dma_addr(gem_obj), gem_obj->size);
> > +}
> > diff --git a/drivers/accel/qda/qda_memory_dma.h b/drivers/accel/qda/qda_memory_dma.h
> > new file mode 100644
> > index 000000000000..99352a99dc33
> > --- /dev/null
> > +++ b/drivers/accel/qda/qda_memory_dma.h
> > @@ -0,0 +1,17 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> > + */
> > +
> > +#ifndef __QDA_MEMORY_DMA_H__
> > +#define __QDA_MEMORY_DMA_H__
> > +
> > +#include <linux/dma-mapping.h>
> > +#include "qda_memory_manager.h"
> > +
> > +int qda_dma_alloc(struct qda_iommu_device *iommu_dev,
> > +               struct qda_gem_obj *gem_obj, size_t size);
> > +void qda_dma_free(struct qda_gem_obj *gem_obj);
> > +int qda_dma_mmap(struct qda_gem_obj *gem_obj, struct vm_area_struct *vma);
> > +
> > +#endif /* __QDA_MEMORY_DMA_H__ */
> > diff --git a/drivers/accel/qda/qda_memory_manager.c b/drivers/accel/qda/qda_memory_manager.c
> > index b1a80ee77c35..d47c7419e11d 100644
> > --- a/drivers/accel/qda/qda_memory_manager.c
> > +++ b/drivers/accel/qda/qda_memory_manager.c
> > @@ -1,14 +1,26 @@
> >  // SPDX-License-Identifier: GPL-2.0-only
> >  // Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> >
> > +#include <linux/refcount.h>
> >  #include <linux/slab.h>
> > +#include <linux/spinlock.h>
> > +#include <drm/drm_file.h>
> > +#include <drm/drm_print.h>
> >  #include "qda_drv.h"
> > +#include "qda_gem.h"
> >  #include "qda_memory_manager.h"
> > +#include "qda_memory_dma.h"
> > +
> > +static void iommu_device_free(struct kref *ref)
> > +{
> > +     kfree(container_of(ref, struct qda_iommu_device, ref));
> > +}
> >
> >  static void cleanup_all_memory_devices(struct qda_memory_manager *mem_mgr)
> >  {
> >       int i;
> >
> > +     mutex_lock(&mem_mgr->process_assignment_lock);
> >       for (i = 0; i < mem_mgr->num_devices; i++) {
> >               struct qda_iommu_device *iommu_dev = mem_mgr->devices[i];
> >
> > @@ -16,9 +28,215 @@ static void cleanup_all_memory_devices(struct qda_memory_manager *mem_mgr)
> >                       continue;
> >
> >               mem_mgr->devices[i] = NULL;
> > -             kfree(iommu_dev);
> > +             kref_put(&iommu_dev->ref, iommu_device_free);
> >       }
> >       mem_mgr->num_devices = 0;
> > +     mutex_unlock(&mem_mgr->process_assignment_lock);
> > +}
> > +
> > +static void init_iommu_device_fields(struct qda_iommu_device *iommu_dev)
> > +{
> > +     spin_lock_init(&iommu_dev->lock);
> > +     refcount_set(&iommu_dev->users, 0);
> > +     kref_init(&iommu_dev->ref);
> > +     iommu_dev->assigned_pid = 0;
> > +     iommu_dev->assigned_file_priv = NULL;
> > +}
> > +
> > +static struct qda_iommu_device *find_device_for_pid(struct qda_memory_manager *mem_mgr,
> > +                                                 pid_t pid)
> > +{
> > +     struct qda_iommu_device *found_dev = NULL;
> > +     unsigned long flags;
> > +     int i;
> > +
> > +     for (i = 0; i < mem_mgr->num_devices; i++) {
> > +             struct qda_iommu_device *iommu_dev = mem_mgr->devices[i];
> > +
> > +             if (!iommu_dev)
> > +                     continue;
> > +
> > +             spin_lock_irqsave(&iommu_dev->lock, flags);
> > +             if (iommu_dev->assigned_pid == pid) {
> > +                     found_dev = iommu_dev;
> > +                     refcount_inc(&found_dev->users);
> > +                     kref_get(&found_dev->ref);
> > +                     spin_unlock_irqrestore(&iommu_dev->lock, flags);
> > +                     break;
> > +             }
> > +             spin_unlock_irqrestore(&iommu_dev->lock, flags);
> > +     }
> > +
> > +     return found_dev;
> > +}
> > +
> > +static struct qda_iommu_device *assign_available_device_to_pid(struct qda_memory_manager *mem_mgr,
> > +                                                            pid_t pid,
> > +                                                            struct drm_file *file_priv)
> > +{
> > +     struct qda_iommu_device *selected_dev = NULL;
> > +     unsigned long flags;
> > +     int i;
> > +
> > +     for (i = 0; i < mem_mgr->num_devices; i++) {
> > +             struct qda_iommu_device *iommu_dev = mem_mgr->devices[i];
> > +
> > +             if (!iommu_dev)
> > +                     continue;
> > +
> > +             spin_lock_irqsave(&iommu_dev->lock, flags);
> > +             if (iommu_dev->assigned_pid == 0) {
> > +                     iommu_dev->assigned_pid = pid;
> > +                     iommu_dev->assigned_file_priv = file_priv;
> > +                     selected_dev = iommu_dev;
> > +                     refcount_set(&selected_dev->users, 1);
> > +                     kref_get(&selected_dev->ref);
> > +                     spin_unlock_irqrestore(&iommu_dev->lock, flags);
> > +                     break;
> > +             }
> > +             spin_unlock_irqrestore(&iommu_dev->lock, flags);
> > +     }
> > +
> > +     return selected_dev;
> > +}
> > +
> > +static struct qda_iommu_device *get_process_iommu_device(struct qda_memory_manager *mem_mgr,
> > +                                                      struct drm_file *file_priv)
> > +{
> > +     struct qda_file_priv *qda_priv;
> > +
> > +     if (!file_priv || !file_priv->driver_priv)
> > +             return NULL;
> > +
> > +     qda_priv = (struct qda_file_priv *)file_priv->driver_priv;
> > +     return qda_priv->assigned_iommu_dev;
> > +}
> > +
> > +/**
> > + * qda_memory_manager_assign_device() - Assign an IOMMU device to a process
> > + * @mem_mgr: Pointer to memory manager
> > + * @file_priv: DRM file private data for process association
> > + *
> > + * On success the caller owns a reference on the device and must release it
> > + * with qda_memory_manager_release_device().
> > + *
> > + * Return: 0 on success, negative error code on failure
> > + */
> > +int qda_memory_manager_assign_device(struct qda_memory_manager *mem_mgr,
> > +                                  struct drm_file *file_priv)
> > +{
> > +     struct qda_file_priv *qda_priv;
> > +     struct qda_iommu_device *selected_dev = NULL;
> > +     int ret = 0;
> > +     pid_t current_pid;
> > +
> > +     if (!file_priv || !file_priv->driver_priv)
> > +             return -EINVAL;
> > +
> > +     qda_priv = (struct qda_file_priv *)file_priv->driver_priv;
> > +     current_pid = qda_priv->pid;
> > +
> > +     mutex_lock(&mem_mgr->process_assignment_lock);
> > +
> > +     if (qda_priv->assigned_iommu_dev) {
> > +             ret = 0;
> > +             goto unlock_and_return;
> > +     }
> > +
> > +     selected_dev = find_device_for_pid(mem_mgr, current_pid);
> > +
> > +     if (selected_dev) {
> > +             qda_priv->assigned_iommu_dev = selected_dev;
> > +             goto unlock_and_return;
> > +     }
> > +
> > +     selected_dev = assign_available_device_to_pid(mem_mgr, current_pid, file_priv);
> > +
> > +     if (!selected_dev) {
> > +             drm_dbg_driver(file_priv->minor->dev, "No context bank available for PID %d\n",
> > +                            current_pid);
> > +             ret = -EBUSY;
> > +             goto unlock_and_return;
> > +     }
> > +
> > +     qda_priv->assigned_iommu_dev = selected_dev;
> > +
> > +unlock_and_return:
> > +     mutex_unlock(&mem_mgr->process_assignment_lock);
> > +
> > +     return ret;
> > +}
> > +
> > +/**
> > + * qda_memory_manager_release_device() - Release a process assignment
> > + * @iommu_dev: Device previously obtained from qda_memory_manager_assign_device()
> > + *
> > + * Drops the caller's reference. Once the last process using the device is
> > + * gone the assignment is cleared so the device can serve another process.
> > + */
> > +void qda_memory_manager_release_device(struct qda_iommu_device *iommu_dev)
> > +{
> > +     unsigned long flags;
> > +
> > +     if (refcount_dec_and_test(&iommu_dev->users)) {
> > +             spin_lock_irqsave(&iommu_dev->lock, flags);
> > +             iommu_dev->assigned_pid = 0;
> > +             iommu_dev->assigned_file_priv = NULL;
> > +             spin_unlock_irqrestore(&iommu_dev->lock, flags);
> > +     }
> > +
> > +     kref_put(&iommu_dev->ref, iommu_device_free);
> > +}
> > +
> > +static struct qda_iommu_device *get_or_assign_iommu_device(struct qda_memory_manager *mem_mgr,
> > +                                                        struct drm_file *file_priv)
> > +{
> > +     struct qda_iommu_device *iommu_dev;
> > +     int ret;
> > +
> > +     iommu_dev = get_process_iommu_device(mem_mgr, file_priv);
> > +     if (iommu_dev)
> > +             return iommu_dev;
> > +
> > +     ret = qda_memory_manager_assign_device(mem_mgr, file_priv);
> > +     if (ret)
> > +             return NULL;
> > +
> > +     iommu_dev = get_process_iommu_device(mem_mgr, file_priv);
> > +     if (iommu_dev)
> > +             return iommu_dev;
> > +
> > +     return NULL;
> > +}
> > +
> > +/**
> > + * qda_memory_manager_alloc() - Allocate memory for a GEM object
> > + * @mem_mgr: Pointer to memory manager
> > + * @gem_obj: Pointer to GEM object to allocate memory for
> > + * @file_priv: DRM file private data for process association
> > + *
> > + * Return: 0 on success, negative error code on failure
> > + */
> > +int qda_memory_manager_alloc(struct qda_memory_manager *mem_mgr, struct qda_gem_obj *gem_obj,
> > +                          struct drm_file *file_priv)
> > +{
> > +     struct qda_iommu_device *selected_dev;
> > +
> > +     selected_dev = get_or_assign_iommu_device(mem_mgr, file_priv);
> > +     if (!selected_dev)
> > +             return -EBUSY;
> > +
> > +     return qda_dma_alloc(selected_dev, gem_obj, gem_obj->size);
> > +}
> > +
> > +/**
> > + * qda_memory_manager_free() - Free memory for a GEM object
> > + * @mem_mgr: Pointer to memory manager
> > + * @gem_obj: Pointer to GEM object to free memory for
> > + */
> > +void qda_memory_manager_free(struct qda_memory_manager *mem_mgr, struct qda_gem_obj *gem_obj)
> > +{
> > +     qda_dma_free(gem_obj);
> >  }
> >
> >  /**
> > @@ -31,13 +249,21 @@ static void cleanup_all_memory_devices(struct qda_memory_manager *mem_mgr)
> >  int qda_memory_manager_register_device(struct qda_memory_manager *mem_mgr,
> >                                      struct qda_iommu_device *iommu_dev)
> >  {
> > -     if (mem_mgr->num_devices >= mem_mgr->max_devices)
> > -             return -ENOSPC;
> > +     int ret = 0;
> >
> > +     mutex_lock(&mem_mgr->process_assignment_lock);
> > +     if (mem_mgr->num_devices >= mem_mgr->max_devices) {
> > +             ret = -ENOSPC;
> > +             goto out;
> > +     }
> > +
> > +     init_iommu_device_fields(iommu_dev);
> >       iommu_dev->id = mem_mgr->num_devices;
> >       mem_mgr->devices[mem_mgr->num_devices++] = iommu_dev;
> >
> > -     return 0;
> > +out:
> > +     mutex_unlock(&mem_mgr->process_assignment_lock);
> > +     return ret;
> >  }
> >
> >  /**
> > @@ -52,14 +278,16 @@ void qda_memory_manager_unregister_device(struct qda_memory_manager *mem_mgr,
> >  {
> >       int i;
> >
> > +     mutex_lock(&mem_mgr->process_assignment_lock);
> >       for (i = 0; i < mem_mgr->num_devices; i++) {
> >               if (mem_mgr->devices[i] == iommu_dev) {
> >                       mem_mgr->devices[i] = NULL;
> >                       break;
> >               }
> >       }
> > +     mutex_unlock(&mem_mgr->process_assignment_lock);
> >
> > -     kfree(iommu_dev);
> > +     kref_put(&iommu_dev->ref, iommu_device_free);
> >  }
> >
> >  /**
> > @@ -80,6 +308,7 @@ int qda_memory_manager_init(struct qda_memory_manager *mem_mgr, int max_devices)
> >
> >       mem_mgr->num_devices = 0;
> >       mem_mgr->max_devices = max_devices;
> > +     mutex_init(&mem_mgr->process_assignment_lock);
> >
> >       return 0;
> >  }
> > diff --git a/drivers/accel/qda/qda_memory_manager.h b/drivers/accel/qda/qda_memory_manager.h
> > index 7e38c8a18284..0949c0c213f6 100644
> > --- a/drivers/accel/qda/qda_memory_manager.h
> > +++ b/drivers/accel/qda/qda_memory_manager.h
> > @@ -7,25 +7,44 @@
> >  #define __QDA_MEMORY_MANAGER_H__
> >
> >  #include <linux/device.h>
> > +#include <linux/kref.h>
> > +#include <linux/mutex.h>
> > +#include <linux/refcount.h>
> > +#include <linux/spinlock.h>
> > +#include <drm/drm_file.h>
> >
> >  /* Forward declarations */
> >  struct qda_dev;
> > +struct qda_gem_obj;
> >
> >  /**
> >   * struct qda_iommu_device - IOMMU device instance for memory management
> >   *
> >   * Represents a single IOMMU-enabled device managed by the memory manager.
> >   * Each device can be assigned to a specific process session.
> > + *
> > + * The object outlives its registration: open files keep a reference through
> > + * @ref, so a device can be unregistered while a process still holds it.
> >   */
> >  struct qda_iommu_device {
> >       /** @dev: Pointer to the underlying device */
> >       struct device *dev;
> >       /** @qdev: Back-pointer to the parent QDA device */
> >       struct qda_dev *qdev;
> > +     /** @assigned_file_priv: DRM file private data for the assigned process */
> > +     struct drm_file *assigned_file_priv;
> >       /** @id: Unique identifier assigned by the memory manager */
> >       u32 id;
> >       /** @sid: Stream ID for IOMMU transactions */
> >       u32 sid;
> > +     /** @assigned_pid: Process ID of the process assigned to this device */
> > +     pid_t assigned_pid;
> > +     /** @users: Number of open files sharing this device */
> > +     refcount_t users;
> > +     /** @ref: Reference count controlling when the object is freed */
> > +     struct kref ref;
> > +     /** @lock: Spinlock protecting concurrent access to device */
> > +     spinlock_t lock;
> >  };
> >
> >  /**
> > @@ -42,6 +61,8 @@ struct qda_memory_manager {
> >       int num_devices;
> >       /** @max_devices: Capacity of the @devices array */
> >       int max_devices;
> > +     /** @process_assignment_lock: Mutex protecting process-to-device assignments */
> > +     struct mutex process_assignment_lock;
> >  };
> >
> >  int qda_memory_manager_init(struct qda_memory_manager *mem_mgr, int max_devices);
> > @@ -51,5 +72,14 @@ int qda_memory_manager_register_device(struct qda_memory_manager *mem_mgr,
> >                                      struct qda_iommu_device *iommu_dev);
> >  void qda_memory_manager_unregister_device(struct qda_memory_manager *mem_mgr,
> >                                         struct qda_iommu_device *iommu_dev);
> > +int qda_memory_manager_assign_device(struct qda_memory_manager *mem_mgr,
> > +                                  struct drm_file *file_priv);
> > +void qda_memory_manager_release_device(struct qda_iommu_device *iommu_dev);
> > +
> > +int qda_memory_manager_alloc(struct qda_memory_manager *mem_mgr,
> > +                          struct qda_gem_obj *gem_obj,
> > +                          struct drm_file *file_priv);
> > +void qda_memory_manager_free(struct qda_memory_manager *mem_mgr,
> > +                          struct qda_gem_obj *gem_obj);
> >
> >  #endif /* __QDA_MEMORY_MANAGER_H__ */
> >
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.