diff options
| -rw-r--r-- | kernel/gsp_phys_booter.c | 133 | ||||
| -rw-r--r-- | kernel/gsp_phys_booter.h | 54 | ||||
| -rw-r--r-- | kernel/gsp_phys_gpu.c | 127 | ||||
| -rw-r--r-- | kernel/gsp_phys_gpu.h | 27 |
4 files changed, 341 insertions, 0 deletions
diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c index 247dcff..255d79e 100644 --- a/kernel/gsp_phys_booter.c +++ b/kernel/gsp_phys_booter.c @@ -4444,6 +4444,139 @@ static int gsp_phys_rm_probe(void __iomem *bar0) } /* ============================================================ + * Backend session transport API (P0 skeleton, inert) + * ============================================================ */ + +static struct gsp_phys_backend_static_config gsp_phys_backend_static; + +int gsp_phys_backend_rpc(u32 function, const void *req, u32 req_len, + void **reply, u32 *reply_len, u32 timeout_ms) +{ + struct gsp_phys_status_queue sq; + struct gsp_phys_rx_record rec; + void __iomem *bar0; + void *payload = NULL; + u32 generation; + int ret; + + if (reply) + *reply = NULL; + if (reply_len) + *reply_len = 0; + + /* + * Lock-free readiness check so a non-READY session is refused without + * taking the real-GSP lock or touching the device. The generation is + * captured before the lock and re-checked under it: a stop that bumps + * the generation between the two checks is seen below and the forward + * is refused instead of racing the release. + */ + if (gsp_phys_backend_state() != GSP_PHYS_BACKEND_READY || + !gsp_phys_gpu_claimed() || !boot_stage.rm_init_done || + boot_stage.rm_unload_submitted) { + pr_info("gsp-phys-boot: backend rpc fn%u refused: not ready (state=%d claimed=%u rm_init_done=%u unload_submitted=%u)\n", + function, gsp_phys_backend_state(), + gsp_phys_gpu_claimed(), boot_stage.rm_init_done, + boot_stage.rm_unload_submitted); + return -EAGAIN; + } + + generation = gsp_phys_backend_generation(); + bar0 = gsp_phys_gpu_bar0(); + if (!bar0) { + pr_info("gsp-phys-boot: backend rpc fn%u refused: no BAR0\n", + function); + return -ENODEV; + } + + mutex_lock(&gsp_phys_rm_lock); + if (gsp_phys_backend_state() != GSP_PHYS_BACKEND_READY || + gsp_phys_backend_generation() != generation || + !boot_stage.rm_init_done || boot_stage.rm_unload_submitted) { + u32 gen_now = gsp_phys_backend_generation(); + + mutex_unlock(&gsp_phys_rm_lock); + pr_info("gsp-phys-boot: backend rpc fn%u refused: session changed while waiting for the GSP lock (generation %u -> %u)\n", + function, generation, gen_now); + return -EAGAIN; + } + + ret = gsp_phys_queue_tx_submit(bar0, function, req, req_len); + if (ret) { + mutex_unlock(&gsp_phys_rm_lock); + pr_err("gsp-phys-boot: backend rpc fn%u submit refused: %d\n", + function, ret); + return ret; + } + + gsp_phys_status_queue_init(&sq); + memset(&rec, 0, sizeof(rec)); + ret = gsp_phys_status_wait(&sq, function, false, 0, timeout_ms, &rec); + if (ret) { + mutex_unlock(&gsp_phys_rm_lock); + pr_err("gsp-phys-boot: backend rpc fn%u no reply within %u ms (%d)\n", + function, timeout_ms, ret); + return ret; + } + + pr_info("gsp-phys-boot: backend rpc fn%u reply: payload=%u B seqNum=%u rpc.seq=%u rpc_result=0x%x private=0x%x checksum=0x%08x\n", + function, rec.payload_len, rec.seq_num, rec.rpc_sequence, + rec.rpc_result, rec.rpc_result_private, rec.checksum); + + if (rec.rpc_result != 0) { + pr_err("gsp-phys-boot: backend rpc fn%u server rpc_result=0x%x (not success)\n", + function, rec.rpc_result); + ret = -EIO; + goto out; + } + + if (reply && rec.payload_len) { + payload = kmemdup(rec.buf + GSP_PHYS_MSG_ELEMENT_PAYLOAD_OFF, + rec.payload_len, GFP_KERNEL); + if (!payload) { + ret = -ENOMEM; + goto out; + } + } + if (reply) + *reply = payload; + if (reply_len) + *reply_len = rec.payload_len; + +out: + gsp_phys_rx_record_free(&rec); + mutex_unlock(&gsp_phys_rm_lock); + return ret; +} + +int gsp_phys_backend_static_snapshot(struct gsp_phys_backend_static_config *out) +{ + u32 generation; + + if (!out) + return -EINVAL; + + if (gsp_phys_backend_state() != GSP_PHYS_BACKEND_READY) + return -EAGAIN; + + generation = gsp_phys_backend_generation(); + mutex_lock(&gsp_phys_rm_lock); + if (gsp_phys_backend_state() != GSP_PHYS_BACKEND_READY || + gsp_phys_backend_generation() != generation) { + mutex_unlock(&gsp_phys_rm_lock); + return -EAGAIN; + } + if (!gsp_phys_backend_static.valid) { + mutex_unlock(&gsp_phys_rm_lock); + pr_debug("gsp-phys-boot: backend static snapshot: cache not populated yet\n"); + return -ENODATA; + } + *out = gsp_phys_backend_static; + mutex_unlock(&gsp_phys_rm_lock); + return 0; +} + +/* ============================================================ * Host RM client ladder (R1): gsp-phys-rm-call * ============================================================ */ diff --git a/kernel/gsp_phys_booter.h b/kernel/gsp_phys_booter.h index 0902830..15068ff 100644 --- a/kernel/gsp_phys_booter.h +++ b/kernel/gsp_phys_booter.h @@ -90,6 +90,60 @@ void gsp_phys_boot_quiesce(void __iomem *bar0); void gsp_phys_boot_staging_free(struct pci_dev *pdev); /* + * Backend session transport API (P0 skeleton; inert until the guest plane + * forwards). The session state/refcount/owner live in gsp_phys_gpu.c; these + * entry points share gsp_phys_rm_lock with the boot/probe/ladder paths so + * there is one real-GSP RPC at a time. Callers check gsp_phys_backend_state() + * first and call here; this file never takes the session lock itself (lock + * order: session lock outermost, rm lock inner). + * + * gsp_phys_backend_rpc() re-checks READY and the generation under the rm + * lock and refuses (-EAGAIN) without touching the device when the session is + * not live. On success *reply owns a kmalloc'd copy of the reply payload + * (*reply_len bytes, NULL/0 for an empty payload, caller frees). + * + * gsp_phys_backend_static_snapshot() returns the cached static-config fields + * decoded from the function-65 reply; the cache is filled at READY in a + * later phase, so it currently reports -ENODATA. + */ +#define GSP_PHYS_BACKEND_GPU_NAME_LEN 64 + +struct gsp_phys_backend_static_config { + bool valid; /* cache holds a decoded function-65 reply */ + u32 fw_crc32; /* cache key: loaded firmware image */ + u32 payload_len; /* decoded reply payload bytes */ + u32 h_internal_client; + u32 h_internal_device; + u32 h_internal_subdevice; + u64 bar1_pde_base; + u64 bar2_pde_base; + u64 fb_length; + u64 fbio_mask; + u64 fbp_mask; + u32 fb_bus_width; + u32 fb_ram_type; + u32 l2_cache_size; + u32 engine_caps[3]; + char gpu_name[GSP_PHYS_BACKEND_GPU_NAME_LEN + 1]; + char gpu_short_name[GSP_PHYS_BACKEND_GPU_NAME_LEN + 1]; + u32 sriov_max_gfid; + u32 vbios_sub_vendor; + u32 vbios_sub_device; + u8 b_vbios_valid; + u8 b_page_retirement; + u8 b_split_vas; + u8 b_self_hosted; + u8 b_ats_supported; + u8 b_poison_fuse; + u8 b_gpu_uefi; + u8 b_efi_init; +}; + +int gsp_phys_backend_rpc(u32 function, const void *req, u32 req_len, + void **reply, u32 *reply_len, u32 timeout_ms); +int gsp_phys_backend_static_snapshot(struct gsp_phys_backend_static_config *out); + +/* * Command-queue TX: build a msgq record with the 570.144 element/rpc header * layout, splitting it into 1..16 4 KB elements when it does not fit one * (reference elemCount = ceil((48 + rpc.length) / 4096), each element copied diff --git a/kernel/gsp_phys_gpu.c b/kernel/gsp_phys_gpu.c index 88d13f2..22cdc33 100644 --- a/kernel/gsp_phys_gpu.c +++ b/kernel/gsp_phys_gpu.c @@ -49,6 +49,7 @@ #include <linux/module.h> #include <linux/kernel.h> +#include <linux/mutex.h> #include <linux/pci.h> #include <linux/pm_runtime.h> #include <linux/delay.h> @@ -1001,3 +1002,129 @@ void gsp_phys_gpu_release(void) gsp_phys_power_unlock(); } + +/* + * Backend session state (P0 skeleton, inert until a caller wires it). One + * physical GPU, one live claim, refcounted between the module-owned + * load-time claim and the guest-facing mdev owner. gsp_phys_session_lock is + * the outermost backend lock: it is held for state/refcount/owner changes + * only and dropped before gsp_phys_gpu_claim()/_release(), which take + * gsp_phys_rm_lock inside. The generation counter is bumped when the session + * leaves READY so a forward that captured the previous value can re-check it + * under the rm lock and refuse instead of racing the release. + */ +static DEFINE_MUTEX(gsp_phys_session_lock); + +static struct gsp_phys_backend_session { + enum gsp_phys_backend_state state; + u32 refcount; + u32 generation; + bool module_owned; +} backend = { + .state = GSP_PHYS_BACKEND_OFF, +}; + +int gsp_phys_backend_state(void) +{ + return READ_ONCE(backend.state); +} + +u32 gsp_phys_backend_generation(void) +{ + return READ_ONCE(backend.generation); +} + +int gsp_phys_backend_start(void) +{ + bool was_claimed; + int ret; + + mutex_lock(&gsp_phys_session_lock); + if (backend.state != GSP_PHYS_BACKEND_OFF) { + backend.refcount++; + pr_info("gsp-phys: backend start: session already up (state=%d, refcount=%u)\n", + backend.state, backend.refcount); + mutex_unlock(&gsp_phys_session_lock); + return 0; + } + + /* + * A live claim can only pre-exist from the load-time module-owned + * path; join it without claiming again and keep the module as the + * owner so a later stop() drops only the reference and leaves the + * release to rmmod. + */ + was_claimed = gsp_phys_gpu_claimed(); + backend.refcount = 1; + backend.module_owned = was_claimed; + WRITE_ONCE(backend.state, GSP_PHYS_BACKEND_CLAIMING); + mutex_unlock(&gsp_phys_session_lock); + + if (!was_claimed) { + pr_info("gsp-phys: backend start: claiming the physical GPU\n"); + ret = gsp_phys_gpu_claim(); + if (ret) { + mutex_lock(&gsp_phys_session_lock); + backend.refcount = 0; + backend.module_owned = false; + WRITE_ONCE(backend.state, GSP_PHYS_BACKEND_OFF); + mutex_unlock(&gsp_phys_session_lock); + pr_err("gsp-phys: backend start: claim failed: %d (backend OFF)\n", + ret); + return ret; + } + } + + /* + * gsp_phys_gpu_claim() runs the stage-3d boot inline when the boot + * parameters are set, so READY here means the session is owned and + * live; gsp_phys_backend_rpc() additionally gates on init-done. + */ + mutex_lock(&gsp_phys_session_lock); + WRITE_ONCE(backend.state, GSP_PHYS_BACKEND_READY); + pr_info("gsp-phys: backend start: READY (module-owned=%u)\n", + backend.module_owned); + mutex_unlock(&gsp_phys_session_lock); + return 0; +} + +void gsp_phys_backend_stop(void) +{ + u32 generation; + + mutex_lock(&gsp_phys_session_lock); + if (backend.state == GSP_PHYS_BACKEND_OFF) { + mutex_unlock(&gsp_phys_session_lock); + pr_info("gsp-phys: backend stop: already OFF\n"); + return; + } + if (backend.refcount) + backend.refcount--; + if (backend.refcount) { + pr_info("gsp-phys: backend stop: refcount=%u, session stays up\n", + backend.refcount); + mutex_unlock(&gsp_phys_session_lock); + return; + } + if (backend.module_owned || + backend.state != GSP_PHYS_BACKEND_READY) { + pr_info("gsp-phys: backend stop: state=%d owner=%s, no release\n", + backend.state, + backend.module_owned ? "module" : "mdev"); + mutex_unlock(&gsp_phys_session_lock); + return; + } + + WRITE_ONCE(backend.state, GSP_PHYS_BACKEND_STOPPING); + generation = ++backend.generation; + mutex_unlock(&gsp_phys_session_lock); + pr_info("gsp-phys: backend stop: STOPPING (generation=%u)\n", generation); + + gsp_phys_gpu_release(); + + mutex_lock(&gsp_phys_session_lock); + backend.module_owned = false; + WRITE_ONCE(backend.state, GSP_PHYS_BACKEND_OFF); + mutex_unlock(&gsp_phys_session_lock); + pr_info("gsp-phys: backend stop: OFF\n"); +} diff --git a/kernel/gsp_phys_gpu.h b/kernel/gsp_phys_gpu.h index b8a1955..36532a5 100644 --- a/kernel/gsp_phys_gpu.h +++ b/kernel/gsp_phys_gpu.h @@ -12,4 +12,31 @@ int gsp_phys_gpu_claim(void); void gsp_phys_gpu_release(void); void __iomem *gsp_phys_gpu_bar0(void); +/* + * Backend session state (P0 skeleton; one physical GPU, one live claim). + * OFF until a start claims the device; READY is the only state in which live + * RPC forwarding is legal. The refcount and owner flag decide who releases: + * the load-time module-owned claim is joined, never released, by a stop(). + */ +enum gsp_phys_backend_state { + GSP_PHYS_BACKEND_OFF = 0, + GSP_PHYS_BACKEND_CLAIMING, + GSP_PHYS_BACKEND_BOOTING, + GSP_PHYS_BACKEND_READY, + GSP_PHYS_BACKEND_STOPPING, +}; + +/* + * Refcounted session start/stop around gsp_phys_gpu_claim()/_release(); safe + * (and idempotent) in any state. The generation counter is bumped when the + * session leaves READY; a forward that captured the previous value re-checks + * it under gsp_phys_rm_lock and refuses, so a stop cannot race an in-flight + * forward. The lock itself stays private to gsp_phys_gpu.c; the state and + * generation accessors are lock-free reads for use from the RPC path. + */ +int gsp_phys_backend_start(void); +void gsp_phys_backend_stop(void); +int gsp_phys_backend_state(void); +u32 gsp_phys_backend_generation(void); + #endif /* GSP_PHYS_GPU_H */ |
