intel_fb: handle rapid config change gracefully

In case of very rapid config ROM change (<300ms), the linux driver may still
be in progress of applying the previous change (e.g. switching connector on/off).

During this progress all tasks may become not runnable (waiting for IRQ/timeouts
until hardware state settles), the newest config ROM change/signal gets
dispatched, which lead to continuing the previous change request, but not
to re-starting/re-applying the new config change. To avoid this situation,
explicitly track whether a previous config change was finished and track if an
interim config change request came in. If so, re-start the lx_user
task with the newest config change.

Fixes #4721
This commit is contained in:
Alexander Boettcher 2023-01-06 15:15:04 +01:00 committed by Christian Helmuth
parent 41ebf3bd94
commit 5f0ba7d722
3 changed files with 27 additions and 3 deletions

View File

@ -35,5 +35,6 @@ void lx_emul_i915_report_connector(void * lx_data, void * genode_xml,
void lx_emul_i915_iterate_modes(void *lx_data, void * genode_data); void lx_emul_i915_iterate_modes(void *lx_data, void * genode_data);
void lx_emul_i915_report_modes(void * genode_xml, struct genode_mode *); void lx_emul_i915_report_modes(void * genode_xml, struct genode_mode *);
void lx_emul_i915_connector_config(char * name, struct genode_mode *); void lx_emul_i915_connector_config(char * name, struct genode_mode *);
int lx_emul_i915_config_done_and_block(void);
#endif /* _LX_I915_H_ */ #endif /* _LX_I915_H_ */

View File

@ -397,7 +397,8 @@ static int configure_connectors(void * data)
retry_count = 0; retry_count = 0;
lx_emul_task_schedule(true); if (lx_emul_i915_config_done_and_block())
lx_emul_task_schedule(true /* block task */);
} }
return 0; return 0;

View File

@ -53,6 +53,9 @@ struct Framebuffer::Driver
Signal_handler<Driver> timer_handler { env.ep(), *this, Signal_handler<Driver> timer_handler { env.ep(), *this,
&Driver::handle_timer }; &Driver::handle_timer };
bool update_in_progress { false };
bool new_config_rom { false };
class Fb class Fb
{ {
private: private:
@ -148,6 +151,11 @@ void Framebuffer::Driver::config_update()
if (!config.valid() || !lx_user_task) if (!config.valid() || !lx_user_task)
return; return;
if (update_in_progress)
new_config_rom = true;
else
update_in_progress = true;
lx_emul_task_unblock(lx_user_task); lx_emul_task_unblock(lx_user_task);
Lx_kit::env().scheduler.schedule(); Lx_kit::env().scheduler.schedule();
} }
@ -178,6 +186,8 @@ void Framebuffer::Driver::generate_report(void *lx_data)
{ {
lx_emul_i915_report(lx_data, &xml); lx_emul_i915_report(lx_data, &xml);
}); });
driver(Lx_kit::env().env).report_updated();
} catch (...) { } catch (...) {
Genode::warning("Failed to generate report"); Genode::warning("Failed to generate report");
} }
@ -288,8 +298,6 @@ void lx_emul_i915_report_connector(void * lx_data, void * genode_xml,
lx_emul_i915_iterate_modes(lx_data, &xml); lx_emul_i915_iterate_modes(lx_data, &xml);
}); });
driver(Lx_kit::env().env).report_updated();
} }
@ -323,6 +331,20 @@ void lx_emul_i915_connector_config(char * name, struct genode_mode * mode)
} }
int lx_emul_i915_config_done_and_block(void)
{
auto &state = driver(Lx_kit::env().env);
bool const new_config = state.new_config_rom;
state.update_in_progress = false;
state.new_config_rom = false;
/* true if linux task should block, otherwise continue due to new config */
return !new_config;
}
void Component::construct(Genode::Env &env) void Component::construct(Genode::Env &env)
{ {
driver(env).start(); driver(env).start();