Skip to content

Commit

Permalink
accel/ivpu: Move parts of MMU event IRQ handling to thread handler
Browse files Browse the repository at this point in the history
To prevent looping infinitely in MMU event handler we stop
generating new events by removing 'R' (record) bit from context
descriptor, but to ensure this change has effect KMD has to perform
configuration invalidation followed by sync command.

Because of that move parts of the interrupt handler that can take longer
to a thread not to block in interrupt handler for too long.
This includes:
 * disabling event queue for the time KMD updates MMU event queue consumer
   to ensure proper synchronization between MMU and KMD

 * removal of 'R' (record) bit from context descriptor to ensure no more
   faults are recorded until that context is destroyed

Signed-off-by: Karol Wachowski <karol.wachowski@intel.com>
Signed-off-by: Maciej Falkowski <maciej.falkowski@linux.intel.com>
Reviewed-by: Jacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
Signed-off-by: Jacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20250107173238.381120-8-maciej.falkowski@linux.intel.com
  • Loading branch information
Karol Wachowski authored and Jacek Lawrynowicz committed Jan 9, 2025
1 parent 0240fa1 commit 4480912
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 33 deletions.
7 changes: 6 additions & 1 deletion drivers/accel/ivpu/ivpu_job.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "ivpu_ipc.h"
#include "ivpu_job.h"
#include "ivpu_jsm_msg.h"
#include "ivpu_mmu.h"
#include "ivpu_pm.h"
#include "ivpu_trace.h"
#include "vpu_boot_api.h"
Expand Down Expand Up @@ -366,13 +367,17 @@ void ivpu_context_abort_locked(struct ivpu_file_priv *file_priv)
unsigned long cmdq_id;

lockdep_assert_held(&file_priv->lock);
ivpu_dbg(vdev, JOB, "Context ID: %u abort\n", file_priv->ctx.id);

xa_for_each(&file_priv->cmdq_xa, cmdq_id, cmdq)
ivpu_cmdq_unregister(file_priv, cmdq);

if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_OS)
ivpu_jsm_context_release(vdev, file_priv->ctx.id);

ivpu_mmu_disable_ssid_events(vdev, file_priv->ctx.id);
ivpu_mmu_discard_events(vdev);

file_priv->aborted = true;
}

Expand Down Expand Up @@ -939,8 +944,8 @@ void ivpu_context_abort_work_fn(struct work_struct *work)
{
struct ivpu_device *vdev = container_of(work, struct ivpu_device, context_abort_work);
struct ivpu_file_priv *file_priv;
unsigned long ctx_id;
struct ivpu_job *job;
unsigned long ctx_id;
unsigned long id;

mutex_lock(&vdev->context_list_lock);
Expand Down
93 changes: 61 additions & 32 deletions drivers/accel/ivpu/ivpu_mmu.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@
#define IVPU_MMU_REG_CR0 0x00200020u
#define IVPU_MMU_REG_CR0ACK 0x00200024u
#define IVPU_MMU_REG_CR0ACK_VAL_MASK GENMASK(31, 0)
#define IVPU_MMU_REG_CR0_ATSCHK_MASK BIT(4)
#define IVPU_MMU_REG_CR0_CMDQEN_MASK BIT(3)
#define IVPU_MMU_REG_CR0_EVTQEN_MASK BIT(2)
#define IVPU_MMU_REG_CR0_PRIQEN_MASK BIT(1)
#define IVPU_MMU_REG_CR0_SMMUEN_MASK BIT(0)

#define IVPU_MMU_REG_CR1 0x00200028u
#define IVPU_MMU_REG_CR2 0x0020002cu
#define IVPU_MMU_REG_IRQ_CTRL 0x00200050u
Expand Down Expand Up @@ -141,12 +147,6 @@
#define IVPU_MMU_IRQ_EVTQ_EN BIT(2)
#define IVPU_MMU_IRQ_GERROR_EN BIT(0)

#define IVPU_MMU_CR0_ATSCHK BIT(4)
#define IVPU_MMU_CR0_CMDQEN BIT(3)
#define IVPU_MMU_CR0_EVTQEN BIT(2)
#define IVPU_MMU_CR0_PRIQEN BIT(1)
#define IVPU_MMU_CR0_SMMUEN BIT(0)

#define IVPU_MMU_CR1_TABLE_SH GENMASK(11, 10)
#define IVPU_MMU_CR1_TABLE_OC GENMASK(9, 8)
#define IVPU_MMU_CR1_TABLE_IC GENMASK(7, 6)
Expand Down Expand Up @@ -596,7 +596,7 @@ static int ivpu_mmu_reset(struct ivpu_device *vdev)
REGV_WR32(IVPU_MMU_REG_CMDQ_PROD, 0);
REGV_WR32(IVPU_MMU_REG_CMDQ_CONS, 0);

val = IVPU_MMU_CR0_CMDQEN;
val = REG_SET_FLD(IVPU_MMU_REG_CR0, CMDQEN, 0);
ret = ivpu_mmu_reg_write_cr0(vdev, val);
if (ret)
return ret;
Expand All @@ -617,12 +617,12 @@ static int ivpu_mmu_reset(struct ivpu_device *vdev)
REGV_WR32(IVPU_MMU_REG_EVTQ_PROD_SEC, 0);
REGV_WR32(IVPU_MMU_REG_EVTQ_CONS_SEC, 0);

val |= IVPU_MMU_CR0_EVTQEN;
val = REG_SET_FLD(IVPU_MMU_REG_CR0, EVTQEN, val);
ret = ivpu_mmu_reg_write_cr0(vdev, val);
if (ret)
return ret;

val |= IVPU_MMU_CR0_ATSCHK;
val = REG_SET_FLD(IVPU_MMU_REG_CR0, ATSCHK, val);
ret = ivpu_mmu_reg_write_cr0(vdev, val);
if (ret)
return ret;
Expand All @@ -631,7 +631,7 @@ static int ivpu_mmu_reset(struct ivpu_device *vdev)
if (ret)
return ret;

val |= IVPU_MMU_CR0_SMMUEN;
val = REG_SET_FLD(IVPU_MMU_REG_CR0, SMMUEN, val);
return ivpu_mmu_reg_write_cr0(vdev, val);
}

Expand Down Expand Up @@ -870,7 +870,47 @@ static u32 *ivpu_mmu_get_event(struct ivpu_device *vdev)
return evt;
}

static int ivpu_mmu_disable_events(struct ivpu_device *vdev, u32 ssid)
static int ivpu_mmu_evtq_set(struct ivpu_device *vdev, bool enable)
{
u32 val = REGV_RD32(IVPU_MMU_REG_CR0);

if (enable)
val = REG_SET_FLD(IVPU_MMU_REG_CR0, EVTQEN, val);
else
val = REG_CLR_FLD(IVPU_MMU_REG_CR0, EVTQEN, val);
REGV_WR32(IVPU_MMU_REG_CR0, val);

return REGV_POLL_FLD(IVPU_MMU_REG_CR0ACK, VAL, val, IVPU_MMU_REG_TIMEOUT_US);
}

static int ivpu_mmu_evtq_enable(struct ivpu_device *vdev)
{
return ivpu_mmu_evtq_set(vdev, true);
}

static int ivpu_mmu_evtq_disable(struct ivpu_device *vdev)
{
return ivpu_mmu_evtq_set(vdev, false);
}

void ivpu_mmu_discard_events(struct ivpu_device *vdev)
{
/*
* Disable event queue (stop MMU from updating the producer)
* to allow synchronization of consumer and producer indexes
*/
ivpu_mmu_evtq_disable(vdev);

vdev->mmu->evtq.cons = REGV_RD32(IVPU_MMU_REG_EVTQ_PROD_SEC);
REGV_WR32(IVPU_MMU_REG_EVTQ_CONS_SEC, vdev->mmu->evtq.cons);
vdev->mmu->evtq.prod = REGV_RD32(IVPU_MMU_REG_EVTQ_PROD_SEC);

ivpu_mmu_evtq_enable(vdev);

drm_WARN_ON_ONCE(&vdev->drm, vdev->mmu->evtq.cons != vdev->mmu->evtq.prod);
}

int ivpu_mmu_disable_ssid_events(struct ivpu_device *vdev, u32 ssid)
{
struct ivpu_mmu_info *mmu = vdev->mmu;
struct ivpu_mmu_cdtab *cdtab = &mmu->cdtab;
Expand All @@ -890,45 +930,34 @@ static int ivpu_mmu_disable_events(struct ivpu_device *vdev, u32 ssid)
clflush_cache_range(entry, IVPU_MMU_CDTAB_ENT_SIZE);

ivpu_mmu_cmdq_write_cfgi_all(vdev);
ivpu_mmu_cmdq_sync(vdev);

return 0;
}

void ivpu_mmu_irq_evtq_handler(struct ivpu_device *vdev)
{
struct ivpu_file_priv *file_priv;
u32 last_ssid = -1;
u32 *event;
u32 ssid;

ivpu_dbg(vdev, IRQ, "MMU event queue\n");

while ((event = ivpu_mmu_get_event(vdev))) {
ssid = FIELD_GET(IVPU_MMU_EVT_SSID_MASK, event[0]);

if (ssid == last_ssid)
continue;
ssid = FIELD_GET(IVPU_MMU_EVT_SSID_MASK, *event);
if (ssid == IVPU_GLOBAL_CONTEXT_MMU_SSID) {
ivpu_mmu_dump_event(vdev, event);
ivpu_pm_trigger_recovery(vdev, "MMU event");
return;
}

xa_lock(&vdev->context_xa);
file_priv = xa_load(&vdev->context_xa, ssid);
if (file_priv) {
if (file_priv->has_mmu_faults) {
event = NULL;
} else {
ivpu_mmu_disable_events(vdev, ssid);
file_priv->has_mmu_faults = true;
if (!READ_ONCE(file_priv->has_mmu_faults)) {
ivpu_mmu_dump_event(vdev, event);
WRITE_ONCE(file_priv->has_mmu_faults, true);
}
}
xa_unlock(&vdev->context_xa);

if (event)
ivpu_mmu_dump_event(vdev, event);

if (ssid == IVPU_GLOBAL_CONTEXT_MMU_SSID) {
ivpu_pm_trigger_recovery(vdev, "MMU event");
return;
}
REGV_WR32(IVPU_MMU_REG_EVTQ_CONS_SEC, vdev->mmu->evtq.cons);
}

queue_work(system_wq, &vdev->context_abort_work);
Expand Down
2 changes: 2 additions & 0 deletions drivers/accel/ivpu/ivpu_mmu.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,7 @@ int ivpu_mmu_invalidate_tlb(struct ivpu_device *vdev, u16 ssid);
void ivpu_mmu_irq_evtq_handler(struct ivpu_device *vdev);
void ivpu_mmu_irq_gerr_handler(struct ivpu_device *vdev);
void ivpu_mmu_evtq_dump(struct ivpu_device *vdev);
void ivpu_mmu_discard_events(struct ivpu_device *vdev);
int ivpu_mmu_disable_ssid_events(struct ivpu_device *vdev, u32 ssid);

#endif /* __IVPU_MMU_H__ */

0 comments on commit 4480912

Please sign in to comment.