From 9d15735bc6ec55a93fafe34efbaab88eeed89554 Mon Sep 17 00:00:00 2001 From: Alexander Boettcher Date: Wed, 17 Jan 2024 14:08:30 +0100 Subject: [PATCH] ahci: 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 block job scheduling on resume Issue #5101 --- repos/os/src/drivers/ahci/ahci.h | 39 +++++++++- repos/os/src/drivers/ahci/ata_protocol.h | 2 + repos/os/src/drivers/ahci/atapi_protocol.h | 5 ++ repos/os/src/drivers/ahci/main.cc | 82 +++++++++++++++++++++- 4 files changed, 125 insertions(+), 3 deletions(-) diff --git a/repos/os/src/drivers/ahci/ahci.h b/repos/os/src/drivers/ahci/ahci.h index 72987d98bc..af6ff32f04 100644 --- a/repos/os/src/drivers/ahci/ahci.h +++ b/repos/os/src/drivers/ahci/ahci.h @@ -113,6 +113,11 @@ struct Ahci::Hba void with_mmio (auto const &, auto const &) const; Hba(Resources &resource) : _resource(resource) + { + reinit(); + } + + void reinit() { with_mmio([&](Hba_mmio &mmio) { @@ -290,6 +295,27 @@ struct Ahci::Resources reinit(); } + void release_device() + { + if (_device.constructed()) + log("release AHCI device for suspend"); + + _mmio .destruct(); + _irq .destruct(); + _device.destruct(); + } + + void acquire_device() + { + _device.construct(_platform); + _irq .construct(*_device); + _mmio .construct(*_device, _mmio_index(_platform)); + + _hba.reinit(); + + reinit(); + } + void with_mmio_irq(auto const &fn, auto const &fn_error) { if (_mmio.constructed() && _irq.constructed()) @@ -679,6 +705,7 @@ struct Ahci::Protocol : Interface virtual Block::Request completed(Port_mmio &) = 0; virtual void handle_irq(Port &, Port_mmio &) = 0; virtual void writeable(bool rw) = 0; + virtual bool pending_requests() const = 0; }; @@ -838,6 +865,8 @@ struct Ahci::Port : private Port_base Region_map &rm; unsigned cmd_slots = hba.command_slots(); + bool stop_processing { }; + Platform::Dma_buffer device_dma { plat, 0x1000, CACHED }; Platform::Dma_buffer cmd_dma { plat, align_addr(cmd_slots * Command_table::size(), 12), CACHED }; @@ -868,6 +897,11 @@ struct Ahci::Port : private Port_base : Port_base(index, plat, hba, delayer), protocol(protocol), rm(rm) + { + reinit(); + } + + void reinit() { _with_port_mmio([&](Port_mmio &mmio) { reset(mmio); @@ -1190,7 +1224,10 @@ struct Ahci::Port : private Port_base request.success = true; fn(request); - }, [&](){ error("for_one_completed_request failed"); }); + }, [&](){ + if (protocol.pending_requests()) + error("for_one_completed_request failed with pending requests"); + }); } void writeable(bool rw) { protocol.writeable(rw); } diff --git a/repos/os/src/drivers/ahci/ata_protocol.h b/repos/os/src/drivers/ahci/ata_protocol.h index a037bea54e..b8abccb9d2 100644 --- a/repos/os/src/drivers/ahci/ata_protocol.h +++ b/repos/os/src/drivers/ahci/ata_protocol.h @@ -363,6 +363,8 @@ class Ata::Protocol : public Ahci::Protocol, Noncopyable return r; } + + bool pending_requests() const override { return !!_slot_states; } }; #endif /* _AHCI__ATA_PROTOCOL_H_ */ diff --git a/repos/os/src/drivers/ahci/atapi_protocol.h b/repos/os/src/drivers/ahci/atapi_protocol.h index 6aed0a1d10..d24f83e45e 100644 --- a/repos/os/src/drivers/ahci/atapi_protocol.h +++ b/repos/os/src/drivers/ahci/atapi_protocol.h @@ -188,6 +188,11 @@ class Atapi::Protocol : public Ahci::Protocol, Noncopyable return request; } + + bool pending_requests() const override + { + return _pending.operation.valid(); + } }; #endif /* _AHCI__ATAPI_PROTOCOL_H_ */ diff --git a/repos/os/src/drivers/ahci/main.cc b/repos/os/src/drivers/ahci/main.cc index 644a23d588..476726b3e1 100644 --- a/repos/os/src/drivers/ahci/main.cc +++ b/repos/os/src/drivers/ahci/main.cc @@ -62,11 +62,16 @@ class Ahci::Driver : Noncopyable Signal_handler _handler { _env.ep(), *this, &Driver::handle_irq }; Resources _resources { _env, _handler }; + Constructible _system_rom { }; + Signal_handler _system_rom_sigh { + _env.ep(), *this, &Driver::_system_update }; + Constructible _ata [MAX_PORTS]; Constructible _atapi[MAX_PORTS]; Constructible _ports[MAX_PORTS]; bool _enable_atapi; + bool _schedule_stop { }; unsigned _scan_ports(Region_map &rm, Platform::Connection &plat, Hba &hba) { @@ -111,9 +116,50 @@ class Ahci::Driver : Noncopyable return port_count; } + 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 (state == "driver_stop") { + _schedule_stop = true; + + for_each_port([&](auto &port, auto, auto) { + port.stop_processing = true; + }); + + device_release_if_stopped_and_idle(); + + return; + } + + if (state == "driver_reinit") { + _resources.acquire_device(); + + _schedule_stop = false; + + /* re-start request handling of client sessions */ + for_each_port([&](auto &port, auto const index, auto) { + port.stop_processing = false; + port.reinit(); + _dispatch.session(index); + }); + + return; + } + } + public: - Driver(Env &env, Dispatch &dispatch, bool support_atapi) + Driver(Env &env, Dispatch &dispatch, bool support_atapi, bool use_system_rom) : _env(env), _dispatch(dispatch), _enable_atapi(support_atapi) { /* search for devices */ @@ -126,6 +172,11 @@ class Ahci::Driver : Noncopyable Hex(hba.cap_np_value()), ",PI=", Hex(hba.pi_value()), ")"); }); }); + + if (use_system_rom) { + _system_rom.construct(_env, "system"); + _system_rom->sigh(_system_rom_sigh); + } } /** @@ -142,6 +193,28 @@ class Ahci::Driver : Noncopyable _dispatch.session(port); }, [&]() { error("hba handle_irq failed"); }); }); + + device_release_if_stopped_and_idle(); + } + + void device_release_if_stopped_and_idle() + { + if (!_schedule_stop) + return; + + /* check for outstanding requests */ + bool pending = false; + + for_each_port([&](auto const &port, auto, auto) { + if (port.protocol.pending_requests()) + pending = true; + }); + + /* avoid disabling device if we have outstanding requests */ + if (pending) + return; + + _resources.release_device(); } Port &port(Session_label const &label, Session_policy const &policy) @@ -270,6 +343,10 @@ struct Ahci::Block_session_component : Rpc_object, }); }); + /* all completed packets are handled, but no further processing */ + if (port.stop_processing) + break; + with_requests([&] (Block::Request request) { Response response = Response::RETRY; @@ -309,8 +386,9 @@ struct Ahci::Main : Rpc_object>, Dispatch { log("--- Starting AHCI driver ---"); bool support_atapi = config.xml().attribute_value("atapi", false); + bool use_system_rom = config.xml().attribute_value("system", false); try { - driver.construct(env, *this, support_atapi); + driver.construct(env, *this, support_atapi, use_system_rom); report_ports(); } catch (Ahci::Missing_controller) { error("no AHCI controller found");