From d14ff6b77824d0e32728869907e88d6866243101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Tue, 10 Jul 2018 18:41:43 +0200 Subject: Ignore RADEON_DRM_QUEUE_ERROR (0) in radeon_drm_abort_entry This allows a following change to be slightly simpler. (Ported from amdgpu commit 8fcc3a9b43d3907052a83a96e5a2423afab5ad3f) Acked-by: Alex Deucher --- src/radeon_drm_queue.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/radeon_drm_queue.c') diff --git a/src/radeon_drm_queue.c b/src/radeon_drm_queue.c index 869f95c3..ac775f86 100644 --- a/src/radeon_drm_queue.c +++ b/src/radeon_drm_queue.c @@ -150,6 +150,9 @@ radeon_drm_abort_entry(uintptr_t seq) { struct radeon_drm_queue_entry *e, *tmp; + if (seq == RADEON_DRM_QUEUE_ERROR) + return; + xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_queue, list) { if (e->seq == seq) { radeon_drm_abort_one(e); -- cgit v1.2.3 From ecdf0b7ec9378bc386ce8276f16fb16d09f72812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Thu, 16 Aug 2018 17:14:42 +0200 Subject: Move DRM event queue related initialization to radeon_drm_queue_init And make radeon_drm_queue_handler not directly accessible outside of radeon_drm_queue.c. (Ported from amdgpu commit 0148283984c77f7a6e97026edc3093497547e0a4) Acked-by: Alex Deucher --- src/radeon_drm_queue.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'src/radeon_drm_queue.c') diff --git a/src/radeon_drm_queue.c b/src/radeon_drm_queue.c index ac775f86..bff010fa 100644 --- a/src/radeon_drm_queue.c +++ b/src/radeon_drm_queue.c @@ -57,7 +57,7 @@ static uintptr_t radeon_drm_queue_seq; /* * Handle a DRM event */ -void +static void radeon_drm_queue_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *user_ptr) { @@ -181,8 +181,15 @@ radeon_drm_abort_id(uint64_t id) * Initialize the DRM event queue */ void -radeon_drm_queue_init() +radeon_drm_queue_init(ScrnInfoPtr scrn) { + RADEONInfoPtr info = RADEONPTR(scrn); + drmmode_ptr drmmode = &info->drmmode; + + drmmode->event_context.version = 2; + drmmode->event_context.vblank_handler = radeon_drm_queue_handler; + drmmode->event_context.page_flip_handler = radeon_drm_queue_handler; + if (radeon_drm_queue_refcnt++) return; -- cgit v1.2.3 From 93621e408c17dd9e082236c17f051c06558d7f4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Thu, 16 Aug 2018 17:27:06 +0200 Subject: Add radeon_drm_wait_pending_flip function Replacing the drmmode_crtc_wait_pending_event macro. (Ported from amdgpu commit 6029794e8a35417faf825491a89b85f713c77fc1) Acked-by: Alex Deucher --- src/radeon_drm_queue.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'src/radeon_drm_queue.c') diff --git a/src/radeon_drm_queue.c b/src/radeon_drm_queue.c index bff010fa..69474be2 100644 --- a/src/radeon_drm_queue.c +++ b/src/radeon_drm_queue.c @@ -177,6 +177,19 @@ radeon_drm_abort_id(uint64_t id) } } +/* + * Wait for pending page flip on given CRTC to complete + */ +void radeon_drm_wait_pending_flip(xf86CrtcPtr crtc) +{ + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + RADEONEntPtr pRADEONEnt = RADEONEntPriv(crtc->scrn); + drmmode_ptr drmmode = drmmode_crtc->drmmode; + + while (drmmode_crtc->flip_pending && + drmHandleEvent(pRADEONEnt->fd, &drmmode->event_context) > 0); +} + /* * Initialize the DRM event queue */ -- cgit v1.2.3 From ba83a866af5a3784fc4822614375cc081e93197c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Thu, 16 Aug 2018 17:44:45 +0200 Subject: Add radeon_drm_handle_event wrapper for drmHandleEvent Instead of processing DRM events directly from drmHandleEvent's callbacks, there are three phases: 1. drmHandleEvent is called, and signalled events are re-queued to _signalled lists from its callbacks. 2. Signalled page flip completion events are processed. 3. Signalled vblank events are processed. This should make sure that we never call drmHandleEvent from one of its callbacks, which would usually result in blocking forever. (Ported from amdgpu commit 739181c8d3334ff14b5a607895dfdeb29b0d9020) Acked-by: Alex Deucher --- src/radeon_drm_queue.c | 110 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 87 insertions(+), 23 deletions(-) (limited to 'src/radeon_drm_queue.c') diff --git a/src/radeon_drm_queue.c b/src/radeon_drm_queue.c index 69474be2..3d2f4d15 100644 --- a/src/radeon_drm_queue.c +++ b/src/radeon_drm_queue.c @@ -40,6 +40,7 @@ struct radeon_drm_queue_entry { struct xorg_list list; + uint64_t usec; uint64_t id; uintptr_t seq; void *data; @@ -47,36 +48,73 @@ struct radeon_drm_queue_entry { xf86CrtcPtr crtc; radeon_drm_handler_proc handler; radeon_drm_abort_proc abort; + unsigned int frame; }; static int radeon_drm_queue_refcnt; static struct xorg_list radeon_drm_queue; +static struct xorg_list radeon_drm_flip_signalled; +static struct xorg_list radeon_drm_vblank_signalled; static uintptr_t radeon_drm_queue_seq; /* - * Handle a DRM event + * Process a DRM event */ static void -radeon_drm_queue_handler(int fd, unsigned int frame, unsigned int sec, - unsigned int usec, void *user_ptr) +radeon_drm_queue_handle_one(struct radeon_drm_queue_entry *e) { - uintptr_t seq = (uintptr_t)user_ptr; - struct radeon_drm_queue_entry *e, *tmp; - - xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_queue, list) { - if (e->seq == seq) { - xorg_list_del(&e->list); - if (e->handler) - e->handler(e->crtc, frame, - (uint64_t)sec * 1000000 + usec, - e->data); - else - e->abort(e->crtc, e->data); - free(e); - break; - } + xorg_list_del(&e->list); + if (e->handler) { + e->handler(e->crtc, e->frame, e->usec, e->data); + } else + e->abort(e->crtc, e->data); + free(e); +} + +static void +radeon_drm_queue_handler(struct xorg_list *signalled, unsigned int frame, + unsigned int sec, unsigned int usec, void *user_ptr) +{ + uintptr_t seq = (uintptr_t)user_ptr; + struct radeon_drm_queue_entry *e, *tmp; + + xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_queue, list) { + if (e->seq == seq) { + if (!e->handler) { + e->abort(e->crtc, e->data); + break; + } + + xorg_list_del(&e->list); + e->usec = (uint64_t)sec * 1000000 + usec; + e->frame = frame; + xorg_list_append(&e->list, signalled); + break; } + } +} + +/* + * Signal a DRM page flip event + */ +static void +radeon_drm_page_flip_handler(int fd, unsigned int frame, unsigned int sec, + unsigned int usec, void *user_ptr) +{ + radeon_drm_queue_handler(&radeon_drm_flip_signalled, frame, sec, usec, + user_ptr); +} + +/* + * Signal a DRM vblank event + */ +static void +radeon_drm_vblank_handler(int fd, unsigned int frame, unsigned int sec, + unsigned int usec, void *user_ptr) +{ + radeon_drm_queue_handler(&radeon_drm_vblank_signalled, frame, sec, usec, + user_ptr); } /* @@ -177,6 +215,26 @@ radeon_drm_abort_id(uint64_t id) } } +/* + * drmHandleEvent wrapper + */ +int +radeon_drm_handle_event(int fd, drmEventContext *event_context) +{ + struct radeon_drm_queue_entry *e, *tmp; + int r; + + r = drmHandleEvent(fd, event_context); + + xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_flip_signalled, list) + radeon_drm_queue_handle_one(e); + + xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_vblank_signalled, list) + radeon_drm_queue_handle_one(e); + + return r; +} + /* * Wait for pending page flip on given CRTC to complete */ @@ -184,10 +242,14 @@ void radeon_drm_wait_pending_flip(xf86CrtcPtr crtc) { drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; RADEONEntPtr pRADEONEnt = RADEONEntPriv(crtc->scrn); - drmmode_ptr drmmode = drmmode_crtc->drmmode; + struct radeon_drm_queue_entry *e, *tmp; + + xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_flip_signalled, list) + radeon_drm_queue_handle_one(e); - while (drmmode_crtc->flip_pending && - drmHandleEvent(pRADEONEnt->fd, &drmmode->event_context) > 0); + while (drmmode_crtc->flip_pending + && radeon_drm_handle_event(pRADEONEnt->fd, + &drmmode_crtc->drmmode->event_context) > 0); } /* @@ -200,13 +262,15 @@ radeon_drm_queue_init(ScrnInfoPtr scrn) drmmode_ptr drmmode = &info->drmmode; drmmode->event_context.version = 2; - drmmode->event_context.vblank_handler = radeon_drm_queue_handler; - drmmode->event_context.page_flip_handler = radeon_drm_queue_handler; + drmmode->event_context.vblank_handler = radeon_drm_vblank_handler; + drmmode->event_context.page_flip_handler = radeon_drm_page_flip_handler; if (radeon_drm_queue_refcnt++) return; xorg_list_init(&radeon_drm_queue); + xorg_list_init(&radeon_drm_flip_signalled); + xorg_list_init(&radeon_drm_vblank_signalled); } /* -- cgit v1.2.3 From c42f6e2e61d166c8d3ef3fcad175d7050a00288b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Fri, 20 Jul 2018 16:56:22 +0200 Subject: Defer vblank event handling while waiting for a pending flip This is to avoid submitting more flips while we are waiting for pending ones to complete. (Ported from amdgpu commit e52872da69ecc84dafb3355839e35b0383f0d228) Acked-by: Alex Deucher --- src/radeon_drm_queue.c | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) (limited to 'src/radeon_drm_queue.c') diff --git a/src/radeon_drm_queue.c b/src/radeon_drm_queue.c index 3d2f4d15..857278fd 100644 --- a/src/radeon_drm_queue.c +++ b/src/radeon_drm_queue.c @@ -117,6 +117,30 @@ radeon_drm_vblank_handler(int fd, unsigned int frame, unsigned int sec, user_ptr); } +/* + * Handle deferred DRM vblank events + * + * This function must be called after radeon_drm_wait_pending_flip, once + * it's safe to attempt queueing a flip again + */ +void +radeon_drm_queue_handle_deferred(xf86CrtcPtr crtc) +{ + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + struct radeon_drm_queue_entry *e, *tmp; + + if (drmmode_crtc->wait_flip_nesting_level == 0 || + --drmmode_crtc->wait_flip_nesting_level > 0) + return; + + xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_vblank_signalled, list) { + drmmode_crtc_private_ptr drmmode_crtc = e->crtc->driver_private; + + if (drmmode_crtc->wait_flip_nesting_level == 0) + radeon_drm_queue_handle_one(e); + } +} + /* * Enqueue a potential drm response; when the associated response * appears, we've got data to pass to the handler from here @@ -191,6 +215,13 @@ radeon_drm_abort_entry(uintptr_t seq) if (seq == RADEON_DRM_QUEUE_ERROR) return; + xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_vblank_signalled, list) { + if (e->seq == seq) { + radeon_drm_abort_one(e); + return; + } + } + xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_queue, list) { if (e->seq == seq) { radeon_drm_abort_one(e); @@ -229,8 +260,12 @@ radeon_drm_handle_event(int fd, drmEventContext *event_context) xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_flip_signalled, list) radeon_drm_queue_handle_one(e); - xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_vblank_signalled, list) - radeon_drm_queue_handle_one(e); + xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_vblank_signalled, list) { + drmmode_crtc_private_ptr drmmode_crtc = e->crtc->driver_private; + + if (drmmode_crtc->wait_flip_nesting_level == 0) + radeon_drm_queue_handle_one(e); + } return r; } @@ -244,6 +279,8 @@ void radeon_drm_wait_pending_flip(xf86CrtcPtr crtc) RADEONEntPtr pRADEONEnt = RADEONEntPriv(crtc->scrn); struct radeon_drm_queue_entry *e, *tmp; + drmmode_crtc->wait_flip_nesting_level++; + xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_flip_signalled, list) radeon_drm_queue_handle_one(e); -- cgit v1.2.3 From 87b9a3e516d19dd1b89a64f6cac990fae53fc1b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Wed, 5 Sep 2018 11:27:25 +0200 Subject: Always delete entry from list in drm_queue_handler We left entries without a handler hook in the list, so the list could keep taking longer to process and use up more memory. (Ported from amdgpu commit 7eea3e2cd74eed22e982319144e18ae5b1087b78) --- src/radeon_drm_queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/radeon_drm_queue.c') diff --git a/src/radeon_drm_queue.c b/src/radeon_drm_queue.c index 857278fd..61a2f5ce 100644 --- a/src/radeon_drm_queue.c +++ b/src/radeon_drm_queue.c @@ -82,7 +82,7 @@ radeon_drm_queue_handler(struct xorg_list *signalled, unsigned int frame, xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_queue, list) { if (e->seq == seq) { if (!e->handler) { - e->abort(e->crtc, e->data); + radeon_drm_queue_handle_one(e); break; } -- cgit v1.2.3 From 4c7d5e50a5e469a541bc463cecb505fe850c0824 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Wed, 5 Sep 2018 11:29:43 +0200 Subject: Don't use xorg_list_for_each_entry_safe for signalled flips drm_wait_pending_flip can get called from drm_handle_event, in which case xorg_list_for_each_entry_safe can end up processing the same entry in both. To avoid this, just process the first list entry until the list is empty. (Ported from amdgpu commit 26770be44b89b83bf39c28f2fe284c8cb92ed0c0) --- src/radeon_drm_queue.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src/radeon_drm_queue.c') diff --git a/src/radeon_drm_queue.c b/src/radeon_drm_queue.c index 61a2f5ce..bf1650ea 100644 --- a/src/radeon_drm_queue.c +++ b/src/radeon_drm_queue.c @@ -257,8 +257,11 @@ radeon_drm_handle_event(int fd, drmEventContext *event_context) r = drmHandleEvent(fd, event_context); - xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_flip_signalled, list) + while (!xorg_list_is_empty(&radeon_drm_flip_signalled)) { + e = xorg_list_first_entry(&radeon_drm_flip_signalled, + struct radeon_drm_queue_entry, list); radeon_drm_queue_handle_one(e); + } xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_vblank_signalled, list) { drmmode_crtc_private_ptr drmmode_crtc = e->crtc->driver_private; @@ -277,12 +280,15 @@ void radeon_drm_wait_pending_flip(xf86CrtcPtr crtc) { drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; RADEONEntPtr pRADEONEnt = RADEONEntPriv(crtc->scrn); - struct radeon_drm_queue_entry *e, *tmp; + struct radeon_drm_queue_entry *e; drmmode_crtc->wait_flip_nesting_level++; - xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_flip_signalled, list) + while (!xorg_list_is_empty(&radeon_drm_flip_signalled)) { + e = xorg_list_first_entry(&radeon_drm_flip_signalled, + struct radeon_drm_queue_entry, list); radeon_drm_queue_handle_one(e); + } while (drmmode_crtc->flip_pending && radeon_drm_handle_event(pRADEONEnt->fd, -- cgit v1.2.3 From 5d5d883496842da84d9418e91cb13454751da625 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Fri, 7 Sep 2018 18:16:22 +0200 Subject: Bail early from drm_wait_pending_flip if there's no pending flip No need to process any events in that case. (Ported from amdgpu commit ca5eb9894fff153c0a1df7bdc4a4745713309e27) --- src/radeon_drm_queue.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/radeon_drm_queue.c') diff --git a/src/radeon_drm_queue.c b/src/radeon_drm_queue.c index bf1650ea..ea78e8e2 100644 --- a/src/radeon_drm_queue.c +++ b/src/radeon_drm_queue.c @@ -284,7 +284,8 @@ void radeon_drm_wait_pending_flip(xf86CrtcPtr crtc) drmmode_crtc->wait_flip_nesting_level++; - while (!xorg_list_is_empty(&radeon_drm_flip_signalled)) { + while (drmmode_crtc->flip_pending && + !xorg_list_is_empty(&radeon_drm_flip_signalled)) { e = xorg_list_first_entry(&radeon_drm_flip_signalled, struct radeon_drm_queue_entry, list); radeon_drm_queue_handle_one(e); -- cgit v1.2.3 From 189b6facb3988c00c96d970f8c13ed8d58fa3998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Thu, 20 Dec 2018 18:44:24 +0100 Subject: Use drm_abort_one in drm_queue_handler At this point, we've already established that e->handler is NULL, no need to check again in drm_queue_handle_one. This also makes it clearer what's happening. (Ported from amdgpu commit eda571222f5a6be47f8897e82d85199bb9d95251) --- src/radeon_drm_queue.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'src/radeon_drm_queue.c') diff --git a/src/radeon_drm_queue.c b/src/radeon_drm_queue.c index ea78e8e2..ebc6a5b6 100644 --- a/src/radeon_drm_queue.c +++ b/src/radeon_drm_queue.c @@ -72,6 +72,19 @@ radeon_drm_queue_handle_one(struct radeon_drm_queue_entry *e) free(e); } +/* + * Abort one queued DRM entry, removing it + * from the list, calling the abort function and + * freeing the memory + */ +static void +radeon_drm_abort_one(struct radeon_drm_queue_entry *e) +{ + xorg_list_del(&e->list); + e->abort(e->crtc, e->data); + free(e); +} + static void radeon_drm_queue_handler(struct xorg_list *signalled, unsigned int frame, unsigned int sec, unsigned int usec, void *user_ptr) @@ -82,7 +95,7 @@ radeon_drm_queue_handler(struct xorg_list *signalled, unsigned int frame, xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_queue, list) { if (e->seq == seq) { if (!e->handler) { - radeon_drm_queue_handle_one(e); + radeon_drm_abort_one(e); break; } @@ -173,19 +186,6 @@ radeon_drm_queue_alloc(xf86CrtcPtr crtc, ClientPtr client, return e->seq; } -/* - * Abort one queued DRM entry, removing it - * from the list, calling the abort function and - * freeing the memory - */ -static void -radeon_drm_abort_one(struct radeon_drm_queue_entry *e) -{ - xorg_list_del(&e->list); - e->abort(e->crtc, e->data); - free(e); -} - /* * Abort drm queue entries for a client * -- cgit v1.2.3 From f450632077843a95a6ef269febbfb64a605045ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Thu, 20 Dec 2018 18:48:19 +0100 Subject: Explicitly keep track of whether a DRM event is for a flip or not When an async flip is performed, and TearFree is enabled on the CRTC used for timing, we schedule a vblank event for completing the page flip. The DRM event queuing code treated this event like a vblank event, but it needs to be treated like a page flip event. (Ported from amdgpu commit e2c7369cae65069aa93eed1c0b678f975ce5c274) --- src/radeon_drm_queue.c | 39 +++++++++++---------------------------- 1 file changed, 11 insertions(+), 28 deletions(-) (limited to 'src/radeon_drm_queue.c') diff --git a/src/radeon_drm_queue.c b/src/radeon_drm_queue.c index ebc6a5b6..acfea3da 100644 --- a/src/radeon_drm_queue.c +++ b/src/radeon_drm_queue.c @@ -48,6 +48,7 @@ struct radeon_drm_queue_entry { xf86CrtcPtr crtc; radeon_drm_handler_proc handler; radeon_drm_abort_proc abort; + Bool is_flip; unsigned int frame; }; @@ -86,8 +87,8 @@ radeon_drm_abort_one(struct radeon_drm_queue_entry *e) } static void -radeon_drm_queue_handler(struct xorg_list *signalled, unsigned int frame, - unsigned int sec, unsigned int usec, void *user_ptr) +radeon_drm_queue_handler(int fd, unsigned int frame, unsigned int sec, + unsigned int usec, void *user_ptr) { uintptr_t seq = (uintptr_t)user_ptr; struct radeon_drm_queue_entry *e, *tmp; @@ -102,34 +103,14 @@ radeon_drm_queue_handler(struct xorg_list *signalled, unsigned int frame, xorg_list_del(&e->list); e->usec = (uint64_t)sec * 1000000 + usec; e->frame = frame; - xorg_list_append(&e->list, signalled); + xorg_list_append(&e->list, e->is_flip ? + &radeon_drm_flip_signalled : + &radeon_drm_vblank_signalled); break; } } } -/* - * Signal a DRM page flip event - */ -static void -radeon_drm_page_flip_handler(int fd, unsigned int frame, unsigned int sec, - unsigned int usec, void *user_ptr) -{ - radeon_drm_queue_handler(&radeon_drm_flip_signalled, frame, sec, usec, - user_ptr); -} - -/* - * Signal a DRM vblank event - */ -static void -radeon_drm_vblank_handler(int fd, unsigned int frame, unsigned int sec, - unsigned int usec, void *user_ptr) -{ - radeon_drm_queue_handler(&radeon_drm_vblank_signalled, frame, sec, usec, - user_ptr); -} - /* * Handle deferred DRM vblank events * @@ -162,7 +143,8 @@ uintptr_t radeon_drm_queue_alloc(xf86CrtcPtr crtc, ClientPtr client, uint64_t id, void *data, radeon_drm_handler_proc handler, - radeon_drm_abort_proc abort) + radeon_drm_abort_proc abort, + Bool is_flip) { struct radeon_drm_queue_entry *e; @@ -180,6 +162,7 @@ radeon_drm_queue_alloc(xf86CrtcPtr crtc, ClientPtr client, e->data = data; e->handler = handler; e->abort = abort; + e->is_flip = is_flip; xorg_list_append(&e->list, &radeon_drm_queue); @@ -306,8 +289,8 @@ radeon_drm_queue_init(ScrnInfoPtr scrn) drmmode_ptr drmmode = &info->drmmode; drmmode->event_context.version = 2; - drmmode->event_context.vblank_handler = radeon_drm_vblank_handler; - drmmode->event_context.page_flip_handler = radeon_drm_page_flip_handler; + drmmode->event_context.vblank_handler = radeon_drm_queue_handler; + drmmode->event_context.page_flip_handler = radeon_drm_queue_handler; if (radeon_drm_queue_refcnt++) return; -- cgit v1.2.3 From 64942d2c49f9fa1afcc42c07943d076a40963e51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Thu, 20 Dec 2018 19:00:08 +0100 Subject: Move deferred vblank events to separate drm_vblank_deferred list It was still possible for nested xorg_list_for_each_entry_safe loops to occur over the drm_vblank_signalled list, which could mess up that list. Moving deferred events to a separate list allows processing the drm_vblank_signalled list without xorg_list_for_each_entry_safe. Bugzilla: https://bugs.freedesktop.org/108600 (Ported from amdgpu commit 51ba6dddee40c3688d4c7b12eabeab516ed153b7) --- src/radeon_drm_queue.c | 57 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 11 deletions(-) (limited to 'src/radeon_drm_queue.c') diff --git a/src/radeon_drm_queue.c b/src/radeon_drm_queue.c index acfea3da..d8a8243c 100644 --- a/src/radeon_drm_queue.c +++ b/src/radeon_drm_queue.c @@ -56,6 +56,7 @@ static int radeon_drm_queue_refcnt; static struct xorg_list radeon_drm_queue; static struct xorg_list radeon_drm_flip_signalled; static struct xorg_list radeon_drm_vblank_signalled; +static struct xorg_list radeon_drm_vblank_deferred; static uintptr_t radeon_drm_queue_seq; @@ -111,6 +112,31 @@ radeon_drm_queue_handler(int fd, unsigned int frame, unsigned int sec, } } +/* + * Handle signalled vblank events. If we're waiting for a flip event, + * put events for that CRTC in the vblank_deferred list. + */ +static void +radeon_drm_handle_vblank_signalled(void) +{ + drmmode_crtc_private_ptr drmmode_crtc; + struct radeon_drm_queue_entry *e; + + while (!xorg_list_is_empty(&radeon_drm_vblank_signalled)) { + e = xorg_list_first_entry(&radeon_drm_vblank_signalled, + struct radeon_drm_queue_entry, list); + drmmode_crtc = e->crtc->driver_private; + + if (drmmode_crtc->wait_flip_nesting_level == 0) { + radeon_drm_queue_handle_one(e); + continue; + } + + xorg_list_del(&e->list); + xorg_list_append(&e->list, &radeon_drm_vblank_deferred); + } +} + /* * Handle deferred DRM vblank events * @@ -127,12 +153,18 @@ radeon_drm_queue_handle_deferred(xf86CrtcPtr crtc) --drmmode_crtc->wait_flip_nesting_level > 0) return; - xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_vblank_signalled, list) { - drmmode_crtc_private_ptr drmmode_crtc = e->crtc->driver_private; + /* Put previously deferred vblank events for this CRTC back in the + * signalled queue + */ + xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_vblank_deferred, list) { + if (e->crtc != crtc) + continue; - if (drmmode_crtc->wait_flip_nesting_level == 0) - radeon_drm_queue_handle_one(e); + xorg_list_del(&e->list); + xorg_list_append(&e->list, &radeon_drm_vblank_signalled); } + + radeon_drm_handle_vblank_signalled(); } /* @@ -205,6 +237,13 @@ radeon_drm_abort_entry(uintptr_t seq) } } + xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_vblank_deferred, list) { + if (e->seq == seq) { + radeon_drm_abort_one(e); + return; + } + } + xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_queue, list) { if (e->seq == seq) { radeon_drm_abort_one(e); @@ -235,7 +274,7 @@ radeon_drm_abort_id(uint64_t id) int radeon_drm_handle_event(int fd, drmEventContext *event_context) { - struct radeon_drm_queue_entry *e, *tmp; + struct radeon_drm_queue_entry *e; int r; r = drmHandleEvent(fd, event_context); @@ -246,12 +285,7 @@ radeon_drm_handle_event(int fd, drmEventContext *event_context) radeon_drm_queue_handle_one(e); } - xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_vblank_signalled, list) { - drmmode_crtc_private_ptr drmmode_crtc = e->crtc->driver_private; - - if (drmmode_crtc->wait_flip_nesting_level == 0) - radeon_drm_queue_handle_one(e); - } + radeon_drm_handle_vblank_signalled(); return r; } @@ -298,6 +332,7 @@ radeon_drm_queue_init(ScrnInfoPtr scrn) xorg_list_init(&radeon_drm_queue); xorg_list_init(&radeon_drm_flip_signalled); xorg_list_init(&radeon_drm_vblank_signalled); + xorg_list_init(&radeon_drm_vblank_deferred); } /* -- cgit v1.2.3 From 227123de3d862e691131708b7f55260bee17f2b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Mon, 28 Jan 2019 18:24:41 +0100 Subject: Call drmHandleEvent again if it was interrupted by a signal drmHandleEvent can be interrupted by a signal in read(), in which case it doesn't process any events but returns -1, which drm_handle_event propagated to its callers. This could cause the following failure cascade: 1. drm_wait_pending_flip stopped waiting for a pending flip. 2. Its caller cleared drmmode_crtc->flip_pending before the flip completed. 3. Another flip was attempted but got an unexpected EBUSY error because the previous flip was still pending. 4. TearFree was disabled due to the error. The solution is to call drmHandleEvent if it was interrupted by a signal. We can do that in drm_handle_event, because when that is called, either it is known that there are events ready to be processed, or the caller has to wait for events to arrive anyway. Bugzilla: https://bugs.freedesktop.org/109364 (Ported from amdgpu commit 3ff2cc225f6bc08364ee007fa54e9d0150adaf11) --- src/radeon_drm_queue.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'src/radeon_drm_queue.c') diff --git a/src/radeon_drm_queue.c b/src/radeon_drm_queue.c index d8a8243c..2e2b8404 100644 --- a/src/radeon_drm_queue.c +++ b/src/radeon_drm_queue.c @@ -30,6 +30,8 @@ #include "config.h" #endif +#include + #include #include #include @@ -277,7 +279,20 @@ radeon_drm_handle_event(int fd, drmEventContext *event_context) struct radeon_drm_queue_entry *e; int r; - r = drmHandleEvent(fd, event_context); + /* Retry drmHandleEvent if it was interrupted by a signal in read() */ + do { + r = drmHandleEvent(fd, event_context); + } while (r < 0 && (errno == EINTR || errno == EAGAIN)); + + if (r < 0) { + static Bool printed; + + if (!printed) { + ErrorF("%s: drmHandleEvent returned %d, errno=%d (%s)\n", + __func__, r, errno, strerror(errno)); + printed = TRUE; + } + } while (!xorg_list_is_empty(&radeon_drm_flip_signalled)) { e = xorg_list_first_entry(&radeon_drm_flip_signalled, -- cgit v1.2.3 From 15697ee242c30b9ea6775624e8282e0171a113a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Mon, 28 Jan 2019 18:27:10 +0100 Subject: Keep waiting for a pending flip if drm_handle_event returns 0 drm_wait_pending_flip stopped waiting if drm_handle_event returned 0, but that might have processed only some unrelated DRM events. As long as the flip is pending, we have to keep waiting for its completion event. Noticed while working on the previous fix. (Ported from amdgpu commit 9045fb310f88780e250e60b80431ca153330e61b) --- src/radeon_drm_queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/radeon_drm_queue.c') diff --git a/src/radeon_drm_queue.c b/src/radeon_drm_queue.c index 2e2b8404..fc043605 100644 --- a/src/radeon_drm_queue.c +++ b/src/radeon_drm_queue.c @@ -325,7 +325,7 @@ void radeon_drm_wait_pending_flip(xf86CrtcPtr crtc) while (drmmode_crtc->flip_pending && radeon_drm_handle_event(pRADEONEnt->fd, - &drmmode_crtc->drmmode->event_context) > 0); + &drmmode_crtc->drmmode->event_context) >= 0); } /* -- cgit v1.2.3