diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-01 22:22:21 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-01 22:22:21 -0400 |
| commit | 2cecb0c172375e45ab554a42c0693135238c03ed (patch) | |
| tree | 393179d97eeb617e49b2da02aa284a774d09383f | |
| parent | aa4cf303dd028b24ca29edbc0a5f3c237ab98e3f (diff) | |
| download | open-iov-2cecb0c172375e45ab554a42c0693135238c03ed.tar.gz open-iov-2cecb0c172375e45ab554a42c0693135238c03ed.zip | |
gsp-phys: mdev-owned backend lifecycle (P1)
Add the gsp_phys_mdev_owned ownership selector (default 0: unchanged
load-time module-owned claim/boot). With 1, module load claims nothing;
the first mdev create starts a refcounted backend session whose
claim+boot runs asynchronously from a work item, so mdev create and
init_work never block. The last mdev remove releases after the emulation
thread is stopped. Failures are fail-open: the backend stays OFF and the
mdev keeps serving the emulation path.
nv_vgpu_exit now gates the session STOPPING under the session lock,
drains the emulation threads via mdev_unregister_driver(), and only then
releases the claim; the module-owned path keeps its previous release
position. A start while STOPPING is refused and a concurrent second
stopper cannot release twice. No forwarding is added.
| -rw-r--r-- | kernel/gsp_phys_gpu.c | 257 | ||||
| -rw-r--r-- | kernel/gsp_phys_gpu.h | 40 | ||||
| -rw-r--r-- | kernel/include/nvidia-vgpu-vfio.h | 6 | ||||
| -rw-r--r-- | kernel/main.c | 75 |
4 files changed, 315 insertions, 63 deletions
diff --git a/kernel/gsp_phys_gpu.c b/kernel/gsp_phys_gpu.c index 22cdc33..1000eaf 100644 --- a/kernel/gsp_phys_gpu.c +++ b/kernel/gsp_phys_gpu.c @@ -59,6 +59,7 @@ #include <linux/crc32.h> #include <linux/firmware.h> #include <linux/string.h> +#include <linux/workqueue.h> #include "gsp_phys_gpu.h" #include "gsp_phys_boot.h" @@ -97,6 +98,21 @@ module_param_named(gsp_phys_hazard_ack, gsp_phys_hazard_ack, bool, 0644); MODULE_PARM_DESC(gsp_phys_hazard_ack, "Acknowledge the host-freeze hazard of unbinding the distribution driver; required in addition to gsp_phys_gpu=1 (default 0)"); +/* + * Ownership selector (P1). Default 0 keeps the supervised module-owned + * behavior: gsp_phys_gpu=1 claims and boots at module load and releases at + * rmmod. With 1, module load claims nothing; the first mdev create starts the + * refcounted backend session (claim+boot asynchronously, so mdev create never + * blocks) and the last mdev remove stops and releases it after the emulation + * thread is stopped. Requires gsp_phys_gpu=1 and gsp_phys_hazard_ack=1; every + * failure is fail-open - the backend stays OFF and the mdev keeps working on + * the existing emulation path. + */ +static bool gsp_phys_mdev_owned_param; +module_param_named(gsp_phys_mdev_owned, gsp_phys_mdev_owned_param, bool, 0644); +MODULE_PARM_DESC(gsp_phys_mdev_owned, + "Let the first mdev create claim/boot the physical GPU and the last remove release it; requires gsp_phys_gpu=1 + gsp_phys_hazard_ack=1 (default 0)"); + /* BAR0 registers sampled after claim as proof of live access. */ #define GSP_PHYS_BOOT_0 0x00000000 #define GSP_PHYS_BOOT_1 0x00000004 @@ -1004,14 +1020,15 @@ void gsp_phys_gpu_release(void) } /* - * 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. + * Backend session state (P0 skeleton + P1 lifecycle). 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 (lock + * order: session lock outermost, rm lock inner; no guest lock is ever held + * across either). 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); @@ -1020,10 +1037,20 @@ static struct gsp_phys_backend_session { u32 refcount; u32 generation; bool module_owned; + bool releasing; } backend = { .state = GSP_PHYS_BACKEND_OFF, }; +static void gsp_phys_backend_start_work(struct work_struct *work); +static DECLARE_WORK(gsp_phys_backend_start_work_item, + gsp_phys_backend_start_work); + +bool gsp_phys_mdev_owned_enabled(void) +{ + return gsp_phys_mdev_owned_param; +} + int gsp_phys_backend_state(void) { return READ_ONCE(backend.state); @@ -1034,63 +1061,166 @@ u32 gsp_phys_backend_generation(void) return READ_ONCE(backend.generation); } +/* + * Claim the release of the session under the session lock. Returns true only + * for the caller that must run the sleeping release sequence. A module-owned + * claim is never released; a concurrent second stopper sees releasing=true + * and returns without touching the device. + */ +static bool gsp_phys_backend_claim_release_locked(const char *who) +{ + if (backend.state == GSP_PHYS_BACKEND_OFF) + return false; + if (backend.module_owned) { + pr_info("gsp-phys: backend %s: module-owned claim stays up (released at rmmod)\n", + who); + return false; + } + if (backend.releasing) { + pr_info("gsp-phys: backend %s: release already in progress\n", + who); + return false; + } + if (backend.state != GSP_PHYS_BACKEND_STOPPING) { + WRITE_ONCE(backend.state, GSP_PHYS_BACKEND_STOPPING); + backend.generation++; + pr_info("gsp-phys: backend %s: STOPPING (generation=%u, refcount=%u)\n", + who, backend.generation, backend.refcount); + } + backend.releasing = true; + return true; +} + +/* + * The sleeping half of a release: wait for an in-flight claim/boot worker to + * finish (cancel_work_sync is a no-op when it never ran or already finished), + * then release the claim and mark the session OFF. A worker that observes + * STOPPING after its claim leaves the claim in place for this function to + * release, so the device is never claimed without an owner. + */ +static void gsp_phys_backend_release_run(void) +{ + cancel_work_sync(&gsp_phys_backend_start_work_item); + + if (gsp_phys_gpu_claimed()) + gsp_phys_gpu_release(); + + mutex_lock(&gsp_phys_session_lock); + backend.refcount = 0; + backend.module_owned = false; + backend.releasing = false; + WRITE_ONCE(backend.state, GSP_PHYS_BACKEND_OFF); + mutex_unlock(&gsp_phys_session_lock); + pr_info("gsp-phys: backend release: OFF (owner=mdev)\n"); +} + int gsp_phys_backend_start(void) { bool was_claimed; - int ret; mutex_lock(&gsp_phys_session_lock); + + if (backend.state == GSP_PHYS_BACKEND_STOPPING) { + mutex_unlock(&gsp_phys_session_lock); + pr_info("gsp-phys: backend start refused: session is STOPPING\n"); + return -EAGAIN; + } + 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); + pr_info("gsp-phys: backend start: joining live session (state=%d, refcount=%u, owner=%s)\n", + backend.state, backend.refcount, + backend.module_owned ? "module" : "mdev"); 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. + * Gate: an mdev-owned start requires gsp_phys_gpu=1; the claim itself + * additionally requires gsp_phys_hazard_ack=1 (gsp_phys_gpu_claim() + * refuses -EPERM without it). A pre-existing live claim is the + * load-time module-owned path and is joined, not claimed again. */ was_claimed = gsp_phys_gpu_claimed(); + if (!was_claimed && !gsp_phys_gpu_enabled()) { + mutex_unlock(&gsp_phys_session_lock); + pr_info("gsp-phys: backend start refused: gsp_phys_gpu=0, no claim requested (guest stays on emulation)\n"); + return -EOPNOTSUPP; + } + backend.refcount = 1; backend.module_owned = was_claimed; + + if (was_claimed) { + WRITE_ONCE(backend.state, GSP_PHYS_BACKEND_READY); + pr_info("gsp-phys: backend start: joined the live module-owned claim (READY)\n"); + mutex_unlock(&gsp_phys_session_lock); + return 0; + } + WRITE_ONCE(backend.state, GSP_PHYS_BACKEND_CLAIMING); + pr_info("gsp-phys: backend start: mdev-owned claim+boot scheduled (refcount=1)\n"); + schedule_work(&gsp_phys_backend_start_work_item); mutex_unlock(&gsp_phys_session_lock); + return 0; +} - 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; - } - } +/* + * Asynchronous claim+boot worker (mdev-owned). The full claim lifecycle - + * including the stage-3c/3d/3e boot when gsp_phys_boot=1 - runs here so mdev + * create and init_work never block on the 60 s init-done poll. A failure + * leaves the backend OFF: the mdev keeps working on the existing emulation + * path. + */ +static void gsp_phys_backend_start_work(struct work_struct *work) +{ + int 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. + * Re-check under the session lock: a stop that set STOPPING after + * schedule_work() but before this worker ran (cancel_work_sync loses + * the race when the worker is already running) must not trigger a + * pointless claim. The window is only narrowed, not closed - a stop + * arriving between this check and the claim is caught by the STOPPING + * re-check after the claim and released by the stopper. */ mutex_lock(&gsp_phys_session_lock); + if (backend.state != GSP_PHYS_BACKEND_CLAIMING) { + mutex_unlock(&gsp_phys_session_lock); + pr_info("gsp-phys: backend start: worker aborted, session state=%d\n", + backend.state); + return; + } + mutex_unlock(&gsp_phys_session_lock); + + pr_info("gsp-phys: backend start: claiming + booting the physical GPU (mdev-owned)\n"); + + ret = gsp_phys_gpu_claim(); + + mutex_lock(&gsp_phys_session_lock); + if (ret) { + 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/boot failed: %d; backend OFF, guest stays on emulation\n", + ret); + return; + } + if (backend.state == GSP_PHYS_BACKEND_STOPPING) { + mutex_unlock(&gsp_phys_session_lock); + pr_info("gsp-phys: backend start: claim completed while STOPPING; release deferred to the stopper\n"); + return; + } WRITE_ONCE(backend.state, GSP_PHYS_BACKEND_READY); - pr_info("gsp-phys: backend start: READY (module-owned=%u)\n", - backend.module_owned); + pr_info("gsp-phys: backend start: READY (owner=mdev, refcount=%u)\n", + backend.refcount); mutex_unlock(&gsp_phys_session_lock); - return 0; } void gsp_phys_backend_stop(void) { - u32 generation; + bool release; mutex_lock(&gsp_phys_session_lock); if (backend.state == GSP_PHYS_BACKEND_OFF) { @@ -1101,30 +1231,55 @@ void gsp_phys_backend_stop(void) if (backend.refcount) backend.refcount--; if (backend.refcount) { - pr_info("gsp-phys: backend stop: refcount=%u, session stays up\n", - backend.refcount); + pr_info("gsp-phys: backend stop: refcount=%u, session stays up (owner=%s)\n", + backend.refcount, + backend.module_owned ? "module" : "mdev"); 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"); + release = gsp_phys_backend_claim_release_locked("stop"); + mutex_unlock(&gsp_phys_session_lock); + + if (release) + gsp_phys_backend_release_run(); +} + +/* + * Two-phase stop for the module-exit path: begin_stop() gates the session + * (STOPPING + generation bump) while the caller drains the emulation threads, + * then end_stop() runs the release. Together they keep the section 1.4 + * ordering "STOPPING -> drain -> release" without holding the session lock + * across the drain or the release. + */ +void gsp_phys_backend_begin_stop(void) +{ + mutex_lock(&gsp_phys_session_lock); + if (backend.state == GSP_PHYS_BACKEND_OFF) { mutex_unlock(&gsp_phys_session_lock); return; } - - WRITE_ONCE(backend.state, GSP_PHYS_BACKEND_STOPPING); - generation = ++backend.generation; + if (backend.module_owned) { + mutex_unlock(&gsp_phys_session_lock); + pr_info("gsp-phys: backend begin stop: module-owned claim; no state change\n"); + return; + } + if (backend.state != GSP_PHYS_BACKEND_STOPPING) { + WRITE_ONCE(backend.state, GSP_PHYS_BACKEND_STOPPING); + backend.generation++; + pr_info("gsp-phys: backend begin stop: STOPPING (generation=%u, refcount=%u)\n", + backend.generation, backend.refcount); + } mutex_unlock(&gsp_phys_session_lock); - pr_info("gsp-phys: backend stop: STOPPING (generation=%u)\n", generation); +} - gsp_phys_gpu_release(); +void gsp_phys_backend_end_stop(void) +{ + bool release; mutex_lock(&gsp_phys_session_lock); - backend.module_owned = false; - WRITE_ONCE(backend.state, GSP_PHYS_BACKEND_OFF); + release = gsp_phys_backend_claim_release_locked("end stop"); mutex_unlock(&gsp_phys_session_lock); - pr_info("gsp-phys: backend stop: OFF\n"); + + if (release) + gsp_phys_backend_release_run(); } diff --git a/kernel/gsp_phys_gpu.h b/kernel/gsp_phys_gpu.h index 36532a5..c8beb1d 100644 --- a/kernel/gsp_phys_gpu.h +++ b/kernel/gsp_phys_gpu.h @@ -13,10 +13,17 @@ 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(). + * Read by the backend session to enforce the mdev-owned start gate; the + * gsp_phys_gpu module parameter itself is owned by main.c. + */ +bool gsp_phys_gpu_enabled(void); + +/* + * Backend session state (P0 skeleton + P1 lifecycle; 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(); an mdev-owned claim is released by the last stop(). */ enum gsp_phys_backend_state { GSP_PHYS_BACKEND_OFF = 0, @@ -28,15 +35,30 @@ enum gsp_phys_backend_state { /* * 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. + * (and idempotent) in any state. gsp_phys_backend_start() never blocks on the + * claim/boot: an mdev-owned start schedules a work item and returns, and a + * failed claim leaves the session OFF (fail-open: the caller keeps using the + * emulation path). 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. + * + * Module exit uses the two-phase form so the ยง1.4 ordering can be kept: + * begin_stop() gates the session (STOPPING + generation bump) before the + * caller drains the emulation threads, then end_stop() runs the release. */ int gsp_phys_backend_start(void); void gsp_phys_backend_stop(void); +void gsp_phys_backend_begin_stop(void); +void gsp_phys_backend_end_stop(void); int gsp_phys_backend_state(void); u32 gsp_phys_backend_generation(void); +/* + * True when the gsp_phys_mdev_owned module parameter is set (default 0: + * load-time module-owned claim/boot, today's behavior). + */ +bool gsp_phys_mdev_owned_enabled(void); + #endif /* GSP_PHYS_GPU_H */ diff --git a/kernel/include/nvidia-vgpu-vfio.h b/kernel/include/nvidia-vgpu-vfio.h index 961bba2..aed3dc5 100644 --- a/kernel/include/nvidia-vgpu-vfio.h +++ b/kernel/include/nvidia-vgpu-vfio.h @@ -135,6 +135,12 @@ struct nv_vgpu_device { /* VFIO device registration state */ bool vfio_dev_registered; + + /* True when this mdev holds a reference on the physical backend + * session (gsp_phys_backend_start() succeeded in init_work); the + * matching gsp_phys_backend_stop() runs in cleanup_work after the + * emulation thread is stopped. */ + bool phys_backend_ref; }; typedef struct nv_vgpu_device nv_vgpu_device; diff --git a/kernel/main.c b/kernel/main.c index babcc22..f47ef69 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -553,6 +553,9 @@ static int nv_vgpu_mdev_create(struct mdev_device *mdev) /* Initialize VFIO device registration state */ vgpu->vfio_dev_registered = false; + /* No physical backend reference until init_work starts one */ + vgpu->phys_backend_ref = false; + /* Initialize VFIO device open/close tracking */ atomic_set(&vgpu->vfio_dev_open_count, 0); init_waitqueue_head(&vgpu->vfio_dev_close_wait); @@ -1447,11 +1450,15 @@ static int __init nv_vgpu_init(void) } pr_info("gsp-resman-vfio: VFIO ops set\n"); - if (gsp_phys_gpu_param) { + if (gsp_phys_gpu_param && !gsp_phys_mdev_owned_enabled()) { ret = gsp_phys_gpu_claim(); if (ret) pr_warn("gsp-resman-vfio: physical GPU claim failed: %d\n", ret); + } else if (gsp_phys_gpu_param) { + pr_info("gsp-resman-vfio: gsp_phys_mdev_owned=1: physical GPU claim+boot deferred to the first mdev create\n"); + } else if (gsp_phys_mdev_owned_enabled()) { + pr_warn("gsp-resman-vfio: gsp_phys_mdev_owned=1 requires gsp_phys_gpu=1; physical backend stays off\n"); } /* Test-enabling trigger for the GSP-RM unload RPC plus the read-only @@ -1487,6 +1494,7 @@ err_class: static void __exit nv_vgpu_exit(void) { struct pci_dev *pdev = NULL; + bool mdev_owned = gsp_phys_mdev_owned_enabled(); pr_info("gsp-resman-vfio: nv_vgpu_exit: starting cleanup\n"); @@ -1497,7 +1505,25 @@ static void __exit nv_vgpu_exit(void) g_vgpu_vfio_ops = NULL; gsp_phys_boot_debugfs_fini(); - gsp_phys_gpu_release(); + + if (mdev_owned) { + /* + * Mdev-owned session: gate every live forward first (STOPPING + * under the session lock, generation bump), drain the + * emulation threads with the mdev unregistration below, and + * only then release the claim - release must not race a live + * forward. The mdev remove path releases when live mdevs + * exist; end_stop() below covers the no-mdev case. + */ + gsp_phys_backend_begin_stop(); + } else { + /* + * Module-owned claim (gsp_phys_gpu=1, mdev_owned=0): keep + * today's position relative to the debugfs teardown and the + * emulation threads; module-owned sessions never forward live. + */ + gsp_phys_gpu_release(); + } if (nv_vgpu_driver.device_count > 0) { struct nv_vgpu_device *vgpu; @@ -1526,10 +1552,17 @@ static void __exit nv_vgpu_exit(void) pr_info("gsp-resman-vfio: nv_vgpu_exit: sema service stopped\n"); pr_info("gsp-resman-vfio: nv_vgpu_exit: unregistering mdev driver\n"); - /* Unregister mdev driver FIRST, then mdev parent */ + /* Unregister mdev driver FIRST, then mdev parent. This drains the + * mdev remove callbacks, hence the emulation threads. */ mdev_unregister_driver(&nv_vgpu_mdev_driver); pr_info("gsp-resman-vfio: nv_vgpu_exit: mdev driver unregistered\n"); + if (mdev_owned) { + /* Drain complete: release the mdev-owned claim (no-op when + * the mdev remove path already released it). */ + gsp_phys_backend_end_stop(); + } + /* Unregister mdev parent via nvidia.ko */ if (pdev) { pr_info("gsp-resman-vfio: nv_vgpu_exit: unregistering mdev parent\n"); @@ -1592,6 +1625,23 @@ static void nv_vgpu_device_init_work(struct work_struct *work) nv_vgpu_warn(vgpu, "Migration init failed: %d", ret); } + /* + * mdev-owned physical backend: the first mdev create (refcount 0 -> 1) + * claims and boots the real GPU asynchronously (gsp_phys_backend_start() + * only schedules the claim/boot work item, so this never blocks). Any + * failure is fail-open: log it, leave the backend OFF and keep serving + * the guest from the emulation path. + */ + if (gsp_phys_mdev_owned_enabled()) { + ret = gsp_phys_backend_start(); + if (ret) { + nv_vgpu_warn(vgpu, "Physical backend start refused/failed: %d; continuing on the emulation path", + ret); + } else { + vgpu->phys_backend_ref = true; + } + } + mutex_lock(&vgpu->lock); vgpu->state = NV_VGPU_STATE_READY; mutex_unlock(&vgpu->lock); @@ -1617,6 +1667,18 @@ static void nv_vgpu_device_cleanup_work(struct work_struct *work) nv_vgpu_migration_cleanup(vgpu); gsp_emu_cleanup(vgpu); + + /* + * Last-reference backend teardown: gsp_emu_cleanup() above has stopped + * this mdev's emulation thread, so no forward can be in flight when the + * claim is released. The backend session stays up until the last mdev + * removes (refcount 1 -> 0). + */ + if (vgpu->phys_backend_ref) { + vgpu->phys_backend_ref = false; + gsp_phys_backend_stop(); + } + nv_vgpu_memory_cleanup(vgpu); nv_vgpu_irq_cleanup(vgpu); nv_vgpu_bar_cleanup(vgpu); @@ -2457,6 +2519,13 @@ MODULE_PARM_DESC(chan_dump, "Dump the sema service channel table (any value)"); module_param_named(gsp_phys_gpu, gsp_phys_gpu_param, bool, 0644); MODULE_PARM_DESC(gsp_phys_gpu, "Claim the physical GPU for the real-GSP backend (default 0)"); +/* Read by the backend session (gsp_phys_gpu.c) to enforce the mdev-owned + * start gate; the parameter itself stays owned here. */ +bool gsp_phys_gpu_enabled(void) +{ + return gsp_phys_gpu_param; +} + /* * GR ring discovery: pin the compute channels' rings/USERDs, which follow the * CE rings in the channel video-memory allocation. Default on; set 0 to leave |
