summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--kernel/gsp_phys_booter.c133
-rw-r--r--kernel/gsp_phys_booter.h54
-rw-r--r--kernel/gsp_phys_gpu.c127
-rw-r--r--kernel/gsp_phys_gpu.h27
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 */