diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-09-13 19:07:41 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-09-13 19:07:41 -0400 |
| commit | 4b745a1c2c0534a1850b6efe6710536b2225c3f4 (patch) | |
| tree | 63cedd53fec1366c1d698c57f46fa57a64b9049d | |
| parent | 00e54c13c7c9030c4d5f7151467f2d85a3730e29 (diff) | |
| download | open-iov-4b745a1c2c0534a1850b6efe6710536b2225c3f4.tar.gz open-iov-4b745a1c2c0534a1850b6efe6710536b2225c3f4.zip | |
Phase 1: Add GSP Emulation Engine (host-side)
- New kernel/gsp_emulation.h: GSP emulation context and API
- New kernel/gsp_emulation.c: Per-mdev emulation thread with:
- Adaptive polling of doorbell at BAR0+0x110C00
- Shared memory RPC processing (4KB at BAR0+0x1000)
- Dispatch for all 12 RPC types via firmware-decompiled handlers
- MSI-X completion interrupt injection via eventfd
- Statistics tracking (processed/failed RPCs, latency)
- Modified kernel/main.c: Integrate gsp_emu_init/cleanup in device lifecycle
- Modified kernel/Makefile: Add gsp_emulation.o to build
- Modified kernel/gsp_rpc_client.h: Export gsp_rpc_emulation_set/is_enabled
| -rw-r--r-- | kernel/Makefile | 1 | ||||
| -rw-r--r-- | kernel/gsp_emulation.c | 506 | ||||
| -rw-r--r-- | kernel/gsp_emulation.h | 69 | ||||
| -rw-r--r-- | kernel/gsp_rpc_client.h | 4 | ||||
| -rw-r--r-- | kernel/main.c | 129 |
5 files changed, 650 insertions, 59 deletions
diff --git a/kernel/Makefile b/kernel/Makefile index 18651cf..1b1c04e 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -5,6 +5,7 @@ open-iov-vfio-y := \ bar_init.o \ bar_emulation.o \ gsp_comm.o \ + gsp_emulation.o \ gsp_rpc_client.o \ gsp_rpc_integration.o \ memory.o \ diff --git a/kernel/gsp_emulation.c b/kernel/gsp_emulation.c new file mode 100644 index 0000000..fe1e91e --- /dev/null +++ b/kernel/gsp_emulation.c @@ -0,0 +1,506 @@ +/* + * GSP Emulation Engine Implementation + * Host-side GSP emulation thread that processes RPCs from guest via shared memory + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/dma-mapping.h> +#include <linux/mutex.h> +#include <linux/spinlock.h> +#include <linux/list.h> +#include <linux/workqueue.h> +#include <linux/delay.h> +#include <linux/kthread.h> +#include <linux/sched.h> +#include <linux/ktime.h> +#include <linux/eventfd.h> +#include <asm/io.h> + +#include "nvidia-vgpu-vfio.h" +#include "gsp_emulation.h" +#include "gsp_rpc_client.h" +#include "bar_emulation.h" +#include "irq.h" +#include "memory.h" + +/* ============================================================ + * Constants + * ============================================================ */ + +#define GSP_EMU_DOORBELL_OFFSET 0x110C00 +#define GSP_EMU_SHARED_MEM_OFFSET 0x1000 +#define GSP_EMU_SHARED_MEM_SIZE 4096 +#define GSP_EMU_MSIX_TABLE_OFFSET 0x2000 +#define GSP_EMU_MSIX_PBA_OFFSET 0x3000 +#define GSP_EMU_POLL_BUSY_US 100 +#define GSP_EMU_POLL_SLEEP_MIN_US 50 +#define GSP_EMU_POLL_SLEEP_MAX_US 100 + +/* ============================================================ + * Helper Functions + * ============================================================ */ + +static inline u32 gsp_emu_read_doorbell(struct gsp_emu_context *ctx) +{ + return readl(ctx->doorbell_reg); +} + +static inline void gsp_emu_write_doorbell(struct gsp_emu_context *ctx, u32 val) +{ + writel(val, ctx->doorbell_reg); +} + +static inline void gsp_emu_copy_request_from_shared(struct gsp_emu_context *ctx, + struct gsp_rpc_msg *req) +{ + memcpy(req, ctx->shared_mem_virt, sizeof(*req)); +} + +static inline void gsp_emu_copy_response_to_shared(struct gsp_emu_context *ctx, + struct gsp_rpc_response *rsp) +{ + memcpy(ctx->shared_mem_virt + 0x80, rsp, sizeof(*rsp)); +} + +/* ============================================================ + * RPC Processing + * ============================================================ */ + +static int gsp_emu_dispatch_rpc(struct gsp_emu_context *ctx, + struct gsp_rpc_msg *req, + struct gsp_rpc_response *rsp) +{ + int ret = 0; + ktime_t start = ktime_get(); + + memset(rsp, 0, sizeof(*rsp)); + rsp->type = req->type; + rsp->seq_id = req->seq_id; + + switch (req->type) { + case GSP_RPC_ALLOC: { + struct gsp_alloc_req *areq = (struct gsp_alloc_req *)req->payload; + struct gsp_alloc_rsp arsp = {0}; + u64 gpu_va = 0, handle = 0, actual_size = 0; + + ret = gsp_rpc_alloc(ctx->rpc_client, + areq->size, areq->alignment, areq->mem_type, + areq->flags, areq->addr_hint, areq->pid, + &gpu_va, &handle, &actual_size); + if (!ret) { + arsp.gpu_va = gpu_va; + arsp.handle = handle; + arsp.size = actual_size; + memcpy(rsp->result, &arsp, sizeof(arsp)); + } + break; + } + + case GSP_RPC_FREE: { + u64 handle = req->payload[0]; + ret = gsp_rpc_free(ctx->rpc_client, handle); + break; + } + + case GSP_RPC_MAP: { + struct gsp_map_req *mreq = (struct gsp_map_req *)req->payload; + struct gsp_map_rsp mrsp = {0}; + u64 mapped_va = 0; + + ret = gsp_rpc_map(ctx->rpc_client, + mreq->handle, mreq->gpu_va, mreq->size, + mreq->prot, mreq->flags, mreq->offset, + &mapped_va); + if (!ret) { + mrsp.gpu_va = mapped_va; + memcpy(rsp->result, &mrsp, sizeof(mrsp)); + } + break; + } + + case GSP_RPC_UNMAP: { + u64 handle = req->payload[0]; + u64 gpu_va = req->payload[1]; + u64 size = req->payload[2]; + ret = gsp_rpc_unmap(ctx->rpc_client, handle, gpu_va, size); + break; + } + + case GSP_RPC_PIN: { + struct gsp_pin_req *preq = (struct gsp_pin_req *)req->payload; + struct gsp_pin_rsp prsp = {0}; + u64 dma_addr = 0; + u32 num_pages = 0; + + ret = gsp_rpc_pin(ctx->rpc_client, + preq->handle, preq->offset, preq->size, + preq->flags, &dma_addr, &num_pages); + if (!ret) { + prsp.dma_addr = dma_addr; + prsp.num_pages = num_pages; + memcpy(rsp->result, &prsp, sizeof(prsp)); + } + break; + } + + case GSP_RPC_UNPIN: { + u64 handle = req->payload[0]; + u64 offset = req->payload[1]; + u64 size = req->payload[2]; + ret = gsp_rpc_unpin(ctx->rpc_client, handle, offset, size); + break; + } + + case GSP_RPC_SCHEDULE: { + struct gsp_sched_req *sreq = (struct gsp_sched_req *)req->payload; + ret = gsp_rpc_schedule(ctx->rpc_client, + sreq->work_handle, sreq->callback, sreq->arg, + sreq->priority, sreq->flags); + break; + } + + case GSP_RPC_YIELD: { + ret = gsp_rpc_yield(ctx->rpc_client); + break; + } + + case GSP_RPC_NOTIFY: { + /* NOTIFY is handled via registration, not direct RPC */ + nv_vgpu_info(ctx->vgpu, "NOTIFY RPC received (type=0x%x)", req->subcmd); + ret = 0; + break; + } + + case GSP_RPC_BAR_TRAP: { + struct gsp_bar_trap_req *breq = (struct gsp_bar_trap_req *)req->payload; + struct gsp_bar_trap_rsp brsp = {0}; + u64 ret_value = 0; + + ret = gsp_rpc_bar_trap(ctx->rpc_client, + breq->bar_addr, breq->access_type, breq->size, + breq->value, breq->bar_num, &ret_value); + if (!ret) { + brsp.value = ret_value; + brsp.handled = 1; + memcpy(rsp->result, &brsp, sizeof(brsp)); + } + break; + } + + case GSP_RPC_MIGRATE: { + struct gsp_migrate_req *mreq = (struct gsp_migrate_req *)req->payload; + struct gsp_migrate_rsp mrsp = {0}; + u64 checkpoint = 0; + + if (mreq->flags == GSP_MIGRATE_SUBCMD_SAVE) { + ret = gsp_rpc_migrate_save(ctx->rpc_client, + mreq->src_gpu_id, mreq->dst_gpu_id, + mreq->memory_bitmap, mreq->page_table_ptr, + &checkpoint); + if (!ret) { + mrsp.checkpoint = checkpoint; + memcpy(rsp->result, &mrsp, sizeof(mrsp)); + } + } else if (mreq->flags == GSP_MIGRATE_SUBCMD_RESTORE) { + ret = gsp_rpc_migrate_restore(ctx->rpc_client, + mreq->src_gpu_id, mreq->dst_gpu_id, + mreq->memory_bitmap, mreq->page_table_ptr, + mreq->context); + } + break; + } + + case GSP_RPC_DEBUG: { + nv_vgpu_info(ctx->vgpu, "DEBUG RPC received (seq=%u)", req->seq_id); + ret = 0; + break; + } + + default: + nv_vgpu_warn(ctx->vgpu, "Unknown RPC type: 0x%x", req->type); + rsp->status = -ENOTSUPP; + ret = -ENOTSUPP; + break; + } + + if (ret) + rsp->status = ret; + + ctx->total_latency_ns += ktime_to_ns(ktime_sub(ktime_get(), start)); + return ret; +} + +/* ============================================================ + * MSI-X Interrupt Injection + * ============================================================ */ + +void gsp_emu_inject_completion_interrupt(struct gsp_emu_context *ctx, u32 seq_id) +{ + /* Write to MSI-X PBA (Pending Bit Array) at BAR0+0x3000 */ + if (ctx->bar0_vaddr) { + u32 *pba = (u32 *)(ctx->bar0_vaddr + GSP_EMU_MSIX_PBA_OFFSET); + writel(1, pba); /* Set pending bit for vector 0 */ + } + + /* Signal eventfd for VFIO interrupt delivery */ + if (ctx->msix_trigger[0]) { + eventfd_signal(ctx->msix_trigger[0]); + } +} + +/* ============================================================ + * Emulation Thread + * ============================================================ */ + +static int gsp_emu_thread_fn(void *data) +{ + struct gsp_emu_context *ctx = data; + struct gsp_rpc_msg req; + struct gsp_rpc_response rsp; + u32 doorbell_val; + int ret; + + nv_vgpu_info(ctx->vgpu, "GSP emulation thread started"); + + while (!kthread_should_stop()) { + /* Read doorbell */ + doorbell_val = gsp_emu_read_doorbell(ctx); + + if (doorbell_val != 0) { + /* ACK doorbell immediately */ + gsp_emu_write_doorbell(ctx, 0); + + /* Copy request from shared memory */ + gsp_emu_copy_request_from_shared(ctx, &req); + + /* Validate sequence ID */ + if (req.seq_id == 0) { + nv_vgpu_warn(ctx->vgpu, "Invalid seq_id=0, ignoring RPC"); + continue; + } + + /* Process RPC */ + ret = gsp_emu_dispatch_rpc(ctx, &req, &rsp); + + /* Write response to shared memory */ + gsp_emu_copy_response_to_shared(ctx, &rsp); + + /* Inject completion interrupt */ + gsp_emu_inject_completion_interrupt(ctx, req.seq_id); + + if (ret) + ctx->failed_rpcs++; + else + ctx->processed_rpcs++; + } else { + /* Adaptive polling: busy-wait first, then sleep */ + static int poll_count = 0; + if (poll_count < GSP_EMU_POLL_BUSY_US) { + udelay(1); + poll_count++; + } else { + usleep_range(GSP_EMU_POLL_SLEEP_MIN_US, GSP_EMU_POLL_SLEEP_MAX_US); + poll_count = 0; + } + } + } + + nv_vgpu_info(ctx->vgpu, "GSP emulation thread stopped (processed=%llu, failed=%llu)", + ctx->processed_rpcs, ctx->failed_rpcs); + return 0; +} + +/* ============================================================ + * Initialization & Cleanup + * ============================================================ */ + +int gsp_emu_init(struct nv_vgpu_device *vgpu) +{ + struct gsp_emu_context *ctx; + phys_addr_t bar0_phys, doorbell_phys, shared_mem_phys; + int ret = 0; + + nv_vgpu_info(vgpu, "Initializing GSP emulation engine"); + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->vgpu = vgpu; + ctx->running = false; + ctx->initialized = false; + spin_lock_init(&ctx->lock); + + /* Get BAR0 physical address and map it */ + bar0_phys = vgpu->bars[0].gpa; + if (!bar0_phys) { + nv_vgpu_err(vgpu, "BAR0 physical address not available"); + ret = -ENODEV; + goto err_free_ctx; + } + + ctx->bar0_vaddr = ioremap(bar0_phys, NV_VGPU_BAR0_SIZE); + if (!ctx->bar0_vaddr) { + nv_vgpu_err(vgpu, "Failed to map BAR0"); + ret = -ENOMEM; + goto err_free_ctx; + } + + /* Locate doorbell register at BAR0+0x110C00 */ + ctx->doorbell_reg = ctx->bar0_vaddr + GSP_EMU_DOORBELL_OFFSET; + + /* Allocate shared memory (4KB, page-aligned, WC mapping) */ + ctx->shared_mem_size = GSP_EMU_SHARED_MEM_SIZE; + ctx->shared_mem_virt = dma_alloc_wc(&vgpu->pdev->dev, ctx->shared_mem_size, + &ctx->shared_mem_dma, GFP_KERNEL); + if (!ctx->shared_mem_virt) { + nv_vgpu_err(vgpu, "Failed to allocate shared memory"); + ret = -ENOMEM; + goto err_unmap_bar0; + } + + /* Map shared memory into guest BAR0 at offset 0x1000 */ + shared_mem_phys = ctx->shared_mem_dma; + /* Note: In real implementation, this would be mapped via IOMMU to guest GPA. + * For now, we use the host virtual address directly since guest accesses + * go through VFIO BAR mapping. */ + ctx->shared_mem = ctx->bar0_vaddr + GSP_EMU_SHARED_MEM_OFFSET; + + /* Initialize RPC client with firmware base and doorbell */ + doorbell_phys = bar0_phys + GSP_EMU_DOORBELL_OFFSET; + ctx->rpc_client = gsp_client_init(&vgpu->dev, bar0_phys, doorbell_phys); + if (IS_ERR(ctx->rpc_client)) { + ret = PTR_ERR(ctx->rpc_client); + nv_vgpu_err(vgpu, "Failed to init GSP RPC client: %d", ret); + goto err_free_shared_mem; + } + + /* Enable emulation mode in RPC client */ + gsp_rpc_emulation_set(true); + + /* Get MSI-X eventfd triggers from IRQ context */ + for (int i = 0; i < NV_VGPU_MAX_IRQS; i++) { + ctx->msix_trigger[i] = vgpu->irqs[i].trigger; + } + + /* Start emulation thread */ + ctx->thread = kthread_run(gsp_emu_thread_fn, ctx, "gsp_emu_%u", vgpu->vgpu_id); + if (IS_ERR(ctx->thread)) { + ret = PTR_ERR(ctx->thread); + nv_vgpu_err(vgpu, "Failed to start GSP emulation thread: %d", ret); + goto err_fini_client; + } + + ctx->running = true; + ctx->initialized = true; + vgpu->gsp_comm_ctx = ctx; /* Reuse existing field for emulation context */ + + nv_vgpu_info(vgpu, "GSP emulation engine initialized successfully"); + return 0; + +err_fini_client: + gsp_client_fini(ctx->rpc_client); +err_free_shared_mem: + dma_free_wc(&vgpu->pdev->dev, ctx->shared_mem_size, + ctx->shared_mem_virt, ctx->shared_mem_dma); +err_unmap_bar0: + iounmap(ctx->bar0_vaddr); +err_free_ctx: + kfree(ctx); + return ret; +} + +void gsp_emu_cleanup(struct nv_vgpu_device *vgpu) +{ + struct gsp_emu_context *ctx = vgpu->gsp_comm_ctx; + + if (!ctx) + return; + + nv_vgpu_info(vgpu, "Cleaning up GSP emulation engine"); + + /* Stop emulation thread */ + if (ctx->thread) { + kthread_stop(ctx->thread); + ctx->thread = NULL; + } + ctx->running = false; + + /* Disable emulation mode */ + gsp_rpc_emulation_set(false); + + /* Cleanup RPC client */ + if (ctx->rpc_client) { + gsp_client_fini(ctx->rpc_client); + ctx->rpc_client = NULL; + } + + /* Free shared memory */ + if (ctx->shared_mem_virt) { + dma_free_wc(&vgpu->pdev->dev, ctx->shared_mem_size, + ctx->shared_mem_virt, ctx->shared_mem_dma); + ctx->shared_mem_virt = NULL; + } + + /* Unmap BAR0 */ + if (ctx->bar0_vaddr) { + iounmap(ctx->bar0_vaddr); + ctx->bar0_vaddr = NULL; + } + + vgpu->gsp_comm_ctx = NULL; + kfree(ctx); + + nv_vgpu_info(vgpu, "GSP emulation engine cleaned up"); +} + +/* ============================================================ + * Public RPC Processing (for external callers) + * ============================================================ */ + +int gsp_emu_process_rpc(struct gsp_emu_context *ctx) +{ + struct gsp_rpc_msg req; + struct gsp_rpc_response rsp; + int ret; + + if (!ctx || !ctx->initialized) + return -ENODEV; + + gsp_emu_copy_request_from_shared(ctx, &req); + + if (req.seq_id == 0) + return -EINVAL; + + ret = gsp_emu_dispatch_rpc(ctx, &req, &rsp); + + gsp_emu_copy_response_to_shared(ctx, &rsp); + gsp_emu_inject_completion_interrupt(ctx, req.seq_id); + + return ret; +} + +/* ============================================================ + * Statistics + * ============================================================ */ + +void gsp_emu_get_stats(struct gsp_emu_context *ctx, + u64 *processed, u64 *failed, u64 *total_latency_ns) +{ + if (!ctx) + return; + + if (processed) + *processed = ctx->processed_rpcs; + if (failed) + *failed = ctx->failed_rpcs; + if (total_latency_ns) + *total_latency_ns = ctx->total_latency_ns; +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Open-IOV Project"); +MODULE_DESCRIPTION("GSP Emulation Engine for Host-Side vGPU");
\ No newline at end of file diff --git a/kernel/gsp_emulation.h b/kernel/gsp_emulation.h new file mode 100644 index 0000000..c4ceb7a --- /dev/null +++ b/kernel/gsp_emulation.h @@ -0,0 +1,69 @@ +/* + * GSP Emulation Engine for Host-Side GSP Emulation + * Replaces guest-side GSP communication with host kernel emulation + */ + +#ifndef _GSP_EMULATION_H_ +#define _GSP_EMULATION_H_ + +#include <linux/types.h> +#include <linux/device.h> +#include <linux/mutex.h> +#include <linux/spinlock.h> +#include <linux/workqueue.h> +#include <linux/list.h> +#include <linux/kthread.h> +#include <linux/eventfd.h> + +#include "nvidia-vgpu-vfio.h" +#include "gsp_rpc_client.h" +#include "bar_emulation.h" +#include "irq.h" +#include "memory.h" + +/* ============================================================ + * GSP Emulation Context + * ============================================================ */ + +struct gsp_emu_context { + struct nv_vgpu_device *vgpu; + struct task_struct *thread; + void __iomem *bar0_vaddr; + void __iomem *doorbell_reg; + void __iomem *shared_mem; + struct gsp_client *rpc_client; + spinlock_t lock; + bool running; + bool initialized; + + /* Shared memory for RPC communication (4KB at BAR0+0x1000) */ + void *shared_mem_virt; + dma_addr_t shared_mem_dma; + size_t shared_mem_size; + + /* Statistics */ + u64 processed_rpcs; + u64 failed_rpcs; + u64 total_latency_ns; + + /* MSI-X injection */ + struct eventfd_ctx *msix_trigger[NV_VGPU_MAX_IRQS]; +}; + +/* ============================================================ + * Public API + * ============================================================ */ + +int gsp_emu_init(struct nv_vgpu_device *vgpu); +void gsp_emu_cleanup(struct nv_vgpu_device *vgpu); +int gsp_emu_process_rpc(struct gsp_emu_context *ctx); +void gsp_emu_inject_completion_interrupt(struct gsp_emu_context *ctx, u32 seq_id); + +/* ============================================================ + * Debug/Statistics + * ============================================================ */ + +void gsp_emu_get_stats(struct gsp_emu_context *ctx, + u64 *processed, u64 *failed, u64 *total_latency_ns); + +#endif /* _GSP_EMULATION_H_ */
\ No newline at end of file diff --git a/kernel/gsp_rpc_client.h b/kernel/gsp_rpc_client.h index 8702b0f..64bfe5b 100644 --- a/kernel/gsp_rpc_client.h +++ b/kernel/gsp_rpc_client.h @@ -310,4 +310,8 @@ int gsp_wpr_write_mutex_release(struct gsp_client *client, u32 wpr_id); int gsp_wpr_scratch_mutex_acquire(struct gsp_client *client, u32 wpr_id); int gsp_wpr_scratch_mutex_release(struct gsp_client *client, u32 wpr_id); +/* Emulation control */ +void gsp_rpc_emulation_set(bool enable); +bool gsp_rpc_emulation_is_enabled(void); + #endif /* _GSP_RPC_CLIENT_H_ */ diff --git a/kernel/main.c b/kernel/main.c index 7d12203..efe5082 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -17,6 +17,7 @@ #include "nvidia-vgpu-vfio.h" #include "bar_emulation.h" #include "gsp_comm.h" +#include "gsp_emulation.h" #include "memory.h" #include "irq.h" #include "migration.h" @@ -450,76 +451,86 @@ static void __exit nv_vgpu_exit(void) static void nv_vgpu_device_init_work(struct work_struct *work) { - struct nv_vgpu_device *vgpu = container_of(work, struct nv_vgpu_device, init_work); - int ret; - - mutex_lock(&vgpu->lock); - vgpu->state = NV_VGPU_STATE_INITIALIZING; - mutex_unlock(&vgpu->lock); - - ret = nv_vgpu_bar_init(vgpu); - if (ret) { - nv_vgpu_err(vgpu, "BAR init failed: %d", ret); - goto err_state; - } - - ret = nv_vgpu_irq_init(vgpu); - if (ret) { - nv_vgpu_err(vgpu, "IRQ init failed: %d", ret); - goto err_bar; - } - - ret = nv_vgpu_memory_init(vgpu); - if (ret) { - nv_vgpu_err(vgpu, "Memory init failed: %d", ret); - goto err_irq; - } - - ret = nv_vgpu_gsp_init(vgpu); - if (ret) { - nv_vgpu_err(vgpu, "GSP init failed: %d", ret); - goto err_memory; - } - - ret = nv_vgpu_migration_init(vgpu); - if (ret) { - nv_vgpu_warn(vgpu, "Migration init failed: %d", ret); - } - - mutex_lock(&vgpu->lock); - vgpu->state = NV_VGPU_STATE_READY; - mutex_unlock(&vgpu->lock); - - nv_vgpu_info(vgpu, "vGPU device initialized successfully"); - return; - + struct nv_vgpu_device *vgpu = container_of(work, struct nv_vgpu_device, init_work); + int ret; + + mutex_lock(&vgpu->lock); + vgpu->state = NV_VGPU_STATE_INITIALIZING; + mutex_unlock(&vgpu->lock); + + ret = nv_vgpu_bar_init(vgpu); + if (ret) { + nv_vgpu_err(vgpu, "BAR init failed: %d", ret); + goto err_state; + } + + ret = nv_vgpu_irq_init(vgpu); + if (ret) { + nv_vgpu_err(vgpu, "IRQ init failed: %d", ret); + goto err_bar; + } + + ret = nv_vgpu_memory_init(vgpu); + if (ret) { + nv_vgpu_err(vgpu, "Memory init failed: %d", ret); + goto err_irq; + } + + ret = nv_vgpu_gsp_init(vgpu); + if (ret) { + nv_vgpu_err(vgpu, "GSP init failed: %d", ret); + goto err_memory; + } + + /* Initialize GSP emulation engine (host-side) */ + ret = gsp_emu_init(vgpu); + if (ret) { + nv_vgpu_err(vgpu, "GSP emulation init failed: %d", ret); + goto err_gsp; + } + + ret = nv_vgpu_migration_init(vgpu); + if (ret) { + nv_vgpu_warn(vgpu, "Migration init failed: %d", ret); + } + + mutex_lock(&vgpu->lock); + vgpu->state = NV_VGPU_STATE_READY; + mutex_unlock(&vgpu->lock); + + nv_vgpu_info(vgpu, "vGPU device initialized successfully"); + return; + +err_gsp: + nv_vgpu_gsp_cleanup(vgpu); err_memory: - nv_vgpu_memory_cleanup(vgpu); + nv_vgpu_memory_cleanup(vgpu); err_irq: - nv_vgpu_irq_cleanup(vgpu); + nv_vgpu_irq_cleanup(vgpu); err_bar: - nv_vgpu_bar_cleanup(vgpu); + nv_vgpu_bar_cleanup(vgpu); err_state: - mutex_lock(&vgpu->lock); - vgpu->state = NV_VGPU_STATE_ERROR; - mutex_unlock(&vgpu->lock); + mutex_lock(&vgpu->lock); + vgpu->state = NV_VGPU_STATE_ERROR; + mutex_unlock(&vgpu->lock); } static void nv_vgpu_device_cleanup_work(struct work_struct *work) { - struct nv_vgpu_device *vgpu = container_of(work, struct nv_vgpu_device, cleanup_work); + struct nv_vgpu_device *vgpu = container_of(work, struct nv_vgpu_device, cleanup_work); - nv_vgpu_migration_cleanup(vgpu); - nv_vgpu_gsp_cleanup(vgpu); - nv_vgpu_memory_cleanup(vgpu); - nv_vgpu_irq_cleanup(vgpu); - nv_vgpu_bar_cleanup(vgpu); + nv_vgpu_migration_cleanup(vgpu); + gsp_emu_cleanup(vgpu); + nv_vgpu_gsp_cleanup(vgpu); + nv_vgpu_memory_cleanup(vgpu); + nv_vgpu_irq_cleanup(vgpu); + nv_vgpu_bar_cleanup(vgpu); - mutex_lock(&vgpu->lock); - vgpu->state = NV_VGPU_STATE_UNINITIALIZED; - mutex_unlock(&vgpu->lock); + mutex_lock(&vgpu->lock); + vgpu->state = NV_VGPU_STATE_UNINITIALIZED; + mutex_unlock(&vgpu->lock); - nv_vgpu_info(vgpu, "vGPU device cleaned up"); + nv_vgpu_info(vgpu, "vGPU device cleaned up"); } static NV_STATUS nv_vgpu_vfio_probe(struct pci_dev *pdev, NvU32 vgpu_type_id, NvU32 *vgpu_id) |
