static void gt_reset_worker(struct work_struct *w);
static int emit_job_sync(struct xe_exec_queue *q, struct xe_bb *bb,
- long timeout_jiffies)
+ long timeout_jiffies, bool force_reset)
{
struct xe_sched_job *job;
struct dma_fence *fence;
if (IS_ERR(job))
return PTR_ERR(job);
+ job->ring_ops_force_reset = force_reset;
+
xe_sched_job_arm(job);
fence = dma_fence_get(&job->drm.s_fence->finished);
xe_sched_job_push(job);
if (IS_ERR(bb))
return PTR_ERR(bb);
- ret = emit_job_sync(q, bb, HZ);
+ ret = emit_job_sync(q, bb, HZ, false);
xe_bb_free(bb, NULL);
return ret;
bb->len = cs - bb->cs;
- ret = emit_job_sync(q, bb, HZ);
+ /* only VFs need to trigger reset to get a clean NULL context */
+ ret = emit_job_sync(q, bb, HZ, IS_SRIOV_VF(gt_to_xe(gt)));
xe_bb_free(bb, NULL);
return i;
}
+static int emit_fake_watchdog(struct xe_lrc *lrc, u32 *dw, int i)
+{
+ /*
+ * Setup a watchdog with impossible condition to always trigger an
+ * hardware interrupt that would force the GuC to reset the engine.
+ */
+
+ dw[i++] = MI_LOAD_REGISTER_IMM | MI_LRI_NUM_REGS(2) | MI_LRI_LRM_CS_MMIO;
+ dw[i++] = PR_CTR_THRSH(0).addr;
+ dw[i++] = 2; /* small threshold */
+ dw[i++] = PR_CTR_CTRL(0).addr;
+ dw[i++] = CTR_LOGIC_OP(START);
+
+ dw[i++] = MI_SEMAPHORE_WAIT | MI_SEMW_GGTT | MI_SEMW_POLL | MI_SEMW_COMPARE(SAD_EQ_SDD);
+ dw[i++] = 0xdead; /* this should never be seen */
+ dw[i++] = lower_32_bits(xe_lrc_ggtt_addr(lrc));
+ dw[i++] = upper_32_bits(xe_lrc_ggtt_addr(lrc));
+ dw[i++] = 0; /* unused token */
+
+ dw[i++] = MI_LOAD_REGISTER_IMM | MI_LRI_NUM_REGS(1) | MI_LRI_LRM_CS_MMIO;
+ dw[i++] = PR_CTR_CTRL(0).addr;
+ dw[i++] = CTR_LOGIC_OP(STOP);
+
+ return i;
+}
+
/* for engines that don't require any special HW handling (no EUs, no aux inval, etc) */
static void __emit_job_gen12_simple(struct xe_sched_job *job, struct xe_lrc *lrc,
u64 batch_addr, u32 *head, u32 seqno)
*head = lrc->ring.tail;
+ if (job->ring_ops_force_reset)
+ i = emit_fake_watchdog(lrc, dw, i);
+
i = emit_copy_timestamp(gt_to_xe(gt), lrc, dw, i);
if (job->ring_ops_flush_tlb) {
*head = lrc->ring.tail;
+ if (job->ring_ops_force_reset)
+ i = emit_fake_watchdog(lrc, dw, i);
+
i = emit_copy_timestamp(xe, lrc, dw, i);
dw[i++] = preparser_disable(true);
*head = lrc->ring.tail;
+ if (job->ring_ops_force_reset)
+ i = emit_fake_watchdog(lrc, dw, i);
+
i = emit_copy_timestamp(xe, lrc, dw, i);
dw[i++] = preparser_disable(true);
*head = lrc->ring.tail;
+ xe_gt_assert(gt, !job->ring_ops_force_reset);
+
i = emit_copy_timestamp(xe, lrc, dw, i);
i = emit_store_imm_ggtt(saddr, seqno, dw, i);