[PATCH V0 13/21] accel/amdxdna: Implement AIE4 kernel queue lifecycle and memory layout

From: David Zhang

Date: Fri Sep 25 2026 - 21:36:33 EST


Initialize kernel-mode submission required buffers, workqueue, and
hardware contexts:
- Update queue definition that is being used to send requests.
- Add job workqueue for pending and running jobs.
- Add mutex protection for each io.
- Initialize queue with direct and indirect packet, format queue header.
- Add kernel-mode submission required steps in hwctx create/destroy.
- Add aie4_get_cert_comp() to safely acquire a reference to the
per-hwctx completion tracker under io_lock in aie4_cmd_wait() before
waiting on the queue, preventing race conditions with
aie4_hwctx_destroy().

Also change aie4_lookup_cert_comp() to return ERR_PTR() instead of NULL
on failure so callers can distinguish error causes, drop the now-stale
drm_WARN_ON() lock assertion in cert_comp_release() (cert_comp_release()
no longer requires cert_comp_lock to be held by all callers), and add
enum aie4_hwctx_flags, a currently-unused flags parameter threaded
through aie4_hwctx_destroy() for use by later patches.

Co-developed-by: Max Zhen <max.zhen@xxxxxxx>
Signed-off-by: Max Zhen <max.zhen@xxxxxxx>
Co-developed-by: Wendy Liang <wendy.liang@xxxxxxx>
Signed-off-by: Wendy Liang <wendy.liang@xxxxxxx>
Signed-off-by: David Zhang <yidong.zhang@xxxxxxx>
---
drivers/accel/amdxdna/aie4_ctx.c | 225 ++++++++++++++++++++----
drivers/accel/amdxdna/aie4_host_queue.h | 65 +++++++
drivers/accel/amdxdna/aie4_pci.h | 54 ++++++
3 files changed, 311 insertions(+), 33 deletions(-)

diff --git a/drivers/accel/amdxdna/aie4_ctx.c b/drivers/accel/amdxdna/aie4_ctx.c
index fba4ef25ffbe..af52a1ea45e8 100644
--- a/drivers/accel/amdxdna/aie4_ctx.c
+++ b/drivers/accel/amdxdna/aie4_ctx.c
@@ -21,6 +21,13 @@
#include "amdxdna_mailbox_helper.h"
#include "amdxdna_pci_drv.h"

+#define CTX_INVALID_ID (~0U)
+#define CTX_INVALID_DOORBELL AMDXDNA_INVALID_DOORBELL_OFFSET
+
+static void job_worker(struct work_struct *work)
+{
+}
+
static struct cert_comp *aie4_lookup_cert_comp(struct amdxdna_dev_hdl *ndev, u32 msix_idx)
{
struct amdxdna_dev *xdna = ndev->aie.xdna;
@@ -37,7 +44,7 @@ static struct cert_comp *aie4_lookup_cert_comp(struct amdxdna_dev_hdl *ndev, u32

cert_comp = kzalloc_obj(*cert_comp);
if (!cert_comp)
- return NULL;
+ return ERR_PTR(-ENOMEM);

cert_comp->ndev = ndev;
cert_comp->msix_idx = msix_idx;
@@ -64,7 +71,7 @@ static struct cert_comp *aie4_lookup_cert_comp(struct amdxdna_dev_hdl *ndev, u32
aie4_free_notification(cert_comp);
free_cert_comp:
kfree(cert_comp);
- return NULL;
+ return ERR_PTR(ret);
}

static void cert_comp_release(struct kref *kref)
@@ -72,8 +79,6 @@ static void cert_comp_release(struct kref *kref)
struct cert_comp *cert_comp = container_of(kref, struct cert_comp, kref);
struct amdxdna_dev_hdl *ndev = cert_comp->ndev;

- drm_WARN_ON(&ndev->aie.xdna->ddev, !mutex_is_locked(&ndev->cert_comp_lock));
-
xa_erase(&ndev->cert_comp_xa, cert_comp->msix_idx);
aie4_free_notification(cert_comp);
kfree(cert_comp);
@@ -81,20 +86,42 @@ static void cert_comp_release(struct kref *kref)

static void aie4_put_cert_comp(struct cert_comp *cert_comp)
{
- struct amdxdna_dev_hdl *ndev;
+ struct amdxdna_dev_hdl *ndev = cert_comp->ndev;

- ndev = cert_comp->ndev;
guard(mutex)(&ndev->cert_comp_lock);

kref_put(&cert_comp->kref, cert_comp_release);
}

-static int aie4_msg_destroy_context(struct amdxdna_dev_hdl *ndev, u32 hw_context_id)
+static struct cert_comp *aie4_get_cert_comp(struct amdxdna_hwctx *hwctx)
+{
+ struct amdxdna_hwctx_priv *priv = hwctx->priv;
+ struct cert_comp *cert_comp;
+
+ /*
+ * priv->cert_comp is the per-hwctx field, guarded by io_lock. A non-NULL
+ * value means this ctx still holds its link-ref, so the object is alive and
+ * the kref_get here cannot race the free.
+ */
+ guard(mutex)(&priv->io_lock);
+
+ cert_comp = READ_ONCE(priv->cert_comp);
+ if (cert_comp)
+ kref_get(&cert_comp->kref);
+
+ return cert_comp;
+}
+
+static void aie4_msg_destroy_context(struct amdxdna_dev_hdl *ndev, u32 hw_context_id)
{
DECLARE_AIE_MSG(aie4_msg_destroy_hw_context, AIE4_MSG_OP_DESTROY_HW_CONTEXT);
+ struct amdxdna_dev *xdna = ndev->aie.xdna;
+ int ret;

req.hw_context_id = hw_context_id;
- return aie_send_mgmt_msg_wait(&ndev->aie, &msg);
+ ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
+ if (ret)
+ XDNA_WARN(xdna, "destroy ctx id %d failed %d", hw_context_id, ret);
}

static u8 aie4_parse_priority_to_dev(u32 priority)
@@ -113,19 +140,20 @@ static u8 aie4_parse_priority_to_dev(u32 priority)
}
}

-static int aie4_hwctx_create(struct amdxdna_hwctx *hwctx)
+int aie4_hwctx_create(struct amdxdna_hwctx *hwctx)
{
DECLARE_AIE_MSG(aie4_msg_create_hw_context, AIE4_MSG_OP_CREATE_HW_CONTEXT);
struct amdxdna_client *client = hwctx->client;
struct amdxdna_hwctx_priv *priv = hwctx->priv;
- struct amdxdna_dev *xdna = hwctx->client->xdna;
+ struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
+ struct cert_comp *cert_comp;
int ret;

drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));

if (!ndev->partition_id || !hwctx->num_tiles) {
- XDNA_ERR(xdna, "invalid request partition_id %d, num_tiles %d",
+ XDNA_ERR(xdna, "invalid request partition_id %u, num_tiles %d",
ndev->partition_id, hwctx->num_tiles);
return -EINVAL;
}
@@ -134,7 +162,6 @@ static int aie4_hwctx_create(struct amdxdna_hwctx *hwctx)
req.request_num_tiles = hwctx->num_tiles;
req.pasid = aie4_msg_pasid(client);
req.priority_band = aie4_parse_priority_to_dev(hwctx->qos.priority);
-
req.hsa_addr_high = upper_32_bits(amdxdna_gem_dev_addr(priv->umq_bo));
req.hsa_addr_low = lower_32_bits(amdxdna_gem_dev_addr(priv->umq_bo));

@@ -148,72 +175,166 @@ static int aie4_hwctx_create(struct amdxdna_hwctx *hwctx)
}

XDNA_DBG(xdna, "resp msix: %d, ctx id: %d, doorbell: %d",
- resp.job_complete_msix_idx,
- resp.hw_context_id,
+ resp.job_complete_msix_idx, resp.hw_context_id,
resp.doorbell_offset);

/* setup interrupt completion per msix index */
- priv->cert_comp = aie4_lookup_cert_comp(ndev, resp.job_complete_msix_idx);
- if (!priv->cert_comp) {
+ cert_comp = aie4_lookup_cert_comp(ndev, resp.job_complete_msix_idx);
+ if (IS_ERR(cert_comp)) {
aie4_msg_destroy_context(ndev, resp.hw_context_id);
- return -EINVAL;
+ return PTR_ERR(cert_comp);
}

priv->hw_ctx_id = resp.hw_context_id;
- hwctx->doorbell_offset = AMDXDNA_INVALID_DOORBELL_OFFSET;
+
+ hwctx->fw_ctx_id = resp.hw_context_id;
+ hwctx->start_col = 0;
+ hwctx->num_col = ndev->total_col;
+
+ /*
+ * Kernel-mode submission: set up this context's doorbell kick target
+ * (transport-specific, via aie4_doorbell_setup) so the driver can ring
+ * it, and keep it out of user space (hand back an invalid offset so the
+ * doorbell cannot be mmap'd/rung by the user).
+ */
+ mutex_lock(&priv->io_lock);
+ ret = aie4_doorbell_setup(hwctx, &resp);
+ if (ret) {
+ mutex_unlock(&priv->io_lock);
+ aie4_put_cert_comp(cert_comp);
+ aie4_msg_destroy_context(ndev, resp.hw_context_id);
+ priv->hw_ctx_id = CTX_INVALID_ID;
+ hwctx->fw_ctx_id = -1;
+ return ret;
+ }
+ WRITE_ONCE(priv->cert_comp, cert_comp);
+ mutex_unlock(&priv->io_lock);
+ hwctx->doorbell_offset = CTX_INVALID_DOORBELL;
+ wake_up_all(&priv->job_list_wq);

return 0;
}

-static void aie4_hwctx_destroy(struct amdxdna_hwctx *hwctx)
+void aie4_hwctx_destroy(struct amdxdna_hwctx *hwctx, enum aie4_hwctx_flags flags)
{
struct amdxdna_client *client = hwctx->client;
struct amdxdna_hwctx_priv *priv = hwctx->priv;
struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
+ struct cert_comp *cert_comp;

drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));

- aie4_msg_destroy_context(ndev, priv->hw_ctx_id);
- aie4_put_cert_comp(priv->cert_comp);
+ mutex_lock(&priv->io_lock);
+ cert_comp = priv->cert_comp;
+ WRITE_ONCE(priv->cert_comp, NULL);
+ mutex_unlock(&priv->io_lock);
+
+ if (cert_comp) {
+ wake_up_all(&cert_comp->waitq);
+ aie4_put_cert_comp(cert_comp);
+ }
+
+ if (flags != AIE4_HWCTX_DISCONNECT)
+ aie4_msg_destroy_context(ndev, priv->hw_ctx_id);
+
+ priv->hw_ctx_id = CTX_INVALID_ID;
+ hwctx->fw_ctx_id = -1;
+ hwctx->doorbell_offset = CTX_INVALID_DOORBELL;
+
+ cancel_work_sync(&priv->job_work);
}

static void aie4_hwctx_umq_fini(struct amdxdna_hwctx *hwctx)
{
if (hwctx->priv && hwctx->priv->umq_bo)
- amdxdna_gem_put_obj(hwctx->priv->umq_bo);
+ drm_gem_object_put(to_gobj(hwctx->priv->umq_bo));
}

static int aie4_hwctx_umq_init(struct amdxdna_hwctx *hwctx)
{
+ const size_t indir_pkts_sz = CTX_MAX_CMDS * HSA_MAX_LEVEL1_INDIRECT_ENTRIES *
+ sizeof(struct host_indirect_packet_data);
+ const size_t pkts_sz = CTX_MAX_CMDS * sizeof(struct host_queue_packet);
struct amdxdna_hwctx_priv *priv = hwctx->priv;
struct amdxdna_dev *xdna = hwctx->client->xdna;
struct amdxdna_gem_obj *umq_bo;
struct host_queue_header *qhdr;
+ u64 data_dev_addr;
+ void *umq_va;
int ret;
+ int i;

+ /*
+ * The HSA queue lives in a user-allocated BO (umq_bo_hdl) in both user- and
+ * kernel-mode submission; the driver does not allocate it privately. Under
+ * PASID/SVA the device reaches the queue through the submitting process's
+ * own page tables, so it must have a user virtual address - a kernel-private
+ * buffer would be unreachable by the device.
+ */
umq_bo = amdxdna_gem_get_obj(hwctx->client, hwctx->umq_bo_hdl, AMDXDNA_BO_SHARE);
if (!umq_bo) {
XDNA_ERR(xdna, "cannot find umq_bo handle %d", hwctx->umq_bo_hdl);
return -ENOENT;
}
- if (umq_bo->mem.size < sizeof(*qhdr)) {
- XDNA_ERR(xdna, "umq_bo size is too small");
+
+ /*
+ * Kernel-mode submission: the driver fills the host queue and rings the
+ * doorbell, so the user umq_bo must hold the header plus the direct and
+ * level-1 indirect packet arrays.
+ */
+ if (umq_bo->mem.size < sizeof(*qhdr) ||
+ (umq_bo->mem.size < sizeof(*qhdr) + pkts_sz + indir_pkts_sz)) {
+ XDNA_ERR(xdna, "umq_bo size %zu is too small",
+ (size_t)umq_bo->mem.size);
ret = -EINVAL;
goto put_umq_bo;
}

- /* get kva address for host queue read index and write index */
- qhdr = amdxdna_gem_vmap(umq_bo);
- if (!qhdr) {
+ umq_va = amdxdna_gem_vmap(umq_bo);
+ if (!umq_va) {
ret = -ENOMEM;
goto put_umq_bo;
}
+ qhdr = umq_va;

priv->umq_bo = umq_bo;
priv->umq_read_index = &qhdr->read_index;
priv->umq_write_index = &qhdr->write_index;

+ /*
+ * The queue content is driver-owned and never trusted from user space
+ * (only read_index is read back to detect completion). Lay out the
+ * direct packets right after the header and the indirect packets after
+ * them, and publish the same base via data_address for CERT.
+ */
+ data_dev_addr = amdxdna_gem_dev_addr(umq_bo) + sizeof(*qhdr);
+ priv->umq_pkts = umq_va + sizeof(*qhdr);
+ priv->umq_indirect_pkts = umq_va + sizeof(*qhdr) + pkts_sz;
+ priv->umq_indirect_pkts_dev_addr = data_dev_addr + pkts_sz;
+
+ /*
+ * Only the header + direct/indirect packet regions are driver-owned and
+ * used for kernel submission; the size check above guarantees they fit.
+ * Clear just that range, not the whole user-sized BO, so an oversized
+ * umq_bo cannot force a huge memset (and page faults) under dev_lock.
+ */
+ memset(umq_va, 0, sizeof(*qhdr) + pkts_sz + indir_pkts_sz);
+ priv->write_index = QUEUE_INDEX_START;
+ qhdr->read_index = QUEUE_INDEX_START;
+ qhdr->write_index = QUEUE_INDEX_START;
+ qhdr->version.major = HOST_QUEUE_MAJOR_VERSION;
+ qhdr->version.minor = HOST_QUEUE_MINOR_VERSION;
+ qhdr->capacity = CTX_MAX_CMDS;
+ qhdr->data_address = data_dev_addr;
+ for (i = 0; i < CTX_MAX_CMDS; i++)
+ priv->umq_pkts[i].pkt_header.common_header.opcode = OPCODE_EXEC_BUF;
+ for (i = 0; i < CTX_MAX_CMDS * HSA_MAX_LEVEL1_INDIRECT_ENTRIES; i++) {
+ priv->umq_indirect_pkts[i].header.opcode = OPCODE_EXEC_BUF;
+ priv->umq_indirect_pkts[i].header.count = sizeof(struct exec_buf);
+ priv->umq_indirect_pkts[i].header.distribute = 1;
+ }
+
return 0;

put_umq_bo:
@@ -225,28 +346,56 @@ int aie4_hwctx_init(struct amdxdna_hwctx *hwctx)
{
struct amdxdna_client *client = hwctx->client;
struct amdxdna_dev *xdna = client->xdna;
+ struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
struct amdxdna_hwctx_priv *priv;
int ret;

+ if (!AIE_FEATURE_ON(&ndev->aie, AIE4_HSA_COMMAND))
+ return -EOPNOTSUPP;
+
priv = kzalloc_obj(*priv);
if (!priv)
return -ENOMEM;
hwctx->priv = priv;
+ priv->hwctx = hwctx;
+
+ /*
+ * io_lock guards the per-hwctx cert_comp binding (the connected sentinel)
+ * for every ctx, so initialize it unconditionally. kzalloc left cert_comp
+ * NULL: disconnected until create links it.
+ */
+ mutex_init(&priv->io_lock);
+
+ INIT_LIST_HEAD(&priv->pending_job_list);
+ INIT_LIST_HEAD(&priv->running_job_list);
+ init_waitqueue_head(&priv->job_list_wq);
+ INIT_WORK(&priv->job_work, job_worker);

ret = aie4_hwctx_umq_init(hwctx);
if (ret)
- goto free_priv;
+ goto destroy_lock;

ret = aie4_hwctx_create(hwctx);
if (ret)
goto umq_fini;

- XDNA_DBG(xdna, "hwctx %s init completed", hwctx->name);
+ priv->job_work_q = alloc_ordered_workqueue("aie4_job_%d_%d", 0,
+ client->pid, hwctx->fw_ctx_id);
+ if (!priv->job_work_q) {
+ XDNA_ERR(xdna, "Create job_work_q failed");
+ ret = -ENOMEM;
+ goto destroy_ctx;
+ }
+
+ XDNA_DBG(xdna, "hwctx %d.%d init completed", client->pid, hwctx->fw_ctx_id);
return 0;

+destroy_ctx:
+ aie4_hwctx_destroy(hwctx, AIE4_HWCTX_NORMAL);
umq_fini:
aie4_hwctx_umq_fini(hwctx);
-free_priv:
+destroy_lock:
+ mutex_destroy(&priv->io_lock);
kfree(priv);
hwctx->priv = NULL;
return ret;
@@ -254,8 +403,14 @@ int aie4_hwctx_init(struct amdxdna_hwctx *hwctx)

void aie4_hwctx_fini(struct amdxdna_hwctx *hwctx)
{
- aie4_hwctx_destroy(hwctx);
+ struct amdxdna_hwctx_priv *priv = hwctx->priv;
+
+ aie4_hwctx_destroy(hwctx, AIE4_HWCTX_ERROR);
+ cancel_work_sync(&priv->job_work);
+ if (priv->job_work_q)
+ destroy_workqueue(priv->job_work_q);
aie4_hwctx_umq_fini(hwctx);
+ mutex_destroy(&priv->io_lock);
kfree(hwctx->priv);
}

@@ -299,10 +454,12 @@ static inline bool check_cmd_done(struct amdxdna_hwctx *hwctx, u64 seq)
int aie4_cmd_wait(struct amdxdna_hwctx *hwctx, u64 seq, u32 timeout)
{
unsigned long wait_jifs = MAX_SCHEDULE_TIMEOUT;
- struct amdxdna_hwctx_priv *priv = hwctx->priv;
- struct cert_comp *cert_comp = priv->cert_comp;
+ struct cert_comp *cert_comp = aie4_get_cert_comp(hwctx);
long ret;

+ if (!cert_comp)
+ return -EAGAIN;
+
if (timeout)
wait_jifs = msecs_to_jiffies(timeout);

@@ -313,5 +470,7 @@ int aie4_cmd_wait(struct amdxdna_hwctx *hwctx, u64 seq, u32 timeout)
if (!ret)
ret = -ETIME;

+ aie4_put_cert_comp(cert_comp);
+
return ret <= 0 ? ret : 0;
}
diff --git a/drivers/accel/amdxdna/aie4_host_queue.h b/drivers/accel/amdxdna/aie4_host_queue.h
index 97e535939b32..6876811b05f2 100644
--- a/drivers/accel/amdxdna/aie4_host_queue.h
+++ b/drivers/accel/amdxdna/aie4_host_queue.h
@@ -6,9 +6,14 @@
#ifndef _AIE4_HOST_QUEUE_H_
#define _AIE4_HOST_QUEUE_H_

+#include <linux/bits.h>
#include <linux/types.h>

#define CTX_MAX_CMDS 32
+#define HSA_MAX_LEVEL1_INDIRECT_ENTRIES 6
+#define QUEUE_INDEX_START 0
+#define HOST_QUEUE_MAJOR_VERSION 1
+#define HOST_QUEUE_MINOR_VERSION 0

/*
* Host queue header layout.
@@ -31,4 +36,64 @@ struct host_queue_header {
__u64 data_address; /* The xdna dev addr for payload. */
} __packed;

+/* Payload for an OPCODE_EXEC_BUF host-queue packet (single command). */
+struct exec_buf {
+ u32 dtrace_buf_host_addr_low;
+ u32 dpu_control_code_host_addr_low;
+ u32 dpu_control_code_host_addr_high;
+ u16 args_len;
+ u16 dtrace_buf_host_addr_high;
+ u32 args_host_addr_low;
+ u32 args_host_addr_high;
+} __packed;
+
+#define OPCODE_EXEC_BUF 1
+#define CHAIN_FLG_LAST_CMD 0
+#define CHAIN_FLG_NOT_LAST_CMD 1
+struct common_header {
+ u16 reserved; /* MBZ. */
+ u8 opcode;
+ u8 chain_flag;
+ u16 count;
+ u8 distribute;
+ u8 indirect;
+} __packed;
+
+struct host_queue_packet_header {
+ struct common_header common_header;
+ u64 completion_signal;
+} __packed;
+
+struct host_queue_packet {
+ struct host_queue_packet_header pkt_header;
+ u32 data[12]; /* total 64-byte packet */
+} __packed;
+
+struct host_indirect_packet_entry {
+ u32 host_addr_low;
+ u32 host_addr_high_uc_index;
+} __packed;
+
+#define HIPE_HOST_ADDR_HIGH_SHIFT 0
+#define HIPE_HOST_ADDR_HIGH_MASK GENMASK(24, 0)
+#define HIPE_UC_INDEX_SHIFT 25
+#define HIPE_UC_INDEX_MASK GENMASK(31, 25)
+
+static inline void hipe_set_host_addr_high(u32 *val, u32 addr_hi)
+{
+ *val &= ~HIPE_HOST_ADDR_HIGH_MASK;
+ *val |= (addr_hi << HIPE_HOST_ADDR_HIGH_SHIFT) & HIPE_HOST_ADDR_HIGH_MASK;
+}
+
+static inline void hipe_set_uc_index(u32 *val, u32 uc_idx)
+{
+ *val &= ~HIPE_UC_INDEX_MASK;
+ *val |= (uc_idx << HIPE_UC_INDEX_SHIFT) & HIPE_UC_INDEX_MASK;
+}
+
+struct host_indirect_packet_data {
+ struct common_header header;
+ struct exec_buf payload;
+} __packed;
+
#endif /* _AIE4_HOST_QUEUE_H_ */
diff --git a/drivers/accel/amdxdna/aie4_pci.h b/drivers/accel/amdxdna/aie4_pci.h
index c6e7f6a80f69..f549d9e69d41 100644
--- a/drivers/accel/amdxdna/aie4_pci.h
+++ b/drivers/accel/amdxdna/aie4_pci.h
@@ -8,7 +8,10 @@

#include <linux/device.h>
#include <linux/iopoll.h>
+#include <linux/list.h>
#include <linux/pci.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>

#include "aie.h"
#include "aie4_msg_priv.h"
@@ -25,15 +28,57 @@ struct cert_comp {
wait_queue_head_t waitq;
};

+/*
+ * aie4 kernel-submission job states (stored in amdxdna_sched_job priv.aie4.state).
+ * Anonymous enum - the aie4_job_state identifier is already a field-access macro.
+ */
+enum {
+ AIE4_JOB_STATE_INIT,
+ AIE4_JOB_STATE_PENDING,
+ AIE4_JOB_STATE_SUBMITTING,
+ AIE4_JOB_STATE_SUBMITTED,
+ AIE4_JOB_STATE_DONE,
+};
+
struct amdxdna_hwctx_priv {
+ struct amdxdna_hwctx *hwctx;
struct amdxdna_gem_obj *umq_bo;
u64 *umq_read_index;
u64 *umq_write_index;
+ /* Last valid read_index, returned when a sampled index looks invalid. */
+ u64 last_read_index;

struct cert_comp *cert_comp;
u32 hw_ctx_id;

+ /* Kernel-mode submission: driver fills the user HSA queue and rings
+ * the doorbell. umq_pkts/umq_indirect_pkts alias the user umq_bo;
+ * their content is driver-owned, only read_index is trusted from the
+ * shared queue.
+ */
+ u64 write_index;
+ struct host_queue_packet *umq_pkts;
+ struct host_indirect_packet_data *umq_indirect_pkts;
+ u64 umq_indirect_pkts_dev_addr;
+ /*
+ * Transport-private doorbell kick target. On PCI this is doorbell_base +
+ * doorbell_off + firmware offset, set by aie4_doorbell_setup() and
+ * dereferenced only by aie4_doorbell_ring() in aie4_pci.c. Never touched
+ * by aie4_ctx.c (unused on the platform build). Gated by the cert_comp
+ * connected sentinel, so it needs no INVALID poison.
+ */
void __iomem *doorbell_addr;
+
+ struct mutex io_lock; /* serialize submit, protect job lists */
+ struct list_head pending_job_list;
+ /* Head of pending_job_list, updated under io_lock; read locklessly by the
+ * submit wait condition so it never takes a lock inside wait_event().
+ */
+ struct amdxdna_sched_job *pending_head;
+ struct list_head running_job_list;
+ wait_queue_head_t job_list_wq;
+ struct work_struct job_work;
+ struct workqueue_struct *job_work_q;
};

struct amdxdna_dev_priv {
@@ -110,9 +155,18 @@ int aie4_set_ctx_hysteresis(struct amdxdna_dev_hdl *ndev, u32 timeout_us);
u32 aie4_msg_pasid(struct amdxdna_client *client);

/* aie4_ctx.c */
+enum aie4_hwctx_flags {
+ AIE4_HWCTX_NORMAL = 0,
+ AIE4_HWCTX_GRACEFUL,
+ AIE4_HWCTX_DISCONNECT, /* sets has_reset, do not destroy context */
+ AIE4_HWCTX_ERROR, /* sets has_reset, destroy context */
+};
+
int aie4_hwctx_init(struct amdxdna_hwctx *hwctx);
void aie4_hwctx_fini(struct amdxdna_hwctx *hwctx);
int aie4_cmd_wait(struct amdxdna_hwctx *hwctx, u64 seq, u32 timeout);
+int aie4_hwctx_create(struct amdxdna_hwctx *hwctx);
+void aie4_hwctx_destroy(struct amdxdna_hwctx *hwctx, enum aie4_hwctx_flags);

/* aie4_pci.c */
int aie4_restore_power_mode(struct amdxdna_dev_hdl *ndev);
--
2.34.1