[Intel-gfx] [PATCH] drm/i915: Deflate error objects upon capture

Chris Wilson chris at chris-wilson.co.uk
Fri Oct 31 10:12:27 CET 2014


When we capture the GPU error state, we allocate large amounts of memory
to preserve copies of the active objects on the GPU. We can compress the
copy in memory, and use an asci85 encoding when printing them out (to
avoid presenting binary data to unsuspecting catting of debugfs/procfs).

v2: Trailing newline and print all compressed objects correctly

Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
Cc: Mika Kuoppala <mika.kuoppala at intel.com>
---
 drivers/gpu/drm/i915/Kconfig          |   1 +
 drivers/gpu/drm/i915/i915_drv.h       |   1 +
 drivers/gpu/drm/i915/i915_gpu_error.c | 156 ++++++++++++++++++++++------------
 3 files changed, 103 insertions(+), 55 deletions(-)

diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig
index 4e39ab34eb1c..d8a0687de4f4 100644
--- a/drivers/gpu/drm/i915/Kconfig
+++ b/drivers/gpu/drm/i915/Kconfig
@@ -6,6 +6,7 @@ config DRM_I915
 	select INTEL_GTT
 	select AGP_INTEL if AGP
 	select INTERVAL_TREE
+	select ZLIB_DEFLATE
 	# we need shmfs for the swappable backing store, and in particular
 	# the shmem_readpage() which depends upon tmpfs
 	select SHMEM
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 12c4b918fa3d..669a066cbd50 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -391,6 +391,7 @@ struct drm_i915_error_state {
 
 		struct drm_i915_error_object {
 			int page_count;
+			int unused;
 			u32 gtt_offset;
 			u32 *pages[0];
 		} *ringbuffer, *batchbuffer, *wa_batchbuffer, *ctx, *hws_page;
diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c
index 3e97db2c9fe8..5b67799e5a9f 100644
--- a/drivers/gpu/drm/i915/i915_gpu_error.c
+++ b/drivers/gpu/drm/i915/i915_gpu_error.c
@@ -28,6 +28,7 @@
  */
 
 #include <generated/utsrelease.h>
+#include <linux/zlib.h>
 #include "i915_drv.h"
 
 static const char *yesno(int v)
@@ -328,18 +329,43 @@ void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...)
 	va_end(args);
 }
 
+static bool
+ascii85_encode(uint32_t in, char *out)
+{
+	int i;
+
+	if (in == 0)
+		return false;
+
+	out[5] = '\0';
+	for (i = 5; i--; ) {
+		int digit = in % 85;
+		out[i] = digit + 33;
+		in /= 85;
+	}
+
+	return true;
+}
+
 static void print_error_obj(struct drm_i915_error_state_buf *m,
 			    struct drm_i915_error_object *obj)
 {
-	int page, offset, elt;
+	char out[6];
+	int page;
 
-	for (page = offset = 0; page < obj->page_count; page++) {
-		for (elt = 0; elt < PAGE_SIZE/4; elt++) {
-			err_printf(m, "%08x :  %08x\n", offset,
-				   obj->pages[page][elt]);
-			offset += 4;
+	for (page = 0; page < obj->page_count; page++) {
+		int i, len = PAGE_SIZE;
+		if (page == obj->page_count - 1)
+			len -= obj->unused;
+		len = (len + 3) / 4;
+		for (i = 0; i < len; i++) {
+			if (ascii85_encode(obj->pages[page][i], out))
+				err_puts(m, out);
+			else
+				err_puts(m, "z");
 		}
 	}
+	err_puts(m, "\n");
 }
 
 int i915_error_state_to_str(struct drm_i915_error_state_buf *m,
@@ -349,8 +375,8 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m,
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	struct drm_i915_error_state *error = error_priv->error;
 	struct drm_i915_error_object *obj;
-	int i, j, offset, elt;
 	int max_hangcheck_score;
+	int i, j;
 
 	if (!error) {
 		err_printf(m, "no error state collected\n");
@@ -475,16 +501,7 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m,
 		if ((obj = ering->hws_page)) {
 			err_printf(m, "%s --- HW Status = 0x%08x\n",
 				   name, obj->gtt_offset);
-			offset = 0;
-			for (elt = 0; elt < PAGE_SIZE/16; elt += 4) {
-				err_printf(m, "[%04x] %08x %08x %08x %08x\n",
-					   offset,
-					   obj->pages[0][elt],
-					   obj->pages[0][elt+1],
-					   obj->pages[0][elt+2],
-					   obj->pages[0][elt+3]);
-					offset += 16;
-			}
+			print_error_obj(m, obj);
 		}
 
 		if ((obj = error->ring[i].ctx)) {
@@ -496,14 +513,7 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m,
 
 	if ((obj = error->semaphore_obj)) {
 		err_printf(m, "Semaphore page = 0x%08x\n", obj->gtt_offset);
-		for (elt = 0; elt < PAGE_SIZE/16; elt += 4) {
-			err_printf(m, "[%04x] %08x %08x %08x %08x\n",
-				   elt * 4,
-				   obj->pages[0][elt],
-				   obj->pages[0][elt+1],
-				   obj->pages[0][elt+2],
-				   obj->pages[0][elt+3]);
-		}
+		print_error_obj(m, obj);
 	}
 
 	if (error->overlay)
@@ -559,7 +569,7 @@ static void i915_error_object_free(struct drm_i915_error_object *obj)
 		return;
 
 	for (page = 0; page < obj->page_count; page++)
-		kfree(obj->pages[page]);
+		free_page((unsigned long)obj->pages[page]);
 
 	kfree(obj);
 }
@@ -585,6 +595,35 @@ static void i915_error_state_free(struct kref *error_ref)
 	kfree(error);
 }
 
+static int compress_page(struct z_stream_s *zstream,
+			 void *src,
+			 struct drm_i915_error_object *dst)
+{
+	zstream->next_in = src;
+	zstream->avail_in = PAGE_SIZE;
+
+	do {
+		if (zstream->avail_out == 0) {
+			zstream->next_out = (void *)__get_free_page(GFP_ATOMIC);
+			if (zstream->next_out == NULL)
+				return -ENOMEM;
+
+			dst->pages[dst->page_count++] = (void *)zstream->next_out;
+			zstream->avail_out = PAGE_SIZE;
+		}
+
+		if (zlib_deflate(zstream, Z_SYNC_FLUSH) != Z_OK)
+			return -EIO;
+
+#if 0
+		if (zstream->total_out > zstream->total_in)
+			return -E2BIG;
+#endif
+	} while (zstream->avail_in);
+
+	return 0;
+}
+
 static struct drm_i915_error_object *
 i915_error_object_create(struct drm_i915_private *dev_priv,
 			 struct i915_vma *vma)
@@ -593,8 +632,8 @@ i915_error_object_create(struct drm_i915_private *dev_priv,
 	struct drm_i915_error_object *dst;
 	int num_pages;
 	bool use_ggtt;
-	int i = 0;
 	u32 reloc_offset;
+	struct z_stream_s zstream;
 
 	if (vma == NULL)
 		return NULL;
@@ -605,11 +644,23 @@ i915_error_object_create(struct drm_i915_private *dev_priv,
 
 	num_pages = src->base.size >> PAGE_SHIFT;
 
-	dst = kmalloc(sizeof(*dst) + num_pages * sizeof(u32 *), GFP_ATOMIC);
+	dst = kmalloc(sizeof(*dst) + (10 * num_pages * sizeof(u32 *) >> 3), GFP_ATOMIC);
 	if (dst == NULL)
 		return NULL;
 
 	dst->gtt_offset = vma->node.start;
+	dst->page_count = 0;
+	dst->unused = 0;
+
+	memset(&zstream, 0, sizeof(zstream));
+	zstream.workspace = kmalloc(max(zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL),
+					zlib_inflate_workspacesize()),
+					GFP_ATOMIC);
+	if (zstream.workspace == NULL ||
+	    zlib_deflateInit(&zstream, Z_DEFAULT_COMPRESSION) != Z_OK) {
+		kfree(dst);
+		return NULL;
+	}
 
 	reloc_offset = dst->gtt_offset;
 	use_ggtt = (src->cache_level == I915_CACHE_NONE &&
@@ -632,53 +683,48 @@ i915_error_object_create(struct drm_i915_private *dev_priv,
 	if (use_ggtt && src->cache_level != I915_CACHE_NONE && !HAS_LLC(dev_priv->dev))
 		goto unwind;
 
-	dst->page_count = num_pages;
+	if (!use_ggtt)
+		reloc_offset = 0;
+
 	while (num_pages--) {
 		unsigned long flags;
-		void *d;
-
-		d = kmalloc(PAGE_SIZE, GFP_ATOMIC);
-		if (d == NULL)
-			goto unwind;
+		void *s;
+		int ret;
 
 		local_irq_save(flags);
 		if (use_ggtt) {
-			void __iomem *s;
-
 			/* Simply ignore tiling or any overlapping fence.
 			 * It's part of the error state, and this hopefully
 			 * captures what the GPU read.
 			 */
-
-			s = io_mapping_map_atomic_wc(dev_priv->gtt.mappable,
-						     reloc_offset);
-			memcpy_fromio(d, s, PAGE_SIZE);
+			s = (void *__force)
+				io_mapping_map_atomic_wc(dev_priv->gtt.mappable,
+							 reloc_offset);
+			ret = compress_page(&zstream, s, dst);
 			io_mapping_unmap_atomic(s);
 		} else {
-			struct page *page;
-			void *s;
-
-			page = i915_gem_object_get_page(src, i);
-
-			drm_clflush_pages(&page, 1);
-
-			s = kmap_atomic(page);
-			memcpy(d, s, PAGE_SIZE);
+			s = kmap_atomic(i915_gem_object_get_page(src, reloc_offset >> PAGE_SHIFT));
+			drm_clflush_virt_range(s, PAGE_SIZE);
+			ret = compress_page(&zstream, s, dst);
 			kunmap_atomic(s);
-
-			drm_clflush_pages(&page, 1);
 		}
 		local_irq_restore(flags);
-
-		dst->pages[i++] = d;
+		if (ret)
+			goto unwind;
 		reloc_offset += PAGE_SIZE;
 	}
+	zlib_deflate(&zstream, Z_FINISH);
+	dst->unused = zstream.avail_out;
+	zlib_deflateEnd(&zstream);
+	kfree(zstream.workspace);
 
 	return dst;
 
 unwind:
-	while (i--)
-		kfree(dst->pages[i]);
+	while (dst->page_count--)
+		free_page((unsigned long)dst->pages[dst->page_count]);
+	zlib_deflateEnd(&zstream);
+	kfree(zstream.workspace);
 	kfree(dst);
 	return NULL;
 }
-- 
2.1.1




More information about the Intel-gfx mailing list