diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-11-09 19:27:48 -0500 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-11-09 19:27:48 -0500 |
| commit | a478f0b418bd3dd5fbcc6260979865160ca25cf0 (patch) | |
| tree | e29fb7ec56741b9be1d8e747c108cb661b89d6c2 | |
| parent | 4a2cf8292ad37767de22a26e7e35d0a8a06edad3 (diff) | |
| download | open-iov-a478f0b418bd3dd5fbcc6260979865160ca25cf0.tar.gz open-iov-a478f0b418bd3dd5fbcc6260979865160ca25cf0.zip | |
vgsp: raise the GSP engine event interrupt so guest ISR drains events and wakes poll() waiters
The emulated GSP only signalled the MSI-X eventfd when it queued a status
record. The guest's primary ISR (nvidia_isr) decides pending from the
CPU_INTR leaf registers, which we never set for the GSP stall vector, so it
returned IRQ_NONE: IRQ 33 counted thousands of interrupts while the nvidia
event thread sat idle and a client blocked in poll() on /dev/nvidia0 (a GLX
swap) waited forever even though the event was already in the status queue.
Mirror what a real GSP-RM does when it queues an event:
- program RISC-V IRQMASK/IRQDEST for SWGEN0 (read-only to the guest; the
GSP firmware owns them),
- set SWGEN0 in the falcon IRQSTAT,
- latch GSP stall vector 155 in the CPU interrupt tree (LEAF[4] bit 27,
UVM_SHARED subtree 2 -> TOP[0] bit 2),
- signal MSI-X.
Raise it only for POST_EVENT records (replies are polled by the client).
Verified on r1680: the guest now writes IRQSCLR SWGEN0, kgspService_TU102
drains events, and glxgears renders (was a hard hang).
| -rw-r--r-- | kernel/gsp_emulation.c | 57 | ||||
| -rw-r--r-- | kernel/gsp_emulation.h | 26 | ||||
| -rw-r--r-- | kernel/gsp_rm_emulation.c | 14 |
3 files changed, 92 insertions, 5 deletions
diff --git a/kernel/gsp_emulation.c b/kernel/gsp_emulation.c index 47c8151..38f6284 100644 --- a/kernel/gsp_emulation.c +++ b/kernel/gsp_emulation.c @@ -644,6 +644,63 @@ void gsp_emu_inject_completion_interrupt(struct gsp_emu_context *ctx, u32 seq_id eventfd_signal(trigger); } +/* + * Raise the GSP engine event interrupt. + * + * Mirrors what a real GSP-RM does when it queues an event for the host: + * - program the RISC-V interrupt mask/destination so SWGEN0 is routed to + * the CPU (read-only to the guest; the GSP firmware owns them), + * - set SWGEN0 in the falcon IRQSTAT, + * - latch the GSP stall vector (155) in the host CPU interrupt tree: + * LEAF[4] bit 27 and TOP[0] bit 2 (UVM_SHARED subtree 2), + * - signal MSI-X. + * + * Without the leaf bit the guest's primary ISR returns IRQ_NONE and the + * threaded handler (kgspService_TU102 -> kgspRpcRecvEvents -> nv_post_event) + * never runs, so a client blocked in poll() on /dev/nvidia0 (a GLX swap) + * waits forever even though the event is sitting in the status queue. + */ +void gsp_emu_raise_gsp_event_irq(struct gsp_emu_context *ctx) +{ + u32 reg, val; + u32 leaf_idx = GSP_EMU_GSP_EVENT_VECTOR / 32; + u32 leaf_bit = GSP_EMU_GSP_EVENT_VECTOR % 32; + u32 subtree = GSP_EMU_GSP_EVENT_VECTOR / 64; + u32 top_idx = subtree / 32; + u32 top_bit = subtree % 32; + + if (!ctx || !ctx->bar0_vaddr) + return; + + /* IRQMASK / IRQDEST: route SWGEN0 to the CPU (idempotent). */ + reg = GSP_EMU_REG_RISCV_IRQMASK; + val = gsp_emu_shadow_read32(ctx, reg); + if (!(val & GSP_EMU_FALCON_IRQSTAT_SWGEN0)) + gsp_emu_shadow_write32(ctx, reg, + val | GSP_EMU_FALCON_IRQSTAT_SWGEN0); + reg = GSP_EMU_REG_RISCV_IRQDEST; + val = gsp_emu_shadow_read32(ctx, reg); + if (!(val & GSP_EMU_FALCON_IRQSTAT_SWGEN0)) + gsp_emu_shadow_write32(ctx, reg, + val | GSP_EMU_FALCON_IRQSTAT_SWGEN0); + + /* Pending source: SWGEN0. */ + reg = GSP_EMU_REG_FALCON_IRQSTAT; + val = gsp_emu_shadow_read32(ctx, reg); + gsp_emu_shadow_write32(ctx, reg, + val | GSP_EMU_FALCON_IRQSTAT_SWGEN0); + + /* CPU interrupt tree: LEAF[leaf_idx] |= bit, TOP[top_idx] |= bit. */ + reg = GSP_EMU_REG_CPU_INTR_LEAF_BASE + leaf_idx * 4; + val = gsp_emu_shadow_read32(ctx, reg); + gsp_emu_shadow_write32(ctx, reg, val | (1U << leaf_bit)); + reg = GSP_EMU_REG_CPU_INTR_TOP_BASE + top_idx * 4; + val = gsp_emu_shadow_read32(ctx, reg); + gsp_emu_shadow_write32(ctx, reg, val | (1U << top_bit)); + + gsp_emu_inject_completion_interrupt(ctx, 0); +} + /* ============================================================ * Emulation Thread * ============================================================ */ diff --git a/kernel/gsp_emulation.h b/kernel/gsp_emulation.h index 9a8f241..d9a7f01 100644 --- a/kernel/gsp_emulation.h +++ b/kernel/gsp_emulation.h @@ -116,6 +116,29 @@ #define GSP_EMU_REG_CPU_INTR_LEAF_BASE (GSP_EMU_REG_FULL_PHYS_OFFSET + 0x1000) #define GSP_EMU_REG_CPU_INTR_TOP_BASE (GSP_EMU_REG_FULL_PHYS_OFFSET + 0x1600) +/* GSP event interrupt (Turing, physical-GPU path). + * + * A real GSP-RM posts an event into the status queue and raises the GSP + * engine interrupt: it sets SWGEN0 in the falcon IRQSTAT and the host CPU's + * interrupt tree latches the GSP stall vector (155 = LEAF[4] bit 27, UVM_SHARED + * subtree 2 -> TOP[0] bit 2). The guest's primary ISR (nvidia_isr) then + * returns IRQ_WAKE_THREAD, kgspService_TU102 reads + * IRQSTAT & RISCV_IRQMASK & RISCV_IRQDEST, sees SWGEN0, calls + * kgspRpcRecvEvents -> nv_post_event -> wake_up_interruptible, which is what + * releases a client blocked in poll() on /dev/nvidia0 (e.g. a GLX swap). + * + * We were only signalling the MSI-X eventfd, so the primary ISR returned + * IRQ_NONE (no pending leaf bit) and the event thread never ran: IRQ 33 + * counted thousands of interrupts while the guest's nvidia worker threads sat + * idle and the client polled forever. IRQMASK/IRQDEST are read-only to the + * guest (the GSP firmware owns them), so the emulated GSP must program them. */ +#define GSP_EMU_REG_FALCON_IRQSCLR 0x110004 /* -W- : write 1 to clear */ +#define GSP_EMU_REG_FALCON_IRQSTAT 0x110008 /* R-- : pending sources */ +#define GSP_EMU_REG_RISCV_IRQMASK 0x1112b4 /* R-I : enabled sources */ +#define GSP_EMU_REG_RISCV_IRQDEST 0x1112b8 /* RW : routed to CPU */ +#define GSP_EMU_FALCON_IRQSTAT_SWGEN0 (1U << 6) +#define GSP_EMU_GSP_EVENT_VECTOR 155 /* MC_ENGINE_IDX_GSP stall */ + /* Windowed aliases of the VF L2 invalidate registers (GPU_VREG_* path). */ #define GSP_EMU_REG_VF_L2_SYSMEM_INV_WIN (GSP_EMU_REG_FULL_PHYS_OFFSET + 0x000f00) #define GSP_EMU_REG_VF_L2_PEERMEM_INV_WIN (GSP_EMU_REG_FULL_PHYS_OFFSET + 0x000f04) @@ -195,6 +218,9 @@ void gsp_emu_cleanup(struct nv_vgpu_device *vgpu); void gsp_emu_clear_boot_state(struct gsp_emu_context *ctx, const char *reason); int gsp_emu_process_rpc(struct gsp_emu_context *ctx); void gsp_emu_inject_completion_interrupt(struct gsp_emu_context *ctx, u32 seq_id); +/* Raise the GSP event interrupt (falcon SWGEN0 + CPU_INTR leaf/top + MSI-X) + * so the guest's ISR drains the status queue and wakes poll() waiters. */ +void gsp_emu_raise_gsp_event_irq(struct gsp_emu_context *ctx); /* ============================================================ * Debug/Statistics diff --git a/kernel/gsp_rm_emulation.c b/kernel/gsp_rm_emulation.c index c14a767..f395ce6 100644 --- a/kernel/gsp_rm_emulation.c +++ b/kernel/gsp_rm_emulation.c @@ -737,11 +737,15 @@ static int gsp_rm_send_flags(struct gsp_emu_context *ctx, u32 function, return ret; } - /* Notify the guest (MSI-X vector 0). The driver's bottom-half drains - * events under the GPU lock; the rpcRecvPoll poller holds the same - * lock from send to poll, so a reply can't be consumed by the event - * drain while a poller waits for it (same as real GSP-RM). */ - gsp_emu_inject_completion_interrupt(ctx, rm->stat_seq); + /* Notify the guest. An event must also raise the GSP engine interrupt + * (falcon SWGEN0 + CPU_INTR leaf/top) so the guest's ISR drains the + * status queue and nv_post_event() wakes a client blocked in poll() on + * /dev/nvidia0 (e.g. a GLX swap). Replies only need the MSI-X ping + * because the client polls the queue for them. */ + if (function == GSPRM_RPC_EVENT_POST_EVENT) + gsp_emu_raise_gsp_event_irq(ctx); + else + gsp_emu_inject_completion_interrupt(ctx, rm->stat_seq); mutex_unlock(&rm->stat_lock); return 0; |
