gpu/intel: release device before suspend

- monitor system ROM changes
- stop processing of new Jobs before suspend
- destruct platform device before suspend, but keep platform DMA buffers
- re-construct platform device and reinit resources (mmio, irq) on resume
- re-start GPU job scheduling on resume

Fixes #5081
This commit is contained in:
Alexander Boettcher 2023-12-08 16:25:33 +01:00 committed by Christian Helmuth
parent 4ce4d4120a
commit 0684101ff0
2 changed files with 160 additions and 15 deletions

View File

@ -606,6 +606,7 @@ struct Igd::Device
Engine<Rcs_context> rcs;
uint32_t active_fences { 0 };
uint64_t _current_seqno { 0 };
Gpu::addr_t _delayed_execute { 0 };
Genode::Attached_ram_dataspace _info_dataspace;
Gpu::Info_intel &_info {
@ -673,6 +674,11 @@ struct Igd::Device
_info.last_completed = Gpu::Sequence_number { .value = current_seqno() };
}
void delay_execute(Gpu::addr_t const vram_addr)
{
_delayed_execute = vram_addr;
}
bool setup_ring_vram(Gpu::addr_t const vram_addr)
{
bool ok = false;
@ -969,16 +975,14 @@ struct Igd::Device
** SCHEDULING **
****************/
Genode::Fifo<Vgpu> _vgpu_list { };
Vgpu *_active_vgpu { nullptr };
Genode::Fifo<Vgpu> _vgpu_list { };
Genode::Fifo<Vgpu> _vgpu_delay { };
Vgpu *_active_vgpu { };
bool _schedule_stop { };
void _submit_execlist(Igd::Mmio &mmio, Engine<Rcs_context> &engine)
bool exec_list_empty(Igd::Mmio &mmio) const
{
Execlist &el = engine.execlist;
int const port = mmio.read<Igd::Mmio::EXECLIST_STATUS_RSCUNIT::Execlist_write_pointer>();
/*
* Exec list might still be executing, check multiple times, in the normal
* case first iteration should succeed. If still running loop should end
@ -994,12 +998,21 @@ struct Igd::Device
}
}
return empty;
}
void _submit_execlist(Igd::Mmio &mmio, Engine<Rcs_context> &engine)
{
Execlist &el = engine.execlist;
int const port = mmio.read<Igd::Mmio::EXECLIST_STATUS_RSCUNIT::Execlist_write_pointer>();
/*
* In case exec list is still running write list anyway, it will preempt
* something in the worst case. Nonetheless if you see the warning below,
* something is not right and should be investigated.
*/
if (!empty)
if (!exec_list_empty(mmio))
warning("exec list is not empty");
el.schedule(port);
@ -1188,6 +1201,50 @@ struct Igd::Device
Genode::Signal_handler<Device> _watchdog_timeout_sigh {
_env.ep(), *this, &Device::_handle_watchdog_timeout };
void handle_system_update(String<32> const & state)
{
if (state == "driver_stop") {
_schedule_stop = true;
device_release_if_stopped_and_idle();
return;
}
if (state == "driver_reinit") {
_resources.acquire_device();
if (!_resources.with_mmio([&](auto &mmio) {
mmio.generation(_info.generation);
reinit(mmio);
_schedule_stop = false;
/* resume if there is a current vGPU */
if (_current_vgpu())
_schedule_current_vgpu(mmio);
/* re-add delayed execute RPCs() of vGPUs to ready list */
_vgpu_delay.dequeue_all([&](auto &vgpu) {
if (vgpu.setup_ring_vram(vgpu._delayed_execute)) {
vgpu_activate(vgpu, mmio);
} else
warning("setup_ring_vram failed");
});
}))
error("reinit - failed");
return;
}
}
void device_release_if_stopped_and_idle()
{
if (!_schedule_stop || _active_vgpu)
return;
_resources.release_device();
}
void _device_reset_and_init(Igd::Mmio &mmio)
{
mmio.reset();
@ -1526,6 +1583,9 @@ struct Igd::Device
_vgpu_list.enqueue(vgpu);
if (_schedule_stop)
return;
if (pending) { return; }
/* none pending, kick-off execution */
@ -1690,7 +1750,7 @@ struct Igd::Device
}
/* keep the ball rolling... */
if (_current_vgpu()) {
if (!_schedule_stop && _current_vgpu()) {
_schedule_current_vgpu(mmio);
}
}
@ -2033,7 +2093,11 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
Gpu::Sequence_number execute(Vram_id id, off_t offset) override
{
bool found = false;
bool found = false;
bool ppgtt_va_valid = false;
Gpu::addr_t ppgtt_va = 0;
bool const dev_offline = !_device._resources.with_mmio([](auto &){});
_apply_vram_local(id, [&] (Vram_local &vram_local) {
@ -2042,9 +2106,7 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
return;
}
addr_t ppgtt_va { 0 };
bool ppgtt_va_valid = vram_local.mappings.with_element(offset,
ppgtt_va_valid = vram_local.mappings.with_element(offset,
[&] (Vram_local::Mapping const &mapping) {
ppgtt_va = mapping.ppgtt_va; return true; },
[]() { return false; });
@ -2055,9 +2117,25 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
throw Gpu::Session::Invalid_state();
}
/* setup ring vram would require MMIO which is not avail */
if (dev_offline)
return;
found = _vgpu.setup_ring_vram(ppgtt_va);
});
if (dev_offline && ppgtt_va_valid) {
/* add vGPU to the delayed list, resumed after dev is re-acquired */
if (!_vgpu.enqueued())
_device._vgpu_delay.enqueue(_vgpu);
/* remember current execute in vGPU */
_vgpu.delay_execute(ppgtt_va);
/* return seqno which will be used after device is re-aquired */
return { .value = _vgpu.current_seqno() + 1 };
}
if (!found)
throw Gpu::Session::Invalid_state();
@ -2410,10 +2488,14 @@ struct Main : Irq_ack_handler, Gpu_reset_handler
Signal_handler<Main> _config_sigh { _env.ep(), *this,
&Main::_handle_config_update };
Platform::Resources _dev { _env, _rm, _irq_dispatcher };
Signal_handler<Main> _system_sigh { _env.ep(), *this,
&Main::_system_update };
Platform::Root _platform_root { _env, _md_alloc, _dev, *this, *this };
Genode::Constructible<Igd::Device> _igd_device { };
Constructible<Igd::Device> _igd_device { };
Constructible<Attached_rom_dataspace> _system_rom { };
Main(Genode::Env &env) : _env(env)
{
@ -2467,12 +2549,49 @@ struct Main : Irq_ack_handler, Gpu_reset_handler
if (!_config_rom.valid()) { return; }
bool const use_system_rom = _config_rom.xml().attribute_value("system", false);
if (use_system_rom) {
_system_rom.construct(_env, "system");
_system_rom->sigh(_system_sigh);
_system_update();
} else
_system_rom.destruct();
if (_igd_device.constructed()) {
Genode::log("gpu device already initialized - ignore");
return;
}
_create_device();
}
void _system_update()
{
if (!_system_rom.constructed())
return;
_system_rom->update();
if (!_system_rom->valid())
return;
auto state = _system_rom->xml().attribute_value("state", String<32>(""));
if (_igd_device.constructed()) {
_igd_device->handle_system_update(state);
} else {
if (state == "driver_stop") {
_dev.release_device();
return;
}
if (state == "driver_reinit") {
_dev.acquire_device();
return;
}
}
}
void handle_irq()
@ -2511,6 +2630,9 @@ struct Main : Irq_ack_handler, Gpu_reset_handler
if (!_dev.with_irq([&](auto &irq) { irq.ack(); }))
error(__func__, " with_irq failed");
if (_igd_device.constructed())
_igd_device->device_release_if_stopped_and_idle();
}
void reset() override

View File

@ -418,6 +418,29 @@ class Platform::Resources : Noncopyable
}
void with_platform(auto const &fn) { fn(_platform); }
void acquire_device()
{
_device.construct(_platform);
_irq.construct(*_device);
_irq->sigh(_irq_cap);
_mmio.construct(*_device, _env);
_gmadr.construct(*_device, Platform::Device::Mmio<0>::Index(1));
_gmadr_mem.construct(_env.rm(), _gmadr->cap());
_reinit();
}
void release_device()
{
_gmadr_mem.destruct();
_gmadr.destruct();
_mmio.destruct();
_irq.destruct();
_device.destruct();
}
};