diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-09-21 13:06:51 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-09-21 13:06:51 -0400 |
| commit | c3a295efd1b1132bbaa5d422cd05b3fa8e89a8bf (patch) | |
| tree | f74bd7724153cd9d870688df3ff75274cd5000da /driver/rpc/gsp_rpc.c | |
| parent | 5d4d26f8e773f2871c1b5c9266b82495d390970e (diff) | |
| download | open-iov-c3a295efd1b1132bbaa5d422cd05b3fa8e89a8bf.tar.gz open-iov-c3a295efd1b1132bbaa5d422cd05b3fa8e89a8bf.zip | |
kernel: Fix GSP emulation shadow binding, eventfd wiring, RPC livelocks + docs suite + repo reorg
CRITICAL FIXES (guest driver bring-up):
- gsp_emulation.c: emulation engine bound to the WRONG BAR0 - it polled the
host physical BAR0 (bars[0].vaddr ioremap) for the doorbell and read RPCs
from a local dma_alloc_wc buffer, while the guest writes land in the
vzalloc'd bar0_shadow. The RPC path could never work; PBA writes even
corrupted the host GPU's physical BAR0+0x3000. gsp_emu_init now binds
exclusively to bar0_shadow (doorbell/mailbox/PBA aliases;
shared_mem_dma==0 marks the alias so cleanup never dma_free's it).
- main.c SET_IRQS: DATA_EVENTFD eventfds were DISCARDED ('TODO' + kfree) -
the guest never received MSI-X completions. Full wiring now:
eventfd_ctx_fdget per vector, fd -1 clears, dev_close releases refs.
gsp_emu_inject_completion_interrupt looks up vgpu->irqs[0].trigger LIVE
(init-time copy was permanently NULL since QEMU wires after gsp_emu_init).
- gsp_rpc_client.c: BAR_TRAP/MIGRATE in emulation mode called
gsp_rpc_submit_raw which rings the SAME doorbell the thread polls -
self-triggered RPC livelock + multi-second stalls. Now fail fast with
-ENOTSUPP + pr_warn_ratelimited (guest BAR access is direct MMIO).
MORE FIXES:
- main.c: nv_vgpu_config_write returned SIZE_MAX (while(count--) wrap) and
was dead code while the VFIO write handler did unfiltered byte copies
(guest could rewrite vendor ID/BARs). Return fixed; VFIO write handler
now routes through the per-register filter.
- kernel/Makefile: M=$(PWD) used the env var which is stale when invoked
via the top-level make (pointed at repo root -> kbuild silently built
NOTHING; explains every 'stale .ko' incident). Now M=$(CURDIR).
- Warnings sweep: header prototypes for mdev/type functions, format casts
in gsp_comm.c DRY-RUN logs, unused variable.
ROBUSTNESS (harness/tooling):
- vgpu_vm.py: run had no timeout (start could block forever);
guest_shell recv could block forever on a wedged guest. Both now bounded;
guest_shell pre-flights guest-ping and reports wedge diagnostics.
- stack_manager.sh: qemu/qemu-debug now launch via the vgpu_vm.py harness
(workflow rule: never raw QEMU).
REPO REORGANIZATION:
- driver/ (legacy guest-side gsp-vgpu.ko) moved to legacy-driver-guest/
(reference only, not built; README explains). 3 legacy un-buildable test
files moved to legacy-driver-guest/kernel-tests/. Top-level Makefile
cleaned of the dead DRIVER_DIR reference.
DOCUMENTATION SUITE (new):
- docs/GSP_EMULATION_ARCHITECTURE.md - emulator deep-dive: shadow BAR0
model, doorbell protocol, dispatch table, MSI-X path, lifecycle, memory-
model caveats
- docs/DEBUGGING.md - QEMU GDB stub workflow, serial console, QMP NMI,
failure taxonomy, full bring-up validation checklist, EPT-livelock case
study, version-matrix discipline
- docs/ONBOARDING.md - repo tour, coding standards (incl. virt_to_phys /
dma_addr_t / get-put rules), first build & test
- docs/VERSION_UPGRADE.md - end-to-end new-driver-version procedure
(firmware fetch, Ghidra analysis, configs, signatures, validation)
- docs/PERFORMANCE.md - bare-metal A/B methodology + targets
- README.md doc index; STATE.md refreshed; DEVELOPMENT_WORKFLOW.md corrected
(load order: open-iov-vfio BEFORE nvidia; stock-driver blacklist note)
Diffstat (limited to 'driver/rpc/gsp_rpc.c')
| -rw-r--r-- | driver/rpc/gsp_rpc.c | 331 |
1 files changed, 0 insertions, 331 deletions
diff --git a/driver/rpc/gsp_rpc.c b/driver/rpc/gsp_rpc.c deleted file mode 100644 index 6bdcdb2..0000000 --- a/driver/rpc/gsp_rpc.c +++ /dev/null @@ -1,331 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * GSP vGPU-IO Driver - RPC Client Implementation - */ - -#include "gsp_core.h" -#include <linux/slab.h> -#include <linux/wait.h> -#include <linux/delay.h> -#include <linux/kthread.h> - -/* Ring Buffer Operations */ -static int gsp_ring_init(struct gsp_msg_ring *ring, void *base, size_t size) -{ - ring->base = base; - ring->size = size; - ring->head = 0; - ring->tail = 0; - spin_lock_init(&ring->lock); - return 0; -} - -static bool gsp_ring_empty(struct gsp_msg_ring *ring) -{ - return ring->head == ring->tail; -} - -static bool gsp_ring_full(struct gsp_msg_ring *ring) -{ - return ((ring->head + 1) % (ring->size / GSP_MSG_MAX_SIZE)) == ring->tail; -} - -static int gsp_ring_write(struct gsp_msg_ring *ring, struct gsp_rpc_message *msg, size_t len) -{ - unsigned long flags; - uint32_t next_head; - void *dst; - - if (len > GSP_MSG_MAX_SIZE) - return -EINVAL; - - spin_lock_irqsave(&ring->lock, flags); - - if (gsp_ring_full(ring)) { - spin_unlock_irqrestore(&ring->lock, flags); - return -EBUSY; - } - - dst = ring->base + (ring->head * GSP_MSG_MAX_SIZE); - memcpy(dst, msg, len); - - next_head = (ring->head + 1) % (ring->size / GSP_MSG_MAX_SIZE); - ring->head = next_head; - - spin_unlock_irqrestore(&ring->lock, flags); - return 0; -} - -static int gsp_ring_read(struct gsp_msg_ring *ring, struct gsp_rpc_message *msg) -{ - unsigned long flags; - void *src; - - spin_lock_irqsave(&ring->lock, flags); - - if (gsp_ring_empty(ring)) { - spin_unlock_irqrestore(&ring->lock, flags); - return 0; - } - - src = ring->base + (ring->tail * GSP_MSG_MAX_SIZE); - memcpy(msg, src, GSP_MSG_MAX_SIZE); - - ring->tail = (ring->tail + 1) % (ring->size / GSP_MSG_MAX_SIZE); - - spin_unlock_irqrestore(&ring->lock, flags); - return 1; -} - -/* RPC Client Initialization */ -struct gsp_rpc_client *gsp_rpc_client_init(struct gsp_device *gsp) -{ - struct gsp_rpc_client *rpc; - void *ring_mem; - - rpc = kzalloc(sizeof(*rpc), GFP_KERNEL); - if (!rpc) - return ERR_PTR(-ENOMEM); - - rpc->gsp = gsp; - mutex_init(&rpc->lock); - atomic_set(&rpc->seq, 0); - init_waitqueue_head(&rpc->wq); - - /* Allocate shared memory for message rings (2 pages each) */ - ring_mem = dma_alloc_coherent(gsp->dev, 8 * PAGE_SIZE, - &rpc->tx_ring_dma, GFP_KERNEL); - if (!ring_mem) { - kfree(rpc); - return ERR_PTR(-ENOMEM); - } - - rpc->tx_ring = kzalloc(sizeof(*rpc->tx_ring), GFP_KERNEL); - rpc->rx_ring = kzalloc(sizeof(*rpc->rx_ring), GFP_KERNEL); - if (!rpc->tx_ring || !rpc->rx_ring) { - dma_free_coherent(gsp->dev, 8 * PAGE_SIZE, ring_mem, rpc->tx_ring_dma); - kfree(rpc->tx_ring); - kfree(rpc->rx_ring); - kfree(rpc); - return ERR_PTR(-ENOMEM); - } - - gsp_ring_init(rpc->tx_ring, ring_mem, 4 * PAGE_SIZE); - gsp_ring_init(rpc->rx_ring, ring_mem + 4 * PAGE_SIZE, 4 * PAGE_SIZE); - - /* Map rings to GSP via queue head registers (BAR0) */ - writel(rpc->tx_ring_dma, gsp->bar0 + GSP_BAR0_QUEUE_HEAD(0)); - writel(rpc->tx_ring_dma >> 32, gsp->bar0 + GSP_BAR0_QUEUE_HEAD(0) + 4); - writel(rpc->rx_ring_dma, gsp->bar0 + GSP_BAR0_QUEUE_HEAD(1)); - writel(rpc->rx_ring_dma >> 32, gsp->bar0 + GSP_BAR0_QUEUE_HEAD(1) + 4); - - /* Allocate response buffers */ - for (int i = 0; i < GSP_MAX_PENDING; i++) { - rpc->resp_buf[i] = kzalloc(GSP_MSG_MAX_SIZE, GFP_KERNEL); - if (!rpc->resp_buf[i]) { - /* Cleanup on failure */ - for (int j = 0; j < i; j++) - kfree(rpc->resp_buf[j]); - gsp_rpc_client_fini(rpc); - return ERR_PTR(-ENOMEM); - } - } - - dev_info(gsp->dev, "RPC client initialized\n"); - return rpc; -} - -void gsp_rpc_client_fini(struct gsp_rpc_client *rpc) -{ - if (!rpc) - return; - - for (int i = 0; i < GSP_MAX_PENDING; i++) - kfree(rpc->resp_buf[i]); - - if (rpc->tx_ring) { - dma_free_coherent(rpc->gsp->dev, 8 * PAGE_SIZE, - rpc->tx_ring->base, rpc->tx_ring_dma); - kfree(rpc->tx_ring); - } - kfree(rpc->rx_ring); - kfree(rpc); -} - -/* Synchronous RPC Send */ -int gsp_rpc_send_sync(struct gsp_rpc_client *rpc, - enum gsp_rpc_type type, - void *payload, size_t payload_size, - void *response, size_t *response_size) -{ - struct gsp_rpc_message msg = {0}; - struct completion done; - uint32_t seq; - int ret; - - if (payload_size > GSP_MSG_MAX_SIZE - sizeof(struct gsp_rpc_header)) - return -EINVAL; - - seq = atomic_inc_return(&rpc->seq); - init_completion(&done); - - msg.hdr.magic = 0x47535052; /* "GSPR" */ - msg.hdr.version = 1; - msg.hdr.rpc_type = type; - msg.hdr.sequence = seq; - msg.hdr.timestamp_ns = ktime_get_ns(); - msg.hdr.payload_size = payload_size; - msg.hdr.flags = GSP_MSG_SYNC; - msg.hdr.correlation_id = seq; - - if (payload && payload_size > 0) - memcpy(msg.payload, payload, payload_size); - - mutex_lock(&rpc->lock); - - rpc->pending[seq % GSP_MAX_PENDING] = &done; - - ret = gsp_ring_write(rpc->tx_ring, &msg, GSP_MSG_MAX_SIZE); - if (ret) { - rpc->pending[seq % GSP_MAX_PENDING] = NULL; - mutex_unlock(&rpc->lock); - return ret; - } - - /* Ring doorbell - write to queue head to signal GSP */ - writel(1, rpc->gsp->bar0 + GSP_BAR0_QUEUE_HEAD(0)); - - mutex_unlock(&rpc->lock); - - /* Wait for completion (5 second timeout) */ - if (!wait_for_completion_timeout(&done, msecs_to_jiffies(5000))) { - mutex_lock(&rpc->lock); - rpc->pending[seq % GSP_MAX_PENDING] = NULL; - mutex_unlock(&rpc->lock); - atomic64_inc(&rpc->gsp->rpc_errors); - return -ETIMEDOUT; - } - - /* Get response */ - struct gsp_rpc_response *resp = rpc->resp_buf[seq % GSP_MAX_PENDING]; - - atomic64_inc(&rpc->gsp->rpc_total); - - if (resp->status != 0) { - atomic64_inc(&rpc->gsp->rpc_errors); - return gsp_errno_to_errno(resp->status); - } - - if (response && response_size) { - size_t copy_size = min(*response_size, (size_t)resp->payload_size); - memcpy(response, resp->payload, copy_size); - *response_size = copy_size; - } - - return 0; -} - -/* Async RPC Send */ -int gsp_rpc_send_async(struct gsp_rpc_client *rpc, - enum gsp_rpc_type type, - void *payload, size_t payload_size, - void (*callback)(struct gsp_rpc_client *, - struct gsp_rpc_response *, - void *), - void *ctx) -{ - struct gsp_rpc_message msg = {0}; - uint32_t seq; - int ret; - - if (payload_size > GSP_MSG_MAX_SIZE - sizeof(struct gsp_rpc_header)) - return -EINVAL; - - seq = atomic_inc_return(&rpc->seq); - - msg.hdr.magic = 0x47535052; - msg.hdr.version = 1; - msg.hdr.rpc_type = type; - msg.hdr.sequence = seq; - msg.hdr.timestamp_ns = ktime_get_ns(); - msg.hdr.payload_size = payload_size; - msg.hdr.flags = GSP_MSG_ASYNC; - msg.hdr.correlation_id = (uint64_t)callback | ((uint64_t)ctx << 32); - - if (payload && payload_size > 0) - memcpy(msg.payload, payload, payload_size); - - mutex_lock(&rpc->lock); - ret = gsp_ring_write(rpc->tx_ring, &msg, GSP_MSG_MAX_SIZE); - if (!ret) - writel(1, rpc->gsp->bar0 + GSP_BAR0_QUEUE_HEAD(0)); - mutex_unlock(&rpc->lock); - - return ret; -} - -/* ISR - Handle Completions */ -irqreturn_t gsp_isr(int irq, void *data) -{ - struct gsp_device *gsp = data; - struct gsp_rpc_message msg; - int handled = 0; - - while (gsp_ring_read(gsp->rpc->rx_ring, &msg)) { - handled = 1; - - if (msg.hdr.flags & GSP_MSG_SYNC) { - uint32_t seq = msg.hdr.sequence; - struct completion *done = gsp->rpc->pending[seq % GSP_MAX_PENDING]; - struct gsp_rpc_response *resp = gsp->rpc->resp_buf[seq % GSP_MAX_PENDING]; - - if (done && resp) { - resp->status = ((struct gsp_rpc_response *)msg.payload)->status; - resp->payload_size = msg.hdr.payload_size; - memcpy(resp->payload, ((struct gsp_rpc_response *)msg.payload)->payload, - msg.hdr.payload_size); - complete(done); - } - gsp->rpc->pending[seq % GSP_MAX_PENDING] = NULL; - } else { - /* Async notification - handle via callback */ - gsp_handle_notification(gsp, &msg); - } - } - - return handled ? IRQ_HANDLED : IRQ_NONE; -} - -/* Notification Handler */ -void gsp_handle_notification(struct gsp_device *gsp, struct gsp_rpc_message *msg) -{ - switch (msg->hdr.rpc_type) { - case GSP_RPC_NOTIFY: - /* Handle VF notifications (WPR, migration, etc.) */ - break; - case GSP_RPC_BAR_TRAP: - atomic64_inc(&gsp->bar_traps); - break; - default: - dev_dbg(gsp->dev, "Unhandled notification type 0x%02x\n", msg->hdr.rpc_type); - } -} - -/* Error Code Translation */ -int gsp_errno_to_errno(int gsp_err) -{ - switch (gsp_err) { - case GSP_OK: return 0; - case GSP_EPERM: return -EPERM; - case GSP_ENOENT: return -ENOENT; - case GSP_EINVAL: return -EINVAL; - case GSP_ENODEV: return -ENODEV; - case GSP_ENOMEM: return -ENOMEM; - case GSP_EBUSY: return -EBUSY; - case GSP_ENOSPC: return -ENOSPC; - case GSP_ETIMEDOUT: return -ETIMEDOUT; - case GSP_EOVERFLOW: return -EOVERFLOW; - case GSP_EDEV: return -EIO; - default: return -EIO; - } -}
\ No newline at end of file |
