diff --git a/compositor/main.c b/compositor/main.c index 72c3cd10..e870dd4a 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -1235,6 +1235,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 7f78699c..00623076 100644 --- a/libweston/compositor-drm.c +++ b/libweston/compositor-drm.c @@ -119,6 +119,7 @@ struct drm_backend { int32_t cursor_height; uint32_t connector; + uint32_t pageflip_timeout; }; struct drm_mode { @@ -182,6 +183,8 @@ struct drm_output { struct vaapi_recorder *recorder; struct wl_listener recorder_frame_listener; + + struct wl_event_source *pageflip_timer; }; /* @@ -225,6 +228,45 @@ 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 on output %s, your " + "driver is probably buggy! Exiting.\n", + output->base.name); + weston_compositor_exit_with_code(compositor, EXIT_FAILURE); + + return 0; +} + +/* 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 == NULL) { + weston_log("creating drm pageflip timer failed: %m\n"); + return -1; + } + + return 0; +} + static void drm_output_set_cursor(struct drm_output *output); @@ -758,6 +800,10 @@ drm_output_repaint(struct weston_output *output_base, output->page_flip_pending = 1; + if (output->pageflip_timer) + wl_event_source_timer_update(output->pageflip_timer, + backend->pageflip_timeout); + drm_output_set_cursor(output); /* @@ -878,6 +924,10 @@ drm_output_start_repaint_loop(struct weston_output *output_base) goto finish_frame; } + if (output->pageflip_timer) + wl_event_source_timer_update(output->pageflip_timer, + backend->pageflip_timeout); + return; finish_frame: @@ -916,6 +966,10 @@ vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, s->next = NULL; if (!output->page_flip_pending) { + /* Stop the pageflip timer instead of rearming it here */ + if (output->pageflip_timer) + wl_event_source_timer_update(output->pageflip_timer, 0); + ts.tv_sec = sec; ts.tv_nsec = usec * 1000; weston_output_finish_frame(&output->base, &ts, flags); @@ -953,6 +1007,10 @@ page_flip_handler(int fd, unsigned int frame, else if (output->disable_pending) weston_output_disable(&output->base); else if (!output->vblank_pending) { + /* Stop the pageflip timer instead of rearming it here */ + if (output->pageflip_timer) + wl_event_source_timer_update(output->pageflip_timer, 0); + ts.tv_sec = sec; ts.tv_nsec = usec * 1000; weston_output_finish_frame(&output->base, &ts, flags); @@ -2418,6 +2476,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"); @@ -2532,6 +2593,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); @@ -3309,6 +3373,7 @@ drm_backend_create(struct weston_compositor *compositor, b->sprites_are_broken = 1; b->compositor = compositor; b->use_pixman = config->use_pixman; + 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 2e2995a2..08707197 100644 --- a/libweston/compositor-drm.h +++ b/libweston/compositor-drm.h @@ -138,6 +138,13 @@ struct weston_drm_backend_config { */ void (*configure_device)(struct weston_compositor *compositor, struct libinput_device *device); + + /** 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 2edb0854..5ec0e1d1 100644 --- a/man/weston.ini.man +++ b/man/weston.ini.man @@ -162,6 +162,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