OSDN Git Service

drm/i915: Handle incomplete Z_FINISH for compressed error states
[android-x86/kernel.git] / drivers / gpu / drm / i915 / i915_gpu_error.c
index f7f2aa7..a262a64 100644 (file)
@@ -232,6 +232,20 @@ static bool compress_init(struct compress *c)
        return true;
 }
 
+static void *compress_next_page(struct drm_i915_error_object *dst)
+{
+       unsigned long page;
+
+       if (dst->page_count >= dst->num_pages)
+               return ERR_PTR(-ENOSPC);
+
+       page = __get_free_page(GFP_ATOMIC | __GFP_NOWARN);
+       if (!page)
+               return ERR_PTR(-ENOMEM);
+
+       return dst->pages[dst->page_count++] = (void *)page;
+}
+
 static int compress_page(struct compress *c,
                         void *src,
                         struct drm_i915_error_object *dst)
@@ -245,19 +259,14 @@ static int compress_page(struct compress *c,
 
        do {
                if (zstream->avail_out == 0) {
-                       unsigned long page;
-
-                       page = __get_free_page(GFP_ATOMIC | __GFP_NOWARN);
-                       if (!page)
-                               return -ENOMEM;
+                       zstream->next_out = compress_next_page(dst);
+                       if (IS_ERR(zstream->next_out))
+                               return PTR_ERR(zstream->next_out);
 
-                       dst->pages[dst->page_count++] = (void *)page;
-
-                       zstream->next_out = (void *)page;
                        zstream->avail_out = PAGE_SIZE;
                }
 
-               if (zlib_deflate(zstream, Z_SYNC_FLUSH) != Z_OK)
+               if (zlib_deflate(zstream, Z_NO_FLUSH) != Z_OK)
                        return -EIO;
        } while (zstream->avail_in);
 
@@ -268,19 +277,42 @@ static int compress_page(struct compress *c,
        return 0;
 }
 
-static void compress_fini(struct compress *c,
+static int compress_flush(struct compress *c,
                          struct drm_i915_error_object *dst)
 {
        struct z_stream_s *zstream = &c->zstream;
 
-       if (dst) {
-               zlib_deflate(zstream, Z_FINISH);
-               dst->unused = zstream->avail_out;
-       }
+       do {
+               switch (zlib_deflate(zstream, Z_FINISH)) {
+               case Z_OK: /* more space requested */
+                       zstream->next_out = compress_next_page(dst);
+                       if (IS_ERR(zstream->next_out))
+                               return PTR_ERR(zstream->next_out);
+
+                       zstream->avail_out = PAGE_SIZE;
+                       break;
+
+               case Z_STREAM_END:
+                       goto end;
+
+               default: /* any error */
+                       return -EIO;
+               }
+       } while (1);
+
+end:
+       memset(zstream->next_out, 0, zstream->avail_out);
+       dst->unused = zstream->avail_out;
+       return 0;
+}
+
+static void compress_fini(struct compress *c,
+                         struct drm_i915_error_object *dst)
+{
+       struct z_stream_s *zstream = &c->zstream;
 
        zlib_deflateEnd(zstream);
        kfree(zstream->workspace);
-
        if (c->tmp)
                free_page((unsigned long)c->tmp);
 }
@@ -319,6 +351,12 @@ static int compress_page(struct compress *c,
        return 0;
 }
 
+static int compress_flush(struct compress *c,
+                         struct drm_i915_error_object *dst)
+{
+       return 0;
+}
+
 static void compress_fini(struct compress *c,
                          struct drm_i915_error_object *dst)
 {
@@ -917,6 +955,7 @@ i915_error_object_create(struct drm_i915_private *i915,
        unsigned long num_pages;
        struct sgt_iter iter;
        dma_addr_t dma;
+       int ret;
 
        if (!vma)
                return NULL;
@@ -930,6 +969,7 @@ i915_error_object_create(struct drm_i915_private *i915,
 
        dst->gtt_offset = vma->node.start;
        dst->gtt_size = vma->node.size;
+       dst->num_pages = num_pages;
        dst->page_count = 0;
        dst->unused = 0;
 
@@ -938,28 +978,26 @@ i915_error_object_create(struct drm_i915_private *i915,
                return NULL;
        }
 
+       ret = -EINVAL;
        for_each_sgt_dma(dma, iter, vma->pages) {
                void __iomem *s;
-               int ret;
 
                ggtt->vm.insert_page(&ggtt->vm, dma, slot, I915_CACHE_NONE, 0);
 
                s = io_mapping_map_atomic_wc(&ggtt->iomap, slot);
                ret = compress_page(&compress, (void  __force *)s, dst);
                io_mapping_unmap_atomic(s);
-
                if (ret)
-                       goto unwind;
+                       break;
        }
-       goto out;
 
-unwind:
-       while (dst->page_count--)
-               free_page((unsigned long)dst->pages[dst->page_count]);
-       kfree(dst);
-       dst = NULL;
+       if (ret || compress_flush(&compress, dst)) {
+               while (dst->page_count--)
+                       free_page((unsigned long)dst->pages[dst->page_count]);
+               kfree(dst);
+               dst = NULL;
+       }
 
-out:
        compress_fini(&compress, dst);
        ggtt->vm.clear_range(&ggtt->vm, slot, PAGE_SIZE);
        return dst;