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

From: Dmitry Baryshkov

Date: Wed May 20 2026 - 10:12:40 EST


On Tue, May 19, 2026 at 11:46:03AM +0530, Ekansh Gupta via B4 Relay wrote:
> 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.

What is the difference?

>
> 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),

Why is it being added in the middle?

> 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) {

Why is it non-NULL here?

> + 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);

Where is INIT_CREATE_ATTR, which you described earlier?

> +}
> +
> +/**
> + * 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
>
>

--
With best wishes
Dmitry