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>
This commit is contained in:
Alexandre Courbot 2026-06-17 22:24:43 +09:00
parent 4439642897
commit b3e079288b

View File

@ -263,35 +263,62 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
}
}
/// 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 @@ pub(crate) fn new(
.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))?,
gsp_resources <- try_pin_init!(GspResources {
device: pdev.as_ref(),
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 `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,
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,
})?,
}),
})
}
}
#[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));
}
}