[PATCH 13/15] accel/qda: Add DSP process creation and release

From: Ekansh Gupta via B4 Relay

Date: Tue May 19 2026 - 02:23:58 EST


From: Ekansh Gupta <ekansh.gupta@xxxxxxxxxxxxxxxx>

Implement the REMOTE_SESSION_CREATE and INIT_RELEASE FastRPC
operations, which establish and tear down a user process on the
DSP.

DRM_IOCTL_QDA_REMOTE_SESSION_CREATE (drm_qda_init_create)
Creates a new process on the DSP by sending an INIT_CREATE message
via the FastRPC INIT_HANDLE. The caller provides an ELF file (via
DMA-BUF fd or direct pointer) and optional process attributes. A
4 MB GEM buffer is allocated per session to hold the DSP process
image; this buffer is stored in qda_file_priv and reused for the
lifetime of the session.

If attrs is non-zero, INIT_CREATE_ATTR is used instead of
INIT_CREATE to pass the extended attribute and signature fields.

INIT_RELEASE
Sends a release message to the DSP when the DRM file is closed
(qda_postclose via qda_release_dsp_process), freeing the remote
process and its resources. The release is skipped if the device
has already been unplugged.

qda_fastrpc.c
fastrpc_prepare_args_init_create() marshals the six-argument
create-process payload: the inbuf descriptor, process name,
ELF file, physical pages, attrs, and siglen.
fastrpc_prepare_args_release_process() marshals the single-
argument release payload (remote_session_id).

qda_drv.c
qda_postclose() is extended to call qda_release_dsp_process()
under drm_dev_enter() so the release message is only sent while
the device is still accessible.

Assisted-by: Claude:claude-4-6-sonnet
Signed-off-by: Ekansh Gupta <ekansh.gupta@xxxxxxxxxxxxxxxx>
---
drivers/accel/qda/qda_drv.c | 8 +++
drivers/accel/qda/qda_drv.h | 5 ++
drivers/accel/qda/qda_fastrpc.c | 140 ++++++++++++++++++++++++++++++++++++++++
drivers/accel/qda/qda_fastrpc.h | 39 +++++++++--
drivers/accel/qda/qda_ioctl.c | 52 +++++++++++++++
drivers/accel/qda/qda_ioctl.h | 1 +
include/uapi/drm/qda_accel.h | 32 ++++++++-
7 files changed, 270 insertions(+), 7 deletions(-)

diff --git a/drivers/accel/qda/qda_drv.c b/drivers/accel/qda/qda_drv.c
index 704c7d3127d2..4eaba9b050c0 100644
--- a/drivers/accel/qda/qda_drv.c
+++ b/drivers/accel/qda/qda_drv.c
@@ -36,6 +36,13 @@ static int qda_open(struct drm_device *dev, struct drm_file *file)
static void qda_postclose(struct drm_device *dev, struct drm_file *file)
{
struct qda_file_priv *qda_file_priv = file->driver_priv;
+ int idx;
+
+ /* Only send the DSP release message while the device is accessible */
+ if (drm_dev_enter(dev, &idx)) {
+ qda_release_dsp_process(qda_file_priv->qda_dev, file);
+ drm_dev_exit(idx);
+ }

if (qda_file_priv->assigned_iommu_dev) {
struct qda_iommu_device *iommu_dev = qda_file_priv->assigned_iommu_dev;
@@ -59,6 +66,7 @@ static const struct drm_ioctl_desc qda_ioctls[] = {
DRM_IOCTL_DEF_DRV(QDA_QUERY, qda_ioctl_query, 0),
DRM_IOCTL_DEF_DRV(QDA_GEM_CREATE, qda_ioctl_gem_create, 0),
DRM_IOCTL_DEF_DRV(QDA_GEM_MMAP_OFFSET, qda_ioctl_gem_mmap_offset, 0),
+ DRM_IOCTL_DEF_DRV(QDA_REMOTE_SESSION_CREATE, qda_ioctl_init_create, 0),
DRM_IOCTL_DEF_DRV(QDA_REMOTE_INVOKE, qda_ioctl_invoke, 0),
};

diff --git a/drivers/accel/qda/qda_drv.h b/drivers/accel/qda/qda_drv.h
index 420cccff42bf..4b4639961d95 100644
--- a/drivers/accel/qda/qda_drv.h
+++ b/drivers/accel/qda/qda_drv.h
@@ -28,6 +28,8 @@ struct qda_file_priv {
struct qda_dev *qda_dev;
/** @assigned_iommu_dev: IOMMU device assigned to this process */
struct qda_iommu_device *assigned_iommu_dev;
+ /** @init_mem_gem_obj: GEM object for PD initialization memory */
+ struct qda_gem_obj *init_mem_gem_obj;
/** @pid: Process ID for tracking */
pid_t pid;
/** @remote_session_id: Unique session identifier */
@@ -83,4 +85,7 @@ void qda_deinit_device(struct qda_dev *qdev);
int qda_register_device(struct qda_dev *qdev);
void qda_unregister_device(struct qda_dev *qdev);

+/* DSP process / protection domain management */
+int qda_release_dsp_process(struct qda_dev *qdev, struct drm_file *file_priv);
+
#endif /* __QDA_DRV_H__ */
diff --git a/drivers/accel/qda/qda_fastrpc.c b/drivers/accel/qda/qda_fastrpc.c
index 0ec37175a098..305915022b91 100644
--- a/drivers/accel/qda/qda_fastrpc.c
+++ b/drivers/accel/qda/qda_fastrpc.c
@@ -524,6 +524,138 @@ int qda_fastrpc_invoke_unpack(struct fastrpc_invoke_context *ctx,
return err;
}

+static void setup_create_process_args(struct drm_qda_fastrpc_invoke_args *args,
+ struct fastrpc_create_process_inbuf *inbuf,
+ struct drm_qda_init_create *init,
+ struct fastrpc_phy_page *pages)
+{
+ args[0].ptr = (u64)(uintptr_t)inbuf;
+ args[0].length = sizeof(*inbuf);
+ args[0].fd = -1;
+
+ args[1].ptr = (u64)(uintptr_t)current->comm;
+ args[1].length = inbuf->namelen;
+ args[1].fd = -1;
+
+ args[2].ptr = (u64)init->file;
+ args[2].length = inbuf->filelen;
+ args[2].fd = init->filefd; /* DMA-BUF fd forwarded to DSP */
+
+ args[3].ptr = (u64)(uintptr_t)pages;
+ args[3].length = 1 * sizeof(*pages);
+ args[3].fd = -1;
+
+ args[4].ptr = (u64)(uintptr_t)&inbuf->attrs;
+ args[4].length = sizeof(inbuf->attrs);
+ args[4].fd = -1;
+
+ args[5].ptr = (u64)(uintptr_t)&inbuf->siglen;
+ args[5].length = sizeof(inbuf->siglen);
+ args[5].fd = -1;
+}
+
+static void setup_single_arg(struct drm_qda_fastrpc_invoke_args *args, const void *ptr, size_t size)
+{
+ args[0].ptr = (u64)(uintptr_t)ptr;
+ args[0].length = size;
+ args[0].fd = -1;
+}
+
+static int fastrpc_prepare_args_release_process(struct fastrpc_invoke_context *ctx)
+{
+ struct drm_qda_fastrpc_invoke_args *args;
+
+ args = kzalloc_obj(*args);
+ if (!args)
+ return -ENOMEM;
+
+ setup_single_arg(args, &ctx->remote_session_id, sizeof(ctx->remote_session_id));
+ ctx->sc = FASTRPC_SCALARS(FASTRPC_RMID_INIT_RELEASE, 1, 0);
+ ctx->args = args;
+ ctx->handle = FASTRPC_INIT_HANDLE;
+
+ return 0;
+}
+
+static int fastrpc_prepare_args_init_create(struct fastrpc_invoke_context *ctx,
+ char __user *argp)
+{
+ struct drm_qda_init_create init;
+ struct drm_qda_fastrpc_invoke_args *args;
+ struct fastrpc_create_process_inbuf *inbuf;
+ int err;
+ u32 sc;
+
+ args = kcalloc(FASTRPC_CREATE_PROCESS_NARGS, sizeof(*args), GFP_KERNEL);
+ if (!args)
+ return -ENOMEM;
+
+ ctx->input_pages = kcalloc(1, sizeof(*ctx->input_pages), GFP_KERNEL);
+ if (!ctx->input_pages) {
+ err = -ENOMEM;
+ goto err_free_args;
+ }
+
+ ctx->inbuf = kcalloc(1, sizeof(*inbuf), GFP_KERNEL);
+ if (!ctx->inbuf) {
+ err = -ENOMEM;
+ goto err_free_input_pages;
+ }
+ inbuf = ctx->inbuf;
+
+ memcpy(&init, argp, sizeof(init));
+
+ if (init.filelen > FASTRPC_INIT_FILELEN_MAX) {
+ err = -EINVAL;
+ goto err_free_inbuf;
+ }
+
+ /*
+ * Validate that the DMA-BUF fd is importable. The fd itself is kept
+ * in init.filefd and forwarded to the DSP via setup_create_process_args().
+ */
+ if (init.filelen && init.filefd > 0) {
+ struct drm_gem_object *file_gem_obj;
+
+ err = get_gem_obj_from_dmabuf_fd(ctx, init.filefd, &file_gem_obj);
+ if (err) {
+ err = -EINVAL;
+ goto err_free_inbuf;
+ }
+ drm_gem_object_put(file_gem_obj);
+ }
+
+ inbuf->remote_session_id = ctx->remote_session_id;
+ inbuf->namelen = strlen(current->comm) + 1;
+ inbuf->filelen = init.filelen;
+ inbuf->pageslen = 1;
+ inbuf->attrs = init.attrs;
+ inbuf->siglen = init.siglen;
+
+ setup_pages_from_gem_obj(ctx->init_mem_gem_obj, &ctx->input_pages[0]);
+
+ setup_create_process_args(args, inbuf, &init, ctx->input_pages);
+
+ sc = FASTRPC_SCALARS(FASTRPC_RMID_INIT_CREATE, 4, 0);
+ if (init.attrs)
+ sc = FASTRPC_SCALARS(FASTRPC_RMID_INIT_CREATE_ATTR, 4, 0);
+ ctx->sc = sc;
+ ctx->args = args;
+ ctx->handle = FASTRPC_INIT_HANDLE;
+
+ return 0;
+
+err_free_inbuf:
+ kfree(ctx->inbuf);
+ ctx->inbuf = NULL;
+err_free_input_pages:
+ kfree(ctx->input_pages);
+ ctx->input_pages = NULL;
+err_free_args:
+ kfree(args);
+ return err;
+}
+
static int fastrpc_prepare_args_invoke(struct fastrpc_invoke_context *ctx, char __user *argp)
{
struct drm_qda_invoke_args invoke_args;
@@ -568,6 +700,14 @@ int qda_fastrpc_prepare_args(struct fastrpc_invoke_context *ctx, char __user *ar
int err;

switch (ctx->type) {
+ case FASTRPC_RMID_INIT_RELEASE:
+ err = fastrpc_prepare_args_release_process(ctx);
+ break;
+ case FASTRPC_RMID_INIT_CREATE:
+ case FASTRPC_RMID_INIT_CREATE_ATTR:
+ ctx->pd = QDA_USER_PD;
+ err = fastrpc_prepare_args_init_create(ctx, argp);
+ break;
case FASTRPC_RMID_INVOKE_DYNAMIC:
err = fastrpc_prepare_args_invoke(ctx, argp);
break;
diff --git a/drivers/accel/qda/qda_fastrpc.h b/drivers/accel/qda/qda_fastrpc.h
index ce77baeccfba..1c1236f9525e 100644
--- a/drivers/accel/qda/qda_fastrpc.h
+++ b/drivers/accel/qda/qda_fastrpc.h
@@ -127,6 +127,27 @@ struct fastrpc_invoke_buf {
u32 pgidx;
};

+/**
+ * struct fastrpc_create_process_inbuf - Input buffer for process creation
+ *
+ * This structure defines the input buffer format for creating a new
+ * process on the remote DSP.
+ */
+struct fastrpc_create_process_inbuf {
+ /** @remote_session_id: Client identifier for the session */
+ int remote_session_id;
+ /** @namelen: Length of the process name string including NUL terminator */
+ u32 namelen;
+ /** @filelen: Length of the ELF shell file in bytes */
+ u32 filelen;
+ /** @pageslen: Number of physical page descriptors */
+ u32 pageslen;
+ /** @attrs: Process attribute flags */
+ u32 attrs;
+ /** @siglen: Length of the signature data in bytes */
+ u32 siglen;
+};
+
/**
* struct fastrpc_msg - FastRPC wire message for remote invocations
*
@@ -153,10 +174,6 @@ struct fastrpc_msg {

/**
* struct qda_msg - FastRPC message with kernel-internal bookkeeping
- *
- * The wire-format portion is kept in the embedded @fastrpc member (must
- * be first) so that &qda_msg->fastrpc can be passed directly to
- * rpmsg_send() without a copy.
*/
struct qda_msg {
/**
@@ -245,7 +262,7 @@ struct fastrpc_invoke_context {
struct qda_gem_obj *msg_gem_obj;
/** @file_priv: DRM file private data */
struct drm_file *file_priv;
- /** @init_mem_gem_obj: GEM object for protection domain init memory */
+ /** @init_mem_gem_obj: GEM object for PD initialization memory */
struct qda_gem_obj *init_mem_gem_obj;
/** @req: Pointer to kernel-internal request buffer */
void *req;
@@ -256,11 +273,23 @@ struct fastrpc_invoke_context {
};

/* Remote Method ID table - identifies initialization and control operations */
+#define FASTRPC_RMID_INIT_RELEASE 1 /* Release DSP process */
+#define FASTRPC_RMID_INIT_CREATE 6 /* Create DSP process */
+#define FASTRPC_RMID_INIT_CREATE_ATTR 7 /* Create DSP process with attributes */
#define FASTRPC_RMID_INVOKE_DYNAMIC 0xFFFFFFFF /* Dynamic method invocation */

/* Common handle for initialization operations */
#define FASTRPC_INIT_HANDLE 0x1

+/* Protection Domain (PD) identifiers */
+#define QDA_ROOT_PD (0)
+#define QDA_USER_PD (1)
+
+/* Number of arguments for process creation */
+#define FASTRPC_CREATE_PROCESS_NARGS 6
+/* Maximum initialization file size (4 MB) */
+#define FASTRPC_INIT_FILELEN_MAX (4 * 1024 * 1024)
+
void qda_fastrpc_context_free(struct kref *ref);
struct fastrpc_invoke_context *qda_fastrpc_context_alloc(void);
int qda_fastrpc_prepare_args(struct fastrpc_invoke_context *ctx, char __user *argp);
diff --git a/drivers/accel/qda/qda_ioctl.c b/drivers/accel/qda/qda_ioctl.c
index c81268c20b04..33f0a798ad13 100644
--- a/drivers/accel/qda/qda_ioctl.c
+++ b/drivers/accel/qda/qda_ioctl.c
@@ -109,6 +109,7 @@ static int fastrpc_invoke(int type, struct drm_device *dev, void *data,
struct drm_gem_object *gem_obj;
int err;
size_t hdr_size;
+ size_t initmem_size = FASTRPC_INIT_FILELEN_MAX;

ctx = qda_fastrpc_context_alloc();
if (IS_ERR(ctx))
@@ -124,6 +125,27 @@ static int fastrpc_invoke(int type, struct drm_device *dev, void *data,
ctx->file_priv = file_priv;
ctx->remote_session_id = qda_file_priv->remote_session_id;

+ if (type == FASTRPC_RMID_INIT_CREATE) {
+ struct drm_gem_object *initmem_gem_obj;
+
+ if (qda_file_priv->init_mem_gem_obj) {
+ drm_gem_object_put(&qda_file_priv->init_mem_gem_obj->base);
+ qda_file_priv->init_mem_gem_obj = NULL;
+ }
+
+ initmem_gem_obj = qda_gem_create_object(dev, qdev->iommu_mgr,
+ initmem_size, file_priv);
+ if (IS_ERR(initmem_gem_obj)) {
+ err = PTR_ERR(initmem_gem_obj);
+ goto err_context_free;
+ }
+
+ ctx->init_mem_gem_obj = to_qda_gem_obj(initmem_gem_obj);
+ qda_file_priv->init_mem_gem_obj = ctx->init_mem_gem_obj;
+ } else if (type == FASTRPC_RMID_INIT_RELEASE) {
+ ctx->init_mem_gem_obj = qda_file_priv->init_mem_gem_obj;
+ }
+
err = qda_fastrpc_prepare_args(ctx, (char __user *)data);
if (err)
goto err_context_free;
@@ -161,11 +183,41 @@ static int fastrpc_invoke(int type, struct drm_device *dev, void *data,
return 0;

err_context_free:
+ if (type == FASTRPC_RMID_INIT_RELEASE && !err && qda_file_priv->init_mem_gem_obj) {
+ drm_gem_object_put(&qda_file_priv->init_mem_gem_obj->base);
+ qda_file_priv->init_mem_gem_obj = NULL;
+ }
+
fastrpc_context_put_id(ctx, qdev);
kref_put(&ctx->refcount, qda_fastrpc_context_free);
return err;
}

+/**
+ * qda_ioctl_init_create() - Create a DSP process
+ * @dev: DRM device structure
+ * @data: User-space data (struct drm_qda_init_create)
+ * @file_priv: DRM file private data
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int qda_ioctl_init_create(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+ return fastrpc_invoke(FASTRPC_RMID_INIT_CREATE, dev, data, file_priv);
+}
+
+/**
+ * qda_release_dsp_process() - Release DSP process resources for a file
+ * @qdev: QDA device structure
+ * @file_priv: DRM file private data
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int qda_release_dsp_process(struct qda_dev *qdev, struct drm_file *file_priv)
+{
+ return fastrpc_invoke(FASTRPC_RMID_INIT_RELEASE, &qdev->drm_dev, NULL, file_priv);
+}
+
/**
* qda_ioctl_invoke() - Perform a dynamic FastRPC method invocation
* @dev: DRM device structure
diff --git a/drivers/accel/qda/qda_ioctl.h b/drivers/accel/qda/qda_ioctl.h
index 3bb9cfd98370..192565434363 100644
--- a/drivers/accel/qda/qda_ioctl.h
+++ b/drivers/accel/qda/qda_ioctl.h
@@ -9,6 +9,7 @@
#include "qda_drv.h"

int qda_ioctl_query(struct drm_device *dev, void *data, struct drm_file *file_priv);
+int qda_ioctl_init_create(struct drm_device *dev, void *data, struct drm_file *file_priv);
int qda_ioctl_gem_create(struct drm_device *dev, void *data, struct drm_file *file_priv);
int qda_ioctl_gem_mmap_offset(struct drm_device *dev, void *data, struct drm_file *file_priv);
int qda_ioctl_invoke(struct drm_device *dev, void *data, struct drm_file *file_priv);
diff --git a/include/uapi/drm/qda_accel.h b/include/uapi/drm/qda_accel.h
index 72512213741f..711e2523a570 100644
--- a/include/uapi/drm/qda_accel.h
+++ b/include/uapi/drm/qda_accel.h
@@ -21,8 +21,9 @@ extern "C" {
#define DRM_QDA_QUERY 0x00
#define DRM_QDA_GEM_CREATE 0x01
#define DRM_QDA_GEM_MMAP_OFFSET 0x02
-/* Command numbers 0x03-0x06 reserved for INIT_ATTACH, INIT_CREATE, MAP, MUNMAP */
-#define DRM_QDA_REMOTE_INVOKE 0x07
+/* Command number 0x03 reserved for INIT_ATTACH; 0x05-0x06 reserved for MAP, MUNMAP */
+#define DRM_QDA_REMOTE_SESSION_CREATE 0x04
+#define DRM_QDA_REMOTE_INVOKE 0x07

/*
* QDA IOCTL definitions
@@ -37,6 +38,9 @@ extern "C" {
struct drm_qda_gem_create)
#define DRM_IOCTL_QDA_GEM_MMAP_OFFSET DRM_IOWR(DRM_COMMAND_BASE + DRM_QDA_GEM_MMAP_OFFSET, \
struct drm_qda_gem_mmap_offset)
+#define DRM_IOCTL_QDA_REMOTE_SESSION_CREATE \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_QDA_REMOTE_SESSION_CREATE, \
+ struct drm_qda_init_create)
#define DRM_IOCTL_QDA_REMOTE_INVOKE DRM_IOWR(DRM_COMMAND_BASE + DRM_QDA_REMOTE_INVOKE, \
struct drm_qda_invoke_args)

@@ -99,6 +103,30 @@ struct drm_qda_fastrpc_invoke_args {
__u32 attr;
};

+/**
+ * struct drm_qda_init_create - Accelerator process initialization parameters
+ * @filelen: Length of the ELF file in bytes
+ * @filefd: DMA-BUF file descriptor containing the ELF file
+ * @attrs: Process attributes flags
+ * @siglen: Length of signature data in bytes
+ * @file: Pointer to ELF file data if not using filefd
+ *
+ * This structure is used with DRM_IOCTL_QDA_INIT_CREATE to initialize
+ * a new process on the accelerator. The process code is provided either
+ * via a file descriptor (filefd, typically a GEM object) or a direct
+ * pointer (file). Set file to 0 if using filefd.
+ *
+ * The attrs field contains bit flags for debug mode, privileged execution,
+ * and other process attributes.
+ */
+struct drm_qda_init_create {
+ __u32 filelen;
+ __s32 filefd;
+ __u32 attrs;
+ __u32 siglen;
+ __u64 file;
+};
+
/**
* struct drm_qda_invoke_args - Dynamic FastRPC invocation parameters
* @handle: Remote handle to invoke on the DSP

--
2.34.1