[PATCH 1/4] Support 64-bit MSC values. Handle kernel vageries about MSC reporting

Keith Packard keithp at keithp.com
Wed Nov 20 12:53:34 PST 2013


The kernel sometimes reports bogus MSC values, especially when
suspending and resuming the machine. Deal with this by tracking an
offset to ensure that the MSC seen by applications increases
monotonically, and at a reasonable pace.

Also, provide a full 64 bits of MSC value by noticing wrapping and
tracking the high 32-bits of MSC separately.

Signed-off-by: Keith Packard <keithp at keithp.com>
---
 src/uxa/intel.h         |   9 ++++
 src/uxa/intel_display.c | 118 ++++++++++++++++++++++++++++++++++++++++-----
 src/uxa/intel_dri.c     | 125 ++++++++++++++++--------------------------------
 3 files changed, 156 insertions(+), 96 deletions(-)

diff --git a/src/uxa/intel.h b/src/uxa/intel.h
index 131f18c..f05b160 100644
--- a/src/uxa/intel.h
+++ b/src/uxa/intel.h
@@ -401,6 +401,15 @@ extern int intel_crtc_id(xf86CrtcPtr crtc);
 extern int intel_output_dpms_status(xf86OutputPtr output);
 extern void intel_copy_fb(ScrnInfoPtr scrn);
 
+int
+intel_get_crtc_msc_ust(ScrnInfoPtr scrn, xf86CrtcPtr crtc, uint64_t *msc, uint64_t *ust);
+
+uint32_t
+intel_crtc_msc_to_sequence(ScrnInfoPtr scrn, xf86CrtcPtr crtc, uint64_t expect);
+
+uint64_t
+intel_sequence_to_crtc_msc(xf86CrtcPtr crtc, uint32_t sequence);
+
 enum DRI2FrameEventType {
 	DRI2_SWAP,
 	DRI2_SWAP_CHAIN,
diff --git a/src/uxa/intel_display.c b/src/uxa/intel_display.c
index 3c2f964..09cd48f 100644
--- a/src/uxa/intel_display.c
+++ b/src/uxa/intel_display.c
@@ -71,9 +71,8 @@ struct intel_mode {
 	DRI2FrameEventPtr flip_info;
 	int old_fb_id;
 	int flip_count;
-	unsigned int fe_frame;
-	unsigned int fe_tv_sec;
-	unsigned int fe_tv_usec;
+	uint64_t fe_msc;
+        uint64_t fe_usec;
 
 	struct list outputs;
 	struct list crtcs;
@@ -97,6 +96,9 @@ struct intel_crtc {
 	struct list link;
 	PixmapPtr scanout_pixmap;
 	uint32_t scanout_fb_id;
+        int32_t vblank_offset;
+        uint32_t msc_prev;
+        uint64_t msc_high;
 };
 
 struct intel_property {
@@ -1647,9 +1649,8 @@ intel_do_pageflip(intel_screen_private *intel,
 	 * Also, flips queued on disabled or incorrectly configured displays
 	 * may never complete; this is a configuration error.
 	 */
-	mode->fe_frame = 0;
-	mode->fe_tv_sec = 0;
-	mode->fe_tv_usec = 0;
+	mode->fe_msc = 0;
+	mode->fe_usec = 0;
 
 	for (i = 0; i < config->num_crtc; i++) {
 		if (!intel_crtc_on(config->crtc[i]))
@@ -1705,6 +1706,102 @@ static const xf86CrtcConfigFuncsRec intel_xf86crtc_config_funcs = {
 	intel_xf86crtc_resize
 };
 
+static uint32_t pipe_select(int pipe)
+{
+	if (pipe > 1)
+		return pipe << DRM_VBLANK_HIGH_CRTC_SHIFT;
+	else if (pipe > 0)
+		return DRM_VBLANK_SECONDARY;
+	else
+		return 0;
+}
+
+/*
+ * Get the current msc/ust value from the kernel
+ */
+static int
+intel_get_msc_ust(ScrnInfoPtr scrn, xf86CrtcPtr crtc, uint32_t *msc, uint64_t *ust)
+{
+	intel_screen_private    *intel = intel_get_screen_private(scrn);
+        drmVBlank               vbl;
+        int                     ret;
+
+        /* Get current count */
+        vbl.request.type = DRM_VBLANK_RELATIVE | pipe_select(intel_crtc_to_pipe(crtc));
+        vbl.request.sequence = 0;
+        vbl.request.signal = 0;
+        ret = drmWaitVBlank(intel->drmSubFD, &vbl);
+        if (ret) {
+                *msc = 0;
+                *ust = 0;
+                return BadMatch;
+        } else {
+                *msc = vbl.reply.sequence;
+                *ust = (CARD64) vbl.reply.tval_sec * 1000000 + vbl.reply.tval_usec;
+        }
+        return Success;
+}
+
+/*
+ * Convert a 32-bit kernel MSC sequence number to a 64-bit local sequence
+ * number, adding in the vblank_offset and high 32 bits, and dealing
+ * with 64-bit wrapping
+ */
+uint64_t
+intel_sequence_to_crtc_msc(xf86CrtcPtr crtc, uint32_t sequence)
+{
+	struct intel_crtc       *intel_crtc = crtc->driver_private;
+        sequence += intel_crtc->vblank_offset;
+
+        if ((int32_t) (sequence - intel_crtc->msc_prev) < -0x40000000)
+                intel_crtc->msc_high += 0x100000000L;
+        intel_crtc->msc_prev = sequence;
+        return intel_crtc->msc_high + sequence;
+}
+
+/*
+ * Get the current 64-bit adjust MSC and UST value
+ */
+int
+intel_get_crtc_msc_ust(ScrnInfoPtr scrn, xf86CrtcPtr crtc, uint64_t *msc, uint64_t *ust)
+{
+        uint32_t                sequence;
+        int                     ret;
+
+        ret = intel_get_msc_ust(scrn, crtc, &sequence, ust);
+        *msc = intel_sequence_to_crtc_msc(crtc, sequence);
+        return ret;
+}
+
+/*
+ * Convert a 64-bit adjusted MSC value into a 32-bit kernel sequence number,
+ * removing the high 32 bits and subtracting out the vblank_offset term.
+ *
+ * This also updates the vblank_offset when it notices that the value should
+ * change.
+ */
+uint32_t
+intel_crtc_msc_to_sequence(ScrnInfoPtr scrn, xf86CrtcPtr crtc, uint64_t expect)
+{
+	struct intel_crtc       *intel_crtc = crtc->driver_private;
+        uint64_t                msc;
+        uint64_t                ust;
+        int64_t                 diff;
+
+        intel_get_crtc_msc_ust(scrn, crtc, &msc, &ust);
+        diff = expect - msc;
+
+        /* We're way off here, assume that the kernel has lost its mind
+         * and smack the vblank back to something sensible
+         */
+        if (diff < -200 || 200 < diff) {
+                intel_crtc->vblank_offset += (int32_t) diff;
+                if (-200 < intel_crtc->vblank_offset && intel_crtc->vblank_offset < 200)
+                        intel_crtc->vblank_offset = 0;
+        }
+        return (uint32_t) (expect - intel_crtc->vblank_offset);
+}
+
 static void
 intel_vblank_handler(int fd, unsigned int frame, unsigned int tv_sec,
 		       unsigned int tv_usec, void *event)
@@ -1722,9 +1819,8 @@ intel_page_flip_handler(int fd, unsigned int frame, unsigned int tv_sec,
 	/* Is this the event whose info shall be delivered to higher level? */
 	if (flip->dispatch_me) {
 		/* Yes: Cache msc, ust for later delivery. */
-		mode->fe_frame = frame;
-		mode->fe_tv_sec = tv_sec;
-		mode->fe_tv_usec = tv_usec;
+		mode->fe_msc = frame;
+		mode->fe_usec = (uint64_t) tv_sec * 1000000 + tv_usec;
 	}
 	free(flip);
 
@@ -1740,8 +1836,8 @@ intel_page_flip_handler(int fd, unsigned int frame, unsigned int tv_sec,
 		return;
 
 	/* Deliver cached msc, ust from reference crtc to flip event handler */
-	I830DRI2FlipEventHandler(mode->fe_frame, mode->fe_tv_sec,
-				 mode->fe_tv_usec, mode->flip_info);
+	I830DRI2FlipEventHandler((uint32_t) mode->fe_msc, mode->fe_usec / 1000000,
+				 mode->fe_usec % 1000000, mode->flip_info);
 }
 
 static void
diff --git a/src/uxa/intel_dri.c b/src/uxa/intel_dri.c
index acedd0b..9c2aafd 100644
--- a/src/uxa/intel_dri.c
+++ b/src/uxa/intel_dri.c
@@ -575,14 +575,13 @@ static void I830DRI2ReferenceBuffer(DRI2Buffer2Ptr buffer)
 	}
 }
 
-static int
-I830DRI2DrawablePipe(DrawablePtr pDraw)
+static xf86CrtcPtr
+I830DRI2DrawableCrtc(DrawablePtr pDraw)
 {
 	ScreenPtr pScreen = pDraw->pScreen;
 	ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
 	BoxRec box, crtcbox;
 	xf86CrtcPtr crtc;
-	int pipe = -1;
 
 	box.x1 = pDraw->x;
 	box.y1 = pDraw->y;
@@ -593,9 +592,9 @@ I830DRI2DrawablePipe(DrawablePtr pDraw)
 
 	/* Make sure the CRTC is valid and this is the real front buffer */
 	if (crtc != NULL && !crtc->rotatedData)
-		pipe = intel_crtc_to_pipe(crtc);
+		return crtc;
 
-	return pipe;
+	return NULL;
 }
 
 static RESTYPE	frame_event_client_type, frame_event_drawable_type;
@@ -953,7 +952,7 @@ can_exchange(DrawablePtr drawable, DRI2BufferPtr front, DRI2BufferPtr back)
 	if (!pScrn->vtSema)
 		return FALSE;
 
-	if (I830DRI2DrawablePipe(drawable) < 0)
+	if (I830DRI2DrawableCrtc(drawable) == NULL)
 		return FALSE;
 
 	if (!DRI2CanFlip(drawable))
@@ -1160,21 +1159,19 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
 	intel_screen_private *intel = intel_get_screen_private(scrn);
 	drmVBlank vbl;
-	int ret, pipe = I830DRI2DrawablePipe(draw), flip = 0;
+	int ret;
+        xf86CrtcPtr crtc = I830DRI2DrawableCrtc(draw);
+        int pipe = crtc ? intel_crtc_to_pipe(crtc) : -1;
+        int flip = 0;
 	DRI2FrameEventPtr swap_info = NULL;
 	enum DRI2FrameEventType swap_type = DRI2_SWAP;
-	CARD64 current_msc;
+	uint64_t current_msc, current_ust;
+        uint64_t request_msc;
 
 	/* Drawable not displayed... just complete the swap */
 	if (pipe == -1)
 	    goto blit_fallback;
 
-	/* Truncate to match kernel interfaces; means occasional overflow
-	 * misses, but that's generally not a big deal */
-	*target_msc &= 0xffffffff;
-	divisor &= 0xffffffff;
-	remainder &= 0xffffffff;
-
 	swap_info = calloc(1, sizeof(DRI2FrameEventRec));
 	if (!swap_info)
 	    goto blit_fallback;
@@ -1197,18 +1194,7 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 	I830DRI2ReferenceBuffer(front);
 	I830DRI2ReferenceBuffer(back);
 
-	/* Get current count */
-	vbl.request.type = DRM_VBLANK_RELATIVE | pipe_select(pipe);
-	vbl.request.sequence = 0;
-	ret = drmWaitVBlank(intel->drmSubFD, &vbl);
-	if (ret) {
-		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
-			   "first get vblank counter failed: %s\n",
-			   strerror(errno));
-		goto blit_fallback;
-	}
-
-	current_msc = vbl.reply.sequence;
+        ret = intel_get_crtc_msc_ust(scrn, crtc, &current_msc, &current_ust);
 
 	/* Flips need to be submitted one frame before */
 	if (can_exchange(draw, front, back)) {
@@ -1257,7 +1243,7 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 		if (current_msc >= *target_msc)
 			*target_msc = current_msc;
 
-		vbl.request.sequence = *target_msc;
+		vbl.request.sequence = intel_crtc_msc_to_sequence(scrn, crtc, *target_msc);
 		vbl.request.signal = (unsigned long)swap_info;
 		ret = drmWaitVBlank(intel->drmSubFD, &vbl);
 		if (ret) {
@@ -1267,7 +1253,7 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 			goto blit_fallback;
 		}
 
-		*target_msc = vbl.reply.sequence + flip;
+                *target_msc = intel_sequence_to_crtc_msc(crtc, vbl.reply.sequence + flip);
 		swap_info->frame = *target_msc;
 
 		return TRUE;
@@ -1283,8 +1269,8 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 	if (flip == 0)
 		vbl.request.type |= DRM_VBLANK_NEXTONMISS;
 
-	vbl.request.sequence = current_msc - (current_msc % divisor) +
-		remainder;
+        request_msc = current_msc - (current_msc % divisor) +
+                remainder;
 
 	/*
 	 * If the calculated deadline vbl.request.sequence is smaller than
@@ -1297,8 +1283,10 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 	 * into account, as well as a potential DRM_VBLANK_NEXTONMISS delay
 	 * if we are blitting/exchanging instead of flipping.
 	 */
-	if (vbl.request.sequence <= current_msc)
-		vbl.request.sequence += divisor;
+	if (request_msc <= current_msc)
+		request_msc += divisor;
+
+        vbl.request.sequence = intel_crtc_msc_to_sequence(scrn, crtc, request_msc);
 
 	/* Account for 1 frame extra pageflip delay if flip > 0 */
 	vbl.request.sequence -= flip;
@@ -1313,7 +1301,7 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 	}
 
 	/* Adjust returned value for 1 fame pageflip offset of flip > 0 */
-	*target_msc = vbl.reply.sequence + flip;
+	*target_msc = intel_sequence_to_crtc_msc(crtc, vbl.reply.sequence + flip);
 	swap_info->frame = *target_msc;
 
 	return TRUE;
@@ -1346,35 +1334,20 @@ I830DRI2GetMSC(DrawablePtr draw, CARD64 *ust, CARD64 *msc)
 {
 	ScreenPtr screen = draw->pScreen;
 	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
-	intel_screen_private *intel = intel_get_screen_private(scrn);
-	drmVBlank vbl;
-	int ret, pipe = I830DRI2DrawablePipe(draw);
+	int ret;
+        xf86CrtcPtr crtc = I830DRI2DrawableCrtc(draw);
 
 	/* Drawable not displayed, make up a *monotonic* value */
-	if (pipe == -1) {
+	if (crtc == NULL) {
 		*ust = gettime_us();
 		*msc = 0;
 		return TRUE;
 	}
 
-	vbl.request.type = DRM_VBLANK_RELATIVE | pipe_select(pipe);
-	vbl.request.sequence = 0;
+        ret = intel_get_crtc_msc_ust(scrn, crtc, msc, ust);
 
-	ret = drmWaitVBlank(intel->drmSubFD, &vbl);
-	if (ret) {
-		static int limit = 5;
-		if (limit) {
-			xf86DrvMsg(scrn->scrnIndex, X_WARNING,
-				   "%s:%d get vblank counter failed: %s\n",
-				   __FUNCTION__, __LINE__,
-				   strerror(errno));
-			limit--;
-		}
-		return FALSE;
-	}
-
-	*ust = ((CARD64)vbl.reply.tval_sec * 1000000) + vbl.reply.tval_usec;
-	*msc = vbl.reply.sequence;
+        if (ret)
+                return FALSE;
 
 	return TRUE;
 }
@@ -1394,14 +1367,10 @@ I830DRI2ScheduleWaitMSC(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
 	intel_screen_private *intel = intel_get_screen_private(scrn);
 	DRI2FrameEventPtr wait_info;
 	drmVBlank vbl;
-	int ret, pipe = I830DRI2DrawablePipe(draw);
-	CARD64 current_msc;
-
-	/* Truncate to match kernel interfaces; means occasional overflow
-	 * misses, but that's generally not a big deal */
-	target_msc &= 0xffffffff;
-	divisor &= 0xffffffff;
-	remainder &= 0xffffffff;
+	int ret;
+        xf86CrtcPtr crtc = I830DRI2DrawableCrtc(draw);
+        int pipe = crtc ? intel_crtc_to_pipe(crtc) : -1;
+	CARD64 current_msc, current_ust, request_msc;
 
 	/* Drawable not visible, return immediately */
 	if (pipe == -1)
@@ -1423,22 +1392,7 @@ I830DRI2ScheduleWaitMSC(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
 	}
 
 	/* Get current count */
-	vbl.request.type = DRM_VBLANK_RELATIVE | pipe_select(pipe);
-	vbl.request.sequence = 0;
-	ret = drmWaitVBlank(intel->drmSubFD, &vbl);
-	if (ret) {
-		static int limit = 5;
-		if (limit) {
-			xf86DrvMsg(scrn->scrnIndex, X_WARNING,
-				   "%s:%d get vblank counter failed: %s\n",
-				   __FUNCTION__, __LINE__,
-				   strerror(errno));
-			limit--;
-		}
-		goto out_free;
-	}
-
-	current_msc = vbl.reply.sequence;
+        ret = intel_get_crtc_msc_ust(scrn, crtc, &current_msc, &current_ust);
 
 	/*
 	 * If divisor is zero, or current_msc is smaller than target_msc,
@@ -1456,7 +1410,7 @@ I830DRI2ScheduleWaitMSC(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
 			target_msc = current_msc;
 		vbl.request.type =
 			DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT | pipe_select(pipe);
-		vbl.request.sequence = target_msc;
+		vbl.request.sequence = intel_crtc_msc_to_sequence(scrn, crtc, target_msc);
 		vbl.request.signal = (unsigned long)wait_info;
 		ret = drmWaitVBlank(intel->drmSubFD, &vbl);
 		if (ret) {
@@ -1471,7 +1425,7 @@ I830DRI2ScheduleWaitMSC(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
 			goto out_free;
 		}
 
-		wait_info->frame = vbl.reply.sequence;
+		wait_info->frame = intel_sequence_to_crtc_msc(crtc, vbl.reply.sequence);
 		DRI2BlockClient(client, draw);
 		return TRUE;
 	}
@@ -1483,9 +1437,8 @@ I830DRI2ScheduleWaitMSC(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
 	vbl.request.type =
 		DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT | pipe_select(pipe);
 
-	vbl.request.sequence = current_msc - (current_msc % divisor) +
-	    remainder;
-
+        request_msc = current_msc - (current_msc % divisor) +
+                remainder;
 	/*
 	 * If calculated remainder is larger than requested remainder,
 	 * it means we've passed the last point where
@@ -1493,7 +1446,9 @@ I830DRI2ScheduleWaitMSC(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
 	 * that will happen.
 	 */
 	if ((current_msc % divisor) >= remainder)
-	    vbl.request.sequence += divisor;
+                request_msc += divisor;
+
+	vbl.request.sequence = intel_crtc_msc_to_sequence(scrn, crtc, request_msc);
 
 	vbl.request.signal = (unsigned long)wait_info;
 	ret = drmWaitVBlank(intel->drmSubFD, &vbl);
@@ -1509,7 +1464,7 @@ I830DRI2ScheduleWaitMSC(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
 		goto out_free;
 	}
 
-	wait_info->frame = vbl.reply.sequence;
+	wait_info->frame = intel_sequence_to_crtc_msc(crtc, vbl.reply.sequence);
 	DRI2BlockClient(client, draw);
 
 	return TRUE;
-- 
1.8.4.2



More information about the xorg-devel mailing list