summaryrefslogtreecommitdiff
path: root/drivers/gpu/nova-core
diff options
context:
space:
mode:
authorAlexandre Courbot <acourbot@nvidia.com>2026-06-17 22:24:43 +0900
committerAlexandre Courbot <acourbot@nvidia.com>2026-06-20 18:08:42 +0900
commitb3e079288bba7a0585a4ceac3a50a32dd712e136 (patch)
treeb084a0009cf6d579564e880d9de672d72bd715f5 /drivers/gpu/nova-core
parent44396428978789e148172d083e90e380d38fb538 (diff)
downloadlinux-next-b3e079288bba7a0585a4ceac3a50a32dd712e136.tar.gz
linux-next-b3e079288bba7a0585a4ceac3a50a32dd712e136.zip
gpu: nova-core: move GSP unload state to a pinned Gpu subobject
`Gpu` currently owns the state needed to unload the GSP directly. This means that `unload_bundle` has to be the last initialized field: once GSP boot succeeds, any later initialization failure would leave `Gpu` partially initialized, and its `PinnedDrop` implementation would not run. This prevents adding fallible `Gpu` fields that need to query the GSP after it has booted. Move the GSP state and unload bundle into a dedicated pinned `GspResources` object. Once that subobject has been initialized, its `PinnedDrop` implementation will run even if initialization of a later `Gpu` field fails, ensuring that the GSP unload sequence is executed. Reviewed-by: Eliot Courtney <ecourtney@nvidia.com> Link: https://patch.msgid.link/20260617-boot-vram-v3-1-20b9ec5fe9f2@nvidia.com Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Diffstat (limited to 'drivers/gpu/nova-core')
-rw-r--r--drivers/gpu/nova-core/gpu.rs104
1 files changed, 60 insertions, 44 deletions
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 6a9572107cf3..acee9a3ab37f 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -263,35 +263,62 @@ impl fmt::Display for Spec {
}
}
-/// Structure holding the resources required to operate the GPU.
+/// Self-contained resources to operate and drop the GSP.
#[pin_data(PinnedDrop)]
-pub(crate) struct Gpu<'gpu> {
+struct GspResources<'gpu> {
/// Device owning the GPU.
device: &'gpu device::Device<device::Bound>,
- spec: Spec,
/// MMIO mapping of PCI BAR 0.
bar: Bar0<'gpu>,
- /// System memory page required for flushing all pending GPU-side memory writes done through
- /// PCIE into system memory, via sysmembar (A GPU-initiated HW memory-barrier operation).
- sysmem_flush: SysmemFlush<'gpu>,
/// GSP falcon instance, used for GSP boot up and cleanup.
gsp_falcon: Falcon<GspFalcon>,
/// SEC2 falcon instance, used for GSP boot up and cleanup.
sec2_falcon: Falcon<Sec2Falcon>,
- /// GSP runtime data. Temporarily an empty placeholder.
+ /// GSP runtime data.
#[pin]
gsp: Gsp,
/// GSP unload firmware bundle, if any.
unload_bundle: Option<gsp::UnloadBundle>,
}
+/// Structure holding the resources required to operate the GPU.
+#[pin_data]
+pub(crate) struct Gpu<'gpu> {
+ spec: Spec,
+ /// GSP and its resources.
+ #[pin]
+ gsp_resources: GspResources<'gpu>,
+ /// System memory page required for flushing all pending GPU-side memory writes done through
+ /// PCIE into system memory, via sysmembar (A GPU-initiated HW memory-barrier operation).
+ ///
+ /// Must be kept declared *after* `gsp_resources`, as the latter's `PinnedDrop` implementation
+ /// requires the sysmem flush page to be in place.
+ sysmem_flush: SysmemFlush<'gpu>,
+}
+
+#[pinned_drop]
+impl PinnedDrop for GspResources<'_> {
+ fn drop(self: Pin<&mut Self>) {
+ let this = self.project();
+ let device = *this.device;
+ let bar = *this.bar;
+ let bundle = this.unload_bundle.take();
+
+ let _ = this
+ .gsp
+ .as_ref()
+ .get_ref()
+ .unload(device, bar, &*this.gsp_falcon, &*this.sec2_falcon, bundle)
+ .inspect_err(|e| dev_err!(device, "failed to unload GSP: {:?}\n", e));
+ }
+}
+
impl<'gpu> Gpu<'gpu> {
pub(crate) fn new(
pdev: &'gpu pci::Device<device::Core<'_>>,
bar: Bar0<'gpu>,
) -> impl PinInit<Self, Error> + 'gpu {
try_pin_init!(Self {
- device: pdev.as_ref(),
spec: Spec::new(pdev.as_ref(), bar).inspect(|spec| {
dev_info!(pdev,"NVIDIA ({})\n", spec);
})?,
@@ -309,46 +336,35 @@ impl<'gpu> Gpu<'gpu> {
.inspect_err(|_| dev_err!(pdev, "GFW boot did not complete\n"))?;
},
+ // Initialize this early because `gsp_resources` depends on it.
sysmem_flush: SysmemFlush::register(pdev.as_ref(), bar, spec.chipset)?,
- gsp_falcon: Falcon::new(
- pdev.as_ref(),
- spec.chipset,
- )
- .inspect(|falcon| falcon.clear_swgen0_intr(bar))?,
-
- sec2_falcon: Falcon::new(pdev.as_ref(), spec.chipset)?,
+ gsp_resources <- try_pin_init!(GspResources {
+ device: pdev.as_ref(),
- gsp <- Gsp::new(pdev),
-
- // This member must be initialized last, so the `UnloadBundle` can never be dropped from
- // outside of the constructed `Gpu`, ensuring that the unload sequence is properly run
- // in case of failure.
- unload_bundle: gsp.boot(GspBootContext {
- pdev,
bar,
- chipset: spec.chipset,
- gsp_falcon,
- sec2_falcon,
- })?,
- bar,
- })
- }
-}
-
-#[pinned_drop]
-impl PinnedDrop for Gpu<'_> {
- fn drop(self: Pin<&mut Self>) {
- let this = self.project();
- let device = *this.device;
- let bar = *this.bar;
- let bundle = this.unload_bundle.take();
- let _ = this
- .gsp
- .as_ref()
- .get_ref()
- .unload(device, bar, &*this.gsp_falcon, &*this.sec2_falcon, bundle)
- .inspect_err(|e| dev_err!(device, "failed to unload GSP: {:?}\n", e));
+ gsp_falcon: Falcon::new(
+ pdev.as_ref(),
+ spec.chipset,
+ )
+ .inspect(|falcon| falcon.clear_swgen0_intr(bar))?,
+
+ sec2_falcon: Falcon::new(pdev.as_ref(), spec.chipset)?,
+
+ gsp <- Gsp::new(pdev),
+
+ // This member must be initialized last, so the `UnloadBundle` can never be dropped
+ // from outside of the constructed `GspResources`, ensuring that the unload sequence
+ // is properly run in case of failure.
+ unload_bundle: gsp.boot(GspBootContext {
+ pdev,
+ bar,
+ chipset: spec.chipset,
+ gsp_falcon,
+ sec2_falcon,
+ })?,
+ }),
+ })
}
}