[patch] via_dma.c

Ian FREISLICH ianf at clue.co.za
Wed Oct 27 08:01:10 UTC 2010


Hi

While trying to make googleearth work on a Via Epia LN board with
openchrome and GL we noticed that the X server crashed after about
30 seconds of use.  I got a flood of the following messages.

Oct 26 12:13:24 test kernel: error: [drm:pid97391:via_hook_segment] *ERROR* 
Paused at incorrect address. 0xe1fcf300, 0xe1fc4900 0x00000000
Oct 26 12:13:24 test kernel: error: [drm:pid97391:via_hook_segment] *ERROR* 
Paused at incorrect address. 0xe1fd0300, 0xe1fc4900 0x00000000
Oct 26 12:13:24 test kernel: error: [drm:pid97391:via_cmdbuf_size] *ERROR* 
VIA_CMDBUF_LAG timed out.

Some research showed that occasionally the GPU pauses and when the
pipe is restarted, commands are reordered and the next command after
the paused command is dropped.

I located a patch which I hand applied because every chunk failed.
It has fixed the stability issue and gpu pipe restarting issue.

I still have a problem with googleearth only displaying its image
in part of the window.  It looks like the plane size is correct but
it gets the offset wrong so there is a blank on the right side the
size of the left menu pane and a blank at the bottom the size of
the window bar and menu bar at the top of the window.

Do you have any ideas how to fix this?

Ian

-- 
Ian Freislich

-------------- next part --------------
Index: sys/dev/drm/via_dma.c
===================================================================
RCS file: /home/ncvs/src/sys/dev/drm/via_dma.c,v
retrieving revision 1.2
diff -u -d -r1.2 via_dma.c
--- sys/dev/drm/via_dma.c	22 Apr 2010 18:21:25 -0000	1.2
+++ sys/dev/drm/via_dma.c	26 Oct 2010 15:03:04 -0000
@@ -119,10 +119,12 @@
 	uint32_t count;
 	hw_addr_ptr = dev_priv->hw_addr_ptr;
 	cur_addr = dev_priv->dma_low;
-	next_addr = cur_addr + size + 512 * 1024;
+	next_addr = cur_addr + size + 64 * 1024;
 	count = 1000000;
 	do {
-		hw_addr = *hw_addr_ptr - agp_base;
+		(void) *hw_addr_ptr;
+		DRM_MEMORYBARRIER();
+		hw_addr = (*hw_addr_ptr - agp_base);
 		if (count-- == 0) {
 			DRM_ERROR
 			    ("via_cmdbuf_wait timed out hw %x cur_addr %x next_addr %x\n",
@@ -272,7 +274,9 @@
 {
 	drm_via_private_t *dev_priv;
 	uint32_t *vb;
+#if 0
 	int ret;
+#endif
 
 	dev_priv = (drm_via_private_t *) dev->dev_private;
 
@@ -285,7 +289,12 @@
 		return -ENOMEM;
 	}
 
-	if (DRM_COPY_FROM_USER(dev_priv->pci_buf, cmd->buf, cmd->size))
+	vb = via_check_dma(dev_priv, (cmd->size < 0x100) ? 0x102 : cmd->size);
+	if (vb == NULL) {
+		return -EAGAIN;
+	}
+
+	if (DRM_COPY_FROM_USER(vb, cmd->buf, cmd->size))
 		return -EFAULT;
 
 	/*
@@ -294,19 +303,15 @@
 	 * copy it to AGP memory when ready.
 	 */
 
+#if 0
 	if ((ret =
 	     via_verify_command_stream((uint32_t *) dev_priv->pci_buf,
 				       cmd->size, dev, 1))) {
 		return ret;
 	}
 
-	vb = via_check_dma(dev_priv, (cmd->size < 0x100) ? 0x102 : cmd->size);
-	if (vb == NULL) {
-		return -EAGAIN;
-	}
-
 	memcpy(vb, dev_priv->pci_buf, cmd->size);
-
+#endif
 	dev_priv->dma_low += cmd->size;
 
 	/*
@@ -467,11 +472,28 @@
 		reader = *(dev_priv->hw_addr_ptr);
 		diff = (uint32_t) (ptr - reader) - dev_priv->dma_diff;
 		diff &= (dev_priv->dma_high - 1);
-		if (diff != 0 && diff < (dev_priv->dma_high >> 1)) {
-			DRM_ERROR("Paused at incorrect address. "
-				  "0x%08x, 0x%08x 0x%08x\n",
-				  ptr, reader, dev_priv->dma_diff);
-		} else if (diff == 0) {
+		if (diff < (dev_priv->dma_high >> 1)) {
+			if (diff != 0) {
+				volatile uint32_t *rekick;
+
+				DRM_INFO("Paused at incorrect address. "
+				    "0x%08x, 0x%08x 0x%08x. Restarting.\n",
+				    ptr, reader, dev_priv->dma_diff);
+
+				/*
+				 * Obtain the new pause address the command
+				 * reader was supposed to pick up.
+				 */
+
+				rekick = (volatile uint32_t *)
+				   dev_priv->dma_ptr +
+				   ((reader - dev_priv->dma_offset -
+				   (uint32_t) dev_priv->agpAddr +
+				   dev_priv->dma_diff - 4) >> 2);
+				pause_addr_lo = *rekick;
+				pause_addr_hi = *(--rekick);
+			}
+
 			/*
 			 * There is a concern that these writes may stall the PCI bus
 			 * if the GPU is not idle. However, idling the GPU first
@@ -612,13 +634,16 @@
 {
 	uint32_t agp_base;
 	uint32_t pause_addr_lo, pause_addr_hi;
-	uint32_t jump_addr_lo, jump_addr_hi;
-	volatile uint32_t *last_pause_ptr;
+	uint32_t jump_addr_lo, jump_addr_hi, hook_addr;
+	volatile uint32_t *hook_ptr;
 	uint32_t dma_low_save1, dma_low_save2;
 
 	agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr;
+	hook_ptr = via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0,
+				 &jump_addr_hi, &jump_addr_lo, 0);
 	via_align_cmd(dev_priv, HC_HAGPBpID_JUMP, 0, &jump_addr_hi,
-		      &jump_addr_lo, 0);
+		      &hook_addr, 0);
+	*(--hook_ptr) = hook_addr;
 
 	dev_priv->dma_wrap = dev_priv->dma_low;
 
@@ -634,13 +659,13 @@
 	via_dummy_bitblt(dev_priv);
 	via_dummy_bitblt(dev_priv);
 
-	last_pause_ptr =
+	hook_ptr =
 	    via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi,
-			  &pause_addr_lo, 0) - 1;
+			  &pause_addr_lo, 0);
 	via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi,
 		      &pause_addr_lo, 0);
 
-	*last_pause_ptr = pause_addr_lo;
+	*(--hook_ptr) = pause_addr_lo;
 	dma_low_save1 = dev_priv->dma_low;
 
 	/*
@@ -652,12 +677,12 @@
 	 * does not seem to get updated immediately when a jump occurs.
 	 */
 
-	last_pause_ptr =
+	hook_ptr =
 		via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi,
-			      &pause_addr_lo, 0) - 1;
+			      &pause_addr_lo, 0);
 	via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi,
 		      &pause_addr_lo, 0);
-	*last_pause_ptr = pause_addr_lo;
+	*(--hook_ptr) = pause_addr_lo;
 
 	dma_low_save2 = dev_priv->dma_low;
 	dev_priv->dma_low = dma_low_save1;
@@ -674,9 +699,16 @@
 
 static void via_cmdbuf_flush(drm_via_private_t * dev_priv, uint32_t cmd_type)
 {
-	uint32_t pause_addr_lo, pause_addr_hi;
+	uint32_t pause_addr_lo, pause_addr_hi, hook;
+	volatile uint32_t *hook_addr;
 
-	via_align_cmd(dev_priv, cmd_type, 0, &pause_addr_hi, &pause_addr_lo, 0);
+	hook_addr = via_align_cmd(dev_priv, cmd_type, 0, &pause_addr_hi, &pause_addr_lo, 0);
+#if 1
+	if (cmd_type == HC_HAGPBpID_PAUSE) {
+		via_align_cmd(dev_priv, cmd_type, 0, &pause_addr_hi, &hook, 0);
+		*(--hook_addr) = hook;
+	}
+#endif
 	via_hook_segment(dev_priv, pause_addr_hi, pause_addr_lo, 0);
 }
 


More information about the freebsd-current mailing list