[PATCH xf86-video-amdgpu 3/4] Defer vblank event handling while waiting for a pending flip

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Michel Dänzer <michel.daenzer@xxxxxxx>

This is a more robust way to prevent hangs due to nested calls to
drmHandleEvent.

Signed-off-by: Michel Dänzer <michel.daenzer at amd.com>
---
 src/amdgpu_drm_queue.c | 124 +++++++++++++++++++++++++++++++++++++----
 src/amdgpu_drm_queue.h |   1 +
 src/drmmode_display.c  |  18 ++++--
 src/drmmode_display.h  |   4 ++
 4 files changed, 131 insertions(+), 16 deletions(-)

diff --git a/src/amdgpu_drm_queue.c b/src/amdgpu_drm_queue.c
index 9c0166147..d4353e703 100644
--- a/src/amdgpu_drm_queue.c
+++ b/src/amdgpu_drm_queue.c
@@ -40,6 +40,7 @@
 
 struct amdgpu_drm_queue_entry {
 	struct xorg_list list;
+	uint64_t usec;
 	uint64_t id;
 	uintptr_t seq;
 	void *data;
@@ -47,13 +48,27 @@ struct amdgpu_drm_queue_entry {
 	xf86CrtcPtr crtc;
 	amdgpu_drm_handler_proc handler;
 	amdgpu_drm_abort_proc abort;
+	unsigned int frame;
 };
 
 static int amdgpu_drm_queue_refcnt;
 static struct xorg_list amdgpu_drm_queue;
+static struct xorg_list amdgpu_drm_queue_deferred;
 static uintptr_t amdgpu_drm_queue_seq;
 
 
+static void
+amdgpu_drm_queue_handle_one(struct amdgpu_drm_queue_entry *e, unsigned int frame,
+			    uint64_t usec)
+{
+	xorg_list_del(&e->list);
+	if (e->handler) {
+		e->handler(e->crtc, frame, usec, e->data);
+	} else
+		e->abort(e->crtc, e->data);
+	free(e);
+}
+
 /*
  * Handle a DRM event
  */
@@ -66,19 +81,80 @@ amdgpu_drm_queue_handler(int fd, unsigned int frame, unsigned int sec,
 
 	xorg_list_for_each_entry_safe(e, tmp, &amdgpu_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);
+			amdgpu_drm_queue_handle_one(e, frame, (uint64_t)sec *
+						    1000000 + usec);
 			break;
 		}
 	}
 }
 
+/*
+ * Handle a DRM vblank event
+ */
+static void
+amdgpu_drm_queue_vblank_handler(int fd, unsigned int frame, unsigned int sec,
+				unsigned int usec, void *user_ptr)
+{
+	uintptr_t seq = (uintptr_t)user_ptr;
+	struct amdgpu_drm_queue_entry *e, *tmp;
+
+	xorg_list_for_each_entry_safe(e, tmp, &amdgpu_drm_queue, list) {
+		if (e->seq == seq) {
+			AMDGPUInfoPtr info = AMDGPUPTR(e->crtc->scrn);
+			drmmode_ptr drmmode = &info->drmmode;
+
+			e->usec = (uint64_t)sec * 1000000 + usec;
+
+			if (drmmode->event_context.vblank_handler ==
+			    amdgpu_drm_queue_vblank_handler) {
+				amdgpu_drm_queue_handle_one(e, frame, e->usec);
+			} else {
+				xorg_list_del(&e->list);
+				e->frame = frame;
+				xorg_list_append(&e->list,
+						 &amdgpu_drm_queue_deferred);
+			}
+
+			break;
+		}
+	}
+}
+
+/*
+ * Re-queue a DRM vblank event for deferred handling
+ */
+static void
+amdgpu_drm_queue_vblank_defer(int fd, unsigned int frame, unsigned int sec,
+			      unsigned int usec, void *user_ptr)
+{
+	amdgpu_drm_queue_vblank_handler(fd, frame, sec, usec, user_ptr);
+}
+
+/*
+ * Handle deferred DRM vblank events
+ *
+ * This function must be called after amdgpu_drm_wait_pending_flip, once
+ * it's safe to attempt queueing a flip again
+ */
+void
+amdgpu_drm_queue_handle_deferred(xf86CrtcPtr crtc)
+{
+	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+	drmmode_ptr drmmode = drmmode_crtc->drmmode;
+	struct amdgpu_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, &amdgpu_drm_queue_deferred, list) {
+		if (e->crtc == crtc)
+			amdgpu_drm_queue_handle_one(e, e->frame, e->usec);
+	}
+
+	drmmode->event_context.vblank_handler = amdgpu_drm_queue_vblank_handler;
+}
+
 /*
  * Enqueue a potential drm response; when the associated response
  * appears, we've got data to pass to the handler from here
@@ -134,12 +210,17 @@ amdgpu_drm_abort_one(struct amdgpu_drm_queue_entry *e)
 void
 amdgpu_drm_abort_client(ClientPtr client)
 {
-	struct amdgpu_drm_queue_entry *e;
+	struct amdgpu_drm_queue_entry *e, *tmp;
 
 	xorg_list_for_each_entry(e, &amdgpu_drm_queue, list) {
 		if (e->client == client)
 			e->handler = NULL;
 	}
+
+	xorg_list_for_each_entry_safe(e, tmp, &amdgpu_drm_queue_deferred, list) {
+		if (e->client == client)
+			amdgpu_drm_abort_one(e);
+	}
 }
 
 /*
@@ -153,6 +234,13 @@ amdgpu_drm_abort_entry(uintptr_t seq)
 	if (seq == AMDGPU_DRM_QUEUE_ERROR)
 		return;
 
+	xorg_list_for_each_entry_safe(e, tmp, &amdgpu_drm_queue_deferred, list) {
+		if (e->seq == seq) {
+			amdgpu_drm_abort_one(e);
+			return;
+		}
+	}
+
 	xorg_list_for_each_entry_safe(e, tmp, &amdgpu_drm_queue, list) {
 		if (e->seq == seq) {
 			amdgpu_drm_abort_one(e);
@@ -169,6 +257,13 @@ amdgpu_drm_abort_id(uint64_t id)
 {
 	struct amdgpu_drm_queue_entry *e, *tmp;
 
+	xorg_list_for_each_entry_safe(e, tmp, &amdgpu_drm_queue_deferred, list) {
+		if (e->id == id) {
+			amdgpu_drm_abort_one(e);
+			break;
+		}
+	}
+
 	xorg_list_for_each_entry_safe(e, tmp, &amdgpu_drm_queue, list) {
 		if (e->id == id) {
 			amdgpu_drm_abort_one(e);
@@ -186,6 +281,9 @@ void amdgpu_drm_wait_pending_flip(xf86CrtcPtr crtc)
 	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(crtc->scrn);
 	drmmode_ptr drmmode = drmmode_crtc->drmmode;
 
+	drmmode_crtc->wait_flip_nesting_level++;
+	drmmode->event_context.vblank_handler = amdgpu_drm_queue_vblank_defer;
+
 	while (drmmode_crtc->flip_pending &&
 	       drmHandleEvent(pAMDGPUEnt->fd, &drmmode->event_context) > 0);
 }
@@ -200,13 +298,14 @@ amdgpu_drm_queue_init(ScrnInfoPtr scrn)
 	drmmode_ptr drmmode = &info->drmmode;
 
 	drmmode->event_context.version = 2;
-	drmmode->event_context.vblank_handler = amdgpu_drm_queue_handler;
+	drmmode->event_context.vblank_handler = amdgpu_drm_queue_vblank_handler;
 	drmmode->event_context.page_flip_handler = amdgpu_drm_queue_handler;
 
 	if (amdgpu_drm_queue_refcnt++)
 		return;
 
 	xorg_list_init(&amdgpu_drm_queue);
+	xorg_list_init(&amdgpu_drm_queue_deferred);
 }
 
 /*
@@ -222,5 +321,10 @@ amdgpu_drm_queue_close(ScrnInfoPtr scrn)
 			amdgpu_drm_abort_one(e);
 	}
 
+	xorg_list_for_each_entry_safe(e, tmp, &amdgpu_drm_queue_deferred, list) {
+		if (e->crtc->scrn == scrn)
+			amdgpu_drm_abort_one(e);
+	}
+
 	amdgpu_drm_queue_refcnt--;
 }
diff --git a/src/amdgpu_drm_queue.h b/src/amdgpu_drm_queue.h
index cb6d5ff59..75609e39b 100644
--- a/src/amdgpu_drm_queue.h
+++ b/src/amdgpu_drm_queue.h
@@ -42,6 +42,7 @@ typedef void (*amdgpu_drm_handler_proc)(xf86CrtcPtr crtc, uint32_t seq,
 					uint64_t usec, void *data);
 typedef void (*amdgpu_drm_abort_proc)(xf86CrtcPtr crtc, void *data);
 
+void amdgpu_drm_queue_handle_deferred(xf86CrtcPtr crtc);
 uintptr_t amdgpu_drm_queue_alloc(xf86CrtcPtr crtc, ClientPtr client,
 				 uint64_t id, void *data,
 				 amdgpu_drm_handler_proc handler,
diff --git a/src/drmmode_display.c b/src/drmmode_display.c
index 615d3be8f..3b2de6839 100644
--- a/src/drmmode_display.c
+++ b/src/drmmode_display.c
@@ -305,6 +305,9 @@ drmmode_do_crtc_dpms(xf86CrtcPtr crtc, int mode)
 				nominal_frame_rate /= pix_in_frame;
 			drmmode_crtc->dpms_last_fps = nominal_frame_rate;
 		}
+
+		drmmode_crtc->dpms_mode = mode;
+		amdgpu_drm_queue_handle_deferred(crtc);
 	} else if (drmmode_crtc->dpms_mode != DPMSModeOn && mode == DPMSModeOn) {
 		/*
 		 * Off->On transition: calculate and accumulate the
@@ -322,8 +325,9 @@ drmmode_do_crtc_dpms(xf86CrtcPtr crtc, int mode)
 			drmmode_crtc->interpolated_vblanks += delta_seq;
 
 		}
+
+		drmmode_crtc->dpms_mode = DPMSModeOn;
 	}
-	drmmode_crtc->dpms_mode = mode;
 }
 
 static void
@@ -1415,6 +1419,7 @@ done:
 		}
 	}
 
+	amdgpu_drm_queue_handle_deferred(crtc);
 	return ret;
 }
 
@@ -2320,11 +2325,6 @@ drmmode_output_set_tear_free(AMDGPUEntPtr pAMDGPUEnt,
 	drmmode_output->tear_free = tear_free;
 
 	if (crtc) {
-		/* Wait for pending flips before drmmode_set_mode_major calls
-		 * drmmode_crtc_update_tear_free, to prevent a nested
-		 * drmHandleEvent call, which would hang
-		 */
-		amdgpu_drm_wait_pending_flip(crtc);
 		drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation,
 				       crtc->x, crtc->y);
 	}
@@ -3861,6 +3861,7 @@ Bool amdgpu_do_pageflip(ScrnInfoPtr scrn, ClientPtr client,
 	int i;
 	uint32_t flip_flags = flip_sync == FLIP_ASYNC ? DRM_MODE_PAGE_FLIP_ASYNC : 0;
 	drmmode_flipdata_ptr flipdata;
+	Bool handle_deferred = FALSE;
 	uintptr_t drm_queue_seq = 0;
 
 	flipdata = calloc(1, sizeof(drmmode_flipdata_rec));
@@ -3942,6 +3943,7 @@ Bool amdgpu_do_pageflip(ScrnInfoPtr scrn, ClientPtr client,
 
 			if (drmmode_crtc->scanout_update_pending) {
 				amdgpu_drm_wait_pending_flip(crtc);
+				handle_deferred = TRUE;
 				amdgpu_drm_abort_entry(drmmode_crtc->scanout_update_pending);
 				drmmode_crtc->scanout_update_pending = 0;
 			}
@@ -3975,6 +3977,8 @@ Bool amdgpu_do_pageflip(ScrnInfoPtr scrn, ClientPtr client,
 		drm_queue_seq = 0;
 	}
 
+	if (handle_deferred)
+		amdgpu_drm_queue_handle_deferred(ref_crtc);
 	if (flipdata->flip_count > 0)
 		return TRUE;
 
@@ -3995,5 +3999,7 @@ error:
 
 	xf86DrvMsg(scrn->scrnIndex, X_WARNING, "Page flip failed: %s\n",
 		   strerror(errno));
+	if (handle_deferred)
+		amdgpu_drm_queue_handle_deferred(ref_crtc);
 	return FALSE;
 }
diff --git a/src/drmmode_display.h b/src/drmmode_display.h
index 8b949f79d..28d15e282 100644
--- a/src/drmmode_display.h
+++ b/src/drmmode_display.h
@@ -119,6 +119,10 @@ typedef struct {
 
 	/* Modeset needed for DPMS on */
 	Bool need_modeset;
+	/* For keeping track of nested calls to drm_wait_pending_flip /
+	 * drm_queue_handle_deferred
+	 */
+	int wait_flip_nesting_level;
 	/* A flip to this FB is pending for this CRTC */
 	struct drmmode_fb *flip_pending;
 	/* The FB currently being scanned out by this CRTC, if any */
-- 
2.18.0



[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux