From a6a923c31bebc2940418a6ea58b35a4ca27c45c0 Mon Sep 17 00:00:00 2001
From: Norman Feske <norman.feske@genode-labs.com>
Date: Mon, 11 Jan 2021 16:53:21 +0100
Subject: [PATCH] driver manager: restart intel_fb when frozen

This patch introduces heartbeat monitoring and automated restarting
of the intel framebuffer driver.
---
 .../raw/drivers_managed-pc/drivers.config     | 49 ++++++-----
 repos/gems/src/app/driver_manager/main.cc     | 86 +++++++++++++++----
 2 files changed, 97 insertions(+), 38 deletions(-)

diff --git a/repos/gems/recipes/raw/drivers_managed-pc/drivers.config b/repos/gems/recipes/raw/drivers_managed-pc/drivers.config
index 772dfee9c4..14c68b4e76 100644
--- a/repos/gems/recipes/raw/drivers_managed-pc/drivers.config
+++ b/repos/gems/recipes/raw/drivers_managed-pc/drivers.config
@@ -39,17 +39,19 @@
 		<resource name="RAM" quantum="2M"/>
 		<provides> <service name="Report"/> <service name="ROM"/> </provides>
 		<config verbose="no">
-			<policy label="platform_drv -> acpi"          report="acpi_drv -> acpi"/>
-			<policy label="driver_manager -> pci_devices" report="platform_drv -> pci"/>
-			<policy label="usb_drv -> config"             report="driver_manager -> usb_drv.config"/>
-			<policy label="driver_manager -> usb_devices" report="usb_drv -> devices"/>
-			<policy label="dynamic -> config"             report="driver_manager -> init.config"/>
-			<policy label="driver_manager -> ahci_ports"  report="dynamic -> ahci_ports"/>
-			<policy label="driver_manager -> nvme_ns"     report="dynamic -> nvme_ns"/>
-			<policy label="rom_reporter -> acpi"          report="acpi_drv -> acpi"/>
-			<policy label="rom_reporter -> pci_devices"   report="platform_drv -> pci"/>
-			<policy label="rom_reporter -> usb_devices"   report="usb_drv -> devices"/>
-			<policy label="usb_hid_drv -> report"         report="usb_drv -> devices"/>
+			<policy label="platform_drv -> acpi"            report="acpi_drv -> acpi"/>
+			<policy label="driver_manager -> pci_devices"   report="platform_drv -> pci"/>
+			<policy label="usb_drv -> config"               report="driver_manager -> usb_drv.config"/>
+			<policy label="driver_manager -> usb_devices"   report="usb_drv -> devices"/>
+			<policy label="driver_manager -> dynamic_state" report="dynamic -> state"/>
+			<policy label="dynamic -> config"               report="driver_manager -> init.config"/>
+			<policy label="driver_manager -> ahci_ports"    report="dynamic -> ahci_ports"/>
+			<policy label="driver_manager -> nvme_ns"       report="dynamic -> nvme_ns"/>
+			<policy label="rom_reporter -> acpi"            report="acpi_drv -> acpi"/>
+			<policy label="rom_reporter -> pci_devices"     report="platform_drv -> pci"/>
+			<policy label="rom_reporter -> usb_devices"     report="usb_drv -> devices"/>
+			<policy label="rom_reporter -> dynamic_state"   report="dynamic -> state"/>
+			<policy label="usb_hid_drv -> report"           report="usb_drv -> devices"/>
 		</config>
 	</start>
 
@@ -59,14 +61,17 @@
 			<rom label="acpi"/>
 			<rom label="pci_devices"/>
 			<rom label="usb_devices"/>
+			<rom label="dynamic_state"/>
 		</config>
 		<route>
-			<service name="ROM"    label="acpi">        <child name="report_rom"/> </service>
-			<service name="ROM"    label="pci_devices"> <child name="report_rom"/> </service>
-			<service name="ROM"    label="usb_devices"> <child name="report_rom"/> </service>
-			<service name="Report" label="acpi">        <parent label="acpi"/>        </service>
-			<service name="Report" label="pci_devices"> <parent label="pci_devices"/> </service>
-			<service name="Report" label="usb_devices"> <parent label="usb_devices"/> </service>
+			<service name="ROM"    label="acpi">          <child name="report_rom"/> </service>
+			<service name="ROM"    label="pci_devices">   <child name="report_rom"/> </service>
+			<service name="ROM"    label="usb_devices">   <child name="report_rom"/> </service>
+			<service name="ROM"    label="dynamic_state"> <child name="report_rom"/> </service>
+			<service name="Report" label="acpi">          <parent label="acpi"/>        </service>
+			<service name="Report" label="pci_devices">   <parent label="pci_devices"/> </service>
+			<service name="Report" label="usb_devices">   <parent label="usb_devices"/> </service>
+			<service name="Report" label="dynamic_state"> <parent label="dynamic -> state"/> </service>
 			<service name="LOG"> <parent/> </service>
 			<service name="PD">  <parent/> </service>
 			<service name="CPU"> <parent/> </service>
@@ -226,10 +231,11 @@
 			<service name="Report" label="init.config"> <child name="report_rom"/> </service>
 			<service name="Report" label="usb_drv.config"> <child name="report_rom"/> </service>
 			<service name="Report" label="block_devices"> <parent label="block_devices"/> </service>
-			<service name="ROM" label="usb_devices"> <child name="report_rom"/> </service>
-			<service name="ROM" label="pci_devices"> <child name="report_rom"/> </service>
-			<service name="ROM" label="ahci_ports">  <child name="report_rom"/> </service>
-			<service name="ROM" label="nvme_ns">     <child name="report_rom"/> </service>
+			<service name="ROM" label="usb_devices">   <child name="report_rom"/> </service>
+			<service name="ROM" label="pci_devices">   <child name="report_rom"/> </service>
+			<service name="ROM" label="ahci_ports">    <child name="report_rom"/> </service>
+			<service name="ROM" label="nvme_ns">       <child name="report_rom"/> </service>
+			<service name="ROM" label="dynamic_state"> <child name="report_rom"/> </service>
 			<service name="LOG"> <parent/> </service>
 			<service name="PD">  <parent/> </service>
 			<service name="CPU"> <parent/> </service>
@@ -248,6 +254,7 @@
 			<service name="Platform"> <child name="platform_drv"/> </service>
 			<service name="Report" label="ahci_ports"> <child name="report_rom"/> </service>
 			<service name="Report" label="nvme_ns"> <child name="report_rom"/> </service>
+			<service name="Report" label="state"> <child name="report_rom"/> </service>
 			<service name="Report"> <parent/> </service>
 			<service name="Usb"> <child name="usb_drv"/> </service>
 			<service name="ROM" label="config"> <child name="report_rom"/> </service>
diff --git a/repos/gems/src/app/driver_manager/main.cc b/repos/gems/src/app/driver_manager/main.cc
index 348b334f6e..3c0902227e 100644
--- a/repos/gems/src/app/driver_manager/main.cc
+++ b/repos/gems/src/app/driver_manager/main.cc
@@ -38,6 +38,8 @@ namespace Driver_manager {
 	struct Nvme_driver;
 
 	struct Priority { int value; };
+
+	struct Version { unsigned value; };
 }
 
 
@@ -62,11 +64,13 @@ class Driver_manager::Device_driver : Noncopyable
 		                                           Binary  const &binary,
 		                                           Ram_quota      ram,
 		                                           Cap_quota      caps,
-		                                           Priority       priority)
+		                                           Priority       priority,
+		                                           Version        version)
 		{
 			xml.attribute("name", name);
 			xml.attribute("caps", String<64>(caps));
 			xml.attribute("priority", priority.value);
+			xml.attribute("version", version.value);
 			xml.node("binary", [&] () { xml.attribute("name", binary); });
 			xml.node("resource", [&] () {
 				xml.attribute("name", "RAM");
@@ -122,12 +126,15 @@ class Driver_manager::Device_driver : Noncopyable
 
 struct Driver_manager::Intel_fb_driver : Device_driver
 {
+	Version version { 0 };
+
 	void generate_start_node(Xml_generator &xml) const override
 	{
 		xml.node("start", [&] () {
 			_gen_common_start_node_content(xml, "intel_fb_drv", "intel_fb_drv",
 			                               Ram_quota{42*1024*1024}, Cap_quota{800},
-			                               Priority{0});
+			                               Priority{0}, version);
+			xml.node("heartbeat", [&] () { });
 			xml.node("route", [&] () {
 				_gen_config_route(xml, "fb_drv.config");
 				_gen_default_parent_route(xml);
@@ -144,7 +151,7 @@ struct Driver_manager::Vesa_fb_driver : Device_driver
 		xml.node("start", [&] () {
 			_gen_common_start_node_content(xml, "vesa_fb_drv", "vesa_fb_drv",
 			                               Ram_quota{8*1024*1024}, Cap_quota{100},
-			                               Priority{-1});
+			                               Priority{-1}, Version{0});
 			xml.node("route", [&] () {
 				_gen_config_route(xml, "fb_drv.config");
 				_gen_default_parent_route(xml);
@@ -188,7 +195,7 @@ struct Driver_manager::Boot_fb_driver : Device_driver
 		xml.node("start", [&] () {
 			_gen_common_start_node_content(xml, "boot_fb_drv", "boot_fb_drv",
 			                               _ram_quota, Cap_quota{100},
-			                               Priority{-1});
+			                               Priority{-1}, Version{0});
 			xml.node("route", [&] () {
 				_gen_config_route(xml, "fb_drv.config");
 				_gen_default_parent_route(xml);
@@ -205,7 +212,7 @@ struct Driver_manager::Ahci_driver : Device_driver
 		xml.node("start", [&] () {
 			_gen_common_start_node_content(xml, "ahci_drv", "ahci_drv",
 			                               Ram_quota{10*1024*1024}, Cap_quota{100},
-			                               Priority{-1});
+			                               Priority{-1}, Version{0});
 			_gen_provides_node<Block::Session>(xml);
 			xml.node("config", [&] () {
 				xml.node("report", [&] () { xml.attribute("ports", "yes"); });
@@ -217,6 +224,7 @@ struct Driver_manager::Ahci_driver : Device_driver
 					});
 				}
 			});
+			xml.node("heartbeat", [&] () { });
 			xml.node("route", [&] () {
 				xml.node("service", [&] () {
 					xml.attribute("name", "Report");
@@ -260,7 +268,7 @@ struct Driver_manager::Nvme_driver : Device_driver
 		xml.node("start", [&] () {
 			_gen_common_start_node_content(xml, "nvme_drv", "nvme_drv",
 			                               Ram_quota{8*1024*1024}, Cap_quota{100},
-			                               Priority{-1});
+			                               Priority{-1}, Version{0});
 			_gen_provides_node<Block::Session>(xml);
 			xml.node("config", [&] () {
 				xml.node("report", [&] () { xml.attribute("namespaces", "yes"); });
@@ -308,12 +316,13 @@ struct Driver_manager::Main : private Block_devices_generator
 {
 	Env &_env;
 
-	Attached_rom_dataspace _platform    { _env, "platform_info" };
-	Attached_rom_dataspace _usb_devices { _env, "usb_devices" };
-	Attached_rom_dataspace _usb_policy  { _env, "usb_policy"  };
-	Attached_rom_dataspace _pci_devices { _env, "pci_devices" };
-	Attached_rom_dataspace _ahci_ports  { _env, "ahci_ports"  };
-	Attached_rom_dataspace _nvme_ns     { _env, "nvme_ns"     };
+	Attached_rom_dataspace _platform      { _env, "platform_info" };
+	Attached_rom_dataspace _usb_devices   { _env, "usb_devices"   };
+	Attached_rom_dataspace _usb_policy    { _env, "usb_policy"    };
+	Attached_rom_dataspace _pci_devices   { _env, "pci_devices"   };
+	Attached_rom_dataspace _ahci_ports    { _env, "ahci_ports"    };
+	Attached_rom_dataspace _nvme_ns       { _env, "nvme_ns"       };
+	Attached_rom_dataspace _dynamic_state { _env, "dynamic_state" };
 
 	Reporter _init_config    { _env, "config", "init.config" };
 	Reporter _usb_drv_config { _env, "config", "usb_drv.config" };
@@ -359,6 +368,11 @@ struct Driver_manager::Main : private Block_devices_generator
 	Signal_handler<Main> _nvme_ns_update_handler {
 		_env.ep(), *this, &Main::_handle_nvme_ns_update };
 
+	Signal_handler<Main> _dynamic_state_handler {
+		_env.ep(), *this, &Main::_handle_dyanmic_state };
+
+	void _handle_dyanmic_state();
+
 	static void _gen_parent_service_xml(Xml_generator &xml, char const *name)
 	{
 		xml.node("service", [&] () { xml.attribute("name", name); });
@@ -381,10 +395,11 @@ struct Driver_manager::Main : private Block_devices_generator
 		_usb_drv_config.enabled(true);
 		_block_devices.enabled(true);
 
-		_pci_devices.sigh(_pci_devices_update_handler);
-		_usb_policy .sigh(_usb_policy_update_handler);
-		_ahci_ports .sigh(_ahci_ports_update_handler);
-		_nvme_ns    .sigh(_nvme_ns_update_handler);
+		_pci_devices  .sigh(_pci_devices_update_handler);
+		_usb_policy   .sigh(_usb_policy_update_handler);
+		_ahci_ports   .sigh(_ahci_ports_update_handler);
+		_nvme_ns      .sigh(_nvme_ns_update_handler);
+		_dynamic_state.sigh(_dynamic_state_handler);
 
 		_generate_init_config(_init_config);
 
@@ -518,7 +533,12 @@ void Driver_manager::Main::_generate_init_config(Reporter &init_config) const
 		xml.attribute("verbose", false);
 		xml.attribute("prio_levels", 2);
 
-		xml.node("report", [&] () { xml.attribute("child_ram", true); });
+		xml.node("report", [&] () {
+			xml.attribute("child_ram", true);
+			xml.attribute("delay_ms", 2500);
+		});
+
+		xml.node("heartbeat", [&] () { xml.attribute("rate_ms", 2500); });
 
 		xml.node("parent-provides", [&] () {
 			_gen_parent_service_xml(xml, Rom_session::service_name());
@@ -708,4 +728,36 @@ void Driver_manager::Main::_generate_usb_drv_config(Reporter &usb_drv_config,
 }
 
 
+void Driver_manager::Main::_handle_dyanmic_state()
+{
+	_dynamic_state.update();
+
+	bool reconfigure_dynamic_init = false;
+
+	_dynamic_state.xml().for_each_sub_node([&] (Xml_node child) {
+
+		using Name = Device_driver::Name;
+
+		Name const name = child.attribute_value("name", Name());
+
+		if (name == "intel_fb_drv") {
+
+			unsigned long const skipped_heartbeats =
+				child.attribute_value("skipped_heartbeats", 0U);
+
+			if (skipped_heartbeats >= 2) {
+
+				if (_intel_fb_driver.constructed()) {
+					_intel_fb_driver->version.value++;
+					reconfigure_dynamic_init = true;
+				}
+			}
+		}
+	});
+
+	if (reconfigure_dynamic_init)
+		_generate_init_config(_init_config);
+}
+
+
 void Component::construct(Genode::Env &env) { static Driver_manager::Main main(env); }