On Tue, May 19, 2026 at 11:45:57AM +0530, Ekansh Gupta via B4 Relay wrote:
From: Ekansh Gupta ekansh.gupta@oss.qualcomm.com
Introduce the QDA memory manager (qda_memory_manager) to track and manage the IOMMU devices that back each compute context bank (CB).
Each CB device registered on the qda-compute-cb bus is assigned a unique ID via an XArray and wrapped in a qda_iommu_device descriptor
Why do you need an XArray? The number of devices is (more or less) fixed. You can use a normal array, allocated in the probe function after counting OF children nodes.
that records the device pointer and its stream ID. This registry allows the driver to look up the correct IOMMU domain for a given session when mapping DSP buffers.
The memory manager is initialised in qda_init_device() before CB devices are populated and torn down in qda_deinit_device() after they are destroyed, ensuring no dangling references remain in the XArray.
qda_cb.c is extended with qda_cb_setup_device(), which is called immediately after a CB device is registered on the bus. It allocates a qda_iommu_device, registers it with the memory manager, and stores it as the CB device's driver data so that qda_destroy_cb_device() can retrieve and unregister it during teardown.
Assisted-by: Claude:claude-4-6-sonnet Signed-off-by: Ekansh Gupta ekansh.gupta@oss.qualcomm.com
drivers/accel/qda/Makefile | 1 + drivers/accel/qda/qda_cb.c | 47 ++++++++++++++ drivers/accel/qda/qda_drv.c | 34 ++++++++++ drivers/accel/qda/qda_drv.h | 5 ++ drivers/accel/qda/qda_memory_manager.c | 111 +++++++++++++++++++++++++++++++++ drivers/accel/qda/qda_memory_manager.h | 49 +++++++++++++++ drivers/accel/qda/qda_rpmsg.c | 7 +++ 7 files changed, 254 insertions(+)
diff --git a/drivers/accel/qda/Makefile b/drivers/accel/qda/Makefile index 143c9e4e789e..701fad5ffb50 100644 --- a/drivers/accel/qda/Makefile +++ b/drivers/accel/qda/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_DRM_ACCEL_QDA) := qda.o qda-y := \ qda_cb.o \ qda_drv.o \
- qda_memory_manager.o \ qda_rpmsg.o
obj-$(CONFIG_DRM_ACCEL_QDA_COMPUTE_BUS) += qda_compute_bus.o diff --git a/drivers/accel/qda/qda_cb.c b/drivers/accel/qda/qda_cb.c index 77caf8438c67..6d540bb0ec7b 100644 --- a/drivers/accel/qda/qda_cb.c +++ b/drivers/accel/qda/qda_cb.c @@ -8,11 +8,42 @@ #include <linux/slab.h> #include <drm/drm_print.h> #include "qda_drv.h" +#include "qda_memory_manager.h" #include "qda_cb.h" +static int qda_cb_setup_device(struct qda_dev *qdev, struct device *cb_dev, u32 sid) +{
- struct qda_iommu_device *iommu_dev;
- int rc;
- drm_dbg_driver(&qdev->drm_dev, "Setting up CB device %s\n", dev_name(cb_dev));
- iommu_dev = kzalloc_obj(*iommu_dev);
- if (!iommu_dev)
return -ENOMEM;- iommu_dev->dev = cb_dev;
- iommu_dev->qdev = qdev;
- iommu_dev->sid = sid;
- rc = qda_memory_manager_register_device(qdev->iommu_mgr, iommu_dev);
- if (rc) {
drm_err(&qdev->drm_dev, "Failed to register IOMMU device: %d\n", rc);kfree(iommu_dev);return rc;- }
- dev_set_drvdata(cb_dev, iommu_dev);
- drm_dbg_driver(&qdev->drm_dev, "CB device setup complete - SID: %u\n", sid);
- return 0;
+}
int qda_create_cb_device(struct qda_dev *qdev, struct device_node *cb_node) { struct device *cb_dev;
- int ret; u32 sid = 0; char name[64]; struct qda_cb_dev *entry;
@@ -30,6 +61,13 @@ int qda_create_cb_device(struct qda_dev *qdev, struct device_node *cb_node) return PTR_ERR(cb_dev); }
- ret = qda_cb_setup_device(qdev, cb_dev, sid);
- if (ret) {
drm_err(&qdev->drm_dev, "CB device setup failed: %d\n", ret);device_unregister(cb_dev);return ret;- }
- entry = kzalloc_obj(*entry); if (!entry) { device_unregister(cb_dev);
@@ -80,6 +118,7 @@ int qda_cb_populate(struct qda_dev *qdev, struct device_node *parent_node) void qda_destroy_cb_device(struct device *cb_dev) { struct iommu_group *group;
- struct qda_iommu_device *iommu_dev;
if (!cb_dev) { pr_debug("qda: NULL CB device passed to destroy\n"); @@ -88,6 +127,14 @@ void qda_destroy_cb_device(struct device *cb_dev) dev_dbg(cb_dev, "Destroying CB device %s\n", dev_name(cb_dev));
- iommu_dev = dev_get_drvdata(cb_dev);
- if (iommu_dev && iommu_dev->qdev && iommu_dev->qdev->iommu_mgr) {
dev_dbg(cb_dev, "Unregistering IOMMU device for %s\n",dev_name(cb_dev));qda_memory_manager_unregister_device(iommu_dev->qdev->iommu_mgr,iommu_dev);- }
- group = iommu_group_get(cb_dev); if (group) { dev_dbg(cb_dev, "Removing %s from IOMMU group\n", dev_name(cb_dev));
diff --git a/drivers/accel/qda/qda_drv.c b/drivers/accel/qda/qda_drv.c index 6c20d6a2fc47..0ad5d9873d7e 100644 --- a/drivers/accel/qda/qda_drv.c +++ b/drivers/accel/qda/qda_drv.c @@ -57,6 +57,40 @@ struct qda_dev *qda_alloc_device(struct device *dev) return qdev; } +static void cleanup_memory_manager(struct qda_dev *qdev)
Prefixes...
+{
- if (qdev->iommu_mgr) {
qda_memory_manager_exit(qdev->iommu_mgr);kfree(qdev->iommu_mgr);qdev->iommu_mgr = NULL;- }
+}
+static int init_memory_manager(struct qda_dev *qdev) +{
- qdev->iommu_mgr = kzalloc_obj(*qdev->iommu_mgr);
- if (!qdev->iommu_mgr)
return -ENOMEM;- return qda_memory_manager_init(qdev->iommu_mgr);
+}
+void qda_deinit_device(struct qda_dev *qdev) +{
- cleanup_memory_manager(qdev);
Ugh, inline all your one-line wrappers.
+}
+int qda_init_device(struct qda_dev *qdev) +{
- int ret;
- ret = init_memory_manager(qdev);
- if (ret)
drm_err(&qdev->drm_dev, "Failed to initialize memory manager: %d\n", ret);- return ret;
+}
void qda_unregister_device(struct qda_dev *qdev) { drm_dev_unregister(&qdev->drm_dev); diff --git a/drivers/accel/qda/qda_drv.h b/drivers/accel/qda/qda_drv.h index 2715f378775d..eb089e586b17 100644 --- a/drivers/accel/qda/qda_drv.h +++ b/drivers/accel/qda/qda_drv.h @@ -13,6 +13,7 @@ #include <drm/drm_device.h> #include <drm/drm_drv.h> #include <drm/drm_file.h> +#include "qda_memory_manager.h" /* Driver identification */ #define QDA_DRIVER_NAME "qda" @@ -40,6 +41,8 @@ struct qda_dev { struct device *dev; /** @cb_devs: Compute context-bank (CB) child devices */ struct list_head cb_devs;
- /** @iommu_mgr: IOMMU/memory manager instance */
- struct qda_memory_manager *iommu_mgr; /** @dsp_name: Name of the DSP domain (e.g. "cdsp", "adsp") */ const char *dsp_name;
}; @@ -59,6 +62,8 @@ static inline struct qda_dev *qda_dev_from_drm(struct drm_device *dev) struct qda_dev *qda_alloc_device(struct device *dev); /* Core device lifecycle */ +int qda_init_device(struct qda_dev *qdev); +void qda_deinit_device(struct qda_dev *qdev); int qda_register_device(struct qda_dev *qdev); void qda_unregister_device(struct qda_dev *qdev); diff --git a/drivers/accel/qda/qda_memory_manager.c b/drivers/accel/qda/qda_memory_manager.c new file mode 100644 index 000000000000..00a9c0ae4224 --- /dev/null +++ b/drivers/accel/qda/qda_memory_manager.c @@ -0,0 +1,111 @@ +// 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 <linux/xarray.h> +#include <drm/drm_file.h> +#include "qda_drv.h" +#include "qda_memory_manager.h"
+static void cleanup_all_memory_devices(struct qda_memory_manager *mem_mgr) +{
- unsigned long index;
- void *entry;
- pr_debug("qda: Starting cleanup of all memory devices\n");
pr_debug is a third way to debug. Stop it, please.
- xa_for_each(&mem_mgr->device_xa, index, entry) {
struct qda_iommu_device *iommu_dev = entry;pr_debug("qda: Cleaning up device id=%lu\n", index);xa_erase(&mem_mgr->device_xa, index);kfree(iommu_dev);- }
- pr_debug("qda: Completed cleanup of all memory devices\n");
+}