backend-drm: improve atomic commit failure handling

When an atomic commit fails then the output will be stuck in
REPAINT_AWAITING_COMPLETION state. It is waiting for a vblank event that was
never scheduled.
If the error is EBUSY then it can be expected to be a transient error. So
propagate the error and schedule a new repaint in the core compositor.

This is necessary because there are some circumstances when the commit can fail
unexpectedly:
- With 'state_invalid == true' one commit will disable all planes. If another
  commit for a different output is triggered immediately afterwards, then this
  commit can temporarily fail with EBUSY because it tries to use the same
  planes.
- At least with i915, if one commit enables an output then a second commit for a
  different output immediately afterwards can temporarily fail with EBUSY. This
  is probably caused by some hardware interdependency.

Signed-off-by: Michael Olbrich <m.olbrich@pengutronix.de>
dev
Michael Olbrich 2 years ago
parent d4eafbaa98
commit 3b3fdc52c3
  1. 6
      libweston/backend-drm/drm.c
  2. 24
      libweston/compositor.c

@ -593,8 +593,8 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
if (ret != 0) { if (ret != 0) {
weston_log("applying repaint-start state failed: %s\n", weston_log("applying repaint-start state failed: %s\n",
strerror(errno)); strerror(errno));
if (ret == -EACCES) if (ret == -EACCES || ret == -EBUSY)
return -1; return ret;
goto finish_frame; goto finish_frame;
} }
@ -657,7 +657,7 @@ drm_repaint_flush(struct weston_compositor *compositor)
drm_debug(b, "[repaint] flushed pending_state %p\n", pending_state); drm_debug(b, "[repaint] flushed pending_state %p\n", pending_state);
device->repaint_data = NULL; device->repaint_data = NULL;
return (ret == -EACCES) ? -1 : 0; return (ret == -EACCES || ret == -EBUSY) ? ret : 0;
} }
/** /**

@ -3405,6 +3405,20 @@ output_repaint_timer_arm(struct weston_compositor *compositor)
wl_event_source_timer_update(compositor->repaint_timer, msec_to_next); wl_event_source_timer_update(compositor->repaint_timer, msec_to_next);
} }
static void
weston_output_schedule_repaint_restart(struct weston_output *output)
{
assert(output->repaint_status == REPAINT_AWAITING_COMPLETION);
/* The device was busy so try again one frame later */
timespec_add_nsec(&output->next_repaint, &output->next_repaint,
millihz_to_nsec(output->current_mode->refresh));
output->repaint_status = REPAINT_SCHEDULED;
TL_POINT(output->compositor, "core_repaint_restart",
TLP_OUTPUT(output), TLP_END);
output_repaint_timer_arm(output->compositor);
weston_output_damage(output);
}
static int static int
output_repaint_timer_handler(void *data) output_repaint_timer_handler(void *data)
{ {
@ -3435,10 +3449,14 @@ output_repaint_timer_handler(void *data)
if (ret != 0) { if (ret != 0) {
wl_list_for_each(output, &compositor->output_list, link) { wl_list_for_each(output, &compositor->output_list, link) {
if (output->repainted) if (output->repainted) {
if (ret == -EBUSY)
weston_output_schedule_repaint_restart(output);
else
weston_output_schedule_repaint_reset(output); weston_output_schedule_repaint_reset(output);
} }
} }
}
wl_list_for_each(output, &compositor->output_list, link) wl_list_for_each(output, &compositor->output_list, link)
output->repainted = false; output->repainted = false;
@ -3583,7 +3601,9 @@ idle_repaint(void *data)
output->repaint_status = REPAINT_AWAITING_COMPLETION; output->repaint_status = REPAINT_AWAITING_COMPLETION;
output->idle_repaint_source = NULL; output->idle_repaint_source = NULL;
ret = output->start_repaint_loop(output); ret = output->start_repaint_loop(output);
if (ret != 0) if (ret == -EBUSY)
weston_output_schedule_repaint_restart(output);
else if (ret != 0)
weston_output_schedule_repaint_reset(output); weston_output_schedule_repaint_reset(output);
} }

Loading…
Cancel
Save