diff options
Diffstat (limited to 'legacy-driver-guest/vf/gsp_vf.c')
| -rw-r--r-- | legacy-driver-guest/vf/gsp_vf.c | 312 |
1 files changed, 312 insertions, 0 deletions
diff --git a/legacy-driver-guest/vf/gsp_vf.c b/legacy-driver-guest/vf/gsp_vf.c new file mode 100644 index 0000000..951c84d --- /dev/null +++ b/legacy-driver-guest/vf/gsp_vf.c @@ -0,0 +1,312 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * GSP vGPU-IO Driver - VF Management + */ + +#include "gsp_core.h" +#include <linux/slab.h> +#include <linux/idr.h> +#include <linux/list.h> + +/* VF Reference Counting */ +static void gsp_vf_release(struct kref *ref) +{ + struct gsp_vf *vf = container_of(ref, struct gsp_vf, ref); + kfree(vf); +} + +struct gsp_vf *gsp_vf_get(struct gsp_device *gsp, uint32_t id) +{ + struct gsp_vf *vf; + + mutex_lock(&gsp->vf_lock); + vf = idr_find(&gsp->vf_idr, id); + if (vf && vf->active) + kref_get(&vf->ref); + else + vf = NULL; + mutex_unlock(&gsp->vf_lock); + + return vf; +} + +void gsp_vf_put(struct gsp_vf *vf) +{ + if (vf) + kref_put(&vf->ref, gsp_vf_release); +} + +/* VF Creation */ +int gsp_vf_create(struct gsp_device *gsp, const struct gsp_vf_config *config) +{ + struct gsp_vf *vf; + int ret, id; + + if (!config || config->id == 0 || config->id > gsp->max_vfs) + return -EINVAL; + + vf = kzalloc(sizeof(*vf), GFP_KERNEL); + if (!vf) + return -ENOMEM; + + vf->gsp = gsp; + vf->id = config->id; + kref_init(&vf->ref); + mutex_init(&vf->lock); + mutex_init(&vf->wpr_lock); + INIT_LIST_HEAD(&vf->wpr_list); + INIT_LIST_HEAD(&vf->list); + atomic_set(&vf->refcount, 1); + + /* Copy configuration */ + vf->bar1_offset = config->bar1_offset; + vf->bar1_size = config->bar1_size; + vf->pt_base = config->pt_base; + vf->pt_size = config->pt_size; + vf->cmd_base = config->cmd_base; + vf->cmd_size = config->cmd_size; + + /* Allocate VF ID */ + mutex_lock(&gsp->vf_lock); + id = idr_alloc(&gsp->vf_idr, vf, config->id, config->id + 1, GFP_KERNEL); + if (id < 0) { + ret = id; + mutex_unlock(&gsp->vf_lock); + goto err_free; + } + + list_add(&vf->list, &gsp->vf_list); + vf->active = true; + vf->state = GSP_VF_ACTIVE; + mutex_unlock(&gsp->vf_lock); + + /* Initialize subsystems */ + ret = gsp_vf_wpr_init(vf); + if (ret) { + dev_err(gsp->dev, "VF %u: WPR init failed: %d\n", vf->id, ret); + goto err_wpr; + } + + ret = gsp_vf_bar_init(vf); + if (ret) { + dev_err(gsp->dev, "VF %u: BAR init failed: %d\n", vf->id, ret); + goto err_bar; + } + + dev_info(gsp->dev, "VF %u created (bar1=0x%llx-0x%llx, pt=0x%llx-0x%llx)\n", + vf->id, vf->bar1_offset, vf->bar1_offset + vf->bar1_size, + vf->pt_base, vf->pt_base + vf->pt_size); + + return 0; + +err_bar: + gsp_vf_wpr_fini(vf); +err_wpr: + mutex_lock(&gsp->vf_lock); + list_del(&vf->list); + idr_remove(&gsp->vf_idr, vf->id); + vf->active = false; + mutex_unlock(&gsp->vf_lock); +err_free: + kfree(vf); + return ret; +} + +/* VF Destruction */ +void gsp_vf_destroy(struct gsp_vf *vf) +{ + struct gsp_device *gsp = vf->gsp; + uint32_t id = vf->id; + + if (!vf->active) + return; + + /* Cancel any ongoing migration */ + mutex_lock(&vf->lock); + if (vf->migration) { + gsp_migration_cancel(vf->migration); + vf->migration = NULL; + } + vf->active = false; + vf->state = GSP_VF_DESTROYING; + mutex_unlock(&vf->lock); + + /* Remove from device lists */ + mutex_lock(&gsp->vf_lock); + list_del(&vf->list); + idr_remove(&gsp->vf_idr, id); + mutex_unlock(&gsp->vf_lock); + + /* Cleanup subsystems */ + gsp_vf_bar_fini(vf); + gsp_vf_wpr_fini(vf); + + dev_info(gsp->dev, "VF %u destroyed\n", id); + + gsp_vf_put(vf); +} + +/* Memory Allocation */ +int gsp_vf_mem_alloc(struct gsp_vf *vf, uint64_t size, uint32_t flags, + uint64_t *handle, uint64_t *va) +{ + struct gsp_alloc_payload payload = { + .size = size, + .type = flags & 0x3, /* Low bits: mem type */ + .alignment = 0x1000, + .va_hint = 0, + .flags = flags >> 2, + .pid = current->pid, + .vf_id = vf->id, + }; + struct gsp_alloc_response resp; + size_t resp_size = sizeof(resp); + int ret; + + if (!handle || !va) + return -EINVAL; + + /* In emulation mode, handle locally */ + if (emulation_mode) { + static uint64_t next_handle = 0x1000; + static uint64_t next_va = 0x100000000ULL; /* 4GB base */ + + *handle = next_handle++; + *va = next_va; + next_va += (size + 0xFFF) & ~0xFFF; /* Align to 4KB */ + + dev_dbg(vf->gsp->dev, "VF %u: alloc handle=0x%llx va=0x%llx size=0x%llx (emulation)\n", + vf->id, *handle, *va, size); + + return 0; + } + + ret = gsp_rpc_send_sync(vf->gsp->rpc, GSP_RPC_ALLOC, + &payload, sizeof(payload), + &resp, &resp_size); + if (ret) + return ret; + + *handle = resp.handle; + *va = resp.va; + + dev_dbg(vf->gsp->dev, "VF %u: alloc handle=0x%llx va=0x%llx size=0x%llx\n", + vf->id, *handle, *va, size); + + return 0; +} + +int gsp_vf_mem_free(struct gsp_vf *vf, uint64_t handle) +{ + /* FREE uses primary dispatcher sub-command */ + struct gsp_subcmd_desc desc; + size_t desc_size = sizeof(desc); + int ret; + + /* Subcmd for FREE: need to determine which subcmd corresponds to FREE */ + /* For now, use a generic FREE RPC if implemented */ + + /* Placeholder - actual implementation depends on FREE subcmd mapping */ + dev_dbg(vf->gsp->dev, "VF %u: free handle=0x%llx\n", vf->id, handle); + + return 0; +} + +/* Memory Pinning */ +int gsp_vf_mem_pin(struct gsp_vf *vf, uint64_t handle, + uint64_t offset, uint64_t size, + uint64_t *dma_addr) +{ + struct gsp_pin_payload payload = { + .handle = handle, + .offset = offset, + .size = size, + .vf_id = vf->id, + .flags = GSP_PIN_READ | GSP_PIN_WRITE, + }; + struct gsp_pin_response resp; + size_t resp_size = sizeof(resp); + int ret; + + if (!dma_addr) + return -EINVAL; + + /* In emulation mode, handle locally */ + if (emulation_mode) { + static uint64_t next_dma = 0x200000000ULL; /* 8GB base */ + *dma_addr = next_dma; + next_dma += (size + 0xFFF) & ~0xFFF; + + dev_dbg(vf->gsp->dev, "VF %u: pin handle=0x%llx offset=0x%llx size=0x%llx -> dma=0x%llx (emulation)\n", + vf->id, handle, offset, size, *dma_addr); + + return 0; + } + + ret = gsp_rpc_send_sync(vf->gsp->rpc, GSP_RPC_PIN, + &payload, sizeof(payload), + &resp, &resp_size); + if (ret) + return ret; + + *dma_addr = resp.dma_addr; + + dev_dbg(vf->gsp->dev, "VF %u: pin handle=0x%llx offset=0x%llx size=0x%llx -> dma=0x%llx\n", + vf->id, handle, offset, size, *dma_addr); + + return 0; +} + +int gsp_vf_mem_unpin(struct gsp_vf *vf, uint64_t handle) +{ + dev_dbg(vf->gsp->dev, "VF %u: unpin handle=0x%llx\n", vf->id, handle); + return 0; +} + +/* Memory Mapping */ +int gsp_vf_mem_map(struct gsp_vf *vf, uint64_t handle, uint64_t gpu_va, + uint64_t size, uint32_t prot, uint32_t flags, + uint64_t offset, uint64_t *mapped_va) +{ + /* In emulation mode, just return the requested VA */ + if (emulation_mode) { + if (mapped_va) + *mapped_va = gpu_va; + + dev_dbg(vf->gsp->dev, "VF %u: map handle=0x%llx gpu_va=0x%llx size=0x%llx -> mapped_va=0x%llx (emulation)\n", + vf->id, handle, gpu_va, size, gpu_va); + + return 0; + } + + /* TODO: Implement GSP RPC for MAP */ + dev_dbg(vf->gsp->dev, "VF %u: map handle=0x%llx gpu_va=0x%llx size=0x%llx\n", + vf->id, handle, gpu_va, size); + + if (mapped_va) + *mapped_va = gpu_va; + + return 0; +} + +int gsp_vf_mem_unmap(struct gsp_vf *vf, uint64_t handle, uint64_t gpu_va, uint64_t size) +{ + dev_dbg(vf->gsp->dev, "VF %u: unmap handle=0x%llx gpu_va=0x%llx size=0x%llx\n", + vf->id, handle, gpu_va, size); + return 0; +} + +/* Scheduling */ +int gsp_vf_schedule(struct gsp_vf *vf, uint32_t priority, uint64_t time_slice_ns) +{ + /* Use primary dispatcher SCHEDULE subcmd or secondary dispatcher */ + dev_dbg(vf->gsp->dev, "VF %u: schedule prio=%u slice=%llu ns\n", + vf->id, priority, time_slice_ns); + return 0; +} + +int gsp_vf_yield(struct gsp_vf *vf) +{ + dev_dbg(vf->gsp->dev, "VF %u: yield\n", vf->id); + return 0; +}
\ No newline at end of file |
