Weston will not repaint until previous update has been acked by a pageflip event coming from the drm driver. However, some buggy drivers won’t return those events or will stop sending them at some point and Weston output repaints will completely freeze. To ease developers’ task in testing their drivers, this patch makes compositor-drm use a timer to detect cases where those pageflip events stop coming.
This timeout implementation is software only and includes basic features usually found in a watchdog. We simply exit Weston gracefully with a log message and an exit code when the timout is reached. The timeout value can be set via weston.ini by adding a pageflip-timeout=<MILLISECONDS> entry under a new [compositor-drm] section. Setting it to 0 disables the timeout feature. https://bugs.freedesktop.org/show_bug.cgi?id=83884 Signed-off-by: Frederic Plourde <frederic.plou...@collabora.co.uk> Signed-off-by: Emmanuel Gil Peyrot <emmanuel.pey...@collabora.com> --- compositor/main.c | 2 ++ libweston/compositor-drm.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++ libweston/compositor-drm.h | 7 +++++ man/weston.ini.man | 5 ++++ 4 files changed, 78 insertions(+) diff --git a/compositor/main.c b/compositor/main.c index 2aa4936e..abc2c2e4 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -1201,6 +1201,8 @@ load_drm_backend(struct weston_compositor *c, weston_config_section_get_string(section, "gbm-format", &config.gbm_format, NULL); + weston_config_section_get_uint(section, "pageflip-timeout", + &config.pageflip_timeout, 0); config.base.struct_version = WESTON_DRM_BACKEND_CONFIG_VERSION; config.base.struct_size = sizeof(struct weston_drm_backend_config); diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c index ecc872ef..8682b11d 100644 --- a/libweston/compositor-drm.c +++ b/libweston/compositor-drm.c @@ -120,6 +120,8 @@ struct drm_backend { int32_t cursor_height; bool use_current_mode; + + uint32_t pageflip_timeout; }; struct drm_mode { @@ -183,6 +185,8 @@ struct drm_output { struct vaapi_recorder *recorder; struct wl_listener recorder_frame_listener; + + struct wl_event_source *pageflip_timer; }; /* @@ -226,6 +230,43 @@ to_drm_backend(struct weston_compositor *base) return container_of(base->backend, struct drm_backend, base); } +static int +pageflip_timeout(void *data) { + /* + * Our timer just went off, that means we're not receiving drm + * page flip events anymore for that output. Let's gracefully exit + * weston with a return value so devs can debug what's going on. + */ + struct drm_output *output = data; + struct weston_compositor *compositor = output->base.compositor; + + weston_log("Pageflip timeout reached, your driver is probably " + "buggy! Exiting.\n"); + weston_compositor_exit_with_code(compositor, EXIT_FAILURE); + + return -1; +} + +/* Creates the pageflip timer. Note that it isn't armed by default */ +static int +drm_output_pageflip_timer_create(struct drm_output *output) +{ + struct wl_event_loop *loop = NULL; + struct weston_compositor *ec = output->base.compositor; + + loop = wl_display_get_event_loop(ec->wl_display); + assert(loop); + output->pageflip_timer = wl_event_loop_add_timer(loop, + pageflip_timeout, + output); + + if (output->pageflip_timer) + return 0; + + weston_log("creating drm pageflip timer failed: %m\n"); + return -1; +} + static void drm_output_set_cursor(struct drm_output *output); @@ -692,6 +733,10 @@ drm_output_repaint(struct weston_output *output_base, output_base->set_dpms(output_base, WESTON_DPMS_ON); } + if (output->pageflip_timer) + wl_event_source_timer_update(output->pageflip_timer, + backend->pageflip_timeout); + if (drmModePageFlip(backend->drm.fd, output->crtc_id, output->next->fb_id, DRM_MODE_PAGE_FLIP_EVENT, output) < 0) { @@ -815,6 +860,10 @@ drm_output_start_repaint_loop(struct weston_output *output_base) */ fb_id = output->current->fb_id; + if (output->pageflip_timer) + wl_event_source_timer_update(output->pageflip_timer, + backend->pageflip_timeout); + if (drmModePageFlip(backend->drm.fd, output->crtc_id, fb_id, DRM_MODE_PAGE_FLIP_EVENT, output) < 0) { weston_log("queueing pageflip failed: %m\n"); @@ -858,6 +907,10 @@ vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, s->current = s->next; s->next = NULL; + /* Stop the pageflip timer instead of rearming it here */ + if (output->pageflip_timer) + wl_event_source_timer_update(output->pageflip_timer, 0); + if (!output->page_flip_pending) { ts.tv_sec = sec; ts.tv_nsec = usec * 1000; @@ -891,6 +944,10 @@ page_flip_handler(int fd, unsigned int frame, output->page_flip_pending = 0; + /* Stop the pageflip timer instead of rearming it here */ + if (output->pageflip_timer) + wl_event_source_timer_update(output->pageflip_timer, 0); + if (output->destroy_pending) drm_output_destroy(&output->base); else if (output->disable_pending) @@ -2378,6 +2435,9 @@ drm_output_enable(struct weston_output *base) output->dpms_prop = drm_get_prop(b->drm.fd, output->connector, "DPMS"); + if (b->pageflip_timeout) + drm_output_pageflip_timer_create(output); + if (b->use_pixman) { if (drm_output_init_pixman(output, b) < 0) { weston_log("Failed to init output pixman state\n"); @@ -2484,6 +2544,9 @@ drm_output_destroy(struct weston_output *base) drmModeFreeCrtc(origcrtc); } + if (output->pageflip_timer) + wl_event_source_remove(output->pageflip_timer); + weston_output_destroy(&output->base); drmModeFreeConnector(output->connector); @@ -3142,6 +3205,7 @@ drm_backend_create(struct weston_compositor *compositor, b->compositor = compositor; b->use_pixman = config->use_pixman; b->use_current_mode = config->use_current_mode; + b->pageflip_timeout = config->pageflip_timeout; if (parse_gbm_format(config->gbm_format, GBM_FORMAT_XRGB8888, &b->gbm_format) < 0) goto err_compositor; diff --git a/libweston/compositor-drm.h b/libweston/compositor-drm.h index 8f89a2b3..78ea10c1 100644 --- a/libweston/compositor-drm.h +++ b/libweston/compositor-drm.h @@ -139,6 +139,13 @@ struct weston_drm_backend_config { void (*configure_device)(struct weston_compositor *compositor, struct libinput_device *device); bool use_current_mode; + + /** Maximum duration for a pageflip event to arrive, after which the + * compositor will consider the DRM driver crashed and will try to exit + * cleanly. + * + * It is exprimed in milliseconds, 0 means disabled. */ + uint32_t pageflip_timeout; }; #ifdef __cplusplus diff --git a/man/weston.ini.man b/man/weston.ini.man index 54668485..db2b1bf4 100644 --- a/man/weston.ini.man +++ b/man/weston.ini.man @@ -159,6 +159,11 @@ By default, xrgb8888 is used. sets Weston's idle timeout in seconds. This idle timeout is the time after which Weston will enter an "inactive" mode and screen will fade to black. A value of 0 disables the timeout. +.TP 7 +.BI "pageflip-timeout="milliseconds +sets Weston's pageflip timeout in milliseconds. This sets a timer to exit +gracefully with a log message and an exit code of 1 in case the DRM driver is +non-responsive. Setting it to 0 disables this feature. .IR Important : This option may also be set via Weston's '-i' command -- 2.11.0 _______________________________________________ wayland-devel mailing list wayland-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/wayland-devel