/** * DOC: Memory Based Interrupts * * MMIO register based interrupts infrastructure used for non-virtualized mode * or SRIOV-8 (which supports 8 Virtual Functions) does not scale efficiently * to allow delivering interrupts to a large number of Virtual machines or * containers. Memory based interrupt status reporting provides an efficient * and scalable infrastructure. * * For memory based interrupt status reporting hardware sequence is: * * Engine writes the interrupt event to memory * (Pointer to memory location is provided by SW. This memory surface must * be mapped to system memory and must be marked as un-cacheable (UC) on * Graphics IP Caches) * * Engine triggers an interrupt to host.
*/
/** * DOC: Memory Based Interrupts Page Layout * * `Memory Based Interrupts`_ requires three different objects, which are * called "page" in the specs, even if they aren't page-sized or aligned. * * To simplify the code we allocate a single page size object and then use * offsets to embedded "pages". The address of those "pages" are then * programmed in the HW via LRI and LRM in the context image. * * - _`Interrupt Status Report Page`: this page contains the interrupt * status vectors for each unit. Each bit in the interrupt vectors is * converted to a byte, with the byte being set to 0xFF when an * interrupt is triggered; interrupt vectors are 16b big so each unit * gets 16B. One space is reserved for each bit in one of the * GT_INTR_DWx registers, so this object needs a total of 1024B. * This object needs to be 4KiB aligned. * * - _`Interrupt Source Report Page`: this is the equivalent of the * GT_INTR_DWx registers, with each bit in those registers being * mapped to a byte here. The offsets are the same, just bytes instead * of bits. This object needs to be cacheline aligned. * * - Interrupt Mask: the HW needs a location to fetch the interrupt * mask vector to be used by the LRM in the context, so we just use * the next available space in the interrupt page. * * :: * * 0x0000 +===========+ <== Interrupt Status Report Page * | | * | | ____ +----+----------------+ * | | / | 0 | USER INTERRUPT | * +-----------+ __/ | 1 | | * | HWE(n) | __ | | CTX SWITCH | * +-----------+ \ | | WAIT SEMAPHORE | * | | \____ | 15 | | * | | +----+----------------+ * | | * 0x0400 +===========+ <== Interrupt Source Report Page * | HWE(0) | * | HWE(1) | * | | * | HWE(x) | * 0x0440 +===========+ <== Interrupt Enable Mask * | | * | | * +-----------+ * * * MSI-X use case * * When using MSI-X, hw engines report interrupt status and source to engine * instance 0. For this scenario, in order to differentiate between the * engines, we need to pass different status/source pointers in the LRC. * * The requirements on those pointers are: * - Interrupt status should be 4KiB aligned * - Interrupt source should be 64 bytes aligned * * To accommodate this, we duplicate the memirq page layout above - * allocating a page for each engine instance and pass this page in the LRC. * Note that the same page can be reused for different engine types. * For example, an LRC executing on CCS #x will have pointers to page #x, * and an LRC executing on BCS #x will have the same pointers. * * :: * * 0x0000 +==============================+ <== page for instance 0 (BCS0, CCS0, etc.) * | Interrupt Status Report Page | * 0x0400 +==============================+ * | Interrupt Source Report Page | * 0x0440 +==============================+ * | Interrupt Enable Mask | * +==============================+ * | Not used | * 0x1000 +==============================+ <== page for instance 1 (BCS1, CCS1, etc.) * | Interrupt Status Report Page | * 0x1400 +==============================+ * | Interrupt Source Report Page | * 0x1440 +==============================+ * | Not used | * 0x2000 +==============================+ <== page for instance 2 (BCS2, CCS2, etc.) * | ... | * +==============================+ *
*/
staticinlinebool hw_reports_to_instance_zero(struct xe_memirq *memirq)
{ /* * When the HW engines are configured to use MSI-X, * they report interrupt status and source to the offset of * engine instance 0.
*/ return xe_device_has_msix(memirq_to_xe(memirq));
}
/** * xe_memirq_init - Initialize data used by `Memory Based Interrupts`_. * @memirq: the &xe_memirq to initialize * * Allocate `Interrupt Source Report Page`_ and `Interrupt Status Report Page`_ * used by `Memory Based Interrupts`_. * * These allocations are managed and will be implicitly released on unload. * * If this function fails then the driver won't be able to operate correctly. * If `Memory Based Interrupts`_ are not used this function will return 0. * * Return: 0 on success or a negative error code on failure.
*/ int xe_memirq_init(struct xe_memirq *memirq)
{ struct xe_device *xe = memirq_to_xe(memirq); int err;
if (!xe_device_uses_memirq(xe)) return 0;
err = memirq_alloc_pages(memirq); if (unlikely(err)) return err;
/* we need to start with all irqs enabled */
memirq_set_enable(memirq, true);
/** * xe_memirq_source_ptr - Get GGTT's offset of the `Interrupt Source Report Page`_. * @memirq: the &xe_memirq to query * @hwe: the hw engine for which we want the report page * * Shall be called when `Memory Based Interrupts`_ are used * and xe_memirq_init() didn't fail. * * Return: GGTT's offset of the `Interrupt Source Report Page`_.
*/
u32 xe_memirq_source_ptr(struct xe_memirq *memirq, struct xe_hw_engine *hwe)
{
memirq_assert(memirq, xe_device_uses_memirq(memirq_to_xe(memirq)));
/** * xe_memirq_status_ptr - Get GGTT's offset of the `Interrupt Status Report Page`_. * @memirq: the &xe_memirq to query * @hwe: the hw engine for which we want the report page * * Shall be called when `Memory Based Interrupts`_ are used * and xe_memirq_init() didn't fail. * * Return: GGTT's offset of the `Interrupt Status Report Page`_.
*/
u32 xe_memirq_status_ptr(struct xe_memirq *memirq, struct xe_hw_engine *hwe)
{
memirq_assert(memirq, xe_device_uses_memirq(memirq_to_xe(memirq)));
/** * xe_memirq_enable_ptr - Get GGTT's offset of the Interrupt Enable Mask. * @memirq: the &xe_memirq to query * * Shall be called when `Memory Based Interrupts`_ are used * and xe_memirq_init() didn't fail. * * Return: GGTT's offset of the Interrupt Enable Mask.
*/
u32 xe_memirq_enable_ptr(struct xe_memirq *memirq)
{
memirq_assert(memirq, xe_device_uses_memirq(memirq_to_xe(memirq)));
memirq_assert(memirq, memirq->bo);
/** * xe_memirq_init_guc - Prepare GuC for `Memory Based Interrupts`_. * @memirq: the &xe_memirq * @guc: the &xe_guc to setup * * Register `Interrupt Source Report Page`_ and `Interrupt Status Report Page`_ * to be used by the GuC when `Memory Based Interrupts`_ are required. * * Shall be called when `Memory Based Interrupts`_ are used * and xe_memirq_init() didn't fail. * * Return: 0 on success or a negative error code on failure.
*/ int xe_memirq_init_guc(struct xe_memirq *memirq, struct xe_guc *guc)
{ bool is_media = xe_gt_is_media_type(guc_to_gt(guc));
u32 offset = is_media ? ilog2(INTR_MGUC) : ilog2(INTR_GUC);
u32 source, status; int err;
err = xe_guc_self_cfg64(guc, GUC_KLV_SELF_CFG_MEMIRQ_SOURCE_ADDR_KEY,
source); if (unlikely(err)) goto failed;
err = xe_guc_self_cfg64(guc, GUC_KLV_SELF_CFG_MEMIRQ_STATUS_ADDR_KEY,
status); if (unlikely(err)) goto failed;
return 0;
failed:
memirq_err(memirq, "Failed to setup report pages in %s (%pe)\n",
guc_name(guc), ERR_PTR(err)); return err;
}
/** * xe_memirq_reset - Disable processing of `Memory Based Interrupts`_. * @memirq: struct xe_memirq * * This is part of the driver IRQ setup flow. * * This function shall only be used on platforms that use * `Memory Based Interrupts`_.
*/ void xe_memirq_reset(struct xe_memirq *memirq)
{
memirq_assert(memirq, xe_device_uses_memirq(memirq_to_xe(memirq)));
if (memirq->bo)
memirq_set_enable(memirq, false);
}
/** * xe_memirq_postinstall - Enable processing of `Memory Based Interrupts`_. * @memirq: the &xe_memirq * * This is part of the driver IRQ setup flow. * * This function shall only be used on platforms that use * `Memory Based Interrupts`_.
*/ void xe_memirq_postinstall(struct xe_memirq *memirq)
{
memirq_assert(memirq, xe_device_uses_memirq(memirq_to_xe(memirq)));
if (memirq->bo)
memirq_set_enable(memirq, true);
}
value = iosys_map_rd(vector, offset, u8); if (value) { if (value != 0xff)
memirq_err_ratelimited(memirq, "Unexpected memirq value %#x from %s at %u\n",
value, name, offset);
iosys_map_wr(vector, offset, u8, 0x00);
}
if (memirq_received(memirq, status, ilog2(GUC_INTR_GUC2HOST), name))
xe_guc_irq_handler(guc, GUC_INTR_GUC2HOST);
if (memirq_received(memirq, status, ilog2(GUC_INTR_SW_INT_0), name))
xe_guc_irq_handler(guc, GUC_INTR_SW_INT_0);
}
/** * xe_memirq_hwe_handler - Check and process interrupts for a specific HW engine. * @memirq: the &xe_memirq * @hwe: the hw engine to process * * This function reads and dispatches `Memory Based Interrupts` for the provided HW engine.
*/ void xe_memirq_hwe_handler(struct xe_memirq *memirq, struct xe_hw_engine *hwe)
{
u16 offset = hwe->irq_offset;
u16 instance = hw_reports_to_instance_zero(memirq) ? hwe->instance : 0; struct iosys_map src_offset = IOSYS_MAP_INIT_OFFSET(&memirq->bo->vmap,
XE_MEMIRQ_SOURCE_OFFSET(instance));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.